8000 dhcpd: send boot request · Andy-Python-Programmer/dhcpd@d888f9d · GitHub
[go: up one dir, main page]

Skip to content

Commit d888f9d

Browse files
dhcpd: send boot request
Signed-off-by: Andy-Python-Programmer <andypythonappdeveloper@gmail.com>
0 parents  commit d888f9d

File tree

5 files changed

+236
-0
lines changed

5 files changed

+236
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"editor.formatOnSave": true
3+
}

Cargo.lock

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "dhcpd"
3+
version = "0.1.0"
4+
edition = "2021"
5+
authors = ["Anhad Singh <andypython@protonmail.com>"]
6+
7+
[dependencies]
8+
simple_endian = "*"
9+
byteorder = { version = "*", default-features = false }

src/main.rs

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Dynamic Host Configuration Protocol
2+
3+
use std::{error::Error, net::UdpSocket};
4+
5+
use byteorder::{ByteOrder, NetworkEndian};
6+
use simple_endian::BigEndian;
7+
8+
/// Size of IPv4 adderess in octets.
9+
///
10+
/// [RFC 8200 § 2]: https://www.rfc-editor.org/rfc/rfc791#section-3.2
11+
pub const ADDR_SIZE: usize = 4;
12+
13+
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
14+
pub struct Ipv4Addr(pub [u8; ADDR_SIZE]);
15+
16+
impl Ipv4Addr {
17+
const EMPTY: Self = Self([0; ADDR_SIZE]);
18+
}
19+
20+
// FIXME: The MAC address is usually obtained by using getifaddrs() which currently
21+
// is unimplemented in mlibc.
22+
const MAC_ADDRESS: &[u8] = &[52, 54, 0, 12, 34, 56];
23+
const DHCP_XID: u32 = 0x43424140;
24+
25+
#[repr(u8)]
26+
enum DhcpType {
27+
BootRequest = 1u8.swap_bytes(),
28+
// BootReply = 2u8.swap_bytes(),
29+
}
30+
31+
#[repr(u8)]
32+
enum HType {
33+
Ethernet = 1u8.swap_bytes(),
34+
}
35+
36+
#[repr(C, packed)]
37+
struct Header {
38+
op: DhcpType,
39+
htype: HType,
40+
hlen: BigEndian<u8>,
41+
hops: BigEndian<u8>,
42+
xid: BigEndian<u32>,
43+
seconds: BigEndian<u16>,
44+
flags: BigEndian<u16>,
45+
client_ip: Ipv4Addr,
46+
your_ip: Ipv4Addr,
47+
server_ip: Ipv4Addr,
48+
gateway_ip: Ipv4Addr,
49+
client_hw_addr: [u8; 16],
50+
server_name: [u8; 64],
51+
file: [u8; 128],
52+
options: [u8; 64],
53+
}
54+
55+
impl Header {
56+
fn options_mut(&mut self) -> OptionsWriter<'_> {
57+
OptionsWriter::new(&mut self.options)
58+
}
59+
}
60+
61+
#[repr(u8)]
62+
enum MessageType {
63+
Discover = 1u8.swap_bytes(),
64+
}
65+
66+
#[repr(u8)]
67+
enum DhcpOption {
68+
HostName = 12,
69+
MessageType = 53,
70+
ParameterRequestList = 55,
71+
ClientIdentifier = 61,
72+
End = 255,
73+
}
74+
75+
struct OptionsWriter<'a>(&'a mut [u8]);
76+
77+
impl<'a> OptionsWriter<'a> {
78+
fn new(options: &'a mut [u8]) -> Self {
79+
options.fill(0);
80+
Self(options).set_magic_cookie()
81+
}
82+
83+
fn insert(&mut self, kind: DhcpOption, data: &'_ [u8]) {
84+
let total_len = 2 + data.len();
85+
86+
assert!(data.len() < u8::MAX as _);
87+
assert!(self.0.len() > total_len);
88+
89+
let (buf, rest) = core::mem::take(&mut self.0).split_at_mut(total_len);
90+
self.0 = rest;
91+
92+
buf[0] = kind as u8;
93+
buf[1] = data.len() as _;
94+
buf[2..].copy_from_slice(data);
95+
}
96+
97+
fn insert_padding(&mut self, size: usize) {
98+
let (buf, rest) = core::mem::take(&mut self.0).split_at_mut(size);
99+
self.0 = rest;
100+
101+
buf.fill(0);
102+
}
103+
104+
fn set_magic_cookie(mut self) -> Self {
105+
let (buf, rest) = core::mem::take(&mut self.0).split_at_mut(core::mem::size_of::<u32>());
106+
107+
// The first four octets of the 'options' field of the DHCP message
108+
// contain the (decimal) values 99, 130, 83 and 99, respectively.
109+
//
110+
// CC: (https://www.rfc-editor.org/rfc/rfc2131#section-3)
111+
NetworkEndian::write_u32(buf, 0x63825363);
112+
self.0 = rest;
113+
self
114+
}
115+
116+
fn set_message_type(mut self, typ: MessageType) -> Self {
117+
self.insert(DhcpOption::MessageType, &[typ as u8]);
118+
self
119+
}
120+
121+
fn set_parameter_request_list(mut self) -> Self {
122+
// TODO: Take all of the request flags as an argument.
123+
self.insert(
124+
DhcpOption::ParameterRequestList,
125+
&[
126+
1, // Subnet Mask
127+
3, // Router
128+
15, // Domain Name
129+
6, // Domain Server
130+
],
131+
);
132+
self
133+
}
134+
135+
fn set_client_identifier(mut self) -> Self {
136+
let mut data = [0; 7];
137+
data[0] = HType::Ethernet as u8;
138+
data[1..].copy_from_slice(MAC_ADDRESS);
139+
140+
self.insert(DhcpOption::ClientIdentifier, data.as_slice());
141+
self
142+
}
143+
144+
fn set_host_name(mut self, name: &str) -> Self {
145+
self.insert(DhcpOption::HostName, name.as_bytes());
146+
self.insert_padding(1); // null-terminator
147+
self
148+
}
149+
}
150+
151+
impl<'a> Drop for OptionsWriter<'a> {
152+
fn drop(&mut self) {
153+
self.insert(DhcpOption::End, &[]);
154+
}
155+
}
156+
157+
pub fn main() -> Result<(), Box<dyn Error>> {
158+
let socket = UdpSocket::bind(("0.0.0.0", 68))?;
159+
socket.connect(("255.255.255.255", 67))?;
160+
161+
let mut client_hw_addr = [0; 16];
162+
client_hw_addr[0..6].copy_from_slice(MAC_ADDRESS);
163+
164+
let mut header = Header {
165+
htype: HType::Ethernet,
166+
hlen: BigEndian::<u8>::from(6),
167+
hops: BigEndian::<u8>::from(0),
168+
xid: BigEndian::<u32>::from(DHCP_XID),
169+
seconds: BigEndian::<u16>::from(0),
170+
client_hw_addr,
171+
server_name: [0; 64],
172+
file: [0; 128],
173+
options: [0; 64],
174+
175+
// request info:
176+
op: DhcpType::BootRequest,
177+
flags: BigEndian::from(0x8000), // broadcast
178+
client_ip: Ipv4Addr::EMPTY,
179+
your_ip: Ipv4Addr::EMPTY,
180+
server_ip: Ipv4Addr::EMPTY,
181+
gateway_ip: Ipv4Addr::EMPTY,
182+
};
183+
184+
let _ = header
185+
.options_mut()
186+
.set_message_type(MessageType::Discover)
187+
.set_client_identifier()
188+
.set_host_name("Aero")
189+
.set_parameter_request_list();
190+
191+
let header_bytes = unsafe {
192+
core::slice::from_raw_parts(
193+
(&header as *const Header) as *const u8,
194+
std::mem::size_of::<Header>(),
195+
)
196+
};
197+
198+
socket.send(&header_bytes)?;
199+
Ok(())
200+
}

0 commit comments

Comments
 (0)
0