Skip to content

Commit df94104

Browse files
committed
chore(ipv6): all logic into ipv6.rs
Signed-off-by: Thibaut Vandervelden <[email protected]>
1 parent 83900c6 commit df94104

File tree

2 files changed

+136
-144
lines changed

2 files changed

+136
-144
lines changed

src/iface/interface/ipv6.rs

Lines changed: 136 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
use super::*;
22

3-
#[cfg(feature = "socket-icmp")]
4-
use crate::socket::icmp;
5-
use crate::socket::AnySocket;
6-
7-
use crate::phy::PacketMeta;
8-
use crate::wire::*;
9-
103
/// Enum used for the process_hopbyhop function. In some cases, when discarding a packet, an ICMMP
114
/// parameter problem message needs to be transmitted to the source of the address. In other cases,
125
/// the processing of the IP packet can continue.
@@ -26,6 +19,133 @@ impl Default for HopByHopResponse<'_> {
2619
}
2720

2821
impl InterfaceInner {
22+
/// Return the IPv6 address that is a candidate source address for the given destination
23+
/// address, based on RFC 6724.
24+
#[allow(unused)]
25+
pub(crate) fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option<Ipv6Address> {
26+
// See RFC 6724 Section 4: Candidate source address
27+
fn is_candidate_source_address(dst_addr: &Ipv6Address, src_addr: &Ipv6Address) -> bool {
28+
// For all multicast and link-local destination addresses, the candidate address MUST
29+
// only be an address from the same link.
30+
if dst_addr.is_link_local() && !src_addr.is_link_local() {
31+
return false;
32+
}
33+
34+
if dst_addr.is_multicast()
35+
&& matches!(dst_addr.scope(), Ipv6AddressScope::LinkLocal)
36+
&& src_addr.is_multicast()
37+
&& !matches!(src_addr.scope(), Ipv6AddressScope::LinkLocal)
38+
{
39+
return false;
40+
}
41+
42+
// Loopback addresses and multicast address can not be in the candidate source address
43+
// list. Except when the destination multicast address has a link-local scope, then the
44+
// source address can also be link-local multicast.
45+
if src_addr.is_loopback() || src_addr.is_multicast() {
46+
return false;
47+
}
48+
49+
true
50+
}
51+
52+
// See RFC 6724 Section 2.2: Common Prefix Length
53+
fn common_prefix_length(dst_addr: &Ipv6Cidr, src_addr: &Ipv6Address) -> usize {
54+
let addr = dst_addr.address();
55+
let mut bits = 0;
56+
for (l, r) in addr.as_bytes().iter().zip(src_addr.as_bytes().iter()) {
57+
if l == r {
58+
bits += 8;
59+
} else {
60+
bits += (l ^ r).leading_zeros();
61+
break;
62+
}
63+
}
64+
65+
bits = bits.min(dst_addr.prefix_len() as u32);
66+
67+
bits as usize
68+
}
69+
70+
// Get the first address that is a candidate address.
71+
let mut candidate = self
72+
.ip_addrs
73+
.iter()
74+
.filter_map(|a| match a {
75+
#[cfg(feature = "proto-ipv4")]
76+
IpCidr::Ipv4(_) => None,
77+
#[cfg(feature = "proto-ipv6")]
78+
IpCidr::Ipv6(a) => Some(a),
79+
})
80+
.find(|a| is_candidate_source_address(dst_addr, &a.address()))
81+
.unwrap();
82+
83+
for addr in self.ip_addrs.iter().filter_map(|a| match a {
84+
#[cfg(feature = "proto-ipv4")]
85+
IpCidr::Ipv4(_) => None,
86+
#[cfg(feature = "proto-ipv6")]
87+
IpCidr::Ipv6(a) => Some(a),
88+
}) {
89+
if !is_candidate_source_address(dst_addr, &addr.address()) {
90+
continue;
91+
}
92+
93+
// Rule 1: prefer the address that is the same as the output destination address.
94+
if candidate.address() != *dst_addr && addr.address() == *dst_addr {
95+
candidate = addr;
96+
}
97+
98+
// Rule 2: prefer appropriate scope.
99+
if (candidate.address().scope() as u8) < (addr.address().scope() as u8) {
100+
if (candidate.address().scope() as u8) < (dst_addr.scope() as u8) {
101+
candidate = addr;
102+
}
103+
} else if (addr.address().scope() as u8) > (dst_addr.scope() as u8) {
104+
candidate = addr;
105+
}
106+
107+
// Rule 3: avoid deprecated addresses (TODO)
108+
// Rule 4: prefer home addresses (TODO)
109+
// Rule 5: prefer outgoing interfaces (TODO)
110+
// Rule 5.5: prefer addresses in a prefix advertises by the next-hop (TODO).
111+
// Rule 6: prefer matching label (TODO)
112+
// Rule 7: prefer temporary addresses (TODO)
113+
// Rule 8: use longest matching prefix
114+
if common_prefix_length(candidate, dst_addr) < common_prefix_length(addr, dst_addr) {
115+
candidate = addr;
116+
}
117+
}
118+
119+
Some(candidate.address())
120+
}
121+
122+
/// Determine if the given `Ipv6Address` is the solicited node
123+
/// multicast address for a IPv6 addresses assigned to the interface.
124+
/// See [RFC 4291 § 2.7.1] for more details.
125+
///
126+
/// [RFC 4291 § 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1
127+
pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool {
128+
self.ip_addrs.iter().any(|cidr| {
129+
match *cidr {
130+
IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK => {
131+
// Take the lower order 24 bits of the IPv6 address and
132+
// append those bits to FF02:0:0:0:0:1:FF00::/104.
133+
addr.as_bytes()[14..] == cidr.address().as_bytes()[14..]
134+
}
135+
_ => false,
136+
}
137+
})
138+
}
139+
140+
/// Get the first IPv6 address if present.
141+
pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
142+
self.ip_addrs.iter().find_map(|addr| match *addr {
143+
IpCidr::Ipv6(cidr) => Some(cidr.address()),
144+
#[allow(unreachable_patterns)]
145+
_ => None,
146+
})
147+
}
148+
29149
pub(super) fn process_ipv6<'frame>(
30150
&mut self,
31151
sockets: &mut SocketSet,
@@ -191,13 +311,16 @@ impl InterfaceInner {
191311
let mut handled_by_icmp_socket = false;
192312

193313
#[cfg(feature = "socket-icmp")]
194-
for icmp_socket in _sockets
195-
.items_mut()
196-
.filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket))
197314
{
198-
if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) {
199-
icmp_socket.process(self, &ip_repr, &icmp_repr.into());
200-
handled_by_icmp_socket = true;
315+
use crate::socket::icmp::Socket as IcmpSocket;
316+
for icmp_socket in _sockets
317+
.items_mut()
318+
.filter_map(|i| IcmpSocket::downcast_mut(&mut i.socket))
319+
{
320+
if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) {
321+
icmp_socket.process(self, &ip_repr, &icmp_repr.into());
322+
handled_by_icmp_socket = true;
323+
}
201324
}
202325
}
203326

