Initial commit.
This commit is contained in:
+192
@@ -0,0 +1,192 @@
|
||||
use eui48::MacAddress;
|
||||
|
||||
// Raw BOOTP Packet.
|
||||
// Total size: 546 bytes
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
pub struct RawBOOTPPacket {
|
||||
// RFC951
|
||||
op: u8, // OpCode
|
||||
htype: u8, // Host Type
|
||||
hlen: u8, // Host Length
|
||||
hops: u8, // Hops
|
||||
xid: u32, // Transaction ID
|
||||
secs: u16, // Seconds since boot
|
||||
flags: u16, // Unused in BOOTP, flags in DHCP
|
||||
ciaddr: u32, // Client Address (provided by client)
|
||||
yiaddr: u32, // Client Address (provided by server)
|
||||
siaddr: u32, // Server Address
|
||||
giaddr: u32, // Gateway Address
|
||||
chaddr: [u8; 16], // Client Hardware (MAC) Address
|
||||
sname: [u8; 64], // Server hostname, null terminated string
|
||||
file: [u8; 128], // Boot file name, null terminated string
|
||||
vend: [u8; 312], // Vendor specific data. In RFC2131, this is required to be 312 octets for DHCP.
|
||||
}
|
||||
|
||||
impl RawBOOTPPacket {
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
unsafe { std::slice::from_raw_parts(self as *const Self as *const u8, 546) }
|
||||
}
|
||||
}
|
||||
|
||||
pub enum OpCode {
|
||||
BOOTREQUEST,
|
||||
BOOTREPLY,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for OpCode {
|
||||
type Error = ();
|
||||
fn try_from(item: u8) -> Result<Self, Self::Error> {
|
||||
match item {
|
||||
1 => Ok(Self::BOOTREQUEST),
|
||||
2 => Ok(Self::BOOTREPLY),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<OpCode> for u8 {
|
||||
type Error = ();
|
||||
fn try_from(item: OpCode) -> Result<Self, Self::Error> {
|
||||
match item {
|
||||
OpCode::BOOTREQUEST => Ok(1),
|
||||
OpCode::BOOTREPLY => Ok(2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Higher level BOOTP Packet representation
|
||||
pub struct BOOTPPacket {
|
||||
pub op: OpCode,
|
||||
pub hops: u8,
|
||||
pub xid: u32,
|
||||
pub secs: u16,
|
||||
pub flags: u16,
|
||||
pub ciaddr: Option<std::net::Ipv4Addr>,
|
||||
pub yiaddr: Option<std::net::Ipv4Addr>,
|
||||
pub siaddr: Option<std::net::Ipv4Addr>,
|
||||
pub giaddr: Option<std::net::Ipv4Addr>,
|
||||
pub chaddr: MacAddress,
|
||||
pub sname: [u8; 64],
|
||||
pub file: [u8; 128],
|
||||
vend: [u8; 312],
|
||||
}
|
||||
|
||||
impl From<BOOTPPacket> for RawBOOTPPacket {
|
||||
fn from(item: BOOTPPacket) -> Self {
|
||||
let mut chaddr: [u8; 16] = [0; 16];
|
||||
chaddr[..6].copy_from_slice(item.chaddr.as_bytes());
|
||||
Self {
|
||||
op: item.op.try_into().unwrap(),
|
||||
htype: 1,
|
||||
hlen: 6,
|
||||
hops: item.hops,
|
||||
xid: item.xid,
|
||||
secs: item.secs,
|
||||
flags: item.flags,
|
||||
ciaddr: if item.ciaddr.is_some() {
|
||||
item.ciaddr.unwrap().into()
|
||||
} else {
|
||||
0
|
||||
},
|
||||
yiaddr: if item.yiaddr.is_some() {
|
||||
item.yiaddr.unwrap().into()
|
||||
} else {
|
||||
0
|
||||
},
|
||||
siaddr: if item.siaddr.is_some() {
|
||||
item.siaddr.unwrap().into()
|
||||
} else {
|
||||
0
|
||||
},
|
||||
giaddr: if item.giaddr.is_some() {
|
||||
item.giaddr.unwrap().into()
|
||||
} else {
|
||||
0
|
||||
},
|
||||
chaddr: chaddr,
|
||||
sname: item.sname,
|
||||
file: item.file,
|
||||
vend: item.vend,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RawBOOTPPacket> for BOOTPPacket {
|
||||
fn from(item: RawBOOTPPacket) -> Self {
|
||||
Self {
|
||||
op: item.op.try_into().unwrap(),
|
||||
hops: item.hops,
|
||||
xid: item.xid,
|
||||
secs: item.secs,
|
||||
flags: item.flags,
|
||||
ciaddr: if item.ciaddr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(std::net::Ipv4Addr::from(item.ciaddr))
|
||||
},
|
||||
yiaddr: if item.yiaddr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(std::net::Ipv4Addr::from(item.yiaddr))
|
||||
},
|
||||
siaddr: if item.siaddr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(std::net::Ipv4Addr::from(item.siaddr))
|
||||
},
|
||||
giaddr: if item.giaddr == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(std::net::Ipv4Addr::from(item.giaddr))
|
||||
},
|
||||
chaddr: MacAddress::from_bytes(&item.chaddr[0..6]).unwrap(),
|
||||
sname: item.sname,
|
||||
file: item.file,
|
||||
vend: item.vend,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BOOTPPacket {
|
||||
/// Create a new BOOTP packet.
|
||||
///
|
||||
/// dhcprs encourages the use of higher level interfaces which are then
|
||||
/// converted to lower level (packed) structures that can be serialised
|
||||
/// in to bytes and then sent down a socket.
|
||||
pub fn new(
|
||||
op: OpCode,
|
||||
hops: u8,
|
||||
xid: u32,
|
||||
secs: u16,
|
||||
flags: u16,
|
||||
ciaddr: Option<std::net::Ipv4Addr>,
|
||||
yiaddr: Option<std::net::Ipv4Addr>,
|
||||
siaddr: Option<std::net::Ipv4Addr>,
|
||||
giaddr: Option<std::net::Ipv4Addr>,
|
||||
chaddr: MacAddress,
|
||||
sname: [u8; 64],
|
||||
file: [u8; 128],
|
||||
vend: [u8; 312],
|
||||
) -> Self {
|
||||
Self {
|
||||
op: op,
|
||||
hops: hops,
|
||||
xid: xid,
|
||||
secs: secs,
|
||||
flags: flags,
|
||||
ciaddr: ciaddr,
|
||||
yiaddr: yiaddr,
|
||||
siaddr: siaddr,
|
||||
giaddr: giaddr,
|
||||
chaddr: chaddr,
|
||||
sname: sname,
|
||||
file: file,
|
||||
vend: vend,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_vend(&self) -> &[u8] {
|
||||
return &self.vend;
|
||||
}
|
||||
}
|
||||
+1809
File diff suppressed because it is too large
Load Diff
+15
@@ -0,0 +1,15 @@
|
||||
//! DHCP/BOOTP packet encode/decode library.
|
||||
//!
|
||||
//! The purpose of this library is to provide an easy to use interface over
|
||||
//! DHCP, BOOTP and UDP packets to enable the creation of programs that
|
||||
//! make use of DHCP.
|
||||
//!
|
||||
//! BOOTP specific functionality is provided by the `dhcprs::bootp` module
|
||||
//! DHCP specific functionality is provided by the `dhcprs::dhcp` module
|
||||
//!
|
||||
//! There is additionally simple UDP packet encode/decode provided by the
|
||||
//! `dhcprs::udpbuilder`.
|
||||
|
||||
pub mod bootp;
|
||||
pub mod dhcp;
|
||||
pub mod udpbuilder;
|
||||
@@ -0,0 +1,175 @@
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
pub struct RawUDPPacket {
|
||||
// IP Header
|
||||
version_ihl: u8, // First 4 bits: Version (always 4), next 4 bits: Internet Header Length (Header size in u32s)
|
||||
dscp_ecn: u8, // Differentiated Services Code Point / Explicit Congestion Notification, zero for our purposes
|
||||
total_length: u16, // Total length of the IP header + UDP header + data
|
||||
identification: u16, // Identification (for fragmentation purposes)
|
||||
flags_fragment_offset: u16, // Flags and the fragment offset (0x4000 for DF)
|
||||
ttl: u8, // Time to live
|
||||
protocol: u8, // Proto (UDP is 0x11 / dec 17)
|
||||
ip_checksum: u16, // Ones' complement of the ones' complement sum of all 16 bit words of the header
|
||||
source_addr: u32, // Source address
|
||||
dest_addr: u32, // Destination address
|
||||
|
||||
// UDP Header
|
||||
source_port: u16, // Source port
|
||||
dest_port: u16, // Destination port
|
||||
length: u16, // Length of UDP Header + data
|
||||
udp_checksum: u16, // Ones' complement of the ones' complement sum of all 16 bit words of the pseudoheader + udp header + data
|
||||
|
||||
// UDP Data
|
||||
data: [u8; 546], // 546 byte array to store a RawBOOTPPacket
|
||||
}
|
||||
|
||||
impl RawUDPPacket {
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
unsafe { std::slice::from_raw_parts(self as *const Self as *const u8, 574) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UDPPacket> for RawUDPPacket {
|
||||
fn from(item: UDPPacket) -> Self {
|
||||
let ph: PseudoHeader = item.clone().into();
|
||||
let udp_checksum = ph.checksum();
|
||||
|
||||
let source_addr: u32 = item.source_addr.into();
|
||||
let dest_addr: u32 = item.dest_addr.into();
|
||||
|
||||
let mut packet = Self {
|
||||
version_ihl: 0x45, // Version 4, IHL of 5 (20 bytes)
|
||||
dscp_ecn: 0, // No need for either DSCP or ECN
|
||||
total_length: 574_u16.to_be(), // Will always be 576 bytes
|
||||
identification: 0x1337, // No need (no fragmentation)
|
||||
flags_fragment_offset: 0,
|
||||
ttl: 64, // Set TTL to 255 because why not
|
||||
protocol: 17, // UDP
|
||||
ip_checksum: 0, // Zero for now, will be set later
|
||||
source_addr: source_addr.to_be(),
|
||||
dest_addr: dest_addr.to_be(),
|
||||
source_port: item.source_port.to_be(),
|
||||
dest_port: item.dest_port.to_be(),
|
||||
length: ((item.data.len() + 8) as u16).to_be(),
|
||||
udp_checksum: udp_checksum,
|
||||
data: item.data,
|
||||
};
|
||||
|
||||
let header_u16 =
|
||||
unsafe { std::slice::from_raw_parts(&packet as *const Self as *const u16, 10) };
|
||||
let mut total: u16 = 0;
|
||||
for &word in header_u16 {
|
||||
let (value, carry) = total.overflowing_add(word);
|
||||
if carry {
|
||||
total = value + 1;
|
||||
} else {
|
||||
total = value;
|
||||
}
|
||||
}
|
||||
|
||||
packet.ip_checksum = !total;
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
struct PseudoHeader {
|
||||
source_addr: u32,
|
||||
dest_addr: u32,
|
||||
zero: u8,
|
||||
protocol: u8,
|
||||
udp_length: u16,
|
||||
source_port: u16,
|
||||
dest_port: u16,
|
||||
udp_length2: u16,
|
||||
udp_checksum: u16,
|
||||
data: [u8; 546],
|
||||
}
|
||||
|
||||
impl From<UDPPacket> for PseudoHeader {
|
||||
fn from(item: UDPPacket) -> Self {
|
||||
let source_addr: u32 = item.source_addr.into();
|
||||
let dest_addr: u32 = item.dest_addr.into();
|
||||
Self {
|
||||
source_addr: source_addr.to_be(),
|
||||
dest_addr: dest_addr.to_be(),
|
||||
zero: 0,
|
||||
protocol: 17,
|
||||
udp_length: ((item.data.len() + 8) as u16).to_be(),
|
||||
source_port: item.source_port.to_be(),
|
||||
dest_port: item.dest_port.to_be(),
|
||||
udp_length2: ((item.data.len() + 8) as u16).to_be(),
|
||||
udp_checksum: 0,
|
||||
data: item.data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PseudoHeader {
|
||||
pub fn checksum(&self) -> u16 {
|
||||
let as_u16 = unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
self as *const Self as *const u16,
|
||||
(self.data.len() + 20) / 2,
|
||||
)
|
||||
};
|
||||
let mut total: u16 = 0;
|
||||
for &word in as_u16 {
|
||||
let (value, carry) = total.overflowing_add(word);
|
||||
if carry {
|
||||
total = value + 1;
|
||||
} else {
|
||||
total = value;
|
||||
}
|
||||
}
|
||||
!total
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UDPPacket {
|
||||
pub source_addr: std::net::Ipv4Addr,
|
||||
pub dest_addr: std::net::Ipv4Addr,
|
||||
pub source_port: u16,
|
||||
pub dest_port: u16,
|
||||
data: [u8; 546],
|
||||
}
|
||||
|
||||
impl From<RawUDPPacket> for UDPPacket {
|
||||
fn from(item: RawUDPPacket) -> Self {
|
||||
Self {
|
||||
source_addr: std::net::Ipv4Addr::from(item.source_addr.to_be_bytes()),
|
||||
dest_addr: std::net::Ipv4Addr::from(item.source_addr.to_be_bytes()),
|
||||
source_port: u16::from_be(item.source_port),
|
||||
dest_port: u16::from_be(item.dest_port),
|
||||
data: item.data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UDPPacket {
|
||||
/// Creates a new UDPPacket
|
||||
///
|
||||
/// Should be converted to a `dhcprs::udpbuilder::RawUDPPacket` to send.
|
||||
pub fn new(
|
||||
source_addr: std::net::Ipv4Addr,
|
||||
dest_addr: std::net::Ipv4Addr,
|
||||
source_port: u16,
|
||||
dest_port: u16,
|
||||
data: [u8; 546],
|
||||
) -> Self {
|
||||
Self {
|
||||
source_addr: source_addr,
|
||||
dest_addr: dest_addr,
|
||||
source_port: source_port,
|
||||
dest_port: dest_port,
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the payload of the UDP packet.
|
||||
pub fn get_data(&self) -> &[u8] {
|
||||
return &self.data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user