Skip to content

Commit

Permalink
Support of DHCPv6
Browse files Browse the repository at this point in the history
Introducing `DhcpV6Client` and `DhcpV6ClientAsync` for DHCPv6.

Example could could be found at `examples/mozim_dhcpv6_sync.rs`.

Integration test case included.

Signed-off-by: Gris Ge <[email protected]>
  • Loading branch information
cathay4t committed Dec 15, 2024
1 parent 285d852 commit da2069a
Show file tree
Hide file tree
Showing 31 changed files with 2,291 additions and 278 deletions.
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[target.x86_64-unknown-linux-gnu]
runner = 'sudo -E'

[env]
RUST_TEST_THREADS = "1"
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ path = "src/lib.rs"
rand = { version = "0.8.5", default-features = false }
libc = "0.2.132"
byteorder = "1.4.3"
dhcproto = "0.9.0"
dhcproto = "0.12.0"
log = "0.4.17"
etherparse = "0.13.0"
nix = { version = "0.29.0", features = ["poll", "time", "event"] }
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ TODO:
* Handle vendor difference: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/848
* Support multiple DHCP servers with `DHCPNAK` reply.
* Support DHCPNAK
* Support `DHCPDECLINE`: Client to server indicating network address is
already in use.
* Support `DHCPINFORM`: Client to server, asking only for local configuration
parameters; client already has externally configured network address.
* Rate control -- Token bucket (RFC 2698)
* Initial sleep before discovery/solicit(need check RFC)

# Try out

