Skip to content

Commit ef6a552

Browse files
committed
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.
1 parent 2ba9799 commit ef6a552

File tree

3 files changed

+251
-74
lines changed

3 files changed

+251
-74
lines changed

src/iface/interface/ipv6.rs

Lines changed: 105 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,24 @@ use crate::socket::AnySocket;
1111
use crate::phy::PacketMeta;
1212
use crate::wire::*;
1313

14+
/// Enum used for the process_hopbyhop function. In some cases, when discarding a packet, an ICMMP
15+
/// parameter problem message needs to be transmitted to the source of the address. In other cases,
16+
/// the processing of the IP packet can continue.
17+
#[allow(clippy::large_enum_variant)]
18+
enum HopByHopResponse<'frame> {
19+
/// Continue processing the IPv6 packet.
20+
Continue((IpProtocol, &'frame [u8])),
21+
/// Discard the packet and maybe send back an ICMPv6 packet.
22+
Discard(Option<IpPacket<'frame>>),
23+
}
24+
25+
// We implement `Default` such that we can use the check! macro.
26+
impl Default for HopByHopResponse<'_> {
27+
fn default() -> Self {
28+
Self::Discard(None)
29+
}
30+
}
31+
1432
impl InterfaceInner {
1533
pub(super) fn process_ipv6<'frame>(
1634
&mut self,
@@ -26,7 +44,19 @@ impl InterfaceInner {
2644
return None;
2745
}
2846

29-
let ip_payload = ipv6_packet.payload();
47+
let (next_header, ip_payload) = if ipv6_repr.next_header == IpProtocol::HopByHop {
48+
match self.process_hopbyhop(ipv6_repr, ipv6_packet.payload()) {
49+
HopByHopResponse::Discard(e) => return e,
50+
HopByHopResponse::Continue(next) => next,
51+
}
52+
} else {
53+
(ipv6_repr.next_header, ipv6_packet.payload())
54+
};
55+
56+
if !self.has_ip_addr(ipv6_repr.dst_addr) && !self.has_multicast_group(ipv6_repr.dst_addr) {
57+
net_trace!("packet IP address not for this interface");
58+
return None;
59+
}
3060

3161
#[cfg(feature = "socket-raw")]
3262
let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload);
@@ -37,15 +67,71 @@ impl InterfaceInner {
3767
sockets,
3868
meta,
3969
ipv6_repr,
40-
ipv6_repr.next_header,
70+
next_header,
4171
handled_by_raw_socket,
4272
ip_payload,
4373
)
4474
}
4575

76+
fn process_hopbyhop<'frame>(
77+
&mut self,
78+
ipv6_repr: Ipv6Repr,
79+
ip_payload: &'frame [u8],
80+
) -> HopByHopResponse<'frame> {
81+
let param_problem = || {
82+
let payload_len =
83+
icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
84+
self.icmpv6_reply(
85+
ipv6_repr,
86+
Icmpv6Repr::ParamProblem {
87+
reason: Icmpv6ParamProblem::UnrecognizedOption,
88+
pointer: ipv6_repr.buffer_len() as u32,
89+
header: ipv6_repr,
90+
data: &ip_payload[0..payload_len],
91+
},
92+
)
93+
};
94+
95+
let ext_hdr = check!(Ipv6ExtHeader::new_checked(ip_payload));
96+
let ext_repr = check!(Ipv6ExtHeaderRepr::parse(&ext_hdr));
97+
let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ext_repr.data));
98+
let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_hdr));
99+
100+
for opt_repr in &hbh_repr.options {
101+
match opt_repr {
102+
Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (),
103+
#[cfg(feature = "proto-rpl")]
104+
Ipv6OptionRepr::Rpl(_) => {}
105+
106+
Ipv6OptionRepr::Unknown { type_, .. } => {
107+
match Ipv6OptionFailureType::from(*type_) {
108+
Ipv6OptionFailureType::Skip => (),
109+
Ipv6OptionFailureType::Discard => {
110+
return HopByHopResponse::Discard(None);
111+
}
112+
Ipv6OptionFailureType::DiscardSendAll => {
113+
return HopByHopResponse::Discard(param_problem());
114+
}
115+
Ipv6OptionFailureType::DiscardSendUnicast
116+
if !ipv6_repr.dst_addr.is_multicast() =>
117+
{
118+
return HopByHopResponse::Discard(param_problem());
119+
}
120+
_ => unreachable!(),
121+
}
122+
}
123+
}
124+
}
125+
126+
HopByHopResponse::Continue((
127+
ext_repr.next_header,
128+
&ip_payload[ext_repr.header_len() + ext_repr.data.len()..],
129+
))
130+
}
131+
46132
/// Given the next header value forward the payload onto the correct process
47133
/// function.
48-
pub(super) fn process_nxt_hdr<'frame>(
134+
fn process_nxt_hdr<'frame>(
49135
&mut self,
50136
sockets: &mut SocketSet,
51137
meta: PacketMeta,
@@ -81,10 +167,6 @@ impl InterfaceInner {
81167
#[cfg(feature = "socket-tcp")]
82168
IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload),
83169

