From b310e3c366b8019fd9703cd38ae4218573805d19 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 30 Nov 2023 15:16:01 +0100 Subject: [PATCH 1/5] fix: get_source_address calls v4 or v6 variant --- src/iface/interface/mod.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index bd682496d..19fa8748e 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -928,14 +928,12 @@ impl InterfaceInner { #[allow(unused)] // unused depending on which sockets are enabled pub(crate) fn get_source_address(&mut self, dst_addr: IpAddress) -> Option { - let v = dst_addr.version(); - for cidr in self.ip_addrs.iter() { - let addr = cidr.address(); - if addr.version() == v { - return Some(addr); - } + match dst_addr { + #[cfg(feature = "proto-ipv4")] + IpAddress::Ipv4(addr) => self.get_source_address_ipv4(addr).map(|a| a.into()), + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(addr) => self.get_source_address_ipv6(addr).map(|a| a.into()), } - None } #[cfg(feature = "proto-ipv4")] From 45f9838ad97a2c9a43b9527844606d7484f60e4b Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Thu, 30 Nov 2023 16:00:38 +0100 Subject: [PATCH 2/5] ipv6: address scope and `is_global_unicast` The scope of an address is used when selecting the source IPv6 address based on the destination address. As the scope is then also used for unicast address, I made the scope only public in the crate instead of outside the crate. Not sure if this should be public or not. This commit also adds the `is_global_unicast` query function for IPv6 addresses. --- src/wire/ipv6.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ src/wire/mod.rs | 2 ++ 2 files changed, 76 insertions(+) diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index cc462e84b..44093c502 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -25,6 +25,41 @@ pub const ADDR_SIZE: usize = 16; /// [RFC 8200 ยง 2]: https://www.rfc-editor.org/rfc/rfc4291#section-2 pub const IPV4_MAPPED_PREFIX_SIZE: usize = ADDR_SIZE - 4; // 4 == ipv4::ADDR_SIZE , cannot DRY here because of dependency on a IPv4 module which is behind the feature +/// The [scope] of an address. +/// +/// [scope]: https://www.rfc-editor.org/rfc/rfc4291#section-2.7 +#[repr(u8)] +pub(crate) enum Scope { + /// Interface Local scope + InterfaceLocal = 0x1, + /// Link local scope + LinkLocal = 0x2, + /// Administratively configured + AdminLocal = 0x4, + /// Single site scope + SiteLocal = 0x5, + /// Organization scope + OrganizationLocal = 0x8, + /// Global scope + Global = 0xE, + /// Unknown scope + Unknown = 0xFF, +} + +impl From for Scope { + fn from(value: u8) -> Self { + match value { + 0x1 => Self::InterfaceLocal, + 0x2 => Self::LinkLocal, + 0x4 => Self::AdminLocal, + 0x5 => Self::SiteLocal, + 0x8 => Self::OrganizationLocal, + 0xE => Self::Global, + _ => Self::Unknown, + } + } +} + /// A sixteen-octet IPv6 address. #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] pub struct Address(pub [u8; ADDR_SIZE]); @@ -143,6 +178,13 @@ impl Address { !(self.is_multicast() || self.is_unspecified()) } + /// Query whether the IPv6 address is a [global unicast address]. + /// + /// [global unicast address]: https://datatracker.ietf.org/doc/html/rfc3587 + pub const fn is_global_unicast(&self) -> bool { + (self.0[0] >> 5) == 0b001 + } + /// Query whether the IPv6 address is a [multicast address]. /// /// [multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7 @@ -228,6 +270,22 @@ impl Address { ]) } + /// Return the scope of the address. + pub(crate) fn scope(&self) -> Scope { + if self.is_multicast() { + return Scope::from(self.as_bytes()[1] & 0b1111); + } + + if self.is_link_local() { + Scope::LinkLocal + } else if self.is_unique_local() || self.is_global_unicast() { + // ULA are considered global scope + Scope::Global + } else { + Scope::Unknown + } + } + /// Convert to an `IpAddress`. /// /// Same as `.into()`, but works in `const`. @@ -840,6 +898,7 @@ mod test { const LINK_LOCAL_ADDR: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); const UNIQUE_LOCAL_ADDR: Address = Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1); + const GLOBAL_UNICAST_ADDR: Address = Address::new(0x2001, 0xdb8, 0x3, 0, 0, 0, 0, 1); #[test] fn test_basic_multicast() { @@ -848,11 +907,13 @@ mod test { assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_link_local()); assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_loopback()); assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_unique_local()); + assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_global_unicast()); assert!(!Address::LINK_LOCAL_ALL_NODES.is_unspecified()); assert!(Address::LINK_LOCAL_ALL_NODES.is_multicast()); assert!(!Address::LINK_LOCAL_ALL_NODES.is_link_local()); assert!(!Address::LINK_LOCAL_ALL_NODES.is_loopback()); assert!(!Address::LINK_LOCAL_ALL_NODES.is_unique_local()); + assert!(!Address::LINK_LOCAL_ALL_NODES.is_global_unicast()); } #[test] @@ -862,6 +923,7 @@ mod test { assert!(LINK_LOCAL_ADDR.is_link_local()); assert!(!LINK_LOCAL_ADDR.is_loopback()); assert!(!LINK_LOCAL_ADDR.is_unique_local()); + assert!(!LINK_LOCAL_ADDR.is_global_unicast()); } #[test] @@ -871,6 +933,7 @@ mod test { assert!(!Address::LOOPBACK.is_link_local()); assert!(Address::LOOPBACK.is_loopback()); assert!(!Address::LOOPBACK.is_unique_local()); + assert!(!Address::LOOPBACK.is_global_unicast()); } #[test] @@ -880,6 +943,17 @@ mod test { assert!(!UNIQUE_LOCAL_ADDR.is_link_local()); assert!(!UNIQUE_LOCAL_ADDR.is_loopback()); assert!(UNIQUE_LOCAL_ADDR.is_unique_local()); + assert!(!UNIQUE_LOCAL_ADDR.is_global_unicast()); + } + + #[test] + fn test_global_unicast() { + assert!(!GLOBAL_UNICAST_ADDR.is_unspecified()); + assert!(!GLOBAL_UNICAST_ADDR.is_multicast()); + assert!(!GLOBAL_UNICAST_ADDR.is_link_local()); + assert!(!GLOBAL_UNICAST_ADDR.is_loopback()); + assert!(!GLOBAL_UNICAST_ADDR.is_unique_local()); + assert!(GLOBAL_UNICAST_ADDR.is_global_unicast()); } #[test] diff --git a/src/wire/mod.rs b/src/wire/mod.rs index 500e359b5..6407553a0 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -190,6 +190,8 @@ pub use self::ipv4::{ Repr as Ipv4Repr, HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU, }; +#[cfg(feature = "proto-ipv6")] +pub(crate) use self::ipv6::Scope as Ipv6AddressScope; #[cfg(feature = "proto-ipv6")] pub use self::ipv6::{ Address as Ipv6Address, Cidr as Ipv6Cidr, Packet as Ipv6Packet, Repr as Ipv6Repr, From dd9eff7cf92cc4ae426dbb858c5b9e145ef7b15d Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Fri, 1 Dec 2023 11:01:12 +0100 Subject: [PATCH 3/5] ipv6: use RFC6724 for selecting IPv6 src address RFC6724 defines how the source address should be selected when given a destination address. Instead of selecting the first address in the list of interface addresses, the source address is selected following the standard. --- examples/ping.rs | 14 +++- src/iface/interface/mod.rs | 133 ++++++++++++++++++++++++++---- src/iface/interface/tests/ipv6.rs | 92 +++++++++++++++++++++ 3 files changed, 221 insertions(+), 18 deletions(-) diff --git a/examples/ping.rs b/examples/ping.rs index 341413a3a..7e33a212a 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -176,7 +176,7 @@ fn main() { ); icmp_repr.emit(&mut icmp_packet, &device_caps.checksum); } - IpAddress::Ipv6(_) => { + IpAddress::Ipv6(address) => { let (icmp_repr, mut icmp_packet) = send_icmp_ping!( Icmpv6Repr, Icmpv6Packet, @@ -187,7 +187,10 @@ fn main() { remote_addr ); icmp_repr.emit( - &iface.ipv6_addr().unwrap().into_address(), + &iface + .get_source_address_ipv6(&address) + .unwrap() + .into_address(), &remote_addr, &mut icmp_packet, &device_caps.checksum, @@ -217,11 +220,14 @@ fn main() { received ); } - IpAddress::Ipv6(_) => { + IpAddress::Ipv6(address) => { let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap(); let icmp_repr = Icmpv6Repr::parse( &remote_addr, - &iface.ipv6_addr().unwrap().into_address(), + &iface + .get_source_address_ipv6(&address) + .unwrap() + .into_address(), &icmp_packet, &device_caps.checksum, ) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 19fa8748e..48edbb555 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -464,6 +464,27 @@ impl Interface { self.inner.ipv6_addr() } + /// Get an address from the interface that could be used as source address. For IPv4, this is + /// the first IPv4 address from the list of addresses. For IPv6, the address is based on the + /// destination address and uses RFC6724 for selecting the source address. + pub fn get_source_address(&self, dst_addr: &IpAddress) -> Option { + self.inner.get_source_address(*dst_addr) + } + + /// Get an address from the interface that could be used as source address. This is the first + /// IPv4 address from the list of addresses in the interface. + #[cfg(feature = "proto-ipv4")] + pub fn get_source_address_ipv4(&self, dst_addr: &Ipv4Address) -> Option { + self.inner.get_source_address_ipv4(*dst_addr) + } + + /// Get an address from the interface that could be used as source address. The selection is + /// based on RFC6724. + #[cfg(feature = "proto-ipv6")] + pub fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option { + self.inner.get_source_address_ipv6(*dst_addr) + } + /// Update the IP addresses of the interface. /// /// # Panics @@ -927,7 +948,7 @@ impl InterfaceInner { } #[allow(unused)] // unused depending on which sockets are enabled - pub(crate) fn get_source_address(&mut self, dst_addr: IpAddress) -> Option { + pub(crate) fn get_source_address(&self, dst_addr: IpAddress) -> Option { match dst_addr { #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(addr) => self.get_source_address_ipv4(addr).map(|a| a.into()), @@ -938,10 +959,7 @@ impl InterfaceInner { #[cfg(feature = "proto-ipv4")] #[allow(unused)] - pub(crate) fn get_source_address_ipv4( - &mut self, - _dst_addr: Ipv4Address, - ) -> Option { + pub(crate) fn get_source_address_ipv4(&self, _dst_addr: Ipv4Address) -> Option { for cidr in self.ip_addrs.iter() { #[allow(irrefutable_let_patterns)] // if only ipv4 is enabled if let IpCidr::Ipv4(cidr) = cidr { @@ -953,17 +971,104 @@ impl InterfaceInner { #[cfg(feature = "proto-ipv6")] #[allow(unused)] - pub(crate) fn get_source_address_ipv6( - &mut self, - _dst_addr: Ipv6Address, - ) -> Option { - for cidr in self.ip_addrs.iter() { - #[allow(irrefutable_let_patterns)] // if only ipv6 is enabled - if let IpCidr::Ipv6(cidr) = cidr { - return Some(cidr.address()); + pub(crate) fn get_source_address_ipv6(&self, dst_addr: Ipv6Address) -> Option { + // RFC 6724 describes how to select the correct source address depending on the destination + // address. + + // See RFC 6724 Section 4: Candidate source address + fn is_candidate_source_address(dst_addr: &Ipv6Address, src_addr: &Ipv6Address) -> bool { + // For all multicast and link-local destination addresses, the candidate address MUST + // only be an address from the same link. + if dst_addr.is_link_local() && !src_addr.is_link_local() { + return false; } + + if dst_addr.is_multicast() + && matches!(dst_addr.scope(), Ipv6AddressScope::LinkLocal) + && src_addr.is_multicast() + && !matches!(src_addr.scope(), Ipv6AddressScope::LinkLocal) + { + return false; + } + + // Loopback addresses and multicast address can not be in the candidate source address + // list. Except when the destination multicast address has a link-local scope, then the + // source address can also be link-local multicast. + if src_addr.is_loopback() || src_addr.is_multicast() { + return false; + } + + true } - None + + // See RFC 6724 Section 2.2: Common Prefix Length + fn common_prefix_length(dst_addr: &Ipv6Cidr, src_addr: &Ipv6Address) -> usize { + let addr = dst_addr.address(); + let mut bits = 0; + for (l, r) in addr.as_bytes().iter().zip(src_addr.as_bytes().iter()) { + if l == r { + bits += 8; + } else { + bits += (l ^ r).leading_zeros(); + break; + } + } + + bits = bits.min(dst_addr.prefix_len() as u32); + + bits as usize + } + + // Get the first address that is a candidate address. + let mut candidate = self + .ip_addrs + .iter() + .filter_map(|a| match a { + #[cfg(feature = "proto-ipv4")] + IpCidr::Ipv4(_) => None, + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(a) => Some(a), + }) + .find(|a| is_candidate_source_address(&dst_addr, &a.address())) + .unwrap(); + + for addr in self.ip_addrs.iter().filter_map(|a| match a { + #[cfg(feature = "proto-ipv4")] + IpCidr::Ipv4(_) => None, + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(a) => Some(a), + }) { + if !is_candidate_source_address(&dst_addr, &addr.address()) { + continue; + } + + // Rule 1: prefer the address that is the same as the output destination address. + if candidate.address() != dst_addr && addr.address() == dst_addr { + candidate = addr; + } + + // Rule 2: prefer appropriate scope. + if (candidate.address().scope() as u8) < (addr.address().scope() as u8) { + if (candidate.address().scope() as u8) < (dst_addr.scope() as u8) { + candidate = addr; + } + } else if (addr.address().scope() as u8) > (dst_addr.scope() as u8) { + candidate = addr; + } + + // Rule 3: avoid deprecated addresses (TODO) + // Rule 4: prefer home addresses (TODO) + // Rule 5: prefer outgoing interfaces (TODO) + // Rule 5.5: prefer addresses in a prefix advertises by the next-hop (TODO). + // Rule 6: prefer matching label (TODO) + // Rule 7: prefer temporary addresses (TODO) + // Rule 8: use longest matching prefix + if common_prefix_length(candidate, &dst_addr) < common_prefix_length(addr, &dst_addr) { + candidate = addr; + } + } + + Some(candidate.address()) } #[cfg(test)] diff --git a/src/iface/interface/tests/ipv6.rs b/src/iface/interface/tests/ipv6.rs index 5ce233f77..b90184889 100644 --- a/src/iface/interface/tests/ipv6.rs +++ b/src/iface/interface/tests/ipv6.rs @@ -735,3 +735,95 @@ fn test_icmp_reply_size(#[case] medium: Medium) { )) ); } + +#[cfg(feature = "medium-ip")] +#[test] +fn get_source_address() { + let (mut iface, _, _) = setup(Medium::Ip); + + const OWN_LINK_LOCAL_ADDR: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); + const OWN_UNIQUE_LOCAL_ADDR1: Ipv6Address = Ipv6Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 2); + const OWN_UNIQUE_LOCAL_ADDR2: Ipv6Address = Ipv6Address::new(0xfd01, 0, 0, 201, 1, 1, 1, 2); + const OWN_GLOBAL_UNICAST_ADDR1: Ipv6Address = + Ipv6Address::new(0x2001, 0x0db8, 0x0003, 0, 0, 0, 0, 1); + + // List of addresses of the interface: + // fe80::1/64 + // fd00::201:1:1:1:2/64 + // fd01::201:1:1:1:2/64 + // 2001:db8:3::1/64 + // ::1/128 + // ::/128 + iface.update_ip_addrs(|addrs| { + addrs.clear(); + + addrs + .push(IpCidr::Ipv6(Ipv6Cidr::new(OWN_LINK_LOCAL_ADDR, 64))) + .unwrap(); + addrs + .push(IpCidr::Ipv6(Ipv6Cidr::new(OWN_UNIQUE_LOCAL_ADDR1, 64))) + .unwrap(); + addrs + .push(IpCidr::Ipv6(Ipv6Cidr::new(OWN_UNIQUE_LOCAL_ADDR2, 64))) + .unwrap(); + addrs + .push(IpCidr::Ipv6(Ipv6Cidr::new(OWN_GLOBAL_UNICAST_ADDR1, 64))) + .unwrap(); + + // These should never be used: + addrs + .push(IpCidr::Ipv6(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128))) + .unwrap(); + addrs + .push(IpCidr::Ipv6(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128))) + .unwrap(); + }); + + // List of addresses we test: + // fe80::42 -> fe80::1 + // fd00::201:1:1:1:1 -> fd00::201:1:1:1:2 + // fd01::201:1:1:1:1 -> fd01::201:1:1:1:2 + // fd02::201:1:1:1:1 -> fd00::201:1:1:1:2 (because first added in the list) + // ff02::1 -> fe80::1 (same scope) + // 2001:db8:3::2 -> 2001:db8:3::1 + // 2001:db9:3::2 -> 2001:db8:3::1 + const LINK_LOCAL_ADDR: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 42); + const UNIQUE_LOCAL_ADDR1: Ipv6Address = Ipv6Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1); + const UNIQUE_LOCAL_ADDR2: Ipv6Address = Ipv6Address::new(0xfd01, 0, 0, 201, 1, 1, 1, 1); + const UNIQUE_LOCAL_ADDR3: Ipv6Address = Ipv6Address::new(0xfd02, 0, 0, 201, 1, 1, 1, 1); + const GLOBAL_UNICAST_ADDR1: Ipv6Address = + Ipv6Address::new(0x2001, 0x0db8, 0x0003, 0, 0, 0, 0, 2); + const GLOBAL_UNICAST_ADDR2: Ipv6Address = + Ipv6Address::new(0x2001, 0x0db9, 0x0003, 0, 0, 0, 0, 2); + + assert_eq!( + iface.inner.get_source_address_ipv6(LINK_LOCAL_ADDR), + Some(OWN_LINK_LOCAL_ADDR) + ); + assert_eq!( + iface.inner.get_source_address_ipv6(UNIQUE_LOCAL_ADDR1), + Some(OWN_UNIQUE_LOCAL_ADDR1) + ); + assert_eq!( + iface.inner.get_source_address_ipv6(UNIQUE_LOCAL_ADDR2), + Some(OWN_UNIQUE_LOCAL_ADDR2) + ); + assert_eq!( + iface.inner.get_source_address_ipv6(UNIQUE_LOCAL_ADDR3), + Some(OWN_UNIQUE_LOCAL_ADDR1) + ); + assert_eq!( + iface + .inner + .get_source_address_ipv6(Ipv6Address::LINK_LOCAL_ALL_NODES), + Some(OWN_LINK_LOCAL_ADDR) + ); + assert_eq!( + iface.inner.get_source_address_ipv6(GLOBAL_UNICAST_ADDR1), + Some(OWN_GLOBAL_UNICAST_ADDR1) + ); + assert_eq!( + iface.inner.get_source_address_ipv6(GLOBAL_UNICAST_ADDR2), + Some(OWN_GLOBAL_UNICAST_ADDR1) + ); +} From 36a143d88e80f947dd87c7535af4b1537ff2899d Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 4 Dec 2023 15:21:08 +0100 Subject: [PATCH 4/5] ipv6: add tests for scope --- src/wire/ipv6.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/wire/ipv6.rs b/src/wire/ipv6.rs index 44093c502..191388212 100644 --- a/src/wire/ipv6.rs +++ b/src/wire/ipv6.rs @@ -29,6 +29,7 @@ pub const IPV4_MAPPED_PREFIX_SIZE: usize = ADDR_SIZE - 4; // 4 == ipv4::ADDR_SIZ /// /// [scope]: https://www.rfc-editor.org/rfc/rfc4291#section-2.7 #[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum Scope { /// Interface Local scope InterfaceLocal = 0x1, @@ -280,6 +281,7 @@ impl Address { Scope::LinkLocal } else if self.is_unique_local() || self.is_global_unicast() { // ULA are considered global scope + // https://www.rfc-editor.org/rfc/rfc6724#section-3.1 Scope::Global } else { Scope::Unknown @@ -1234,6 +1236,46 @@ mod test { let _ = Address::from_parts(&[0u16; 7]); } + #[test] + fn test_scope() { + use super::*; + assert_eq!( + Address::new(0xff01, 0, 0, 0, 0, 0, 0, 1).scope(), + Scope::InterfaceLocal + ); + assert_eq!( + Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1).scope(), + Scope::LinkLocal + ); + assert_eq!( + Address::new(0xff03, 0, 0, 0, 0, 0, 0, 1).scope(), + Scope::Unknown + ); + assert_eq!( + Address::new(0xff04, 0, 0, 0, 0, 0, 0, 1).scope(), + Scope::AdminLocal + ); + assert_eq!( + Address::new(0xff05, 0, 0, 0, 0, 0, 0, 1).scope(), + Scope::SiteLocal + ); + assert_eq!( + Address::new(0xff08, 0, 0, 0, 0, 0, 0, 1).scope(), + Scope::OrganizationLocal + ); + assert_eq!( + Address::new(0xff0e, 0, 0, 0, 0, 0, 0, 1).scope(), + Scope::Global + ); + + assert_eq!(Address::LINK_LOCAL_ALL_NODES.scope(), Scope::LinkLocal); + + // For source address selection, unicast addresses also have a scope: + assert_eq!(LINK_LOCAL_ADDR.scope(), Scope::LinkLocal); + assert_eq!(GLOBAL_UNICAST_ADDR.scope(), Scope::Global); + assert_eq!(UNIQUE_LOCAL_ADDR.scope(), Scope::Global); + } + static REPR_PACKET_BYTES: [u8; 52] = [ 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, From 434f7eb37fa5ad29464b9dc43ba2299dfd3551a8 Mon Sep 17 00:00:00 2001 From: Thibaut Vandervelden Date: Mon, 4 Dec 2023 17:51:02 +0100 Subject: [PATCH 5/5] ip: pass address by ref for get_source_address_ip --- src/iface/interface/mod.rs | 20 +++++++------- src/iface/interface/tests/ipv6.rs | 43 ++++++++++++++++++++++++++----- src/socket/dns.rs | 2 +- src/socket/icmp.rs | 4 +-- src/socket/tcp.rs | 2 +- src/socket/udp.rs | 2 +- 6 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 48edbb555..bd4e071e8 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -468,21 +468,21 @@ impl Interface { /// the first IPv4 address from the list of addresses. For IPv6, the address is based on the /// destination address and uses RFC6724 for selecting the source address. pub fn get_source_address(&self, dst_addr: &IpAddress) -> Option { - self.inner.get_source_address(*dst_addr) + self.inner.get_source_address(dst_addr) } /// Get an address from the interface that could be used as source address. This is the first /// IPv4 address from the list of addresses in the interface. #[cfg(feature = "proto-ipv4")] pub fn get_source_address_ipv4(&self, dst_addr: &Ipv4Address) -> Option { - self.inner.get_source_address_ipv4(*dst_addr) + self.inner.get_source_address_ipv4(dst_addr) } /// Get an address from the interface that could be used as source address. The selection is /// based on RFC6724. #[cfg(feature = "proto-ipv6")] pub fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option { - self.inner.get_source_address_ipv6(*dst_addr) + self.inner.get_source_address_ipv6(dst_addr) } /// Update the IP addresses of the interface. @@ -948,7 +948,7 @@ impl InterfaceInner { } #[allow(unused)] // unused depending on which sockets are enabled - pub(crate) fn get_source_address(&self, dst_addr: IpAddress) -> Option { + pub(crate) fn get_source_address(&self, dst_addr: &IpAddress) -> Option { match dst_addr { #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(addr) => self.get_source_address_ipv4(addr).map(|a| a.into()), @@ -959,7 +959,7 @@ impl InterfaceInner { #[cfg(feature = "proto-ipv4")] #[allow(unused)] - pub(crate) fn get_source_address_ipv4(&self, _dst_addr: Ipv4Address) -> Option { + pub(crate) fn get_source_address_ipv4(&self, _dst_addr: &Ipv4Address) -> Option { for cidr in self.ip_addrs.iter() { #[allow(irrefutable_let_patterns)] // if only ipv4 is enabled if let IpCidr::Ipv4(cidr) = cidr { @@ -971,7 +971,7 @@ impl InterfaceInner { #[cfg(feature = "proto-ipv6")] #[allow(unused)] - pub(crate) fn get_source_address_ipv6(&self, dst_addr: Ipv6Address) -> Option { + pub(crate) fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option { // RFC 6724 describes how to select the correct source address depending on the destination // address. @@ -1029,7 +1029,7 @@ impl InterfaceInner { #[cfg(feature = "proto-ipv6")] IpCidr::Ipv6(a) => Some(a), }) - .find(|a| is_candidate_source_address(&dst_addr, &a.address())) + .find(|a| is_candidate_source_address(dst_addr, &a.address())) .unwrap(); for addr in self.ip_addrs.iter().filter_map(|a| match a { @@ -1038,12 +1038,12 @@ impl InterfaceInner { #[cfg(feature = "proto-ipv6")] IpCidr::Ipv6(a) => Some(a), }) { - if !is_candidate_source_address(&dst_addr, &addr.address()) { + if !is_candidate_source_address(dst_addr, &addr.address()) { continue; } // Rule 1: prefer the address that is the same as the output destination address. - if candidate.address() != dst_addr && addr.address() == dst_addr { + if candidate.address() != *dst_addr && addr.address() == *dst_addr { candidate = addr; } @@ -1063,7 +1063,7 @@ impl InterfaceInner { // Rule 6: prefer matching label (TODO) // Rule 7: prefer temporary addresses (TODO) // Rule 8: use longest matching prefix - if common_prefix_length(candidate, &dst_addr) < common_prefix_length(addr, &dst_addr) { + if common_prefix_length(candidate, dst_addr) < common_prefix_length(addr, dst_addr) { candidate = addr; } } diff --git a/src/iface/interface/tests/ipv6.rs b/src/iface/interface/tests/ipv6.rs index b90184889..4867711b5 100644 --- a/src/iface/interface/tests/ipv6.rs +++ b/src/iface/interface/tests/ipv6.rs @@ -797,33 +797,62 @@ fn get_source_address() { Ipv6Address::new(0x2001, 0x0db9, 0x0003, 0, 0, 0, 0, 2); assert_eq!( - iface.inner.get_source_address_ipv6(LINK_LOCAL_ADDR), + iface.inner.get_source_address_ipv6(&LINK_LOCAL_ADDR), Some(OWN_LINK_LOCAL_ADDR) ); assert_eq!( - iface.inner.get_source_address_ipv6(UNIQUE_LOCAL_ADDR1), + iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR1), Some(OWN_UNIQUE_LOCAL_ADDR1) ); assert_eq!( - iface.inner.get_source_address_ipv6(UNIQUE_LOCAL_ADDR2), + iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR2), Some(OWN_UNIQUE_LOCAL_ADDR2) ); assert_eq!( - iface.inner.get_source_address_ipv6(UNIQUE_LOCAL_ADDR3), + iface.inner.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR3), Some(OWN_UNIQUE_LOCAL_ADDR1) ); assert_eq!( iface .inner - .get_source_address_ipv6(Ipv6Address::LINK_LOCAL_ALL_NODES), + .get_source_address_ipv6(&Ipv6Address::LINK_LOCAL_ALL_NODES), Some(OWN_LINK_LOCAL_ADDR) ); assert_eq!( - iface.inner.get_source_address_ipv6(GLOBAL_UNICAST_ADDR1), + iface.inner.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR1), Some(OWN_GLOBAL_UNICAST_ADDR1) ); assert_eq!( - iface.inner.get_source_address_ipv6(GLOBAL_UNICAST_ADDR2), + iface.inner.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR2), + Some(OWN_GLOBAL_UNICAST_ADDR1) + ); + + assert_eq!( + iface.get_source_address_ipv6(&LINK_LOCAL_ADDR), + Some(OWN_LINK_LOCAL_ADDR) + ); + assert_eq!( + iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR1), + Some(OWN_UNIQUE_LOCAL_ADDR1) + ); + assert_eq!( + iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR2), + Some(OWN_UNIQUE_LOCAL_ADDR2) + ); + assert_eq!( + iface.get_source_address_ipv6(&UNIQUE_LOCAL_ADDR3), + Some(OWN_UNIQUE_LOCAL_ADDR1) + ); + assert_eq!( + iface.get_source_address_ipv6(&Ipv6Address::LINK_LOCAL_ALL_NODES), + Some(OWN_LINK_LOCAL_ADDR) + ); + assert_eq!( + iface.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR1), + Some(OWN_GLOBAL_UNICAST_ADDR1) + ); + assert_eq!( + iface.get_source_address_ipv6(&GLOBAL_UNICAST_ADDR2), Some(OWN_GLOBAL_UNICAST_ADDR1) ); } diff --git a/src/socket/dns.rs b/src/socket/dns.rs index ca267b055..610d5c6b4 100644 --- a/src/socket/dns.rs +++ b/src/socket/dns.rs @@ -610,7 +610,7 @@ impl<'a> Socket<'a> { }; let dst_addr = servers[pq.server_idx]; - let src_addr = cx.get_source_address(dst_addr).unwrap(); // TODO remove unwrap + let src_addr = cx.get_source_address(&dst_addr).unwrap(); // TODO remove unwrap let ip_repr = IpRepr::new( src_addr, dst_addr, diff --git a/src/socket/icmp.rs b/src/socket/icmp.rs index 0c4516070..89d3f8efe 100644 --- a/src/socket/icmp.rs +++ b/src/socket/icmp.rs @@ -539,7 +539,7 @@ impl<'a> Socket<'a> { match *remote_endpoint { #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(dst_addr) => { - let src_addr = match cx.get_source_address_ipv4(dst_addr) { + let src_addr = match cx.get_source_address_ipv4(&dst_addr) { Some(addr) => addr, None => { net_trace!( @@ -571,7 +571,7 @@ impl<'a> Socket<'a> { } #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(dst_addr) => { - let src_addr = match cx.get_source_address_ipv6(dst_addr) { + let src_addr = match cx.get_source_address_ipv6(&dst_addr) { Some(addr) => addr, None => { net_trace!( diff --git a/src/socket/tcp.rs b/src/socket/tcp.rs index 8f41196f6..67172e931 100644 --- a/src/socket/tcp.rs +++ b/src/socket/tcp.rs @@ -851,7 +851,7 @@ impl<'a> Socket<'a> { addr } None => cx - .get_source_address(remote_endpoint.addr) + .get_source_address(&remote_endpoint.addr) .ok_or(ConnectError::Unaddressable)?, }, port: local_endpoint.port, diff --git a/src/socket/udp.rs b/src/socket/udp.rs index 9eb2bcf59..82eebf26d 100644 --- a/src/socket/udp.rs +++ b/src/socket/udp.rs @@ -519,7 +519,7 @@ impl<'a> Socket<'a> { let res = self.tx_buffer.dequeue_with(|packet_meta, payload_buf| { let src_addr = match endpoint.addr { Some(addr) => addr, - None => match cx.get_source_address(packet_meta.endpoint.addr) { + None => match cx.get_source_address(&packet_meta.endpoint.addr) { Some(addr) => addr, None => { net_trace!(