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, 43, 44, 50, 51, 60, 135, 139, 140 ];
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 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 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
230impl 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 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 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 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}