socket/dhcp: add retransmission/timeout tests

master
Dario Nieuwenhuis 2021-10-15 00:38:42 +02:00
parent 6768d89165
commit a43a6772c9
1 changed files with 148 additions and 44 deletions

View File

@ -545,30 +545,43 @@ mod test {
socket.process(&cx, &ip_repr, &udp_repr, &payload)
}
fn recv<F>(socket: &mut Dhcpv4Socket, timestamp: Instant, mut f: F)
where
F: FnMut(Result<(Ipv4Repr, UdpRepr, DhcpRepr)>),
{
fn recv(
socket: &mut Dhcpv4Socket,
timestamp: Instant,
reprs: &[(Ipv4Repr, UdpRepr, DhcpRepr)],
) {
let mut cx = Context::DUMMY.clone();
cx.now = timestamp;
let result = socket.dispatch(&cx, |(mut ip_repr, udp_repr, dhcp_repr)| {
assert_eq!(ip_repr.protocol, IpProtocol::Udp);
assert_eq!(
ip_repr.payload_len,
udp_repr.header_len() + dhcp_repr.buffer_len()
);
// We validated the payload len, change it to 0 to make equality testing easier
ip_repr.payload_len = 0;
let mut i = 0;
net_trace!("recv: {:?}", ip_repr);
net_trace!(" {:?}", udp_repr);
net_trace!(" {:?}", dhcp_repr);
Ok(f(Ok((ip_repr, udp_repr, dhcp_repr))))
});
match result {
Ok(()) => (),
Err(e) => f(Err(e)),
while socket.poll_at(&cx) <= PollAt::Time(timestamp) {
let _ = socket.dispatch(&cx, |(mut ip_repr, udp_repr, dhcp_repr)| {
assert_eq!(ip_repr.protocol, IpProtocol::Udp);
assert_eq!(
ip_repr.payload_len,
udp_repr.header_len() + dhcp_repr.buffer_len()
);
// We validated the payload len, change it to 0 to make equality testing easier
ip_repr.payload_len = 0;
net_trace!("recv: {:?}", ip_repr);
net_trace!(" {:?}", udp_repr);
net_trace!(" {:?}", dhcp_repr);
let got_repr = (ip_repr, udp_repr, dhcp_repr);
match reprs.get(i) {
Some(want_repr) => assert_eq!(want_repr, &got_repr),
None => panic!("Too many reprs emitted"),
}
i += 1;
Ok(())
});
}
if i != reprs.len() {
panic!("Too few reprs emitted. Wanted {}, got {}", reprs.len(), i);
}
}
@ -584,20 +597,12 @@ mod test {
}
macro_rules! recv {
($socket:ident, [$( $repr:expr ),*]) => ({
$( recv!($socket, Ok($repr)); )*
recv!($socket, Err(Error::Exhausted))
($socket:ident, $reprs:expr) => ({
recv!($socket, time 0, $reprs);
});
($socket:ident, time $time:expr, [$( $repr:expr ),*]) => ({
$( recv!($socket, time $time, Ok($repr)); )*
recv!($socket, time $time, Err(Error::Exhausted))
($socket:ident, time $time:expr, $reprs:expr) => ({
recv(&mut $socket, Instant::from_millis($time), &$reprs);
});
($socket:ident, $result:expr) =>
(recv!($socket, time 0, $result));
($socket:ident, time $time:expr, $result:expr) =>
(recv(&mut $socket, Instant::from_millis($time), |result| {
assert_eq!(result, $result)
}));
}
#[cfg(feature = "log")]
@ -710,7 +715,7 @@ mod test {
router: Some(SERVER_IP),
subnet_mask: Some(MASK_24),
dns_servers: Some(DNS_IPS),
lease_duration: Some(60),
lease_duration: Some(1000),
..DHCP_DEFAULT
};
@ -735,7 +740,7 @@ mod test {
router: Some(SERVER_IP),
subnet_mask: Some(MASK_24),
dns_servers: Some(DNS_IPS),
lease_duration: Some(60),
lease_duration: Some(1000),
..DHCP_DEFAULT
};
@ -776,8 +781,8 @@ mod test {
address: SERVER_IP,
identifier: SERVER_IP,
},
renew_at: Instant::from_secs(30),
expires_at: Instant::from_secs(60),
renew_at: Instant::from_secs(500),
expires_at: Instant::from_secs(1000),
});
s
@ -804,43 +809,142 @@ mod test {
}))
);
match s.state {
match &s.state {
ClientState::Renewing(r) => {
assert_eq!(r.renew_at, Instant::from_secs(30));
assert_eq!(r.expires_at, Instant::from_secs(60));
assert_eq!(r.renew_at, Instant::from_secs(500));
assert_eq!(r.expires_at, Instant::from_secs(1000));
}
_ => panic!("Invalid state"),
}
}
#[test]
fn test_discover_retransmit() {
let mut s = socket();
recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
recv!(s, time 1_000, []);
recv!(s, time 10_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
recv!(s, time 11_000, []);
recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
// check after retransmits it still works
send!(s, time 20_000, (IP_RECV, UDP_RECV, DHCP_OFFER));
recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
}
#[test]
fn test_request_retransmit() {
let mut s = socket();
recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
send!(s, time 0, (IP_RECV, UDP_RECV, DHCP_OFFER));
recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
recv!(s, time 1_000, []);
recv!(s, time 5_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
recv!(s, time 6_000, []);
recv!(s, time 10_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
recv!(s, time 15_000, []);
recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
// check after retransmits it still works
send!(s, time 20_000, (IP_RECV, UDP_RECV, DHCP_ACK));
match &s.state {
ClientState::Renewing(r) => {
assert_eq!(r.renew_at, Instant::from_secs(20 + 500));
assert_eq!(r.expires_at, Instant::from_secs(20 + 1000));
}
_ => panic!("Invalid state"),
}
}
#[test]
fn test_request_timeout() {
let mut s = socket();
recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
send!(s, time 0, (IP_RECV, UDP_RECV, DHCP_OFFER));
recv!(s, time 0, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
recv!(s, time 5_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
recv!(s, time 10_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
recv!(s, time 20_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
recv!(s, time 30_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
// After 5 tries and 70 seconds, it gives up.
// 5 + 5 + 10 + 10 + 20 = 70
recv!(s, time 70_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
// check it still works
send!(s, time 60_000, (IP_RECV, UDP_RECV, DHCP_OFFER));
recv!(s, time 60_000, [(IP_BROADCAST, UDP_SEND, DHCP_REQUEST)]);
}
#[test]
fn test_renew() {
let mut s = socket_bound();
recv!(s, []);
assert_eq!(s.poll(), None);
recv!(s, time 40_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
assert_eq!(s.poll(), None);
match &s.state {
ClientState::Renewing(r) => {
// the expiration still hasn't been bumped, because
// we haven't received the ACK yet
assert_eq!(r.expires_at, Instant::from_secs(60));
assert_eq!(r.expires_at, Instant::from_secs(1000));
}
_ => panic!("Invalid state"),
}
send!(s, time 40_000, (IP_RECV, UDP_RECV, DHCP_ACK));
send!(s, time 500_000, (IP_RECV, UDP_RECV, DHCP_ACK));
assert_eq!(s.poll(), None);
match &s.state {
ClientState::Renewing(r) => {
// NOW the expiration gets bumped
assert_eq!(r.renew_at, Instant::from_secs(40 + 30));
assert_eq!(r.expires_at, Instant::from_secs(40 + 60));
assert_eq!(r.renew_at, Instant::from_secs(500 + 500));
assert_eq!(r.expires_at, Instant::from_secs(500 + 1000));
}
_ => panic!("Invalid state"),
}
}
#[test]
fn test_renew_retransmit() {
let mut s = socket_bound();
recv!(s, []);
recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
recv!(s, time 749_000, []);
recv!(s, time 750_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
recv!(s, time 874_000, []);
recv!(s, time 875_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
// check it still works
send!(s, time 875_000, (IP_RECV, UDP_RECV, DHCP_ACK));
match &s.state {
ClientState::Renewing(r) => {
// NOW the expiration gets bumped
assert_eq!(r.renew_at, Instant::from_secs(875 + 500));
assert_eq!(r.expires_at, Instant::from_secs(875 + 1000));
}
_ => panic!("Invalid state"),
}
}
#[test]
fn test_renew_timeout() {
let mut s = socket_bound();
recv!(s, []);
recv!(s, time 500_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
recv!(s, time 999_000, [(IP_SEND, UDP_SEND, DHCP_RENEW)]);
recv!(s, time 1_000_000, [(IP_BROADCAST, UDP_SEND, DHCP_DISCOVER)]);
match &s.state {
ClientState::Discovering(_) => {}
_ => panic!("Invalid state"),
}
}
}