1use super::*;
2
3#[allow(clippy::large_enum_variant)]
7enum HopByHopResponse<'frame> {
8 Continue((IpProtocol, &'frame [u8])),
10 Discard(Option<Packet<'frame>>),
12}
13
14impl Default for HopByHopResponse<'_> {
16 fn default() -> Self {
17 Self::Discard(None)
18 }
19}
20
21impl InterfaceInner {
22 #[allow(unused)]
28 pub(crate) fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Ipv6Address {
29 assert!(!dst_addr.is_unspecified());
30
31 fn is_candidate_source_address(dst_addr: &Ipv6Address, src_addr: &Ipv6Address) -> bool {
33 if dst_addr.is_link_local() && !src_addr.is_link_local() {
36 return false;
37 }
38
39 if dst_addr.is_multicast()
40 && matches!(dst_addr.x_multicast_scope(), Ipv6MulticastScope::LinkLocal)
41 && src_addr.is_multicast()
42 && !matches!(src_addr.x_multicast_scope(), Ipv6MulticastScope::LinkLocal)
43 {
44 return false;
45 }
46
47 if src_addr.is_unspecified() || src_addr.is_multicast() {
51 return false;
52 }
53
54 true
55 }
56
57 fn common_prefix_length(dst_addr: &Ipv6Cidr, src_addr: &Ipv6Address) -> usize {
59 let addr = dst_addr.address();
60 let mut bits = 0;
61 for (l, r) in addr.octets().iter().zip(src_addr.octets().iter()) {
62 if l == r {
63 bits += 8;
64 } else {
65 bits += (l ^ r).leading_zeros();
66 break;
67 }
68 }
69
70 bits = bits.min(dst_addr.prefix_len() as u32);
71
72 bits as usize
73 }
74
75 if dst_addr.is_loopback()
78 || self
79 .ip_addrs
80 .iter()
81 .filter(|a| matches!(a, IpCidr::Ipv6(_)))
82 .count()
83 == 0
84 {
85 return Ipv6Address::LOCALHOST;
86 }
87
88 let mut candidate = self
89 .ip_addrs
90 .iter()
91 .find_map(|a| match a {
92 #[cfg(feature = "proto-ipv4")]
93 IpCidr::Ipv4(_) => None,
94 IpCidr::Ipv6(a) => Some(a),
95 })
96 .unwrap(); for addr in self.ip_addrs.iter().filter_map(|a| match a {
99 #[cfg(feature = "proto-ipv4")]
100 IpCidr::Ipv4(_) => None,
101 #[cfg(feature = "proto-ipv6")]
102 IpCidr::Ipv6(a) => Some(a),
103 }) {
104 if !is_candidate_source_address(dst_addr, &addr.address()) {
105 continue;
106 }
107
108 if candidate.address() != *dst_addr && addr.address() == *dst_addr {
110 candidate = addr;
111 }
112
113 if (candidate.address().x_multicast_scope() as u8)
115 < (addr.address().x_multicast_scope() as u8)
116 {
117 if (candidate.address().x_multicast_scope() as u8)
118 < (dst_addr.x_multicast_scope() as u8)
119 {
120 candidate = addr;
121 }
122 } else if (addr.address().x_multicast_scope() as u8)
123 > (dst_addr.x_multicast_scope() as u8)
124 {
125 candidate = addr;
126 }
127
128 if common_prefix_length(candidate, dst_addr) < common_prefix_length(addr, dst_addr) {
136 candidate = addr;
137 }
138 }
139
140 candidate.address()
141 }
142
143 pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool {
149 self.ip_addrs.iter().any(|cidr| {
150 match *cidr {
151 IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOCALHOST => {
152 addr.octets()[14..] == cidr.address().octets()[14..]
155 }
156 _ => false,
157 }
158 })
159 }
160
161 pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
163 self.ip_addrs.iter().find_map(|addr| match *addr {
164 IpCidr::Ipv6(cidr) => Some(cidr.address()),
165 #[allow(unreachable_patterns)]
166 _ => None,
167 })
168 }
169
170 fn link_local_ipv6_address(&self) -> Option<Ipv6Address> {
172 self.ip_addrs.iter().find_map(|addr| match *addr {
173 #[cfg(feature = "proto-ipv4")]
174 IpCidr::Ipv4(_) => None,
175 #[cfg(feature = "proto-ipv6")]
176 IpCidr::Ipv6(cidr) => {
177 let addr = cidr.address();
178 if addr.is_link_local() {
179 Some(addr)
180 } else {
181 None
182 }
183 }
184 })
185 }
186
187 pub(super) fn process_ipv6<'frame>(
188 &mut self,
189 sockets: &mut SocketSet,
190 meta: PacketMeta,
191 source_hardware_addr: HardwareAddress,
192 ipv6_packet: &Ipv6Packet<&'frame [u8]>,
193 ) -> Option<Packet<'frame>> {
194 let ipv6_repr = check!(Ipv6Repr::parse(ipv6_packet));
195
196 if !ipv6_repr.src_addr.x_is_unicast() {
197 net_debug!("non-unicast source address");
199 return None;
200 }
201
202 let (next_header, ip_payload) = if ipv6_repr.next_header == IpProtocol::HopByHop {
203 match self.process_hopbyhop(ipv6_repr, ipv6_packet.payload()) {
204 HopByHopResponse::Discard(e) => return e,
205 HopByHopResponse::Continue(next) => next,
206 }
207 } else {
208 (ipv6_repr.next_header, ipv6_packet.payload())
209 };
210
211 if !self.has_ip_addr(ipv6_repr.dst_addr)
212 && !self.has_multicast_group(ipv6_repr.dst_addr)
213 && !ipv6_repr.dst_addr.is_loopback()
214 {
215 if !self.any_ip {
216 net_trace!("Rejecting IPv6 packet; any_ip=false");
217 return None;
218 }
219
220 if !ipv6_repr.dst_addr.x_is_unicast() {
221 net_trace!(
222 "Rejecting IPv6 packet; {} is not a unicast address",
223 ipv6_repr.dst_addr
224 );
225 return None;
226 }
227
228 if self
229 .routes
230 .lookup(&IpAddress::Ipv6(ipv6_repr.dst_addr), self.now)
231 .map_or(true, |router_addr| !self.has_ip_addr(router_addr))
232 {
233 net_trace!("Rejecting IPv6 packet; no matching routes");
234
235 return None;
236 }
237 }
238
239 #[cfg(feature = "socket-raw")]
240 let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload);
241 #[cfg(not(feature = "socket-raw"))]
242 let handled_by_raw_socket = false;
243
244 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
245 if ipv6_repr.dst_addr.x_is_unicast() {
246 self.neighbor_cache.reset_expiry_if_existing(
247 IpAddress::Ipv6(ipv6_repr.src_addr),
248 source_hardware_addr,
249 self.now,
250 );
251 }
252
253 self.process_nxt_hdr(
254 sockets,
255 meta,
256 ipv6_repr,
257 next_header,
258 handled_by_raw_socket,
259 ip_payload,
260 )
261 }
262
263 fn process_hopbyhop<'frame>(
264 &mut self,
265 ipv6_repr: Ipv6Repr,
266 ip_payload: &'frame [u8],
267 ) -> HopByHopResponse<'frame> {
268 let param_problem = || {
269 let payload_len =
270 icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
271 self.icmpv6_reply(
272 ipv6_repr,
273 Icmpv6Repr::ParamProblem {
274 reason: Icmpv6ParamProblem::UnrecognizedOption,
275 pointer: ipv6_repr.buffer_len() as u32,
276 header: ipv6_repr,
277 data: &ip_payload[0..payload_len],
278 },
279 )
280 };
281
282 let ext_hdr = check!(Ipv6ExtHeader::new_checked(ip_payload));
283 let ext_repr = check!(Ipv6ExtHeaderRepr::parse(&ext_hdr));
284 let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ext_repr.data));
285 let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_hdr));
286
287 for opt_repr in &hbh_repr.options {
288 match opt_repr {
289 Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) | Ipv6OptionRepr::RouterAlert(_) => {
290 }
291 #[cfg(feature = "proto-rpl")]
292 Ipv6OptionRepr::Rpl(_) => {}
293
294 Ipv6OptionRepr::Unknown { type_, .. } => {
295 match Ipv6OptionFailureType::from(*type_) {
296 Ipv6OptionFailureType::Skip => (),
297 Ipv6OptionFailureType::Discard => {
298 return HopByHopResponse::Discard(None);
299 }
300 Ipv6OptionFailureType::DiscardSendAll => {
301 return HopByHopResponse::Discard(param_problem());
302 }
303 Ipv6OptionFailureType::DiscardSendUnicast => {
304 if !ipv6_repr.dst_addr.is_multicast() {
305 return HopByHopResponse::Discard(param_problem());
306 } else {
307 return HopByHopResponse::Discard(None);
308 }
309 }
310 }
311 }
312 }
313 }
314
315 HopByHopResponse::Continue((
316 ext_repr.next_header,
317 &ip_payload[ext_repr.header_len() + ext_repr.data.len()..],
318 ))
319 }
320
321 fn process_nxt_hdr<'frame>(
324 &mut self,
325 sockets: &mut SocketSet,
326 meta: PacketMeta,
327 ipv6_repr: Ipv6Repr,
328 nxt_hdr: IpProtocol,
329 handled_by_raw_socket: bool,
330 ip_payload: &'frame [u8],
331 ) -> Option<Packet<'frame>> {
332 match nxt_hdr {
333 IpProtocol::Icmpv6 => self.process_icmpv6(sockets, ipv6_repr, ip_payload),
334
335 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
336 IpProtocol::Udp => self.process_udp(
337 sockets,
338 meta,
339 handled_by_raw_socket,
340 ipv6_repr.into(),
341 ip_payload,
342 ),
343
344 #[cfg(feature = "socket-tcp")]
345 IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload),
346
347 #[cfg(feature = "socket-raw")]
348 _ if handled_by_raw_socket => None,
349
350 _ => {
351 let payload_len =
353 icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
354 let icmp_reply_repr = Icmpv6Repr::ParamProblem {
355 reason: Icmpv6ParamProblem::UnrecognizedNxtHdr,
356 pointer: ipv6_repr.buffer_len() as u32,
358 header: ipv6_repr,
359 data: &ip_payload[0..payload_len],
360 };
361 self.icmpv6_reply(ipv6_repr, icmp_reply_repr)
362 }
363 }
364 }
365
366 pub(super) fn process_icmpv6<'frame>(
367 &mut self,
368 _sockets: &mut SocketSet,
369 ip_repr: Ipv6Repr,
370 ip_payload: &'frame [u8],
371 ) -> Option<Packet<'frame>> {
372 let icmp_packet = check!(Icmpv6Packet::new_checked(ip_payload));
373 let icmp_repr = check!(Icmpv6Repr::parse(
374 &ip_repr.src_addr,
375 &ip_repr.dst_addr,
376 &icmp_packet,
377 &self.caps.checksum,
378 ));
379
380 #[cfg(feature = "socket-icmp")]
381 let mut handled_by_icmp_socket = false;
382
383 #[cfg(feature = "socket-icmp")]
384 {
385 use crate::socket::icmp::Socket as IcmpSocket;
386 for icmp_socket in _sockets
387 .items_mut()
388 .filter_map(|i| IcmpSocket::downcast_mut(&mut i.socket))
389 {
390 if icmp_socket.accepts_v6(self, &ip_repr, &icmp_repr) {
391 icmp_socket.process_v6(self, &ip_repr, &icmp_repr);
392 handled_by_icmp_socket = true;
393 }
394 }
395 }
396
397 match icmp_repr {
398 Icmpv6Repr::EchoRequest {
400 ident,
401 seq_no,
402 data,
403 } => {
404 let icmp_reply_repr = Icmpv6Repr::EchoReply {
405 ident,
406 seq_no,
407 data,
408 };
409 self.icmpv6_reply(ip_repr, icmp_reply_repr)
410 }
411
412 Icmpv6Repr::EchoReply { .. } => None,
414
415 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
417 Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit == 0xff => match self.caps.medium {
418 #[cfg(feature = "medium-ethernet")]
419 Medium::Ethernet => self.process_ndisc(ip_repr, repr),
420 #[cfg(feature = "medium-ieee802154")]
421 Medium::Ieee802154 => self.process_ndisc(ip_repr, repr),
422 #[cfg(feature = "medium-ip")]
423 Medium::Ip => None,
424 },
425 #[cfg(feature = "multicast")]
426 Icmpv6Repr::Mld(repr) => match repr {
427 MldRepr::Query { .. }
429 if ip_repr.hop_limit == 1 && ip_repr.src_addr.is_link_local() =>
430 {
431 self.process_mldv2(ip_repr, repr)
432 }
433 _ => None,
434 },
435
436 #[cfg(feature = "socket-icmp")]
439 _ if handled_by_icmp_socket => None,
440
441 _ => None,
443 }
444 }
445
446 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
447 pub(super) fn process_ndisc<'frame>(
448 &mut self,
449 ip_repr: Ipv6Repr,
450 repr: NdiscRepr<'frame>,
451 ) -> Option<Packet<'frame>> {
452 match repr {
453 NdiscRepr::NeighborAdvert {
454 lladdr,
455 target_addr,
456 flags,
457 } => {
458 let ip_addr = ip_repr.src_addr.into();
459 if let Some(lladdr) = lladdr {
460 let lladdr = check!(lladdr.parse(self.caps.medium));
461 if !lladdr.is_unicast() || !target_addr.x_is_unicast() {
462 return None;
463 }
464 if flags.contains(NdiscNeighborFlags::OVERRIDE)
465 || !self.neighbor_cache.lookup(&ip_addr, self.now).found()
466 {
467 self.neighbor_cache.fill(ip_addr, lladdr, self.now)
468 }
469 }
470 None
471 }
472 NdiscRepr::NeighborSolicit {
473 target_addr,
474 lladdr,
475 ..
476 } => {
477 if let Some(lladdr) = lladdr {
478 let lladdr = check!(lladdr.parse(self.caps.medium));
479 if !lladdr.is_unicast() || !target_addr.x_is_unicast() {
480 return None;
481 }
482 self.neighbor_cache
483 .fill(ip_repr.src_addr.into(), lladdr, self.now);
484 }
485
486 if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) {
487 let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
488 flags: NdiscNeighborFlags::SOLICITED,
489 target_addr,
490 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
491 lladdr: Some(self.hardware_addr.into()),
492 });
493 let ip_repr = Ipv6Repr {
494 src_addr: target_addr,
495 dst_addr: ip_repr.src_addr,
496 next_header: IpProtocol::Icmpv6,
497 hop_limit: 0xff,
498 payload_len: advert.buffer_len(),
499 };
500 Some(Packet::new_ipv6(ip_repr, IpPayload::Icmpv6(advert)))
501 } else {
502 None
503 }
504 }
505 _ => None,
506 }
507 }
508
509 pub(super) fn icmpv6_reply<'frame, 'icmp: 'frame>(
510 &self,
511 ipv6_repr: Ipv6Repr,
512 icmp_repr: Icmpv6Repr<'icmp>,
513 ) -> Option<Packet<'frame>> {
514 let src_addr = ipv6_repr.dst_addr;
515 let dst_addr = ipv6_repr.src_addr;
516
517 let src_addr = if src_addr.x_is_unicast() {
518 src_addr
519 } else {
520 self.get_source_address_ipv6(&dst_addr)
521 };
522
523 let ipv6_reply_repr = Ipv6Repr {
524 src_addr,
525 dst_addr,
526 next_header: IpProtocol::Icmpv6,
527 payload_len: icmp_repr.buffer_len(),
528 hop_limit: 64,
529 };
530 Some(Packet::new_ipv6(
531 ipv6_reply_repr,
532 IpPayload::Icmpv6(icmp_repr),
533 ))
534 }
535
536 pub(super) fn mldv2_report_packet<'any>(
537 &self,
538 records: &'any [MldAddressRecordRepr<'any>],
539 ) -> Option<Packet<'any>> {
540 let src_addr = self
544 .link_local_ipv6_address()
545 .unwrap_or(Ipv6Address::UNSPECIFIED);
546
547 let dst_addr = IPV6_LINK_LOCAL_ALL_MLDV2_ROUTERS;
550
551 let dummy_ext_hdr = Ipv6ExtHeaderRepr {
554 next_header: IpProtocol::Unknown(0),
555 length: 0,
556 data: &[],
557 };
558
559 let mut hbh_repr = Ipv6HopByHopRepr::mldv2_router_alert();
560 hbh_repr.push_padn_option(0);
561
562 let mld_repr = MldRepr::ReportRecordReprs(records);
563 let records_len = records
564 .iter()
565 .map(MldAddressRecordRepr::buffer_len)
566 .sum::<usize>();
567
568 Some(Packet::new_ipv6(
570 Ipv6Repr {
571 src_addr,
572 dst_addr,
573 next_header: IpProtocol::HopByHop,
574 payload_len: dummy_ext_hdr.header_len()
575 + hbh_repr.buffer_len()
576 + mld_repr.buffer_len()
577 + records_len,
578 hop_limit: 1,
579 },
580 IpPayload::HopByHopIcmpv6(hbh_repr, Icmpv6Repr::Mld(mld_repr)),
581 ))
582 }
583}