mozim/dhcpv6/
config.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use std::net::Ipv6Addr;
4
5use crate::{
6    DhcpError, DhcpV6Duid, DhcpV6DuidLinkLayerAddr, DhcpV6OptionCode, ETH_ALEN,
7};
8
9// https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
10const ARP_HW_TYPE_ETHERNET: u16 = 1;
11
12#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash, Default)]
13#[non_exhaustive]
14pub enum DhcpV6IaType {
15    #[default]
16    NonTemporaryAddresses,
17    TemporaryAddresses,
18    PrefixDelegation,
19}
20
21#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash, Default)]
22#[non_exhaustive]
23pub enum DhcpV6Mode {
24    #[default]
25    NonTemporaryAddresses,
26    TemporaryAddresses,
27    /// Request prefix delegation with specified prefix length.
28    /// This is just hint for DHCPv6 server, server might reply prefix with
29    /// smaller prefix length.
30    PrefixDelegation(u8),
31    // As describe in RFC 3736, request stateless configuration options from
32    // DHCPv6 server. The node must have obtained its IPv6 addresses through
33    // some other mechanism(e.g. SLAAC).
34    //Stateless,
35}
36
37impl std::fmt::Display for DhcpV6Mode {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        match self {
40            Self::NonTemporaryAddresses => {
41                write!(f, "Non-temporary Addresses(IA_NA)")
42            }
43            Self::TemporaryAddresses => write!(f, "Temporary Addresses(IA_TA)"),
44            Self::PrefixDelegation(d) => {
45                write!(f, "Prefix Delegation(IA_PD)-{d}")
46            } // Self::Stateless => "Stateless",
47        }
48    }
49}
50
51#[derive(Debug, PartialEq, Eq, Clone)]
52#[non_exhaustive]
53pub struct DhcpV6Config {
54    pub iface_name: String,
55    pub iface_index: u32,
56    pub duid: DhcpV6Duid,
57    pub mode: DhcpV6Mode,
58    pub src_ip: Ipv6Addr,
59    // TODO: Inifniband has 128 bits MAC address.
60    pub(crate) src_mac: Option<[u8; ETH_ALEN]>,
61    /// Timeout in seconds for getting/refreshing lease.
62    /// 0 means infinitely.
63    /// By default is wait infinitely.
64    pub timeout_sec: u32,
65    pub request_opts: Vec<DhcpV6OptionCode>,
66}
67
68impl Default for DhcpV6Config {
69    fn default() -> Self {
70        Self {
71            iface_name: String::new(),
72            iface_index: 0,
73            duid: DhcpV6Duid::Raw(Vec::new()),
74            mode: DhcpV6Mode::default(),
75            src_ip: Ipv6Addr::UNSPECIFIED,
76            src_mac: None,
77            timeout_sec: 0,
78            request_opts: vec![
79                DhcpV6OptionCode::OptionRequestOption,
80                DhcpV6OptionCode::Preference,
81                DhcpV6OptionCode::DnsServers,
82                DhcpV6OptionCode::DomainList,
83                DhcpV6OptionCode::NtpServer,
84            ],
85        }
86    }
87}
88
89impl DhcpV6Config {
90    pub fn new(iface_name: &str, mode: DhcpV6Mode) -> Self {
91        Self {
92            iface_name: iface_name.to_string(),
93            mode,
94            ..Default::default()
95        }
96    }
97
98    pub(crate) fn need_resolve(&self) -> bool {
99        self.iface_index == 0 || self.src_ip.is_unspecified()
100    }
101
102    /// Get interface MAC address, IPv6 link-local address and interface index.
103    #[cfg(feature = "netlink")]
104    pub(crate) async fn resolve(&mut self) -> Result<(), DhcpError> {
105        self.iface_index =
106            crate::netlink::get_iface_index(&self.iface_name).await?;
107
108        if let Ok((_, src_mac)) =
109            crate::netlink::get_iface_index_mac(&self.iface_name).await
110        {
111            if src_mac.len() == ETH_ALEN {
112                let mut tmp_src_mac = [0u8; ETH_ALEN];
113                tmp_src_mac.copy_from_slice(&src_mac[..ETH_ALEN]);
114                self.src_mac = Some(tmp_src_mac);
115            }
116        }
117
118        self.src_ip =
119            crate::netlink::get_link_local_addr(self.iface_index).await?;
120        self.get_duid_or_init();
121        Ok(())
122    }
123
124    #[cfg(not(feature = "netlink"))]
125    pub(crate) async fn resolve(&mut self) -> Result<(), DhcpError> {
126        Err(DhcpError::new(
127            crate::ErrorKind::InvalidArgument,
128            format!(
129                "Feature `netlink` not enabled, cannot resolve interface {} \
130                 index and IPv6 link local address, please set them manually",
131                self.iface_name,
132            ),
133        ))
134    }
135
136    pub fn set_iface_index(&mut self, iface_index: u32) -> &mut Self {
137        self.iface_index = iface_index;
138        self
139    }
140
141    /// Set the link local IP address
142    pub fn set_link_local_ip(&mut self, addr: Ipv6Addr) -> &mut Self {
143        self.src_ip = addr;
144        self
145    }
146
147    /// Set arbitrary DUID
148    pub fn set_duid(&mut self, duid: DhcpV6Duid) -> &mut Self {
149        self.duid = duid;
150        self
151    }
152
153    /// Use MAC address of interface to setup DUID to
154    /// `DhcpV6Duid::LinkLayerAddress`.
155    pub fn set_duid_by_iface_mac(&mut self, mac: &[u8]) -> &mut Self {
156        self.duid = DhcpV6Duid::LinkLayerAddress(DhcpV6DuidLinkLayerAddr::new(
157            ARP_HW_TYPE_ETHERNET,
158            mac,
159        ));
160        self
161    }
162
163    /// Get DUID or initialize to DhcpV6Duid::LinkLayerAddress() when found MAC
164    /// address of specified interface, fallback to `DhcpV6Duid::default()`
165    /// if no MAC address.
166    pub fn get_duid_or_init(&mut self) -> &DhcpV6Duid {
167        if self.duid.is_empty() {
168            self.duid = if let Some(mac) = self.src_mac.as_ref() {
169                DhcpV6Duid::LinkLayerAddress(DhcpV6DuidLinkLayerAddr::new(
170                    ARP_HW_TYPE_ETHERNET,
171                    mac,
172                ))
173            } else {
174                DhcpV6Duid::default()
175            };
176        }
177        &self.duid
178    }
179
180    /// Timeout in seconds for getting/refreshing lease.
181    /// 0 means infinitely.
182    /// By default is wait infinitely.
183    pub fn set_timeout_sec(&mut self, timeout_sec: u32) -> &mut Self {
184        self.timeout_sec = timeout_sec;
185        self
186    }
187
188    pub fn request_extra_dhcp_opts(&mut self, opts: &[u16]) -> &mut Self {
189        for opt in opts {
190            self.request_opts.push((*opt).into());
191        }
192        self.request_opts.sort_unstable();
193        self.request_opts.dedup();
194        self
195    }
196
197    /// Specify arbitrary DHCP options to request.
198    pub fn override_request_dhcp_opts(&mut self, opts: &[u16]) -> &mut Self {
199        self.request_opts =
200            opts.iter().map(|c| DhcpV6OptionCode::from(*c)).collect();
201        self.request_opts.sort_unstable();
202        self.request_opts.dedup();
203        self
204    }
205}