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, Ipv4Cidr, Ipv4Packet, Ipv4Repr};
8#[cfg(feature = "proto-ipv6")]
9use crate::wire::{Ipv6Address, 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 fn v6(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
108 Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7))
109 }
110
111 pub const fn version(&self) -> Version {
113 match self {
114 #[cfg(feature = "proto-ipv4")]
115 Address::Ipv4(_) => Version::Ipv4,
116 #[cfg(feature = "proto-ipv6")]
117 Address::Ipv6(_) => Version::Ipv6,
118 }
119 }
120
121 pub const fn as_bytes(&self) -> &[u8] {
123 match self {
124 #[cfg(feature = "proto-ipv4")]
125 Address::Ipv4(addr) => addr.as_bytes(),
126 #[cfg(feature = "proto-ipv6")]
127 Address::Ipv6(addr) => addr.as_bytes(),
128 }
129 }
130
131 pub fn is_unicast(&self) -> bool {
133 match self {
134 #[cfg(feature = "proto-ipv4")]
135 Address::Ipv4(addr) => addr.is_unicast(),
136 #[cfg(feature = "proto-ipv6")]
137 Address::Ipv6(addr) => addr.is_unicast(),
138 }
139 }
140
141 pub const fn is_multicast(&self) -> bool {
143 match self {
144 #[cfg(feature = "proto-ipv4")]
145 Address::Ipv4(addr) => addr.is_multicast(),
146 #[cfg(feature = "proto-ipv6")]
147 Address::Ipv6(addr) => addr.is_multicast(),
148 }
149 }
150
151 pub fn is_broadcast(&self) -> bool {
153 match self {
154 #[cfg(feature = "proto-ipv4")]
155 Address::Ipv4(addr) => addr.is_broadcast(),
156 #[cfg(feature = "proto-ipv6")]
157 Address::Ipv6(_) => false,
158 }
159 }
160
161 pub fn is_unspecified(&self) -> bool {
163 match self {
164 #[cfg(feature = "proto-ipv4")]
165 Address::Ipv4(addr) => addr.is_unspecified(),
166 #[cfg(feature = "proto-ipv6")]
167 Address::Ipv6(addr) => addr.is_unspecified(),
168 }
169 }
170
171 pub fn prefix_len(&self) -> Option<u8> {
174 let mut ones = true;
175 let mut prefix_len = 0;
176 for byte in self.as_bytes() {
177 let mut mask = 0x80;
178 for _ in 0..8 {
179 let one = *byte & mask != 0;
180 if ones {
181 if one {
183 prefix_len += 1;
184 } else {
185 ones = false;
186 }
187 } else if one {
188 return None;
190 }
191 mask >>= 1;
192 }
193 }
194 Some(prefix_len)
195 }
196}
197
198#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
199impl From<::std::net::IpAddr> for Address {
200 fn from(x: ::std::net::IpAddr) -> Address {
201 match x {
202 ::std::net::IpAddr::V4(ipv4) => Address::Ipv4(ipv4.into()),
203 ::std::net::IpAddr::V6(ipv6) => Address::Ipv6(ipv6.into()),
204 }
205 }
206}
207
208#[cfg(feature = "std")]
209impl From<Address> for ::std::net::IpAddr {
210 fn from(x: Address) -> ::std::net::IpAddr {
211 match x {
212 #[cfg(feature = "proto-ipv4")]
213 Address::Ipv4(ipv4) => ::std::net::IpAddr::V4(ipv4.into()),
214 #[cfg(feature = "proto-ipv6")]
215 Address::Ipv6(ipv6) => ::std::net::IpAddr::V6(ipv6.into()),
216 }
217 }
218}
219
220#[cfg(all(feature = "std", feature = "proto-ipv4"))]
221impl From<::std::net::Ipv4Addr> for Address {
222 fn from(ipv4: ::std::net::Ipv4Addr) -> Address {
223 Address::Ipv4(ipv4.into())
224 }
225}
226
227#[cfg(all(feature = "std", feature = "proto-ipv6"))]
228impl From<::std::net::Ipv6Addr> for Address {
229 fn from(ipv6: ::std::net::Ipv6Addr) -> Address {
230 Address::Ipv6(ipv6.into())
231 }
232}
233
234#[cfg(feature = "proto-ipv4")]
235impl From<Ipv4Address> for Address {
236 fn from(addr: Ipv4Address) -> Self {
237 Address::Ipv4(addr)
238 }
239}
240
241#[cfg(feature = "proto-ipv6")]
242impl From<Ipv6Address> for Address {
243 fn from(addr: Ipv6Address) -> Self {
244 Address::Ipv6(addr)
245 }
246}
247
248impl fmt::Display for Address {
249 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250 match *self {
251 #[cfg(feature = "proto-ipv4")]
252 Address::Ipv4(addr) => write!(f, "{addr}"),
253 #[cfg(feature = "proto-ipv6")]
254 Address::Ipv6(addr) => write!(f, "{addr}"),
255 }
256 }
257}
258
259#[cfg(feature = "defmt")]
260impl defmt::Format for Address {
261 fn format(&self, f: defmt::Formatter) {
262 match self {
263 #[cfg(feature = "proto-ipv4")]
264 &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr),
265 #[cfg(feature = "proto-ipv6")]
266 &Address::Ipv6(addr) => defmt::write!(f, "{:?}", addr),
267 }
268 }
269}
270
271#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
274pub enum Cidr {
275 #[cfg(feature = "proto-ipv4")]
276 Ipv4(Ipv4Cidr),
277 #[cfg(feature = "proto-ipv6")]
278 Ipv6(Ipv6Cidr),
279}
280
281impl Cidr {
282 pub fn new(addr: Address, prefix_len: u8) -> Cidr {
287 match addr {
288 #[cfg(feature = "proto-ipv4")]
289 Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
290 #[cfg(feature = "proto-ipv6")]
291 Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)),
292 }
293 }
294
295 pub const fn address(&self) -> Address {
297 match *self {
298 #[cfg(feature = "proto-ipv4")]
299 Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()),
300 #[cfg(feature = "proto-ipv6")]
301 Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()),
302 }
303 }
304
305 pub const fn prefix_len(&self) -> u8 {
307 match *self {
308 #[cfg(feature = "proto-ipv4")]
309 Cidr::Ipv4(cidr) => cidr.prefix_len(),
310 #[cfg(feature = "proto-ipv6")]
311 Cidr::Ipv6(cidr) => cidr.prefix_len(),
312 }
313 }
314
315 pub fn contains_addr(&self, addr: &Address) -> bool {
318 match (self, addr) {
319 #[cfg(feature = "proto-ipv4")]
320 (Cidr::Ipv4(cidr), Address::Ipv4(addr)) => cidr.contains_addr(addr),
321 #[cfg(feature = "proto-ipv6")]
322 (Cidr::Ipv6(cidr), Address::Ipv6(addr)) => cidr.contains_addr(addr),
323 #[allow(unreachable_patterns)]
324 _ => false,
325 }
326 }
327
328 pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
331 match (self, subnet) {
332 #[cfg(feature = "proto-ipv4")]
333 (Cidr::Ipv4(cidr), Cidr::Ipv4(other)) => cidr.contains_subnet(other),
334 #[cfg(feature = "proto-ipv6")]
335 (Cidr::Ipv6(cidr), Cidr::Ipv6(other)) => cidr.contains_subnet(other),
336 #[allow(unreachable_patterns)]
337 _ => false,
338 }
339 }
340}
341
342#[cfg(feature = "proto-ipv4")]
343impl From<Ipv4Cidr> for Cidr {
344 fn from(addr: Ipv4Cidr) -> Self {
345 Cidr::Ipv4(addr)
346 }
347}
348
349#[cfg(feature = "proto-ipv6")]
350impl From<Ipv6Cidr> for Cidr {
351 fn from(addr: Ipv6Cidr) -> Self {
352 Cidr::Ipv6(addr)
353 }
354}
355
356impl fmt::Display for Cidr {
357 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
358 match *self {
359 #[cfg(feature = "proto-ipv4")]
360 Cidr::Ipv4(cidr) => write!(f, "{cidr}"),
361 #[cfg(feature = "proto-ipv6")]
362 Cidr::Ipv6(cidr) => write!(f, "{cidr}"),
363 }
364 }
365}
366
367#[cfg(feature = "defmt")]
368impl defmt::Format for Cidr {
369 fn format(&self, f: defmt::Formatter) {
370 match self {
371 #[cfg(feature = "proto-ipv4")]
372 &Cidr::Ipv4(cidr) => defmt::write!(f, "{:?}", cidr),
373 #[cfg(feature = "proto-ipv6")]
374 &Cidr::Ipv6(cidr) => defmt::write!(f, "{:?}", cidr),
375 }
376 }
377}
378
379#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
386pub struct Endpoint {
387 pub addr: Address,
388 pub port: u16,
389}
390
391impl Endpoint {
392 pub const fn new(addr: Address, port: u16) -> Endpoint {
394 Endpoint { addr: addr, port }
395 }
396}
397
398#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
399impl From<::std::net::SocketAddr> for Endpoint {
400 fn from(x: ::std::net::SocketAddr) -> Endpoint {
401 Endpoint {
402 addr: x.ip().into(),
403 port: x.port(),
404 }
405 }
406}
407
408#[cfg(all(feature = "std", feature = "proto-ipv4"))]
409impl From<::std::net::SocketAddrV4> for Endpoint {
410 fn from(x: ::std::net::SocketAddrV4) -> Endpoint {
411 Endpoint {
412 addr: (*x.ip()).into(),
413 port: x.port(),
414 }
415 }
416}
417
418#[cfg(all(feature = "std", feature = "proto-ipv6"))]
419impl From<::std::net::SocketAddrV6> for Endpoint {
420 fn from(x: ::std::net::SocketAddrV6) -> Endpoint {
421 Endpoint {
422 addr: (*x.ip()).into(),
423 port: x.port(),
424 }
425 }
426}
427
428impl fmt::Display for Endpoint {
429 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
430 write!(f, "{}:{}", self.addr, self.port)
431 }
432}
433
434#[cfg(feature = "defmt")]
435impl defmt::Format for Endpoint {
436 fn format(&self, f: defmt::Formatter) {
437 defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
438 }
439}
440
441impl<T: Into<Address>> From<(T, u16)> for Endpoint {
442 fn from((addr, port): (T, u16)) -> Endpoint {
443 Endpoint {
444 addr: addr.into(),
445 port,
446 }
447 }
448}
449
450#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
457pub struct ListenEndpoint {
458 pub addr: Option<Address>,
459 pub port: u16,
460}
461
462impl ListenEndpoint {
463 pub const fn is_specified(&self) -> bool {
465 self.addr.is_some() && self.port != 0
466 }
467}
468
469#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
470impl From<::std::net::SocketAddr> for ListenEndpoint {
471 fn from(x: ::std::net::SocketAddr) -> ListenEndpoint {
472 ListenEndpoint {
473 addr: Some(x.ip().into()),
474 port: x.port(),
475 }
476 }
477}
478
479#[cfg(all(feature = "std", feature = "proto-ipv4"))]
480impl From<::std::net::SocketAddrV4> for ListenEndpoint {
481 fn from(x: ::std::net::SocketAddrV4) -> ListenEndpoint {
482 ListenEndpoint {
483 addr: Some((*x.ip()).into()),
484 port: x.port(),
485 }
486 }
487}
488
489#[cfg(all(feature = "std", feature = "proto-ipv6"))]
490impl From<::std::net::SocketAddrV6> for ListenEndpoint {
491 fn from(x: ::std::net::SocketAddrV6) -> ListenEndpoint {
492 ListenEndpoint {
493 addr: Some((*x.ip()).into()),
494 port: x.port(),
495 }
496 }
497}
498
499impl fmt::Display for ListenEndpoint {
500 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
501 if let Some(addr) = self.addr {
502 write!(f, "{}:{}", addr, self.port)
503 } else {
504 write!(f, "*:{}", self.port)
505 }
506 }
507}
508
509#[cfg(feature = "defmt")]
510impl defmt::Format for ListenEndpoint {
511 fn format(&self, f: defmt::Formatter) {
512 defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
513 }
514}
515
516impl From<u16> for ListenEndpoint {
517 fn from(port: u16) -> ListenEndpoint {
518 ListenEndpoint { addr: None, port }
519 }
520}
521
522impl From<Endpoint> for ListenEndpoint {
523 fn from(endpoint: Endpoint) -> ListenEndpoint {
524 ListenEndpoint {
525 addr: Some(endpoint.addr),
526 port: endpoint.port,
527 }
528 }
529}
530
531impl<T: Into<Address>> From<(T, u16)> for ListenEndpoint {
532 fn from((addr, port): (T, u16)) -> ListenEndpoint {
533 ListenEndpoint {
534 addr: Some(addr.into()),
535 port,
536 }
537 }
538}
539
540#[derive(Debug, Clone, PartialEq, Eq)]
545#[cfg_attr(feature = "defmt", derive(defmt::Format))]
546pub enum Repr {
547 #[cfg(feature = "proto-ipv4")]
548 Ipv4(Ipv4Repr),
549 #[cfg(feature = "proto-ipv6")]
550 Ipv6(Ipv6Repr),
551}
552
553#[cfg(feature = "proto-ipv4")]
554impl From<Ipv4Repr> for Repr {
555 fn from(repr: Ipv4Repr) -> Repr {
556 Repr::Ipv4(repr)
557 }
558}
559
560#[cfg(feature = "proto-ipv6")]
561impl From<Ipv6Repr> for Repr {
562 fn from(repr: Ipv6Repr) -> Repr {
563 Repr::Ipv6(repr)
564 }
565}
566
567impl Repr {
568 pub fn new(
574 src_addr: Address,
575 dst_addr: Address,
576 next_header: Protocol,
577 payload_len: usize,
578 hop_limit: u8,
579 ) -> Self {
580 match (src_addr, dst_addr) {
581 #[cfg(feature = "proto-ipv4")]
582 (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => Self::Ipv4(Ipv4Repr {
583 src_addr,
584 dst_addr,
585 next_header,
586 payload_len,
587 hop_limit,
588 }),
589 #[cfg(feature = "proto-ipv6")]
590 (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => Self::Ipv6(Ipv6Repr {
591 src_addr,
592 dst_addr,
593 next_header,
594 payload_len,
595 hop_limit,
596 }),
597 #[allow(unreachable_patterns)]
598 _ => panic!("IP version mismatch: src={src_addr:?} dst={dst_addr:?}"),
599 }
600 }
601
602 pub const fn version(&self) -> Version {
604 match *self {
605 #[cfg(feature = "proto-ipv4")]
606 Repr::Ipv4(_) => Version::Ipv4,
607 #[cfg(feature = "proto-ipv6")]
608 Repr::Ipv6(_) => Version::Ipv6,
609 }
610 }
611
612 pub const fn src_addr(&self) -> Address {
614 match *self {
615 #[cfg(feature = "proto-ipv4")]
616 Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
617 #[cfg(feature = "proto-ipv6")]
618 Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
619 }
620 }
621
622 pub const fn dst_addr(&self) -> Address {
624 match *self {
625 #[cfg(feature = "proto-ipv4")]
626 Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
627 #[cfg(feature = "proto-ipv6")]
628 Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
629 }
630 }
631
632 pub const fn next_header(&self) -> Protocol {
634 match *self {
635 #[cfg(feature = "proto-ipv4")]
636 Repr::Ipv4(repr) => repr.next_header,
637 #[cfg(feature = "proto-ipv6")]
638 Repr::Ipv6(repr) => repr.next_header,
639 }
640 }
641
642 pub const fn payload_len(&self) -> usize {
644 match *self {
645 #[cfg(feature = "proto-ipv4")]
646 Repr::Ipv4(repr) => repr.payload_len,
647 #[cfg(feature = "proto-ipv6")]
648 Repr::Ipv6(repr) => repr.payload_len,
649 }
650 }
651
652 pub fn set_payload_len(&mut self, length: usize) {
654 match self {
655 #[cfg(feature = "proto-ipv4")]
656 Repr::Ipv4(Ipv4Repr { payload_len, .. }) => *payload_len = length,
657 #[cfg(feature = "proto-ipv6")]
658 Repr::Ipv6(Ipv6Repr { payload_len, .. }) => *payload_len = length,
659 }
660 }
661
662 pub const fn hop_limit(&self) -> u8 {
664 match *self {
665 #[cfg(feature = "proto-ipv4")]
666 Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
667 #[cfg(feature = "proto-ipv6")]
668 Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
669 }
670 }
671
672 pub const fn header_len(&self) -> usize {
674 match *self {
675 #[cfg(feature = "proto-ipv4")]
676 Repr::Ipv4(repr) => repr.buffer_len(),
677 #[cfg(feature = "proto-ipv6")]
678 Repr::Ipv6(repr) => repr.buffer_len(),
679 }
680 }
681
682 pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
684 &self,
685 buffer: T,
686 _checksum_caps: &ChecksumCapabilities,
687 ) {
688 match *self {
689 #[cfg(feature = "proto-ipv4")]
690 Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
691 #[cfg(feature = "proto-ipv6")]
692 Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
693 }
694 }
695
696 pub const fn buffer_len(&self) -> usize {
701 self.header_len() + self.payload_len()
702 }
703}
704
705pub mod checksum {
706 use byteorder::{ByteOrder, NetworkEndian};
707
708 use super::*;
709
710 const fn propagate_carries(word: u32) -> u16 {
711 let sum = (word >> 16) + (word & 0xffff);
712 ((sum >> 16) as u16) + (sum as u16)
713 }
714
715 pub fn data(mut data: &[u8]) -> u16 {
717 let mut accum = 0;
718
719 const CHUNK_SIZE: usize = 32;
721 while data.len() >= CHUNK_SIZE {
722 let mut d = &data[..CHUNK_SIZE];
723 while d.len() >= 2 {
725 accum += NetworkEndian::read_u16(d) as u32;
726 d = &d[2..];
727 }
728
729 data = &data[CHUNK_SIZE..];
730 }
731
732 while data.len() >= 2 {
735 accum += NetworkEndian::read_u16(data) as u32;
736 data = &data[2..];
737 }
738
739 if let Some(&value) = data.first() {
741 accum += (value as u32) << 8;
742 }
743
744 propagate_carries(accum)
745 }
746
747 pub fn combine(checksums: &[u16]) -> u16 {
749 let mut accum: u32 = 0;
750 for &word in checksums {
751 accum += word as u32;
752 }
753 propagate_carries(accum)
754 }
755
756 pub fn pseudo_header(
758 src_addr: &Address,
759 dst_addr: &Address,
760 next_header: Protocol,
761 length: u32,
762 ) -> u16 {
763 match (src_addr, dst_addr) {
764 #[cfg(feature = "proto-ipv4")]
765 (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
766 let mut proto_len = [0u8; 4];
767 proto_len[1] = next_header.into();
768 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
769
770 combine(&[
771 data(src_addr.as_bytes()),
772 data(dst_addr.as_bytes()),
773 data(&proto_len[..]),
774 ])
775 }
776
777 #[cfg(feature = "proto-ipv6")]
778 (&Address::Ipv6(src_addr), &Address::Ipv6(dst_addr)) => {
779 let mut proto_len = [0u8; 8];
780 proto_len[7] = next_header.into();
781 NetworkEndian::write_u32(&mut proto_len[0..4], length);
782 combine(&[
783 data(src_addr.as_bytes()),
784 data(dst_addr.as_bytes()),
785 data(&proto_len[..]),
786 ])
787 }
788
789 #[allow(unreachable_patterns)]
790 _ => panic!("Unexpected pseudo header addresses: {src_addr}, {dst_addr}"),
791 }
792 }
793
794 pub(crate) fn format_checksum(f: &mut fmt::Formatter, correct: bool) -> fmt::Result {
796 if !correct {
797 write!(f, " (checksum incorrect)")
798 } else {
799 Ok(())
800 }
801 }
802}
803
804use crate::wire::pretty_print::PrettyIndent;
805
806pub fn pretty_print_ip_payload<T: Into<Repr>>(
807 f: &mut fmt::Formatter,
808 indent: &mut PrettyIndent,
809 ip_repr: T,
810 payload: &[u8],
811) -> fmt::Result {
812 #[cfg(feature = "proto-ipv4")]
813 use super::pretty_print::PrettyPrint;
814 use crate::wire::ip::checksum::format_checksum;
815 #[cfg(feature = "proto-ipv4")]
816 use crate::wire::Icmpv4Packet;
817 use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
818
819 let checksum_caps = ChecksumCapabilities::ignored();
820 let repr = ip_repr.into();
821 match repr.next_header() {
822 #[cfg(feature = "proto-ipv4")]
823 Protocol::Icmp => {
824 indent.increase(f)?;
825 Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
826 }
827 Protocol::Udp => {
828 indent.increase(f)?;
829 match UdpPacket::<&[u8]>::new_checked(payload) {
830 Err(err) => write!(f, "{indent}({err})"),
831 Ok(udp_packet) => {
832 match UdpRepr::parse(
833 &udp_packet,
834 &repr.src_addr(),
835 &repr.dst_addr(),
836 &checksum_caps,
837 ) {
838 Err(err) => write!(f, "{indent}{udp_packet} ({err})"),
839 Ok(udp_repr) => {
840 write!(
841 f,
842 "{}{} len={}",
843 indent,
844 udp_repr,
845 udp_packet.payload().len()
846 )?;
847 let valid =
848 udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
849 format_checksum(f, valid)
850 }
851 }
852 }
853 }
854 }
855 Protocol::Tcp => {
856 indent.increase(f)?;
857 match TcpPacket::<&[u8]>::new_checked(payload) {
858 Err(err) => write!(f, "{indent}({err})"),
859 Ok(tcp_packet) => {
860 match TcpRepr::parse(
861 &tcp_packet,
862 &repr.src_addr(),
863 &repr.dst_addr(),
864 &checksum_caps,
865 ) {
866 Err(err) => write!(f, "{indent}{tcp_packet} ({err})"),
867 Ok(tcp_repr) => {
868 write!(f, "{indent}{tcp_repr}")?;
869 let valid =
870 tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
871 format_checksum(f, valid)
872 }
873 }
874 }
875 }
876 }
877 _ => Ok(()),
878 }
879}
880
881#[cfg(test)]
882pub(crate) mod test {
883 #![allow(unused)]
884
885 #[cfg(feature = "proto-ipv6")]
886 pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([
887 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
888 ]));
889 #[cfg(feature = "proto-ipv6")]
890 pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([
891 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
892 ]));
893 #[cfg(feature = "proto-ipv6")]
894 pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([
895 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
896 ]));
897 #[cfg(feature = "proto-ipv6")]
898 pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([
899 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
900 ]));
901 #[cfg(feature = "proto-ipv6")]
902 pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv6(Ipv6Address::UNSPECIFIED);
903
904 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
905 pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1]));
906 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
907 pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 2]));
908 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
909 pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 3]));
910 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
911 pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 4]));
912 #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
913 pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED);
914
915 use super::*;
916 use crate::wire::{IpAddress, IpCidr, IpProtocol};
917 #[cfg(feature = "proto-ipv4")]
918 use crate::wire::{Ipv4Address, Ipv4Repr};
919
920 #[test]
921 #[cfg(feature = "proto-ipv4")]
922 fn to_prefix_len_ipv4() {
923 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
924 assert_eq!(Some(prefix_len), mask.into().prefix_len());
925 }
926
927 test_eq(0, Ipv4Address::new(0, 0, 0, 0));
928 test_eq(1, Ipv4Address::new(128, 0, 0, 0));
929 test_eq(2, Ipv4Address::new(192, 0, 0, 0));
930 test_eq(3, Ipv4Address::new(224, 0, 0, 0));
931 test_eq(4, Ipv4Address::new(240, 0, 0, 0));
932 test_eq(5, Ipv4Address::new(248, 0, 0, 0));
933 test_eq(6, Ipv4Address::new(252, 0, 0, 0));
934 test_eq(7, Ipv4Address::new(254, 0, 0, 0));
935 test_eq(8, Ipv4Address::new(255, 0, 0, 0));
936 test_eq(9, Ipv4Address::new(255, 128, 0, 0));
937 test_eq(10, Ipv4Address::new(255, 192, 0, 0));
938 test_eq(11, Ipv4Address::new(255, 224, 0, 0));
939 test_eq(12, Ipv4Address::new(255, 240, 0, 0));
940 test_eq(13, Ipv4Address::new(255, 248, 0, 0));
941 test_eq(14, Ipv4Address::new(255, 252, 0, 0));
942 test_eq(15, Ipv4Address::new(255, 254, 0, 0));
943 test_eq(16, Ipv4Address::new(255, 255, 0, 0));
944 test_eq(17, Ipv4Address::new(255, 255, 128, 0));
945 test_eq(18, Ipv4Address::new(255, 255, 192, 0));
946 test_eq(19, Ipv4Address::new(255, 255, 224, 0));
947 test_eq(20, Ipv4Address::new(255, 255, 240, 0));
948 test_eq(21, Ipv4Address::new(255, 255, 248, 0));
949 test_eq(22, Ipv4Address::new(255, 255, 252, 0));
950 test_eq(23, Ipv4Address::new(255, 255, 254, 0));
951 test_eq(24, Ipv4Address::new(255, 255, 255, 0));
952 test_eq(25, Ipv4Address::new(255, 255, 255, 128));
953 test_eq(26, Ipv4Address::new(255, 255, 255, 192));
954 test_eq(27, Ipv4Address::new(255, 255, 255, 224));
955 test_eq(28, Ipv4Address::new(255, 255, 255, 240));
956 test_eq(29, Ipv4Address::new(255, 255, 255, 248));
957 test_eq(30, Ipv4Address::new(255, 255, 255, 252));
958 test_eq(31, Ipv4Address::new(255, 255, 255, 254));
959 test_eq(32, Ipv4Address::new(255, 255, 255, 255));
960 }
961
962 #[test]
963 #[cfg(feature = "proto-ipv4")]
964 fn to_prefix_len_ipv4_error() {
965 assert_eq!(
966 None,
967 IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
968 );
969 }
970
971 #[test]
972 #[cfg(feature = "proto-ipv6")]
973 fn to_prefix_len_ipv6() {
974 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
975 assert_eq!(Some(prefix_len), mask.into().prefix_len());
976 }
977
978 test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
979 test_eq(
980 128,
981 Ipv6Address::new(
982 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
983 ),
984 );
985 }
986
987 #[test]
988 #[cfg(feature = "proto-ipv6")]
989 fn to_prefix_len_ipv6_error() {
990 assert_eq!(
991 None,
992 IpAddress::from(Ipv6Address::new(
993 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
994 ))
995 .prefix_len()
996 );
997 }
998}