use std::fmt;
use std::net;
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Cursor, Write};
type ByteOrder = BigEndian;
#[cfg(target_os = "macos")]
static LOOPBACK_SIZE: usize = 4;
#[cfg(target_os = "macos")]
static LOOPBACK: [u8; 4] = [0, 0, 0, 2];
#[cfg(not(target_os = "macos"))]
static LOOPBACK_SIZE: usize = 0;
#[cfg(not(target_os = "macos"))]
static LOOPBACK: [u8; 0] = [];
pub struct Packet {
inner: Vec<u8>,
}
impl Packet {
pub fn new(buf: &[u8]) -> Packet {
let mut inner = vec![0; buf.len() - LOOPBACK_SIZE];
inner.copy_from_slice(&buf[LOOPBACK_SIZE..]);
return Packet { inner };
}
pub fn out(&self) -> Vec<u8> {
let mut inner = vec![0; self.inner.len() + LOOPBACK_SIZE];
inner[..LOOPBACK_SIZE].copy_from_slice(&LOOPBACK[..]);
inner[LOOPBACK_SIZE..].copy_from_slice(self.as_ref());
inner
}
pub fn source_addr(&self) -> net::IpAddr {
return self.read_ip(&self.inner[12..16]);
}
pub fn write_source_addr(&mut self, addr: net::IpAddr) {
write_ip(&mut self.inner[12..16], addr);
}
pub fn dest_addr(&self) -> net::IpAddr {
return self.read_ip(&self.inner[16..20]);
}
pub fn write_dest_addr(&mut self, addr: net::IpAddr) {
write_ip(&mut self.inner[16..20], addr);
}
pub fn total_length(&self) -> usize {
self.read_u16(2) as _
}
pub fn version(&self) -> u8 {
self.read_bits(0, 4)
}
pub fn ihl(&self) -> u8 {
self.read_bits(4, 4)
}
pub fn type_of_service(&self) -> u8 {
return self.read_u8(1);
}
pub fn identification(&self) -> u16 {
return self.read_u16(4);
}
pub fn flags(&self) -> u8 {
return self.read_bits(48, 3);
}
pub fn ttl(&self) -> u8 {
return self.read_u8(8);
}
pub fn protocol(&self) -> u8 {
return self.read_u8(9);
}
pub fn checksum(&self) -> u16 {
self.read_u16(10)
}
pub fn write_checksum(&mut self, checksum: u16) {
Cursor::new(&mut self.inner[10..])
.write_u16::<ByteOrder>(checksum)
.unwrap();
}
pub fn cal_checksum(&self) -> u16 {
let mut result: u32 = 0xffff;
let mut cursor = Cursor::new(&self.inner[..]);
let end = self.ihl() as u64 * 4;
while cursor.position() < end {
let val = cursor.read_u16::<ByteOrder>().unwrap();
if cursor.position() == 12 {
continue;
}
result += val as u32;
if result > 0xffff {
result -= 0xffff;
}
}
return !result as u16;
}
pub fn payload(&self) -> &[u8] {
&self.inner[self.ihl() as usize * 4..]
}
pub fn payload_mut(&mut self) -> &mut [u8] {
let start = self.ihl();
&mut self.inner[start as usize * 4..]
}
}
impl Packet {
fn cursor(&self, off: usize) -> Cursor<&[u8]> {
Cursor::new(&self.inner[off..])
}
fn read_u16(&self, off: usize) -> u16 {
self.cursor(off).read_u16::<ByteOrder>().unwrap() as _
}
fn read_u8(&self, off: usize) -> u8 {
self.cursor(off).read_u8().unwrap()
}
fn read_bits(&self, bit_start: usize, bit_size: u8) -> u8 {
assert!((bit_start % 8) + bit_size as usize <= 8);
let val = self.cursor(bit_start / 8).read_u8().unwrap();
let mask: u8 = ((2 ^ bit_size) - 1) as _;
let bit_off: u8 = (bit_start % 8) as _;
let shift = 8 - bit_size - bit_off;
(val & (mask << shift)) >> shift
}
fn read_ip(&self, buf: &[u8]) -> net::IpAddr {
let ip = Cursor::new(buf).read_u32::<ByteOrder>().unwrap();
net::IpAddr::from(net::Ipv4Addr::from(ip))
}
}
fn write_ip(dest: &mut [u8], addr: net::IpAddr) {
match addr {
net::IpAddr::V4(addr) => Cursor::new(dest)
.write_u32::<ByteOrder>(addr.into())
.unwrap(),
_ => panic!("unsupport ipv6"),
}
}
impl AsRef<[u8]> for Packet {
fn as_ref(&self) -> &[u8] {
self.inner.as_ref()
}
}
impl fmt::Debug for Packet {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
f,
"{}->{}, {}",
self.source_addr(),
self.dest_addr(),
self.inner.len()
)
}
}
#[cfg(test)]
mod test {
use crate::*;
use std::net::IpAddr;
#[test]
fn test() {
let data = &[
0, 0, 0, 2, 69, 0, 0, 84, 155, 56, 0, 0, 64, 1, 72, 29, 192, 168, 11, 3, 192, 168, 11, 0,
162, 232, 154, 232, 87, 72, 0, 0, 94, 235, 247, 137, 0, 10, 196, 76, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
];
let mut pkt = Packet::new(data);
assert_eq!(pkt.protocol(), 1);
assert_eq!(pkt.source_addr(), "192.168.11.3".parse::<IpAddr>().unwrap());
assert_eq!(pkt.dest_addr(), "192.168.11.0".parse::<IpAddr>().unwrap());
assert_eq!(pkt.ihl(), 5);
let mut icmp = protocol::icmp::from(pkt.payload_mut());
let echo = icmp.echo();
assert_eq!(echo.typ(), 0);
assert_eq!(echo.checksum(), echo.cal_checksum());
}
#[test]
fn ping() {
let data = &[
0, 0, 0, 2, 69, 0, 0, 84, 114, 154, 0, 0, 64, 1, 112, 187, 192, 168, 11, 0, 192, 168, 11, 3,
8, 0, 14, 229, 245, 73, 0, 0, 94, 235, 248, 49, 0, 0, 177, 176, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
];
let mut pkt = Packet::new(data);
assert_eq!(pkt.protocol(), 1);
assert_eq!(pkt.dest_addr(), "192.168.11.3".parse::<IpAddr>().unwrap());
assert_eq!(pkt.source_addr(), "192.168.11.0".parse::<IpAddr>().unwrap());
assert_eq!(pkt.ihl(), 5);
let mut icmp = protocol::icmp::from(pkt.payload_mut());
let echo = icmp.echo();
assert_eq!(echo.typ(), 8);
assert_eq!(echo.checksum(), echo.cal_checksum());
{
assert_eq!(echo.data().len(), 56);
let data = &[];
assert_eq!(echo.data(), data);
}
}
}