1 use core::convert::From;
2 use core::fmt;
3
4 use super::{Error, Result};
5 use crate::phy::ChecksumCapabilities;
6 #[cfg(feature = "proto-ipv4")]
7 use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr};
8 #[cfg(feature = "proto-ipv6")]
9 use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
10
11 /// Internet protocol version.
12 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
13 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
14 pub enum Version {
15 #[cfg(feature = "proto-ipv4")]
16 Ipv4,
17 #[cfg(feature = "proto-ipv6")]
18 Ipv6,
19 }
20
21 impl Version {
22 /// Return the version of an IP packet stored in the provided buffer.
23 ///
24 /// This function never returns `Ok(IpVersion::Unspecified)`; instead,
25 /// unknown versions result in `Err(Error)`.
of_packet(data: &[u8]) -> Result<Version>26 pub const fn of_packet(data: &[u8]) -> Result<Version> {
27 match data[0] >> 4 {
28 #[cfg(feature = "proto-ipv4")]
29 4 => Ok(Version::Ipv4),
30 #[cfg(feature = "proto-ipv6")]
31 6 => Ok(Version::Ipv6),
32 _ => Err(Error),
33 }
34 }
35 }
36
37 impl fmt::Display for Version {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 match *self {
40 #[cfg(feature = "proto-ipv4")]
41 Version::Ipv4 => write!(f, "IPv4"),
42 #[cfg(feature = "proto-ipv6")]
43 Version::Ipv6 => write!(f, "IPv6"),
44 }
45 }
46 }
47
48 enum_with_unknown! {
49 /// IP datagram encapsulated protocol.
50 pub enum Protocol(u8) {
51 HopByHop = 0x00,
52 Icmp = 0x01,
53 Igmp = 0x02,
54 Tcp = 0x06,
55 Udp = 0x11,
56 Ipv6Route = 0x2b,
57 Ipv6Frag = 0x2c,
58 Icmpv6 = 0x3a,
59 Ipv6NoNxt = 0x3b,
60 Ipv6Opts = 0x3c
61 }
62 }
63
64 impl fmt::Display for Protocol {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 match *self {
67 Protocol::HopByHop => write!(f, "Hop-by-Hop"),
68 Protocol::Icmp => write!(f, "ICMP"),
69 Protocol::Igmp => write!(f, "IGMP"),
70 Protocol::Tcp => write!(f, "TCP"),
71 Protocol::Udp => write!(f, "UDP"),
72 Protocol::Ipv6Route => write!(f, "IPv6-Route"),
73 Protocol::Ipv6Frag => write!(f, "IPv6-Frag"),
74 Protocol::Icmpv6 => write!(f, "ICMPv6"),
75 Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"),
76 Protocol::Ipv6Opts => write!(f, "IPv6-Opts"),
77 Protocol::Unknown(id) => write!(f, "0x{id:02x}"),
78 }
79 }
80 }
81
82 /// An internetworking address.
83 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
84 pub enum Address {
85 /// An IPv4 address.
86 #[cfg(feature = "proto-ipv4")]
87 Ipv4(Ipv4Address),
88 /// An IPv6 address.
89 #[cfg(feature = "proto-ipv6")]
90 Ipv6(Ipv6Address),
91 }
92
93 impl Address {
94 /// Create an address wrapping an IPv4 address with the given octets.
95 #[cfg(feature = "proto-ipv4")]
v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address96 pub const fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address {
97 Address::Ipv4(Ipv4Address::new(a0, a1, a2, a3))
98 }
99
100 /// Create an address wrapping an IPv6 address with the given octets.
101 #[cfg(feature = "proto-ipv6")]
102 #[allow(clippy::too_many_arguments)]
v6(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address103 pub fn v6(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
104 Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7))
105 }
106
107 /// Return the protocol version.
version(&self) -> Version108 pub const fn version(&self) -> Version {
109 match self {
110 #[cfg(feature = "proto-ipv4")]
111 Address::Ipv4(_) => Version::Ipv4,
112 #[cfg(feature = "proto-ipv6")]
113 Address::Ipv6(_) => Version::Ipv6,
114 }
115 }
116
117 /// Return an address as a sequence of octets, in big-endian.
as_bytes(&self) -> &[u8]118 pub const fn as_bytes(&self) -> &[u8] {
119 match self {
120 #[cfg(feature = "proto-ipv4")]
121 Address::Ipv4(addr) => addr.as_bytes(),
122 #[cfg(feature = "proto-ipv6")]
123 Address::Ipv6(addr) => addr.as_bytes(),
124 }
125 }
126
127 /// Query whether the address is a valid unicast address.
is_unicast(&self) -> bool128 pub fn is_unicast(&self) -> bool {
129 match self {
130 #[cfg(feature = "proto-ipv4")]
131 Address::Ipv4(addr) => addr.is_unicast(),
132 #[cfg(feature = "proto-ipv6")]
133 Address::Ipv6(addr) => addr.is_unicast(),
134 }
135 }
136
137 /// Query whether the address is a valid multicast address.
is_multicast(&self) -> bool138 pub const fn is_multicast(&self) -> bool {
139 match self {
140 #[cfg(feature = "proto-ipv4")]
141 Address::Ipv4(addr) => addr.is_multicast(),
142 #[cfg(feature = "proto-ipv6")]
143 Address::Ipv6(addr) => addr.is_multicast(),
144 }
145 }
146
147 /// Query whether the address is the broadcast address.
is_broadcast(&self) -> bool148 pub fn is_broadcast(&self) -> bool {
149 match self {
150 #[cfg(feature = "proto-ipv4")]
151 Address::Ipv4(addr) => addr.is_broadcast(),
152 #[cfg(feature = "proto-ipv6")]
153 Address::Ipv6(_) => false,
154 }
155 }
156
157 /// Query whether the address falls into the "unspecified" range.
is_unspecified(&self) -> bool158 pub fn is_unspecified(&self) -> bool {
159 match self {
160 #[cfg(feature = "proto-ipv4")]
161 Address::Ipv4(addr) => addr.is_unspecified(),
162 #[cfg(feature = "proto-ipv6")]
163 Address::Ipv6(addr) => addr.is_unspecified(),
164 }
165 }
166
167 /// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`,
168 /// where `prefix_len` is the number of leading zeroes. Return `None` otherwise.
prefix_len(&self) -> Option<u8>169 pub fn prefix_len(&self) -> Option<u8> {
170 let mut ones = true;
171 let mut prefix_len = 0;
172 for byte in self.as_bytes() {
173 let mut mask = 0x80;
174 for _ in 0..8 {
175 let one = *byte & mask != 0;
176 if ones {
177 // Expect 1s until first 0
178 if one {
179 prefix_len += 1;
180 } else {
181 ones = false;
182 }
183 } else if one {
184 // 1 where 0 was expected
185 return None;
186 }
187 mask >>= 1;
188 }
189 }
190 Some(prefix_len)
191 }
192 }
193
194 #[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
195 impl From<::std::net::IpAddr> for Address {
from(x: ::std::net::IpAddr) -> Address196 fn from(x: ::std::net::IpAddr) -> Address {
197 match x {
198 ::std::net::IpAddr::V4(ipv4) => Address::Ipv4(ipv4.into()),
199 ::std::net::IpAddr::V6(ipv6) => Address::Ipv6(ipv6.into()),
200 }
201 }
202 }
203
204 #[cfg(feature = "std")]
205 impl From<Address> for ::std::net::IpAddr {
from(x: Address) -> ::std::net::IpAddr206 fn from(x: Address) -> ::std::net::IpAddr {
207 match x {
208 #[cfg(feature = "proto-ipv4")]
209 Address::Ipv4(ipv4) => ::std::net::IpAddr::V4(ipv4.into()),
210 #[cfg(feature = "proto-ipv6")]
211 Address::Ipv6(ipv6) => ::std::net::IpAddr::V6(ipv6.into()),
212 }
213 }
214 }
215
216 #[cfg(all(feature = "std", feature = "proto-ipv4"))]
217 impl From<::std::net::Ipv4Addr> for Address {
from(ipv4: ::std::net::Ipv4Addr) -> Address218 fn from(ipv4: ::std::net::Ipv4Addr) -> Address {
219 Address::Ipv4(ipv4.into())
220 }
221 }
222
223 #[cfg(all(feature = "std", feature = "proto-ipv6"))]
224 impl From<::std::net::Ipv6Addr> for Address {
from(ipv6: ::std::net::Ipv6Addr) -> Address225 fn from(ipv6: ::std::net::Ipv6Addr) -> Address {
226 Address::Ipv6(ipv6.into())
227 }
228 }
229
230 #[cfg(feature = "proto-ipv4")]
231 impl From<Ipv4Address> for Address {
from(addr: Ipv4Address) -> Self232 fn from(addr: Ipv4Address) -> Self {
233 Address::Ipv4(addr)
234 }
235 }
236
237 #[cfg(feature = "proto-ipv6")]
238 impl From<Ipv6Address> for Address {
from(addr: Ipv6Address) -> Self239 fn from(addr: Ipv6Address) -> Self {
240 Address::Ipv6(addr)
241 }
242 }
243
244 impl fmt::Display for Address {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result245 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246 match *self {
247 #[cfg(feature = "proto-ipv4")]
248 Address::Ipv4(addr) => write!(f, "{addr}"),
249 #[cfg(feature = "proto-ipv6")]
250 Address::Ipv6(addr) => write!(f, "{addr}"),
251 }
252 }
253 }
254
255 #[cfg(feature = "defmt")]
256 impl defmt::Format for Address {
format(&self, f: defmt::Formatter)257 fn format(&self, f: defmt::Formatter) {
258 match self {
259 #[cfg(feature = "proto-ipv4")]
260 &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr),
261 #[cfg(feature = "proto-ipv6")]
262 &Address::Ipv6(addr) => defmt::write!(f, "{:?}", addr),
263 }
264 }
265 }
266
267 /// A specification of a CIDR block, containing an address and a variable-length
268 /// subnet masking prefix length.
269 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
270 pub enum Cidr {
271 #[cfg(feature = "proto-ipv4")]
272 Ipv4(Ipv4Cidr),
273 #[cfg(feature = "proto-ipv6")]
274 Ipv6(Ipv6Cidr),
275 }
276
277 impl Cidr {
278 /// Create a CIDR block from the given address and prefix length.
279 ///
280 /// # Panics
281 /// This function panics if the given prefix length is invalid for the given address.
new(addr: Address, prefix_len: u8) -> Cidr282 pub fn new(addr: Address, prefix_len: u8) -> Cidr {
283 match addr {
284 #[cfg(feature = "proto-ipv4")]
285 Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
286 #[cfg(feature = "proto-ipv6")]
287 Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)),
288 }
289 }
290
291 /// Return the IP address of this CIDR block.
address(&self) -> Address292 pub const fn address(&self) -> Address {
293 match *self {
294 #[cfg(feature = "proto-ipv4")]
295 Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()),
296 #[cfg(feature = "proto-ipv6")]
297 Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()),
298 }
299 }
300
301 /// Return the prefix length of this CIDR block.
prefix_len(&self) -> u8302 pub const fn prefix_len(&self) -> u8 {
303 match *self {
304 #[cfg(feature = "proto-ipv4")]
305 Cidr::Ipv4(cidr) => cidr.prefix_len(),
306 #[cfg(feature = "proto-ipv6")]
307 Cidr::Ipv6(cidr) => cidr.prefix_len(),
308 }
309 }
310
311 /// Query whether the subnetwork described by this CIDR block contains
312 /// the given address.
contains_addr(&self, addr: &Address) -> bool313 pub fn contains_addr(&self, addr: &Address) -> bool {
314 match (self, addr) {
315 #[cfg(feature = "proto-ipv4")]
316 (Cidr::Ipv4(cidr), Address::Ipv4(addr)) => cidr.contains_addr(addr),
317 #[cfg(feature = "proto-ipv6")]
318 (Cidr::Ipv6(cidr), Address::Ipv6(addr)) => cidr.contains_addr(addr),
319 #[allow(unreachable_patterns)]
320 _ => false,
321 }
322 }
323
324 /// Query whether the subnetwork described by this CIDR block contains
325 /// the subnetwork described by the given CIDR block.
contains_subnet(&self, subnet: &Cidr) -> bool326 pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
327 match (self, subnet) {
328 #[cfg(feature = "proto-ipv4")]
329 (Cidr::Ipv4(cidr), Cidr::Ipv4(other)) => cidr.contains_subnet(other),
330 #[cfg(feature = "proto-ipv6")]
331 (Cidr::Ipv6(cidr), Cidr::Ipv6(other)) => cidr.contains_subnet(other),
332 #[allow(unreachable_patterns)]
333 _ => false,
334 }
335 }
336 }
337
338 #[cfg(feature = "proto-ipv4")]
339 impl From<Ipv4Cidr> for Cidr {
from(addr: Ipv4Cidr) -> Self340 fn from(addr: Ipv4Cidr) -> Self {
341 Cidr::Ipv4(addr)
342 }
343 }
344
345 #[cfg(feature = "proto-ipv6")]
346 impl From<Ipv6Cidr> for Cidr {
from(addr: Ipv6Cidr) -> Self347 fn from(addr: Ipv6Cidr) -> Self {
348 Cidr::Ipv6(addr)
349 }
350 }
351
352 impl fmt::Display for Cidr {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result353 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
354 match *self {
355 #[cfg(feature = "proto-ipv4")]
356 Cidr::Ipv4(cidr) => write!(f, "{cidr}"),
357 #[cfg(feature = "proto-ipv6")]
358 Cidr::Ipv6(cidr) => write!(f, "{cidr}"),
359 }
360 }
361 }
362
363 #[cfg(feature = "defmt")]
364 impl defmt::Format for Cidr {
format(&self, f: defmt::Formatter)365 fn format(&self, f: defmt::Formatter) {
366 match self {
367 #[cfg(feature = "proto-ipv4")]
368 &Cidr::Ipv4(cidr) => defmt::write!(f, "{:?}", cidr),
369 #[cfg(feature = "proto-ipv6")]
370 &Cidr::Ipv6(cidr) => defmt::write!(f, "{:?}", cidr),
371 }
372 }
373 }
374
375 /// An internet endpoint address.
376 ///
377 /// `Endpoint` always fully specifies both the address and the port.
378 ///
379 /// See also ['ListenEndpoint'], which allows not specifying the address
380 /// in order to listen on a given port on any address.
381 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
382 pub struct Endpoint {
383 pub addr: Address,
384 pub port: u16,
385 }
386
387 impl Endpoint {
388 /// Create an endpoint address from given address and port.
new(addr: Address, port: u16) -> Endpoint389 pub const fn new(addr: Address, port: u16) -> Endpoint {
390 Endpoint { addr: addr, port }
391 }
392 }
393
394 #[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
395 impl From<::std::net::SocketAddr> for Endpoint {
from(x: ::std::net::SocketAddr) -> Endpoint396 fn from(x: ::std::net::SocketAddr) -> Endpoint {
397 Endpoint {
398 addr: x.ip().into(),
399 port: x.port(),
400 }
401 }
402 }
403
404 #[cfg(all(feature = "std", feature = "proto-ipv4"))]
405 impl From<::std::net::SocketAddrV4> for Endpoint {
from(x: ::std::net::SocketAddrV4) -> Endpoint406 fn from(x: ::std::net::SocketAddrV4) -> Endpoint {
407 Endpoint {
408 addr: (*x.ip()).into(),
409 port: x.port(),
410 }
411 }
412 }
413
414 #[cfg(all(feature = "std", feature = "proto-ipv6"))]
415 impl From<::std::net::SocketAddrV6> for Endpoint {
from(x: ::std::net::SocketAddrV6) -> Endpoint416 fn from(x: ::std::net::SocketAddrV6) -> Endpoint {
417 Endpoint {
418 addr: (*x.ip()).into(),
419 port: x.port(),
420 }
421 }
422 }
423
424 impl fmt::Display for Endpoint {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result425 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
426 write!(f, "{}:{}", self.addr, self.port)
427 }
428 }
429
430 #[cfg(feature = "defmt")]
431 impl defmt::Format for Endpoint {
format(&self, f: defmt::Formatter)432 fn format(&self, f: defmt::Formatter) {
433 defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
434 }
435 }
436
437 impl<T: Into<Address>> From<(T, u16)> for Endpoint {
from((addr, port): (T, u16)) -> Endpoint438 fn from((addr, port): (T, u16)) -> Endpoint {
439 Endpoint {
440 addr: addr.into(),
441 port,
442 }
443 }
444 }
445
446 /// An internet endpoint address for listening.
447 ///
448 /// In contrast with [`Endpoint`], `ListenEndpoint` allows not specifying the address,
449 /// in order to listen on a given port at all our addresses.
450 ///
451 /// An endpoint can be constructed from a port, in which case the address is unspecified.
452 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
453 pub struct ListenEndpoint {
454 pub addr: Option<Address>,
455 pub port: u16,
456 }
457
458 impl ListenEndpoint {
459 /// Query whether the endpoint has a specified address and port.
is_specified(&self) -> bool460 pub const fn is_specified(&self) -> bool {
461 self.addr.is_some() && self.port != 0
462 }
463 }
464
465 #[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
466 impl From<::std::net::SocketAddr> for ListenEndpoint {
from(x: ::std::net::SocketAddr) -> ListenEndpoint467 fn from(x: ::std::net::SocketAddr) -> ListenEndpoint {
468 ListenEndpoint {
469 addr: Some(x.ip().into()),
470 port: x.port(),
471 }
472 }
473 }
474
475 #[cfg(all(feature = "std", feature = "proto-ipv4"))]
476 impl From<::std::net::SocketAddrV4> for ListenEndpoint {
from(x: ::std::net::SocketAddrV4) -> ListenEndpoint477 fn from(x: ::std::net::SocketAddrV4) -> ListenEndpoint {
478 ListenEndpoint {
479 addr: Some((*x.ip()).into()),
480 port: x.port(),
481 }
482 }
483 }
484
485 #[cfg(all(feature = "std", feature = "proto-ipv6"))]
486 impl From<::std::net::SocketAddrV6> for ListenEndpoint {
from(x: ::std::net::SocketAddrV6) -> ListenEndpoint487 fn from(x: ::std::net::SocketAddrV6) -> ListenEndpoint {
488 ListenEndpoint {
489 addr: Some((*x.ip()).into()),
490 port: x.port(),
491 }
492 }
493 }
494
495 impl fmt::Display for ListenEndpoint {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result496 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
497 if let Some(addr) = self.addr {
498 write!(f, "{}:{}", addr, self.port)
499 } else {
500 write!(f, "*:{}", self.port)
501 }
502 }
503 }
504
505 #[cfg(feature = "defmt")]
506 impl defmt::Format for ListenEndpoint {
format(&self, f: defmt::Formatter)507 fn format(&self, f: defmt::Formatter) {
508 defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
509 }
510 }
511
512 impl From<u16> for ListenEndpoint {
from(port: u16) -> ListenEndpoint513 fn from(port: u16) -> ListenEndpoint {
514 ListenEndpoint { addr: None, port }
515 }
516 }
517
518 impl From<Endpoint> for ListenEndpoint {
from(endpoint: Endpoint) -> ListenEndpoint519 fn from(endpoint: Endpoint) -> ListenEndpoint {
520 ListenEndpoint {
521 addr: Some(endpoint.addr),
522 port: endpoint.port,
523 }
524 }
525 }
526
527 impl<T: Into<Address>> From<(T, u16)> for ListenEndpoint {
from((addr, port): (T, u16)) -> ListenEndpoint528 fn from((addr, port): (T, u16)) -> ListenEndpoint {
529 ListenEndpoint {
530 addr: Some(addr.into()),
531 port,
532 }
533 }
534 }
535
536 /// An IP packet representation.
537 ///
538 /// This enum abstracts the various versions of IP packets. It either contains an IPv4
539 /// or IPv6 concrete high-level representation.
540 #[derive(Debug, Clone, PartialEq, Eq)]
541 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
542 pub enum Repr {
543 #[cfg(feature = "proto-ipv4")]
544 Ipv4(Ipv4Repr),
545 #[cfg(feature = "proto-ipv6")]
546 Ipv6(Ipv6Repr),
547 }
548
549 #[cfg(feature = "proto-ipv4")]
550 impl From<Ipv4Repr> for Repr {
from(repr: Ipv4Repr) -> Repr551 fn from(repr: Ipv4Repr) -> Repr {
552 Repr::Ipv4(repr)
553 }
554 }
555
556 #[cfg(feature = "proto-ipv6")]
557 impl From<Ipv6Repr> for Repr {
from(repr: Ipv6Repr) -> Repr558 fn from(repr: Ipv6Repr) -> Repr {
559 Repr::Ipv6(repr)
560 }
561 }
562
563 impl Repr {
564 /// Create a new IpRepr, choosing the right IP version for the src/dst addrs.
565 ///
566 /// # Panics
567 ///
568 /// Panics if `src_addr` and `dst_addr` are different IP version.
new( src_addr: Address, dst_addr: Address, next_header: Protocol, payload_len: usize, hop_limit: u8, ) -> Self569 pub fn new(
570 src_addr: Address,
571 dst_addr: Address,
572 next_header: Protocol,
573 payload_len: usize,
574 hop_limit: u8,
575 ) -> Self {
576 match (src_addr, dst_addr) {
577 #[cfg(feature = "proto-ipv4")]
578 (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => Self::Ipv4(Ipv4Repr {
579 src_addr,
580 dst_addr,
581 next_header,
582 payload_len,
583 hop_limit,
584 }),
585 #[cfg(feature = "proto-ipv6")]
586 (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => Self::Ipv6(Ipv6Repr {
587 src_addr,
588 dst_addr,
589 next_header,
590 payload_len,
591 hop_limit,
592 }),
593 #[allow(unreachable_patterns)]
594 _ => panic!("IP version mismatch: src={src_addr:?} dst={dst_addr:?}"),
595 }
596 }
597
598 /// Return the protocol version.
version(&self) -> Version599 pub const fn version(&self) -> Version {
600 match *self {
601 #[cfg(feature = "proto-ipv4")]
602 Repr::Ipv4(_) => Version::Ipv4,
603 #[cfg(feature = "proto-ipv6")]
604 Repr::Ipv6(_) => Version::Ipv6,
605 }
606 }
607
608 /// Return the source address.
src_addr(&self) -> Address609 pub const fn src_addr(&self) -> Address {
610 match *self {
611 #[cfg(feature = "proto-ipv4")]
612 Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
613 #[cfg(feature = "proto-ipv6")]
614 Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
615 }
616 }
617
618 /// Return the destination address.
dst_addr(&self) -> Address619 pub const fn dst_addr(&self) -> Address {
620 match *self {
621 #[cfg(feature = "proto-ipv4")]
622 Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
623 #[cfg(feature = "proto-ipv6")]
624 Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
625 }
626 }
627
628 /// Return the next header (protocol).
next_header(&self) -> Protocol629 pub const fn next_header(&self) -> Protocol {
630 match *self {
631 #[cfg(feature = "proto-ipv4")]
632 Repr::Ipv4(repr) => repr.next_header,
633 #[cfg(feature = "proto-ipv6")]
634 Repr::Ipv6(repr) => repr.next_header,
635 }
636 }
637
638 /// Return the payload length.
payload_len(&self) -> usize639 pub const fn payload_len(&self) -> usize {
640 match *self {
641 #[cfg(feature = "proto-ipv4")]
642 Repr::Ipv4(repr) => repr.payload_len,
643 #[cfg(feature = "proto-ipv6")]
644 Repr::Ipv6(repr) => repr.payload_len,
645 }
646 }
647
648 /// Set the payload length.
set_payload_len(&mut self, length: usize)649 pub fn set_payload_len(&mut self, length: usize) {
650 match self {
651 #[cfg(feature = "proto-ipv4")]
652 Repr::Ipv4(Ipv4Repr { payload_len, .. }) => *payload_len = length,
653 #[cfg(feature = "proto-ipv6")]
654 Repr::Ipv6(Ipv6Repr { payload_len, .. }) => *payload_len = length,
655 }
656 }
657
658 /// Return the TTL value.
hop_limit(&self) -> u8659 pub const fn hop_limit(&self) -> u8 {
660 match *self {
661 #[cfg(feature = "proto-ipv4")]
662 Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
663 #[cfg(feature = "proto-ipv6")]
664 Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
665 }
666 }
667
668 /// Return the length of a header that will be emitted from this high-level representation.
header_len(&self) -> usize669 pub const fn header_len(&self) -> usize {
670 match *self {
671 #[cfg(feature = "proto-ipv4")]
672 Repr::Ipv4(repr) => repr.buffer_len(),
673 #[cfg(feature = "proto-ipv6")]
674 Repr::Ipv6(repr) => repr.buffer_len(),
675 }
676 }
677
678 /// Emit this high-level representation into a buffer.
emit<T: AsRef<[u8]> + AsMut<[u8]>>( &self, buffer: T, _checksum_caps: &ChecksumCapabilities, )679 pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
680 &self,
681 buffer: T,
682 _checksum_caps: &ChecksumCapabilities,
683 ) {
684 match *self {
685 #[cfg(feature = "proto-ipv4")]
686 Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
687 #[cfg(feature = "proto-ipv6")]
688 Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
689 }
690 }
691
692 /// Return the total length of a packet that will be emitted from this
693 /// high-level representation.
694 ///
695 /// This is the same as `repr.buffer_len() + repr.payload_len()`.
buffer_len(&self) -> usize696 pub const fn buffer_len(&self) -> usize {
697 self.header_len() + self.payload_len()
698 }
699 }
700
701 pub mod checksum {
702 use byteorder::{ByteOrder, NetworkEndian};
703
704 use super::*;
705
propagate_carries(word: u32) -> u16706 const fn propagate_carries(word: u32) -> u16 {
707 let sum = (word >> 16) + (word & 0xffff);
708 ((sum >> 16) as u16) + (sum as u16)
709 }
710
711 /// Compute an RFC 1071 compliant checksum (without the final complement).
data(mut data: &[u8]) -> u16712 pub fn data(mut data: &[u8]) -> u16 {
713 let mut accum = 0;
714
715 // For each 32-byte chunk...
716 const CHUNK_SIZE: usize = 32;
717 while data.len() >= CHUNK_SIZE {
718 let mut d = &data[..CHUNK_SIZE];
719 // ... take by 2 bytes and sum them.
720 while d.len() >= 2 {
721 accum += NetworkEndian::read_u16(d) as u32;
722 d = &d[2..];
723 }
724
725 data = &data[CHUNK_SIZE..];
726 }
727
728 // Sum the rest that does not fit the last 32-byte chunk,
729 // taking by 2 bytes.
730 while data.len() >= 2 {
731 accum += NetworkEndian::read_u16(data) as u32;
732 data = &data[2..];
733 }
734
735 // Add the last remaining odd byte, if any.
736 if let Some(&value) = data.first() {
737 accum += (value as u32) << 8;
738 }
739
740 propagate_carries(accum)
741 }
742
743 /// Combine several RFC 1071 compliant checksums.
combine(checksums: &[u16]) -> u16744 pub fn combine(checksums: &[u16]) -> u16 {
745 let mut accum: u32 = 0;
746 for &word in checksums {
747 accum += word as u32;
748 }
749 propagate_carries(accum)
750 }
751
752 /// Compute an IP pseudo header checksum.
pseudo_header( src_addr: &Address, dst_addr: &Address, next_header: Protocol, length: u32, ) -> u16753 pub fn pseudo_header(
754 src_addr: &Address,
755 dst_addr: &Address,
756 next_header: Protocol,
757 length: u32,
758 ) -> u16 {
759 match (src_addr, dst_addr) {
760 #[cfg(feature = "proto-ipv4")]
761 (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
762 let mut proto_len = [0u8; 4];
763 proto_len[1] = next_header.into();
764 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
765
766 combine(&[
767 data(src_addr.as_bytes()),
768 data(dst_addr.as_bytes()),
769 data(&proto_len[..]),
770 ])
771 }
772
773 #[cfg(feature = "proto-ipv6")]
774 (&Address::Ipv6(src_addr), &Address::Ipv6(dst_addr)) => {
775 let mut proto_len = [0u8; 8];
776 proto_len[7] = next_header.into();
777 NetworkEndian::write_u32(&mut proto_len[0..4], length);
778 combine(&[
779 data(src_addr.as_bytes()),
780 data(dst_addr.as_bytes()),
781 data(&proto_len[..]),
782 ])
783 }
784
785 #[allow(unreachable_patterns)]
786 _ => panic!("Unexpected pseudo header addresses: {src_addr}, {dst_addr}"),
787 }
788 }
789
790 // We use this in pretty printer implementations.
format_checksum(f: &mut fmt::Formatter, correct: bool) -> fmt::Result791 pub(crate) fn format_checksum(f: &mut fmt::Formatter, correct: bool) -> fmt::Result {
792 if !correct {
793 write!(f, " (checksum incorrect)")
794 } else {
795 Ok(())
796 }
797 }
798 }
799
800 use crate::wire::pretty_print::PrettyIndent;
801
pretty_print_ip_payload<T: Into<Repr>>( f: &mut fmt::Formatter, indent: &mut PrettyIndent, ip_repr: T, payload: &[u8], ) -> fmt::Result802 pub fn pretty_print_ip_payload<T: Into<Repr>>(
803 f: &mut fmt::Formatter,
804 indent: &mut PrettyIndent,
805 ip_repr: T,
806 payload: &[u8],
807 ) -> fmt::Result {
808 #[cfg(feature = "proto-ipv4")]
809 use super::pretty_print::PrettyPrint;
810 use crate::wire::ip::checksum::format_checksum;
811 #[cfg(feature = "proto-ipv4")]
812 use crate::wire::Icmpv4Packet;
813 use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
814
815 let checksum_caps = ChecksumCapabilities::ignored();
816 let repr = ip_repr.into();
817 match repr.next_header() {
818 #[cfg(feature = "proto-ipv4")]
819 Protocol::Icmp => {
820 indent.increase(f)?;
821 Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
822 }
823 Protocol::Udp => {
824 indent.increase(f)?;
825 match UdpPacket::<&[u8]>::new_checked(payload) {
826 Err(err) => write!(f, "{indent}({err})"),
827 Ok(udp_packet) => {
828 match UdpRepr::parse(
829 &udp_packet,
830 &repr.src_addr(),
831 &repr.dst_addr(),
832 &checksum_caps,
833 ) {
834 Err(err) => write!(f, "{indent}{udp_packet} ({err})"),
835 Ok(udp_repr) => {
836 write!(
837 f,
838 "{}{} len={}",
839 indent,
840 udp_repr,
841 udp_packet.payload().len()
842 )?;
843 let valid =
844 udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
845 format_checksum(f, valid)
846 }
847 }
848 }
849 }
850 }
851 Protocol::Tcp => {
852 indent.increase(f)?;
853 match TcpPacket::<&[u8]>::new_checked(payload) {
854 Err(err) => write!(f, "{indent}({err})"),
855 Ok(tcp_packet) => {
856 match TcpRepr::parse(
857 &tcp_packet,
858 &repr.src_addr(),
859 &repr.dst_addr(),
860 &checksum_caps,
861 ) {
862 Err(err) => write!(f, "{indent}{tcp_packet} ({err})"),
863 Ok(tcp_repr) => {
864 write!(f, "{indent}{tcp_repr}")?;
865 let valid =
866 tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
867 format_checksum(f, valid)
868 }
869 }
870 }
871 }
872 }
873 _ => Ok(()),
874 }
875 }
876
877 #[cfg(test)]
878 pub(crate) mod test {
879 #![allow(unused)]
880
881 #[cfg(feature = "proto-ipv6")]
882 pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([
883 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
884 ]));
885 #[cfg(feature = "proto-ipv6")]
886 pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([
887 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
888 ]));
889 #[cfg(feature = "proto-ipv6")]
890 pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([
891 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
892 ]));
893 #[cfg(feature = "proto-ipv6")]
894 pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([
895 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
896 ]));
897 #[cfg(feature = "proto-ipv6")]
898 pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv6(Ipv6Address::UNSPECIFIED);
899
900 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
901 pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1]));
902 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
903 pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 2]));
904 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
905 pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 3]));
906 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
907 pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 4]));
908 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
909 pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED);
910
911 use super::*;
912 use crate::wire::{IpAddress, IpCidr, IpProtocol};
913 #[cfg(feature = "proto-ipv4")]
914 use crate::wire::{Ipv4Address, Ipv4Repr};
915
916 #[test]
917 #[cfg(feature = "proto-ipv4")]
to_prefix_len_ipv4()918 fn to_prefix_len_ipv4() {
919 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
920 assert_eq!(Some(prefix_len), mask.into().prefix_len());
921 }
922
923 test_eq(0, Ipv4Address::new(0, 0, 0, 0));
924 test_eq(1, Ipv4Address::new(128, 0, 0, 0));
925 test_eq(2, Ipv4Address::new(192, 0, 0, 0));
926 test_eq(3, Ipv4Address::new(224, 0, 0, 0));
927 test_eq(4, Ipv4Address::new(240, 0, 0, 0));
928 test_eq(5, Ipv4Address::new(248, 0, 0, 0));
929 test_eq(6, Ipv4Address::new(252, 0, 0, 0));
930 test_eq(7, Ipv4Address::new(254, 0, 0, 0));
931 test_eq(8, Ipv4Address::new(255, 0, 0, 0));
932 test_eq(9, Ipv4Address::new(255, 128, 0, 0));
933 test_eq(10, Ipv4Address::new(255, 192, 0, 0));
934 test_eq(11, Ipv4Address::new(255, 224, 0, 0));
935 test_eq(12, Ipv4Address::new(255, 240, 0, 0));
936 test_eq(13, Ipv4Address::new(255, 248, 0, 0));
937 test_eq(14, Ipv4Address::new(255, 252, 0, 0));
938 test_eq(15, Ipv4Address::new(255, 254, 0, 0));
939 test_eq(16, Ipv4Address::new(255, 255, 0, 0));
940 test_eq(17, Ipv4Address::new(255, 255, 128, 0));
941 test_eq(18, Ipv4Address::new(255, 255, 192, 0));
942 test_eq(19, Ipv4Address::new(255, 255, 224, 0));
943 test_eq(20, Ipv4Address::new(255, 255, 240, 0));
944 test_eq(21, Ipv4Address::new(255, 255, 248, 0));
945 test_eq(22, Ipv4Address::new(255, 255, 252, 0));
946 test_eq(23, Ipv4Address::new(255, 255, 254, 0));
947 test_eq(24, Ipv4Address::new(255, 255, 255, 0));
948 test_eq(25, Ipv4Address::new(255, 255, 255, 128));
949 test_eq(26, Ipv4Address::new(255, 255, 255, 192));
950 test_eq(27, Ipv4Address::new(255, 255, 255, 224));
951 test_eq(28, Ipv4Address::new(255, 255, 255, 240));
952 test_eq(29, Ipv4Address::new(255, 255, 255, 248));
953 test_eq(30, Ipv4Address::new(255, 255, 255, 252));
954 test_eq(31, Ipv4Address::new(255, 255, 255, 254));
955 test_eq(32, Ipv4Address::new(255, 255, 255, 255));
956 }
957
958 #[test]
959 #[cfg(feature = "proto-ipv4")]
to_prefix_len_ipv4_error()960 fn to_prefix_len_ipv4_error() {
961 assert_eq!(
962 None,
963 IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
964 );
965 }
966
967 #[test]
968 #[cfg(feature = "proto-ipv6")]
to_prefix_len_ipv6()969 fn to_prefix_len_ipv6() {
970 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
971 assert_eq!(Some(prefix_len), mask.into().prefix_len());
972 }
973
974 test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
975 test_eq(
976 128,
977 Ipv6Address::new(
978 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
979 ),
980 );
981 }
982
983 #[test]
984 #[cfg(feature = "proto-ipv6")]
to_prefix_len_ipv6_error()985 fn to_prefix_len_ipv6_error() {
986 assert_eq!(
987 None,
988 IpAddress::from(Ipv6Address::new(
989 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
990 ))
991 .prefix_len()
992 );
993 }
994 }
995