netstack_testing_common/
dhcpv4.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Utilities for interacting with DHCPv4 client/server during integration
6//! tests.
7
8#![warn(missing_docs)]
9
10use std::ops::Range;
11
12use dhcpv4::protocol::IntoFidlExt as _;
13
14use futures::StreamExt as _;
15use net_declare::net::prefix_length_v4;
16use net_types::ip::{Ipv4, PrefixLength};
17
18/// Encapsulates a minimal configuration needed to test a DHCP client/server combination.
19pub struct TestConfig {
20    /// Server IP address.
21    pub server_addr: fidl_fuchsia_net::Ipv4Address,
22
23    /// Address pool for the DHCP server.
24    pub managed_addrs: dhcpv4::configuration::ManagedAddresses,
25}
26
27impl TestConfig {
28    /// Given offsets for the server's own address and the managed address pool,
29    /// constructs a `TestConfig` for a DHCP server managing addresses in the
30    /// 192.168.0.0/25 subnet.
31    pub const fn new(server_addr_offset: u8, pool_offsets: Range<u8>) -> Self {
32        const fn from_offset(offset: u8) -> std::net::Ipv4Addr {
33            std::net::Ipv4Addr::new(192, 168, 0, offset)
34        }
35
36        let Range { start, end } = pool_offsets;
37
38        let max_offset =
39            1u8 << 32u8.checked_sub(DEFAULT_TEST_ADDRESS_POOL_PREFIX_LENGTH.get()).unwrap();
40
41        assert!(
42            server_addr_offset < max_offset,
43            "server_addr_offset must fit within default address pool prefix"
44        );
45        assert!(pool_offsets.start < pool_offsets.end, "pool_offsets start must be less than end");
46        assert!(
47            pool_offsets.start < max_offset,
48            "pool_offsets.start must fit within default address pool prefix"
49        );
50        assert!(
51            pool_offsets.end < max_offset,
52            "pool_offsets.end must fit within default address pool prefix"
53        );
54
55        let server_addr =
56            fidl_fuchsia_net::Ipv4Address { addr: from_offset(server_addr_offset).octets() };
57        let pool_range_start = from_offset(start);
58        let pool_range_stop = from_offset(end);
59
60        Self {
61            server_addr,
62            managed_addrs: dhcpv4::configuration::ManagedAddresses {
63                mask: dhcpv4::configuration::SubnetMask::new(
64                    DEFAULT_TEST_ADDRESS_POOL_PREFIX_LENGTH,
65                ),
66                pool_range_start,
67                pool_range_stop,
68            },
69        }
70    }
71
72    /// The IPv4 address a client will acquire from the server.
73    pub fn expected_acquired(&self) -> fidl_fuchsia_net::Subnet {
74        let Self {
75            server_addr: _,
76            managed_addrs:
77                dhcpv4::configuration::ManagedAddresses { mask, pool_range_start, pool_range_stop: _ },
78        } = self;
79        fidl_fuchsia_net::Subnet {
80            addr: fidl_fuchsia_net::IpAddress::Ipv4(pool_range_start.into_fidl()),
81            prefix_len: mask.ones(),
82        }
83    }
84
85    /// The IPv4 address and prefix the server will assign to itself.
86    pub fn server_addr_with_prefix(&self) -> fidl_fuchsia_net::Ipv4AddressWithPrefix {
87        let Self {
88            server_addr,
89            managed_addrs:
90                dhcpv4::configuration::ManagedAddresses {
91                    mask,
92                    pool_range_start: _,
93                    pool_range_stop: _,
94                },
95        } = self;
96        fidl_fuchsia_net::Ipv4AddressWithPrefix { addr: *server_addr, prefix_len: mask.ones() }
97    }
98
99    /// The FIDL parameters a DHCPv4 server should be configured with.
100    pub fn dhcp_parameters(&self) -> Vec<fidl_fuchsia_net_dhcp::Parameter> {
101        let Self { server_addr, managed_addrs } = self;
102        vec![
103            fidl_fuchsia_net_dhcp::Parameter::IpAddrs(vec![*server_addr]),
104            fidl_fuchsia_net_dhcp::Parameter::AddressPool(managed_addrs.into_fidl()),
105        ]
106    }
107}
108
109/// Default prefix length of default configuration's address pool.
110pub const DEFAULT_TEST_ADDRESS_POOL_PREFIX_LENGTH: PrefixLength<Ipv4> = prefix_length_v4!(25);
111
112/// Default configuration.
113pub const DEFAULT_TEST_CONFIG: TestConfig = TestConfig::new(1, 2..5);
114
115/// Set DHCPv4 server settings.
116pub async fn set_server_settings(
117    dhcp_server: &fidl_fuchsia_net_dhcp::Server_Proxy,
118    parameters: impl IntoIterator<Item = fidl_fuchsia_net_dhcp::Parameter>,
119    options: impl IntoIterator<Item = fidl_fuchsia_net_dhcp::Option_>,
120) {
121    let parameters = futures::stream::iter(parameters.into_iter()).for_each_concurrent(
122        None,
123        |parameter| async move {
124            dhcp_server
125                .set_parameter(&parameter)
126                .await
127                .expect("failed to call dhcp/Server.SetParameter")
128                .map_err(zx::Status::from_raw)
129                .unwrap_or_else(|e| {
130                    panic!("dhcp/Server.SetParameter({:?}) returned error: {:?}", parameter, e)
131                })
132        },
133    );
134    let options =
135        futures::stream::iter(options.into_iter()).for_each_concurrent(None, |option| async move {
136            dhcp_server
137                .set_option(&option)
138                .await
139                .expect("failed to call dhcp/Server.SetOption")
140                .map_err(zx::Status::from_raw)
141                .unwrap_or_else(|e| {
142                    panic!("dhcp/Server.SetOption({:?}) returned error: {:?}", option, e)
143                })
144        });
145    let ((), ()) = futures::future::join(parameters, options).await;
146}