From 106fde60c628d88c886f373e2eb1b7f13ec931cc Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 28 Nov 2023 11:17:41 +0100 Subject: [PATCH 1/4] ipv6: add ALL_RPL_NODES multicast address --- src/wire/ipv6.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 191388212..236600d3a 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -87,6 +87,14 @@ impl Address { 0x02, ]); + /// The link-local [all RPL nodes multicast address]. + /// + /// [all RPL nodes multicast address]: https://www.rfc-editor.org/rfc/rfc6550.html#section-20.19 + pub const LINK_LOCAL_ALL_RPL_NODES: Address = Address([ + 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1a, + ]); + /// The [loopback address]. /// /// [loopback address]: https://tools.ietf.org/html/rfc4291#section-2.5.3 From 4cb10fc47bc30f67cd1c6f82db21b30dac1d4fdc Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 28 Nov 2023 11:18:49 +0100 Subject: [PATCH 2/4] ip: add IPv6 multic. addr. to has_multicast_group has_multicast_group was only used for IPv4 with IGMP. However, this is also needed for IPv6, where we check the ALL_NODES, ALL_RPL_NODES and the solicited multicast addresses. In case of IPv4, when the `proto-igmp` feature is not enabled, this function will return false. --- src/iface/interface/igmp.rs | 15 --------------- src/iface/interface/mod.rs | 25 +++++++++++++++++++++++-- src/iface/interface/tests/sixlowpan.rs | 21 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/iface/interface/igmp.rs b/src/iface/interface/igmp.rs index 890bdb551..14856ca07 100644 --- a/src/iface/interface/igmp.rs +++ b/src/iface/interface/igmp.rs @@ -208,21 +208,6 @@ impl Interface { } impl InterfaceInner { - /// Check whether the interface listens to given destination multicast IP address. - /// - /// If built without feature `proto-igmp` this function will - /// always return `false`. - pub fn has_multicast_group>(&self, addr: T) -> bool { - match addr.into() { - IpAddress::Ipv4(key) => { - key == Ipv4Address::MULTICAST_ALL_SYSTEMS - || self.ipv4_multicast_groups.get(&key).is_some() - } - #[allow(unreachable_patterns)] - _ => false, - } - } - /// Host duties of the **IGMPv2** protocol. /// /// Sets up `igmp_report_state` for responding to IGMP general/specific membership queries. diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index c38843514..54099ef6f 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -1007,9 +1007,30 @@ impl InterfaceInner { }) } - #[cfg(not(feature = "proto-igmp"))] + /// Check whether the interface listens to given destination multicast IP address. + /// + /// If built without feature `proto-igmp` this function will + /// always return `false` when using IPv4. fn has_multicast_group>(&self, addr: T) -> bool { - false + match addr.into() { + #[cfg(feature = "proto-igmp")] + IpAddress::Ipv4(key) => { + key == Ipv4Address::MULTICAST_ALL_SYSTEMS + || self.ipv4_multicast_groups.get(&key).is_some() + } + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(Ipv6Address::LINK_LOCAL_ALL_NODES) => true, + #[cfg(feature = "proto-rpl")] + IpAddress::Ipv6(Ipv6Address::LINK_LOCAL_ALL_RPL_NODES) => true, + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(addr) => self.ip_addrs.iter().any(|a| match a.address() { + IpAddress::Ipv6(a) => a.solicited_node() == addr, + #[allow(unreachable_patterns)] + _ => false, + }), + #[allow(unreachable_patterns)] + _ => false, + } } #[cfg(feature = "medium-ip")] diff --git a/src/iface/interface/tests/sixlowpan.rs b/src/iface/interface/tests/sixlowpan.rs index 62cba96fe..676835ed7 100644 --- a/src/iface/interface/tests/sixlowpan.rs +++ b/src/iface/interface/tests/sixlowpan.rs @@ -59,6 +59,13 @@ fn icmp_echo_request(#[case] medium: Medium) { )); let (mut iface, mut sockets, _device) = setup(medium); + iface.update_ip_addrs(|ips| { + ips.push(IpCidr::Ipv6(Ipv6Cidr::new( + Ipv6Address::from_parts(&[0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242]), + 10, + ))) + .unwrap(); + }); assert_eq!( iface.inner.process_ieee802154( @@ -77,6 +84,13 @@ fn test_echo_request_sixlowpan_128_bytes() { use crate::phy::Checksum; let (mut iface, mut sockets, mut device) = setup(Medium::Ieee802154); + iface.update_ip_addrs(|ips| { + ips.push(IpCidr::Ipv6(Ipv6Cidr::new( + Ipv6Address::new(0xfe80, 0x0, 0x0, 0x0, 0x92fc, 0x48c2, 0xa441, 0xfc76), + 10, + ))) + .unwrap(); + }); // TODO: modify the example, such that we can also test if the checksum is correctly // computed. iface.inner.caps.checksum.icmpv6 = Checksum::None; @@ -283,6 +297,13 @@ fn test_sixlowpan_udp_with_fragmentation() { }; let (mut iface, mut sockets, mut device) = setup(Medium::Ieee802154); + iface.update_ip_addrs(|ips| { + ips.push(IpCidr::Ipv6(Ipv6Cidr::new( + Ipv6Address::new(0xfe80, 0x0, 0x0, 0x0, 0x92fc, 0x48c2, 0xa441, 0xfc76), + 10, + ))) + .unwrap(); + }); iface.inner.caps.checksum.udp = Checksum::None; let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024 * 4]); From d0b7fa84616d2ab82bb1ece314730d6fb96293c9 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Tue, 28 Nov 2023 11:21:29 +0100 Subject: [PATCH 3/4] ipv6: check hbh before checking IPv6 addr. With IPv6, the hop-by-hop option should be checked allong every node on the route. Only then the IPv6 destination address is checked. ip(hbh): return Param Problem for some options For some options, a ICMP parameter problem needs to be transmitted back to the source. --- src/iface/interface/ipv6.rs | 177 +++++++++++++++++++----------- src/iface/interface/mod.rs | 6 +- src/iface/interface/tests/ipv6.rs | 136 ++++++++++++++++++++++- 3 files changed, 245 insertions(+), 74 deletions(-) diff --git a/src/iface/interface/ipv6.rs b/src/iface/interface/ipv6.rs index dcfaffa76..8f5df4971 100644 --- a/src/iface/interface/ipv6.rs +++ b/src/iface/interface/ipv6.rs @@ -7,6 +7,24 @@ use crate::socket::AnySocket; use crate::phy::PacketMeta; use crate::wire::*; +/// Enum used for the process_hopbyhop function. In some cases, when discarding a packet, an ICMMP +/// parameter problem message needs to be transmitted to the source of the address. In other cases, +/// the processing of the IP packet can continue. +#[allow(clippy::large_enum_variant)] +enum HopByHopResponse<'frame> { + /// Continue processing the IPv6 packet. + Continue((IpProtocol, &'frame [u8])), + /// Discard the packet and maybe send back an ICMPv6 packet. + Discard(Option>), +} + +// We implement `Default` such that we can use the check! macro. +impl Default for HopByHopResponse<'_> { + fn default() -> Self { + Self::Discard(None) + } +} + impl InterfaceInner { pub(super) fn process_ipv6<'frame>( &mut self, @@ -22,7 +40,19 @@ impl InterfaceInner { return None; } - let ip_payload = ipv6_packet.payload(); + let (next_header, ip_payload) = if ipv6_repr.next_header == IpProtocol::HopByHop { + match self.process_hopbyhop(ipv6_repr, ipv6_packet.payload()) { + HopByHopResponse::Discard(e) => return e, + HopByHopResponse::Continue(next) => next, + } + } else { + (ipv6_repr.next_header, ipv6_packet.payload()) + }; + + if !self.has_ip_addr(ipv6_repr.dst_addr) && !self.has_multicast_group(ipv6_repr.dst_addr) { + net_trace!("packet IP address not for this interface"); + return None; + } #[cfg(feature = "socket-raw")] let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload); @@ -33,15 +63,71 @@ impl InterfaceInner { sockets, meta, ipv6_repr, - ipv6_repr.next_header, + next_header, handled_by_raw_socket, ip_payload, ) } + fn process_hopbyhop<'frame>( + &mut self, + ipv6_repr: Ipv6Repr, + ip_payload: &'frame [u8], + ) -> HopByHopResponse<'frame> { + let param_problem = || { + let payload_len = + icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len()); + self.icmpv6_reply( + ipv6_repr, + Icmpv6Repr::ParamProblem { + reason: Icmpv6ParamProblem::UnrecognizedOption, + pointer: ipv6_repr.buffer_len() as u32, + header: ipv6_repr, + data: &ip_payload[0..payload_len], + }, + ) + }; + + let ext_hdr = check!(Ipv6ExtHeader::new_checked(ip_payload)); + let ext_repr = check!(Ipv6ExtHeaderRepr::parse(&ext_hdr)); + let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ext_repr.data)); + let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_hdr)); + + for opt_repr in &hbh_repr.options { + match opt_repr { + Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (), + #[cfg(feature = "proto-rpl")] + Ipv6OptionRepr::Rpl(_) => {} + + Ipv6OptionRepr::Unknown { type_, .. } => { + match Ipv6OptionFailureType::from(*type_) { + Ipv6OptionFailureType::Skip => (), + Ipv6OptionFailureType::Discard => { + return HopByHopResponse::Discard(None); + } + Ipv6OptionFailureType::DiscardSendAll => { + return HopByHopResponse::Discard(param_problem()); + } + Ipv6OptionFailureType::DiscardSendUnicast + if !ipv6_repr.dst_addr.is_multicast() => + { + return HopByHopResponse::Discard(param_problem()); + } + _ => unreachable!(), + } + } + } + } + + HopByHopResponse::Continue(( + ext_repr.next_header, + &ip_payload[ext_repr.header_len() + ext_repr.data.len()..], + )) + } + /// Given the next header value forward the payload onto the correct process /// function. - pub(super) fn process_nxt_hdr<'frame>( + fn process_nxt_hdr<'frame>( &mut self, sockets: &mut SocketSet, meta: PacketMeta, @@ -77,10 +163,6 @@ impl InterfaceInner { #[cfg(feature = "socket-tcp")] IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload), - IpProtocol::HopByHop => { - self.process_hopbyhop(sockets, meta, ipv6_repr, handled_by_raw_socket, ip_payload) - } - #[cfg(feature = "socket-raw")] _ if handled_by_raw_socket => None, @@ -238,70 +320,33 @@ impl InterfaceInner { } } - pub(super) fn process_hopbyhop<'frame>( - &mut self, - sockets: &mut SocketSet, - meta: PacketMeta, - ipv6_repr: Ipv6Repr, - handled_by_raw_socket: bool, - ip_payload: &'frame [u8], - ) -> Option> { - let ext_hdr = check!(Ipv6ExtHeader::new_checked(ip_payload)); - let ext_repr = check!(Ipv6ExtHeaderRepr::parse(&ext_hdr)); - let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ext_repr.data)); - let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_hdr)); - - for opt_repr in &hbh_repr.options { - match opt_repr { - Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (), - #[cfg(feature = "proto-rpl")] - Ipv6OptionRepr::Rpl(_) => {} - - Ipv6OptionRepr::Unknown { type_, .. } => { - match Ipv6OptionFailureType::from(*type_) { - Ipv6OptionFailureType::Skip => (), - Ipv6OptionFailureType::Discard => { - return None; - } - _ => { - // FIXME(dlrobertson): Send an ICMPv6 parameter problem message - // here. - return None; - } - } - } - } - } - self.process_nxt_hdr( - sockets, - meta, - ipv6_repr, - ext_repr.next_header, - handled_by_raw_socket, - &ip_payload[ext_repr.header_len() + ext_repr.data.len()..], - ) - } - pub(super) fn icmpv6_reply<'frame, 'icmp: 'frame>( &self, ipv6_repr: Ipv6Repr, icmp_repr: Icmpv6Repr<'icmp>, ) -> Option> { - if ipv6_repr.dst_addr.is_unicast() { - let ipv6_reply_repr = Ipv6Repr { - src_addr: ipv6_repr.dst_addr, - dst_addr: ipv6_repr.src_addr, - next_header: IpProtocol::Icmpv6, - payload_len: icmp_repr.buffer_len(), - hop_limit: 64, - }; - Some(Packet::new_ipv6( - ipv6_reply_repr, - IpPayload::Icmpv6(icmp_repr), - )) + let src_addr = ipv6_repr.dst_addr; + let dst_addr = ipv6_repr.src_addr; + + let src_addr = if src_addr.is_unicast() { + src_addr + } else if let Some(addr) = self.get_source_address_ipv6(&dst_addr) { + addr } else { - // Do not send any ICMP replies to a broadcast destination address. - None - } + net_debug!("no suitable source address found"); + return None; + }; + + let ipv6_reply_repr = Ipv6Repr { + src_addr, + dst_addr, + next_header: IpProtocol::Icmpv6, + payload_len: icmp_repr.buffer_len(), + hop_limit: 64, + }; + Some(Packet::new_ipv6( + ipv6_reply_repr, + IpPayload::Icmpv6(icmp_repr), + )) } } diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 54099ef6f..7d6bdc820 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -1023,11 +1023,7 @@ impl InterfaceInner { #[cfg(feature = "proto-rpl")] IpAddress::Ipv6(Ipv6Address::LINK_LOCAL_ALL_RPL_NODES) => true, #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(addr) => self.ip_addrs.iter().any(|a| match a.address() { - IpAddress::Ipv6(a) => a.solicited_node() == addr, - #[allow(unreachable_patterns)] - _ => false, - }), + IpAddress::Ipv6(addr) => self.has_solicited_node(addr), #[allow(unreachable_patterns)] _ => false, } diff --git a/src/iface/interface/tests/ipv6.rs b/src/iface/interface/tests/ipv6.rs index 6e4563490..9c5f09980 100644 --- a/src/iface/interface/tests/ipv6.rs +++ b/src/iface/interface/tests/ipv6.rs @@ -140,6 +140,117 @@ fn hop_by_hop_discard_with_icmp(#[case] medium: Medium) { ); } +#[rstest] +#[case::ip(Medium::Ip)] +#[cfg(feature = "medium-ip")] +fn hop_by_hop_discard_param_problem(#[case] medium: Medium) { + // The following contains: + // - IPv6 header + // - Hop-by-hop, with options: + // - PADN (skipped) + // - Unknown option (discard + ParamProblem) + // - ICMP echo request + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x0, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x3a, 0x0, 0xC0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88, + 0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d, + ]; + + let response = Some(Packet::new_ipv6( + Ipv6Repr { + src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1), + dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2), + next_header: IpProtocol::Icmpv6, + payload_len: 75, + hop_limit: 64, + }, + IpPayload::Icmpv6(Icmpv6Repr::ParamProblem { + reason: Icmpv6ParamProblem::UnrecognizedOption, + pointer: 40, + header: Ipv6Repr { + src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2), + dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1), + next_header: IpProtocol::HopByHop, + payload_len: 27, + hop_limit: 64, + }, + data: &[ + 0x3a, 0x0, 0xC0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88, 0x0, 0x2a, 0x1, + 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d, + ], + }), + )); + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data[..]).unwrap() + ), + response + ); +} + +#[rstest] +#[case::ip(Medium::Ip)] +#[cfg(feature = "medium-ip")] +fn hop_by_hop_discard_with_multicast(#[case] medium: Medium) { + // The following contains: + // - IPv6 header + // - Hop-by-hop, with options: + // - PADN (skipped) + // - Unknown option (discard (0b11) + ParamProblem) + // - ICMP echo request + // + // In this case, even if the destination address is a multicast address, an ICMPv6 ParamProblem + // should be transmitted. + let data = [ + 0x60, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x0, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0x02, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x1, 0x3a, 0x0, 0x80, 0x0, 0x40, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88, + 0x0, 0x2a, 0x1, 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d, + ]; + + let response = Some(Packet::new_ipv6( + Ipv6Repr { + src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 1), + dst_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2), + next_header: IpProtocol::Icmpv6, + payload_len: 75, + hop_limit: 64, + }, + IpPayload::Icmpv6(Icmpv6Repr::ParamProblem { + reason: Icmpv6ParamProblem::UnrecognizedOption, + pointer: 40, + header: Ipv6Repr { + src_addr: Ipv6Address::new(0xfdbe, 0, 0, 0, 0, 0, 0, 2), + dst_addr: Ipv6Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1), + next_header: IpProtocol::HopByHop, + payload_len: 27, + hop_limit: 64, + }, + data: &[ + 0x3a, 0x0, 0x80, 0x0, 0x40, 0x0, 0x1, 0x0, 0x80, 0x0, 0x2c, 0x88, 0x0, 0x2a, 0x1, + 0xa4, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x49, 0x70, 0x73, 0x75, 0x6d, + ], + }), + )); + + let (mut iface, mut sockets, _device) = setup(medium); + + assert_eq!( + iface.inner.process_ipv6( + &mut sockets, + PacketMeta::default(), + &Ipv6Packet::new_checked(&data[..]).unwrap() + ), + response + ); +} + #[rstest] #[case::ip(Medium::Ip)] #[cfg(feature = "medium-ip")] @@ -314,14 +425,33 @@ fn icmp_echo_reply_as_input(#[case] medium: Medium) { #[case::ieee802154(Medium::Ieee802154)] #[cfg(feature = "medium-ieee802154")] fn unknown_proto_with_multicast_dst_address(#[case] medium: Medium) { - // Since the destination address is multicast, we should not answer with an ICMPv6 message. let data = [ 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, ]; - let response = None; + let response = Some(Packet::new_ipv6( + Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0001]), + dst_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + hop_limit: 64, + next_header: IpProtocol::Icmpv6, + payload_len: 48, + }, + IpPayload::Icmpv6(Icmpv6Repr::ParamProblem { + reason: Icmpv6ParamProblem::UnrecognizedNxtHdr, + pointer: 40, + header: Ipv6Repr { + src_addr: Ipv6Address::from_parts(&[0xfdbe, 0, 0, 0, 0, 0, 0, 0x0002]), + dst_addr: Ipv6Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 0x0001]), + hop_limit: 64, + next_header: IpProtocol::Unknown(0x0c), + payload_len: 0, + }, + data: &[], + }), + )); let (mut iface, mut sockets, _device) = setup(medium); @@ -343,7 +473,7 @@ fn unknown_proto_with_multicast_dst_address(#[case] medium: Medium) { #[case::ieee802154(Medium::Ieee802154)] #[cfg(feature = "medium-ieee802154")] fn unknown_proto(#[case] medium: Medium) { - // Since the destination address is multicast, we should not answer with an ICMPv6 message. + // Since the destination address is multicast, we should answer with an ICMPv6 message. let data = [ 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x40, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, From 1001586430186bc467606c4a55c30c8d0f63d87e Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Wed, 29 Nov 2023 16:36:01 +0100 Subject: [PATCH 4/4] fix(ipv6): allow loopback destination address --- src/iface/interface/ipv6.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/iface/interface/ipv6.rs b/src/iface/interface/ipv6.rs index 8f5df4971..22e65b967 100644 --- a/src/iface/interface/ipv6.rs +++ b/src/iface/interface/ipv6.rs @@ -49,7 +49,10 @@ impl InterfaceInner { (ipv6_repr.next_header, ipv6_packet.payload()) }; - if !self.has_ip_addr(ipv6_repr.dst_addr) && !self.has_multicast_group(ipv6_repr.dst_addr) { + if !self.has_ip_addr(ipv6_repr.dst_addr) + && !self.has_multicast_group(ipv6_repr.dst_addr) + && !ipv6_repr.dst_addr.is_loopback() + { net_trace!("packet IP address not for this interface"); return None; }