use std::net::{Ipv4Addr, SocketAddrV4};
use std::rc::Rc;
use pkt::eth::{eth_hdr, ethertype};
use pkt::ipv4::{ip_csum_fold, ip_csum_partial, ip_hdr, ip_pseudo_hdr, proto, udp_hdr};
use pkt::{Hdr, Packet};
#[derive(Debug, PartialEq, Eq)]
pub struct UdpFlow {
cl: SocketAddrV4,
sv: SocketAddrV4,
raw: bool,
}
pub struct UdpDgram {
pkt: Packet,
eth: Option<Hdr<eth_hdr>>,
ip: Hdr<ip_hdr>,
udp: Hdr<udp_hdr>,
}
impl UdpDgram {
const RAW_OVERHEAD: usize = std::mem::size_of::<ip_hdr>() + std::mem::size_of::<udp_hdr>();
const OVERHEAD: usize = std::mem::size_of::<eth_hdr>() + Self::RAW_OVERHEAD;
#[must_use]
pub fn with_capacity(payload_sz: usize, raw: bool) -> Self {
let pkt = if raw {
Packet::with_capacity(Self::RAW_OVERHEAD + payload_sz)
} else {
Packet::with_capacity(Self::OVERHEAD + payload_sz)
};
let eth = if raw {
None
} else {
Some(pkt.push(eth_hdr::with_proto(ethertype::IPV4)))
};
let mut iph = ip_hdr::default();
iph.set_protocol(proto::UDP)
.set_tot_len(Self::RAW_OVERHEAD as u16)
.calc_csum();
let ip = pkt.push(iph);
let udp = pkt.push(udp_hdr::default());
Self { pkt, eth, ip, udp }
}
#[must_use]
pub fn new() -> Self {
Self::with_capacity(0, false)
}
pub fn raw() -> Self {
Self::with_capacity(0, true)
}
pub fn of_type(raw: bool) -> Self {
Self::with_capacity(0, raw)
}
#[must_use]
pub fn srcip(self, src: Ipv4Addr) -> Self {
self.ip.get_mut(&self.pkt).set_saddr(src);
self
}
#[must_use]
pub fn frag_off(self, frag_off: u16) -> Self {
self.ip.get_mut(&self.pkt).set_frag_off(frag_off);
self
}
#[must_use]
pub fn src(self, src: SocketAddrV4) -> Self {
if let Some(eth) = self.eth {
eth.get_mut(&self.pkt).src_from_ip(*src.ip());
}
self.ip.get_mut(&self.pkt).set_saddr(*src.ip());
self.udp.get_mut(&self.pkt).set_sport(src.port());
self
}
#[must_use]
pub fn dst(self, dst: SocketAddrV4) -> Self {
if let Some(eth) = self.eth {
eth.get_mut(&self.pkt).dst_from_ip(*dst.ip());
}
self.ip.get_mut(&self.pkt).set_daddr(*dst.ip());
self.udp.get_mut(&self.pkt).set_dport(dst.port());
self
}
#[must_use]
pub fn broadcast(self) -> Self {
if let Some(eth) = self.eth {
eth.get_mut(&self.pkt).set_broadcast();
}
self
}
#[must_use]
pub fn push<T: AsRef<[u8]>>(self, bytes: T) -> Self {
let buf = bytes.as_ref();
self.pkt.push_bytes(buf);
self.update_tot_len(buf.len() as u16)
.update_dgram_len(buf.len() as u16)
}
#[must_use]
fn update_tot_len(self, more: u16) -> Self {
self.ip.get_mut(&self.pkt).add_tot_len(more).calc_csum();
self
}
#[must_use]
fn update_dgram_len(self, more: u16) -> Self {
self.udp.get_mut(&self.pkt).add_len(more);
self
}
fn ip_pseudo_hdr(&self, len: u16) -> ip_pseudo_hdr {
self.ip.get(&self.pkt).get_pseudo_hdr(len)
}
fn csum_len(&self) -> u16 {
self.udp.len_from(&self.pkt) as u16
}
pub fn csum(self) -> Self {
let ip_phdr = self.ip_pseudo_hdr(self.csum_len()).csum_partial();
let udp_hdr = ip_csum_partial(&self.udp.as_bytes(&self.pkt));
let payload = ip_csum_partial(
&self
.udp
.bytes_after(&self.pkt, self.udp.len_after(&self.pkt)),
);
self.udp
.get_mut(&self.pkt)
.set_csum(ip_csum_fold(ip_phdr + udp_hdr + payload));
self
}
pub fn udp_hdr_bytes(&self) -> pkt::SliceRef<'_> {
self.udp.as_bytes(&self.pkt)
}
pub fn udp_dgram(&self) -> pkt::PktSlice {
self.udp.packet(self.udp.len_from(&self.pkt))
}
pub fn udp_dgram_bytes(&self) -> pkt::SliceRef<'_> {
self.udp_dgram().get(&self.pkt)
}
pub fn into_udp_dgram(self) -> Vec<u8> {
let pktslice = self.udp_dgram();
let pkt: Packet = self.into();
pkt.into_vec_from(pktslice)
}
}
impl Default for UdpDgram {
fn default() -> Self {
Self::new()
}
}
impl From<UdpDgram> for Rc<Packet> {
fn from(seg: UdpDgram) -> Self {
Rc::new(seg.pkt)
}
}
impl From<UdpDgram> for Packet {
fn from(seg: UdpDgram) -> Self {
seg.pkt
}
}
impl UdpFlow {
pub fn new(cl: SocketAddrV4, sv: SocketAddrV4, raw: bool) -> Self {
Self { cl, sv, raw }
}
fn clnt(&self) -> UdpDgram {
UdpDgram::of_type(self.raw).src(self.cl).dst(self.sv)
}
fn srvr(&self) -> UdpDgram {
UdpDgram::of_type(self.raw).src(self.sv).dst(self.cl)
}
pub fn client_dgram(&mut self, bytes: &[u8]) -> UdpDgram {
self.clnt().push(bytes)
}
pub fn server_dgram(&mut self, bytes: &[u8]) -> UdpDgram {
self.srvr().push(bytes)
}
}