I need to write a loopless version of UDP header calculation for a fixed length packet. My packet has always exactly 13 bytes of payload. I made the following test program for it, but for some reason I can’t get the right answer from reading the UDP checksum spec. Any ideas where the problem is?
use etherparse::PacketBuilder;
const SRC_IP: usize = 12;
const DST_IP: usize = 16;
const UDP_START: usize = 20;
const SRC_PORT: usize = 0;
const DST_PORT: usize = 2;
const LEN: usize = 4;
const CHECK: usize = 6;
const IP_HEADER_LEN: u32 = 20;
const UDP_HEADER_LEN: u32 = 8;
const PAYLOAD_LEN: u32 = 13;
const PAYLOAD_START: usize = IP_HEADER_LEN as usize + UDP_HEADER_LEN as usize;
fn my_checksum(packet: [u8; (IP_HEADER_LEN + UDP_HEADER_LEN + PAYLOAD_LEN) as usize]) -> [u8; 2] {
let mut sum: u32 = UDP_HEADER_LEN + PAYLOAD_LEN + 17; // 17 = UDP protocol number
// Source IP
sum += (packet[SRC_IP] as u32) << 8 | (packet[SRC_IP + 1] as u32);
sum += (packet[SRC_IP + 2] as u32) << 8 | (packet[SRC_IP + 3] as u32);
// Destination IP
sum += (packet[DST_IP] as u32) << 8 | (packet[DST_IP + 1] as u32);
sum += (packet[DST_IP + 2] as u32) << 8 | (packet[DST_IP + 3] as u32);
// Source PORT
sum += (packet[UDP_START + SRC_PORT] as u32) << 8 | packet[UDP_START + SRC_PORT + 1] as u32;
// Destination PORT
sum += (packet[UDP_START + DST_PORT] as u32) << 8 | packet[UDP_START + DST_PORT + 1] as u32;
// Payload
sum += (packet[PAYLOAD_START + 0] as u32) << 8 |packet[PAYLOAD_START + 1] as u32;
sum += (packet[PAYLOAD_START + 2] as u32) << 8 |packet[PAYLOAD_START + 3] as u32;
sum += (packet[PAYLOAD_START + 4] as u32) << 8 |packet[PAYLOAD_START + 5] as u32;
sum += (packet[PAYLOAD_START + 6] as u32) << 8 |packet[PAYLOAD_START + 7] as u32;
sum += (packet[PAYLOAD_START + 8] as u32) << 8 |packet[PAYLOAD_START + 9] as u32;
sum += (packet[PAYLOAD_START + 10] as u32) << 8 |packet[PAYLOAD_START + 11] as u32;
sum += (packet[PAYLOAD_START + 12] as u32) << 8;
// We need at most 2 rounds for overflow clearing for a packet less than 64 bytes.
sum = (sum & 0xffff) + (sum >> 16);
sum = (sum & 0xffff) + (sum >> 16);
let sum = !(sum as u16);
sum.to_be_bytes()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_packet() {
// Build packet using etherparse
let builder = PacketBuilder::
ipv4([192,168,1,1],[192,168,1,2],20)
.udp(10000,5000);
let payload = [1,2,3,4,5,6,7,8,9,10,11,12,13];
let mut result = Vec::<u8>::with_capacity(builder.size(payload.len()));
builder.write(&mut result, &payload).unwrap();
println!("{:?}", result);
assert_eq!([result[UDP_START + CHECK], result[UDP_START + CHECK + 1]], my_checksum(result.try_into().unwrap()));
}
}