aboutsummaryrefslogtreecommitdiff
path: root/src/dhcp.rs
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2025-10-11 11:34:59 +0100
committerdiogo464 <[email protected]>2025-10-11 11:34:59 +0100
commit521218ce06fbb7bd518eb6a069406936079e3ec2 (patch)
tree862e84ec23175119abb7652e197a4113e7fcc31b /src/dhcp.rs
parent89db26c86bf48a4c527778fc254765a38b7e9085 (diff)
initial working version
Diffstat (limited to 'src/dhcp.rs')
-rw-r--r--src/dhcp.rs398
1 files changed, 369 insertions, 29 deletions
diff --git a/src/dhcp.rs b/src/dhcp.rs
index 38cc8e4..51680d1 100644
--- a/src/dhcp.rs
+++ b/src/dhcp.rs
@@ -1,6 +1,7 @@
1use std::{ 1use std::{
2 io::{Cursor, Read as _, Result, Write}, 2 io::{Cursor, Read as _, Result, Write},
3 net::Ipv4Addr, 3 net::Ipv4Addr,
4 str::FromStr,
4}; 5};
5 6
6use crate::wire; 7use crate::wire;
@@ -8,6 +9,11 @@ use crate::wire;
8const MAGIC_COOKIE: [u8; 4] = [0x63, 0x82, 0x53, 0x63]; 9const MAGIC_COOKIE: [u8; 4] = [0x63, 0x82, 0x53, 0x63];
9const FLAG_BROADCAST: u16 = 1 << 15; 10const FLAG_BROADCAST: u16 = 1 << 15;
10 11
12pub const VENDOR_CLASS_PXE_CLIENT: &'static [u8] = b"PXEClient";
13pub const VENDOR_CLASS_PXE_SERVER: &'static [u8] = b"PXEServer";
14
15pub const USER_CLASS_IPXE: &'static [u8] = b"iPXE";
16
11#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] 17#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum BootOp { 18pub enum BootOp {
13 #[default] 19 #[default]
@@ -29,6 +35,8 @@ impl From<BootOp> for u8 {
29 } 35 }
30} 36}
31 37
38pub type HardwareAddress = [u8; 16];
39
32#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] 40#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
33pub enum HardwareType { 41pub enum HardwareType {
34 #[default] 42 #[default]
@@ -78,10 +86,11 @@ pub enum DhcpOption {
78 End, 86 End,
79 MessageType(DhcpMessageType), 87 MessageType(DhcpMessageType),
80 ServerIdentifier(Ipv4Addr), 88 ServerIdentifier(Ipv4Addr),
81 VendorClassIdentifier(String), 89 VendorClassIdentifier(Vec<u8>),
82 TftpServerName(String), 90 TftpServerName(String),
83 TftpFileName(String), 91 TftpFileName(String),
84 UserClassInformation(String), 92 UserClassInformation(Vec<u8>),
93 ClientSystemArchitecture(SystemArchitecture),
85 ClientMachineIdentifier(Vec<u8>), 94 ClientMachineIdentifier(Vec<u8>),
86 Unknown { code: u8, data: Vec<u8> }, 95 Unknown { code: u8, data: Vec<u8> },
87} 96}
@@ -95,6 +104,7 @@ impl DhcpOption {
95 pub const CODE_TFTP_SERVER_NAME: u8 = 66; 104 pub const CODE_TFTP_SERVER_NAME: u8 = 66;
96 pub const CODE_TFTP_FILE_NAME: u8 = 67; 105 pub const CODE_TFTP_FILE_NAME: u8 = 67;
97 pub const CODE_USER_CLASS_INFORMATION: u8 = 77; 106 pub const CODE_USER_CLASS_INFORMATION: u8 = 77;
107 pub const CODE_CLIENT_SYSTEM_ARCHITECTURE: u8 = 93;
98 pub const CODE_CLIENT_MACHINE_IDENTIFIER: u8 = 97; 108 pub const CODE_CLIENT_MACHINE_IDENTIFIER: u8 = 97;
99 109
100 pub fn code(&self) -> u8 { 110 pub fn code(&self) -> u8 {
@@ -107,12 +117,294 @@ impl DhcpOption {
107 DhcpOption::TftpServerName(_) => Self::CODE_TFTP_SERVER_NAME, 117 DhcpOption::TftpServerName(_) => Self::CODE_TFTP_SERVER_NAME,
108 DhcpOption::TftpFileName(_) => Self::CODE_TFTP_FILE_NAME, 118 DhcpOption::TftpFileName(_) => Self::CODE_TFTP_FILE_NAME,
109 DhcpOption::UserClassInformation(_) => Self::CODE_USER_CLASS_INFORMATION, 119 DhcpOption::UserClassInformation(_) => Self::CODE_USER_CLASS_INFORMATION,
120 DhcpOption::ClientSystemArchitecture(_) => Self::CODE_CLIENT_SYSTEM_ARCHITECTURE,
110 DhcpOption::ClientMachineIdentifier(_) => Self::CODE_CLIENT_MACHINE_IDENTIFIER, 121 DhcpOption::ClientMachineIdentifier(_) => Self::CODE_CLIENT_MACHINE_IDENTIFIER,
111 DhcpOption::Unknown { code, .. } => *code, 122 DhcpOption::Unknown { code, .. } => *code,
112 } 123 }
113 } 124 }
114} 125}
115 126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
128pub enum SystemArchitecture {
129 IntelX86pc,
130 NECPC98,
131 EfiItanium,
132 DecAlpha,
133 ArcX86,
134 IntelLeanClient,
135 EfiIA32,
136 EfiBC,
137 EfiXscale,
138 EfiX86_64,
139 EfiARM32,
140 EfiARM64,
141 EfiARM32Http,
142 EfiARM64Http,
143 ARM32Uboot,
144 ARM64Uboot,
145 Unknown(u16),
146}
147
148impl SystemArchitecture {
149 pub const CODE_INTEL_X86_PC: u16 = 0;
150 pub const CODE_NEC_PC98: u16 = 1;
151 pub const CODE_EFI_ITANIUM: u16 = 2;
152 pub const CODE_DEC_ALPHA: u16 = 3;
153 pub const CODE_ARC_X86: u16 = 4;
154 pub const CODE_INTEL_LEAN_CLIENT: u16 = 5;
155 pub const CODE_EFI_IA32: u16 = 6;
156 pub const CODE_EFI_BC: u16 = 7;
157 pub const CODE_EFI_XSCALE: u16 = 8;
158 pub const CODE_EFI_X86_64: u16 = 9;
159 pub const CODE_EFI_ARM32: u16 = 10;
160 pub const CODE_EFI_ARM64: u16 = 11;
161 pub const CODE_EFI_ARM32_HTTP: u16 = 18;
162 pub const CODE_EFI_ARM64_HTTP: u16 = 19;
163 pub const CODE_ARM32_UBOOT: u16 = 21;
164 pub const CODE_ARM64_UBOOT: u16 = 22;
165}
166
167impl From<u16> for SystemArchitecture {
168 fn from(value: u16) -> Self {
169 match value {
170 Self::CODE_INTEL_X86_PC => SystemArchitecture::IntelX86pc,
171 Self::CODE_NEC_PC98 => SystemArchitecture::NECPC98,
172 Self::CODE_EFI_ITANIUM => SystemArchitecture::EfiItanium,
173 Self::CODE_DEC_ALPHA => SystemArchitecture::DecAlpha,
174 Self::CODE_ARC_X86 => SystemArchitecture::ArcX86,
175 Self::CODE_INTEL_LEAN_CLIENT => SystemArchitecture::IntelLeanClient,
176 Self::CODE_EFI_IA32 => SystemArchitecture::EfiIA32,
177 Self::CODE_EFI_BC => SystemArchitecture::EfiBC,
178 Self::CODE_EFI_XSCALE => SystemArchitecture::EfiXscale,
179 Self::CODE_EFI_X86_64 => SystemArchitecture::EfiX86_64,
180 Self::CODE_EFI_ARM32 => SystemArchitecture::EfiARM32,
181 Self::CODE_EFI_ARM64 => SystemArchitecture::EfiARM64,
182 Self::CODE_EFI_ARM32_HTTP => SystemArchitecture::EfiARM32Http,
183 Self::CODE_EFI_ARM64_HTTP => SystemArchitecture::EfiARM64Http,
184 Self::CODE_ARM32_UBOOT => SystemArchitecture::ARM32Uboot,
185 Self::CODE_ARM64_UBOOT => SystemArchitecture::ARM64Uboot,
186 _ => SystemArchitecture::Unknown(value),
187 }
188 }
189}
190
191impl From<SystemArchitecture> for u16 {
192 fn from(value: SystemArchitecture) -> Self {
193 match value {
194 SystemArchitecture::IntelX86pc => SystemArchitecture::CODE_INTEL_X86_PC,
195 SystemArchitecture::NECPC98 => SystemArchitecture::CODE_NEC_PC98,
196 SystemArchitecture::EfiItanium => SystemArchitecture::CODE_EFI_ITANIUM,
197 SystemArchitecture::DecAlpha => SystemArchitecture::CODE_DEC_ALPHA,
198 SystemArchitecture::ArcX86 => SystemArchitecture::CODE_ARC_X86,
199 SystemArchitecture::IntelLeanClient => SystemArchitecture::CODE_INTEL_LEAN_CLIENT,
200 SystemArchitecture::EfiIA32 => SystemArchitecture::CODE_EFI_IA32,
201 SystemArchitecture::EfiBC => SystemArchitecture::CODE_EFI_BC,
202 SystemArchitecture::EfiXscale => SystemArchitecture::CODE_EFI_XSCALE,
203 SystemArchitecture::EfiX86_64 => SystemArchitecture::CODE_EFI_X86_64,
204 SystemArchitecture::EfiARM32 => SystemArchitecture::CODE_EFI_ARM32,
205 SystemArchitecture::EfiARM64 => SystemArchitecture::CODE_EFI_ARM64,
206 SystemArchitecture::EfiARM32Http => SystemArchitecture::CODE_EFI_ARM32_HTTP,
207 SystemArchitecture::EfiARM64Http => SystemArchitecture::CODE_EFI_ARM64_HTTP,
208 SystemArchitecture::ARM32Uboot => SystemArchitecture::CODE_ARM32_UBOOT,
209 SystemArchitecture::ARM64Uboot => SystemArchitecture::CODE_ARM64_UBOOT,
210 SystemArchitecture::Unknown(code) => code,
211 }
212 }
213}
214
215impl FromStr for SystemArchitecture {
216 type Err = <u16 as FromStr>::Err;
217
218 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
219 s.parse::<u16>().map(From::from)
220 }
221}
222
223#[derive(Debug)]
224pub struct InvalidPxeClassIdentifierKind(String);
225
226impl InvalidPxeClassIdentifierKind {
227 fn new(kind: impl Into<String>) -> Self {
228 Self(kind.into())
229 }
230}
231
232impl std::fmt::Display for InvalidPxeClassIdentifierKind {
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 write!(
235 f,
236 "invalid pxe class identifier kind '{}', expected 'PXEClient' or 'PXEServer'",
237 self.0
238 )
239 }
240}
241
242impl std::error::Error for InvalidPxeClassIdentifierKind {}
243
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
245pub enum PxeClassIdentifierKind {
246 Client,
247 Server,
248}
249
250impl PxeClassIdentifierKind {
251 pub const KIND_CLIENT: &'static str = "PXEClient";
252 pub const KIND_SERVER: &'static str = "PXEServer";
253}
254
255impl FromStr for PxeClassIdentifierKind {
256 type Err = InvalidPxeClassIdentifierKind;
257
258 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
259 match s {
260 Self::KIND_CLIENT => Ok(Self::Client),
261 Self::KIND_SERVER => Ok(Self::Server),
262 _ => Err(InvalidPxeClassIdentifierKind::new(s)),
263 }
264 }
265}
266
267#[derive(Debug)]
268pub struct InvalidPxeClassIdentifier(String, String);
269
270impl InvalidPxeClassIdentifier {
271 fn new(class: impl Into<String>, reason: impl Into<String>) -> Self {
272 Self(class.into(), reason.into())
273 }
274}
275
276impl std::fmt::Display for InvalidPxeClassIdentifier {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 write!(f, "invalid pxe class identifier '{}': {}", self.0, self.1)
279 }
280}
281
282impl std::error::Error for InvalidPxeClassIdentifier {}
283
284#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
285pub enum PxeClassIdentifier {
286 Client(PxeClassIdentifierClient),
287 Server(PxeClassIdentifierServer),
288}
289
290#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
291pub struct PxeClassIdentifierServer;
292
293impl std::fmt::Display for PxeClassIdentifierServer {
294 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
295 f.write_str("PXEServer")
296 }
297}
298
299#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
300pub struct PxeClassIdentifierClient {
301 pub architecture: SystemArchitecture,
302 pub undi_major: u16,
303 pub undi_minor: u16,
304}
305
306impl std::fmt::Display for PxeClassIdentifierClient {
307 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308 write!(
309 f,
310 "PXEClient:Arch:{:05}:UNDI:{:03}{:03}",
311 u16::from(self.architecture),
312 self.undi_major,
313 self.undi_minor
314 )
315 }
316}
317
318impl std::fmt::Display for PxeClassIdentifier {
319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320 match self {
321 PxeClassIdentifier::Client(client) => client.fmt(f),
322 PxeClassIdentifier::Server(server) => server.fmt(f),
323 }
324 }
325}
326
327impl TryFrom<&[u8]> for PxeClassIdentifier {
328 type Error = InvalidPxeClassIdentifier;
329
330 fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
331 let str = std::str::from_utf8(value).map_err(|err| {
332 InvalidPxeClassIdentifier::new(
333 format!("{value:?}"),
334 format!("invalid utf-8 string: {err}"),
335 )
336 })?;
337 str.parse()
338 }
339}
340
341impl FromStr for PxeClassIdentifier {
342 type Err = InvalidPxeClassIdentifier;
343
344 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
345 let mut parts = s.split(":");
346 let make_err = |reason: String| InvalidPxeClassIdentifier::new(s, reason);
347
348 let kind = match parts.next() {
349 Some(kind) => kind
350 .parse::<PxeClassIdentifierKind>()
351 .map_err(|err| make_err(err.to_string()))?,
352 None => return Err(make_err("missing class kind".to_string())),
353 };
354
355 if kind == PxeClassIdentifierKind::Server {
356 if parts.next().is_some() {
357 return Err(make_err("invalid class".to_string()));
358 }
359 return Ok(Self::Server(PxeClassIdentifierServer));
360 }
361
362 if !parts.next().map(|s| s == "Arch").unwrap_or(false) {
363 return Err(make_err("invalid class".to_string()));
364 }
365
366 let architecture = match parts.next() {
367 Some(arch) => arch
368 .parse::<SystemArchitecture>()
369 .map_err(|err| make_err(err.to_string()))?,
370 None => return Err(make_err("missing architecture".to_string())),
371 };
372
373 if !parts.next().map(|s| s == "UNDI").unwrap_or(false) {
374 return Err(make_err("invalid class".to_string()));
375 }
376
377 let undi_str = match parts.next() {
378 Some(undi_str) => undi_str,
379 None => return Err(make_err("missing undi version".to_string())),
380 };
381
382 if undi_str.len() != 6 {
383 return Err(make_err("invalid undi version length".to_string()));
384 }
385
386 let (undi_major_str, undi_minor_str) = undi_str.split_at_checked(3).unwrap();
387
388 let undi_major = undi_major_str
389 .parse::<u16>()
390 .map_err(|err| make_err(err.to_string()))?;
391
392 let undi_minor = undi_minor_str
393 .parse::<u16>()
394 .map_err(|err| make_err(err.to_string()))?;
395
396 if parts.next().is_some() {
397 return Err(make_err("invalid class".to_string()));
398 }
399
400 Ok(Self::Client(PxeClassIdentifierClient {
401 architecture,
402 undi_major,
403 undi_minor,
404 }))
405 }
406}
407
116#[derive(Debug)] 408#[derive(Debug)]
117pub struct DhcpPacket { 409pub struct DhcpPacket {
118 pub op: BootOp, 410 pub op: BootOp,
@@ -125,7 +417,7 @@ pub struct DhcpPacket {
125 pub yiaddr: Ipv4Addr, 417 pub yiaddr: Ipv4Addr,
126 pub siaddr: Ipv4Addr, 418 pub siaddr: Ipv4Addr,
127 pub giaddr: Ipv4Addr, 419 pub giaddr: Ipv4Addr,
128 pub chaddr: [u8; 16], 420 pub chaddr: HardwareAddress,
129 // server host name 421 // server host name
130 pub sname: String, 422 pub sname: String,
131 // boot file name 423 // boot file name
@@ -158,11 +450,21 @@ impl DhcpPacket {
158 pub fn new_boot( 450 pub fn new_boot(
159 xid: u32, 451 xid: u32,
160 chaddr: [u8; 16], 452 chaddr: [u8; 16],
161 client_uuid: Vec<u8>, 453 client_uuid: Option<Vec<u8>>,
162 local_ip: Ipv4Addr, 454 local_ip: Ipv4Addr,
163 local_hostname: String, 455 local_hostname: String,
164 filename: String, 456 filename: String,
165 ) -> Self { 457 ) -> Self {
458 let mut options = vec![
459 DhcpOption::MessageType(DhcpMessageType::Offer),
460 DhcpOption::ServerIdentifier(local_ip),
461 DhcpOption::VendorClassIdentifier(b"PXEClient".to_vec()),
462 DhcpOption::TftpServerName(local_hostname),
463 DhcpOption::TftpFileName(filename),
464 ];
465 if let Some(uuid) = client_uuid {
466 options.push(DhcpOption::ClientMachineIdentifier(uuid));
467 }
166 Self { 468 Self {
167 op: BootOp::Reply, 469 op: BootOp::Reply,
168 htype: HardwareType::Ethernet, 470 htype: HardwareType::Ethernet,
@@ -177,25 +479,28 @@ impl DhcpPacket {
177 chaddr, 479 chaddr,
178 sname: Default::default(), 480 sname: Default::default(),
179 file: Default::default(), 481 file: Default::default(),
180 options: vec![ 482 options,
181 DhcpOption::MessageType(DhcpMessageType::Offer),
182 DhcpOption::ServerIdentifier(local_ip),
183 DhcpOption::VendorClassIdentifier("PXEClient".to_string()),
184 DhcpOption::ClientMachineIdentifier(client_uuid),
185 DhcpOption::TftpServerName(local_hostname),
186 DhcpOption::TftpFileName(filename),
187 ],
188 } 483 }
189 } 484 }
190 485
191 pub fn new_boot_ack( 486 pub fn new_boot_ack(
192 xid: u32, 487 xid: u32,
193 chaddr: [u8; 16], 488 chaddr: [u8; 16],
194 client_uuid: Vec<u8>, 489 client_uuid: Option<Vec<u8>>,
195 local_ip: Ipv4Addr, 490 local_ip: Ipv4Addr,
196 hostname: String, 491 hostname: String,
197 filename: String, 492 filename: String,
198 ) -> Self { 493 ) -> Self {
494 let mut options = vec![
495 DhcpOption::MessageType(DhcpMessageType::Ack),
496 DhcpOption::ServerIdentifier(local_ip),
497 DhcpOption::VendorClassIdentifier(b"PXEClient".to_vec()),
498 DhcpOption::TftpServerName(hostname),
499 DhcpOption::TftpFileName(filename),
500 ];
501 if let Some(uuid) = client_uuid {
502 options.push(DhcpOption::ClientMachineIdentifier(uuid));
503 }
199 Self { 504 Self {
200 op: BootOp::Reply, 505 op: BootOp::Reply,
201 htype: HardwareType::Ethernet, 506 htype: HardwareType::Ethernet,
@@ -210,14 +515,7 @@ impl DhcpPacket {
210 chaddr, 515 chaddr,
211 sname: Default::default(), 516 sname: Default::default(),
212 file: Default::default(), 517 file: Default::default(),
213 options: vec![ 518 options,
214 DhcpOption::MessageType(DhcpMessageType::Ack),
215 DhcpOption::ServerIdentifier(local_ip),
216 DhcpOption::VendorClassIdentifier("PXEClient".to_string()),
217 DhcpOption::ClientMachineIdentifier(client_uuid),
218 DhcpOption::TftpServerName(hostname),
219 DhcpOption::TftpFileName(filename),
220 ],
221 } 519 }
222 } 520 }
223 521
@@ -296,10 +594,23 @@ fn read_option(cursor: &mut Cursor<&[u8]>) -> Result<DhcpOption> {
296 DhcpOption::CODE_PAD => DhcpOption::Pad, 594 DhcpOption::CODE_PAD => DhcpOption::Pad,
297 DhcpOption::CODE_END => DhcpOption::End, 595 DhcpOption::CODE_END => DhcpOption::End,
298 DhcpOption::CODE_VENDOR_CLASS_IDENTIFIER => { 596 DhcpOption::CODE_VENDOR_CLASS_IDENTIFIER => {
299 DhcpOption::VendorClassIdentifier(read_len8_prefixed_string(cursor)?) 597 DhcpOption::VendorClassIdentifier(read_len8_prefixed_vec(cursor)?)
300 } 598 }
301 DhcpOption::CODE_USER_CLASS_INFORMATION => { 599 DhcpOption::CODE_USER_CLASS_INFORMATION => {
302 DhcpOption::UserClassInformation(read_len8_prefixed_string(cursor)?) 600 DhcpOption::UserClassInformation(read_len8_prefixed_vec(cursor)?)
601 }
602 DhcpOption::CODE_CLIENT_SYSTEM_ARCHITECTURE => {
603 let len = read_u8(cursor)?;
604 assert_eq!(len, 2);
605
606 let mut buf = [0u8; 2];
607 cursor.read_exact(&mut buf)?;
608
609 let arch = SystemArchitecture::from(u16::from_be_bytes(buf));
610 DhcpOption::ClientSystemArchitecture(arch)
611 }
612 DhcpOption::CODE_CLIENT_MACHINE_IDENTIFIER => {
613 DhcpOption::ClientMachineIdentifier(read_len8_prefixed_vec(cursor)?)
303 } 614 }
304 _ => { 615 _ => {
305 let len = read_u8(cursor)?; 616 let len = read_u8(cursor)?;
@@ -353,6 +664,20 @@ pub fn parse_packet(buf: &[u8]) -> Result<DhcpPacket> {
353} 664}
354 665
355pub fn write_packet<W: Write>(mut writer: W, packet: &DhcpPacket) -> Result<()> { 666pub fn write_packet<W: Write>(mut writer: W, packet: &DhcpPacket) -> Result<()> {
667 if packet.sname.len() >= 64 {
668 return Err(std::io::Error::new(
669 std::io::ErrorKind::InvalidInput,
670 "sname cannot be longer than 64 bytes",
671 ));
672 }
673
674 if packet.file.len() >= 128 {
675 return Err(std::io::Error::new(
676 std::io::ErrorKind::InvalidInput,
677 "filename cannot be longer than 128 bytes",
678 ));
679 }
680
356 wire::write_u8(&mut writer, u8::from(packet.op))?; 681 wire::write_u8(&mut writer, u8::from(packet.op))?;
357 wire::write_u8(&mut writer, u8::from(packet.htype))?; 682 wire::write_u8(&mut writer, u8::from(packet.htype))?;
358 wire::write_u8(&mut writer, packet.htype.hardware_len())?; 683 wire::write_u8(&mut writer, packet.htype.hardware_len())?;
@@ -365,10 +690,21 @@ pub fn write_packet<W: Write>(mut writer: W, packet: &DhcpPacket) -> Result<()>
365 wire::write_ipv4(&mut writer, packet.siaddr)?; 690 wire::write_ipv4(&mut writer, packet.siaddr)?;
366 wire::write_ipv4(&mut writer, packet.giaddr)?; 691 wire::write_ipv4(&mut writer, packet.giaddr)?;
367 wire::write(&mut writer, &packet.chaddr)?; 692 wire::write(&mut writer, &packet.chaddr)?;
368 //wire::write_null_terminated_string(&mut writer, &packet.sname)?; 693
369 //wire::write_null_terminated_string(&mut writer, &packet.file)?; 694 let sname_bytes = packet.sname.as_bytes();
370 wire::write(&mut writer, &vec![0u8; 64])?; 695 wire::write(&mut writer, sname_bytes)?;
371 wire::write(&mut writer, &vec![0u8; 128])?; 696 for _ in 0..(64 - sname_bytes.len()) {
697 wire::write_u8(&mut writer, 0)?;
698 }
699
700 let file_bytes = packet.file.as_bytes();
701 wire::write(&mut writer, file_bytes)?;
702 for _ in 0..(128 - file_bytes.len()) {
703 wire::write_u8(&mut writer, 0)?;
704 }
705
706 // wire::write(&mut writer, &vec![0u8; 64])?;
707 // wire::write(&mut writer, &vec![0u8; 128])?;
372 wire::write(&mut writer, &MAGIC_COOKIE)?; 708 wire::write(&mut writer, &MAGIC_COOKIE)?;
373 for option in &packet.options { 709 for option in &packet.options {
374 write_option(&mut writer, option)?; 710 write_option(&mut writer, option)?;
@@ -390,12 +726,16 @@ pub fn write_option<W: Write>(mut writer: W, option: &DhcpOption) -> Result<()>
390 wire::write_ipv4(&mut writer, *ip)?; 726 wire::write_ipv4(&mut writer, *ip)?;
391 } 727 }
392 DhcpOption::VendorClassIdentifier(vendor_class) => { 728 DhcpOption::VendorClassIdentifier(vendor_class) => {
393 write_option_len_prefixed_string(&mut writer, &vendor_class)? 729 write_option_len_prefixed_buf(&mut writer, &vendor_class)?
394 } 730 }
395 DhcpOption::TftpServerName(name) => write_option_len_prefixed_string(&mut writer, &name)?, 731 DhcpOption::TftpServerName(name) => write_option_len_prefixed_string(&mut writer, &name)?,
396 DhcpOption::TftpFileName(name) => write_option_len_prefixed_string(&mut writer, &name)?, 732 DhcpOption::TftpFileName(name) => write_option_len_prefixed_string(&mut writer, &name)?,
397 DhcpOption::UserClassInformation(user_class) => { 733 DhcpOption::UserClassInformation(user_class) => {
398 write_option_len_prefixed_string(&mut writer, &user_class)? 734 write_option_len_prefixed_buf(&mut writer, &user_class)?
735 }
736 DhcpOption::ClientSystemArchitecture(arch) => {
737 wire::write_u8(&mut writer, 2)?;
738 wire::write_u16(&mut writer, u16::from(*arch))?;
399 } 739 }
400 DhcpOption::ClientMachineIdentifier(identifier) => { 740 DhcpOption::ClientMachineIdentifier(identifier) => {
401 write_option_len_prefixed_buf(&mut writer, &identifier)? 741 write_option_len_prefixed_buf(&mut writer, &identifier)?