internet_packet/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use std::fmt;
4use std::fmt::{Display, Formatter};
5use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
6
7
8#[cfg(feature = "internet-checksum")]
9use internet_checksum::Checksum;
10
11#[derive(PartialEq, Eq, Debug, Clone)]
12#[repr(u8)]
13pub enum IpVersion {
14    V4 = 4,
15    V6 = 6,
16}
17
18impl Display for IpVersion {
19    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
20        match self {
21            IpVersion::V4 => write!(f, "IPv4"),
22            IpVersion::V6 => write!(f, "IPv6"),
23        }
24    }
25}
26
27#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Debug, Hash)]
28#[repr(u8)]
29pub enum TransportProtocol {
30    Tcp = 0x06,
31    Udp = 0x11,
32}
33
34impl Display for TransportProtocol {
35    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
36        match self {
37            TransportProtocol::Tcp => write!(f, "TCP"),
38            TransportProtocol::Udp => write!(f, "UDP"),
39        }
40    }
41}
42
43impl TryFrom<u8> for TransportProtocol {
44    type Error = ParseError;
45
46    fn try_from(value: u8) -> Result<Self, Self::Error> {
47        match value {
48            0x06 => Ok(TransportProtocol::Tcp),
49            0x11 => Ok(TransportProtocol::Udp),
50            proto => Err(ParseError::UnknownTransportProtocol(proto)),
51        }
52    }
53}
54
55#[derive(Debug, Clone, PartialEq)]
56pub enum ParseError {
57    UnknownTransportProtocol(u8),
58    Malformed,
59    Fragmented,
60}
61
62impl Display for ParseError {
63    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
64        match self {
65            ParseError::UnknownTransportProtocol(proto) => {
66                write!(f, "Unknown transport protocol: {proto}")
67            }
68            ParseError::Malformed => write!(f, "Malformed packet"),
69            ParseError::Fragmented => write!(f, "Fragmented packet"),
70        }
71    }
72}
73
74impl std::error::Error for ParseError {}
75
76const IPV6_EXTENSION_HEADERS: [u8; 9] = [
77    0,  // Hop-by-Hop Options
78    43, // Routing
79    44, // Fragment
80    50, // Encapsulating Security Payload
81    51, // Authentication Header
82    60, // Destination Options
83    135, // Mobility
84    139, // Host Identity Protocol
85    140 // Shim6
86];
87
88#[derive(Clone, Eq, Ord, PartialEq, PartialOrd, Debug, Hash)]
89pub struct ConnectionId {
90    pub proto: TransportProtocol,
91    pub src: SocketAddr,
92    pub dst: SocketAddr,
93}
94
95impl ConnectionId {
96    pub fn reverse(&self) -> Self {
97        ConnectionId {
98            proto: self.proto,
99            dst: self.src,
100            src: self.dst,
101        }
102    }
103    pub fn canonical_form(self) -> Self {
104        if self.src < self.dst {
105            self.reverse()
106        } else {
107            self
108        }
109    }
110}
111
112#[derive(Debug, Clone, PartialEq)]
113pub struct InternetPacket {
114    data: Vec<u8>,
115    ip_version: IpVersion,
116    transport_proto: TransportProtocol,
117    transport_proto_offset: usize,
118    payload_offset: usize,
119    payload_end: usize
120}
121
122impl TryFrom<Vec<u8>> for InternetPacket {
123    type Error = ParseError;
124
125    fn try_from(data: Vec<u8>) -> Result<Self, Self::Error> {
126        if data.is_empty() {
127            return Err(ParseError::Malformed);
128        }
129
130        let ip_version = match data[0] >> 4 {
131            4 => IpVersion::V4,
132            6 => IpVersion::V6,
133            _ => return Err(ParseError::Malformed),
134        };
135
136        let (transport_proto, transport_proto_offset, payload_end) = match ip_version {
137            IpVersion::V4 => {
138                if data.len() < 20 {
139                    return Err(ParseError::Malformed);
140                }
141                if (data[6] & 0x3F) != 0 || data[7] != 0 {
142                    return Err(ParseError::Fragmented);
143                }
144                let proto = data[9];
145                let offset = (data[0] & 0x0F) as usize * 4;
146                let total_length = ((data[2] as usize) << 8) + data[3] as usize;
147                (proto, offset, total_length)
148            }
149            IpVersion::V6 => {
150                if data.len() < 40 {
151                    return Err(ParseError::Malformed);
152                }
153                let mut next_header = data[6];
154                let mut offset = 40;
155
156                while IPV6_EXTENSION_HEADERS.contains(&next_header) {
157                    if data.len() < offset + 8 {
158                        return Err(ParseError::Malformed);
159                    }
160                    if next_header == 44 {
161                        return Err(ParseError::Fragmented);
162                    }
163                    if next_header == 51 {
164                        // Authentication header is calculated differently.
165                        next_header = data[offset];
166                        offset += (data[offset + 1] as usize + 2) * 4;
167                    } else {
168                        next_header = data[offset];
169                        offset += (1 + data[offset + 1] as usize) * 8 - 8;
170                    }
171                }
172
173                let payload_length = ((data[4] as usize) << 8) + data[5] as usize;
174
175                (next_header, offset, payload_length + 40)
176            }
177        };
178
179        let transport_proto = match transport_proto {
180            0x06 => TransportProtocol::Tcp,
181            0x11 => TransportProtocol::Udp,
182            _ => return Err(ParseError::UnknownTransportProtocol(transport_proto)),
183        };
184
185        let payload_offset = match transport_proto {
186            TransportProtocol::Tcp => {
187                let data_offset =
188                    (data.get(transport_proto_offset + 12).unwrap_or(&0xff) >> 4) as usize * 4;
189                transport_proto_offset + data_offset
190            }
191            TransportProtocol::Udp => transport_proto_offset + 8,
192        };
193
194        // We currently assume that packets are well-formed.
195        if data.len() < payload_offset {
196            return Err(ParseError::Malformed);
197        }
198
199        Ok(InternetPacket {
200            data,
201            ip_version,
202            transport_proto,
203            transport_proto_offset,
204            payload_offset,
205            payload_end,
206        })
207    }
208}
209
210#[cfg(feature = "smoltcp")]
211#[cfg_attr(docsrs, doc(cfg(feature = "smoltcp")))]
212impl TryFrom<smoltcp::wire::Ipv4Packet<Vec<u8>>> for InternetPacket {
213    type Error = ParseError;
214
215    fn try_from(value: smoltcp::wire::Ipv4Packet<Vec<u8>>) -> Result<Self, Self::Error> {
216        InternetPacket::try_from(value.into_inner())
217    }
218}
219
220#[cfg(feature = "smoltcp")]
221#[cfg_attr(docsrs, doc(cfg(feature = "smoltcp")))]
222impl TryFrom<smoltcp::wire::Ipv6Packet<Vec<u8>>> for InternetPacket {
223    type Error = ParseError;
224
225    fn try_from(value: smoltcp::wire::Ipv6Packet<Vec<u8>>) -> Result<Self, Self::Error> {
226        InternetPacket::try_from(value.into_inner())
227    }
228}
229
230/// A simple representation of TCP/UDP over IPv4/IPv6 packets.
231impl InternetPacket {
232    pub fn src_ip(&self) -> IpAddr {
233        match self.ip_version {
234            IpVersion::V4 => {
235                let bytes: [u8; 4] = self.data[12..16].try_into().unwrap();
236                IpAddr::V4(Ipv4Addr::from(bytes))
237            }
238            IpVersion::V6 => {
239                let bytes: [u8; 16] = self.data[8..24].try_into().unwrap();
240                IpAddr::V6(Ipv6Addr::from(bytes))
241            }
242        }
243    }
244
245    pub fn dst_ip(&self) -> IpAddr {
246        match self.ip_version {
247            IpVersion::V4 => {
248                let bytes: [u8; 4] = self.data[16..20].try_into().unwrap();
249                IpAddr::V4(Ipv4Addr::from(bytes))
250            }
251            IpVersion::V6 => {
252                let bytes: [u8; 16] = self.data[24..40].try_into().unwrap();
253                IpAddr::V6(Ipv6Addr::from(bytes))
254            }
255        }
256    }
257
258    pub fn set_src_ip(&mut self, addr: IpAddr) {
259        match addr {
260            IpAddr::V4(addr) => {
261                assert_eq!(self.ip_version, IpVersion::V4);
262                self.data[12..16].copy_from_slice(&addr.octets());
263            }
264            IpAddr::V6(addr) => {
265                assert_eq!(self.ip_version, IpVersion::V6);
266                self.data[8..24].copy_from_slice(&addr.octets());
267            }
268        }
269    }
270
271    pub fn set_dst_ip(&mut self, addr: IpAddr) {
272        match addr {
273            IpAddr::V4(addr) => {
274                assert_eq!(self.ip_version, IpVersion::V4);
275                self.data[16..20].copy_from_slice(&addr.octets());
276            }
277            IpAddr::V6(addr) => {
278                assert_eq!(self.ip_version, IpVersion::V6);
279                self.data[24..40].copy_from_slice(&addr.octets());
280            }
281        }
282    }
283
284    pub fn src_port(&self) -> u16 {
285        u16::from_be_bytes(
286            self.data[self.transport_proto_offset..self.transport_proto_offset + 2]
287                .try_into()
288                .unwrap(),
289        )
290    }
291
292    pub fn dst_port(&self) -> u16 {
293        u16::from_be_bytes(
294            self.data[self.transport_proto_offset + 2..self.transport_proto_offset + 4]
295                .try_into()
296                .unwrap(),
297        )
298    }
299
300    pub fn set_src_port(&mut self, port: u16) {
301        self.data[self.transport_proto_offset..self.transport_proto_offset + 2]
302            .copy_from_slice(&port.to_be_bytes());
303    }
304
305    pub fn set_dst_port(&mut self, port: u16) {
306        self.data[self.transport_proto_offset + 2..self.transport_proto_offset + 4]
307            .copy_from_slice(&port.to_be_bytes());
308    }
309
310    pub fn src(&self) -> SocketAddr {
311        SocketAddr::from((self.src_ip(), self.src_port()))
312    }
313
314    pub fn dst(&self) -> SocketAddr {
315        SocketAddr::from((self.dst_ip(), self.dst_port()))
316    }
317
318    pub fn set_src(&mut self, src: &SocketAddr) {
319        self.set_src_ip(src.ip());
320        self.set_src_port(src.port());
321    }
322
323    pub fn set_dst(&mut self, dst: &SocketAddr) {
324        self.set_dst_ip(dst.ip());
325        self.set_dst_port(dst.port());
326    }
327
328    pub fn connection_id(&self) -> ConnectionId {
329        ConnectionId {
330            proto: self.transport_proto,
331            src: self.src(),
332            dst: self.dst(),
333        }
334    }
335
336    pub fn inner(self) -> Vec<u8> {
337        self.data
338    }
339
340    pub fn hop_limit(&self) -> u8 {
341        match self.ip_version {
342            IpVersion::V4 => self.data[8],
343            IpVersion::V6 => self.data[7],
344        }
345    }
346
347    pub fn set_hop_limit(&mut self, hop_limit: u8) {
348        match self.ip_version {
349            IpVersion::V4 => self.data[8] = hop_limit,
350            IpVersion::V6 => self.data[7] = hop_limit,
351        }
352    }
353
354    pub fn tcp_sequence_number(&self) -> u32 {
355        match self.transport_proto {
356            TransportProtocol::Tcp => {
357                u32::from_be_bytes(
358                    self.data[self.transport_proto_offset + 4..self.transport_proto_offset + 8]
359                        .try_into()
360                        .unwrap(),
361                )
362            }
363            _ => 0,
364        }
365    }
366
367    /// This method is a no-op if this is not a TCP packet.
368    pub fn set_tcp_sequence_number(&mut self, seq: u32) {
369        match self.transport_proto {
370            TransportProtocol::Tcp => {
371                self.data[self.transport_proto_offset + 4..self.transport_proto_offset + 8].copy_from_slice(&seq.to_be_bytes());
372            }
373            _ => (),
374        }
375    }
376
377    pub fn tcp_acknowledgement_number(&self) -> u32 {
378        match self.transport_proto {
379            TransportProtocol::Tcp => {
380                u32::from_be_bytes(
381                    self.data[self.transport_proto_offset + 8..self.transport_proto_offset + 12]
382                        .try_into()
383                        .unwrap(),
384                )
385            }
386            _ => 0,
387        }
388    }
389
390    /// This method is a no-op if this is not a TCP packet.
391    pub fn set_tcp_acknowledgement_number(&mut self, ack: u32) {
392        match self.transport_proto {
393            TransportProtocol::Tcp => {
394                self.data[self.transport_proto_offset + 8..self.transport_proto_offset + 12].copy_from_slice(&ack.to_be_bytes());
395            }
396            _ => (),
397        }
398    }
399
400    pub fn tcp_flags(&self) -> u8 {
401        match self.transport_proto {
402            TransportProtocol::Tcp => self.data[self.transport_proto_offset + 13],
403            _ => 0,
404        }
405    }
406
407    /// This method is a no-op if this is not a TCP packet.
408    pub fn set_tcp_flags(&mut self, flags: u8) {
409        match self.transport_proto {
410            TransportProtocol::Tcp => {
411                self.data[self.transport_proto_offset + 13] = flags;
412            },
413            _ => (),
414        }
415    }
416
417    pub fn tcp_syn(&self) -> bool {
418        self.tcp_flags() & 0x02 != 0
419    }
420
421    pub fn tcp_ack(&self) -> bool {
422        self.tcp_flags() & 0x10 != 0
423    }
424
425    pub fn tcp_flag_str(&self) -> String {
426        let mut flags: Vec<&str> = vec![];
427        let flag_bits = self.tcp_flags();
428        if flag_bits & 0x01 != 0 {
429            flags.push("FIN");
430        }
431        if flag_bits & 0x02 != 0 {
432            flags.push("SYN");
433        }
434        if flag_bits & 0x04 != 0 {
435            flags.push("RST");
436        }
437        if flag_bits & 0x08 != 0 {
438            flags.push("PSH");
439        }
440        if flag_bits & 0x10 != 0 {
441            flags.push("ACK");
442        }
443        if flag_bits & 0x20 != 0 {
444            flags.push("URG");
445        }
446        flags.join("/")
447    }
448
449    pub fn protocol(&self) -> TransportProtocol {
450        self.transport_proto
451    }
452
453    pub fn payload(&self) -> &[u8] {
454        &self.data[self.payload_offset..self.payload_end]
455    }
456
457    #[cfg(feature = "internet-checksum")]
458    #[cfg_attr(docsrs, doc(cfg(feature = "internet-checksum")))]
459    pub fn recalculate_ip_checksum(&mut self) {
460        if self.ip_version == IpVersion::V4 {
461            self.data[10..12].copy_from_slice(&[0, 0]);
462            let mut checksum = Checksum::new();
463            checksum.add_bytes(&self.data[0..20]);
464            self.data[10..12].copy_from_slice(&checksum.checksum());
465        }
466    }
467
468    #[cfg(feature = "internet-checksum")]
469    fn pseudo_header_checksum(&self) -> Checksum {
470        let upper_layer_packet_length = self.data.len() - self.transport_proto_offset;
471
472        let mut checksum = Checksum::new();
473        match self.ip_version {
474            IpVersion::V4 => {
475                let mut pseudo = [0u8; 12];
476                pseudo[0..8].copy_from_slice(&self.data[12..20]);
477                pseudo[9] = self.transport_proto as u8;
478                pseudo[10..12].copy_from_slice(&(upper_layer_packet_length as u16).to_be_bytes());
479                checksum.add_bytes(&pseudo);
480            }
481            IpVersion::V6 => {
482                let mut pseudo = [0u8; 40];
483                pseudo[0..32].copy_from_slice(&self.data[8..40]);
484                pseudo[32..36].copy_from_slice(&(upper_layer_packet_length as u32).to_be_bytes());
485                pseudo[39] = self.transport_proto as u8;
486                checksum.add_bytes(&pseudo);
487            }
488        };
489        checksum
490    }
491
492    #[cfg(feature = "internet-checksum")]
493    #[cfg_attr(docsrs, doc(cfg(feature = "internet-checksum")))]
494    pub fn recalculate_tcp_checksum(&mut self) {
495        if self.transport_proto != TransportProtocol::Tcp {
496            return;
497        }
498
499        let checksum_offset = self.transport_proto_offset + 16;
500        let mut checksum = self.pseudo_header_checksum();
501        self.data[checksum_offset..checksum_offset + 2].copy_from_slice(&[0, 0]);
502        checksum.add_bytes(&self.data[self.transport_proto_offset..]);
503        self.data[checksum_offset..checksum_offset + 2].copy_from_slice(&checksum.checksum());
504    }
505
506    #[cfg(feature = "internet-checksum")]
507    #[cfg_attr(docsrs, doc(cfg(feature = "internet-checksum")))]
508    pub fn recalculate_udp_checksum(&mut self) {
509        if self.transport_proto != TransportProtocol::Udp {
510            return;
511        }
512
513        let checksum_offset = self.transport_proto_offset + 6;
514        let mut checksum = self.pseudo_header_checksum();
515        self.data[checksum_offset..checksum_offset + 2].copy_from_slice(&[0, 0]);
516        checksum.add_bytes(&self.data[self.transport_proto_offset..]);
517        self.data[checksum_offset..checksum_offset + 2].copy_from_slice(&checksum.checksum());
518    }
519}
520
521impl Display for ConnectionId {
522    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
523        write!(f, "{} {} -> {}", self.proto, self.src, self.dst)
524    }
525}
526
527#[cfg(test)]
528mod tests {
529    use std::str::FromStr;
530
531    use data_encoding::HEXLOWER;
532
533    use super::*;
534
535    const IPV4_TCP_SYN: &[u8] = b"45000034d14b4000800680e0c0a8b2145db8d822d92100508ad94999000000008002faf01da30000020405b40103030801010402";
536    const IPV6_DNS_REQ: &[u8] =
537        b"60000000003c33403ffe050700000001020086fffe0580da3ffe0501481900000000000000000042\
538    11040000000009070000010bbc09dd98f9b0b12e647f4454\
539    095c00350024f0090006010000010000000000000669746f6a756e036f72670000ff0001";
540
541    const IPV4_TCP_ACK_WITH_PADDING: &[u8] = b"4500002869330000320676a85db8d822c0a8b2710050d5224e95b5f3e586c974501000807d6800000000";
542
543    const IPV4_FRAG_1: &[u8] = b"450005dcd0fe2000401109e6c118e3eeacd9284c";
544    const IPV4_FRAG_2: &[u8] = b"450000fad0fe00b940112e0fc118e3eeacd9284c";
545    const IPV6_FRAG_1: &[u8] = b"600787fd05b02c4020010470765b0000000000000a2500532a00145040130c03000000000000010a1100000128403c0b";
546    const IPV6_FRAG_2: &[u8] = b"600787fd00452c4020010470765b0000000000000a2500532a00145040130c03000000000000010a110005a828403c0b";
547
548
549    #[test]
550    fn parse_fragmented() {
551        assert_eq!(
552                InternetPacket::try_from(HEXLOWER.decode(IPV4_FRAG_1).unwrap()),
553                Err(ParseError::Fragmented)
554            );
555        assert_eq!(
556                InternetPacket::try_from(HEXLOWER.decode(IPV4_FRAG_2).unwrap()),
557                Err(ParseError::Fragmented)
558            );
559        assert_eq!(
560                InternetPacket::try_from(HEXLOWER.decode(IPV6_FRAG_1).unwrap()),
561                Err(ParseError::Fragmented)
562            );
563        assert_eq!(
564                InternetPacket::try_from(HEXLOWER.decode(IPV6_FRAG_2).unwrap()),
565                Err(ParseError::Fragmented)
566            );
567    }
568
569    #[test]
570    fn parse_udp_ipv6_packet() {
571        let mut packet = InternetPacket::try_from(HEXLOWER.decode(IPV6_DNS_REQ).unwrap()).unwrap();
572        assert_eq!(packet.ip_version, IpVersion::V6);
573        assert_eq!(
574            packet.connection_id(),
575            ConnectionId {
576                proto: TransportProtocol::Udp,
577                src: SocketAddr::from_str("[3ffe:507:0:1:200:86ff:fe05:80da]:2396").unwrap(),
578                dst: SocketAddr::from_str("[3ffe:501:4819::42]:53").unwrap(),
579            }
580        );
581        assert_eq!(packet.hop_limit(), 64);
582        assert_eq!(packet.payload().len(), 28);
583
584        packet.set_src(&SocketAddr::from_str("[::1]:2").unwrap());
585        packet.set_dst(&SocketAddr::from_str("[::3]:4").unwrap());
586        assert_eq!(
587            packet.connection_id(),
588            ConnectionId {
589                proto: TransportProtocol::Udp,
590                src: SocketAddr::from_str("[::1]:2").unwrap(),
591                dst: SocketAddr::from_str("[::3]:4").unwrap(),
592            }
593        );
594
595        packet.set_hop_limit(42);
596        assert_eq!(packet.hop_limit(), 42);
597        assert_eq!(packet.tcp_flag_str(), "");
598    }
599
600    #[test]
601    fn parse_udp_ipv6_packet_malformed() {
602        let data = HEXLOWER.decode(IPV6_DNS_REQ).unwrap();
603        for i in 0..72 {
604            assert!(matches!(
605                InternetPacket::try_from(data[..i].to_vec()),
606                Err(ParseError::Malformed)
607            ));
608        }
609        assert!(matches!(InternetPacket::try_from(data[..72].to_vec()), Ok(_)));
610    }
611
612    #[test]
613    fn parse_tcp_ipv4_packet() {
614        let mut packet = InternetPacket::try_from(HEXLOWER.decode(IPV4_TCP_SYN).unwrap()).unwrap();
615        assert_eq!(packet.ip_version, IpVersion::V4);
616        assert_eq!(
617            packet.connection_id(),
618            ConnectionId {
619                proto: TransportProtocol::Tcp,
620                src: SocketAddr::from_str("192.168.178.20:55585").unwrap(),
621                dst: SocketAddr::from_str("93.184.216.34:80").unwrap(),
622            }
623        );
624        assert_eq!(packet.hop_limit(), 128);
625        assert_eq!(packet.payload(), vec![]);
626
627        packet.set_src(&SocketAddr::from_str("1.2.3.4:5").unwrap());
628        packet.set_dst(&SocketAddr::from_str("4.3.2.1:0").unwrap());
629        assert_eq!(
630            packet.connection_id(),
631            ConnectionId {
632                proto: TransportProtocol::Tcp,
633                src: SocketAddr::from_str("1.2.3.4:5").unwrap(),
634                dst: SocketAddr::from_str("4.3.2.1:0").unwrap(),
635            }
636        );
637
638        packet.set_hop_limit(42);
639        assert_eq!(packet.hop_limit(), 42);
640        assert_eq!(packet.tcp_flag_str(), "SYN");
641        assert_eq!(packet.tcp_flags(), 0x02);
642        assert_eq!(packet.tcp_sequence_number(), 2329495961);
643        assert_eq!(packet.tcp_acknowledgement_number(), 0);
644
645        packet.set_tcp_sequence_number(1122);
646        packet.set_tcp_acknowledgement_number(3344);
647        assert_eq!(packet.tcp_sequence_number(), 1122);
648        assert_eq!(packet.tcp_acknowledgement_number(), 3344);
649
650        packet.set_tcp_flags(0xff);
651        assert_eq!(packet.tcp_flag_str(), "FIN/SYN/RST/PSH/ACK/URG");
652    }
653
654    #[test]
655    fn parse_tcp_ipv4_packet_malformed() {
656        let data = HEXLOWER.decode(IPV4_TCP_SYN).unwrap();
657        for i in 0..data.len() {
658            assert!(matches!(
659                InternetPacket::try_from(data[..i].to_vec()),
660                Err(ParseError::Malformed)
661            ));
662        }
663    }
664
665    #[test]
666    fn parse_tcp_ipv4_packet_with_padding() {
667        let packet = InternetPacket::try_from(HEXLOWER.decode(IPV4_TCP_ACK_WITH_PADDING).unwrap()).unwrap();
668        assert_eq!(packet.ip_version, IpVersion::V4);
669        assert!(!packet.tcp_syn());
670        assert!(packet.tcp_ack());
671        assert_eq!(packet.payload().len(), 0);
672    }
673
674    #[cfg(feature = "internet-checksum")]
675    #[test]
676    fn recalculate_ipv4_checksum() {
677        let raw = HEXLOWER.decode(IPV4_TCP_SYN).unwrap();
678        let mut raw2 = raw.clone();
679        raw2[10..12].copy_from_slice(&[0xab, 0xcd]);
680        let mut packet = InternetPacket::try_from(raw2).unwrap();
681
682        packet.recalculate_ip_checksum();
683        assert_eq!(packet.data, raw);
684    }
685
686    #[cfg(feature = "internet-checksum")]
687    #[test]
688    fn recalculate_tcp_checksum_ipv4() {
689        let raw = HEXLOWER.decode(IPV4_TCP_SYN).unwrap();
690        let mut raw2 = raw.clone();
691        raw2[36..38].copy_from_slice(&[0xab, 0xcd]);
692        let mut packet = InternetPacket::try_from(raw2).unwrap();
693        packet.recalculate_tcp_checksum();
694        assert_eq!(packet.data, raw);
695    }
696
697    #[cfg(feature = "internet-checksum")]
698    #[test]
699    fn recalculate_udp_checksum_ipv6() {
700        let raw = HEXLOWER.decode(IPV6_DNS_REQ).unwrap();
701        let mut raw2 = raw.clone();
702        raw2[70..72].copy_from_slice(&[0xab, 0xcd]);
703        let mut packet = InternetPacket::try_from(raw2).unwrap();
704        packet.recalculate_udp_checksum();
705        assert_eq!(packet.data, raw);
706    }
707
708    #[test]
709    fn canonicalize_connection_id() {
710        let a = ConnectionId {
711            proto: TransportProtocol::Tcp,
712            src: SocketAddr::from_str("[::1]:2").unwrap(),
713            dst: SocketAddr::from_str("[::3]:4").unwrap(),
714        };
715        let b = a.reverse();
716        assert_ne!(a, b);
717        assert_eq!(a.canonical_form(), b.canonical_form());
718    }
719
720    #[cfg(feature = "smoltcp")]
721    #[test]
722    fn from_smoltcp_ipv4() {
723        let buf = Vec::from(HEXLOWER.decode(IPV4_TCP_SYN).unwrap());
724        let smol_packet = smoltcp::wire::Ipv4Packet::new_checked(buf).unwrap();
725        let packet = InternetPacket::try_from(smol_packet).unwrap();
726        assert_eq!(packet.hop_limit(), 128);
727    }
728
729    #[cfg(feature = "smoltcp")]
730    #[test]
731    fn from_smoltcp_ipv6() {
732        let buf = Vec::from(HEXLOWER.decode(IPV6_DNS_REQ).unwrap());
733        let smol_packet = smoltcp::wire::Ipv6Packet::new_checked(buf).unwrap();
734        let packet = InternetPacket::try_from(smol_packet).unwrap();
735        assert_eq!(packet.hop_limit(), 64);
736    }
737}