src/iface/interface/mod.rs

Lines changed: 0 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -732,108 +732,6 @@ impl InterfaceInner {
732732
}
733733
}
734734

735-
#[cfg(feature = "proto-ipv6")]
736-
#[allow(unused)]
737-
pub(crate) fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option<Ipv6Address> {
738-
// RFC 6724 describes how to select the correct source address depending on the destination
739-
// address.
740-
741-
// See RFC 6724 Section 4: Candidate source address
742-
fn is_candidate_source_address(dst_addr: &Ipv6Address, src_addr: &Ipv6Address) -> bool {
743-
// For all multicast and link-local destination addresses, the candidate address MUST
744-
// only be an address from the same link.
745-
if dst_addr.is_link_local() && !src_addr.is_link_local() {
746-
return false;
747-
}
748-
749-
if dst_addr.is_multicast()
750-
&& matches!(dst_addr.scope(), Ipv6AddressScope::LinkLocal)
751-
&& src_addr.is_multicast()
752-
&& !matches!(src_addr.scope(), Ipv6AddressScope::LinkLocal)
753-
{
754-
return false;
755-
}
756-
757-
// Loopback addresses and multicast address can not be in the candidate source address
758-
// list. Except when the destination multicast address has a link-local scope, then the
759-
// source address can also be link-local multicast.
760-
if src_addr.is_loopback() || src_addr.is_multicast() {
761-
return false;
762-
}
763-
764-
true
765-
}
766-
767-
// See RFC 6724 Section 2.2: Common Prefix Length
768-
fn common_prefix_length(dst_addr: &Ipv6Cidr, src_addr: &Ipv6Address) -> usize {
769-
let addr = dst_addr.address();
770-
let mut bits = 0;
771-
for (l, r) in addr.as_bytes().iter().zip(src_addr.as_bytes().iter()) {
772-
if l == r {
773-
bits += 8;
774-
} else {
775-
bits += (l ^ r).leading_zeros();
776-
break;
777-
}
778-
}
779-
780-
bits = bits.min(dst_addr.prefix_len() as u32);
781-
782-
bits as usize
783-
}
784-
785-
// Get the first address that is a candidate address.
786-
let mut candidate = self
787-
.ip_addrs
788-
.iter()
789-
.filter_map(|a| match a {
790-
#[cfg(feature = "proto-ipv4")]
791-
IpCidr::Ipv4(_) => None,
792-
#[cfg(feature = "proto-ipv6")]
793-
IpCidr::Ipv6(a) => Some(a),
794-
})
795-
.find(|a| is_candidate_source_address(dst_addr, &a.address()))
796-
.unwrap();
797-
798-
for addr in self.ip_addrs.iter().filter_map(|a| match a {
799-
#[cfg(feature = "proto-ipv4")]
800-
IpCidr::Ipv4(_) => None,
801-
#[cfg(feature = "proto-ipv6")]
802-
IpCidr::Ipv6(a) => Some(a),
803-
}) {
804-
if !is_candidate_source_address(dst_addr, &addr.address()) {
805-
continue;
806-
}
807-
808-
// Rule 1: prefer the address that is the same as the output destination address.
809-
if candidate.address() != *dst_addr && addr.address() == *dst_addr {
810-
candidate = addr;
811-
}
812-
813-
// Rule 2: prefer appropriate scope.
814-
if (candidate.address().scope() as u8) < (addr.address().scope() as u8) {
815-
if (candidate.address().scope() as u8) < (dst_addr.scope() as u8) {
816-
candidate = addr;
817-
}
818-
} else if (addr.address().scope() as u8) > (dst_addr.scope() as u8) {
819-
candidate = addr;
820-
}
821-
822-
// Rule 3: avoid deprecated addresses (TODO)
823-
// Rule 4: prefer home addresses (TODO)
824-
// Rule 5: prefer outgoing interfaces (TODO)
825-
// Rule 5.5: prefer addresses in a prefix advertises by the next-hop (TODO).
826-
// Rule 6: prefer matching label (TODO)
827-
// Rule 7: prefer temporary addresses (TODO)
828-
// Rule 8: use longest matching prefix
829-
if common_prefix_length(candidate, dst_addr) < common_prefix_length(addr, dst_addr) {
830-
candidate = addr;
831-
}
832-
}
833-
834-
Some(candidate.address())
835-
}
836-
837735
#[cfg(test)]
838736
#[allow(unused)] // unused depending on which sockets are enabled
839737
pub(crate) fn set_now(&mut self, now: Instant) {
@@ -855,41 +753,12 @@ impl InterfaceInner {
855753
}
856754
}
857755

