ipv6.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. use super::*;
  2. /// Enum used for the process_hopbyhop function. In some cases, when discarding a packet, an ICMMP
  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. #[allow(unused)]
  22. pub(crate) fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option<Ipv6Address> {
  23. // See RFC 6724 Section 4: Candidate source address
  24. fn is_candidate_source_address(dst_addr: &Ipv6Address, src_addr: &Ipv6Address) -> bool {
  25. // For all multicast and link-local destination addresses, the candidate address MUST
  26. // only be an address from the same link.
  27. if dst_addr.is_link_local() && !src_addr.is_link_local() {
  28. return false;
  29. }
  30. if dst_addr.is_multicast()
  31. && matches!(dst_addr.multicast_scope(), Ipv6MulticastScope::LinkLocal)
  32. && src_addr.is_multicast()
  33. && !matches!(src_addr.multicast_scope(), Ipv6MulticastScope::LinkLocal)
  34. {
  35. return false;
  36. }
  37. // Loopback addresses and multicast address can not be in the candidate source address
  38. // list. Except when the destination multicast address has a link-local scope, then the
  39. // source address can also be link-local multicast.
  40. if src_addr.is_loopback() || src_addr.is_multicast() {
  41. return false;
  42. }
  43. true
  44. }
  45. // See RFC 6724 Section 2.2: Common Prefix Length
  46. fn common_prefix_length(dst_addr: &Ipv6Cidr, src_addr: &Ipv6Address) -> usize {
  47. let addr = dst_addr.address();
  48. let mut bits = 0;
  49. for (l, r) in addr.as_bytes().iter().zip(src_addr.as_bytes().iter()) {
  50. if l == r {
  51. bits += 8;
  52. } else {
  53. bits += (l ^ r).leading_zeros();
  54. break;
  55. }
  56. }
  57. bits = bits.min(dst_addr.prefix_len() as u32);
  58. bits as usize
  59. }
  60. // Get the first address that is a candidate address.
  61. let mut candidate = self
  62. .ip_addrs
  63. .iter()
  64. .filter_map(|a| match a {
  65. #[cfg(feature = "proto-ipv4")]
  66. IpCidr::Ipv4(_) => None,
  67. #[cfg(feature = "proto-ipv6")]
  68. IpCidr::Ipv6(a) => Some(a),
  69. })
  70. .find(|a| is_candidate_source_address(dst_addr, &a.address()))
  71. .unwrap();
  72. for addr in self.ip_addrs.iter().filter_map(|a| match a {
  73. #[cfg(feature = "proto-ipv4")]
  74. IpCidr::Ipv4(_) => None,
  75. #[cfg(feature = "proto-ipv6")]
  76. IpCidr::Ipv6(a) => Some(a),
  77. }) {
  78. if !is_candidate_source_address(dst_addr, &addr.address()) {
  79. continue;
  80. }
  81. // Rule 1: prefer the address that is the same as the output destination address.
  82. if candidate.address() != *dst_addr && addr.address() == *dst_addr {
  83. candidate = addr;
  84. }
  85. // Rule 2: prefer appropriate scope.
  86. if (candidate.address().multicast_scope() as u8)
  87. < (addr.address().multicast_scope() as u8)
  88. {
  89. if (candidate.address().multicast_scope() as u8)
  90. < (dst_addr.multicast_scope() as u8)
  91. {
  92. candidate = addr;
  93. }
  94. } else if (addr.address().multicast_scope() as u8) > (dst_addr.multicast_scope() as u8)
  95. {
  96. candidate = addr;
  97. }
  98. // Rule 3: avoid deprecated addresses (TODO)
  99. // Rule 4: prefer home addresses (TODO)
  100. // Rule 5: prefer outgoing interfaces (TODO)
  101. // Rule 5.5: prefer addresses in a prefix advertises by the next-hop (TODO).
  102. // Rule 6: prefer matching label (TODO)
  103. // Rule 7: prefer temporary addresses (TODO)
  104. // Rule 8: use longest matching prefix
  105. if common_prefix_length(candidate, dst_addr) < common_prefix_length(addr, dst_addr) {
  106. candidate = addr;
  107. }
  108. }
  109. Some(candidate.address())
  110. }
  111. /// Determine if the given `Ipv6Address` is the solicited node
  112. /// multicast address for a IPv6 addresses assigned to the interface.
  113. /// See [RFC 4291 § 2.7.1] for more details.
  114. ///
  115. /// [RFC 4291 § 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1
  116. pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool {
  117. self.ip_addrs.iter().any(|cidr| {
  118. match *cidr {
  119. IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK => {
  120. // Take the lower order 24 bits of the IPv6 address and
  121. // append those bits to FF02:0:0:0:0:1:FF00::/104.
  122. addr.as_bytes()[14..] == cidr.address().as_bytes()[14..]
  123. }
  124. _ => false,
  125. }
  126. })
  127. }
  128. /// Get the first IPv6 address if present.
  129. pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
  130. self.ip_addrs.iter().find_map(|addr| match *addr {
  131. IpCidr::Ipv6(cidr) => Some(cidr.address()),
  132. #[allow(unreachable_patterns)]
  133. _ => None,
  134. })
  135. }
  136. pub(super) fn process_ipv6<'frame>(
  137. &mut self,
  138. sockets: &mut SocketSet,
  139. meta: PacketMeta,
  140. ipv6_packet: &Ipv6Packet<&'frame [u8]>,
  141. ) -> Option<Packet<'frame>> {
  142. let ipv6_repr = check!(Ipv6Repr::parse(ipv6_packet));
  143. if !ipv6_repr.src_addr.is_unicast() {
  144. // Discard packets with non-unicast source addresses.
  145. net_debug!("non-unicast source address");
  146. return None;
  147. }
  148. let (next_header, ip_payload) = if ipv6_repr.next_header == IpProtocol::HopByHop {
  149. match self.process_hopbyhop(ipv6_repr, ipv6_packet.payload()) {
  150. HopByHopResponse::Discard(e) => return e,
  151. HopByHopResponse::Continue(next) => next,
  152. }
  153. } else {
  154. (ipv6_repr.next_header, ipv6_packet.payload())
  155. };
  156. if !self.has_ip_addr(ipv6_repr.dst_addr)
  157. && !self.has_multicast_group(ipv6_repr.dst_addr)
  158. && !ipv6_repr.dst_addr.is_loopback()
  159. {
  160. net_trace!("packet IP address not for this interface");
  161. return None;
  162. }
  163. #[cfg(feature = "socket-raw")]
  164. let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload);
  165. #[cfg(not(feature = "socket-raw"))]
  166. let handled_by_raw_socket = false;
  167. self.process_nxt_hdr(
  168. sockets,
  169. meta,
  170. ipv6_repr,
  171. next_header,
  172. handled_by_raw_socket,
  173. ip_payload,
  174. )
  175. }
  176. fn process_hopbyhop<'frame>(
  177. &mut self,
  178. ipv6_repr: Ipv6Repr,
  179. ip_payload: &'frame [u8],
  180. ) -> HopByHopResponse<'frame> {
  181. let param_problem = || {
  182. let payload_len =
  183. icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
  184. self.icmpv6_reply(
  185. ipv6_repr,
  186. Icmpv6Repr::ParamProblem {
  187. reason: Icmpv6ParamProblem::UnrecognizedOption,
  188. pointer: ipv6_repr.buffer_len() as u32,
  189. header: ipv6_repr,
  190. data: &ip_payload[0..payload_len],
  191. },
  192. )
  193. };
  194. let ext_hdr = check!(Ipv6ExtHeader::new_checked(ip_payload));
  195. let ext_repr = check!(Ipv6ExtHeaderRepr::parse(&ext_hdr));
  196. let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ext_repr.data));
  197. let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_hdr));
  198. for opt_repr in &hbh_repr.options {
  199. match opt_repr {
  200. Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (),
  201. #[cfg(feature = "proto-rpl")]
  202. Ipv6OptionRepr::Rpl(_) => {}
  203. Ipv6OptionRepr::Unknown { type_, .. } => {
  204. match Ipv6OptionFailureType::from(*type_) {
  205. Ipv6OptionFailureType::Skip => (),
  206. Ipv6OptionFailureType::Discard => {
  207. return HopByHopResponse::Discard(None);
  208. }
  209. Ipv6OptionFailureType::DiscardSendAll => {
  210. return HopByHopResponse::Discard(param_problem());
  211. }
  212. Ipv6OptionFailureType::DiscardSendUnicast
  213. if !ipv6_repr.dst_addr.is_multicast() =>
  214. {
  215. return HopByHopResponse::Discard(param_problem());
  216. }
  217. _ => unreachable!(),
  218. }
  219. }
  220. }
  221. }
  222. HopByHopResponse::Continue((
  223. ext_repr.next_header,
  224. &ip_payload[ext_repr.header_len() + ext_repr.data.len()..],
  225. ))
  226. }
  227. /// Given the next header value forward the payload onto the correct process
  228. /// function.
  229. fn process_nxt_hdr<'frame>(
  230. &mut self,
  231. sockets: &mut SocketSet,
  232. meta: PacketMeta,
  233. ipv6_repr: Ipv6Repr,
  234. nxt_hdr: IpProtocol,
  235. handled_by_raw_socket: bool,
  236. ip_payload: &'frame [u8],
  237. ) -> Option<Packet<'frame>> {
  238. match nxt_hdr {
  239. IpProtocol::Icmpv6 => self.process_icmpv6(sockets, ipv6_repr, ip_payload),
  240. #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
  241. IpProtocol::Udp => self.process_udp(
  242. sockets,
  243. meta,
  244. handled_by_raw_socket,
  245. ipv6_repr.into(),
  246. ip_payload,
  247. ),
  248. #[cfg(feature = "socket-tcp")]
  249. IpProtocol::Tcp => self.process_tcp(sockets, ipv6_repr.into(), ip_payload),
  250. #[cfg(feature = "socket-raw")]
  251. _ if handled_by_raw_socket => None,
  252. _ => {
  253. // Send back as much of the original payload as we can.
  254. let payload_len =
  255. icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
  256. let icmp_reply_repr = Icmpv6Repr::ParamProblem {
  257. reason: Icmpv6ParamProblem::UnrecognizedNxtHdr,
  258. // The offending packet is after the IPv6 header.
  259. pointer: ipv6_repr.buffer_len() as u32,
  260. header: ipv6_repr,
  261. data: &ip_payload[0..payload_len],
  262. };
  263. self.icmpv6_reply(ipv6_repr, icmp_reply_repr)
  264. }
  265. }
  266. }
  267. pub(super) fn process_icmpv6<'frame>(
  268. &mut self,
  269. _sockets: &mut SocketSet,
  270. ip_repr: Ipv6Repr,
  271. ip_payload: &'frame [u8],
  272. ) -> Option<Packet<'frame>> {
  273. let icmp_packet = check!(Icmpv6Packet::new_checked(ip_payload));
  274. let icmp_repr = check!(Icmpv6Repr::parse(
  275. &ip_repr.src_addr,
  276. &ip_repr.dst_addr,
  277. &icmp_packet,
  278. &self.caps.checksum,
  279. ));
  280. #[cfg(feature = "socket-icmp")]
  281. let mut handled_by_icmp_socket = false;
  282. #[cfg(feature = "socket-icmp")]
  283. {
  284. use crate::socket::icmp::Socket as IcmpSocket;
  285. for icmp_socket in _sockets
  286. .items_mut()
  287. .filter_map(|i| IcmpSocket::downcast_mut(&mut i.socket))
  288. {
  289. if icmp_socket.accepts_v6(self, &ip_repr, &icmp_repr) {
  290. icmp_socket.process_v6(self, &ip_repr, &icmp_repr);
  291. handled_by_icmp_socket = true;
  292. }
  293. }
  294. }
  295. match icmp_repr {
  296. // Respond to echo requests.
  297. Icmpv6Repr::EchoRequest {
  298. ident,
  299. seq_no,
  300. data,
  301. } => {
  302. let icmp_reply_repr = Icmpv6Repr::EchoReply {
  303. ident,
  304. seq_no,
  305. data,
  306. };
  307. self.icmpv6_reply(ip_repr, icmp_reply_repr)
  308. }
  309. // Ignore any echo replies.
  310. Icmpv6Repr::EchoReply { .. } => None,
  311. // Forward any NDISC packets to the ndisc packet handler
  312. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
  313. Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit == 0xff => match self.caps.medium {
  314. #[cfg(feature = "medium-ethernet")]
  315. Medium::Ethernet => self.process_ndisc(ip_repr, repr),
  316. #[cfg(feature = "medium-ieee802154")]
  317. Medium::Ieee802154 => self.process_ndisc(ip_repr, repr),
  318. #[cfg(feature = "medium-ip")]
  319. Medium::Ip => None,
  320. },
  321. // Don't report an error if a packet with unknown type
  322. // has been handled by an ICMP socket
  323. #[cfg(feature = "socket-icmp")]
  324. _ if handled_by_icmp_socket => None,
  325. // FIXME: do something correct here?
  326. _ => None,
  327. }
  328. }
  329. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
  330. pub(super) fn process_ndisc<'frame>(
  331. &mut self,
  332. ip_repr: Ipv6Repr,
  333. repr: NdiscRepr<'frame>,
  334. ) -> Option<Packet<'frame>> {
  335. match repr {
  336. NdiscRepr::NeighborAdvert {
  337. lladdr,
  338. target_addr,
  339. flags,
  340. } => {
  341. let ip_addr = ip_repr.src_addr.into();
  342. if let Some(lladdr) = lladdr {
  343. let lladdr = check!(lladdr.parse(self.caps.medium));
  344. if !lladdr.is_unicast() || !target_addr.is_unicast() {
  345. return None;
  346. }
  347. if flags.contains(NdiscNeighborFlags::OVERRIDE)
  348. || !self.neighbor_cache.lookup(&ip_addr, self.now).found()
  349. {
  350. self.neighbor_cache.fill(ip_addr, lladdr, self.now)
  351. }
  352. }
  353. None
  354. }
  355. NdiscRepr::NeighborSolicit {
  356. target_addr,
  357. lladdr,
  358. ..
  359. } => {
  360. if let Some(lladdr) = lladdr {
  361. let lladdr = check!(lladdr.parse(self.caps.medium));
  362. if !lladdr.is_unicast() || !target_addr.is_unicast() {
  363. return None;
  364. }
  365. self.neighbor_cache
  366. .fill(ip_repr.src_addr.into(), lladdr, self.now);
  367. }
  368. if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) {
  369. let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
  370. flags: NdiscNeighborFlags::SOLICITED,
  371. target_addr,
  372. #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
  373. lladdr: Some(self.hardware_addr.into()),
  374. });
  375. let ip_repr = Ipv6Repr {
  376. src_addr: target_addr,
  377. dst_addr: ip_repr.src_addr,
  378. next_header: IpProtocol::Icmpv6,
  379. hop_limit: 0xff,
  380. payload_len: advert.buffer_len(),
  381. };
  382. Some(Packet::new_ipv6(ip_repr, IpPayload::Icmpv6(advert)))
  383. } else {
  384. None
  385. }
  386. }
  387. _ => None,
  388. }
  389. }
  390. pub(super) fn icmpv6_reply<'frame, 'icmp: 'frame>(
  391. &self,
  392. ipv6_repr: Ipv6Repr,
  393. icmp_repr: Icmpv6Repr<'icmp>,
  394. ) -> Option<Packet<'frame>> {
  395. let src_addr = ipv6_repr.dst_addr;
  396. let dst_addr = ipv6_repr.src_addr;
  397. let src_addr = if src_addr.is_unicast() {
  398. src_addr
  399. } else if let Some(addr) = self.get_source_address_ipv6(&dst_addr) {
  400. addr
  401. } else {
  402. net_debug!("no suitable source address found");
  403. return None;
  404. };
  405. let ipv6_reply_repr = Ipv6Repr {
  406. src_addr,
  407. dst_addr,
  408. next_header: IpProtocol::Icmpv6,
  409. payload_len: icmp_repr.buffer_len(),
  410. hop_limit: 64,
  411. };
  412. Some(Packet::new_ipv6(
  413. ipv6_reply_repr,
  414. IpPayload::Icmpv6(icmp_repr),
  415. ))
  416. }
  417. }