diff --git a/examples/client.rs b/examples/client.rs index 3fe69bdb..8c7cd4ab 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -51,20 +51,24 @@ fn main() { let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs).routes(routes); + #[cfg(feature = "proto-ipv4-fragmentation")] + let mut ipv4_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-ipv4-fragmentation")] { let ipv4_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); - builder = builder.ipv4_fragments_cache(ipv4_frag_cache); + builder = builder + .ipv4_fragments_cache(ipv4_frag_cache) + .ipv4_out_packet_cache(&mut ipv4_out_packet_cache[..]); } #[cfg(feature = "proto-sixlowpan-fragmentation")] - let mut out_packet_buffer = [0u8; 1280]; + let mut sixlowpan_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-sixlowpan-fragmentation")] { let sixlowpan_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); builder = builder .sixlowpan_fragments_cache(sixlowpan_frag_cache) - .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); + .sixlowpan_out_packet_cache(&mut sixlowpan_out_packet_cache[..]); } if medium == Medium::Ethernet { diff --git a/examples/server.rs b/examples/server.rs index 07b18c40..7f003dc5 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -4,7 +4,6 @@ use log::debug; use std::collections::BTreeMap; use std::fmt::Write; use std::os::unix::io::AsRawFd; -use std::str; #[cfg(any( feature = "proto-sixlowpan-fragmentation", @@ -32,8 +31,14 @@ fn main() { let neighbor_cache = NeighborCache::new(BTreeMap::new()); - let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 65535]); - let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 65535]); + let udp_rx_buffer = udp::PacketBuffer::new( + vec![udp::PacketMetadata::EMPTY, udp::PacketMetadata::EMPTY], + vec![0; 65535], + ); + let udp_tx_buffer = udp::PacketBuffer::new( + vec![udp::PacketMetadata::EMPTY, udp::PacketMetadata::EMPTY], + vec![0; 65535], + ); let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 64]); @@ -62,20 +67,31 @@ fn main() { let medium = device.capabilities().medium; let mut builder = InterfaceBuilder::new().ip_addrs(ip_addrs); + builder = builder.random_seed( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(), + ); + + #[cfg(feature = "proto-ipv4-fragmentation")] + let mut ipv4_out_packet_cache = [0u8; 10_000]; #[cfg(feature = "proto-ipv4-fragmentation")] { let ipv4_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); - builder = builder.ipv4_fragments_cache(ipv4_frag_cache); + builder = builder + .ipv4_fragments_cache(ipv4_frag_cache) + .ipv4_out_packet_cache(&mut ipv4_out_packet_cache[..]); } #[cfg(feature = "proto-sixlowpan-fragmentation")] - let mut out_packet_buffer = [0u8; 1280]; + let mut sixlowpan_out_packet_cache = [0u8; 1280]; #[cfg(feature = "proto-sixlowpan-fragmentation")] { let sixlowpan_frag_cache = FragmentsCache::new(vec![], BTreeMap::new()); builder = builder .sixlowpan_fragments_cache(sixlowpan_frag_cache) - .sixlowpan_out_packet_cache(&mut out_packet_buffer[..]); + .sixlowpan_out_packet_cache(&mut sixlowpan_out_packet_cache[..]); } if medium == Medium::Ethernet { @@ -110,22 +126,16 @@ fn main() { let client = match socket.recv() { Ok((data, endpoint)) => { - debug!( - "udp:6969 recv data: {:?} from {}", - str::from_utf8(data).unwrap(), - endpoint - ); - Some(endpoint) + debug!("udp:6969 recv data: {:?} from {}", data, endpoint); + let mut data = data.to_vec(); + data.reverse(); + Some((endpoint, data)) } Err(_) => None, }; - if let Some(endpoint) = client { - let data = b"hello\n"; - debug!( - "udp:6969 send data: {:?}", - str::from_utf8(data.as_ref()).unwrap() - ); - socket.send_slice(data, endpoint).unwrap(); + if let Some((endpoint, data)) = client { + debug!("udp:6969 send data: {:?} to {}", data, endpoint,); + socket.send_slice(&data, endpoint).unwrap(); } // tcp:6969: respond "hello" @@ -160,10 +170,7 @@ fn main() { let recvd_len = buffer.len(); let mut data = buffer.to_owned(); if !data.is_empty() { - debug!( - "tcp:6970 recv data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") - ); + debug!("tcp:6970 recv data: {:?}", data); data = data.split(|&b| b == b'\n').collect::>().concat(); data.reverse(); data.extend(b"\n"); @@ -172,10 +179,7 @@ fn main() { }) .unwrap(); if socket.can_send() && !data.is_empty() { - debug!( - "tcp:6970 send data: {:?}", - str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)") - ); + debug!("tcp:6970 send data: {:?}", data); socket.send_slice(&data[..]).unwrap(); } } else if socket.may_send() { diff --git a/src/iface/interface.rs b/src/iface/interface.rs index ba5e2e6a..8e103d08 100644 --- a/src/iface/interface.rs +++ b/src/iface/interface.rs @@ -37,6 +37,8 @@ pub(crate) struct FragmentsBuffer<'a> { } pub(crate) struct OutPackets<'a> { + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_packet: Ipv4OutPacket<'a>, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_out_packet: SixlowpanOutPacket<'a>, @@ -45,10 +47,90 @@ pub(crate) struct OutPackets<'a> { } impl<'a> OutPackets<'a> { - #[cfg(feature = "proto-sixlowpan-fragmentation")] + #[cfg(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + ))] /// Returns `true` when all the data of the outgoing buffers are transmitted. fn all_transmitted(&self) -> bool { - self.sixlowpan_out_packet.finished() || self.sixlowpan_out_packet.is_empty() + #[cfg(feature = "proto-ipv4-fragmentation")] + if !self.ipv4_out_packet.is_empty() { + return false; + } + + #[cfg(feature = "proto-sixlowpan-fragmentation")] + if !self.sixlowpan_out_packet.is_empty() { + return false; + } + + true + } +} + +#[allow(unused)] +#[cfg(feature = "proto-ipv4")] +pub(crate) struct Ipv4OutPacket<'a> { + /// The buffer that holds the unfragmented 6LoWPAN packet. + buffer: ManagedSlice<'a, u8>, + /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers. + packet_len: usize, + /// The amount of bytes that already have been transmitted. + sent_bytes: usize, + + /// The IPv4 representation. + repr: Ipv4Repr, + /// The destination hardware address. + dst_hardware_addr: EthernetAddress, + /// The offset of the next fragment. + frag_offset: u16, + /// The identifier of the stream. + ident: u16, +} + +#[cfg(feature = "proto-ipv4-fragmentation")] +impl<'a> Ipv4OutPacket<'a> { + pub(crate) fn new(buffer: ManagedSlice<'a, u8>) -> Self { + Self { + buffer, + packet_len: 0, + sent_bytes: 0, + repr: Ipv4Repr { + src_addr: Ipv4Address::default(), + dst_addr: Ipv4Address::default(), + next_header: IpProtocol::Unknown(0), + payload_len: 0, + hop_limit: 0, + }, + dst_hardware_addr: EthernetAddress::default(), + frag_offset: 0, + ident: 0, + } + } + + /// Return `true` when everything is transmitted. + #[inline] + fn finished(&self) -> bool { + self.packet_len == self.sent_bytes + } + + /// Returns `true` when there is nothing to transmit. + #[inline] + fn is_empty(&self) -> bool { + self.packet_len == 0 + } + + // Reset the buffer. + fn reset(&mut self) { + self.packet_len = 0; + self.sent_bytes = 0; + self.repr = Ipv4Repr { + src_addr: Ipv4Address::default(), + dst_addr: Ipv4Address::default(), + next_header: IpProtocol::Unknown(0), + payload_len: 0, + hop_limit: 0, + }; + self.dst_hardware_addr = EthernetAddress::default(); } } @@ -163,6 +245,8 @@ pub struct InterfaceInner<'a> { sequence_no: u8, #[cfg(feature = "medium-ieee802154")] pan_id: Option, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_id: u16, #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: u16, ip_addrs: ManagedSlice<'a, IpCidr>, @@ -195,14 +279,16 @@ pub struct InterfaceBuilder<'a> { random_seed: u64, #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: Option>, + ipv4_fragments: PacketAssemblerSet<'a, Ipv4FragKey>, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_buffer: ManagedSlice<'a, u8>, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: Option>, + sixlowpan_fragments: PacketAssemblerSet<'a, SixlowpanFragKey>, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments_cache_timeout: Duration, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_buffer: Option>, + sixlowpan_out_buffer: ManagedSlice<'a, u8>, } impl<'a> InterfaceBuilder<'a> { @@ -238,7 +324,9 @@ let builder = InterfaceBuilder::new() .ip_addrs(ip_addrs); # #[cfg(feature = "proto-ipv4-fragmentation")] -let builder = builder.ipv4_fragments_cache(ipv4_frag_cache); +let builder = builder + .ipv4_fragments_cache(ipv4_frag_cache) + .ipv4_out_packet_cache(vec![]); let iface = builder.finalize(&mut device); ``` @@ -264,14 +352,16 @@ let iface = builder.finalize(&mut device); random_seed: 0, #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: None, + ipv4_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_buffer: ManagedSlice::Borrowed(&mut [][..]), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: None, + sixlowpan_fragments: PacketAssemblerSet::new(&mut [][..], &mut [][..]), #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments_cache_timeout: Duration::from_secs(60), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_buffer: None, + sixlowpan_out_buffer: ManagedSlice::Borrowed(&mut [][..]), } } @@ -385,7 +475,16 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-ipv4-fragmentation")] pub fn ipv4_fragments_cache(mut self, storage: PacketAssemblerSet<'a, Ipv4FragKey>) -> Self { - self.ipv4_fragments = Some(storage); + self.ipv4_fragments = storage; + self + } + + #[cfg(feature = "proto-ipv4-fragmentation")] + pub fn ipv4_out_packet_cache(mut self, storage: T) -> Self + where + T: Into>, + { + self.ipv4_out_buffer = storage.into(); self } @@ -394,7 +493,7 @@ let iface = builder.finalize(&mut device); mut self, storage: PacketAssemblerSet<'a, SixlowpanFragKey>, ) -> Self { - self.sixlowpan_fragments = Some(storage); + self.sixlowpan_fragments = storage; self } @@ -412,7 +511,7 @@ let iface = builder.finalize(&mut device); where T: Into>, { - self.sixlowpan_out_buffer = Some(storage.into()); + self.sixlowpan_out_buffer = storage.into(); self } @@ -471,10 +570,7 @@ let iface = builder.finalize(&mut device); ), }; - #[cfg(feature = "medium-ieee802154")] let mut rand = Rand::new(self.random_seed); - #[cfg(not(feature = "medium-ieee802154"))] - let rand = Rand::new(self.random_seed); #[cfg(feature = "medium-ieee802154")] let mut sequence_no; @@ -491,22 +587,29 @@ let iface = builder.finalize(&mut device); #[cfg(feature = "proto-sixlowpan")] loop { - tag = (rand.rand_u32() & 0xffff) as u16; + tag = rand.rand_u16(); if tag != 0 { break; } } + #[cfg(feature = "proto-ipv4")] + let mut ipv4_id; + + #[cfg(feature = "proto-ipv4")] + loop { + ipv4_id = rand.rand_u16(); + if ipv4_id != 0 { + break; + } + } + Interface { fragments: FragmentsBuffer { #[cfg(feature = "proto-ipv4-fragmentation")] - ipv4_fragments: self - .ipv4_fragments - .expect("Cache for incoming IPv4 fragments is required"), + ipv4_fragments: self.ipv4_fragments, #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_fragments: self - .sixlowpan_fragments - .expect("Cache for incoming 6LoWPAN fragments is required"), + sixlowpan_fragments: self.sixlowpan_fragments, #[cfg(feature = "proto-sixlowpan-fragmentation")] sixlowpan_fragments_cache_timeout: self.sixlowpan_fragments_cache_timeout, @@ -517,11 +620,10 @@ let iface = builder.finalize(&mut device); _lifetime: core::marker::PhantomData, }, out_packets: OutPackets { + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_out_packet: Ipv4OutPacket::new(self.ipv4_out_buffer), #[cfg(feature = "proto-sixlowpan-fragmentation")] - sixlowpan_out_packet: SixlowpanOutPacket::new( - self.sixlowpan_out_buffer - .expect("Cache for outgoing 6LoWPAN fragments is required"), - ), + sixlowpan_out_packet: SixlowpanOutPacket::new(self.sixlowpan_out_buffer), #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] _lifetime: core::marker::PhantomData, @@ -547,6 +649,8 @@ let iface = builder.finalize(&mut device); pan_id: self.pan_id, #[cfg(feature = "proto-sixlowpan-fragmentation")] tag, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_id, rand, }, } @@ -603,7 +707,7 @@ impl<'a> IpPacket<'a> { pub(crate) fn emit_payload( &self, - _ip_repr: IpRepr, + _ip_repr: &IpRepr, payload: &mut [u8], caps: &DeviceCapabilities, ) { @@ -645,7 +749,7 @@ impl<'a> IpPacket<'a> { // I'm really not happy about this "solution" but I don't know what else to do. if let Some(max_burst_size) = caps.max_burst_size { let mut max_segment_size = caps.max_transmission_unit; - max_segment_size -= _ip_repr.buffer_len(); + max_segment_size -= _ip_repr.header_len(); max_segment_size -= tcp_repr.header_len(); let max_window_size = max_burst_size * max_segment_size; @@ -928,6 +1032,13 @@ impl<'a> Interface<'a> { return Err(e); } + #[cfg(feature = "proto-ipv4-fragmentation")] + match self.ipv4_egress(device) { + Ok(true) => return Ok(true), + Err(e) => return Err(e), + _ => (), + } + #[cfg(feature = "proto-sixlowpan-fragmentation")] match self.sixlowpan_egress(device) { Ok(true) => return Ok(true), @@ -1021,7 +1132,7 @@ impl<'a> Interface<'a> { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { if let Some(packet) = inner.process_ethernet(sockets, &frame, _fragments) { - if let Err(err) = inner.dispatch(tx_token, packet) { + if let Err(err) = inner.dispatch(tx_token, packet, Some(_out_packets)) { net_debug!("Failed to send response: {}", err); } } @@ -1029,7 +1140,9 @@ impl<'a> Interface<'a> { #[cfg(feature = "medium-ip")] Medium::Ip => { if let Some(packet) = inner.process_ip(sockets, &frame, _fragments) { - if let Err(err) = inner.dispatch_ip(tx_token, packet, None) { + if let Err(err) = + inner.dispatch_ip(tx_token, packet, Some(_out_packets)) + { net_debug!("Failed to send response: {}", err); } } @@ -1083,13 +1196,19 @@ impl<'a> Interface<'a> { neighbor_addr = Some(response.ip_repr().dst_addr()); match device.transmit().ok_or(Error::Exhausted) { Ok(_t) => { - #[cfg(feature = "proto-sixlowpan-fragmentation")] + #[cfg(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + ))] if let Err(e) = inner.dispatch_ip(_t, response, Some(_out_packets)) { net_debug!("failed to dispatch IP: {}", e); return Err(e); } - #[cfg(not(feature = "proto-sixlowpan-fragmentation"))] + #[cfg(not(any( + feature = "proto-ipv4-fragmentation", + feature = "proto-sixlowpan-fragmentation" + )))] if let Err(e) = inner.dispatch_ip(_t, response, None) { net_debug!("failed to dispatch IP: {}", e); return Err(e); @@ -1230,11 +1349,60 @@ impl<'a> Interface<'a> { } } + #[cfg(feature = "proto-ipv4-fragmentation")] + fn ipv4_egress(&mut self, device: &mut D) -> Result + where + D: for<'d> Device<'d> + ?Sized, + { + // Reset the buffer when we transmitted everything. + if self.out_packets.ipv4_out_packet.finished() { + self.out_packets.ipv4_out_packet.reset(); + } + + if self.out_packets.ipv4_out_packet.is_empty() { + return Ok(false); + } + + let Ipv4OutPacket { + packet_len, + sent_bytes, + .. + } = &self.out_packets.ipv4_out_packet; + + if *packet_len > *sent_bytes { + match device.transmit().ok_or(Error::Exhausted) { + Ok(tx_token) => { + if let Err(e) = self + .inner + .dispatch_ipv4_out_packet(tx_token, &mut self.out_packets.ipv4_out_packet) + { + net_debug!("failed to transmit: {}", e); + } + } + Err(e) => { + net_debug!("failed to transmit: {}", e); + } + } + Ok(true) + } else { + Ok(false) + } + } + #[cfg(feature = "proto-sixlowpan-fragmentation")] fn sixlowpan_egress(&mut self, device: &mut D) -> Result where D: for<'d> Device<'d> + ?Sized, { + // Reset the buffer when we transmitted everything. + if self.out_packets.sixlowpan_out_packet.finished() { + self.out_packets.sixlowpan_out_packet.reset(); + } + + if self.out_packets.sixlowpan_out_packet.is_empty() { + return Ok(false); + } + let SixlowpanOutPacket { packet_len, sent_bytes, @@ -1254,11 +1422,6 @@ impl<'a> Interface<'a> { ) { net_debug!("failed to transmit: {}", e); } - - // Reset the buffer when we transmitted everything. - if self.out_packets.sixlowpan_out_packet.finished() { - self.out_packets.sixlowpan_out_packet.reset(); - } } Err(e) => { net_debug!("failed to transmit: {}", e); @@ -1388,6 +1551,9 @@ impl<'a> InterfaceInner<'a> { #[cfg(feature = "proto-sixlowpan-fragmentation")] tag: 1, + #[cfg(feature = "proto-ipv4-fragmentation")] + ipv4_id: 1, + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] hardware_addr: Some(crate::wire::HardwareAddress::Ethernet( crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]), @@ -1430,6 +1596,13 @@ impl<'a> InterfaceInner<'a> { no } + #[cfg(feature = "proto-ipv4-fragmentation")] + fn get_ipv4_ident(&mut self) -> u16 { + let ipv4_id = self.ipv4_id; + self.ipv4_id = self.ipv4_id.wrapping_add(1); + ipv4_id + } + #[cfg(feature = "proto-sixlowpan-fragmentation")] fn get_sixlowpan_fragment_tag(&mut self) -> u16 { let tag = self.tag; @@ -2031,11 +2204,21 @@ impl<'a> InterfaceInner<'a> { let f = match fragments.get_packet_assembler_mut(&key) { Ok(f) => f, Err(_) => { - check!(check!(fragments.reserve_with_key(&key)).start( + let p = match fragments.reserve_with_key(&key) { + Ok(p) => p, + Err(Error::PacketAssemblerSetFull) => { + net_debug!("No available packet assembler for fragmented packet"); + return Default::default(); + } + e => check!(e), + }; + + check!(p.start( None, self.now + Duration::from_secs(REASSEMBLY_TIMEOUT), - 0, + 0 )); + check!(fragments.get_packet_assembler_mut(&key)) } }; @@ -2661,7 +2844,12 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "medium-ethernet")] - fn dispatch(&mut self, tx_token: Tx, packet: EthernetPacket) -> Result<()> + fn dispatch( + &mut self, + tx_token: Tx, + packet: EthernetPacket, + _out_packet: Option<&mut OutPackets<'_>>, + ) -> Result<()> where Tx: TxToken, { @@ -2683,7 +2871,7 @@ impl<'a> InterfaceInner<'a> { arp_repr.emit(&mut packet); }) } - EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, None), + EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, _out_packet), } } @@ -2899,65 +3087,196 @@ impl<'a> InterfaceInner<'a> { packet: IpPacket, _out_packet: Option<&mut OutPackets<'_>>, ) -> Result<()> { - let ip_repr = packet.ip_repr(); + let mut ip_repr = packet.ip_repr(); assert!(!ip_repr.dst_addr().is_unspecified()); - match self.caps.medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => { - let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( - tx_token, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - )? { - (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), - #[cfg(feature = "medium-ieee802154")] - (HardwareAddress::Ieee802154(_), _) => unreachable!(), - }; + // Dispatch IEEE802.15.4: - let caps = self.caps.clone(); - self.dispatch_ethernet(tx_token, ip_repr.total_len(), |mut frame| { - frame.set_dst_addr(dst_hardware_addr); - match ip_repr { - #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4), - #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(_) => frame.set_ethertype(EthernetProtocol::Ipv6), + #[cfg(feature = "medium-ieee802154")] + if matches!(self.caps.medium, Medium::Ieee802154) { + let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( + tx_token, + &ip_repr.src_addr(), + &ip_repr.dst_addr(), + )? { + (HardwareAddress::Ieee802154(addr), tx_token) => (addr, tx_token), + _ => unreachable!(), + }; + + return self.dispatch_ieee802154( + dst_hardware_addr, + &ip_repr, + tx_token, + packet, + _out_packet, + ); + } + + // Dispatch IP/Ethernet: + + let caps = self.caps.clone(); + + #[cfg(feature = "proto-ipv4-fragmentation")] + let ipv4_id = self.get_ipv4_ident(); + + // First we calculate the total length that we will have to emit. + let mut total_len = ip_repr.buffer_len(); + + // Add the size of the Ethernet header if the medium is Ethernet. + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + total_len = EthernetFrame::<&[u8]>::buffer_len(total_len); + } + + // If the medium is Ethernet, then we need to retrieve the destination hardware address. + #[cfg(feature = "medium-ethernet")] + let (dst_hardware_addr, tx_token) = + match self.lookup_hardware_addr(tx_token, &ip_repr.src_addr(), &ip_repr.dst_addr())? { + (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token), + #[cfg(feature = "medium-ieee802154")] + (HardwareAddress::Ieee802154(_), _) => unreachable!(), + }; + + // Emit function for the Ethernet header. + #[cfg(feature = "medium-ethernet")] + let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { + let mut frame = EthernetFrame::new_unchecked(tx_buffer); + + let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; + + frame.set_src_addr(src_addr); + frame.set_dst_addr(dst_hardware_addr); + + match repr.version() { + #[cfg(feature = "proto-ipv4")] + IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4), + #[cfg(feature = "proto-ipv6")] + IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6), + } + + Ok(()) + }; + + // Emit function for the IP header and payload. + let emit_ip = |repr: &IpRepr, mut tx_buffer: &mut [u8]| { + repr.emit(&mut tx_buffer, &self.caps.checksum); + + let payload = &mut tx_buffer[repr.header_len()..]; + packet.emit_payload(repr, payload, &caps); + }; + + let total_ip_len = ip_repr.buffer_len(); + + match ip_repr { + #[cfg(feature = "proto-ipv4")] + IpRepr::Ipv4(ref mut repr) => { + // If we have an IPv4 packet, then we need to check if we need to fragment it. + if total_ip_len > self.caps.max_transmission_unit { + cfg_if::cfg_if! { + if #[cfg(feature = "proto-ipv4-fragmentation")] { + net_debug!("start fragmentation"); + + let Ipv4OutPacket { + buffer, + packet_len, + sent_bytes, + repr: out_packet_repr, + frag_offset, + ident, + dst_hardware_addr: dst_address, + } = &mut _out_packet.unwrap().ipv4_out_packet; + + // Calculate how much we will send now (including the Ethernet header). + let tx_len = self.caps.max_transmission_unit; + + let ip_header_len = repr.buffer_len(); + let first_frag_ip_len = self.caps.ip_mtu(); + + if buffer.len() < first_frag_ip_len { + net_debug!("Fragmentation buffer is too small"); + return Err(Error::Exhausted); + } + + *dst_address = dst_hardware_addr; + + // Save the total packet len (without the Ethernet header, but with the first + // IP header). + *packet_len = total_ip_len; + + // Save the IP header for other fragments. + *out_packet_repr = *repr; + + // Save how much bytes we will send now. + *sent_bytes = first_frag_ip_len; + + // Modify the IP header + repr.payload_len = first_frag_ip_len - repr.buffer_len(); + + // Emit the IP header to the buffer. + emit_ip(&ip_repr, buffer); + let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut buffer[..]); + *ident = ipv4_id; + ipv4_packet.set_ident(ipv4_id); + ipv4_packet.set_more_frags(true); + ipv4_packet.set_dont_frag(false); + ipv4_packet.set_frag_offset(0); + + if caps.checksum.ipv4.tx() { + ipv4_packet.fill_checksum(); + } + + // Transmit the first packet. + tx_token.consume(self.now, tx_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&ip_repr, tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } + + // Change the offset for the next packet. + *frag_offset = (first_frag_ip_len - ip_header_len) as u16; + + // Copy the IP header and the payload. + tx_buffer[..first_frag_ip_len] + .copy_from_slice(&buffer[..first_frag_ip_len]); + + Ok(()) + }) + } else { + net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support."); + Ok(()) + } } + } else { + // No fragmentation is required. + tx_token.consume(self.now, total_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&ip_repr, tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } - ip_repr.emit(frame.payload_mut(), &caps.checksum); - - let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..]; - packet.emit_payload(ip_repr, payload, &caps); - }) + emit_ip(&ip_repr, tx_buffer); + Ok(()) + }) + } } - #[cfg(feature = "medium-ip")] - Medium::Ip => { - let tx_len = ip_repr.total_len(); - tx_token.consume(self.now, tx_len, |mut tx_buffer| { - debug_assert!(tx_buffer.as_ref().len() == tx_len); + // We don't support IPv6 fragmentation yet. + #[cfg(feature = "proto-ipv6")] + IpRepr::Ipv6(_) => tx_token.consume(self.now, total_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&ip_repr, tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } - ip_repr.emit(&mut tx_buffer, &self.caps.checksum); - - let payload = &mut tx_buffer[ip_repr.buffer_len()..]; - packet.emit_payload(ip_repr, payload, &self.caps); - - Ok(()) - }) - } - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => { - let (dst_hardware_addr, tx_token) = match self.lookup_hardware_addr( - tx_token, - &ip_repr.src_addr(), - &ip_repr.dst_addr(), - )? { - (HardwareAddress::Ieee802154(addr), tx_token) => (addr, tx_token), - _ => unreachable!(), - }; - - self.dispatch_ieee802154(dst_hardware_addr, &ip_repr, tx_token, packet, _out_packet) - } + emit_ip(&ip_repr, tx_buffer); + Ok(()) + }), } } @@ -3076,6 +3395,11 @@ impl<'a> InterfaceInner<'a> { .. } = &mut _out_packet.unwrap().sixlowpan_out_packet; + if buffer.len() < total_size { + net_debug!("6LoWPAN: Fragmentation buffer is too small"); + return Err(Error::Exhausted); + } + *ll_dst_addr = ll_dst_a; *ll_src_addr = ll_src_a; @@ -3316,6 +3640,91 @@ impl<'a> InterfaceInner<'a> { ) } + #[cfg(feature = "proto-ipv4-fragmentation")] + fn dispatch_ipv4_out_packet( + &mut self, + tx_token: Tx, + out_packet: &mut Ipv4OutPacket, + ) -> Result<()> { + let Ipv4OutPacket { + buffer, + packet_len, + sent_bytes, + repr, + dst_hardware_addr, + frag_offset, + ident, + .. + } = out_packet; + + let caps = self.caps.clone(); + + let mtu_max = self.ip_mtu(); + let ip_len = (*packet_len - *sent_bytes + repr.buffer_len()).min(mtu_max); + let payload_len = ip_len - repr.buffer_len(); + + let more_frags = (*packet_len - *sent_bytes) != payload_len; + repr.payload_len = payload_len; + *sent_bytes += payload_len; + + let mut tx_len = ip_len; + #[cfg(feature = "medium-ethernet")] + if matches!(caps.medium, Medium::Ethernet) { + tx_len += EthernetFrame::<&[u8]>::header_len(); + } + + // Emit function for the Ethernet header. + let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| { + let mut frame = EthernetFrame::new_unchecked(tx_buffer); + + let src_addr = if let Some(HardwareAddress::Ethernet(addr)) = self.hardware_addr { + addr + } else { + return Err(Error::Malformed); + }; + + frame.set_src_addr(src_addr); + frame.set_dst_addr(*dst_hardware_addr); + + match repr.version() { + #[cfg(feature = "proto-ipv4")] + IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4), + #[cfg(feature = "proto-ipv6")] + IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6), + } + + Ok(()) + }; + + tx_token.consume(self.now, tx_len, |mut tx_buffer| { + #[cfg(feature = "medium-ethernet")] + if matches!(self.caps.medium, Medium::Ethernet) { + emit_ethernet(&IpRepr::Ipv4(*repr), tx_buffer)?; + tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..]; + } + + let mut packet = Ipv4Packet::new_unchecked(&mut tx_buffer[..repr.buffer_len()]); + repr.emit(&mut packet, &caps.checksum); + packet.set_ident(*ident); + packet.set_more_frags(more_frags); + packet.set_dont_frag(false); + packet.set_frag_offset(*frag_offset); + + if caps.checksum.ipv4.tx() { + packet.fill_checksum(); + } + + tx_buffer[repr.buffer_len()..][..payload_len].copy_from_slice( + &buffer[*frag_offset as usize + repr.buffer_len() as usize..][..payload_len], + ); + + // Update the frag offset for the next fragment. + *frag_offset += payload_len as u16; + + Ok(()) + }) + } + #[cfg(feature = "proto-igmp")] fn igmp_report_packet<'any>( &self, @@ -3335,7 +3744,6 @@ impl<'a> InterfaceInner<'a> { next_header: IpProtocol::Igmp, payload_len: igmp_repr.buffer_len(), hop_limit: 1, - // TODO: add Router Alert IPv4 header option. See // [#183](https://github.com/m-labs/smoltcp/issues/183). }, igmp_repr, @@ -3408,8 +3816,9 @@ mod test { let iface_builder = InterfaceBuilder::new().ip_addrs(ip_addrs); #[cfg(feature = "proto-ipv4-fragmentation")] - let iface_builder = - iface_builder.ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())); + let iface_builder = iface_builder + .ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .ipv4_out_packet_cache(vec![]); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); @@ -3442,8 +3851,9 @@ mod test { .sixlowpan_out_packet_cache(vec![]); #[cfg(feature = "proto-ipv4-fragmentation")] - let iface_builder = - iface_builder.ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())); + let iface_builder = iface_builder + .ipv4_fragments_cache(PacketAssemblerSet::new(vec![], BTreeMap::new())) + .ipv4_out_packet_cache(vec![]); #[cfg(feature = "proto-igmp")] let iface_builder = iface_builder.ipv4_multicast_groups(BTreeMap::new()); @@ -4124,7 +4534,7 @@ mod test { solicit.emit( &remote_ip_addr.into(), &local_ip_addr.solicited_node().into(), - &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.buffer_len()..]), + &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.header_len()..]), &ChecksumCapabilities::default(), ); diff --git a/src/socket/raw.rs b/src/socket/raw.rs index 08c3ddfd..f07a9596 100644 --- a/src/socket/raw.rs +++ b/src/socket/raw.rs @@ -261,7 +261,7 @@ impl<'a> Socket<'a> { pub(crate) fn process(&mut self, cx: &mut Context, ip_repr: &IpRepr, payload: &[u8]) { debug_assert!(self.accepts(ip_repr)); - let header_len = ip_repr.buffer_len(); + let header_len = ip_repr.header_len(); let total_len = header_len + payload.len(); net_trace!( diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index a3d81377..6b05bce4 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -2099,7 +2099,7 @@ impl<'a> Socket<'a> { // 3. MSS we can send, determined by our MTU. let size = win_limit .min(self.remote_mss) - .min(cx.ip_mtu() - ip_repr.buffer_len() - TCP_HEADER_LEN); + .min(cx.ip_mtu() - ip_repr.header_len() - TCP_HEADER_LEN); let offset = self.remote_last_seq - self.local_seq_no; repr.payload = self.tx_buffer.get_allocated(offset, size); @@ -2161,7 +2161,7 @@ impl<'a> Socket<'a> { if repr.control == TcpControl::Syn { // Fill the MSS option. See RFC 6691 for an explanation of this calculation. - let max_segment_size = cx.ip_mtu() - ip_repr.buffer_len() - TCP_HEADER_LEN; + let max_segment_size = cx.ip_mtu() - ip_repr.header_len() - TCP_HEADER_LEN; repr.max_seg_size = Some(max_segment_size as u16); } diff --git a/src/wire/icmpv4.rs b/src/wire/icmpv4.rs index f1546294..7d3cf5a4 100644 --- a/src/wire/icmpv4.rs +++ b/src/wire/icmpv4.rs @@ -665,7 +665,7 @@ mod test { packet.set_echo_seq_no(0xabcd); packet.data_mut().copy_from_slice(&ECHO_DATA_BYTES[..]); packet.fill_checksum(); - assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]); + assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); } fn echo_packet_repr() -> Repr<'static> { @@ -689,7 +689,7 @@ mod test { let mut bytes = vec![0xa5; repr.buffer_len()]; let mut packet = Packet::new_unchecked(&mut bytes); repr.emit(&mut packet, &ChecksumCapabilities::default()); - assert_eq!(&*packet.into_inner(), &ECHO_PACKET_BYTES[..]); + assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); } #[test] diff --git a/src/wire/ip.rs b/src/wire/ip.rs index b4d6a2d1..519203a4 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -666,7 +666,7 @@ impl Repr { } /// Return the length of a header that will be emitted from this high-level representation. - pub fn buffer_len(&self) -> usize { + pub fn header_len(&self) -> usize { match *self { #[cfg(feature = "proto-ipv4")] Repr::Ipv4(repr) => repr.buffer_len(), @@ -693,8 +693,8 @@ impl Repr { /// high-level representation. /// /// This is the same as `repr.buffer_len() + repr.payload_len()`. - pub fn total_len(&self) -> usize { - self.buffer_len() + self.payload_len() + pub fn buffer_len(&self) -> usize { + self.header_len() + self.payload_len() } } diff --git a/src/wire/ipv4.rs b/src/wire/ipv4.rs index 0b0d0e22..2ca3e1aa 100644 --- a/src/wire/ipv4.rs +++ b/src/wire/ipv4.rs @@ -26,6 +26,7 @@ pub struct Key { id: u16, src_addr: Address, dst_addr: Address, + protocol: Protocol, } /// A four-octet IPv4 address. @@ -457,6 +458,7 @@ impl> Packet { id: self.ident(), src_addr: self.src_addr(), dst_addr: self.dst_addr(), + protocol: self.next_header(), } } } diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 65b5bb93..72c14557 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -52,7 +52,7 @@ let repr = Ipv4Repr { dst_addr: Ipv4Address::new(10, 0, 0, 2), next_header: IpProtocol::Tcp, payload_len: 10, - hop_limit: 64 + hop_limit: 64, }; let mut buffer = vec![0; repr.buffer_len() + repr.payload_len]; { // emission