vmm/dumbo/pdu/mod.rs
1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! A module for interpreting byte slices as protocol data units (PDUs).
5//!
6//! A PDU represents data transmitted as a single unit during communication using a specific
7//! protocol. Ethernet frames, IP packets, and TCP segments are all examples of protocol data
8//! units.
9
10use std::fmt::Debug;
11use std::net::Ipv4Addr;
12
13use crate::dumbo::pdu::bytes::NetworkBytes;
14use crate::dumbo::pdu::ipv4::{PROTOCOL_TCP, PROTOCOL_UDP};
15
16pub mod arp;
17pub mod bytes;
18pub mod ethernet;
19pub mod ipv4;
20pub mod tcp;
21pub mod udp;
22
23/// This is the baseline definition of the `Incomplete` struct, which wraps a PDU that does is
24/// still missing some values or content.
25///
26/// It's mostly important when writing PDUs, because fields like checksum
27/// can only be computed after the payload becomes known. Also, the length of the underlying slice
28/// should be equal to the actual size for a complete PDU. To that end, whenever a variable-length
29/// payload is involved, the slice is shrunk to an exact fit. The particular ways of completing an
30/// `Incomplete<T>` are implemented for each specific PDU.
31#[derive(Debug)]
32pub struct Incomplete<T> {
33 inner: T,
34}
35
36impl<T: Debug> Incomplete<T> {
37 #[inline]
38 fn new(inner: T) -> Self {
39 Incomplete { inner }
40 }
41
42 /// Returns a reference to the wrapped object.
43 #[inline]
44 pub fn inner(&self) -> &T {
45 &self.inner
46 }
47
48 /// Returns a mutable reference to the wrapped object.
49 #[inline]
50 pub fn inner_mut(&mut self) -> &mut T {
51 &mut self.inner
52 }
53}
54
55#[repr(u8)]
56#[derive(Debug, Copy, Clone, PartialEq)]
57enum ChecksumProto {
58 Tcp = PROTOCOL_TCP,
59 Udp = PROTOCOL_UDP,
60}
61
62/// Computes the checksum of a TCP/UDP packet. Since both protocols use
63/// the same algorithm to compute the checksum.
64///
65/// # Arguments
66/// * `bytes` - Raw bytes of a TCP packet or a UDP datagram
67/// * `src_addr` - IPv4 source address
68/// * `dst_addr` - IPv4 destination address
69/// * `protocol` - **must** be either `PROTOCOL_TCP` or `PROTOCOL_UDP` defined in `ipv4` module
70///
71/// More details about TCP checksum computation can be found [here].
72///
73/// [here]: https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Checksum_computation
74#[inline]
75fn compute_checksum<T: NetworkBytes + Debug>(
76 bytes: &T,
77 src_addr: Ipv4Addr,
78 dst_addr: Ipv4Addr,
79 protocol: ChecksumProto,
80) -> u16 {
81 let mut sum = 0usize;
82
83 let a = u32::from(src_addr) as usize;
84 sum += a & 0xffff;
85 sum += a >> 16;
86
87 let b = u32::from(dst_addr) as usize;
88 sum += b & 0xffff;
89 sum += b >> 16;
90
91 let len = bytes.len();
92 sum += protocol as usize;
93 sum += len;
94
95 for i in 0..len / 2 {
96 sum += usize::from(bytes.ntohs_unchecked(i * 2));
97 }
98
99 if len % 2 != 0 {
100 sum += usize::from(bytes[len - 1]) << 8;
101 }
102
103 while sum >> 16 != 0 {
104 sum = (sum & 0xffff) + (sum >> 16);
105 }
106
107 // Safe to unwrap due to the while loop above
108 let mut csum = !u16::try_from(sum).unwrap();
109 // If a UDP packet checksum is 0, an all ones value is transmitted
110 if protocol == ChecksumProto::Udp && csum == 0x0 {
111 csum = !csum;
112 }
113
114 csum
115}