858-
/// Determine if the given `Ipv6Address` is the solicited node
859-
/// multicast address for a IPv6 addresses assigned to the interface.
860-
/// See [RFC 4291 § 2.7.1] for more details.
861-
///
862-
/// [RFC 4291 § 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1
863-
#[cfg(feature = "proto-ipv6")]
864-
pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool {
865-
self.ip_addrs.iter().any(|cidr| {
866-
match *cidr {
867-
IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK => {
868-
// Take the lower order 24 bits of the IPv6 address and
869-
// append those bits to FF02:0:0:0:0:1:FF00::/104.
870-
addr.as_bytes()[14..] == cidr.address().as_bytes()[14..]
871-
}
872-
_ => false,
873-
}
874-
})
875-
}
876-
877756
/// Check whether the interface has the given IP address assigned.
878757
fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
879758
let addr = addr.into();
880759
self.ip_addrs.iter().any(|probe| probe.address() == addr)
881760
}
882761

883-
/// Get the first IPv6 address if present.
884-
#[cfg(feature = "proto-ipv6")]
885-
pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
886-
self.ip_addrs.iter().find_map(|addr| match *addr {
887-
IpCidr::Ipv6(cidr) => Some(cidr.address()),
888-
#[allow(unreachable_patterns)]
889-
_ => None,
890-
})
891-
}
892-
893762
/// Check whether the interface listens to given destination multicast IP address.
894763
///
895764
/// If built without feature `proto-igmp` this function will

0 commit comments

Comments
 (0)