1use core::convert::From;
2use core::fmt;
3
4use super::{Error, Result};
5use crate::phy::ChecksumCapabilities;
6#[cfg(feature = "proto-ipv4")]
7use crate::wire::{Ipv4Address, Ipv4AddressExt, Ipv4Cidr, Ipv4Packet, Ipv4Repr};
8#[cfg(feature = "proto-ipv6")]
9use crate::wire::{Ipv6Address, Ipv6AddressExt, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
10
11#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14pub enum Version {
15 #[cfg(feature = "proto-ipv4")]
16 Ipv4,
17 #[cfg(feature = "proto-ipv6")]
18 Ipv6,
19}
20
21impl Version {
22 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
37impl fmt::Display for Version {
38 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
48enum_with_unknown! {
49 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 IpSecEsp = 0x32,
59 IpSecAh = 0x33,
60 Icmpv6 = 0x3a,
61 Ipv6NoNxt = 0x3b,
62 Ipv6Opts = 0x3c
63 }
64}
65
66impl fmt::Display for Protocol {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 match *self {
69 Protocol::HopByHop => write!(f, "Hop-by-Hop"),
70 Protocol::Icmp => write!(f, "ICMP"),
71 Protocol::Igmp => write!(f, "IGMP"),
72 Protocol::Tcp => write!(f, "TCP"),
73 Protocol::Udp => write!(f, "UDP"),
74 Protocol::Ipv6Route => write!(f, "IPv6-Route"),
75 Protocol::Ipv6Frag => write!(f, "IPv6-Frag"),
76 Protocol::IpSecEsp => write!(f, "IPsec-ESP"),
77 Protocol::IpSecAh => write!(f, "IPsec-AH"),
78 Protocol::Icmpv6 => write!(f, "ICMPv6"),
79 Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"),
80 Protocol::Ipv6Opts => write!(f, "IPv6-Opts"),
81 Protocol::Unknown(id) => write!(f, "0x{id:02x}"),
82 }
83 }
84}
85
86#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
88pub enum Address {
89 #[cfg(feature = "proto-ipv4")]
91 Ipv4(Ipv4Address),
92 #[cfg(feature = "proto-ipv6")]
94 Ipv6(Ipv6Address),
95}
96
97impl Address {
98 #[cfg(feature = "proto-ipv4")]
100 pub const fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address {
101 Address::Ipv4(Ipv4Address::new(a0, a1, a2, a3))
102 }
103
104 #[cfg(feature = "proto-ipv6")]
106 #[allow(clippy::too_many_arguments)]
107 pub const fn v6(
108 a0: u16,
109 a1: u16,
110 a2: u16,
111 a3: u16,
112 a4: u16,
113 a5: u16,
114 a6: u16,
115 a7: u16,
116 ) -> Address {
117 Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7))
118 }
119
120 pub const fn version(&self) -> Version {
122 match self {
123 #[cfg(feature = "proto-ipv4")]
124 Address::Ipv4(_) => Version::Ipv4,
125 #[cfg(feature = "proto-ipv6")]
126 Address::Ipv6(_) => Version::Ipv6,
127 }
128 }
129
130 pub fn is_unicast(&self) -> bool {
132 match self {
133 #[cfg(feature = "proto-ipv4")]
134 Address::Ipv4(addr) => addr.x_is_unicast(),
135 #[cfg(feature = "proto-ipv6")]
136 Address::Ipv6(addr) => addr.x_is_unicast(),
137 }
138 }
139
140 pub const fn is_multicast(&self) -> bool {
142 match self {
143 #[cfg(feature = "proto-ipv4")]
144 Address::Ipv4(addr) => addr.is_multicast(),
145 #[cfg(feature = "proto-ipv6")]
146 Address::Ipv6(addr) => addr.is_multicast(),
147 }
148 }
149
150 pub fn is_broadcast(&self) -> bool {
152 match self {
153 #[cfg(feature = "proto-ipv4")]
154 Address::Ipv4(addr) => addr.is_broadcast(),
155 #[cfg(feature = "proto-ipv6")]
156 Address::Ipv6(_) => false,
157 }
158 }
159
160 pub fn is_unspecified(&self) -> bool {
162 match self {
163 #[cfg(feature = "proto-ipv4")]
164 Address::Ipv4(addr) => addr.is_unspecified(),
165 #[cfg(feature = "proto-ipv6")]
166 Address::Ipv6(addr) => addr.is_unspecified(),
167 }
168 }
169
170 pub fn prefix_len(&self) -> Option<u8> {
173 match self {
174 #[cfg(feature = "proto-ipv4")]
175 Address::Ipv4(addr) => addr.prefix_len(),
176 #[cfg(feature = "proto-ipv6")]
177 Address::Ipv6(addr) => addr.prefix_len(),
178 }
179 }
180}
181
182#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
183impl From<::core::net::IpAddr> for Address {
184 fn from(x: ::core::net::IpAddr) -> Address {
185 match x {
186 ::core::net::IpAddr::V4(ipv4) => Address::Ipv4(ipv4),
187 ::core::net::IpAddr::V6(ipv6) => Address::Ipv6(ipv6),
188 }
189 }
190}
191
192impl From<Address> for ::core::net::IpAddr {
193 fn from(x: Address) -> ::core::net::IpAddr {
194 match x {
195 #[cfg(feature = "proto-ipv4")]
196 Address::Ipv4(ipv4) => ::core::net::IpAddr::V4(ipv4),
197 #[cfg(feature = "proto-ipv6")]
198 Address::Ipv6(ipv6) => ::core::net::IpAddr::V6(ipv6),
199 }
200 }
201}
202
203#[cfg(feature = "proto-ipv4")]
204impl From<Ipv4Address> for Address {
205 fn from(ipv4: Ipv4Address) -> Address {
206 Address::Ipv4(ipv4)
207 }
208}
209
210#[cfg(feature = "proto-ipv6")]
211impl From<Ipv6Address> for Address {
212 fn from(addr: Ipv6Address) -> Self {
213 Address::Ipv6(addr)
214 }
215}
216
217impl fmt::Display for Address {
218 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
219 match *self {
220 #[cfg(feature = "proto-ipv4")]
221 Address::Ipv4(addr) => write!(f, "{addr}"),
222 #[cfg(feature = "proto-ipv6")]
223 Address::Ipv6(addr) => write!(f, "{addr}"),
224 }
225 }
226}
227
228#[cfg(feature = "defmt")]
229impl defmt::Format for Address {
230 fn format(&self, f: defmt::Formatter) {
231 match self {
232 #[cfg(feature = "proto-ipv4")]
233 &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr),
234 #[cfg(feature = "proto-ipv6")]
235 &Address::Ipv6(addr) => defmt::write!(f, "{:?}", addr),
236 }
237 }
238}
239
240#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
243pub enum Cidr {
244 #[cfg(feature = "proto-ipv4")]
245 Ipv4(Ipv4Cidr),
246 #[cfg(feature = "proto-ipv6")]
247 Ipv6(Ipv6Cidr),
248}
249
250impl Cidr {
251 pub fn new(addr: Address, prefix_len: u8) -> Cidr {
256 match addr {
257 #[cfg(feature = "proto-ipv4")]
258 Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
259 #[cfg(feature = "proto-ipv6")]
260 Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)),
261 }
262 }
263
264 pub const fn address(&self) -> Address {
266 match *self {
267 #[cfg(feature = "proto-ipv4")]
268 Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()),
269 #[cfg(feature = "proto-ipv6")]
270 Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()),
271 }
272 }
273
274 pub const fn prefix_len(&self) -> u8 {
276 match *self {
277 #[cfg(feature = "proto-ipv4")]
278 Cidr::Ipv4(cidr) => cidr.prefix_len(),
279 #[cfg(feature = "proto-ipv6")]
280 Cidr::Ipv6(cidr) => cidr.prefix_len(),
281 }
282 }
283
284 pub fn contains_addr(&self, addr: &Address) -> bool {
287 match (self, addr) {
288 #[cfg(feature = "proto-ipv4")]
289 (Cidr::Ipv4(cidr), Address::Ipv4(addr)) => cidr.contains_addr(addr),
290 #[cfg(feature = "proto-ipv6")]
291 (Cidr::Ipv6(cidr), Address::Ipv6(addr)) => cidr.contains_addr(addr),
292 #[allow(unreachable_patterns)]
293 _ => false,
294 }
295 }
296
297 pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
300 match (self, subnet) {
301 #[cfg(feature = "proto-ipv4")]
302 (Cidr::Ipv4(cidr), Cidr::Ipv4(other)) => cidr.contains_subnet(other),
303 #[cfg(feature = "proto-ipv6")]
304 (Cidr::Ipv6(cidr), Cidr::Ipv6(other)) => cidr.contains_subnet(other),
305 #[allow(unreachable_patterns)]
306 _ => false,
307 }
308 }
309}
310
311#[cfg(feature = "proto-ipv4")]
312impl From<Ipv4Cidr> for Cidr {
313 fn from(addr: Ipv4Cidr) -> Self {
314 Cidr::Ipv4(addr)
315 }
316}
317
318#[cfg(feature = "proto-ipv6")]
319impl From<Ipv6Cidr> for Cidr {
320 fn from(addr: Ipv6Cidr) -> Self {
321 Cidr::Ipv6(addr)
322 }
323}
324
325impl fmt::Display for Cidr {
326 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
327 match *self {
328 #[cfg(feature = "proto-ipv4")]
329 Cidr::Ipv4(cidr) => write!(f, "{cidr}"),
330 #[cfg(feature = "proto-ipv6")]
331 Cidr::Ipv6(cidr) => write!(f, "{cidr}"),
332 }
333 }
334}
335
336#[cfg(feature = "defmt")]
337impl defmt::Format for Cidr {
338 fn format(&self, f: defmt::Formatter) {
339 match self {
340 #[cfg(feature = "proto-ipv4")]
341 &Cidr::Ipv4(cidr) => defmt::write!(f, "{:?}", cidr),
342 #[cfg(feature = "proto-ipv6")]
343 &Cidr::Ipv6(cidr) => defmt::write!(f, "{:?}", cidr),
344 }
345 }
346}
347
348#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
355pub struct Endpoint {
356 pub addr: Address,
357 pub port: u16,
358}
359
360impl Endpoint {
361 pub const fn new(addr: Address, port: u16) -> Endpoint {
363 Endpoint { addr, port }
364 }
365}
366
367#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
368impl From<::core::net::SocketAddr> for Endpoint {
369 fn from(x: ::core::net::SocketAddr) -> Endpoint {
370 Endpoint {
371 addr: x.ip().into(),
372 port: x.port(),
373 }
374 }
375}
376
377#[cfg(feature = "proto-ipv4")]
378impl From<::core::net::SocketAddrV4> for Endpoint {
379 fn from(x: ::core::net::SocketAddrV4) -> Endpoint {
380 Endpoint {
381 addr: (*x.ip()).into(),
382 port: x.port(),
383 }
384 }
385}
386
387#[cfg(feature = "proto-ipv6")]
388impl From<::core::net::SocketAddrV6> for Endpoint {
389 fn from(x: ::core::net::SocketAddrV6) -> Endpoint {
390 Endpoint {
391 addr: (*x.ip()).into(),
392 port: x.port(),
393 }
394 }
395}
396
397impl fmt::Display for Endpoint {
398 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
399 write!(f, "{}:{}", self.addr, self.port)
400 }
401}
402
403#[cfg(feature = "defmt")]
404impl defmt::Format for Endpoint {
405 fn format(&self, f: defmt::Formatter) {
406 defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
407 }
408}
409
410impl<T: Into<Address>> From<(T, u16)> for Endpoint {
411 fn from((addr, port): (T, u16)) -> Endpoint {
412 Endpoint {
413 addr: addr.into(),
414 port,
415 }
416 }
417}
418
419#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
426pub struct ListenEndpoint {
427 pub addr: Option<Address>,
428 pub port: u16,
429}
430
431impl ListenEndpoint {
432 pub const fn is_specified(&self) -> bool {
434 self.addr.is_some() && self.port != 0
435 }
436}
437
438#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
439impl From<::core::net::SocketAddr> for ListenEndpoint {
440 fn from(x: ::core::net::SocketAddr) -> ListenEndpoint {
441 ListenEndpoint {
442 addr: Some(x.ip().into()),
443 port: x.port(),
444 }
445 }
446}
447
448#[cfg(feature = "proto-ipv4")]
449impl From<::core::net::SocketAddrV4> for ListenEndpoint {
450 fn from(x: ::core::net::SocketAddrV4) -> ListenEndpoint {
451 ListenEndpoint {
452 addr: Some((*x.ip()).into()),
453 port: x.port(),
454 }
455 }
456}
457
458#[cfg(feature = "proto-ipv6")]
459impl From<::core::net::SocketAddrV6> for ListenEndpoint {
460 fn from(x: ::core::net::SocketAddrV6) -> ListenEndpoint {
461 ListenEndpoint {
462 addr: Some((*x.ip()).into()),
463 port: x.port(),
464 }
465 }
466}
467
468impl fmt::Display for ListenEndpoint {
469 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
470 if let Some(addr) = self.addr {
471 write!(f, "{}:{}", addr, self.port)
472 } else {
473 write!(f, "*:{}", self.port)
474 }
475 }
476}
477
478#[cfg(feature = "defmt")]
479impl defmt::Format for ListenEndpoint {
480 fn format(&self, f: defmt::Formatter) {
481 defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
482 }
483}
484
485impl From<u16> for ListenEndpoint {
486 fn from(port: u16) -> ListenEndpoint {
487 ListenEndpoint { addr: None, port }
488 }
489}
490
491impl From<Endpoint> for ListenEndpoint {
492 fn from(endpoint: Endpoint) -> ListenEndpoint {
493 ListenEndpoint {
494 addr: Some(endpoint.addr),
495 port: endpoint.port,
496 }
497 }
498}
499
500impl<T: Into<Address>> From<(T, u16)> for ListenEndpoint {
501 fn from((addr, port): (T, u16)) -> ListenEndpoint {
502 ListenEndpoint {
503 addr: Some(addr.into()),
504 port,
505 }
506 }
507}
508
509#[derive(Debug, Clone, PartialEq, Eq)]
514#[cfg_attr(feature = "defmt", derive(defmt::Format))]
515pub enum Repr {
516 #[cfg(feature = "proto-ipv4")]
517 Ipv4(Ipv4Repr),
518 #[cfg(feature = "proto-ipv6")]
519 Ipv6(Ipv6Repr),
520}
521
522#[cfg(feature = "proto-ipv4")]
523impl From<Ipv4Repr> for Repr {
524 fn from(repr: Ipv4Repr) -> Repr {
525 Repr::Ipv4(repr)
526 }
527}
528
529#[cfg(feature = "proto-ipv6")]
530impl From<Ipv6Repr> for Repr {
531 fn from(repr: Ipv6Repr) -> Repr {
532 Repr::Ipv6(repr)
533 }
534}
535
536impl Repr {
537 pub fn new(
543 src_addr: Address,
544 dst_addr: Address,
545 next_header: Protocol,
546 payload_len: usize,
547 hop_limit: u8,
548 ) -> Self {
549 match (src_addr, dst_addr) {
550 #[cfg(feature = "proto-ipv4")]
551 (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => Self::Ipv4(Ipv4Repr {
552 src_addr,
553 dst_addr,
554 next_header,
555 payload_len,
556 hop_limit,
557 }),
558 #[cfg(feature = "proto-ipv6")]
559 (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => Self::Ipv6(Ipv6Repr {
560 src_addr,
561 dst_addr,
562 next_header,
563 payload_len,
564 hop_limit,
565 }),
566 #[allow(unreachable_patterns)]
567 _ => panic!("IP version mismatch: src={src_addr:?} dst={dst_addr:?}"),
568 }
569 }
570
571 pub const fn version(&self) -> Version {
573 match *self {
574 #[cfg(feature = "proto-ipv4")]
575 Repr::Ipv4(_) => Version::Ipv4,
576 #[cfg(feature = "proto-ipv6")]
577 Repr::Ipv6(_) => Version::Ipv6,
578 }
579 }
580
581 pub const fn src_addr(&self) -> Address {
583 match *self {
584 #[cfg(feature = "proto-ipv4")]
585 Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
586 #[cfg(feature = "proto-ipv6")]
587 Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
588 }
589 }
590
591 pub const fn dst_addr(&self) -> Address {
593 match *self {
594 #[cfg(feature = "proto-ipv4")]
595 Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
596 #[cfg(feature = "proto-ipv6")]
597 Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
598 }
599 }
600
601 pub const fn next_header(&self) -> Protocol {
603 match *self {
604 #[cfg(feature = "proto-ipv4")]
605 Repr::Ipv4(repr) => repr.next_header,
606 #[cfg(feature = "proto-ipv6")]
607 Repr::Ipv6(repr) => repr.next_header,
608 }
609 }
610
611 pub const fn payload_len(&self) -> usize {
613 match *self {
614 #[cfg(feature = "proto-ipv4")]
615 Repr::Ipv4(repr) => repr.payload_len,
616 #[cfg(feature = "proto-ipv6")]
617 Repr::Ipv6(repr) => repr.payload_len,
618 }
619 }
620
621 pub fn set_payload_len(&mut self, length: usize) {
623 match self {
624 #[cfg(feature = "proto-ipv4")]
625 Repr::Ipv4(Ipv4Repr { payload_len, .. }) => *payload_len = length,
626 #[cfg(feature = "proto-ipv6")]
627 Repr::Ipv6(Ipv6Repr { payload_len, .. }) => *payload_len = length,
628 }
629 }
630
631 pub const fn hop_limit(&self) -> u8 {
633 match *self {
634 #[cfg(feature = "proto-ipv4")]
635 Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
636 #[cfg(feature = "proto-ipv6")]
637 Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
638 }
639 }
640
641 pub const fn header_len(&self) -> usize {
643 match *self {
644 #[cfg(feature = "proto-ipv4")]
645 Repr::Ipv4(repr) => repr.buffer_len(),
646 #[cfg(feature = "proto-ipv6")]
647 Repr::Ipv6(repr) => repr.buffer_len(),
648 }
649 }
650
651 pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
653 &self,
654 buffer: T,
655 _checksum_caps: &ChecksumCapabilities,
656 ) {
657 match *self {
658 #[cfg(feature = "proto-ipv4")]
659 Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
660 #[cfg(feature = "proto-ipv6")]
661 Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
662 }
663 }
664
665 pub const fn buffer_len(&self) -> usize {
670 self.header_len() + self.payload_len()
671 }
672}
673
674pub mod checksum {
675 use byteorder::{ByteOrder, NetworkEndian};
676
677 use super::*;
678
679 const fn propagate_carries(word: u32) -> u16 {
680 let sum = (word >> 16) + (word & 0xffff);
681 ((sum >> 16) as u16) + (sum as u16)
682 }
683
684 pub fn data(mut data: &[u8]) -> u16 {
686 let mut accum = 0;
687
688 const CHUNK_SIZE: usize = 32;
690 while data.len() >= CHUNK_SIZE {
691 let mut d = &data[..CHUNK_SIZE];
692 while d.len() >= 2 {
694 accum += NetworkEndian::read_u16(d) as u32;
695 d = &d[2..];
696 }
697
698 data = &data[CHUNK_SIZE..];
699 }
700
701 while data.len() >= 2 {
704 accum += NetworkEndian::read_u16(data) as u32;
705 data = &data[2..];
706 }
707
708 if let Some(&value) = data.first() {
710 accum += (value as u32) << 8;
711 }
712
713 propagate_carries(accum)
714 }
715
716 pub fn combine(checksums: &[u16]) -> u16 {
718 let mut accum: u32 = 0;
719 for &word in checksums {
720 accum += word as u32;
721 }
722 propagate_carries(accum)
723 }
724
725 #[cfg(feature = "proto-ipv4")]
726 pub fn pseudo_header_v4(
727 src_addr: &Ipv4Address,
728 dst_addr: &Ipv4Address,
729 next_header: Protocol,
730 length: u32,
731 ) -> u16 {
732 let mut proto_len = [0u8; 4];
733 proto_len[1] = next_header.into();
734 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
735
736 combine(&[
737 data(&src_addr.octets()),
738 data(&dst_addr.octets()),
739 data(&proto_len[..]),
740 ])
741 }
742
743 #[cfg(feature = "proto-ipv6")]
744 pub fn pseudo_header_v6(
745 src_addr: &Ipv6Address,
746 dst_addr: &Ipv6Address,
747 next_header: Protocol,
748 length: u32,
749 ) -> u16 {
750 let mut proto_len = [0u8; 4];
751 proto_len[1] = next_header.into();
752 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
753
754 combine(&[
755 data(&src_addr.octets()),
756 data(&dst_addr.octets()),
757 data(&proto_len[..]),
758 ])
759 }
760
761 pub fn pseudo_header(
762 src_addr: &Address,
763 dst_addr: &Address,
764 next_header: Protocol,
765 length: u32,
766 ) -> u16 {
767 match (src_addr, dst_addr) {
768 #[cfg(feature = "proto-ipv4")]
769 (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => {
770 pseudo_header_v4(src_addr, dst_addr, next_header, length)
771 }
772 #[cfg(feature = "proto-ipv6")]
773 (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => {
774 pseudo_header_v6(src_addr, dst_addr, next_header, length)
775 }
776 #[allow(unreachable_patterns)]
777 _ => unreachable!(),
778 }
779 }
780
781 pub(crate) fn format_checksum(f: &mut fmt::Formatter, correct: bool) -> fmt::Result {
783 if !correct {
784 write!(f, " (checksum incorrect)")
785 } else {
786 Ok(())
787 }
788 }
789}
790
791use crate::wire::pretty_print::PrettyIndent;
792
793pub fn pretty_print_ip_payload<T: Into<Repr>>(
794 f: &mut fmt::Formatter,
795 indent: &mut PrettyIndent,
796 ip_repr: T,
797 payload: &[u8],
798) -> fmt::Result {
799 #[cfg(feature = "proto-ipv4")]
800 use super::pretty_print::PrettyPrint;
801 use crate::wire::ip::checksum::format_checksum;
802 #[cfg(feature = "proto-ipv4")]
803 use crate::wire::Icmpv4Packet;
804 use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
805
806 let checksum_caps = ChecksumCapabilities::ignored();
807 let repr = ip_repr.into();
808 match repr.next_header() {
809 #[cfg(feature = "proto-ipv4")]
810 Protocol::Icmp => {
811 indent.increase(f)?;
812 Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
813 }
814 Protocol::Udp => {
815 indent.increase(f)?;
816 match UdpPacket::<&[u8]>::new_checked(payload) {
817 Err(err) => write!(f, "{indent}({err})"),
818 Ok(udp_packet) => {
819 match UdpRepr::parse(
820 &udp_packet,
821 &repr.src_addr(),
822 &repr.dst_addr(),
823 &checksum_caps,
824 ) {
825 Err(err) => write!(f, "{indent}{udp_packet} ({err})"),
826 Ok(udp_repr) => {
827 write!(
828 f,
829 "{}{} len={}",
830 indent,
831 udp_repr,
832 udp_packet.payload().len()
833 )?;
834 let valid =
835 udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
836 format_checksum(f, valid)
837 }
838 }
839 }
840 }
841 }
842 Protocol::Tcp => {
843 indent.increase(f)?;
844 match TcpPacket::<&[u8]>::new_checked(payload) {
845 Err(err) => write!(f, "{indent}({err})"),
846 Ok(tcp_packet) => {
847 match TcpRepr::parse(
848 &tcp_packet,
849 &repr.src_addr(),
850 &repr.dst_addr(),
851 &checksum_caps,
852 ) {
853 Err(err) => write!(f, "{indent}{tcp_packet} ({err})"),
854 Ok(tcp_repr) => {
855 write!(f, "{indent}{tcp_repr}")?;
856 let valid =
857 tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
858 format_checksum(f, valid)
859 }
860 }
861 }
862 }
863 }
864 _ => Ok(()),
865 }
866}
867
868#[cfg(test)]
869pub(crate) mod test {
870 #![allow(unused)]
871
872 use super::*;
873 use crate::wire::{IpAddress, IpCidr, IpProtocol};
874 #[cfg(feature = "proto-ipv4")]
875 use crate::wire::{Ipv4Address, Ipv4Repr};
876
877 #[test]
878 #[cfg(feature = "proto-ipv4")]
879 fn to_prefix_len_ipv4() {
880 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
881 assert_eq!(Some(prefix_len), mask.into().prefix_len());
882 }
883
884 test_eq(0, Ipv4Address::new(0, 0, 0, 0));
885 test_eq(1, Ipv4Address::new(128, 0, 0, 0));
886 test_eq(2, Ipv4Address::new(192, 0, 0, 0));
887 test_eq(3, Ipv4Address::new(224, 0, 0, 0));
888 test_eq(4, Ipv4Address::new(240, 0, 0, 0));
889 test_eq(5, Ipv4Address::new(248, 0, 0, 0));
890 test_eq(6, Ipv4Address::new(252, 0, 0, 0));
891 test_eq(7, Ipv4Address::new(254, 0, 0, 0));
892 test_eq(8, Ipv4Address::new(255, 0, 0, 0));
893 test_eq(9, Ipv4Address::new(255, 128, 0, 0));
894 test_eq(10, Ipv4Address::new(255, 192, 0, 0));
895 test_eq(11, Ipv4Address::new(255, 224, 0, 0));
896 test_eq(12, Ipv4Address::new(255, 240, 0, 0));
897 test_eq(13, Ipv4Address::new(255, 248, 0, 0));
898 test_eq(14, Ipv4Address::new(255, 252, 0, 0));
899 test_eq(15, Ipv4Address::new(255, 254, 0, 0));
900 test_eq(16, Ipv4Address::new(255, 255, 0, 0));
901 test_eq(17, Ipv4Address::new(255, 255, 128, 0));
902 test_eq(18, Ipv4Address::new(255, 255, 192, 0));
903 test_eq(19, Ipv4Address::new(255, 255, 224, 0));
904 test_eq(20, Ipv4Address::new(255, 255, 240, 0));
905 test_eq(21, Ipv4Address::new(255, 255, 248, 0));
906 test_eq(22, Ipv4Address::new(255, 255, 252, 0));
907 test_eq(23, Ipv4Address::new(255, 255, 254, 0));
908 test_eq(24, Ipv4Address::new(255, 255, 255, 0));
909 test_eq(25, Ipv4Address::new(255, 255, 255, 128));
910 test_eq(26, Ipv4Address::new(255, 255, 255, 192));
911 test_eq(27, Ipv4Address::new(255, 255, 255, 224));
912 test_eq(28, Ipv4Address::new(255, 255, 255, 240));
913 test_eq(29, Ipv4Address::new(255, 255, 255, 248));
914 test_eq(30, Ipv4Address::new(255, 255, 255, 252));
915 test_eq(31, Ipv4Address::new(255, 255, 255, 254));
916 test_eq(32, Ipv4Address::new(255, 255, 255, 255));
917 }
918
919 #[test]
920 #[cfg(feature = "proto-ipv4")]
921 fn to_prefix_len_ipv4_error() {
922 assert_eq!(
923 None,
924 IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
925 );
926 }
927
928 #[test]
929 #[cfg(feature = "proto-ipv6")]
930 fn to_prefix_len_ipv6() {
931 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
932 assert_eq!(Some(prefix_len), mask.into().prefix_len());
933 }
934
935 test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
936 test_eq(
937 128,
938 Ipv6Address::new(
939 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
940 ),
941 );
942 }
943
944 #[test]
945 #[cfg(feature = "proto-ipv6")]
946 fn to_prefix_len_ipv6_error() {
947 assert_eq!(
948 None,
949 IpAddress::from(Ipv6Address::new(
950 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
951 ))
952 .prefix_len()
953 );
954 }
955}