Expand All @@ -24,5 +30,6 @@ TODO:
# The `eth1.ep` is DHCP server interface running dnsmasq in `mozim` network
# namespace.
sudo ./utils/test_env_mozim &
cargo run --example mozim_async
cargo run --example mozim_dhcpv4_sync
cargo run --example mozim_dhcpv6_sync
```
165 changes: 165 additions & 0 deletions doc/dhcpv6_notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
## RFC 8415

Clients and servers exchange DHCP messages using UDP (see [RFC768]
and BCP 145 [RFC8085]). The client uses a link-local address or
addresses determined through other mechanisms for transmitting and
receiving DHCP messages.

`All_DHCP_Relay_Agents_and_Servers`: ff02::1:2
`All_DHCP_Servers`: ff05::1:3

Clients listen for DHCP messages on UDP port 546. Servers and relay
agents listen for DHCP messages on UDP port 547.



Server Server
(not selected) Client (selected)

v v v
| | |
| Begins initialization |
| | |
start of | _____________/|\_____________ |
4-message |/ Solicit | Solicit \|
exchange | | |
Determines | Determines
configuration | configuration
| | |
|\ | ____________/|
| \________ | /Advertise |
| Advertise\ |/ |
| \ | |
| Collects Advertises |
| \ | |
| Selects configuration |
| | |
| _____________/|\_____________ |
|/ Request | Request \|
| | |
| | Commits configuration
| | |
end of | | _____________/|
4-message | |/ Reply |
exchange | | |
| Initialization complete |
| | |
. . .
. . .
| T1 (renewal) timer expires |
| | |
2-message | _____________/|\_____________ |
exchange |/ Renew | Renew \|
| | |
| | Commits extended lease(s)
| | |
| | _____________/|
| |/ Reply |
. . .
. . .
| | |
| Graceful shutdown |
| | |
2-message | _____________/|\_____________ |
exchange |/ Release | Release \|
| | |
| | Discards lease(s)
| | |
| | _____________/|
| |/ Reply |
| | |
v v v


The IAID uniquely identifies the IA and MUST be chosen to be unique
among the IAIDs for that IA type on the client (e.g., an IA_NA with
an IAID of 0 and an IA_PD with an IAID of 0 are each considered
unique). The IAID is chosen by the client. For any given use of an
IA by the client, the IAID for that IA MUST be consistent across
restarts of the DHCP client.



0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| msg-type | transaction-id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
. options .
. (variable number and length) .
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 2: Client/Server Message Format


16.2. Solicit Message

Clients MUST discard any received Solicit messages.

Servers MUST discard any Solicit messages that do not include a
Client Identifier option or that do include a Server Identifier
option.

16.3. Advertise Message

Clients MUST discard any received Advertise message that meets any of
the following conditions:

- the message does not include a Server Identifier option (see
Section 21.3).

- the message does not include a Client Identifier option (see
Section 21.2).

- the contents of the Client Identifier option do not match the
client's DUID.

- the "transaction-id" field value does not match the value the
client used in its Solicit message.

Servers and relay agents MUST discard any received Advertise
messages.


16.4. Request Message

Clients MUST discard any received Request messages.

Servers MUST discard any received Request message that meets any of
the following conditions:

- the message does not include a Server Identifier option (see
Section 21.3).

- the contents of the Server Identifier option do not match the
server's DUID.

- the message does not include a Client Identifier option (see
Section 21.2).


16.6. Renew Message

Clients MUST discard any received Renew messages.

Servers MUST discard any received Renew message that meets any of the
following conditions:

- the message does not include a Server Identifier option (see
Section 21.3).

- the contents of the Server Identifier option do not match the
server's identifier.

- the message does not include a Client Identifier option (see
Section 21.2).

16.7. Rebind Message

Clients MUST discard any received Rebind messages.

Servers MUST discard any received Rebind messages that do not include
a Client Identifier option (see Section 21.2) or that do include a
Server Identifier option (see Section 21.3).
File renamed without changes.
29 changes: 29 additions & 0 deletions examples/mozim_dhcpv6_sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0

use mozim::{DhcpV6Client, DhcpV6Config, DhcpV6IaType};

const TEST_NIC: &str = "dhcpcli";
const POLL_WAIT_TIME: u32 = 5;

fn main() -> Result<(), Box<dyn std::error::Error>> {
enable_log();
let mut config =
DhcpV6Config::new(TEST_NIC, DhcpV6IaType::NonTemporaryAddresses);
config.set_timeout(60);
let mut cli = DhcpV6Client::init(config, None).unwrap();

loop {
for event in cli.poll(POLL_WAIT_TIME)? {
if let Some(lease) = cli.process(event)? {
println!("Got DHCPv6 lease {:?}", lease);
}
}
}
}

fn enable_log() {
env_logger::Builder::new()
.filter(Some("nispor"), log::LevelFilter::Debug)
.filter(Some("mozim"), log::LevelFilter::Debug)
.init();
}
87 changes: 86 additions & 1 deletion src/client_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use futures::{
};
use nix::poll::{PollFd, PollFlags};

use crate::{DhcpError, DhcpV4Client, DhcpV4Config, DhcpV4Lease, ErrorKind};
use crate::{
DhcpError, DhcpV4Client, DhcpV4Config, DhcpV4Lease, DhcpV6Client,
DhcpV6Config, DhcpV6Lease, ErrorKind,
};

const POLL_TIMEOUT: u16 = 1000; // milliseconds

Expand Down Expand Up @@ -156,3 +159,85 @@ fn poll_thread(fd: RawFd, share_state: Arc<Mutex<ShareState>>) {
}
}
}

#[derive(Debug)]
pub struct DhcpV6ClientAsync {
client: DhcpV6Client,
share_state: Arc<Mutex<ShareState>>,
}

impl Stream for DhcpV6ClientAsync {
type Item = Result<DhcpV6Lease, DhcpError>;

fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
// Poll without wait
match self.client.poll(0) {
Ok(events) => {
for event in events {
match self.client.process(event) {
Ok(Some(lease)) => {
return Poll::Ready(Some(Ok(lease)));
}
Ok(None) => (),
Err(e) => {
return Poll::Ready(Some(Err(e)));
}
}
}
}
Err(e) => {
log::error!("DHCP client poll error: {e}");
return Poll::Ready(Some(Err(e)));
}
}

let mut share_state = match self.share_state.lock() {
Ok(s) => s,
Err(e) => {
return Poll::Ready(Some(Err(DhcpError::new(
ErrorKind::Bug,
format!(
"BUG: DhcpV6ClientAsync::poll_next() \
Failed to acquire lock on share_state {e}",
),
))));
}
};
if share_state.waker.is_none() {
share_state.waker = Some(cx.waker().clone());
drop(share_state);
let fd = self.client.as_raw_fd();
let share_state = self.share_state.clone();
std::thread::spawn(move || poll_thread(fd, share_state));
} else {
share_state.waker = Some(cx.waker().clone());
drop(share_state);
}

Poll::Pending
}
}

impl DhcpV6ClientAsync {
pub fn init(
config: DhcpV6Config,
lease: Option<DhcpV6Lease>,
) -> Result<Self, DhcpError> {
Ok(Self {
client: DhcpV6Client::init(config, lease)?,
share_state: Arc::new(Mutex::new(ShareState { waker: None })),
})
}
}

impl std::ops::Drop for DhcpV6ClientAsync {
fn drop(&mut self) {
if let Ok(mut s) = self.share_state.lock() {
// Signal `poll_thread()` to quit
s.waker = None;
}
}
}
Loading

0 comments on commit da2069a

Please sign in to comment.