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 const 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 From<Endpoint> for ::core::net::SocketAddr {
398 fn from(x: Endpoint) -> ::core::net::SocketAddr {
399 ::core::net::SocketAddr::new(x.addr.into(), x.port)
400 }
401}
402
403impl fmt::Display for Endpoint {
404 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
405 write!(f, "{}:{}", self.addr, self.port)
406 }
407}
408
409#[cfg(feature = "defmt")]
410impl defmt::Format for Endpoint {
411 fn format(&self, f: defmt::Formatter) {
412 defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
413 }
414}
415
416impl<T: Into<Address>> From<(T, u16)> for Endpoint {
417 fn from((addr, port): (T, u16)) -> Endpoint {
418 Endpoint {
419 addr: addr.into(),
420 port,
421 }
422 }
423}
424
425#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
432pub struct ListenEndpoint {
433 pub addr: Option<Address>,
434 pub port: u16,
435}
436
437impl ListenEndpoint {
438 pub const fn is_specified(&self) -> bool {
440 self.addr.is_some() && self.port != 0
441 }
442}
443
444#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
445impl From<::core::net::SocketAddr> for ListenEndpoint {
446 fn from(x: ::core::net::SocketAddr) -> ListenEndpoint {
447 ListenEndpoint {
448 addr: Some(x.ip().into()),
449 port: x.port(),
450 }
451 }
452}
453
454#[cfg(feature = "proto-ipv4")]
455impl From<::core::net::SocketAddrV4> for ListenEndpoint {
456 fn from(x: ::core::net::SocketAddrV4) -> ListenEndpoint {
457 ListenEndpoint {
458 addr: Some((*x.ip()).into()),
459 port: x.port(),
460 }
461 }
462}
463
464#[cfg(feature = "proto-ipv6")]
465impl From<::core::net::SocketAddrV6> for ListenEndpoint {
466 fn from(x: ::core::net::SocketAddrV6) -> ListenEndpoint {
467 ListenEndpoint {
468 addr: Some((*x.ip()).into()),
469 port: x.port(),
470 }
471 }
472}
473
474impl fmt::Display for ListenEndpoint {
475 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
476 if let Some(addr) = self.addr {
477 write!(f, "{}:{}", addr, self.port)
478 } else {
479 write!(f, "*:{}", self.port)
480 }
481 }
482}
483
484#[cfg(feature = "defmt")]
485impl defmt::Format for ListenEndpoint {
486 fn format(&self, f: defmt::Formatter) {
487 defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
488 }
489}
490
491impl From<u16> for ListenEndpoint {
492 fn from(port: u16) -> ListenEndpoint {
493 ListenEndpoint { addr: None, port }
494 }
495}
496
497impl From<Endpoint> for ListenEndpoint {
498 fn from(endpoint: Endpoint) -> ListenEndpoint {
499 ListenEndpoint {
500 addr: Some(endpoint.addr),
501 port: endpoint.port,
502 }
503 }
504}
505
506impl<T: Into<Address>> From<(T, u16)> for ListenEndpoint {
507 fn from((addr, port): (T, u16)) -> ListenEndpoint {
508 ListenEndpoint {
509 addr: Some(addr.into()),
510 port,
511 }
512 }
513}
514
515#[derive(Debug, Clone, PartialEq, Eq)]
520#[cfg_attr(feature = "defmt", derive(defmt::Format))]
521pub enum Repr {
522 #[cfg(feature = "proto-ipv4")]
523 Ipv4(Ipv4Repr),
524 #[cfg(feature = "proto-ipv6")]
525 Ipv6(Ipv6Repr),
526}
527
528#[cfg(feature = "proto-ipv4")]
529impl From<Ipv4Repr> for Repr {
530 fn from(repr: Ipv4Repr) -> Repr {
531 Repr::Ipv4(repr)
532 }
533}
534
535#[cfg(feature = "proto-ipv6")]
536impl From<Ipv6Repr> for Repr {
537 fn from(repr: Ipv6Repr) -> Repr {
538 Repr::Ipv6(repr)
539 }
540}
541
542#[derive(Debug, PartialEq, Eq, Clone)]
544#[cfg_attr(feature = "defmt", derive(defmt::Format))]
545pub struct Packet<T: AsRef<[u8]>> {
546 buffer: T,
547}
548
549mod field {
550 use crate::wire::field::*;
551 pub const VER: Field = 0..1;
553}
554
555impl<T: AsRef<[u8]>> Packet<T> {
556 pub const fn new_unchecked(buffer: T) -> Packet<T> {
559 Packet { buffer }
560 }
561
562 pub fn new_checked(buffer: T) -> Result<Packet<T>> {
567 let packet = Self::new_unchecked(buffer);
568 packet.check_len()?;
569 Ok(packet)
570 }
571
572 pub fn check_len(&self) -> Result<()> {
575 if self.buffer.as_ref().len() < field::VER.end {
578 Err(Error)
579 } else {
580 Ok(())
581 }
582 }
583
584 pub fn into_inner(self) -> T {
586 self.buffer
587 }
588
589 pub fn version(&self) -> u8 {
591 let data = self.buffer.as_ref();
592 data[field::VER.start] >> 4
593 }
594}
595
596impl Repr {
597 pub fn new(
603 src_addr: Address,
604 dst_addr: Address,
605 next_header: Protocol,
606 payload_len: usize,
607 hop_limit: u8,
608 ) -> Self {
609 match (src_addr, dst_addr) {
610 #[cfg(feature = "proto-ipv4")]
611 (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => Self::Ipv4(Ipv4Repr {
612 src_addr,
613 dst_addr,
614 next_header,
615 payload_len,
616 hop_limit,
617 }),
618 #[cfg(feature = "proto-ipv6")]
619 (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => Self::Ipv6(Ipv6Repr {
620 src_addr,
621 dst_addr,
622 next_header,
623 payload_len,
624 hop_limit,
625 }),
626 #[allow(unreachable_patterns)]
627 _ => panic!("IP version mismatch: src={src_addr:?} dst={dst_addr:?}"),
628 }
629 }
630
631 pub fn parse<T: AsRef<[u8]> + ?Sized>(
638 packet: &Packet<&T>,
639 checksum_caps: &ChecksumCapabilities,
640 ) -> Result<Repr> {
641 packet.check_len()?;
642 match packet.version() {
643 #[cfg(feature = "proto-ipv4")]
644 4 => {
645 let packet = Ipv4Packet::new_checked(packet.buffer)?;
646 let ipv4_repr = Ipv4Repr::parse(&packet, checksum_caps)?;
647 Ok(Repr::Ipv4(ipv4_repr))
648 }
649 #[cfg(feature = "proto-ipv6")]
650 6 => {
651 let packet = Ipv6Packet::new_checked(packet.buffer)?;
652 let ipv6_repr = Ipv6Repr::parse(&packet)?;
653 Ok(Repr::Ipv6(ipv6_repr))
654 }
655 _ => Err(Error),
656 }
657 }
658
659 pub const fn version(&self) -> Version {
661 match *self {
662 #[cfg(feature = "proto-ipv4")]
663 Repr::Ipv4(_) => Version::Ipv4,
664 #[cfg(feature = "proto-ipv6")]
665 Repr::Ipv6(_) => Version::Ipv6,
666 }
667 }
668
669 pub const fn src_addr(&self) -> Address {
671 match *self {
672 #[cfg(feature = "proto-ipv4")]
673 Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
674 #[cfg(feature = "proto-ipv6")]
675 Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
676 }
677 }
678
679 pub const fn dst_addr(&self) -> Address {
681 match *self {
682 #[cfg(feature = "proto-ipv4")]
683 Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
684 #[cfg(feature = "proto-ipv6")]
685 Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
686 }
687 }
688
689 pub const fn next_header(&self) -> Protocol {
691 match *self {
692 #[cfg(feature = "proto-ipv4")]
693 Repr::Ipv4(repr) => repr.next_header,
694 #[cfg(feature = "proto-ipv6")]
695 Repr::Ipv6(repr) => repr.next_header,
696 }
697 }
698
699 pub const fn payload_len(&self) -> usize {
701 match *self {
702 #[cfg(feature = "proto-ipv4")]
703 Repr::Ipv4(repr) => repr.payload_len,
704 #[cfg(feature = "proto-ipv6")]
705 Repr::Ipv6(repr) => repr.payload_len,
706 }
707 }
708
709 pub fn set_payload_len(&mut self, length: usize) {
711 match self {
712 #[cfg(feature = "proto-ipv4")]
713 Repr::Ipv4(Ipv4Repr { payload_len, .. }) => *payload_len = length,
714 #[cfg(feature = "proto-ipv6")]
715 Repr::Ipv6(Ipv6Repr { payload_len, .. }) => *payload_len = length,
716 }
717 }
718
719 pub const fn hop_limit(&self) -> u8 {
721 match *self {
722 #[cfg(feature = "proto-ipv4")]
723 Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
724 #[cfg(feature = "proto-ipv6")]
725 Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
726 }
727 }
728
729 pub const fn header_len(&self) -> usize {
731 match *self {
732 #[cfg(feature = "proto-ipv4")]
733 Repr::Ipv4(repr) => repr.buffer_len(),
734 #[cfg(feature = "proto-ipv6")]
735 Repr::Ipv6(repr) => repr.buffer_len(),
736 }
737 }
738
739 pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
741 &self,
742 buffer: T,
743 _checksum_caps: &ChecksumCapabilities,
744 ) {
745 match *self {
746 #[cfg(feature = "proto-ipv4")]
747 Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
748 #[cfg(feature = "proto-ipv6")]
749 Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
750 }
751 }
752
753 pub const fn buffer_len(&self) -> usize {
758 self.header_len() + self.payload_len()
759 }
760}
761
762pub mod checksum {
763 use byteorder::{ByteOrder, NetworkEndian};
764
765 use super::*;
766
767 const fn propagate_carries(word: u32) -> u16 {
768 let sum = (word >> 16) + (word & 0xffff);
769 ((sum >> 16) as u16) + (sum as u16)
770 }
771
772 pub fn data(data: &[u8]) -> u16 {
774 let mut accum: u32 = 0;
777
778 let (chunks, mut rem) = data.as_chunks::<4>();
783 for chunk in chunks {
784 let val_0 = u16::from_ne_bytes(chunk[..2].try_into().unwrap());
785 let val_1 = u16::from_ne_bytes(chunk[2..4].try_into().unwrap());
786 accum += val_0 as u32;
787 accum += val_1 as u32;
788 }
789
790 if rem.len() >= 2 {
792 let val = u16::from_ne_bytes(rem[..2].try_into().unwrap());
793 accum += val as u32;
794 rem = &rem[2..];
795 }
796
797 if let Some(&value) = rem.first() {
799 accum += u16::from_ne_bytes([value, 0]) as u32;
800 }
801
802 let collapsed = propagate_carries(accum);
803 u16::to_be(collapsed)
804 }
805
806 pub fn combine(checksums: &[u16]) -> u16 {
808 let mut accum: u32 = 0;
809 for &word in checksums {
810 accum += word as u32;
811 }
812 propagate_carries(accum)
813 }
814
815 #[cfg(feature = "proto-ipv4")]
816 pub fn pseudo_header_v4(
817 src_addr: &Ipv4Address,
818 dst_addr: &Ipv4Address,
819 next_header: Protocol,
820 length: u32,
821 ) -> u16 {
822 let mut proto_len = [0u8; 4];
823 proto_len[1] = next_header.into();
824 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
825
826 combine(&[
827 data(&src_addr.octets()),
828 data(&dst_addr.octets()),
829 data(&proto_len[..]),
830 ])
831 }
832
833 #[cfg(feature = "proto-ipv6")]
834 pub fn pseudo_header_v6(
835 src_addr: &Ipv6Address,
836 dst_addr: &Ipv6Address,
837 next_header: Protocol,
838 length: u32,
839 ) -> u16 {
840 let mut proto_len = [0u8; 4];
841 proto_len[1] = next_header.into();
842 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
843
844 combine(&[
845 data(&src_addr.octets()),
846 data(&dst_addr.octets()),
847 data(&proto_len[..]),
848 ])
849 }
850
851 pub fn pseudo_header(
852 src_addr: &Address,
853 dst_addr: &Address,
854 next_header: Protocol,
855 length: u32,
856 ) -> u16 {
857 match (src_addr, dst_addr) {
858 #[cfg(feature = "proto-ipv4")]
859 (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => {
860 pseudo_header_v4(src_addr, dst_addr, next_header, length)
861 }
862 #[cfg(feature = "proto-ipv6")]
863 (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => {
864 pseudo_header_v6(src_addr, dst_addr, next_header, length)
865 }
866 #[allow(unreachable_patterns)]
867 _ => unreachable!(),
868 }
869 }
870
871 pub(crate) fn format_checksum(
873 f: &mut fmt::Formatter,
874 correct: bool,
875 partially_correct: bool,
876 ) -> fmt::Result {
877 if !correct {
878 if partially_correct {
879 write!(f, " (partial checksum correct)")
880 } else {
881 write!(f, " (checksum incorrect)")
882 }
883 } else {
884 Ok(())
885 }
886 }
887}
888
889use crate::wire::pretty_print::PrettyIndent;
890
891pub fn pretty_print_ip_payload<T: Into<Repr>>(
892 f: &mut fmt::Formatter,
893 indent: &mut PrettyIndent,
894 ip_repr: T,
895 payload: &[u8],
896) -> fmt::Result {
897 #[cfg(feature = "proto-ipv4")]
898 use super::pretty_print::PrettyPrint;
899 #[cfg(feature = "proto-ipv4")]
900 use crate::wire::Icmpv4Packet;
901 use crate::wire::ip::checksum::format_checksum;
902 use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
903
904 let checksum_caps = ChecksumCapabilities::ignored();
905 let repr = ip_repr.into();
906 match repr.next_header() {
907 #[cfg(feature = "proto-ipv4")]
908 Protocol::Icmp => {
909 indent.increase(f)?;
910 Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
911 }
912 Protocol::Udp => {
913 indent.increase(f)?;
914 match UdpPacket::<&[u8]>::new_checked(payload) {
915 Err(err) => write!(f, "{indent}({err})"),
916 Ok(udp_packet) => {
917 match UdpRepr::parse(
918 &udp_packet,
919 &repr.src_addr(),
920 &repr.dst_addr(),
921 &checksum_caps,
922 ) {
923 Err(err) => write!(f, "{indent}{udp_packet} ({err})"),
924 Ok(udp_repr) => {
925 write!(
926 f,
927 "{}{} len={}",
928 indent,
929 udp_repr,
930 udp_packet.payload().len()
931 )?;
932 let valid =
933 udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
934 let partially_valid = udp_packet
935 .verify_partial_checksum(&repr.src_addr(), &repr.dst_addr());
936
937 format_checksum(f, valid, partially_valid)
938 }
939 }
940 }
941 }
942 }
943 Protocol::Tcp => {
944 indent.increase(f)?;
945 match TcpPacket::<&[u8]>::new_checked(payload) {
946 Err(err) => write!(f, "{indent}({err})"),
947 Ok(tcp_packet) => {
948 match TcpRepr::parse(
949 &tcp_packet,
950 &repr.src_addr(),
951 &repr.dst_addr(),
952 &checksum_caps,
953 ) {
954 Err(err) => write!(f, "{indent}{tcp_packet} ({err})"),
955 Ok(tcp_repr) => {
956 write!(f, "{indent}{tcp_repr}")?;
957 let valid =
958 tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
959 let partially_valid = tcp_packet
960 .verify_partial_checksum(&repr.src_addr(), &repr.dst_addr());
961
962 format_checksum(f, valid, partially_valid)
963 }
964 }
965 }
966 }
967 }
968 _ => Ok(()),
969 }
970}
971
972#[cfg(test)]
973pub(crate) mod test {
974 #![allow(unused)]
975
976 use super::*;
977 use crate::wire::{IpAddress, IpCidr, IpProtocol, IpRepr};
978 #[cfg(feature = "proto-ipv4")]
979 use crate::wire::{Ipv4Address, Ipv4Repr};
980
981 #[test]
982 #[cfg(feature = "proto-ipv4")]
983 fn to_prefix_len_ipv4() {
984 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
985 assert_eq!(Some(prefix_len), mask.into().prefix_len());
986 }
987
988 test_eq(0, Ipv4Address::new(0, 0, 0, 0));
989 test_eq(1, Ipv4Address::new(128, 0, 0, 0));
990 test_eq(2, Ipv4Address::new(192, 0, 0, 0));
991 test_eq(3, Ipv4Address::new(224, 0, 0, 0));
992 test_eq(4, Ipv4Address::new(240, 0, 0, 0));
993 test_eq(5, Ipv4Address::new(248, 0, 0, 0));
994 test_eq(6, Ipv4Address::new(252, 0, 0, 0));
995 test_eq(7, Ipv4Address::new(254, 0, 0, 0));
996 test_eq(8, Ipv4Address::new(255, 0, 0, 0));
997 test_eq(9, Ipv4Address::new(255, 128, 0, 0));
998 test_eq(10, Ipv4Address::new(255, 192, 0, 0));
999 test_eq(11, Ipv4Address::new(255, 224, 0, 0));
1000 test_eq(12, Ipv4Address::new(255, 240, 0, 0));
1001 test_eq(13, Ipv4Address::new(255, 248, 0, 0));
1002 test_eq(14, Ipv4Address::new(255, 252, 0, 0));
1003 test_eq(15, Ipv4Address::new(255, 254, 0, 0));
1004 test_eq(16, Ipv4Address::new(255, 255, 0, 0));
1005 test_eq(17, Ipv4Address::new(255, 255, 128, 0));
1006 test_eq(18, Ipv4Address::new(255, 255, 192, 0));
1007 test_eq(19, Ipv4Address::new(255, 255, 224, 0));
1008 test_eq(20, Ipv4Address::new(255, 255, 240, 0));
1009 test_eq(21, Ipv4Address::new(255, 255, 248, 0));
1010 test_eq(22, Ipv4Address::new(255, 255, 252, 0));
1011 test_eq(23, Ipv4Address::new(255, 255, 254, 0));
1012 test_eq(24, Ipv4Address::new(255, 255, 255, 0));
1013 test_eq(25, Ipv4Address::new(255, 255, 255, 128));
1014 test_eq(26, Ipv4Address::new(255, 255, 255, 192));
1015 test_eq(27, Ipv4Address::new(255, 255, 255, 224));
1016 test_eq(28, Ipv4Address::new(255, 255, 255, 240));
1017 test_eq(29, Ipv4Address::new(255, 255, 255, 248));
1018 test_eq(30, Ipv4Address::new(255, 255, 255, 252));
1019 test_eq(31, Ipv4Address::new(255, 255, 255, 254));
1020 test_eq(32, Ipv4Address::new(255, 255, 255, 255));
1021 }
1022
1023 #[test]
1024 #[cfg(feature = "proto-ipv4")]
1025 fn to_prefix_len_ipv4_error() {
1026 assert_eq!(
1027 None,
1028 IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
1029 );
1030 }
1031
1032 #[test]
1033 #[cfg(feature = "proto-ipv6")]
1034 fn to_prefix_len_ipv6() {
1035 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
1036 assert_eq!(Some(prefix_len), mask.into().prefix_len());
1037 }
1038
1039 test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
1040 test_eq(
1041 128,
1042 Ipv6Address::new(
1043 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1044 ),
1045 );
1046 }
1047
1048 #[test]
1049 #[cfg(feature = "proto-ipv6")]
1050 fn to_prefix_len_ipv6_error() {
1051 assert_eq!(
1052 None,
1053 IpAddress::from(Ipv6Address::new(
1054 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
1055 ))
1056 .prefix_len()
1057 );
1058 }
1059
1060 #[test]
1061 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1062 fn parse_ipv4_packet() {
1063 let ipv4_packet_bytes: [u8; 20] = [
1064 0x45, 0x00, 0x00, 0x14, 0x00, 0x01, 0x40, 0x00, 0x40, 0x01, 0xd2, 0x7c, 0x11, 0x12,
1065 0x13, 0x14, 0x21, 0x22, 0x23, 0x24,
1066 ];
1067
1068 let expected = Ipv4Repr {
1069 src_addr: crate::wire::ipv4::Address::new(0x11, 0x12, 0x13, 0x14),
1070 dst_addr: crate::wire::ipv4::Address::new(0x21, 0x22, 0x23, 0x24),
1071 next_header: Protocol::Icmp,
1072 payload_len: 0,
1073 hop_limit: 64,
1074 };
1075
1076 let packet = Packet::new_unchecked(&ipv4_packet_bytes[..]);
1077 let ip_repr = IpRepr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1078 assert_eq!(ip_repr.version(), Version::Ipv4);
1079 let IpRepr::Ipv4(ipv4_repr) = ip_repr else {
1080 panic!("expected Ipv4Repr");
1081 };
1082 assert_eq!(ipv4_repr, expected);
1083 assert_eq!(packet.into_inner(), ipv4_packet_bytes);
1084 }
1085
1086 #[test]
1087 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1088 fn parse_ipv4_packet_error() {
1089 let ipv4_packet_bytes: [u8; 29] = [
1090 0x45, 0x00, 0x00, 0x1e, 0x01, 0x02, 0x62, 0x03, 0x1a, 0x01, 0xd5, 0x6e, 0x11, 0x12,
1091 0x13, 0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1092 0x00,
1093 ];
1094
1095 let packet = Packet::new_unchecked(&ipv4_packet_bytes[..]);
1096 let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1097 assert!(ip_repr_result.is_err());
1098 }
1099
1100 #[test]
1101 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1102 fn parse_ipv6_packet() {
1103 let ipv6_packet_bytes: [u8; 52] = [
1104 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
1105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00,
1106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
1107 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
1108 ];
1109
1110 let expected = Ipv6Repr {
1111 src_addr: crate::wire::ipv6::Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
1112 dst_addr: crate::wire::ipv6::Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
1113 next_header: Protocol::Udp,
1114 payload_len: 12,
1115 hop_limit: 64,
1116 };
1117
1118 let packet = Packet::new_unchecked(&ipv6_packet_bytes[..]);
1119 let ip_repr = IpRepr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1120 assert_eq!(ip_repr.version(), Version::Ipv6);
1121 let IpRepr::Ipv6(ipv6_repr) = ip_repr else {
1122 panic!("expected Ipv6Repr");
1123 };
1124 assert_eq!(ipv6_repr, expected);
1125 assert_eq!(packet.into_inner(), ipv6_packet_bytes);
1126 }
1127
1128 #[test]
1129 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1130 fn parse_ipv6_packet_error() {
1131 let ipv6_packet_bytes: [u8; 51] = [
1132 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
1133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00,
1134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
1135 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff,
1136 ];
1137
1138 let packet = Packet::new_unchecked(&ipv6_packet_bytes[..]);
1139 let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1140 assert!(ip_repr_result.is_err());
1141 }
1142
1143 #[test]
1144 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1145 fn parse_packet_too_short() {
1146 let ip_packet = [0u8; 0];
1148 let packet_result = Packet::new_checked(&ip_packet[..]);
1149 assert!(packet_result.is_err());
1150 }
1151
1152 #[test]
1153 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1154 fn parse_packet_invalid_version() {
1155 let packet_bytes: [u8; 1] = [0xFF];
1156 let packet = Packet::new_unchecked(&packet_bytes[..]);
1157 let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1158 assert!(ip_repr_result.is_err());
1159 }
1160}