use super::*; #[rstest] #[case(Medium::Ip)] #[cfg(feature = "medium-ip")] #[case(Medium::Ethernet)] #[cfg(feature = "medium-ethernet")] fn test_no_icmp_no_unicast(#[case] medium: Medium) { let (mut iface, mut sockets, _) = setup(medium); // Unknown Ipv4 Protocol // // Because the destination is the broadcast address // this should not trigger and Destination Unreachable // response. See RFC 1122 ยง 3.2.2. let repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), dst_addr: Ipv4Address::BROADCAST, next_header: IpProtocol::Unknown(0x0c), payload_len: 0, hop_limit: 0x40, }); let mut bytes = vec![0u8; 54]; repr.emit(&mut bytes, &ChecksumCapabilities::default()); let frame = Ipv4Packet::new_unchecked(&bytes[..]); // Ensure that the unknown protocol frame does not trigger an // ICMP error response when the destination address is a // broadcast address assert_eq!( iface.inner.process_ipv4( &mut sockets, PacketMeta::default(), &frame, &mut iface.fragments ), None ); } #[rstest] #[case(Medium::Ip)] #[cfg(feature = "medium-ip")] #[case(Medium::Ethernet)] #[cfg(feature = "medium-ethernet")] fn test_icmp_error_no_payload(#[case] medium: Medium) { static NO_BYTES: [u8; 0] = []; let (mut iface, mut sockets, _device) = setup(medium); // Unknown Ipv4 Protocol with no payload let repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), next_header: IpProtocol::Unknown(0x0c), payload_len: 0, hop_limit: 0x40, }); let mut bytes = vec![0u8; 34]; repr.emit(&mut bytes, &ChecksumCapabilities::default()); let frame = Ipv4Packet::new_unchecked(&bytes[..]); // The expected Destination Unreachable response due to the // unknown protocol let icmp_repr = Icmpv4Repr::DstUnreachable { reason: Icmpv4DstUnreachable::ProtoUnreachable, header: Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), next_header: IpProtocol::Unknown(12), payload_len: 0, hop_limit: 64, }, data: &NO_BYTES, }; let expected_repr = Packet::new_ipv4( Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), next_header: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), hop_limit: 64, }, IpPayload::Icmpv4(icmp_repr), ); // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. assert_eq!( iface.inner.process_ipv4( &mut sockets, PacketMeta::default(), &frame, &mut iface.fragments ), Some(expected_repr) ); } #[rstest] #[case(Medium::Ip)] #[cfg(feature = "medium-ip")] #[case(Medium::Ethernet)] #[cfg(feature = "medium-ethernet")] fn test_local_subnet_broadcasts(#[case] medium: Medium) { let (mut iface, _, _device) = setup(medium); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); }); }); assert!(iface .inner .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); assert!(!iface .inner .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); assert!(iface.inner.is_broadcast_v4(Ipv4Address([192, 168, 1, 255]))); assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 168, 1, 254]))); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); }); }); assert!(iface .inner .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); assert!(!iface .inner .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); assert!(!iface .inner .is_broadcast_v4(Ipv4Address([192, 168, 23, 255]))); assert!(!iface .inner .is_broadcast_v4(Ipv4Address([192, 168, 23, 254]))); assert!(!iface .inner .is_broadcast_v4(Ipv4Address([192, 168, 255, 254]))); assert!(iface .inner .is_broadcast_v4(Ipv4Address([192, 168, 255, 255]))); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); }); }); assert!(iface .inner .is_broadcast_v4(Ipv4Address([255, 255, 255, 255]))); assert!(!iface .inner .is_broadcast_v4(Ipv4Address([255, 255, 255, 254]))); assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 23, 1, 255]))); assert!(!iface.inner.is_broadcast_v4(Ipv4Address([192, 23, 1, 254]))); assert!(!iface .inner .is_broadcast_v4(Ipv4Address([192, 255, 255, 254]))); assert!(iface .inner .is_broadcast_v4(Ipv4Address([192, 255, 255, 255]))); } #[rstest] #[case(Medium::Ip)] #[cfg(all(feature = "medium-ip", feature = "socket-udp"))] #[case(Medium::Ethernet)] #[cfg(all(feature = "medium-ethernet", feature = "socket-udp"))] fn test_icmp_error_port_unreachable(#[case] medium: Medium) { static UDP_PAYLOAD: [u8; 12] = [ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x6c, 0x64, 0x21, ]; let (mut iface, mut sockets, _device) = setup(medium); let mut udp_bytes_unicast = vec![0u8; 20]; let mut udp_bytes_broadcast = vec![0u8; 20]; let mut packet_unicast = UdpPacket::new_unchecked(&mut udp_bytes_unicast); let mut packet_broadcast = UdpPacket::new_unchecked(&mut udp_bytes_broadcast); let udp_repr = UdpRepr { src_port: 67, dst_port: 68, }; let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), next_header: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64, }); // Emit the representations to a packet udp_repr.emit( &mut packet_unicast, &ip_repr.src_addr(), &ip_repr.dst_addr(), UDP_PAYLOAD.len(), |buf| buf.copy_from_slice(&UDP_PAYLOAD), &ChecksumCapabilities::default(), ); let data = packet_unicast.into_inner(); // The expected Destination Unreachable ICMPv4 error response due // to no sockets listening on the destination port. let icmp_repr = Icmpv4Repr::DstUnreachable { reason: Icmpv4DstUnreachable::PortUnreachable, header: Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), next_header: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64, }, data, }; let expected_repr = Packet::new_ipv4( Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), next_header: IpProtocol::Icmp, payload_len: icmp_repr.buffer_len(), hop_limit: 64, }, IpPayload::Icmpv4(icmp_repr), ); // Ensure that the unknown protocol triggers an error response. // And we correctly handle no payload. assert_eq!( iface.inner.process_udp( &mut sockets, PacketMeta::default(), ip_repr, udp_repr, false, &UDP_PAYLOAD, data ), Some(expected_repr) ); let ip_repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), dst_addr: Ipv4Address::BROADCAST, next_header: IpProtocol::Udp, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), hop_limit: 64, }); // Emit the representations to a packet udp_repr.emit( &mut packet_broadcast, &ip_repr.src_addr(), &IpAddress::Ipv4(Ipv4Address::BROADCAST), UDP_PAYLOAD.len(), |buf| buf.copy_from_slice(&UDP_PAYLOAD), &ChecksumCapabilities::default(), ); // Ensure that the port unreachable error does not trigger an // ICMP error response when the destination address is a // broadcast address and no socket is bound to the port. assert_eq!( iface.inner.process_udp( &mut sockets, PacketMeta::default(), ip_repr, udp_repr, false, &UDP_PAYLOAD, packet_broadcast.into_inner(), ), None ); } #[rstest] #[case(Medium::Ip)] #[cfg(feature = "medium-ip")] #[case(Medium::Ethernet)] #[cfg(feature = "medium-ethernet")] fn test_handle_ipv4_broadcast(#[case] medium: Medium) { use crate::wire::{Icmpv4Packet, Icmpv4Repr}; let (mut iface, mut sockets, _device) = setup(medium); let our_ipv4_addr = iface.ipv4_addr().unwrap(); let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]); // ICMPv4 echo request let icmpv4_data: [u8; 4] = [0xaa, 0x00, 0x00, 0xff]; let icmpv4_repr = Icmpv4Repr::EchoRequest { ident: 0x1234, seq_no: 0xabcd, data: &icmpv4_data, }; // Send to IPv4 broadcast address let ipv4_repr = Ipv4Repr { src_addr: src_ipv4_addr, dst_addr: Ipv4Address::BROADCAST, next_header: IpProtocol::Icmp, hop_limit: 64, payload_len: icmpv4_repr.buffer_len(), }; // Emit to ip frame let mut bytes = vec![0u8; ipv4_repr.buffer_len() + icmpv4_repr.buffer_len()]; let frame = { ipv4_repr.emit( &mut Ipv4Packet::new_unchecked(&mut bytes[..]), &ChecksumCapabilities::default(), ); icmpv4_repr.emit( &mut Icmpv4Packet::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), &ChecksumCapabilities::default(), ); Ipv4Packet::new_unchecked(&bytes[..]) }; // Expected ICMPv4 echo reply let expected_icmpv4_repr = Icmpv4Repr::EchoReply { ident: 0x1234, seq_no: 0xabcd, data: &icmpv4_data, }; let expected_ipv4_repr = Ipv4Repr { src_addr: our_ipv4_addr, dst_addr: src_ipv4_addr, next_header: IpProtocol::Icmp, hop_limit: 64, payload_len: expected_icmpv4_repr.buffer_len(), }; let expected_packet = Packet::new_ipv4(expected_ipv4_repr, IpPayload::Icmpv4(expected_icmpv4_repr)); assert_eq!( iface.inner.process_ipv4( &mut sockets, PacketMeta::default(), &frame, &mut iface.fragments ), Some(expected_packet) ); } #[rstest] #[case(Medium::Ethernet)] #[cfg(feature = "medium-ethernet")] fn test_handle_valid_arp_request(#[case] medium: Medium) { let (mut iface, mut sockets, _device) = setup(medium); let mut eth_bytes = vec![0u8; 42]; let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); let local_hw_addr = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]); let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); let repr = ArpRepr::EthernetIpv4 { operation: ArpOperation::Request, source_hardware_addr: remote_hw_addr, source_protocol_addr: remote_ip_addr, target_hardware_addr: EthernetAddress::default(), target_protocol_addr: local_ip_addr, }; let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); frame.set_dst_addr(EthernetAddress::BROADCAST); frame.set_src_addr(remote_hw_addr); frame.set_ethertype(EthernetProtocol::Arp); let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); repr.emit(&mut packet); // Ensure an ARP Request for us triggers an ARP Reply assert_eq!( iface.inner.process_ethernet( &mut sockets, PacketMeta::default(), frame.into_inner(), &mut iface.fragments ), Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, source_protocol_addr: local_ip_addr, target_hardware_addr: remote_hw_addr, target_protocol_addr: remote_ip_addr })) ); // Ensure the address of the requester was entered in the cache assert_eq!( iface.inner.lookup_hardware_addr( MockTxToken, &IpAddress::Ipv4(local_ip_addr), &IpAddress::Ipv4(remote_ip_addr), &mut iface.fragmenter, ), Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) ); } #[rstest] #[case(Medium::Ethernet)] #[cfg(feature = "medium-ethernet")] fn test_handle_other_arp_request(#[case] medium: Medium) { let (mut iface, mut sockets, _device) = setup(medium); let mut eth_bytes = vec![0u8; 42]; let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); let repr = ArpRepr::EthernetIpv4 { operation: ArpOperation::Request, source_hardware_addr: remote_hw_addr, source_protocol_addr: remote_ip_addr, target_hardware_addr: EthernetAddress::default(), target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x03]), }; let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); frame.set_dst_addr(EthernetAddress::BROADCAST); frame.set_src_addr(remote_hw_addr); frame.set_ethertype(EthernetProtocol::Arp); let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); repr.emit(&mut packet); // Ensure an ARP Request for someone else does not trigger an ARP Reply assert_eq!( iface.inner.process_ethernet( &mut sockets, PacketMeta::default(), frame.into_inner(), &mut iface.fragments ), None ); // Ensure the address of the requester was NOT entered in the cache assert_eq!( iface.inner.lookup_hardware_addr( MockTxToken, &IpAddress::Ipv4(Ipv4Address([0x7f, 0x00, 0x00, 0x01])), &IpAddress::Ipv4(remote_ip_addr), &mut iface.fragmenter, ), Err(DispatchError::NeighborPending) ); } #[rstest] #[case(Medium::Ethernet)] #[cfg(feature = "medium-ethernet")] fn test_arp_flush_after_update_ip(#[case] medium: Medium) { let (mut iface, mut sockets, _device) = setup(medium); let mut eth_bytes = vec![0u8; 42]; let local_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); let remote_ip_addr = Ipv4Address([0x7f, 0x00, 0x00, 0x02]); let local_hw_addr = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]); let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]); let repr = ArpRepr::EthernetIpv4 { operation: ArpOperation::Request, source_hardware_addr: remote_hw_addr, source_protocol_addr: remote_ip_addr, target_hardware_addr: EthernetAddress::default(), target_protocol_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), }; let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); frame.set_dst_addr(EthernetAddress::BROADCAST); frame.set_src_addr(remote_hw_addr); frame.set_ethertype(EthernetProtocol::Arp); { let mut packet = ArpPacket::new_unchecked(frame.payload_mut()); repr.emit(&mut packet); } // Ensure an ARP Request for us triggers an ARP Reply assert_eq!( iface.inner.process_ethernet( &mut sockets, PacketMeta::default(), frame.into_inner(), &mut iface.fragments ), Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, source_hardware_addr: local_hw_addr, source_protocol_addr: local_ip_addr, target_hardware_addr: remote_hw_addr, target_protocol_addr: remote_ip_addr })) ); // Ensure the address of the requester was entered in the cache assert_eq!( iface.inner.lookup_hardware_addr( MockTxToken, &IpAddress::Ipv4(local_ip_addr), &IpAddress::Ipv4(remote_ip_addr), &mut iface.fragmenter, ), Ok((HardwareAddress::Ethernet(remote_hw_addr), MockTxToken)) ); // Update IP addrs to trigger ARP cache flush let local_ip_addr_new = Ipv4Address([0x7f, 0x00, 0x00, 0x01]); iface.update_ip_addrs(|addrs| { addrs.iter_mut().next().map(|addr| { *addr = IpCidr::Ipv4(Ipv4Cidr::new(local_ip_addr_new, 24)); }); }); // ARP cache flush after address change assert!(!iface.inner.has_neighbor(&IpAddress::Ipv4(remote_ip_addr))); } #[rstest] #[case(Medium::Ip)] #[cfg(all(feature = "socket-icmp", feature = "medium-ip"))] #[case(Medium::Ethernet)] #[cfg(all(feature = "socket-icmp", feature = "medium-ethernet"))] fn test_icmpv4_socket(#[case] medium: Medium) { use crate::wire::Icmpv4Packet; let (mut iface, mut sockets, _device) = setup(medium); let rx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); let tx_buffer = icmp::PacketBuffer::new(vec![icmp::PacketMetadata::EMPTY], vec![0; 24]); let icmpv4_socket = icmp::Socket::new(rx_buffer, tx_buffer); let socket_handle = sockets.add(icmpv4_socket); let ident = 0x1234; let seq_no = 0x5432; let echo_data = &[0xff; 16]; let socket = sockets.get_mut::(socket_handle); // Bind to the ID 0x1234 assert_eq!(socket.bind(icmp::Endpoint::Ident(ident)), Ok(())); // Ensure the ident we bound to and the ident of the packet are the same. let mut bytes = [0xff; 24]; let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]); let echo_repr = Icmpv4Repr::EchoRequest { ident, seq_no, data: echo_data, }; echo_repr.emit(&mut packet, &ChecksumCapabilities::default()); let icmp_data = &*packet.into_inner(); let ipv4_repr = Ipv4Repr { src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02), dst_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x01), next_header: IpProtocol::Icmp, payload_len: 24, hop_limit: 64, }; let ip_repr = IpRepr::Ipv4(ipv4_repr); // Open a socket and ensure the packet is handled due to the listening // socket. assert!(!sockets.get_mut::(socket_handle).can_recv()); // Confirm we still get EchoReply from `smoltcp` even with the ICMP socket listening let echo_reply = Icmpv4Repr::EchoReply { ident, seq_no, data: echo_data, }; let ipv4_reply = Ipv4Repr { src_addr: ipv4_repr.dst_addr, dst_addr: ipv4_repr.src_addr, ..ipv4_repr }; assert_eq!( iface.inner.process_icmpv4(&mut sockets, ip_repr, icmp_data), Some(Packet::new_ipv4(ipv4_reply, IpPayload::Icmpv4(echo_reply))) ); let socket = sockets.get_mut::(socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), Ok(( icmp_data, IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02)) )) ); } #[rstest] #[case(Medium::Ip)] #[cfg(all(feature = "proto-igmp", feature = "medium-ip"))] #[case(Medium::Ethernet)] #[cfg(all(feature = "proto-igmp", feature = "medium-ethernet"))] fn test_handle_igmp(#[case] medium: Medium) { fn recv_igmp( device: &mut crate::tests::TestingDevice, timestamp: Instant, ) -> Vec<(Ipv4Repr, IgmpRepr)> { let caps = device.capabilities(); let checksum_caps = &caps.checksum; recv_all(device, timestamp) .iter() .filter_map(|frame| { let ipv4_packet = match caps.medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => { let eth_frame = EthernetFrame::new_checked(frame).ok()?; Ipv4Packet::new_checked(eth_frame.payload()).ok()? } #[cfg(feature = "medium-ip")] Medium::Ip => Ipv4Packet::new_checked(&frame[..]).ok()?, #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => todo!(), }; let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, checksum_caps).ok()?; let ip_payload = ipv4_packet.payload(); let igmp_packet = IgmpPacket::new_checked(ip_payload).ok()?; let igmp_repr = IgmpRepr::parse(&igmp_packet).ok()?; Some((ipv4_repr, igmp_repr)) }) .collect::>() } let groups = [ Ipv4Address::new(224, 0, 0, 22), Ipv4Address::new(224, 0, 0, 56), ]; let (mut iface, mut sockets, mut device) = setup(medium); // Join multicast groups let timestamp = Instant::ZERO; for group in &groups { iface .join_multicast_group(&mut device, *group, timestamp) .unwrap(); } let reports = recv_igmp(&mut device, timestamp); assert_eq!(reports.len(), 2); for (i, group_addr) in groups.iter().enumerate() { assert_eq!(reports[i].0.next_header, IpProtocol::Igmp); assert_eq!(reports[i].0.dst_addr, *group_addr); assert_eq!( reports[i].1, IgmpRepr::MembershipReport { group_addr: *group_addr, version: IgmpVersion::Version2, } ); } // General query let timestamp = Instant::ZERO; const GENERAL_QUERY_BYTES: &[u8] = &[ 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, 0x63, 0x04, 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, 0x11, 0x64, 0xec, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; { // Transmit GENERAL_QUERY_BYTES into loopback let tx_token = device.transmit(timestamp).unwrap(); tx_token.consume(GENERAL_QUERY_BYTES.len(), |buffer| { buffer.copy_from_slice(GENERAL_QUERY_BYTES); }); } // Trigger processing until all packets received through the // loopback have been processed, including responses to // GENERAL_QUERY_BYTES. Therefore `recv_all()` would return 0 // pkts that could be checked. iface.socket_ingress(&mut device, &mut sockets); // Leave multicast groups let timestamp = Instant::ZERO; for group in &groups { iface .leave_multicast_group(&mut device, *group, timestamp) .unwrap(); } let leaves = recv_igmp(&mut device, timestamp); assert_eq!(leaves.len(), 2); for (i, group_addr) in groups.iter().cloned().enumerate() { assert_eq!(leaves[i].0.next_header, IpProtocol::Igmp); assert_eq!(leaves[i].0.dst_addr, Ipv4Address::MULTICAST_ALL_ROUTERS); assert_eq!(leaves[i].1, IgmpRepr::LeaveGroup { group_addr }); } } #[rstest] #[case(Medium::Ip)] #[cfg(all(feature = "socket-raw", feature = "medium-ip"))] #[case(Medium::Ethernet)] #[cfg(all(feature = "socket-raw", feature = "medium-ethernet"))] fn test_raw_socket_no_reply(#[case] medium: Medium) { use crate::wire::{IpVersion, UdpPacket, UdpRepr}; let (mut iface, mut sockets, _) = setup(medium); let packets = 1; let rx_buffer = raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); let tx_buffer = raw::PacketBuffer::new( vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * packets], ); let raw_socket = raw::Socket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer); sockets.add(raw_socket); let src_addr = Ipv4Address([127, 0, 0, 2]); let dst_addr = Ipv4Address([127, 0, 0, 1]); const PAYLOAD_LEN: usize = 10; let udp_repr = UdpRepr { src_port: 67, dst_port: 68, }; let mut bytes = vec![0xff; udp_repr.header_len() + PAYLOAD_LEN]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); udp_repr.emit( &mut packet, &src_addr.into(), &dst_addr.into(), PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), &ChecksumCapabilities::default(), ); let ipv4_repr = Ipv4Repr { src_addr, dst_addr, next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + PAYLOAD_LEN, }; // Emit to frame let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + PAYLOAD_LEN]; let frame = { ipv4_repr.emit( &mut Ipv4Packet::new_unchecked(&mut bytes), &ChecksumCapabilities::default(), ); udp_repr.emit( &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), &ChecksumCapabilities::default(), ); Ipv4Packet::new_unchecked(&bytes[..]) }; assert_eq!( iface.inner.process_ipv4( &mut sockets, PacketMeta::default(), &frame, &mut iface.fragments ), None ); } #[rstest] #[case(Medium::Ip)] #[cfg(all(feature = "socket-raw", feature = "socket-udp", feature = "medium-ip"))] #[case(Medium::Ethernet)] #[cfg(all( feature = "socket-raw", feature = "socket-udp", feature = "medium-ethernet" ))] fn test_raw_socket_with_udp_socket(#[case] medium: Medium) { use crate::wire::{IpEndpoint, IpVersion, UdpPacket, UdpRepr}; static UDP_PAYLOAD: [u8; 5] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]; let (mut iface, mut sockets, _) = setup(medium); let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 15]); let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer); let udp_socket_handle = sockets.add(udp_socket); // Bind the socket to port 68 let socket = sockets.get_mut::(udp_socket_handle); assert_eq!(socket.bind(68), Ok(())); assert!(!socket.can_recv()); assert!(socket.can_send()); let packets = 1; let raw_rx_buffer = raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * 1]); let raw_tx_buffer = raw::PacketBuffer::new( vec![raw::PacketMetadata::EMPTY; packets], vec![0; 48 * packets], ); let raw_socket = raw::Socket::new( IpVersion::Ipv4, IpProtocol::Udp, raw_rx_buffer, raw_tx_buffer, ); sockets.add(raw_socket); let src_addr = Ipv4Address([127, 0, 0, 2]); let dst_addr = Ipv4Address([127, 0, 0, 1]); let udp_repr = UdpRepr { src_port: 67, dst_port: 68, }; let mut bytes = vec![0xff; udp_repr.header_len() + UDP_PAYLOAD.len()]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); udp_repr.emit( &mut packet, &src_addr.into(), &dst_addr.into(), UDP_PAYLOAD.len(), |buf| buf.copy_from_slice(&UDP_PAYLOAD), &ChecksumCapabilities::default(), ); let ipv4_repr = Ipv4Repr { src_addr, dst_addr, next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + UDP_PAYLOAD.len(), }; // Emit to frame let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.header_len() + UDP_PAYLOAD.len()]; let frame = { ipv4_repr.emit( &mut Ipv4Packet::new_unchecked(&mut bytes), &ChecksumCapabilities::default(), ); udp_repr.emit( &mut UdpPacket::new_unchecked(&mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), UDP_PAYLOAD.len(), |buf| buf.copy_from_slice(&UDP_PAYLOAD), &ChecksumCapabilities::default(), ); Ipv4Packet::new_unchecked(&bytes[..]) }; assert_eq!( iface.inner.process_ipv4( &mut sockets, PacketMeta::default(), &frame, &mut iface.fragments ), None ); // Make sure the UDP socket can still receive in presence of a Raw socket that handles UDP let socket = sockets.get_mut::(udp_socket_handle); assert!(socket.can_recv()); assert_eq!( socket.recv(), Ok(( &UDP_PAYLOAD[..], IpEndpoint::new(src_addr.into(), 67).into() )) ); } #[rstest] #[case(Medium::Ip)] #[cfg(all(feature = "socket-udp", feature = "medium-ip"))] #[case(Medium::Ethernet)] #[cfg(all(feature = "socket-udp", feature = "medium-ethernet"))] fn test_icmp_reply_size(#[case] medium: Medium) { use crate::wire::IPV4_MIN_MTU as MIN_MTU; const MAX_PAYLOAD_LEN: usize = 528; let (mut iface, mut sockets, _device) = setup(medium); let src_addr = Ipv4Address([192, 168, 1, 1]); let dst_addr = Ipv4Address([192, 168, 1, 2]); // UDP packet that if not tructated will cause a icmp port unreachable reply // to exceed the minimum mtu bytes in length. let udp_repr = UdpRepr { src_port: 67, dst_port: 68, }; let mut bytes = vec![0xff; udp_repr.header_len() + MAX_PAYLOAD_LEN]; let mut packet = UdpPacket::new_unchecked(&mut bytes[..]); udp_repr.emit( &mut packet, &src_addr.into(), &dst_addr.into(), MAX_PAYLOAD_LEN, |buf| fill_slice(buf, 0x2a), &ChecksumCapabilities::default(), ); let ip_repr = Ipv4Repr { src_addr, dst_addr, next_header: IpProtocol::Udp, hop_limit: 64, payload_len: udp_repr.header_len() + MAX_PAYLOAD_LEN, }; let payload = packet.into_inner(); let expected_icmp_repr = Icmpv4Repr::DstUnreachable { reason: Icmpv4DstUnreachable::PortUnreachable, header: ip_repr, data: &payload[..MAX_PAYLOAD_LEN], }; let expected_ip_repr = Ipv4Repr { src_addr: dst_addr, dst_addr: src_addr, next_header: IpProtocol::Icmp, hop_limit: 64, payload_len: expected_icmp_repr.buffer_len(), }; assert_eq!( expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(), MIN_MTU ); assert_eq!( iface.inner.process_udp( &mut sockets, PacketMeta::default(), ip_repr.into(), udp_repr, false, &vec![0x2a; MAX_PAYLOAD_LEN], payload, ), Some(Packet::new_ipv4( expected_ip_repr, IpPayload::Icmpv4(expected_icmp_repr) )) ); }