Skip to content

Commit f4cd00c

Browse files
committed
Add a few DHCP options and internal IpAddress.to_prefix_len()
1 parent 4a253fe commit f4cd00c

File tree

4 files changed

+139
-7
lines changed

4 files changed

+139
-7
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ verbose = []
3939
"socket-udp" = []
4040
"socket-tcp" = []
4141
"socket-icmp" = []
42+
"proto-dhcpv4" = ["proto-ipv4"]
4243
default = [
4344
"std", "log", # needed for `cargo test --no-default-features --features default` :/
4445
"phy-raw_socket", "phy-tap_interface",

src/wire/dhcpv4.rs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ pub enum DhcpOption<'a> {
5050
RequestedIp(Ipv4Address),
5151
ClientIdentifier(EthernetAddress),
5252
ServerIdentifier(Ipv4Address),
53+
Router(Ipv4Address),
54+
SubnetMask(Ipv4Address),
5355
Other { kind: u8, data: &'a [u8] }
5456
}
5557

@@ -91,6 +93,12 @@ impl<'a> DhcpOption<'a> {
9193
(field::OPT_SERVER_IDENTIFIER, 4) => {
9294
option = DhcpOption::ServerIdentifier(Ipv4Address::from_bytes(data));
9395
}
96+
(field::OPT_ROUTER, 4) => {
97+
option = DhcpOption::Router(Ipv4Address::from_bytes(data));
98+
}
99+
(field::OPT_SUBNET_MASK, 4) => {
100+
option = DhcpOption::SubnetMask(Ipv4Address::from_bytes(data));
101+
}
94102
(_, _) => {
95103
option = DhcpOption::Other { kind: kind, data: data };
96104
}
@@ -108,7 +116,10 @@ impl<'a> DhcpOption<'a> {
108116
&DhcpOption::ClientIdentifier(eth_addr) => {
109117
3 + eth_addr.as_bytes().len()
110118
}
111-
&DhcpOption::RequestedIp(ip) | &DhcpOption::ServerIdentifier(ip) => {
119+
&DhcpOption::RequestedIp(ip) |
120+
&DhcpOption::ServerIdentifier(ip) |
121+
&DhcpOption::Router(ip) |
122+
&DhcpOption::SubnetMask(ip) => {
112123
2 + ip.as_bytes().len()
113124
},
114125
&DhcpOption::Other { data, .. } => 2 + data.len()
@@ -148,6 +159,14 @@ impl<'a> DhcpOption<'a> {
148159
buffer[0] = field::OPT_SERVER_IDENTIFIER;
149160
buffer[2..6].copy_from_slice(ip.as_bytes());
150161
}
162+
&DhcpOption::Router(ip) => {
163+
buffer[0] = field::OPT_ROUTER;
164+
buffer[2..6].copy_from_slice(ip.as_bytes());
165+
}
166+
&DhcpOption::SubnetMask(mask) => {
167+
buffer[0] = field::OPT_SUBNET_MASK;
168+
buffer[2..6].copy_from_slice(mask.as_bytes());
169+
}
151170
&DhcpOption::Other { kind, data: provided } => {
152171
buffer[0] = kind;
153172
buffer[2..skip_length].copy_from_slice(provided);
@@ -165,7 +184,7 @@ pub struct Packet<T: AsRef<[u8]>> {
165184
buffer: T
166185
}
167186

168-
mod field {
187+
pub(crate) mod field {
169188
#![allow(non_snake_case)]
170189
#![allow(unused)]
171190

@@ -603,6 +622,10 @@ pub struct Repr<'a> {
603622
/// This field is also known as `siaddr` in the RFC. It may be set by the server in DHCPOFFER
604623
/// and DHCPACK messages, and represent the address of the next server to use in bootstrap.
605624
pub server_ip: Ipv4Address,
625+
/// Default gateway
626+
pub router: Option<Ipv4Address>,
627+
/// This field comes from a corresponding DhcpOption.
628+
pub subnet_mask: Option<Ipv4Address>,
606629
/// This field is also known as `giaddr` in the RFC. In order to allow DHCP clients on subnets
607630
/// not directly served by DHCP servers to communicate with DHCP servers, DHCP relay agents can
608631
/// be installed on these subnets. The DHCP client broadcasts on the local link; the relay
@@ -635,6 +658,8 @@ pub struct Repr<'a> {
635658
/// The parameter request list informs the server about which configuration parameters
636659
/// the client is interested in.
637660
pub parameter_request_list: Option<&'a [u8]>,
661+
/// DNS servers
662+
pub dns_servers: Option<[Option<Ipv4Address>; 3]>,
638663
}
639664

640665
impl<'a> Repr<'a> {
@@ -680,7 +705,10 @@ impl<'a> Repr<'a> {
680705
let mut requested_ip = None;
681706
let mut client_identifier = None;
682707
let mut server_identifier = None;
708+
let mut router = None;
709+
let mut subnet_mask = None;
683710
let mut parameter_request_list = None;
711+
let mut dns_servers = None;
684712

685713
let mut options = packet.options()?;
686714
while options.len() > 0 {
@@ -702,9 +730,25 @@ impl<'a> Repr<'a> {
702730
DhcpOption::ServerIdentifier(ip) => {
703731
server_identifier = Some(ip);
704732
}
733+
DhcpOption::Router(ip) => {
734+
router = Some(ip);
735+
}
736+
DhcpOption::SubnetMask(mask) => {
737+
subnet_mask = Some(mask);
738+
}
705739
DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => {
706740
parameter_request_list = Some(data);
707741
}
742+
DhcpOption::Other {kind: field::OPT_DOMAIN_NAME_SERVER, data} => {
743+
let mut dns_servers_inner = [None; 3];
744+
for i in 0.. {
745+
let offset = 4 * i;
746+
let end = offset + 4;
747+
if end > data.len() { break }
748+
dns_servers_inner[i] = Some(Ipv4Address::from_bytes(&data[offset..end]));
749+
}
750+
dns_servers = Some(dns_servers_inner);
751+
}
708752
DhcpOption::Other {..} => {}
709753
}
710754
options = next_options;
@@ -714,7 +758,8 @@ impl<'a> Repr<'a> {
714758

715759
Ok(Repr {
716760
transaction_id, client_hardware_address, client_ip, your_ip, server_ip, relay_agent_ip,
717-
broadcast, requested_ip, server_identifier, client_identifier, parameter_request_list,
761+
broadcast, requested_ip, server_identifier, router,
762+
subnet_mask, client_identifier, parameter_request_list, dns_servers,
718763
message_type: message_type?,
719764
})
720765
}
@@ -746,6 +791,12 @@ impl<'a> Repr<'a> {
746791
if let Some(ip) = self.server_identifier {
747792
let tmp = options; options = DhcpOption::ServerIdentifier(ip).emit(tmp);
748793
}
794+
if let Some(ip) = self.router {
795+
let tmp = options; options = DhcpOption::Router(ip).emit(tmp);
796+
}
797+
if let Some(ip) = self.subnet_mask {
798+
let tmp = options; options = DhcpOption::SubnetMask(ip).emit(tmp);
799+
}
749800
if let Some(ip) = self.requested_ip {
750801
let tmp = options; options = DhcpOption::RequestedIp(ip).emit(tmp);
751802
}
@@ -878,12 +929,15 @@ mod test {
878929
client_ip: IP_NULL,
879930
your_ip: IP_NULL,
880931
server_ip: IP_NULL,
932+
router: None,
933+
subnet_mask: None,
881934
relay_agent_ip: IP_NULL,
882935
broadcast: false,
883936
requested_ip: Some(IP_NULL),
884937
client_identifier: Some(CLIENT_MAC),
885938
server_identifier: None,
886939
parameter_request_list: Some(&[1, 3, 6, 42]),
940+
dns_servers: None,
887941
}
888942
}
889943

src/wire/ip.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,23 @@ impl Address {
184184
&Address::__Nonexhaustive => unreachable!()
185185
}
186186
}
187+
188+
/// Treat as subnet mask, get position of first 0
189+
pub(crate) fn to_prefix_len(&self) -> u8 {
190+
let mut prefix_len = 0;
191+
for byte in self.as_bytes() {
192+
let mut mask = 0x80;
193+
for _ in 0..8 {
194+
if *byte & mask == 0 {
195+
return prefix_len
196+
}
197+
198+
prefix_len += 1;
199+
mask >>= 1;
200+
}
201+
}
202+
prefix_len
203+
}
187204
}
188205

189206
#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
@@ -1074,4 +1091,63 @@ pub(crate) mod test {
10741091
fn endpoint_unspecified() {
10751092
assert!(!Endpoint::UNSPECIFIED.is_specified());
10761093
}
1094+
1095+
#[test]
1096+
#[cfg(feature = "proto-ipv4")]
1097+
fn to_prefix_len_ipv4() {
1098+
fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
1099+
assert_eq!(
1100+
prefix_len,
1101+
mask.into().to_prefix_len()
1102+
);
1103+
}
1104+
1105+
test_eq(0, Ipv4Address::new(0, 0, 0, 0));
1106+
test_eq(1, Ipv4Address::new(128, 0, 0, 0));
1107+
test_eq(2, Ipv4Address::new(192, 0, 0, 0));
1108+
test_eq(3, Ipv4Address::new(224, 0, 0, 0));
1109+
test_eq(4, Ipv4Address::new(240, 0, 0, 0));
1110+
test_eq(5, Ipv4Address::new(248, 0, 0, 0));
1111+
test_eq(6, Ipv4Address::new(252, 0, 0, 0));
1112+
test_eq(7, Ipv4Address::new(254, 0, 0, 0));
1113+
test_eq(8, Ipv4Address::new(255, 0, 0, 0));
1114+
test_eq(9, Ipv4Address::new(255, 128, 0, 0));
1115+
test_eq(10, Ipv4Address::new(255, 192, 0, 0));
1116+
test_eq(11, Ipv4Address::new(255, 224, 0, 0));
1117+
test_eq(12, Ipv4Address::new(255, 240, 0, 0));
1118+
test_eq(13, Ipv4Address::new(255, 248, 0, 0));
1119+
test_eq(14, Ipv4Address::new(255, 252, 0, 0));
1120+
test_eq(15, Ipv4Address::new(255, 254, 0, 0));
1121+
test_eq(16, Ipv4Address::new(255, 255, 0, 0));
1122+
test_eq(17, Ipv4Address::new(255, 255, 128, 0));
1123+
test_eq(18, Ipv4Address::new(255, 255, 192, 0));
1124+
test_eq(19, Ipv4Address::new(255, 255, 224, 0));
1125+
test_eq(20, Ipv4Address::new(255, 255, 240, 0));
1126+
test_eq(21, Ipv4Address::new(255, 255, 248, 0));
1127+
test_eq(22, Ipv4Address::new(255, 255, 252, 0));
1128+
test_eq(23, Ipv4Address::new(255, 255, 254, 0));
1129+
test_eq(24, Ipv4Address::new(255, 255, 255, 0));
1130+
test_eq(25, Ipv4Address::new(255, 255, 255, 128));
1131+
test_eq(26, Ipv4Address::new(255, 255, 255, 192));
1132+
test_eq(27, Ipv4Address::new(255, 255, 255, 224));
1133+
test_eq(28, Ipv4Address::new(255, 255, 255, 240));
1134+
test_eq(29, Ipv4Address::new(255, 255, 255, 248));
1135+
test_eq(30, Ipv4Address::new(255, 255, 255, 252));
1136+
test_eq(31, Ipv4Address::new(255, 255, 255, 254));
1137+
test_eq(32, Ipv4Address::new(255, 255, 255, 255));
1138+
}
1139+
1140+
#[test]
1141+
#[cfg(feature = "proto-ipv6")]
1142+
fn to_prefix_len_ipv6() {
1143+
fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
1144+
assert_eq!(
1145+
prefix_len,
1146+
mask.into().to_prefix_len()
1147+
);
1148+
}
1149+
1150+
test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
1151+
test_eq(128, Ipv6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff));
1152+
}
10771153
}

src/wire/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ mod ndiscoption;
109109
mod mld;
110110
mod udp;
111111
mod tcp;
112-
#[cfg(feature = "proto-ipv4")]
113-
mod dhcpv4;
112+
#[cfg(feature = "proto-dhcpv4")]
113+
pub(crate) mod dhcpv4;
114114

115115
pub use self::pretty_print::PrettyPrinter;
116116

@@ -216,6 +216,7 @@ pub use self::tcp::{SeqNumber as TcpSeqNumber,
216216
Repr as TcpRepr,
217217
Control as TcpControl};
218218

219-
#[cfg(feature = "proto-ipv4")]
219+
#[cfg(feature = "proto-dhcpv4")]
220220
pub use self::dhcpv4::{Packet as DhcpPacket,
221-
Repr as DhcpRepr};
221+
Repr as DhcpRepr,
222+
MessageType as DhcpMessageType};

0 commit comments

Comments
 (0)