Calculate UDP Checksum in Rust? - rust

I am trying to manually build packets and am having trouble calculating a correct UDP checksum. Can someone tell me what I am doing wrong in the below code? The packet being passed in is the complete packet to be sent with a placeholder for the UDP Checksum currently of 0x0000, but I sum the psuedoheader, udp header, and udp payload, but according to wireshark my UDP checksums are incorrect. (Mine: 0x9f4c vs Wireshark: 0x2b7b for example)
fn udp_checksum (packet: &Vec<u8>) -> [u8; 2] {
let mut idx = 0;
let mut idx_end = 2;
let mut payload = &packet[42..];
let payload_len = payload.len();
if payload_len % 2 != 0 {
payload.to_vec().push(0);
}
let source_ip_1 = BigEndian::read_u16(&packet[26..28]); //source ip 1 of 2
let source_ip_2 = BigEndian::read_u16(&packet[28..30]); //source ip 2 of 2
let dest_ip_1 = BigEndian::read_u16(&packet[30..32]); //dest ip 1 of 2
let dest_ip_2 = BigEndian::read_u16(&packet[32..34]); //dest ip 2 of 2
let udp_len = BigEndian::read_u16(&packet[38..40]);
let source_port = BigEndian::read_u16(&packet[34..36]);
let dest_port = BigEndian::read_u16(&packet[36..38]);
let mut header_sum = UDP_PROTO as u32 + source_ip_1 as u32 + source_ip_2 as u32 + dest_ip_1 as u32 + dest_ip_2 as u32 + udp_len as u32 + source_port as u32 + dest_port as u32 + udp_len as u32;
// println!("Payload Len: {:?}", &payload.len());
// println!("Payload: {:?}", &payload);
// println!("First Payload Slice: {:?}", &payload[idx..idx_end]);
// println!("First BE U32: {:?}", BigEndian::read_u16(&payload[idx..idx_end]) as u32);
while idx < &payload.len() - 2 {
header_sum += BigEndian::read_u16(&payload[idx..idx_end]) as u32;
println!("Header Sum: {:0x?}", &header_sum);
idx += 2;
idx_end += 2;
}
while header_sum > 0xffff {
header_sum -= 0xffff;
header_sum += 1;
}
let udp_csum = 0xffff - (header_sum as u16);
let csum_one: u8 = header_sum as u8;
let csum_two: u8 = (header_sum >> 8) as u8;
println!("Calculated CSUM: {:?}", udp_csum);
println!("Checksum: {:0x}{:0x}", csum_one, csum_two);
return [csum_one, csum_two];
}```

It's maybe only a partial solution to the problem.
payload.to_vec() creates a new vector.
Extending it has no influence on payload.
Making payload mutable will enable working on the extended vector if necessary.
Here is a minimal example.
fn main() {
let v1 = vec![9, 1, 2, 3];
let mut v2 = Vec::new(); // empty for now
let mut sl = &v1[1..]; // could be reassigned to v2
println!("before v1: {:?}", v1);
println!("before v2: {:?}", v2);
println!("before sl: {:?}", sl);
if sl.len() % 2 != 0 {
v2 = sl.to_vec();
v2.push(0);
sl = &v2[..];
}
println!("after v1: {:?}", v1);
println!("after v2: {:?}", v2);
println!("after sl: {:?}", sl);
}
/*
before v1: [9, 1, 2, 3]
before v2: []
before sl: [1, 2, 3]
after v1: [9, 1, 2, 3]
after v2: [1, 2, 3, 0]
after sl: [1, 2, 3, 0]
*/
Another solution, avoiding the copy, would be to stop one byte earlier (if payload.len() is odd) the loop, then deal with the remaining byte.

Related

Transferring memory between processes

I want to transfer an item between processes (specifically a TcpStream). I attempted to do this with a more complex example however failed in this. So I am trying to do this with a simpler example:
My current attempt is:
use bytes::{Buf, BytesMut};
use tokio::{
io::AsyncWriteExt,
net::{tcp::OwnedWriteHalf, TcpListener, TcpStream},
};
use tokio_stream::StreamExt;
use tokio_util::codec::{Decoder, FramedRead};
const TRANSFER_ADDRESS: &str = "127.0.0.1:8081";
#[tokio::main]
async fn main() {
if let Ok(existing_server_process_listener) =
TcpStream::connect(TRANSFER_ADDRESS).await
{
let (read, _write) = existing_server_process_listener.into_split();
let mut reader = FramedRead::new(read, DatabaseTransferDecoder);
let bytes = reader.next().await.unwrap().unwrap();
dbg!(&bytes);
let addr = usize::from_ne_bytes(bytes.try_into().unwrap());
dbg!(addr);
let ptr = addr as *mut u32;
dbg!(ptr);
let box_ptr = unsafe { Box::from_raw(ptr)};
dbg!(box_ptr);
}
else {
let update_listener = TcpListener::bind(TRANSFER_ADDRESS).await.unwrap();
let (stream, _socket) = update_listener.accept().await.unwrap();
// Splits our stream into read/write
let (_read, mut write) = stream.into_split();
let x = Box::new(4u32);
let ptr = Box::into_raw(x);
dbg!(ptr);
let addr = ptr as usize;
dbg!(addr);
let bytes = addr.to_ne_bytes();
dbg!(bytes);
write_fn(&mut write,&bytes).await;
}
}
async fn write_fn(writer: &mut OwnedWriteHalf, string: &[u8]) {
let buffer = {
let mut b = Vec::with_capacity(4 + string.len());
b.extend_from_slice(&u32::to_le_bytes(u32::try_from(string.len()).unwrap()));
b.extend_from_slice(string);
b
};
writer.write_all(&buffer).await.unwrap();
}
struct DatabaseTransferDecoder;
/// Implement `tokio_util::codec::decoder` for framed reads.
impl Decoder for DatabaseTransferDecoder {
type Error = std::io::Error;
type Item = Vec<u8>;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
// We use u32 for len marker, so need an initial 4 bytes
if src.len() < 4 {
return Ok(None);
}
// Get length
let length = {
let mut length_bytes = [0; 4];
length_bytes.copy_from_slice(&src[..4]);
u32::from_le_bytes(length_bytes) as usize
};
if src.len() < 4 + length {
src.reserve(4 + length - src.len());
return Ok(None);
}
// Read data
let data = src[4..4 + length].to_vec();
// Advance buffer to discard data
src.advance(4 + length);
Ok(Some(data))
}
}
Running the first process then running the second process causes the first process to output:
[src\main.rs:33] ptr = 0x000001f8b8435980
[src\main.rs:35] addr = 2167754938752
[src\main.rs:37] bytes = [
128,
89,
67,
184,
248,
1,
0,
0,
]
and the second process to output:
[src\main.rs:18] &bytes = [
128,
89,
67,
184,
248,
1,
0,
0,
]
[src\main.rs:20] addr = 2167754938752
[src\main.rs:22] ptr = 0x000001f8b8435980
[src\main.rs:24] box_ptr = error: process didn't exit successfully: `target\debug\testing-bin.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)
Why would accessing this memory cause this error?