84-
IpProtocol::HopByHop => {
85-
self.process_hopbyhop(sockets, meta, ipv6_repr, handled_by_raw_socket, ip_payload)
86-
}
87-
88170
#[cfg(feature = "socket-raw")]
89171
_ if handled_by_raw_socket => None,
90172

@@ -235,70 +317,27 @@ impl InterfaceInner {
235317
}
236318
}
237319

238-
pub(super) fn process_hopbyhop<'frame>(
239-
&mut self,
240-
sockets: &mut SocketSet,
241-
meta: PacketMeta,
242-
ipv6_repr: Ipv6Repr,
243-
handled_by_raw_socket: bool,
244-
ip_payload: &'frame [u8],
245-
) -> Option<IpPacket<'frame>> {
246-
let ext_hdr = check!(Ipv6ExtHeader::new_checked(ip_payload));
247-
let ext_repr = check!(Ipv6ExtHeaderRepr::parse(&ext_hdr));
248-
let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ext_repr.data));
249-
let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_hdr));
250-
251-
for opt_repr in &hbh_repr.options {
252-
match opt_repr {
253-
Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (),
254-
#[cfg(feature = "proto-rpl")]
255-
Ipv6OptionRepr::Rpl(_) => {}
256-
257-
Ipv6OptionRepr::Unknown { type_, .. } => {
258-
match Ipv6OptionFailureType::from(*type_) {
259-
Ipv6OptionFailureType::Skip => (),
260-
Ipv6OptionFailureType::Discard => {
261-
return None;
262-
}
263-
_ => {
264-
// FIXME(dlrobertson): Send an ICMPv6 parameter problem message
265-
// here.
266-
return None;
267-
}
268-
}
269-
}
270-
}
271-
}
272-
self.process_nxt_hdr(
273-
sockets,
274-
meta,
275-
ipv6_repr,
276-
ext_repr.next_header,
277-
handled_by_raw_socket,
278-
&ip_payload[ext_repr.header_len() + ext_repr.data.len()..],
279-
)
280-
}
281-
282320
pub(super) fn icmpv6_reply<'frame, 'icmp: 'frame>(
283321
&self,
284322
ipv6_repr: Ipv6Repr,
285323
icmp_repr: Icmpv6Repr<'icmp>,
286324
) -> Option<IpPacket<'frame>> {
287-
if ipv6_repr.dst_addr.is_unicast() {
288-
let ipv6_reply_repr = Ipv6Repr {
289-
src_addr: ipv6_repr.dst_addr,
290-
dst_addr: ipv6_repr.src_addr,
291-
next_header: IpProtocol::Icmpv6,
292-
payload_len: icmp_repr.buffer_len(),
293-
hop_limit: 64,
294-
};
295-
Some(IpPacket::new_ipv6(
296-
ipv6_reply_repr,
297-
IpPayload::Icmpv6(icmp_repr),
298-
))
325+
let src_addr = if ipv6_repr.dst_addr.is_unicast() {
326+
ipv6_repr.dst_addr
299327
} else {
300-
// Do not send any ICMP replies to a broadcast destination address.
301-
None
302-
}
328+
self.ipv6_addr().unwrap()
329+
};
330+
331+
let ipv6_reply_repr = Ipv6Repr {
332+
src_addr,
333+
dst_addr: ipv6_repr.src_addr,
334+
next_header: IpProtocol::Icmpv6,
335+
payload_len: icmp_repr.buffer_len(),
336+
hop_limit: 64,
337+
};
338+
Some(IpPacket::new_ipv6(
339+
ipv6_reply_repr,
340+
IpPayload::Icmpv6(icmp_repr),
341+
))
303342
}
304343
}

src/iface/interface/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,11 +1071,7 @@ impl InterfaceInner {
10711071
#[cfg(feature = "proto-rpl")]
10721072
IpAddress::Ipv6(Ipv6Address::LINK_LOCAL_ALL_RPL_NODES) => true,
10731073
#[cfg(feature = "proto-ipv6")]
1074-
IpAddress::Ipv6(addr) => self.ip_addrs.iter().any(|a| match a.address() {
1075-
IpAddress::Ipv6(a) => a.solicited_node() == addr,
1076-
#[allow(unreachable_patterns)]
1077-
_ => false,
1078-
}),
1074+
IpAddress::Ipv6(addr) => self.has_solicited_node(addr),
10791075
#[allow(unreachable_patterns)]
10801076
_ => false,
10811077
}

0 commit comments

Comments
 (0)