Skip to content

Commit 14695c1

Browse files
committed
Implement DHCPv4 client + example.
1 parent 7c606ac commit 14695c1

File tree

8 files changed

+569
-4
lines changed

8 files changed

+569
-4
lines changed

.travis.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ matrix:
1919
env: FEATURES='std proto-ipv4 proto-igmp socket-raw' MODE='test'
2020
- rust: nightly
2121
env: FEATURES='std proto-ipv4 socket-udp socket-tcp' MODE='test'
22+
- rust: nightly
23+
env: FEATURES='std proto-ipv4 proto-dhcpv4 socket-udp' MODE='test'
2224
- rust: nightly
2325
env: FEATURES='std proto-ipv6 socket-udp' MODE='test'
2426
- rust: nightly
@@ -35,7 +37,7 @@ matrix:
3537
env: FEATURES='proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp alloc'
3638
MODE='test'
3739
- rust: nightly
38-
env: FEATURES='proto-ipv4 proto-ipv6 proto-igmp socket-raw socket-udp socket-tcp socket-icmp'
40+
env: FEATURES='proto-ipv4 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp'
3941
MODE='build'
4042
- rust: nightly
4143
env: MODE='fuzz run' ARGS='packet_parser -- -max_len=1536 -max_total_time=30'

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ verbose = []
3535
"phy-tap_interface" = ["std", "libc"]
3636
"proto-ipv4" = []
3737
"proto-igmp" = ["proto-ipv4"]
38+
"proto-dhcpv4" = ["proto-ipv4", "socket-raw"]
3839
"proto-ipv6" = []
3940
"socket-raw" = []
4041
"socket-udp" = []
4142
"socket-tcp" = []
4243
"socket-icmp" = []
43-
"proto-dhcpv4" = ["proto-ipv4"]
4444
default = [
4545
"std", "log", # needed for `cargo test --no-default-features --features default` :/
4646
"phy-raw_socket", "phy-tap_interface",
@@ -88,5 +88,9 @@ required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-igmp", "so
8888
name = "benchmark"
8989
required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-raw", "socket-udp"]
9090

91+
[[example]]
92+
name = "dhcp_client"
93+
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"]
94+
9195
[profile.release]
9296
debug = 2

examples/dhcp_client.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#[macro_use]
2+
extern crate log;
3+
extern crate env_logger;
4+
extern crate getopts;
5+
extern crate smoltcp;
6+
7+
mod utils;
8+
9+
use std::collections::BTreeMap;
10+
use std::os::unix::io::AsRawFd;
11+
use smoltcp::phy::wait as phy_wait;
12+
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr};
13+
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes};
14+
use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata};
15+
use smoltcp::time::Instant;
16+
use smoltcp::dhcp::Dhcpv4Client;
17+
18+
fn main() {
19+
#[cfg(feature = "log")]
20+
utils::setup_logging("");
21+
22+
let (mut opts, mut free) = utils::create_options();
23+
utils::add_tap_options(&mut opts, &mut free);
24+
utils::add_middleware_options(&mut opts, &mut free);
25+
26+
let mut matches = utils::parse_options(&opts, free);
27+
let device = utils::parse_tap_options(&mut matches);
28+
let fd = device.as_raw_fd();
29+
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
30+
31+
let neighbor_cache = NeighborCache::new(BTreeMap::new());
32+
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
33+
let ip_addrs = [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)];
34+
let mut routes_storage = [None; 1];
35+
let routes = Routes::new(&mut routes_storage[..]);
36+
let mut iface = EthernetInterfaceBuilder::new(device)
37+
.ethernet_addr(ethernet_addr)
38+
.neighbor_cache(neighbor_cache)
39+
.ip_addrs(ip_addrs)
40+
.routes(routes)
41+
.finalize();
42+
43+
let mut sockets = SocketSet::new(vec![]);
44+
let dhcp_rx_buffer = RawSocketBuffer::new(
45+
[RawPacketMetadata::EMPTY; 1],
46+
vec![0; 900]
47+
);
48+
let dhcp_tx_buffer = RawSocketBuffer::new(
49+
[RawPacketMetadata::EMPTY; 1],
50+
vec![0; 600]
51+
);
52+
let mut dhcp = Dhcpv4Client::new(&mut sockets, dhcp_rx_buffer, dhcp_tx_buffer, Instant::now());
53+
let mut prev_cidr = Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0);
54+
loop {
55+
let timestamp = Instant::now();
56+
iface.poll(&mut sockets, timestamp)
57+
.map(|_| ())
58+
.unwrap_or_else(|e| println!("Poll: {:?}", e));
59+
let config = dhcp.poll(&mut iface, &mut sockets, timestamp)
60+
.unwrap_or_else(|e| {
61+
println!("DHCP: {:?}", e);
62+
None
63+
});
64+
config.map(|config| {
65+
println!("DHCP config: {:?}", config);
66+
match config.address {
67+
Some(cidr) => if cidr != prev_cidr {
68+
iface.update_ip_addrs(|addrs| {
69+
addrs.iter_mut().nth(0)
70+
.map(|addr| {
71+
*addr = IpCidr::Ipv4(cidr);
72+
});
73+
});
74+
prev_cidr = cidr;
75+
println!("Assigned a new IPv4 address: {}", cidr);
76+
}
77+
_ => {}
78+
}
79+
80+
config.router.map(|router| iface.routes_mut()
81+
.add_default_ipv4_route(router.into())
82+
.unwrap()
83+
);
84+
iface.routes_mut()
85+
.update(|routes_map| {
86+
routes_map.get(&IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0))
87+
.map(|default_route| {
88+
println!("Default gateway: {}", default_route.via_router);
89+
});
90+
});
91+
92+
if config.dns_servers.iter().any(|s| s.is_some()) {
93+
println!("DNS servers:");
94+
for dns_server in config.dns_servers.iter().filter_map(|s| *s) {
95+
println!("- {}", dns_server);
96+
}
97+
}
98+
});
99+
100+
let mut timeout = dhcp.next_poll(timestamp);
101+
iface.poll_delay(&sockets, timestamp)
102+
.map(|sockets_timeout| timeout = sockets_timeout);
103+
phy_wait(fd, Some(timeout))
104+
.unwrap_or_else(|e| println!("Wait: {:?}", e));;
105+
}
106+
}

0 commit comments

Comments
 (0)