iface: Begin parsing Hop-By-Hop extension headers

Begin add support for parsing extension headers.

Closes: #212
Approved by: whitequark
v0.7.x
Dan Robertson 2018-05-09 19:05:19 +00:00 committed by Homu
parent 42a1da4d72
commit 7462664954
2 changed files with 83 additions and 13 deletions

View File

@ -1,5 +1,6 @@
// Heads up! Before working on this file you should read the parts
// of RFC 1122 that discuss Ethernet, ARP and IP.
// of RFC 1122 that discuss Ethernet, ARP and IP for any IPv4 work
// and RFCs 8200 and 4861 for any IPv6 and NDISC work.
use core::cmp;
use managed::ManagedSlice;
@ -23,6 +24,10 @@ use wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem};
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
use wire::IcmpRepr;
#[cfg(feature = "proto-ipv6")]
use wire::{Ipv6HopByHopHeader, Ipv6HopByHopRepr};
#[cfg(feature = "proto-ipv6")]
use wire::{Ipv6OptionRepr, Ipv6OptionFailureType};
#[cfg(feature = "proto-ipv6")]
use wire::{NdiscNeighborFlags, NdiscRepr};
#[cfg(all(feature = "proto-ipv6", feature = "socket-udp"))]
use wire::Icmpv6DstUnreachable;
@ -690,23 +695,39 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
}
}
let ip_repr = IpRepr::Ipv6(ipv6_repr);
let ip_payload = ipv6_packet.payload();
#[cfg(feature = "socket-raw")]
let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload);
let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload);
#[cfg(not(feature = "socket-raw"))]
let handled_by_raw_socket = false;
match ipv6_repr.next_header {
self.process_nxt_hdr(sockets, timestamp, ipv6_repr, ipv6_repr.next_header,
handled_by_raw_socket, ip_payload)
}
/// Given the next header value forward the payload onto the correct process
/// function.
#[cfg(feature = "proto-ipv6")]
fn process_nxt_hdr<'frame>
(&mut self, sockets: &mut SocketSet, timestamp: Instant, ipv6_repr: Ipv6Repr,
nxt_hdr: IpProtocol, handled_by_raw_socket: bool, ip_payload: &'frame [u8])
-> Result<Packet<'frame>>
{
match nxt_hdr {
IpProtocol::Icmpv6 =>
self.process_icmpv6(sockets, timestamp, ip_repr, ip_payload),
self.process_icmpv6(sockets, timestamp, ipv6_repr.into(), ip_payload),
#[cfg(feature = "socket-udp")]
IpProtocol::Udp =>
self.process_udp(sockets, ip_repr, ip_payload),
self.process_udp(sockets, ipv6_repr.into(), ip_payload),
#[cfg(feature = "socket-tcp")]
IpProtocol::Tcp =>
self.process_tcp(sockets, timestamp, ip_repr, ip_payload),
self.process_tcp(sockets, timestamp, ipv6_repr.into(), ip_payload),
IpProtocol::HopByHop =>
self.process_hopbyhop(sockets, timestamp, ipv6_repr, handled_by_raw_socket, ip_payload),
#[cfg(feature = "socket-raw")]
_ if handled_by_raw_socket =>
@ -906,6 +927,37 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
packet
}
#[cfg(feature = "proto-ipv6")]
fn process_hopbyhop<'frame>(&mut self, sockets: &mut SocketSet, timestamp: Instant,
ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool,
ip_payload: &'frame [u8]) -> Result<Packet<'frame>>
{
let hbh_pkt = Ipv6HopByHopHeader::new_checked(ip_payload)?;
let hbh_repr = Ipv6HopByHopRepr::parse(&hbh_pkt)?;
for result in hbh_repr.options() {
let opt_repr = result?;
match opt_repr {
Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (),
Ipv6OptionRepr::Unknown { type_, .. } => {
match Ipv6OptionFailureType::from(type_) {
Ipv6OptionFailureType::Skip => (),
Ipv6OptionFailureType::Discard => {
return Ok(Packet::None);
},
_ => {
// FIXME(dlrobertson): Send an ICMPv6 parameter problem message
// here.
return Err(Error::Unrecognized);
}
}
}
_ => return Err(Error::Unrecognized),
}
}
self.process_nxt_hdr(sockets, timestamp, ipv6_repr, hbh_repr.next_header,
handled_by_raw_socket, &ip_payload[hbh_repr.buffer_len()..])
}
#[cfg(feature = "proto-ipv4")]
fn process_icmpv4<'frame>(&self, _sockets: &mut SocketSet, ip_repr: IpRepr,
ip_payload: &'frame [u8]) -> Result<Packet<'frame>>
@ -1378,6 +1430,8 @@ mod test {
use wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem};
#[cfg(feature = "proto-ipv6")]
use wire::{NdiscNeighborFlags, NdiscRepr};
#[cfg(feature = "proto-ipv6")]
use wire::{Ipv6HopByHopHeader, Ipv6Option, Ipv6OptionRepr};
use super::Packet;
@ -1944,25 +1998,40 @@ mod test {
let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x01]);
let mut eth_bytes = vec![0; 58];
let mut eth_bytes = vec![0; 66];
let payload = [0x12, 0x34, 0x56, 0x78];
let ipv6_repr = Ipv6Repr {
src_addr: remote_ip_addr,
dst_addr: Ipv6Address::LOOPBACK,
next_header: IpProtocol::Unknown(0x0c),
payload_len: 4,
next_header: IpProtocol::HopByHop,
payload_len: 12,
hop_limit: 0x40,
};
let ip_repr = IpRepr::Ipv6(ipv6_repr);
let frame = {
let mut frame = EthernetFrame::new(&mut eth_bytes);
let ip_repr = IpRepr::Ipv6(ipv6_repr);
frame.set_dst_addr(EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]));
frame.set_src_addr(remote_hw_addr);
frame.set_ethertype(EthernetProtocol::Ipv6);
ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default());
frame.payload_mut()[ip_repr.buffer_len()..].copy_from_slice(&payload);
let mut offset = ipv6_repr.buffer_len();
{
let mut hbh_pkt = Ipv6HopByHopHeader::new(&mut frame.payload_mut()[offset..]);
hbh_pkt.set_next_header(IpProtocol::Unknown(0x0c));
hbh_pkt.set_header_len(0);
offset += 8;
{
let mut pad_pkt = Ipv6Option::new(&mut hbh_pkt.options_mut()[..]);
Ipv6OptionRepr::PadN(3).emit(&mut pad_pkt);
}
{
let mut pad_pkt = Ipv6Option::new(&mut hbh_pkt.options_mut()[5..]);
Ipv6OptionRepr::Pad1.emit(&mut pad_pkt);
}
}
frame.payload_mut()[offset..].copy_from_slice(&payload);
EthernetFrame::new(&*frame.into_inner())
};

View File

@ -147,7 +147,8 @@ pub use self::ipv6::{Address as Ipv6Address,
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6option::{Ipv6Option,
Repr as Ipv6OptionRepr,
Type as Ipv6OptionType};
Type as Ipv6OptionType,
FailureType as Ipv6OptionFailureType};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader,