1use core::convert::From;
2use core::fmt;
3
4use crate::phy::ChecksumCapabilities;
5#[cfg(feature = "proto-ipv4")]
6use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr};
7#[cfg(feature = "proto-ipv6")]
8use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
9use crate::{Error, Result};
10
11#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14#[non_exhaustive]
15pub enum Version {
16 Unspecified,
17 #[cfg(feature = "proto-ipv4")]
18 Ipv4,
19 #[cfg(feature = "proto-ipv6")]
20 Ipv6,
21}
22
23impl Version {
24 pub fn of_packet(data: &[u8]) -> Result<Version> {
29 match data[0] >> 4 {
30 #[cfg(feature = "proto-ipv4")]
31 4 => Ok(Version::Ipv4),
32 #[cfg(feature = "proto-ipv6")]
33 6 => Ok(Version::Ipv6),
34 _ => Err(Error::Unrecognized),
35 }
36 }
37}
38
39impl fmt::Display for Version {
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 match *self {
42 Version::Unspecified => write!(f, "IPv?"),
43 #[cfg(feature = "proto-ipv4")]
44 Version::Ipv4 => write!(f, "IPv4"),
45 #[cfg(feature = "proto-ipv6")]
46 Version::Ipv6 => write!(f, "IPv6"),
47 }
48 }
49}
50
51enum_with_unknown! {
52 pub enum Protocol(u8) {
54 HopByHop = 0x00,
55 Icmp = 0x01,
56 Igmp = 0x02,
57 Tcp = 0x06,
58 Udp = 0x11,
59 Ipv6Route = 0x2b,
60 Ipv6Frag = 0x2c,
61 Icmpv6 = 0x3a,
62 Ipv6NoNxt = 0x3b,
63 Ipv6Opts = 0x3c
64 }
65}
66
67impl fmt::Display for Protocol {
68 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69 match *self {
70 Protocol::HopByHop => write!(f, "Hop-by-Hop"),
71 Protocol::Icmp => write!(f, "ICMP"),
72 Protocol::Igmp => write!(f, "IGMP"),
73 Protocol::Tcp => write!(f, "TCP"),
74 Protocol::Udp => write!(f, "UDP"),
75 Protocol::Ipv6Route => write!(f, "IPv6-Route"),
76 Protocol::Ipv6Frag => write!(f, "IPv6-Frag"),
77 Protocol::Icmpv6 => write!(f, "ICMPv6"),
78 Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"),
79 Protocol::Ipv6Opts => write!(f, "IPv6-Opts"),
80 Protocol::Unknown(id) => write!(f, "0x{:02x}", id),
81 }
82 }
83}
84
85#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
87#[non_exhaustive]
88pub enum Address {
89 Unspecified,
92 #[cfg(feature = "proto-ipv4")]
94 Ipv4(Ipv4Address),
95 #[cfg(feature = "proto-ipv6")]
97 Ipv6(Ipv6Address),
98}
99
100impl Address {
101 #[cfg(feature = "proto-ipv4")]
103 pub fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address {
104 Address::Ipv4(Ipv4Address::new(a0, a1, a2, a3))
105 }
106
107 #[cfg(feature = "proto-ipv6")]
109 #[allow(clippy::too_many_arguments)]
110 pub fn v6(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
111 Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7))
112 }
113
114 pub fn as_bytes(&self) -> &[u8] {
116 match *self {
117 Address::Unspecified => &[],
118 #[cfg(feature = "proto-ipv4")]
119 Address::Ipv4(ref addr) => addr.as_bytes(),
120 #[cfg(feature = "proto-ipv6")]
121 Address::Ipv6(ref addr) => addr.as_bytes(),
122 }
123 }
124
125 pub fn is_unicast(&self) -> bool {
127 match *self {
128 Address::Unspecified => false,
129 #[cfg(feature = "proto-ipv4")]
130 Address::Ipv4(addr) => addr.is_unicast(),
131 #[cfg(feature = "proto-ipv6")]
132 Address::Ipv6(addr) => addr.is_unicast(),
133 }
134 }
135
136 pub fn is_multicast(&self) -> bool {
138 match *self {
139 Address::Unspecified => false,
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 pub fn is_broadcast(&self) -> bool {
149 match *self {
150 Address::Unspecified => false,
151 #[cfg(feature = "proto-ipv4")]
152 Address::Ipv4(addr) => addr.is_broadcast(),
153 #[cfg(feature = "proto-ipv6")]
154 Address::Ipv6(_) => false,
155 }
156 }
157
158 pub fn is_unspecified(&self) -> bool {
160 match *self {
161 Address::Unspecified => true,
162 #[cfg(feature = "proto-ipv4")]
163 Address::Ipv4(addr) => addr.is_unspecified(),
164 #[cfg(feature = "proto-ipv6")]
165 Address::Ipv6(addr) => addr.is_unspecified(),
166 }
167 }
168
169 pub fn as_unspecified(&self) -> Address {
171 match *self {
172 Address::Unspecified => Address::Unspecified,
173 #[cfg(feature = "proto-ipv4")]
174 Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED),
175 #[cfg(feature = "proto-ipv6")]
176 Address::Ipv6(_) => Address::Ipv6(Ipv6Address::UNSPECIFIED),
177 }
178 }
179
180 pub fn prefix_len(&self) -> Option<u8> {
183 let mut ones = true;
184 let mut prefix_len = 0;
185 for byte in self.as_bytes() {
186 let mut mask = 0x80;
187 for _ in 0..8 {
188 let one = *byte & mask != 0;
189 if ones {
190 if one {
192 prefix_len += 1;
193 } else {
194 ones = false;
195 }
196 } else if one {
197 return None;
199 }
200 mask >>= 1;
201 }
202 }
203 Some(prefix_len)
204 }
205}
206
207#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
208impl From<::std::net::IpAddr> for Address {
209 fn from(x: ::std::net::IpAddr) -> Address {
210 match x {
211 ::std::net::IpAddr::V4(ipv4) => Address::Ipv4(ipv4.into()),
212 ::std::net::IpAddr::V6(ipv6) => Address::Ipv6(ipv6.into()),
213 }
214 }
215}
216
217#[cfg(feature = "std")]
218impl From<Address> for ::std::net::IpAddr {
219 fn from(x: Address) -> ::std::net::IpAddr {
220 match x {
221 #[cfg(feature = "proto-ipv4")]
222 Address::Ipv4(ipv4) => ::std::net::IpAddr::V4(ipv4.into()),
223 #[cfg(feature = "proto-ipv6")]
224 Address::Ipv6(ipv6) => ::std::net::IpAddr::V6(ipv6.into()),
225 _ => unreachable!(),
226 }
227 }
228}
229
230#[cfg(all(feature = "std", feature = "proto-ipv4"))]
231impl From<::std::net::Ipv4Addr> for Address {
232 fn from(ipv4: ::std::net::Ipv4Addr) -> Address {
233 Address::Ipv4(ipv4.into())
234 }
235}
236
237#[cfg(all(feature = "std", feature = "proto-ipv6"))]
238impl From<::std::net::Ipv6Addr> for Address {
239 fn from(ipv6: ::std::net::Ipv6Addr) -> Address {
240 Address::Ipv6(ipv6.into())
241 }
242}
243
244impl Default for Address {
245 fn default() -> Address {
246 Address::Unspecified
247 }
248}
249
250#[cfg(feature = "proto-ipv4")]
251impl From<Ipv4Address> for Address {
252 fn from(addr: Ipv4Address) -> Self {
253 Address::Ipv4(addr)
254 }
255}
256
257#[cfg(feature = "proto-ipv6")]
258impl From<Ipv6Address> for Address {
259 fn from(addr: Ipv6Address) -> Self {
260 Address::Ipv6(addr)
261 }
262}
263
264impl fmt::Display for Address {
265 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266 match *self {
267 Address::Unspecified => write!(f, "*"),
268 #[cfg(feature = "proto-ipv4")]
269 Address::Ipv4(addr) => write!(f, "{}", addr),
270 #[cfg(feature = "proto-ipv6")]
271 Address::Ipv6(addr) => write!(f, "{}", addr),
272 }
273 }
274}
275
276#[cfg(feature = "defmt")]
277impl defmt::Format for Address {
278 fn format(&self, f: defmt::Formatter) {
279 match self {
280 &Address::Unspecified => defmt::write!(f, "{:?}", "*"),
281 #[cfg(feature = "proto-ipv4")]
282 &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr),
283 #[cfg(feature = "proto-ipv6")]
284 &Address::Ipv6(addr) => defmt::write!(f, "{:?}", addr),
285 }
286 }
287}
288
289#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
292#[non_exhaustive]
293pub enum Cidr {
294 #[cfg(feature = "proto-ipv4")]
295 Ipv4(Ipv4Cidr),
296 #[cfg(feature = "proto-ipv6")]
297 Ipv6(Ipv6Cidr),
298}
299
300impl Cidr {
301 pub fn new(addr: Address, prefix_len: u8) -> Cidr {
307 match addr {
308 #[cfg(feature = "proto-ipv4")]
309 Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
310 #[cfg(feature = "proto-ipv6")]
311 Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)),
312 Address::Unspecified => {
313 panic!("a CIDR block cannot be based on an unspecified address")
314 }
315 }
316 }
317
318 pub fn address(&self) -> Address {
320 match *self {
321 #[cfg(feature = "proto-ipv4")]
322 Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()),
323 #[cfg(feature = "proto-ipv6")]
324 Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()),
325 }
326 }
327
328 pub fn prefix_len(&self) -> u8 {
330 match *self {
331 #[cfg(feature = "proto-ipv4")]
332 Cidr::Ipv4(cidr) => cidr.prefix_len(),
333 #[cfg(feature = "proto-ipv6")]
334 Cidr::Ipv6(cidr) => cidr.prefix_len(),
335 }
336 }
337
338 pub fn contains_addr(&self, addr: &Address) -> bool {
341 match (self, addr) {
342 #[cfg(feature = "proto-ipv4")]
343 (&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) => cidr.contains_addr(addr),
344 #[cfg(feature = "proto-ipv6")]
345 (&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) => cidr.contains_addr(addr),
346 #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))]
347 (&Cidr::Ipv4(_), &Address::Ipv6(_)) | (&Cidr::Ipv6(_), &Address::Ipv4(_)) => false,
348 (_, &Address::Unspecified) =>
349 {
352 false
353 }
354 }
355 }
356
357 pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
360 match (self, subnet) {
361 #[cfg(feature = "proto-ipv4")]
362 (&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) => cidr.contains_subnet(other),
363 #[cfg(feature = "proto-ipv6")]
364 (&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) => cidr.contains_subnet(other),
365 #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))]
366 (&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) => false,
367 }
368 }
369}
370
371#[cfg(feature = "proto-ipv4")]
372impl From<Ipv4Cidr> for Cidr {
373 fn from(addr: Ipv4Cidr) -> Self {
374 Cidr::Ipv4(addr)
375 }
376}
377
378#[cfg(feature = "proto-ipv6")]
379impl From<Ipv6Cidr> for Cidr {
380 fn from(addr: Ipv6Cidr) -> Self {
381 Cidr::Ipv6(addr)
382 }
383}
384
385impl fmt::Display for Cidr {
386 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
387 match *self {
388 #[cfg(feature = "proto-ipv4")]
389 Cidr::Ipv4(cidr) => write!(f, "{}", cidr),
390 #[cfg(feature = "proto-ipv6")]
391 Cidr::Ipv6(cidr) => write!(f, "{}", cidr),
392 }
393 }
394}
395
396#[cfg(feature = "defmt")]
397impl defmt::Format for Cidr {
398 fn format(&self, f: defmt::Formatter) {
399 match self {
400 #[cfg(feature = "proto-ipv4")]
401 &Cidr::Ipv4(cidr) => defmt::write!(f, "{:?}", cidr),
402 #[cfg(feature = "proto-ipv6")]
403 &Cidr::Ipv6(cidr) => defmt::write!(f, "{:?}", cidr),
404 }
405 }
406}
407
408#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
412pub struct Endpoint {
413 pub addr: Address,
414 pub port: u16,
415}
416
417impl Endpoint {
418 pub const UNSPECIFIED: Endpoint = Endpoint {
420 addr: Address::Unspecified,
421 port: 0,
422 };
423
424 pub fn new(addr: Address, port: u16) -> Endpoint {
426 Endpoint { addr, port }
427 }
428
429 pub fn is_specified(&self) -> bool {
431 !self.addr.is_unspecified() && self.port != 0
432 }
433}
434
435#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
436impl From<::std::net::SocketAddr> for Endpoint {
437 fn from(x: ::std::net::SocketAddr) -> Endpoint {
438 Endpoint {
439 addr: x.ip().into(),
440 port: x.port(),
441 }
442 }
443}
444
445#[cfg(all(feature = "std", feature = "proto-ipv4"))]
446impl From<::std::net::SocketAddrV4> for Endpoint {
447 fn from(x: ::std::net::SocketAddrV4) -> Endpoint {
448 Endpoint {
449 addr: (*x.ip()).into(),
450 port: x.port(),
451 }
452 }
453}
454
455#[cfg(all(feature = "std", feature = "proto-ipv6"))]
456impl From<::std::net::SocketAddrV6> for Endpoint {
457 fn from(x: ::std::net::SocketAddrV6) -> Endpoint {
458 Endpoint {
459 addr: (*x.ip()).into(),
460 port: x.port(),
461 }
462 }
463}
464
465impl fmt::Display for Endpoint {
466 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
467 write!(f, "{}:{}", self.addr, self.port)
468 }
469}
470
471#[cfg(feature = "defmt")]
472impl defmt::Format for Endpoint {
473 fn format(&self, f: defmt::Formatter) {
474 defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
475 }
476}
477
478impl From<u16> for Endpoint {
479 fn from(port: u16) -> Endpoint {
480 Endpoint {
481 addr: Address::Unspecified,
482 port,
483 }
484 }
485}
486
487impl<T: Into<Address>> From<(T, u16)> for Endpoint {
488 fn from((addr, port): (T, u16)) -> Endpoint {
489 Endpoint {
490 addr: addr.into(),
491 port,
492 }
493 }
494}
495
496#[derive(Debug, Clone, PartialEq, Eq)]
502#[cfg_attr(feature = "defmt", derive(defmt::Format))]
503#[non_exhaustive]
504pub enum Repr {
505 Unspecified {
506 src_addr: Address,
507 dst_addr: Address,
508 protocol: Protocol,
509 payload_len: usize,
510 hop_limit: u8,
511 },
512 #[cfg(feature = "proto-ipv4")]
513 Ipv4(Ipv4Repr),
514 #[cfg(feature = "proto-ipv6")]
515 Ipv6(Ipv6Repr),
516}
517
518#[cfg(feature = "proto-ipv4")]
519impl From<Ipv4Repr> for Repr {
520 fn from(repr: Ipv4Repr) -> Repr {
521 Repr::Ipv4(repr)
522 }
523}
524
525#[cfg(feature = "proto-ipv6")]
526impl From<Ipv6Repr> for Repr {
527 fn from(repr: Ipv6Repr) -> Repr {
528 Repr::Ipv6(repr)
529 }
530}
531
532impl Repr {
533 pub fn version(&self) -> Version {
535 match *self {
536 Repr::Unspecified { .. } => Version::Unspecified,
537 #[cfg(feature = "proto-ipv4")]
538 Repr::Ipv4(_) => Version::Ipv4,
539 #[cfg(feature = "proto-ipv6")]
540 Repr::Ipv6(_) => Version::Ipv6,
541 }
542 }
543
544 pub fn src_addr(&self) -> Address {
546 match *self {
547 Repr::Unspecified { src_addr, .. } => src_addr,
548 #[cfg(feature = "proto-ipv4")]
549 Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
550 #[cfg(feature = "proto-ipv6")]
551 Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
552 }
553 }
554
555 pub fn dst_addr(&self) -> Address {
557 match *self {
558 Repr::Unspecified { dst_addr, .. } => dst_addr,
559 #[cfg(feature = "proto-ipv4")]
560 Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
561 #[cfg(feature = "proto-ipv6")]
562 Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
563 }
564 }
565
566 pub fn protocol(&self) -> Protocol {
568 match *self {
569 Repr::Unspecified { protocol, .. } => protocol,
570 #[cfg(feature = "proto-ipv4")]
571 Repr::Ipv4(repr) => repr.protocol,
572 #[cfg(feature = "proto-ipv6")]
573 Repr::Ipv6(repr) => repr.next_header,
574 }
575 }
576
577 pub fn payload_len(&self) -> usize {
579 match *self {
580 Repr::Unspecified { payload_len, .. } => payload_len,
581 #[cfg(feature = "proto-ipv4")]
582 Repr::Ipv4(repr) => repr.payload_len,
583 #[cfg(feature = "proto-ipv6")]
584 Repr::Ipv6(repr) => repr.payload_len,
585 }
586 }
587
588 pub fn set_payload_len(&mut self, length: usize) {
590 match *self {
591 Repr::Unspecified {
592 ref mut payload_len,
593 ..
594 } => *payload_len = length,
595 #[cfg(feature = "proto-ipv4")]
596 Repr::Ipv4(Ipv4Repr {
597 ref mut payload_len,
598 ..
599 }) => *payload_len = length,
600 #[cfg(feature = "proto-ipv6")]
601 Repr::Ipv6(Ipv6Repr {
602 ref mut payload_len,
603 ..
604 }) => *payload_len = length,
605 }
606 }
607
608 pub fn hop_limit(&self) -> u8 {
610 match *self {
611 Repr::Unspecified { hop_limit, .. } => hop_limit,
612 #[cfg(feature = "proto-ipv4")]
613 Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
614 #[cfg(feature = "proto-ipv6")]
615 Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
616 }
617 }
618
619 pub fn lower(&self, fallback_src_addrs: &[Cidr]) -> Result<Repr> {
626 macro_rules! resolve_unspecified {
627 ($reprty:path, $ipty:path, $iprepr:expr, $fallbacks:expr) => {
628 if $iprepr.src_addr.is_unspecified() {
629 for cidr in $fallbacks {
630 match cidr.address() {
631 $ipty(addr) => {
632 $iprepr.src_addr = addr;
633 return Ok($reprty($iprepr));
634 }
635 _ => (),
636 }
637 }
638 Err(Error::Unaddressable)
639 } else {
640 Ok($reprty($iprepr))
641 }
642 };
643 }
644
645 match self {
646 #[cfg(feature = "proto-ipv4")]
647 &Repr::Unspecified {
648 src_addr: src_addr @ Address::Unspecified,
649 dst_addr: Address::Ipv4(dst_addr),
650 protocol,
651 payload_len,
652 hop_limit,
653 }
654 | &Repr::Unspecified {
655 src_addr: src_addr @ Address::Ipv4(_),
656 dst_addr: Address::Ipv4(dst_addr),
657 protocol,
658 payload_len,
659 hop_limit,
660 } if src_addr.is_unspecified() => {
661 let mut src_addr = if let Address::Ipv4(src_ipv4_addr) = src_addr {
662 Some(src_ipv4_addr)
663 } else {
664 None
665 };
666 for cidr in fallback_src_addrs {
667 if let Address::Ipv4(addr) = cidr.address() {
668 src_addr = Some(addr);
669 break;
670 }
671 }
672 Ok(Repr::Ipv4(Ipv4Repr {
673 src_addr: src_addr.ok_or(Error::Unaddressable)?,
674 dst_addr,
675 protocol,
676 payload_len,
677 hop_limit,
678 }))
679 }
680
681 #[cfg(feature = "proto-ipv6")]
682 &Repr::Unspecified {
683 src_addr: src_addr @ Address::Unspecified,
684 dst_addr: Address::Ipv6(dst_addr),
685 protocol,
686 payload_len,
687 hop_limit,
688 }
689 | &Repr::Unspecified {
690 src_addr: src_addr @ Address::Ipv6(_),
691 dst_addr: Address::Ipv6(dst_addr),
692 protocol,
693 payload_len,
694 hop_limit,
695 } if src_addr.is_unspecified() => {
696 let mut src_addr = if let Address::Ipv6(src_ipv6_addr) = src_addr {
697 Some(src_ipv6_addr)
698 } else {
699 None
700 };
701 for cidr in fallback_src_addrs {
702 if let Address::Ipv6(addr) = cidr.address() {
703 src_addr = Some(addr);
704 break;
705 }
706 }
707 Ok(Repr::Ipv6(Ipv6Repr {
708 src_addr: src_addr.ok_or(Error::Unaddressable)?,
709 next_header: protocol,
710 dst_addr,
711 payload_len,
712 hop_limit,
713 }))
714 }
715
716 #[cfg(feature = "proto-ipv4")]
717 &Repr::Unspecified {
718 src_addr: Address::Ipv4(src_addr),
719 dst_addr: Address::Ipv4(dst_addr),
720 protocol,
721 payload_len,
722 hop_limit,
723 } => Ok(Repr::Ipv4(Ipv4Repr {
724 src_addr: src_addr,
725 dst_addr: dst_addr,
726 protocol: protocol,
727 payload_len: payload_len,
728 hop_limit,
729 })),
730
731 #[cfg(feature = "proto-ipv6")]
732 &Repr::Unspecified {
733 src_addr: Address::Ipv6(src_addr),
734 dst_addr: Address::Ipv6(dst_addr),
735 protocol,
736 payload_len,
737 hop_limit,
738 } => Ok(Repr::Ipv6(Ipv6Repr {
739 src_addr: src_addr,
740 dst_addr: dst_addr,
741 next_header: protocol,
742 payload_len: payload_len,
743 hop_limit: hop_limit,
744 })),
745
746 #[cfg(feature = "proto-ipv4")]
747 &Repr::Ipv4(mut repr) => {
748 resolve_unspecified!(Repr::Ipv4, Address::Ipv4, repr, fallback_src_addrs)
749 }
750
751 #[cfg(feature = "proto-ipv6")]
752 &Repr::Ipv6(mut repr) => {
753 resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs)
754 }
755
756 &Repr::Unspecified { .. } => {
757 panic!("source and destination IP address families do not match")
758 }
759 }
760 }
761
762 pub fn buffer_len(&self) -> usize {
767 match *self {
768 Repr::Unspecified { .. } => panic!("unspecified IP representation"),
769 #[cfg(feature = "proto-ipv4")]
770 Repr::Ipv4(repr) => repr.buffer_len(),
771 #[cfg(feature = "proto-ipv6")]
772 Repr::Ipv6(repr) => repr.buffer_len(),
773 }
774 }
775
776 pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
781 &self,
782 buffer: T,
783 _checksum_caps: &ChecksumCapabilities,
784 ) {
785 match *self {
786 Repr::Unspecified { .. } => panic!("unspecified IP representation"),
787 #[cfg(feature = "proto-ipv4")]
788 Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
789 #[cfg(feature = "proto-ipv6")]
790 Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
791 }
792 }
793
794 pub fn total_len(&self) -> usize {
802 self.buffer_len() + self.payload_len()
803 }
804}
805
806pub mod checksum {
807 use byteorder::{ByteOrder, NetworkEndian};
808
809 use super::*;
810
811 fn propagate_carries(word: u32) -> u16 {
812 let sum = (word >> 16) + (word & 0xffff);
813 ((sum >> 16) as u16) + (sum as u16)
814 }
815
816 pub fn data(mut data: &[u8]) -> u16 {
818 let mut accum = 0;
819
820 const CHUNK_SIZE: usize = 32;
822 while data.len() >= CHUNK_SIZE {
823 let mut d = &data[..CHUNK_SIZE];
824 while d.len() >= 2 {
826 accum += NetworkEndian::read_u16(d) as u32;
827 d = &d[2..];
828 }
829
830 data = &data[CHUNK_SIZE..];
831 }
832
833 while data.len() >= 2 {
836 accum += NetworkEndian::read_u16(data) as u32;
837 data = &data[2..];
838 }
839
840 if let Some(&value) = data.first() {
842 accum += (value as u32) << 8;
843 }
844
845 propagate_carries(accum)
846 }
847
848 pub fn combine(checksums: &[u16]) -> u16 {
850 let mut accum: u32 = 0;
851 for &word in checksums {
852 accum += word as u32;
853 }
854 propagate_carries(accum)
855 }
856
857 pub fn pseudo_header(
859 src_addr: &Address,
860 dst_addr: &Address,
861 protocol: Protocol,
862 length: u32,
863 ) -> u16 {
864 match (src_addr, dst_addr) {
865 #[cfg(feature = "proto-ipv4")]
866 (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
867 let mut proto_len = [0u8; 4];
868 proto_len[1] = protocol.into();
869 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
870
871 combine(&[
872 data(src_addr.as_bytes()),
873 data(dst_addr.as_bytes()),
874 data(&proto_len[..]),
875 ])
876 }
877
878 #[cfg(feature = "proto-ipv6")]
879 (&Address::Ipv6(src_addr), &Address::Ipv6(dst_addr)) => {
880 let mut proto_len = [0u8; 8];
881 proto_len[7] = protocol.into();
882 NetworkEndian::write_u32(&mut proto_len[0..4], length);
883 combine(&[
884 data(src_addr.as_bytes()),
885 data(dst_addr.as_bytes()),
886 data(&proto_len[..]),
887 ])
888 }
889
890 _ => panic!(
891 "Unexpected pseudo header addresses: {}, {}",
892 src_addr, dst_addr
893 ),
894 }
895 }
896
897 pub(crate) fn format_checksum(f: &mut fmt::Formatter, correct: bool) -> fmt::Result {
899 if !correct {
900 write!(f, " (checksum incorrect)")
901 } else {
902 Ok(())
903 }
904 }
905}
906
907use crate::wire::pretty_print::PrettyIndent;
908
909pub fn pretty_print_ip_payload<T: Into<Repr>>(
910 f: &mut fmt::Formatter,
911 indent: &mut PrettyIndent,
912 ip_repr: T,
913 payload: &[u8],
914) -> fmt::Result {
915 #[cfg(feature = "proto-ipv4")]
916 use super::pretty_print::PrettyPrint;
917 use crate::wire::ip::checksum::format_checksum;
918 #[cfg(feature = "proto-ipv4")]
919 use crate::wire::Icmpv4Packet;
920 use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
921
922 let checksum_caps = ChecksumCapabilities::ignored();
923 let repr = ip_repr.into();
924 match repr.protocol() {
925 #[cfg(feature = "proto-ipv4")]
926 Protocol::Icmp => {
927 indent.increase(f)?;
928 Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
929 }
930 Protocol::Udp => {
931 indent.increase(f)?;
932 match UdpPacket::<&[u8]>::new_checked(payload) {
933 Err(err) => write!(f, "{}({})", indent, err),
934 Ok(udp_packet) => {
935 match UdpRepr::parse(
936 &udp_packet,
937 &repr.src_addr(),
938 &repr.dst_addr(),
939 &checksum_caps,
940 ) {
941 Err(err) => write!(f, "{}{} ({})", indent, udp_packet, err),
942 Ok(udp_repr) => {
943 write!(
944 f,
945 "{}{} len={}",
946 indent,
947 udp_repr,
948 udp_packet.payload().len()
949 )?;
950 let valid =
951 udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
952 format_checksum(f, valid)
953 }
954 }
955 }
956 }
957 }
958 Protocol::Tcp => {
959 indent.increase(f)?;
960 match TcpPacket::<&[u8]>::new_checked(payload) {
961 Err(err) => write!(f, "{}({})", indent, err),
962 Ok(tcp_packet) => {
963 match TcpRepr::parse(
964 &tcp_packet,
965 &repr.src_addr(),
966 &repr.dst_addr(),
967 &checksum_caps,
968 ) {
969 Err(err) => write!(f, "{}{} ({})", indent, tcp_packet, err),
970 Ok(tcp_repr) => {
971 write!(f, "{}{}", indent, tcp_repr)?;
972 let valid =
973 tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
974 format_checksum(f, valid)
975 }
976 }
977 }
978 }
979 }
980 _ => Ok(()),
981 }
982}
983
984#[cfg(test)]
985pub(crate) mod test {
986 #![allow(unused)]
987
988 #[cfg(feature = "proto-ipv6")]
989 pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([
990 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
991 ]));
992 #[cfg(feature = "proto-ipv6")]
993 pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([
994 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
995 ]));
996 #[cfg(feature = "proto-ipv6")]
997 pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([
998 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
999 ]));
1000 #[cfg(feature = "proto-ipv6")]
1001 pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([
1002 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
1003 ]));
1004 #[cfg(feature = "proto-ipv6")]
1005 pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv6(Ipv6Address::UNSPECIFIED);
1006
1007 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
1008 pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1]));
1009 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
1010 pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 2]));
1011 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
1012 pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 3]));
1013 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
1014 pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 4]));
1015 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
1016 pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED);
1017
1018 use super::*;
1019 use crate::wire::{IpAddress, IpCidr, IpProtocol};
1020 #[cfg(feature = "proto-ipv4")]
1021 use crate::wire::{Ipv4Address, Ipv4Repr};
1022
1023 macro_rules! generate_common_tests {
1024 ($name:ident, $repr:ident, $ip_repr:path, $ip_addr:path,
1025 $addr_from:path, $nxthdr:ident, $bytes_a:expr, $bytes_b:expr,
1026 $unspecified:expr) => {
1027 mod $name {
1028 use super::*;
1029
1030 #[test]
1031 fn test_ip_repr_lower() {
1032 let ip_addr_a = $addr_from(&$bytes_a);
1033 let ip_addr_b = $addr_from(&$bytes_b);
1034 let proto = IpProtocol::Icmp;
1035 let payload_len = 10;
1036
1037 assert_eq!(
1038 Repr::Unspecified {
1039 src_addr: $ip_addr(ip_addr_a),
1040 dst_addr: $ip_addr(ip_addr_b),
1041 protocol: proto,
1042 hop_limit: 0x2a,
1043 payload_len,
1044 }
1045 .lower(&[]),
1046 Ok($ip_repr($repr {
1047 src_addr: ip_addr_a,
1048 dst_addr: ip_addr_b,
1049 $nxthdr: proto,
1050 hop_limit: 0x2a,
1051 payload_len
1052 }))
1053 );
1054
1055 assert_eq!(
1056 Repr::Unspecified {
1057 src_addr: IpAddress::Unspecified,
1058 dst_addr: $ip_addr(ip_addr_b),
1059 protocol: proto,
1060 hop_limit: 64,
1061 payload_len
1062 }
1063 .lower(&[]),
1064 Err(Error::Unaddressable)
1065 );
1066
1067 assert_eq!(
1068 Repr::Unspecified {
1069 src_addr: IpAddress::Unspecified,
1070 dst_addr: $ip_addr(ip_addr_b),
1071 protocol: proto,
1072 hop_limit: 64,
1073 payload_len
1074 }
1075 .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
1076 Ok($ip_repr($repr {
1077 src_addr: ip_addr_a,
1078 dst_addr: ip_addr_b,
1079 $nxthdr: proto,
1080 hop_limit: 64,
1081 payload_len
1082 }))
1083 );
1084
1085 assert_eq!(
1086 Repr::Unspecified {
1087 src_addr: $ip_addr($unspecified),
1088 dst_addr: $ip_addr(ip_addr_b),
1089 protocol: proto,
1090 hop_limit: 64,
1091 payload_len
1092 }
1093 .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
1094 Ok($ip_repr($repr {
1095 src_addr: ip_addr_a,
1096 dst_addr: ip_addr_b,
1097 $nxthdr: proto,
1098 hop_limit: 64,
1099 payload_len
1100 }))
1101 );
1102
1103 assert_eq!(
1104 Repr::Unspecified {
1105 src_addr: $ip_addr($unspecified),
1106 dst_addr: $ip_addr(ip_addr_b),
1107 protocol: proto,
1108 hop_limit: 64,
1109 payload_len
1110 }
1111 .lower(&[]),
1112 Ok($ip_repr($repr {
1113 src_addr: $unspecified,
1114 dst_addr: ip_addr_b,
1115 $nxthdr: proto,
1116 hop_limit: 64,
1117 payload_len
1118 }))
1119 );
1120
1121 assert_eq!(
1122 $ip_repr($repr {
1123 src_addr: ip_addr_a,
1124 dst_addr: ip_addr_b,
1125 $nxthdr: proto,
1126 hop_limit: 255,
1127 payload_len
1128 })
1129 .lower(&[]),
1130 Ok($ip_repr($repr {
1131 src_addr: ip_addr_a,
1132 dst_addr: ip_addr_b,
1133 $nxthdr: proto,
1134 hop_limit: 255,
1135 payload_len
1136 }))
1137 );
1138
1139 assert_eq!(
1140 $ip_repr($repr {
1141 src_addr: $unspecified,
1142 dst_addr: ip_addr_b,
1143 $nxthdr: proto,
1144 hop_limit: 255,
1145 payload_len
1146 })
1147 .lower(&[]),
1148 Err(Error::Unaddressable)
1149 );
1150
1151 assert_eq!(
1152 $ip_repr($repr {
1153 src_addr: $unspecified,
1154 dst_addr: ip_addr_b,
1155 $nxthdr: proto,
1156 hop_limit: 64,
1157 payload_len
1158 })
1159 .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
1160 Ok($ip_repr($repr {
1161 src_addr: ip_addr_a,
1162 dst_addr: ip_addr_b,
1163 $nxthdr: proto,
1164 hop_limit: 64,
1165 payload_len
1166 }))
1167 );
1168 }
1169 }
1170 };
1171 (ipv4 $addr_bytes_a:expr, $addr_bytes_b:expr) => {
1172 generate_common_tests!(
1173 ipv4,
1174 Ipv4Repr,
1175 Repr::Ipv4,
1176 IpAddress::Ipv4,
1177 Ipv4Address::from_bytes,
1178 protocol,
1179 $addr_bytes_a,
1180 $addr_bytes_b,
1181 Ipv4Address::UNSPECIFIED
1182 );
1183 };
1184 (ipv6 $addr_bytes_a:expr, $addr_bytes_b:expr) => {
1185 generate_common_tests!(
1186 ipv6,
1187 Ipv6Repr,
1188 Repr::Ipv6,
1189 IpAddress::Ipv6,
1190 Ipv6Address::from_bytes,
1191 next_header,
1192 $addr_bytes_a,
1193 $addr_bytes_b,
1194 Ipv6Address::UNSPECIFIED
1195 );
1196 };
1197 }
1198
1199 #[cfg(feature = "proto-ipv4")]
1200 generate_common_tests!(ipv4
1201 [1, 2, 3, 4],
1202 [5, 6, 7, 8]);
1203
1204 #[cfg(feature = "proto-ipv6")]
1205 generate_common_tests!(ipv6
1206 [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
1207 [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
1208
1209 #[test]
1210 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1211 #[should_panic(expected = "source and destination IP address families do not match")]
1212 fn test_lower_between_families() {
1213 Repr::Unspecified {
1214 src_addr: Address::Ipv6(Ipv6Address::UNSPECIFIED),
1215 dst_addr: Address::Ipv4(Ipv4Address::UNSPECIFIED),
1216 protocol: IpProtocol::Icmpv6,
1217 hop_limit: 0xff,
1218 payload_len: 0,
1219 }
1220 .lower(&[]);
1221 }
1222
1223 #[test]
1224 fn endpoint_unspecified() {
1225 assert!(!Endpoint::UNSPECIFIED.is_specified());
1226 }
1227
1228 #[test]
1229 #[cfg(feature = "proto-ipv4")]
1230 fn to_prefix_len_ipv4() {
1231 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
1232 assert_eq!(Some(prefix_len), mask.into().prefix_len());
1233 }
1234
1235 test_eq(0, Ipv4Address::new(0, 0, 0, 0));
1236 test_eq(1, Ipv4Address::new(128, 0, 0, 0));
1237 test_eq(2, Ipv4Address::new(192, 0, 0, 0));
1238 test_eq(3, Ipv4Address::new(224, 0, 0, 0));
1239 test_eq(4, Ipv4Address::new(240, 0, 0, 0));
1240 test_eq(5, Ipv4Address::new(248, 0, 0, 0));
1241 test_eq(6, Ipv4Address::new(252, 0, 0, 0));
1242 test_eq(7, Ipv4Address::new(254, 0, 0, 0));
1243 test_eq(8, Ipv4Address::new(255, 0, 0, 0));
1244 test_eq(9, Ipv4Address::new(255, 128, 0, 0));
1245 test_eq(10, Ipv4Address::new(255, 192, 0, 0));
1246 test_eq(11, Ipv4Address::new(255, 224, 0, 0));
1247 test_eq(12, Ipv4Address::new(255, 240, 0, 0));
1248 test_eq(13, Ipv4Address::new(255, 248, 0, 0));
1249 test_eq(14, Ipv4Address::new(255, 252, 0, 0));
1250 test_eq(15, Ipv4Address::new(255, 254, 0, 0));
1251 test_eq(16, Ipv4Address::new(255, 255, 0, 0));
1252 test_eq(17, Ipv4Address::new(255, 255, 128, 0));
1253 test_eq(18, Ipv4Address::new(255, 255, 192, 0));
1254 test_eq(19, Ipv4Address::new(255, 255, 224, 0));
1255 test_eq(20, Ipv4Address::new(255, 255, 240, 0));
1256 test_eq(21, Ipv4Address::new(255, 255, 248, 0));
1257 test_eq(22, Ipv4Address::new(255, 255, 252, 0));
1258 test_eq(23, Ipv4Address::new(255, 255, 254, 0));
1259 test_eq(24, Ipv4Address::new(255, 255, 255, 0));
1260 test_eq(25, Ipv4Address::new(255, 255, 255, 128));
1261 test_eq(26, Ipv4Address::new(255, 255, 255, 192));
1262 test_eq(27, Ipv4Address::new(255, 255, 255, 224));
1263 test_eq(28, Ipv4Address::new(255, 255, 255, 240));
1264 test_eq(29, Ipv4Address::new(255, 255, 255, 248));
1265 test_eq(30, Ipv4Address::new(255, 255, 255, 252));
1266 test_eq(31, Ipv4Address::new(255, 255, 255, 254));
1267 test_eq(32, Ipv4Address::new(255, 255, 255, 255));
1268 }
1269
1270 #[cfg(feature = "proto-ipv4")]
1271 fn to_prefix_len_ipv4_error() {
1272 assert_eq!(
1273 None,
1274 IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
1275 );
1276 }
1277
1278 #[test]
1279 #[cfg(feature = "proto-ipv6")]
1280 fn to_prefix_len_ipv6() {
1281 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
1282 assert_eq!(Some(prefix_len), mask.into().prefix_len());
1283 }
1284
1285 test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
1286 test_eq(
1287 128,
1288 Ipv6Address::new(
1289 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1290 ),
1291 );
1292 }
1293
1294 #[cfg(feature = "proto-ipv6")]
1295 fn to_prefix_len_ipv6_error() {
1296 assert_eq!(
1297 None,
1298 IpAddress::from(Ipv6Address::new(
1299 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
1300 ))
1301 .prefix_len()
1302 );
1303 }
1304}