1use std::net::Ipv6Addr;
4
5use crate::{
6 DhcpError, DhcpV6Duid, DhcpV6DuidLinkLayerAddr, DhcpV6OptionCode, ETH_ALEN,
7};
8
9const 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 PrefixDelegation(u8),
31 }
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 } }
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 pub(crate) src_mac: Option<[u8; ETH_ALEN]>,
61 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 #[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 pub fn set_link_local_ip(&mut self, addr: Ipv6Addr) -> &mut Self {
143 self.src_ip = addr;
144 self
145 }
146
147 pub fn set_duid(&mut self, duid: DhcpV6Duid) -> &mut Self {
149 self.duid = duid;
150 self
151 }
152
153 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 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 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 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}