Initial commit.

This commit is contained in:
2022-09-23 13:58:17 +01:00
commit 0aa92d8021
9 changed files with 2351 additions and 0 deletions
+192
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -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;
+175
View File
@@ -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;
}
}