stackforge_core/layer/dhcp/
options.rs1pub mod msg_type {
3 pub const DISCOVER: u8 = 1;
4 pub const OFFER: u8 = 2;
5 pub const REQUEST: u8 = 3;
6 pub const DECLINE: u8 = 4;
7 pub const ACK: u8 = 5;
8 pub const NAK: u8 = 6;
9 pub const RELEASE: u8 = 7;
10 pub const INFORM: u8 = 8;
11}
12
13pub mod code {
15 pub const SUBNET_MASK: u8 = 1;
16 pub const ROUTER: u8 = 3;
17 pub const DNS: u8 = 6;
18 pub const HOSTNAME: u8 = 12;
19 pub const DOMAIN_NAME: u8 = 15;
20 pub const BROADCAST_ADDR: u8 = 28;
21 pub const REQUESTED_IP: u8 = 50;
22 pub const LEASE_TIME: u8 = 51;
23 pub const MESSAGE_TYPE: u8 = 53;
24 pub const SERVER_ID: u8 = 54;
25 pub const PARAM_REQUEST_LIST: u8 = 55;
26 pub const MAX_MSG_SIZE: u8 = 57;
27 pub const RENEWAL_TIME: u8 = 58;
28 pub const REBINDING_TIME: u8 = 59;
29 pub const CLIENT_ID: u8 = 61;
30 pub const END: u8 = 255;
31 pub const PAD: u8 = 0;
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct DhcpOption {
37 pub code: u8,
38 pub data: Vec<u8>,
39}
40
41impl DhcpOption {
42 #[must_use]
43 pub fn new(code: u8, data: Vec<u8>) -> Self {
44 Self { code, data }
45 }
46
47 #[must_use]
49 pub fn message_type(msg_type: u8) -> Self {
50 Self::new(code::MESSAGE_TYPE, vec![msg_type])
51 }
52
53 #[must_use]
55 pub fn server_id(ip: std::net::Ipv4Addr) -> Self {
56 Self::new(code::SERVER_ID, ip.octets().to_vec())
57 }
58
59 #[must_use]
61 pub fn lease_time(seconds: u32) -> Self {
62 Self::new(code::LEASE_TIME, seconds.to_be_bytes().to_vec())
63 }
64
65 #[must_use]
67 pub fn subnet_mask(mask: std::net::Ipv4Addr) -> Self {
68 Self::new(code::SUBNET_MASK, mask.octets().to_vec())
69 }
70
71 #[must_use]
73 pub fn router(ip: std::net::Ipv4Addr) -> Self {
74 Self::new(code::ROUTER, ip.octets().to_vec())
75 }
76
77 #[must_use]
79 pub fn dns(servers: &[std::net::Ipv4Addr]) -> Self {
80 let mut data = Vec::with_capacity(servers.len() * 4);
81 for s in servers {
82 data.extend_from_slice(&s.octets());
83 }
84 Self::new(code::DNS, data)
85 }
86
87 #[must_use]
89 pub fn domain_name(name: &str) -> Self {
90 Self::new(code::DOMAIN_NAME, name.as_bytes().to_vec())
91 }
92
93 #[must_use]
95 pub fn to_bytes(&self) -> Vec<u8> {
96 if self.code == code::PAD || self.code == code::END {
97 return vec![self.code];
98 }
99 let mut out = Vec::with_capacity(2 + self.data.len());
100 out.push(self.code);
101 out.push(self.data.len() as u8);
102 out.extend_from_slice(&self.data);
103 out
104 }
105
106 #[must_use]
108 pub fn as_message_type(&self) -> Option<u8> {
109 if self.code == code::MESSAGE_TYPE && !self.data.is_empty() {
110 Some(self.data[0])
111 } else {
112 None
113 }
114 }
115
116 #[must_use]
118 pub fn as_ipv4(&self) -> Option<std::net::Ipv4Addr> {
119 if self.data.len() >= 4 {
120 Some(std::net::Ipv4Addr::new(
121 self.data[0],
122 self.data[1],
123 self.data[2],
124 self.data[3],
125 ))
126 } else {
127 None
128 }
129 }
130}
131
132pub fn parse_options(data: &[u8]) -> Vec<DhcpOption> {
134 let mut opts = Vec::new();
135 let mut i = 0;
136
137 while i < data.len() {
138 let code = data[i];
139 if code == code::END {
140 break;
141 }
142 if code == code::PAD {
143 i += 1;
144 continue;
145 }
146 i += 1;
147 if i >= data.len() {
148 break;
149 }
150 let len = data[i] as usize;
151 i += 1;
152 if i + len > data.len() {
153 break;
154 }
155 opts.push(DhcpOption::new(code, data[i..i + len].to_vec()));
156 i += len;
157 }
158
159 opts
160}
161
162pub fn serialize_options(opts: &[DhcpOption]) -> Vec<u8> {
164 let mut out = Vec::new();
165 for opt in opts {
166 out.extend_from_slice(&opt.to_bytes());
167 }
168 out.push(code::END);
169 out
170}