Skip to content

Commit f08e687

Browse files
authored
Merge pull request #861 from thvdveld/check-ipv6-address
Check IPv6 address after processing HBH
2 parents 3d3fea4 + 1001586 commit f08e687

File tree

6 files changed

+295
-86
lines changed

6 files changed

+295
-86
lines changed

src/iface/interface/igmp.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -208,21 +208,6 @@ impl Interface {
208208
}
209209

210210
impl InterfaceInner {
211-
/// Check whether the interface listens to given destination multicast IP address.
212-
///
213-
/// If built without feature `proto-igmp` this function will
214-
/// always return `false`.
215-
pub fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
216-
match addr.into() {
217-
IpAddress::Ipv4(key) => {
218-
key == Ipv4Address::MULTICAST_ALL_SYSTEMS
219-
|| self.ipv4_multicast_groups.get(&key).is_some()
220-
}
221-
#[allow(unreachable_patterns)]
222-
_ => false,
223-
}
224-
}
225-
226211
/// Host duties of the **IGMPv2** protocol.
227212
///
228213
/// Sets up `igmp_report_state` for responding to IGMP general/specific membership queries.

src/iface/interface/ipv6.rs

Lines changed: 114 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ use crate::socket::AnySocket;
77
use crate::phy::PacketMeta;
88
use crate::wire::*;
99

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

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

2760
#[cfg(feature = "socket-raw")]
2861
let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload);
@@ -33,15 +66,71 @@ impl InterfaceInner {
3366
sockets,
3467
meta,
3568
ipv6_repr,
36-
ipv6_repr.next_header,
69+
next_header,
3770
handled_by_raw_socket,
3871
ip_payload,
3972
)
4073
}
4174

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

80-
IpProtocol::HopByHop => {
81-
self.process_hopbyhop(sockets, meta, ipv6_repr, handled_by_raw_socket, ip_payload)
82-
}
83-
84169
#[cfg(feature = "socket-raw")]
85170
_ if handled_by_raw_socket => None,
86171

@@ -238,70 +323,33 @@ impl InterfaceInner {
238323
}
239324
}
240325

241-
pub(super) fn process_hopbyhop<'frame>(
242-
&mut self,
243-
sockets: &mut SocketSet,
244-
meta: PacketMeta,
245-
ipv6_repr: Ipv6Repr,
246-
handled_by_raw_socket: bool,
247-
ip_payload: &'frame [u8],
248-
) -> Option<Packet<'frame>> {
249-
let ext_hdr = check!(Ipv6ExtHeader::new_checked(ip_payload));
250-
let ext_repr = check!(Ipv6ExtHeaderRepr::parse(&ext_hdr));
251-
let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ext_repr.data));
252-
let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_hdr));
253-
254-
for opt_repr in &hbh_repr.options {
255-
match opt_repr {
256-
Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (),
257-
#[cfg(feature = "proto-rpl")]
258-
Ipv6OptionRepr::Rpl(_) => {}
259-
260-
Ipv6OptionRepr::Unknown { type_, .. } => {
261-
match Ipv6OptionFailureType::from(*type_) {
262-
Ipv6OptionFailureType::Skip => (),
263-
Ipv6OptionFailureType::Discard => {
264-
return None;
265-
}
266-
_ => {
267-
// FIXME(dlrobertson): Send an ICMPv6 parameter problem message
268-
// here.
269-
return None;
270-
}
271-
}
272-
}
273-
}
274-
}
275-
self.process_nxt_hdr(
276-
sockets,
277-
meta,
278-
ipv6_repr,
279-
ext_repr.next_header,
280-
handled_by_raw_socket,
281-
&ip_payload[ext_repr.header_len() + ext_repr.data.len()..],
282-
)
283-
}
284-
285326
pub(super) fn icmpv6_reply<'frame, 'icmp: 'frame>(
286327
&self,
287328
ipv6_repr: Ipv6Repr,
288329
icmp_repr: Icmpv6Repr<'icmp>,
289330
) -> Option<Packet<'frame>> {
290-
if ipv6_repr.dst_addr.is_unicast() {
291-
let ipv6_reply_repr = Ipv6Repr {
292-
src_addr: ipv6_repr.dst_addr,
293-
dst_addr: ipv6_repr.src_addr,
294-
next_header: IpProtocol::Icmpv6,
295-
payload_len: icmp_repr.buffer_len(),
296-
hop_limit: 64,
297-
};
298-
Some(Packet::new_ipv6(
299-
ipv6_reply_repr,
300-
IpPayload::Icmpv6(icmp_repr),
301-
))
331+
let src_addr = ipv6_repr.dst_addr;
332+
let dst_addr = ipv6_repr.src_addr;
333+
334+
let src_addr = if src_addr.is_unicast() {
335+
src_addr
336+
} else if let Some(addr) = self.get_source_address_ipv6(&dst_addr) {
337+
addr
302338
} else {
303-
// Do not send any ICMP replies to a broadcast destination address.
304-
None
305-
}
339+
net_debug!("no suitable source address found");
340+
return None;
341+
};
342+
343+
let ipv6_reply_repr = Ipv6Repr {
344+
src_addr,
345+
dst_addr,
346+
next_header: IpProtocol::Icmpv6,
347+
payload_len: icmp_repr.buffer_len(),
348+
hop_limit: 64,
349+
};
350+
Some(Packet::new_ipv6(
351+
ipv6_reply_repr,
352+
IpPayload::Icmpv6(icmp_repr),
353+
))
306354
}
307355
}

src/iface/interface/mod.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,9 +1007,26 @@ impl InterfaceInner {
10071007
})
10081008
}
10091009

1010-
#[cfg(not(feature = "proto-igmp"))]
1010+
/// Check whether the interface listens to given destination multicast IP address.
1011+
///
1012+
/// If built without feature `proto-igmp` this function will
1013+
/// always return `false` when using IPv4.
10111014
fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
1012-
false
1015+
match addr.into() {
1016+
#[cfg(feature = "proto-igmp")]
1017+
IpAddress::Ipv4(key) => {
1018+
key == Ipv4Address::MULTICAST_ALL_SYSTEMS
1019+
|| self.ipv4_multicast_groups.get(&key).is_some()
1020+
}
1021+
#[cfg(feature = "proto-ipv6")]
1022+
IpAddress::Ipv6(Ipv6Address::LINK_LOCAL_ALL_NODES) => true,
1023+
#[cfg(feature = "proto-rpl")]
1024+
IpAddress::Ipv6(Ipv6Address::LINK_LOCAL_ALL_RPL_NODES) => true,
1025+
#[cfg(feature = "proto-ipv6")]
1026+
IpAddress::Ipv6(addr) => self.has_solicited_node(addr),
1027+
#[allow(unreachable_patterns)]
1028+
_ => false,
1029+
}
10131030
}
10141031

10151032
#[cfg(feature = "medium-ip")]

0 commit comments

Comments
 (0)