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(mut data: &[u8]) -> u16 {
774 let mut accum = 0;
775
776 const CHUNK_SIZE: usize = 32;
778 while data.len() >= CHUNK_SIZE {
779 let mut d = &data[..CHUNK_SIZE];
780 while d.len() >= 2 {
782 accum += NetworkEndian::read_u16(d) as u32;
783 d = &d[2..];
784 }
785
786 data = &data[CHUNK_SIZE..];
787 }
788
789 while data.len() >= 2 {
792 accum += NetworkEndian::read_u16(data) as u32;
793 data = &data[2..];
794 }
795
796 if let Some(&value) = data.first() {
798 accum += (value as u32) << 8;
799 }
800
801 propagate_carries(accum)
802 }
803
804 pub fn combine(checksums: &[u16]) -> u16 {
806 let mut accum: u32 = 0;
807 for &word in checksums {
808 accum += word as u32;
809 }
810 propagate_carries(accum)
811 }
812
813 #[cfg(feature = "proto-ipv4")]
814 pub fn pseudo_header_v4(
815 src_addr: &Ipv4Address,
816 dst_addr: &Ipv4Address,
817 next_header: Protocol,
818 length: u32,
819 ) -> u16 {
820 let mut proto_len = [0u8; 4];
821 proto_len[1] = next_header.into();
822 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
823
824 combine(&[
825 data(&src_addr.octets()),
826 data(&dst_addr.octets()),
827 data(&proto_len[..]),
828 ])
829 }
830
831 #[cfg(feature = "proto-ipv6")]
832 pub fn pseudo_header_v6(
833 src_addr: &Ipv6Address,
834 dst_addr: &Ipv6Address,
835 next_header: Protocol,
836 length: u32,
837 ) -> u16 {
838 let mut proto_len = [0u8; 4];
839 proto_len[1] = next_header.into();
840 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
841
842 combine(&[
843 data(&src_addr.octets()),
844 data(&dst_addr.octets()),
845 data(&proto_len[..]),
846 ])
847 }
848
849 pub fn pseudo_header(
850 src_addr: &Address,
851 dst_addr: &Address,
852 next_header: Protocol,
853 length: u32,
854 ) -> u16 {
855 match (src_addr, dst_addr) {
856 #[cfg(feature = "proto-ipv4")]
857 (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => {
858 pseudo_header_v4(src_addr, dst_addr, next_header, length)
859 }
860 #[cfg(feature = "proto-ipv6")]
861 (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => {
862 pseudo_header_v6(src_addr, dst_addr, next_header, length)
863 }
864 #[allow(unreachable_patterns)]
865 _ => unreachable!(),
866 }
867 }
868
869 pub(crate) fn format_checksum(
871 f: &mut fmt::Formatter,
872 correct: bool,
873 partially_correct: bool,
874 ) -> fmt::Result {
875 if !correct {
876 if partially_correct {
877 write!(f, " (partial checksum correct)")
878 } else {
879 write!(f, " (checksum incorrect)")
880 }
881 } else {
882 Ok(())
883 }
884 }
885}
886
887use crate::wire::pretty_print::PrettyIndent;
888
889pub fn pretty_print_ip_payload<T: Into<Repr>>(
890 f: &mut fmt::Formatter,
891 indent: &mut PrettyIndent,
892 ip_repr: T,
893 payload: &[u8],
894) -> fmt::Result {
895 #[cfg(feature = "proto-ipv4")]
896 use super::pretty_print::PrettyPrint;
897 #[cfg(feature = "proto-ipv4")]
898 use crate::wire::Icmpv4Packet;
899 use crate::wire::ip::checksum::format_checksum;
900 use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
901
902 let checksum_caps = ChecksumCapabilities::ignored();
903 let repr = ip_repr.into();
904 match repr.next_header() {
905 #[cfg(feature = "proto-ipv4")]
906 Protocol::Icmp => {
907 indent.increase(f)?;
908 Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
909 }
910 Protocol::Udp => {
911 indent.increase(f)?;
912 match UdpPacket::<&[u8]>::new_checked(payload) {
913 Err(err) => write!(f, "{indent}({err})"),
914 Ok(udp_packet) => {
915 match UdpRepr::parse(
916 &udp_packet,
917 &repr.src_addr(),
918 &repr.dst_addr(),
919 &checksum_caps,
920 ) {
921 Err(err) => write!(f, "{indent}{udp_packet} ({err})"),
922 Ok(udp_repr) => {
923 write!(
924 f,
925 "{}{} len={}",
926 indent,
927 udp_repr,
928 udp_packet.payload().len()
929 )?;
930 let valid =
931 udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
932 let partially_valid = udp_packet
933 .verify_partial_checksum(&repr.src_addr(), &repr.dst_addr());
934
935 format_checksum(f, valid, partially_valid)
936 }
937 }
938 }
939 }
940 }
941 Protocol::Tcp => {
942 indent.increase(f)?;
943 match TcpPacket::<&[u8]>::new_checked(payload) {
944 Err(err) => write!(f, "{indent}({err})"),
945 Ok(tcp_packet) => {
946 match TcpRepr::parse(
947 &tcp_packet,
948 &repr.src_addr(),
949 &repr.dst_addr(),
950 &checksum_caps,
951 ) {
952 Err(err) => write!(f, "{indent}{tcp_packet} ({err})"),
953 Ok(tcp_repr) => {
954 write!(f, "{indent}{tcp_repr}")?;
955 let valid =
956 tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
957 let partially_valid = tcp_packet
958 .verify_partial_checksum(&repr.src_addr(), &repr.dst_addr());
959
960 format_checksum(f, valid, partially_valid)
961 }
962 }
963 }
964 }
965 }
966 _ => Ok(()),
967 }
968}
969
970#[cfg(test)]
971pub(crate) mod test {
972 #![allow(unused)]
973
974 use super::*;
975 use crate::wire::{IpAddress, IpCidr, IpProtocol, IpRepr};
976 #[cfg(feature = "proto-ipv4")]
977 use crate::wire::{Ipv4Address, Ipv4Repr};
978
979 #[test]
980 #[cfg(feature = "proto-ipv4")]
981 fn to_prefix_len_ipv4() {
982 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
983 assert_eq!(Some(prefix_len), mask.into().prefix_len());
984 }
985
986 test_eq(0, Ipv4Address::new(0, 0, 0, 0));
987 test_eq(1, Ipv4Address::new(128, 0, 0, 0));
988 test_eq(2, Ipv4Address::new(192, 0, 0, 0));
989 test_eq(3, Ipv4Address::new(224, 0, 0, 0));
990 test_eq(4, Ipv4Address::new(240, 0, 0, 0));
991 test_eq(5, Ipv4Address::new(248, 0, 0, 0));
992 test_eq(6, Ipv4Address::new(252, 0, 0, 0));
993 test_eq(7, Ipv4Address::new(254, 0, 0, 0));
994 test_eq(8, Ipv4Address::new(255, 0, 0, 0));
995 test_eq(9, Ipv4Address::new(255, 128, 0, 0));
996 test_eq(10, Ipv4Address::new(255, 192, 0, 0));
997 test_eq(11, Ipv4Address::new(255, 224, 0, 0));
998 test_eq(12, Ipv4Address::new(255, 240, 0, 0));
999 test_eq(13, Ipv4Address::new(255, 248, 0, 0));
1000 test_eq(14, Ipv4Address::new(255, 252, 0, 0));
1001 test_eq(15, Ipv4Address::new(255, 254, 0, 0));
1002 test_eq(16, Ipv4Address::new(255, 255, 0, 0));
1003 test_eq(17, Ipv4Address::new(255, 255, 128, 0));
1004 test_eq(18, Ipv4Address::new(255, 255, 192, 0));
1005 test_eq(19, Ipv4Address::new(255, 255, 224, 0));
1006 test_eq(20, Ipv4Address::new(255, 255, 240, 0));
1007 test_eq(21, Ipv4Address::new(255, 255, 248, 0));
1008 test_eq(22, Ipv4Address::new(255, 255, 252, 0));
1009 test_eq(23, Ipv4Address::new(255, 255, 254, 0));
1010 test_eq(24, Ipv4Address::new(255, 255, 255, 0));
1011 test_eq(25, Ipv4Address::new(255, 255, 255, 128));
1012 test_eq(26, Ipv4Address::new(255, 255, 255, 192));
1013 test_eq(27, Ipv4Address::new(255, 255, 255, 224));
1014 test_eq(28, Ipv4Address::new(255, 255, 255, 240));
1015 test_eq(29, Ipv4Address::new(255, 255, 255, 248));
1016 test_eq(30, Ipv4Address::new(255, 255, 255, 252));
1017 test_eq(31, Ipv4Address::new(255, 255, 255, 254));
1018 test_eq(32, Ipv4Address::new(255, 255, 255, 255));
1019 }
1020
1021 #[test]
1022 #[cfg(feature = "proto-ipv4")]
1023 fn to_prefix_len_ipv4_error() {
1024 assert_eq!(
1025 None,
1026 IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
1027 );
1028 }
1029
1030 #[test]
1031 #[cfg(feature = "proto-ipv6")]
1032 fn to_prefix_len_ipv6() {
1033 fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
1034 assert_eq!(Some(prefix_len), mask.into().prefix_len());
1035 }
1036
1037 test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
1038 test_eq(
1039 128,
1040 Ipv6Address::new(
1041 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1042 ),
1043 );
1044 }
1045
1046 #[test]
1047 #[cfg(feature = "proto-ipv6")]
1048 fn to_prefix_len_ipv6_error() {
1049 assert_eq!(
1050 None,
1051 IpAddress::from(Ipv6Address::new(
1052 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
1053 ))
1054 .prefix_len()
1055 );
1056 }
1057
1058 #[test]
1059 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1060 fn parse_ipv4_packet() {
1061 let ipv4_packet_bytes: [u8; 20] = [
1062 0x45, 0x00, 0x00, 0x14, 0x00, 0x01, 0x40, 0x00, 0x40, 0x01, 0xd2, 0x7c, 0x11, 0x12,
1063 0x13, 0x14, 0x21, 0x22, 0x23, 0x24,
1064 ];
1065
1066 let expected = Ipv4Repr {
1067 src_addr: crate::wire::ipv4::Address::new(0x11, 0x12, 0x13, 0x14),
1068 dst_addr: crate::wire::ipv4::Address::new(0x21, 0x22, 0x23, 0x24),
1069 next_header: Protocol::Icmp,
1070 payload_len: 0,
1071 hop_limit: 64,
1072 };
1073
1074 let packet = Packet::new_unchecked(&ipv4_packet_bytes[..]);
1075 let ip_repr = IpRepr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1076 assert_eq!(ip_repr.version(), Version::Ipv4);
1077 let IpRepr::Ipv4(ipv4_repr) = ip_repr else {
1078 panic!("expected Ipv4Repr");
1079 };
1080 assert_eq!(ipv4_repr, expected);
1081 assert_eq!(packet.into_inner(), ipv4_packet_bytes);
1082 }
1083
1084 #[test]
1085 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1086 fn parse_ipv4_packet_error() {
1087 let ipv4_packet_bytes: [u8; 29] = [
1088 0x45, 0x00, 0x00, 0x1e, 0x01, 0x02, 0x62, 0x03, 0x1a, 0x01, 0xd5, 0x6e, 0x11, 0x12,
1089 0x13, 0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1090 0x00,
1091 ];
1092
1093 let packet = Packet::new_unchecked(&ipv4_packet_bytes[..]);
1094 let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1095 assert!(ip_repr_result.is_err());
1096 }
1097
1098 #[test]
1099 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1100 fn parse_ipv6_packet() {
1101 let ipv6_packet_bytes: [u8; 52] = [
1102 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
1103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00,
1104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
1105 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
1106 ];
1107
1108 let expected = Ipv6Repr {
1109 src_addr: crate::wire::ipv6::Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
1110 dst_addr: crate::wire::ipv6::Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
1111 next_header: Protocol::Udp,
1112 payload_len: 12,
1113 hop_limit: 64,
1114 };
1115
1116 let packet = Packet::new_unchecked(&ipv6_packet_bytes[..]);
1117 let ip_repr = IpRepr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1118 assert_eq!(ip_repr.version(), Version::Ipv6);
1119 let IpRepr::Ipv6(ipv6_repr) = ip_repr else {
1120 panic!("expected Ipv6Repr");
1121 };
1122 assert_eq!(ipv6_repr, expected);
1123 assert_eq!(packet.into_inner(), ipv6_packet_bytes);
1124 }
1125
1126 #[test]
1127 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1128 fn parse_ipv6_packet_error() {
1129 let ipv6_packet_bytes: [u8; 51] = [
1130 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
1131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00,
1132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
1133 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff,
1134 ];
1135
1136 let packet = Packet::new_unchecked(&ipv6_packet_bytes[..]);
1137 let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1138 assert!(ip_repr_result.is_err());
1139 }
1140
1141 #[test]
1142 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1143 fn parse_packet_too_short() {
1144 let ip_packet = [0u8; 0];
1146 let packet_result = Packet::new_checked(&ip_packet[..]);
1147 assert!(packet_result.is_err());
1148 }
1149
1150 #[test]
1151 #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1152 fn parse_packet_invalid_version() {
1153 let packet_bytes: [u8; 1] = [0xFF];
1154 let packet = Packet::new_unchecked(&packet_bytes[..]);
1155 let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1156 assert!(ip_repr_result.is_err());
1157 }
1158}