use crate::Protocol;
use core::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
#[repr(transparent)]
pub struct Packet(pub [u8]);
impl Packet {
#[inline]
pub fn from_slice(b: &[u8]) -> &Packet {
unsafe { &*(b as *const [u8] as *const Packet) }
}
#[inline]
pub fn from_mut(b: &mut [u8]) -> &mut Packet {
unsafe { &mut *(b as *mut [u8] as *mut Packet) }
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
#[inline]
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.0
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn is_valid(&self) -> bool {
match self.version() {
4 => self.0.len() >= 20,
6 => self.0.len() >= 40,
_ => false,
}
}
#[inline]
pub fn version(&self) -> u8 {
if self.0.is_empty() {
0
} else {
self.0[0] >> 4
}
}
pub fn ipv4_header_len(&self) -> usize {
if self.0.is_empty() {
0
} else {
(self.0[0] & 0x0F) as usize * 4
}
}
pub fn ipv4_total_len(&self) -> u16 {
if self.0.len() < 4 {
0
} else {
u16::from_be_bytes([self.0[2], self.0[3]])
}
}
pub fn ipv4_ttl(&self) -> u8 {
if self.0.len() < 9 {
0
} else {
self.0[8]
}
}
pub fn ipv4_protocol(&self) -> Protocol {
if self.0.len() < 10 {
Protocol(0)
} else {
Protocol(self.0[9])
}
}
pub fn ipv4_src_addr(&self) -> Option<Ipv4Addr> {
if self.0.len() < 16 {
None
} else {
let mut b = [0u8; 4];
b.copy_from_slice(&self.0[12..16]);
Some(Ipv4Addr::from(b))
}
}
pub fn ipv4_dst_addr(&self) -> Option<Ipv4Addr> {
if self.0.len() < 20 {
None
} else {
let mut b = [0u8; 4];
b.copy_from_slice(&self.0[16..20]);
Some(Ipv4Addr::from(b))
}
}
pub fn ipv4_payload(&self) -> &[u8] {
let hl = self.ipv4_header_len();
let tl = self.ipv4_total_len() as usize;
if hl == 0 || tl < hl || self.0.len() < tl {
return &[];
}
&self.0[hl..tl]
}
pub fn set_ipv4_src_addr(&mut self, addr: Ipv4Addr) {
if self.0.len() < 16 {
return;
}
self.0[12..16].copy_from_slice(&addr.octets());
}
pub fn set_ipv4_dst_addr(&mut self, addr: Ipv4Addr) {
if self.0.len() < 20 {
return;
}
self.0[16..20].copy_from_slice(&addr.octets());
}
pub fn ipv6_payload_len(&self) -> u16 {
if self.0.len() < 6 {
0
} else {
u16::from_be_bytes([self.0[4], self.0[5]])
}
}
pub fn ipv6_next_header(&self) -> Protocol {
if self.0.len() < 7 {
Protocol(0)
} else {
Protocol(self.0[6])
}
}
pub fn ipv6_hop_limit(&self) -> u8 {
if self.0.len() < 8 {
0
} else {
self.0[7]
}
}
pub fn ipv6_src_addr(&self) -> Option<Ipv6Addr> {
if self.0.len() < 24 {
None
} else {
let mut b = [0u8; 16];
b.copy_from_slice(&self.0[8..24]);
Some(Ipv6Addr::from(b))
}
}
pub fn ipv6_dst_addr(&self) -> Option<Ipv6Addr> {
if self.0.len() < 40 {
None
} else {
let mut b = [0u8; 16];
b.copy_from_slice(&self.0[24..40]);
Some(Ipv6Addr::from(b))
}
}
pub fn ipv6_payload(&self) -> &[u8] {
if self.0.len() < 40 {
return &[];
}
let pl = self.ipv6_payload_len() as usize;
let end = 40 + pl;
if end > self.0.len() {
return &[];
}
&self.0[40..end]
}
pub fn set_ipv6_src_addr(&mut self, addr: Ipv6Addr) {
if self.0.len() < 24 {
return;
}
self.0[8..24].copy_from_slice(&addr.octets());
}
pub fn set_ipv6_dst_addr(&mut self, addr: Ipv6Addr) {
if self.0.len() < 40 {
return;
}
self.0[24..40].copy_from_slice(&addr.octets());
}
pub fn src_addr(&self) -> Option<IpAddr> {
match self.version() {
4 => self.ipv4_src_addr().map(IpAddr::V4),
6 => self.ipv6_src_addr().map(IpAddr::V6),
_ => None,
}
}
pub fn dst_addr(&self) -> Option<IpAddr> {
match self.version() {
4 => self.ipv4_dst_addr().map(IpAddr::V4),
6 => self.ipv6_dst_addr().map(IpAddr::V6),
_ => None,
}
}
pub fn ip_protocol(&self) -> Protocol {
match self.version() {
4 => self.ipv4_protocol(),
6 => self.ipv6_next_header(),
_ => Protocol(0),
}
}
pub fn payload(&self) -> &[u8] {
match self.version() {
4 => self.ipv4_payload(),
6 => self.ipv6_payload(),
_ => &[],
}
}
pub fn is_broadcast(&self) -> bool {
if self.version() != 4 || self.0.len() < 20 {
return false;
}
self.0[16..20] == [0xff; 4]
}
pub fn is_multicast(&self) -> bool {
match self.version() {
4 => self.0.len() >= 20 && self.0[16] & 0xF0 == 0xE0,
6 => self.0.len() >= 40 && self.0[24] == 0xFF,
_ => false,
}
}
}
impl fmt::Debug for Packet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Packet")
.field("len", &self.len())
.field("version", &self.version())
.field("src", &self.src_addr())
.field("dst", &self.dst_addr())
.field("proto", &self.ip_protocol())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn v4_min() -> Vec<u8> {
let mut p = vec![0u8; 20];
p[0] = 0x45; p[2..4].copy_from_slice(&20u16.to_be_bytes());
p[8] = 64; p[9] = Protocol::TCP.0;
p[12..16].copy_from_slice(&[10, 0, 0, 1]);
p[16..20].copy_from_slice(&[10, 0, 0, 2]);
p
}
fn v6_min() -> Vec<u8> {
let mut p = vec![0u8; 40];
p[0] = 0x60; p[4..6].copy_from_slice(&0u16.to_be_bytes());
p[6] = Protocol::UDP.0;
p[7] = 64;
p
}
#[test]
fn ipv4_accessors() {
let buf = v4_min();
let p = Packet::from_slice(&buf);
assert!(p.is_valid());
assert_eq!(p.version(), 4);
assert_eq!(p.ipv4_header_len(), 20);
assert_eq!(p.ipv4_total_len(), 20);
assert_eq!(p.ipv4_ttl(), 64);
assert_eq!(p.ipv4_protocol(), Protocol::TCP);
assert_eq!(p.ipv4_src_addr(), Some(Ipv4Addr::new(10, 0, 0, 1)));
assert_eq!(p.ipv4_dst_addr(), Some(Ipv4Addr::new(10, 0, 0, 2)));
assert_eq!(p.src_addr(), Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))));
assert_eq!(p.ip_protocol(), Protocol::TCP);
assert!(!p.is_broadcast());
assert!(!p.is_multicast());
}
#[test]
fn ipv6_accessors() {
let buf = v6_min();
let p = Packet::from_slice(&buf);
assert!(p.is_valid());
assert_eq!(p.version(), 6);
assert_eq!(p.ipv6_payload_len(), 0);
assert_eq!(p.ipv6_next_header(), Protocol::UDP);
assert_eq!(p.ipv6_hop_limit(), 64);
assert_eq!(p.ip_protocol(), Protocol::UDP);
assert_eq!(p.payload(), &[] as &[u8]);
}
#[test]
fn broadcast_and_multicast() {
let mut buf = v4_min();
buf[16..20].copy_from_slice(&[0xff; 4]);
assert!(Packet::from_slice(&buf).is_broadcast());
buf[16..20].copy_from_slice(&[224, 0, 0, 1]);
assert!(Packet::from_slice(&buf).is_multicast());
let mut buf6 = v6_min();
buf6[24] = 0xff;
assert!(Packet::from_slice(&buf6).is_multicast());
}
#[test]
fn unknown_version() {
let buf = vec![0u8; 5];
let p = Packet::from_slice(&buf);
assert!(!p.is_valid());
assert_eq!(p.version(), 0);
assert_eq!(p.payload(), &[] as &[u8]);
}
#[test]
fn setters_roundtrip() {
let mut buf = v4_min();
let p = Packet::from_mut(&mut buf);
p.set_ipv4_src_addr(Ipv4Addr::new(192, 168, 1, 2));
p.set_ipv4_dst_addr(Ipv4Addr::new(192, 168, 1, 3));
assert_eq!(p.ipv4_src_addr(), Some(Ipv4Addr::new(192, 168, 1, 2)));
assert_eq!(p.ipv4_dst_addr(), Some(Ipv4Addr::new(192, 168, 1, 3)));
}
}