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}