How to convert a bool array into a byte array

How do I convert a bool array into a byte array?
I would like the resulting vector to have 1/8th the length of the input vector.
Like this
let a = [false; 160];
let b: [u8; 20] = ???
I guess I can make a for-loop and do some arithmetic, but I wonder if there is a simpler way of doing it.
As an alternative to the crate-based solutions, the "hand-rolled" version isn't too difficult to implement, and avoids the allocations:
let mut b = [0u8;20];
for (idx, bit) in a.into_iter().enumerate() {
let byte = idx / 8;
let shift = 7 - idx % 8;
b[byte] |= (bit as u8) << shift;
}
The issue making this generic is that the current const generics don't support arithmetics on constants.
In nightly with generic_const_expr, it's possible for the thing to work on arbitrary input sizes (this incomplete implementation will panic on incomplete trailing bytes):
#![feature(generic_const_exprs)]
fn to_bits<const N: usize>(bools: [bool;N]) -> [u8;N/8] {
let mut out = [0;N/8];
for (idx, bit) in bools.into_iter().enumerate() {
let byte = idx / 8;
let shift = 7 - idx % 8;
out[byte] |= (bit as u8) << shift;
}
out
}
fn main() {
let mut a = [false; 160];
a[42] = true;
let b = to_bits(a);
println!("{:?}", b);
}
though I think bitvec also has a BitArray type which might allow doing this without allocations as well.
This works and doesn't require a new dependency.
let mut b = [0u8;20];
for i in 0..160 {
b[i / 8] |= u8::from(a[i]) << (i % 8);
}
Using the bitvec crate, something like this should come close:
use bitvec::vec::BitVec;
let a = [false; 160];
let b = [0; 20]
b[..].copy_from_slice (a.iter().collect::<BitVec>().as_raw_slice());
I don't think there is a way to do that with just the built-in std library.
With the excellent bit-vec crate, though:
use bit_vec::BitVec;
fn main() {
let mut a = [false; 160];
a[42] = true;
let bitvec: BitVec = a.into_iter().collect();
let b: [u8; 20] = bitvec.to_bytes().try_into().unwrap();
println!("{:?}", b);
}
[0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
You can put it inside a generic function, but that requires nightly and unstable features:
#![feature(generic_const_exprs)]
use bit_vec::BitVec;
fn convert_vector<const N: usize>(bits: [bool; N]) -> [u8; N / 8] {
let bitvec: BitVec = bits.into_iter().collect();
bitvec.to_bytes().try_into().unwrap()
}
fn main() {
let mut a = [false; 160];
a[42] = true;
println!("{:?}", convert_vector(a));
}

How to slice to a particular element in a Vec?

What is the best way to slice a Vec to the first occurrence of a particular element?
A naive method demonstrating what I want to do:
fn main() {
let v = vec![1, 2, 3, 4, 5, 6];
let to_num = 5;
let mut idx = 0;
for x in &v {
if x != &to_num {
idx += 1
} else {
break;
}
}
let slice = &v[..idx];
println!("{:?}", slice); //prints [1, 2, 3, 4]
}
^ on the Rust playground
You can use <[T]>::split():
let slice = v.split(|x| *x == to_num).next().unwrap();
Playground.

Type conversion and casting

I am beginner in rust. I have simple script but I have to use too much type casting.
Point of script: search clusters of neighbour cels with the same value in matrix (using flood fill algo with queue https://en.wikipedia.org/wiki/Flood_fill).
This is full code:
fn find_clusters(playground: [[u8; SIZE]; SIZE]) -> Vec<Cluster> {
let directions_cluster: [[i8; 2]; 4] = [[0, 1], [0, -1], [1, 0], [-1, 0]];
let mut clusters: Vec<Cluster> = Vec::new();
let mut queue: Vec<[usize; 2]> = Vec::new();
let mut marked_cells: [[u8; SIZE]; SIZE] = [[0; SIZE]; SIZE];
for i in 0..SIZE {
for j in 0..SIZE {
if marked_cells[i][j] == 1 { continue; }
let code = playground[i][j];
let mut cluster = Cluster::new();
queue.push([i, j]);
marked_cells[i][j] = 1;
while !queue.is_empty() {
let coords = queue.pop().unwrap();
cluster.coords.push(coords);
for direction in &directions_cluster {
let check_i = coords[0] as i8 + direction[0];
if check_i < 0 || check_i as usize >= SIZE {continue;}
let check_j = coords[1] as i8 + direction[1];
if check_j < 0 || check_j as usize >= SIZE {continue;}
let ni = check_i as usize;
let nj = check_j as usize;
if playground[ni][nj] == code && marked_cells[ni][nj] == 0 {
queue.push([ni, nj]);
marked_cells[ni][nj] = 1;
}
}
}
if cluster.coords.len() >= 5 {
cluster.code = code;
clusters.push(cluster);
}
};
};
return clusters;
}
But I don't like this part:
for direction in &directions_cluster {
let check_i = coords[0] as i8 + direction[0];
if check_i < 0 || check_i as usize >= SIZE {continue;}
let check_j = coords[1] as i8 + direction[1];
if check_j < 0 || check_j as usize >= SIZE {continue;}
let ni = check_i as usize;
let nj = check_j as usize;
if playground[ni][nj] == code && marked_cells[ni][nj] == 0 {
queue.push([ni, nj]);
marked_cells[ni][nj] = 1;
}
}
I even had to define additional variables (check_i, check_j) to not use casting for ni/nj each time later.
What the best way of type casting in may case?
You can use the TryInto trait from the standard library to abstract away from the overflow checking. This is how I would implement it:
use std::convert::TryInto;
type Pos = [usize; 2];
fn add_to_coords(coords: Pos, offset: [i8; 2]) -> Option<Pos> {
let ni: usize = (coords[0] as i8 + direction[0]).try_into().ok()?;
let nj: usize = (coords[1] as i8 + direction[1]).try_into().ok()?;
[ni, nj]
}
// ...
for [ni, nj] in directions.flat_map(|dir| add_to_coords(coords, dir)) {
// ...
}
// ...
The call to flat_map filters out all the return values that were None, in case you were wondering where the continue went.

Binary search a vector in chunks

I have a file of ipv4 addresses, which as we know are 4 bytes each. I wish to do a binary search over the file contents to find a given IP address. Rust has a built-in binary search but it doesn't let you pass a len and it instead reads it from the vector.
I have tried to adapt the built-in rust binary search but am a bit lost. This is where i am so far. Maybe there is a way to use the built in method?
fn binary_search(s: &Vec<&u8>, x: &u32) -> Result<usize, usize> {
let f = |p: &[u8]| p.cmp(x); // need to compare byte slices somehow
let mut size = s.len() / 4;
if size == 0 {
return Err(0);
}
let mut base = 0usize;
while size > 1 {
let half = size / 2;
let mid = base + half;
let cmp = f(s[mid..mid+4]);
base = if cmp == Greater { base } else { mid };
size -= half;
}
let cmp = f(s[base..base+4]);
if cmp == Equal {
Ok(base)
} else {
Err(base + (cmp == Less) as usize)
}
}
It’d be better to have a slice with one element per address, either of 4-byte arrays ([u8; 4]), some equivalent struct (hey, Ipv4Addr), or just u32. Unfortunately, I don’t think it’s possible to reinterpret a &[u8] with a length divisible by 4 as &[[u8; 4]] yet (and the other options would need alignment). You could do this conversion while reading the file in chunks, though.
So first, in an equivalent example program:
use std::net::Ipv4Addr;
fn main() {
let vec: Vec<Ipv4Addr> = vec![
[10, 0, 0, 0].into(),
[20, 0, 0, 0].into(),
[30, 0, 0, 0].into(),
];
println!("vec {:?}", vec);
let found = vec.binary_search(&Ipv4Addr::from_str("20.0.0.0").unwrap());
println!("found {:?}", found);
}
(playground)
Then reading from a file would look something like:
let mut vec: Vec<Ipv4Addr> = vec![];
loop {
let mut address = [0; 4];
match f.read_exact(&mut address) {
Ok(()) => {},
Err(err) if err.kind() == ErrorKind::UnexpectedEof => break,
err => err?,
}
vec.push(address.into());
}
(although this one is slightly lax in that it ignores any trailing bytes that don’t form a multiple of 4)
where f is a BufReader around a file.
I think i have a working solution now, but i'm not a master at rust so please critique it harshly.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6e3102ea622f1ae0d66465f4007ccb03
use std::cmp::Ordering::{self, Equal, Greater, Less};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
fn binary_search(s: Vec<u8>, x: Vec<u8>) -> Result<usize, usize> {
let f = |p: &[u8]| p.cmp(&x);
let mut size = s.len() / 4;
if size == 0 {
return Err(0);
}
let mut base = 0usize;
while size > 1 {
let half = size / 2;
let mid = base + half;
// mid is always in [0, size), that means mid is >= 0 and < size.
// mid >= 0: by definition
// mid < size: mid = size / 2 + size / 4 + size / 8 ...
let cmp = f(s[mid*4..(mid+1)*4].to_vec());
base = if cmp == Greater { base } else { mid };
size -= half;
}
// base is always in [0, size) because base <= mid.
let cmp = f(s[base*4..(base+1)*4].to_vec());
if cmp == Equal {
Ok(base*4)
} else {
Err(base*4 + ((cmp == Less) as usize) * 4)
}
}
fn main() {
let vec: Vec<u8> = vec![10, 0, 0, 0, 20, 0, 0, 0, 30, 0, 0, 0];
println!("vec {:?}", vec);
let found = binary_search(vec, Ipv4Addr::from_str("20.0.0.0").unwrap().octets().to_vec());
println!("found {:?}", found);
}

Resources