ipv6.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. use super::*;
  2. /// Enum used for the process_hopbyhop function. In some cases, when discarding a packet, an ICMP
  3. /// parameter problem message needs to be transmitted to the source of the address. In other cases,
  4. /// the processing of the IP packet can continue.
  5. #[allow(clippy::large_enum_variant)]
  6. enum HopByHopResponse<'frame> {
  7. /// Continue processing the IPv6 packet.
  8. Continue((IpProtocol, &'frame [u8])),
  9. /// Discard the packet and maybe send back an ICMPv6 packet.
  10. Discard(Option<Packet<'frame>>),
  11. }
  12. // We implement `Default` such that we can use the check! macro.
  13. impl Default for HopByHopResponse<'_> {
  14. fn default() -> Self {
  15. Self::Discard(None)
  16. }
  17. }
  18. impl InterfaceInner {
  19. /// Return the IPv6 address that is a candidate source address for the given destination
  20. /// address, based on RFC 6724.
  21. ///
  22. /// # Panics
  23. /// This function panics if the destination address is unspecified.
  24. #[allow(unused)]
  25. pub(crate) fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Ipv6Address {
  26. assert!(!dst_addr.is_unspecified());
  27. // See RFC 6724 Section 4: Candidate source address
  28. fn is_candidate_source_address(dst_addr: &Ipv6Address, src_addr: &Ipv6Address) -> bool {
  29. // For all multicast and link-local destination addresses, the candidate address MUST
  30. // only be an address from the same link.
  31. if dst_addr.is_link_local() && !src_addr.is_link_local() {
  32. return false;
  33. }
  34. if dst_addr.is_multicast()
  35. && matches!(dst_addr.multicast_scope(), Ipv6MulticastScope::LinkLocal)
  36. && src_addr.is_multicast()
  37. && !matches!(src_addr.multicast_scope(), Ipv6MulticastScope::LinkLocal)
  38. {
  39. return false;
  40. }
  41. // Unspecified addresses and multicast address can not be in the candidate source address
  42. // list. Except when the destination multicast address has a link-local scope, then the
  43. // source address can also be link-local multicast.
  44. if src_addr.is_unspecified() || src_addr.is_multicast() {
  45. return false;
  46. }
  47. true
  48. }
  49. // See RFC 6724 Section 2.2: Common Prefix Length
  50. fn common_prefix_length(dst_addr: &Ipv6Cidr, src_addr: &Ipv6Address) -> usize {
  51. let addr = dst_addr.address();
  52. let mut bits = 0;
  53. for (l, r) in addr.octets().iter().zip(src_addr.octets().iter()) {
  54. if l == r {
  55. bits += 8;
  56. } else {
  57. bits += (l ^ r).leading_zeros();
  58. break;
  59. }
  60. }
  61. bits = bits.min(dst_addr.prefix_len() as u32);
  62. bits as usize
  63. }
  64. // If the destination address is a loopback address, or when there are no IPv6 addresses in
  65. // the interface, then the loopback address is the only candidate source address.
  66. if dst_addr.is_loopback()
  67. || self
  68. .ip_addrs
  69. .iter()
  70. .filter(|a| matches!(a, IpCidr::Ipv6(_)))
  71. .count()
  72. == 0
  73. {
  74. return Ipv6Address::LOCALHOST;
  75. }
  76. let mut candidate = self
  77. .ip_addrs
  78. .iter()
  79. .find_map(|a| match a {
  80. #[cfg(feature = "proto-ipv4")]
  81. IpCidr::Ipv4(_) => None,
  82. IpCidr::Ipv6(a) => Some(a),
  83. })
  84. .unwrap(); // NOTE: we check above that there is at least one IPv6 address.
  85. for addr in self.ip_addrs.iter().filter_map(|a| match a {
  86. #[cfg(feature = "proto-ipv4")]
  87. IpCidr::Ipv4(_) => None,
  88. #[cfg(feature = "proto-ipv6")]
  89. IpCidr::Ipv6(a) => Some(a),
  90. }) {
  91. if !is_candidate_source_address(dst_addr, &addr.address()) {
  92. continue;
  93. }
  94. // Rule 1: prefer the address that is the same as the output destination address.
  95. if candidate.address() != *dst_addr && addr.address() == *dst_addr {
  96. candidate = addr;
  97. }
  98. // Rule 2: prefer appropriate scope.
  99. if (candidate.address().multicast_scope() as u8)
  100. < (addr.address().multicast_scope() as u8)
  101. {
  102. if (candidate.address().multicast_scope() as u8)
  103. < (dst_addr.multicast_scope() as u8)
  104. {
  105. candidate = addr;
  106. }
  107. } else if (addr.address().multicast_scope() as u8) > (dst_addr.multicast_scope() as u8)
  108. {
  109. candidate = addr;
  110. }
  111. // Rule 3: avoid deprecated addresses (TODO)
  112. // Rule 4: prefer home addresses (TODO)
  113. // Rule 5: prefer outgoing interfaces (TODO)
  114. // Rule 5.5: prefer addresses in a prefix advertises by the next-hop (TODO).
  115. // Rule 6: prefer matching label (TODO)
  116. // Rule 7: prefer temporary addresses (TODO)
  117. // Rule 8: use longest matching prefix
  118. if common_prefix_length(candidate, dst_addr) < common_prefix_length(addr, dst_addr) {
  119. candidate = addr;
  120. }
  121. }
  122. candidate.address()
  123. }
  124. /// Determine if the given `Ipv6Address` is the solicited node
  125. /// multicast address for a IPv6 addresses assigned to the interface.
  126. /// See [RFC 4291 § 2.7.1] for more details.
  127. ///
  128. /// [RFC 4291 § 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1
  129. pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool {
  130. self.ip_addrs.iter().any(|cidr| {
  131. match *cidr {
  132. IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOCALHOST => {
  133. // Take the lower order 24 bits of the IPv6 address and
  134. // append those bits to FF02:0:0:0:0:1:FF00::/104.
  135. addr.octets()[14..] == cidr.address().octets()[14..]
  136. }
  137. _ => false,
  138. }
  139. })
  140. }
  141. /// Get the first IPv6 address if present.
  142. pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
  143. self.ip_addrs.iter().find_map(|addr| match *addr {
  144. IpCidr::Ipv6(cidr) => Some(cidr.address()),
  145. #[allow(unreachable_patterns)]
  146. _ => None,
  147. })
  148. }
  149. /// Get the first link-local IPv6 address of the interface, if present.
  150. fn link_local_ipv6_address(&self) -> Option<Ipv6Address> {
  151. self.ip_addrs.iter().find_map(|addr| match *addr {
  152. #[cfg(feature = "proto-ipv4")]
  153. IpCidr::Ipv4(_) => None,
  154. #[cfg(feature = "proto-ipv6")]
  155. IpCidr::Ipv6(cidr) => {
  156. let addr = cidr.address();
  157. if addr.is_link_local() {
  158. Some(addr)
  159. } else {
  160. None
  161. }
  162. }
  163. })
  164. }
  165. pub(super) fn process_ipv6<'frame>(
  166. &mut self,
  167. sockets: &mut SocketSet,
  168. meta: PacketMeta,
  169. source_hardware_addr: HardwareAddress,
  170. ipv6_packet: &Ipv6Packet<&'frame [u8]>,
  171. ) -> Option<Packet<'frame>> {
  172. let ipv6_repr = check!(Ipv6Repr::parse(ipv6_packet));
  173. if !ipv6_repr.src_addr.is_unicast() {
  174. // Discard packets with non-unicast source addresses.
  175. net_debug!("non-unicast source address");
  176. return None;
  177. }
  178. let (next_header, ip_payload) = if ipv6_repr.next_header == IpProtocol::HopByHop {
  179. match self.process_hopbyhop(ipv6_repr, ipv6_packet.payload()) {
  180. HopByHopResponse::Discard(e) => return e,
  181. HopByHopResponse::Continue(next) => next,
  182. }
  183. } else {
  184. (ipv6_repr.next_header, ipv6_packet.payload())
  185. };
  186. if !self.has_ip_addr(ipv6_repr.dst_addr)
  187. && !self.has_multicast_group(ipv6_repr.dst_addr)
  188. && !ipv6_repr.dst_addr.is_loopback()
  189. {
  190. // If AnyIP is enabled, also check if the packet is routed locally.
  191. if !self.any_ip
  192. || !ipv6_repr.dst_addr.is_unicast()
  193. || self
  194. .routes
  195. .lookup(&IpAddress::Ipv6(ipv6_repr.dst_addr), self.now)
  196. .map_or(true, |router_addr| !self.has_ip_addr(router_addr))
  197. {
  198. net_trace!("packet IP address not for this interface");
  199. return None;
  200. }
  201. }
  202. #[cfg(feature = "socket-raw")]
  203. let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload);
  204. #[cfg(not(feature = "socket-raw"))]
  205. let handled_by_raw_socket = false;
  206. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
  207. if ipv6_repr.dst_addr.is_unicast() {
  208. self.neighbor_cache.reset_expiry_if_existing(
  209. IpAddress::Ipv6(ipv6_repr.src_addr),
  210. source_hardware_addr,
  211. self.now,
  212. );
  213. }
  214. self.process_nxt_hdr(
  215. sockets,
  216. meta,
  217. ipv6_repr,
  218. next_header,
  219. handled_by_raw_socket,
  220. ip_payload,
  221. )
  222. }
  223. fn process_hopbyhop<'frame>(
  224. &mut self,
  225. ipv6_repr: Ipv6Repr,
  226. ip_payload: &'frame [u8],
  227. ) -> HopByHopResponse<'frame> {
  228. let param_problem = || {
  229. let payload_len =
  230. icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
  231. self.icmpv6_reply(
  232. ipv6_repr,
  233. Icmpv6Repr::ParamProblem {
  234. reason: Icmpv6ParamProblem::UnrecognizedOption,
  235. pointer: ipv6_repr.buffer_len() as u32,
  236. header: ipv6_repr,
  237. data: &ip_payload[0..payload_len],
  238. },
  239. )
  240. };
  241. let ext_hdr = check!(Ipv6ExtHeader::new_checked(ip_payload));
  242. let ext_repr = check!(Ipv6ExtHeaderRepr::parse(&ext_hdr));
  243. let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ext_repr.data));
  244. let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_hdr));
  245. for opt_repr in &hbh_repr.options {
  246. match opt_repr {
  247. Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) | Ipv6OptionRepr::RouterAlert(_) => {
  248. }
  249. #[cfg(feature = "proto-rpl")]
  250. Ipv6OptionRepr::Rpl(_) => {}
  251. Ipv6OptionRepr::Unknown { type_, .. } => {
  252. match Ipv6OptionFailureType::from(*type_) {
  253. Ipv6OptionFailureType::Skip => (),
  254. Ipv6OptionFailureType::Discard => {
  255. return HopByHopResponse::Discard(None);
  256. }
  257. Ipv6OptionFailureType::DiscardSendAll => {
  258. return HopByHopResponse::Discard(param_problem());
  259. }
  260. Ipv6OptionFailureType::DiscardSendUnicast => {
  261. if !ipv6_repr.dst_addr.is_multicast() {
  262. return HopByHopResponse::Discard(param_problem());
  263. } else {
  264. return HopByHopResponse::Discard(None);
  265. }
  266. }
  267. }
  268. }
  269. }
  270. }
  271. HopByHopResponse::Continue((
  272. ext_repr.next_header,
  273. &ip_payload[ext_repr.header_len() + ext_repr.data.len()..],
  274. ))
  275. }
  276. /// Given the next header value forward the payload onto the correct process
  277. /// function.
  278. fn process_nxt_hdr<'frame>(
  279. &mut self,
  280. sockets: &mut SocketSet,
  281. meta: PacketMeta,
  282. ipv6_repr: Ipv6Repr,
  283. nxt_hdr: IpProtocol,
  284. handled_by_raw_socket: bool,
  285. ip_payload: &'frame [u8],
  286. ) -> Option<Packet<'frame>> {
  287. match nxt_hdr {
  288. IpProtocol::Icmpv6 => self.process_icmpv6(sockets, ipv6_repr, ip_payload),
  289. #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
  290. IpProtocol::Udp => self.process_udp(
  291. sockets,
  292. meta,
  293. handled_by_raw_socket,
  294. ipv6_repr.into(),
  295. ip_payload,
  296. ),
  297. #[cfg(feature = "socket-tcp")]
  298. IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload),
  299. #[cfg(feature = "socket-raw")]
  300. _ if handled_by_raw_socket => None,
  301. _ => {
  302. // Send back as much of the original payload as we can.
  303. let payload_len =
  304. icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
  305. let icmp_reply_repr = Icmpv6Repr::ParamProblem {
  306. reason: Icmpv6ParamProblem::UnrecognizedNxtHdr,
  307. // The offending packet is after the IPv6 header.
  308. pointer: ipv6_repr.buffer_len() as u32,
  309. header: ipv6_repr,
  310. data: &ip_payload[0..payload_len],
  311. };
  312. self.icmpv6_reply(ipv6_repr, icmp_reply_repr)
  313. }
  314. }
  315. }
  316. pub(super) fn process_icmpv6<'frame>(
  317. &mut self,
  318. _sockets: &mut SocketSet,
  319. ip_repr: Ipv6Repr,
  320. ip_payload: &'frame [u8],
  321. ) -> Option<Packet<'frame>> {
  322. let icmp_packet = check!(Icmpv6Packet::new_checked(ip_payload));
  323. let icmp_repr = check!(Icmpv6Repr::parse(
  324. &ip_repr.src_addr,
  325. &ip_repr.dst_addr,
  326. &icmp_packet,
  327. &self.caps.checksum,
  328. ));
  329. #[cfg(feature = "socket-icmp")]
  330. let mut handled_by_icmp_socket = false;
  331. #[cfg(feature = "socket-icmp")]
  332. {
  333. use crate::socket::icmp::Socket as IcmpSocket;
  334. for icmp_socket in _sockets
  335. .items_mut()
  336. .filter_map(|i| IcmpSocket::downcast_mut(&mut i.socket))
  337. {
  338. if icmp_socket.accepts_v6(self, &ip_repr, &icmp_repr) {
  339. icmp_socket.process_v6(self, &ip_repr, &icmp_repr);
  340. handled_by_icmp_socket = true;
  341. }
  342. }
  343. }
  344. match icmp_repr {
  345. // Respond to echo requests.
  346. Icmpv6Repr::EchoRequest {
  347. ident,
  348. seq_no,
  349. data,
  350. } => {
  351. let icmp_reply_repr = Icmpv6Repr::EchoReply {
  352. ident,
  353. seq_no,
  354. data,
  355. };
  356. self.icmpv6_reply(ip_repr, icmp_reply_repr)
  357. }
  358. // Ignore any echo replies.
  359. Icmpv6Repr::EchoReply { .. } => None,
  360. // Forward any NDISC packets to the ndisc packet handler
  361. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
  362. Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit == 0xff => match self.caps.medium {
  363. #[cfg(feature = "medium-ethernet")]
  364. Medium::Ethernet => self.process_ndisc(ip_repr, repr),
  365. #[cfg(feature = "medium-ieee802154")]
  366. Medium::Ieee802154 => self.process_ndisc(ip_repr, repr),
  367. #[cfg(feature = "medium-ip")]
  368. Medium::Ip => None,
  369. },
  370. // Don't report an error if a packet with unknown type
  371. // has been handled by an ICMP socket
  372. #[cfg(feature = "socket-icmp")]
  373. _ if handled_by_icmp_socket => None,
  374. // FIXME: do something correct here?
  375. _ => None,
  376. }
  377. }
  378. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
  379. pub(super) fn process_ndisc<'frame>(
  380. &mut self,
  381. ip_repr: Ipv6Repr,
  382. repr: NdiscRepr<'frame>,
  383. ) -> Option<Packet<'frame>> {
  384. match repr {
  385. NdiscRepr::NeighborAdvert {
  386. lladdr,
  387. target_addr,
  388. flags,
  389. } => {
  390. let ip_addr = ip_repr.src_addr.into();
  391. if let Some(lladdr) = lladdr {
  392. let lladdr = check!(lladdr.parse(self.caps.medium));
  393. if !lladdr.is_unicast() || !target_addr.is_unicast() {
  394. return None;
  395. }
  396. if flags.contains(NdiscNeighborFlags::OVERRIDE)
  397. || !self.neighbor_cache.lookup(&ip_addr, self.now).found()
  398. {
  399. self.neighbor_cache.fill(ip_addr, lladdr, self.now)
  400. }
  401. }
  402. None
  403. }
  404. NdiscRepr::NeighborSolicit {
  405. target_addr,
  406. lladdr,
  407. ..
  408. } => {
  409. if let Some(lladdr) = lladdr {
  410. let lladdr = check!(lladdr.parse(self.caps.medium));
  411. if !lladdr.is_unicast() || !target_addr.is_unicast() {
  412. return None;
  413. }
  414. self.neighbor_cache
  415. .fill(ip_repr.src_addr.into(), lladdr, self.now);
  416. }
  417. if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) {
  418. let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
  419. flags: NdiscNeighborFlags::SOLICITED,
  420. target_addr,
  421. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
  422. lladdr: Some(self.hardware_addr.into()),
  423. });
  424. let ip_repr = Ipv6Repr {
  425. src_addr: target_addr,
  426. dst_addr: ip_repr.src_addr,
  427. next_header: IpProtocol::Icmpv6,
  428. hop_limit: 0xff,
  429. payload_len: advert.buffer_len(),
  430. };
  431. Some(Packet::new_ipv6(ip_repr, IpPayload::Icmpv6(advert)))
  432. } else {
  433. None
  434. }
  435. }
  436. _ => None,
  437. }
  438. }
  439. pub(super) fn icmpv6_reply<'frame, 'icmp: 'frame>(
  440. &self,
  441. ipv6_repr: Ipv6Repr,
  442. icmp_repr: Icmpv6Repr<'icmp>,
  443. ) -> Option<Packet<'frame>> {
  444. let src_addr = ipv6_repr.dst_addr;
  445. let dst_addr = ipv6_repr.src_addr;
  446. let src_addr = if src_addr.is_unicast() {
  447. src_addr
  448. } else {
  449. self.get_source_address_ipv6(&dst_addr)
  450. };
  451. let ipv6_reply_repr = Ipv6Repr {
  452. src_addr,
  453. dst_addr,
  454. next_header: IpProtocol::Icmpv6,
  455. payload_len: icmp_repr.buffer_len(),
  456. hop_limit: 64,
  457. };
  458. Some(Packet::new_ipv6(
  459. ipv6_reply_repr,
  460. IpPayload::Icmpv6(icmp_repr),
  461. ))
  462. }
  463. pub(super) fn mldv2_report_packet<'any>(
  464. &self,
  465. records: &'any [MldAddressRecordRepr<'any>],
  466. ) -> Option<Packet<'any>> {
  467. // Per [RFC 3810 § 5.2.13], source addresses must be link-local, falling
  468. // back to the unspecified address if we haven't acquired one.
  469. // [RFC 3810 § 5.2.13]: https://tools.ietf.org/html/rfc3810#section-5.2.13
  470. let src_addr = self
  471. .link_local_ipv6_address()
  472. .unwrap_or(Ipv6Address::UNSPECIFIED);
  473. // Per [RFC 3810 § 5.2.14], all MLDv2 reports are sent to ff02::16.
  474. // [RFC 3810 § 5.2.14]: https://tools.ietf.org/html/rfc3810#section-5.2.14
  475. let dst_addr = IPV6_LINK_LOCAL_ALL_MLDV2_ROUTERS;
  476. // Create a dummy IPv6 extension header so we can calculate the total length of the packet.
  477. // The actual extension header will be created later by Packet::emit_payload().
  478. let dummy_ext_hdr = Ipv6ExtHeaderRepr {
  479. next_header: IpProtocol::Unknown(0),
  480. length: 0,
  481. data: &[],
  482. };
  483. let mut hbh_repr = Ipv6HopByHopRepr::mldv2_router_alert();
  484. hbh_repr.push_padn_option(0);
  485. let mld_repr = MldRepr::ReportRecordReprs(records);
  486. let records_len = records
  487. .iter()
  488. .map(MldAddressRecordRepr::buffer_len)
  489. .sum::<usize>();
  490. // All MLDv2 messages must be sent with an IPv6 Hop limit of 1.
  491. Some(Packet::new_ipv6(
  492. Ipv6Repr {
  493. src_addr,
  494. dst_addr,
  495. next_header: IpProtocol::HopByHop,
  496. payload_len: dummy_ext_hdr.header_len()
  497. + hbh_repr.buffer_len()
  498. + mld_repr.buffer_len()
  499. + records_len,
  500. hop_limit: 1,
  501. },
  502. IpPayload::HopByHopIcmpv6(hbh_repr, Icmpv6Repr::Mld(mld_repr)),
  503. ))
  504. }
  505. }