mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-03-21 17:41:52 +00:00
Within Firezone's Rust codebase, we use the `etherparse` crate extensively to parse network packets. To provide a more ergonomic API, this is all encapsulated in our `ip-packet` crate. For #7518, we need to write an eBPF kernel that parses and manipulates network packets. Etherparse itself doesn't provide any facilities to manipulate network packets. That is an open feature request: https://github.com/JulianSchmid/etherparse/issues/9. For the packet manipulation that we are doing in `connlib`, we already wrote certain extensions to the `etherparse` crate but today, those are all within the `ip-packet` crate. In order to reuse that within the eBPF kernel, we cannot just depend on `ip-packet` directly because eBPF is a no-std and no-alloc environment, thus no crate in the dependency tree is allowed to depend on Rust's std-lib. `etherparse` itself actually has an `std` feature flag that we can turn off. Introducing the same in `ip-packet` would require a lot of conditional-compilation gates using `#[cfg]`. it is much easier to just introduce a new crate that houses all our in-house extensions to `etherparse`. Eventually, we can hopefully upstream those which is another motivator to separate this out.
83 lines
2.4 KiB
Rust
83 lines
2.4 KiB
Rust
use crate::slice_utils::write_to_offset_unchecked;
|
|
use etherparse::UdpHeaderSlice;
|
|
|
|
pub struct UdpHeaderSliceMut<'a> {
|
|
slice: &'a mut [u8],
|
|
}
|
|
|
|
impl<'a> UdpHeaderSliceMut<'a> {
|
|
/// Creates a new [`UdpHeaderSliceMut`].
|
|
pub fn from_slice(slice: &'a mut [u8]) -> Result<Self, etherparse::err::LenError> {
|
|
UdpHeaderSlice::from_slice(slice)?;
|
|
|
|
Ok(Self { slice })
|
|
}
|
|
|
|
/// Creates a new [`UdpHeaderSliceMut`] without checking the slice.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// The caller must guarantee that the slice is at least of length 8.
|
|
pub unsafe fn from_slice_unchecked(slice: &'a mut [u8]) -> Self {
|
|
Self { slice }
|
|
}
|
|
|
|
pub fn get_source_port(&self) -> u16 {
|
|
u16::from_be_bytes([self.slice[0], self.slice[1]])
|
|
}
|
|
|
|
pub fn get_destination_port(&self) -> u16 {
|
|
u16::from_be_bytes([self.slice[2], self.slice[3]])
|
|
}
|
|
|
|
pub fn set_source_port(&mut self, src: u16) {
|
|
// Safety: Slice it at least of length 8 as checked in the ctor.
|
|
unsafe { write_to_offset_unchecked(self.slice, 0, src.to_be_bytes()) };
|
|
}
|
|
|
|
pub fn set_destination_port(&mut self, dst: u16) {
|
|
// Safety: Slice it at least of length 8 as checked in the ctor.
|
|
unsafe { write_to_offset_unchecked(self.slice, 2, dst.to_be_bytes()) };
|
|
}
|
|
|
|
pub fn set_length(&mut self, length: u16) {
|
|
// Safety: Slice it at least of length 8 as checked in the ctor.
|
|
unsafe { write_to_offset_unchecked(self.slice, 4, length.to_be_bytes()) };
|
|
}
|
|
|
|
pub fn set_checksum(&mut self, checksum: u16) {
|
|
// Safety: Slice it at least of length 8 as checked in the ctor.
|
|
unsafe { write_to_offset_unchecked(self.slice, 6, checksum.to_be_bytes()) };
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use etherparse::PacketBuilder;
|
|
|
|
#[test]
|
|
fn smoke() {
|
|
let mut buf = Vec::new();
|
|
|
|
PacketBuilder::ipv4([0u8; 4], [0u8; 4], 0)
|
|
.udp(10, 20)
|
|
.write(&mut buf, &[])
|
|
.unwrap();
|
|
|
|
let mut slice = UdpHeaderSliceMut::from_slice(&mut buf[20..]).unwrap();
|
|
|
|
slice.set_source_port(30);
|
|
slice.set_destination_port(40);
|
|
slice.set_length(50);
|
|
slice.set_checksum(60);
|
|
|
|
let slice = UdpHeaderSlice::from_slice(&buf[20..]).unwrap();
|
|
|
|
assert_eq!(slice.source_port(), 30);
|
|
assert_eq!(slice.destination_port(), 40);
|
|
assert_eq!(slice.length(), 50);
|
|
assert_eq!(slice.checksum(), 60);
|
|
}
|
|
}
|