1use std::{
4 net::{IpAddr, Ipv4Addr, Ipv6Addr},
5 ops::Deref,
6};
7
8use anyhow::Context;
9use byteorder::{ByteOrder, NativeEndian};
10use netlink_packet_utils::{
11 nla::{Nla, NlaBuffer, NlasIterator},
12 parsers::{parse_ip, parse_mac, parse_u16, parse_u32, parse_u8},
13 traits::{Emitable, Parseable},
14 DecodeError,
15};
16
17use crate::constants::*;
18
19#[derive(Debug, Clone, Eq, PartialEq)]
20#[non_exhaustive]
21pub enum BondAdInfo {
22 Aggregator(u16),
23 NumPorts(u16),
24 ActorKey(u16),
25 PartnerKey(u16),
26 PartnerMac([u8; 6]),
27}
28
29impl Nla for BondAdInfo {
30 fn value_len(&self) -> usize {
31 use self::BondAdInfo::*;
32 match self {
33 Aggregator(_) | NumPorts(_) | ActorKey(_) | PartnerKey(_) => 2,
34 PartnerMac(_) => 6,
35 }
36 }
37
38 fn kind(&self) -> u16 {
39 use self::BondAdInfo::*;
40 match self {
41 Aggregator(_) => IFLA_BOND_AD_INFO_AGGREGATOR,
42 NumPorts(_) => IFLA_BOND_AD_INFO_NUM_PORTS,
43 ActorKey(_) => IFLA_BOND_AD_INFO_ACTOR_KEY,
44 PartnerKey(_) => IFLA_BOND_AD_INFO_PARTNER_KEY,
45 PartnerMac(_) => IFLA_BOND_AD_INFO_PARTNER_MAC,
46 }
47 }
48
49 fn emit_value(&self, buffer: &mut [u8]) {
50 use self::BondAdInfo::*;
51 match self {
52 Aggregator(d) | NumPorts(d) | ActorKey(d) | PartnerKey(d) => {
53 NativeEndian::write_u16(buffer, *d)
54 }
55 PartnerMac(mac) => buffer.copy_from_slice(mac),
56 }
57 }
58}
59
60impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for BondAdInfo {
61 fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
62 use self::BondAdInfo::*;
63 let payload = buf.value();
64 Ok(match buf.kind() {
65 IFLA_BOND_AD_INFO_AGGREGATOR => Aggregator(
66 parse_u16(payload)
67 .context("invalid IFLA_BOND_AD_INFO_AGGREGATOR value")?,
68 ),
69 IFLA_BOND_AD_INFO_NUM_PORTS => NumPorts(
70 parse_u16(payload)
71 .context("invalid IFLA_BOND_AD_INFO_NUM_PORTS value")?,
72 ),
73 IFLA_BOND_AD_INFO_ACTOR_KEY => ActorKey(
74 parse_u16(payload)
75 .context("invalid IFLA_BOND_AD_INFO_ACTOR_KEY value")?,
76 ),
77 IFLA_BOND_AD_INFO_PARTNER_KEY => PartnerKey(
78 parse_u16(payload)
79 .context("invalid IFLA_BOND_AD_INFO_PARTNER_KEY value")?,
80 ),
81 IFLA_BOND_AD_INFO_PARTNER_MAC => PartnerMac(
82 parse_mac(payload)
83 .context("invalid IFLA_BOND_AD_INFO_PARTNER_MAC value")?,
84 ),
85 _ => return Err(format!("unknown NLA type {}", buf.kind()).into()),
86 })
87 }
88}
89
90struct BondIpAddrNla {
95 index: u16,
96 addr: IpAddr,
97}
98
99struct BondIpAddrNlaList(Vec<BondIpAddrNla>);
100
101impl Deref for BondIpAddrNlaList {
102 type Target = Vec<BondIpAddrNla>;
103
104 fn deref(&self) -> &Self::Target {
105 &self.0
106 }
107}
108
109impl From<&Vec<Ipv4Addr>> for BondIpAddrNlaList {
110 fn from(addrs: &Vec<Ipv4Addr>) -> Self {
111 let mut nlas = Vec::new();
112 for (i, addr) in addrs.iter().enumerate() {
113 let nla = BondIpAddrNla {
114 index: i as u16,
115 addr: IpAddr::V4(*addr),
116 };
117 nlas.push(nla);
118 }
119 BondIpAddrNlaList(nlas)
120 }
121}
122
123impl From<&Vec<Ipv6Addr>> for BondIpAddrNlaList {
124 fn from(addrs: &Vec<Ipv6Addr>) -> Self {
125 let mut nlas = Vec::new();
126 for (i, addr) in addrs.iter().enumerate() {
127 let nla = BondIpAddrNla {
128 index: i as u16,
129 addr: IpAddr::V6(*addr),
130 };
131 nlas.push(nla);
132 }
133 BondIpAddrNlaList(nlas)
134 }
135}
136
137impl Nla for BondIpAddrNla {
138 fn value_len(&self) -> usize {
139 if self.addr.is_ipv4() {
140 4
141 } else {
142 16
143 }
144 }
145 fn emit_value(&self, buffer: &mut [u8]) {
146 match self.addr {
147 IpAddr::V4(addr) => buffer.copy_from_slice(&addr.octets()),
148 IpAddr::V6(addr) => buffer.copy_from_slice(&addr.octets()),
149 }
150 }
151 fn kind(&self) -> u16 {
152 self.index
153 }
154}
155
156#[derive(Debug, PartialEq, Eq, Clone)]
157#[non_exhaustive]
158pub enum InfoBond {
159 Mode(u8),
160 ActivePort(u32),
161 MiiMon(u32),
162 UpDelay(u32),
163 DownDelay(u32),
164 UseCarrier(u8),
165 ArpInterval(u32),
166 ArpIpTarget(Vec<Ipv4Addr>),
167 ArpValidate(u32),
168 ArpAllTargets(u32),
169 Primary(u32),
170 PrimaryReselect(u8),
171 FailOverMac(u8),
172 XmitHashPolicy(u8),
173 ResendIgmp(u32),
174 NumPeerNotif(u8),
175 AllPortsActive(u8),
176 MinLinks(u32),
177 LpInterval(u32),
178 PacketsPerPort(u32),
179 AdLacpRate(u8),
180 AdSelect(u8),
181 AdInfo(Vec<BondAdInfo>),
182 AdActorSysPrio(u16),
183 AdUserPortKey(u16),
184 AdActorSystem([u8; 6]),
185 TlbDynamicLb(u8),
186 PeerNotifDelay(u32),
187 AdLacpActive(u8),
188 MissedMax(u8),
189 NsIp6Target(Vec<Ipv6Addr>),
190}
191
192impl Nla for InfoBond {
193 #[rustfmt::skip]
194 fn value_len(&self) -> usize {
195 use self::InfoBond::*;
196 match *self {
197 Mode(_)
198 | UseCarrier(_)
199 | PrimaryReselect(_)
200 | FailOverMac(_)
201 | XmitHashPolicy(_)
202 | NumPeerNotif(_)
203 | AllPortsActive(_)
204 | AdLacpActive(_)
205 | AdLacpRate(_)
206 | AdSelect(_)
207 | TlbDynamicLb(_)
208 | MissedMax(_)
209 => 1,
210 AdActorSysPrio(_)
211 | AdUserPortKey(_)
212 => 2,
213 ActivePort(_)
214 | MiiMon(_)
215 | UpDelay(_)
216 | DownDelay(_)
217 | ArpInterval(_)
218 | ArpValidate(_)
219 | ArpAllTargets(_)
220 | Primary(_)
221 | ResendIgmp(_)
222 | MinLinks(_)
223 | LpInterval(_)
224 | PacketsPerPort(_)
225 | PeerNotifDelay(_)
226 => 4,
227 ArpIpTarget(ref addrs)
228 => {
229 BondIpAddrNlaList::from(addrs).as_slice().buffer_len()
230 },
231 NsIp6Target(ref addrs)
232 => {
233 BondIpAddrNlaList::from(addrs).as_slice().buffer_len()
234 },
235 AdActorSystem(_) => 6,
236 AdInfo(ref infos)
237 => infos.as_slice().buffer_len(),
238 }
239 }
240
241 #[rustfmt::skip]
242 fn emit_value(&self, buffer: &mut [u8]) {
243 use self::InfoBond::*;
244 match self {
245 Mode(value)
246 | UseCarrier(value)
247 | PrimaryReselect(value)
248 | FailOverMac(value)
249 | XmitHashPolicy(value)
250 | NumPeerNotif(value)
251 | AllPortsActive(value)
252 | AdLacpActive(value)
253 | AdLacpRate(value)
254 | AdSelect(value)
255 | TlbDynamicLb(value)
256 | MissedMax(value)
257 => buffer[0] = *value,
258 AdActorSysPrio(value)
259 | AdUserPortKey(value)
260 => NativeEndian::write_u16(buffer, *value),
261 ActivePort(value)
262 | MiiMon(value)
263 | UpDelay(value)
264 | DownDelay(value)
265 | ArpInterval(value)
266 | ArpValidate(value)
267 | ArpAllTargets(value)
268 | Primary(value)
269 | ResendIgmp(value)
270 | MinLinks(value)
271 | LpInterval(value)
272 | PacketsPerPort(value)
273 | PeerNotifDelay(value)
274 => NativeEndian::write_u32(buffer, *value),
275 AdActorSystem(bytes) => buffer.copy_from_slice(bytes),
276 ArpIpTarget(addrs) => {
277 BondIpAddrNlaList::from(addrs).as_slice().emit(buffer)
278 },
279 NsIp6Target(addrs) => {
280 BondIpAddrNlaList::from(addrs).as_slice().emit(buffer)
281 },
282 AdInfo(infos) => infos.as_slice().emit(buffer),
283 }
284 }
285
286 fn kind(&self) -> u16 {
287 use self::InfoBond::*;
288
289 match self {
290 Mode(_) => IFLA_BOND_MODE,
291 ActivePort(_) => IFLA_BOND_ACTIVE_PORT,
292 MiiMon(_) => IFLA_BOND_MIIMON,
293 UpDelay(_) => IFLA_BOND_UPDELAY,
294 DownDelay(_) => IFLA_BOND_DOWNDELAY,
295 UseCarrier(_) => IFLA_BOND_USE_CARRIER,
296 ArpInterval(_) => IFLA_BOND_ARP_INTERVAL,
297 ArpIpTarget(_) => IFLA_BOND_ARP_IP_TARGET,
298 ArpValidate(_) => IFLA_BOND_ARP_VALIDATE,
299 ArpAllTargets(_) => IFLA_BOND_ARP_ALL_TARGETS,
300 Primary(_) => IFLA_BOND_PRIMARY,
301 PrimaryReselect(_) => IFLA_BOND_PRIMARY_RESELECT,
302 FailOverMac(_) => IFLA_BOND_FAIL_OVER_MAC,
303 XmitHashPolicy(_) => IFLA_BOND_XMIT_HASH_POLICY,
304 ResendIgmp(_) => IFLA_BOND_RESEND_IGMP,
305 NumPeerNotif(_) => IFLA_BOND_NUM_PEER_NOTIF,
306 AllPortsActive(_) => IFLA_BOND_ALL_PORTS_ACTIVE,
307 MinLinks(_) => IFLA_BOND_MIN_LINKS,
308 LpInterval(_) => IFLA_BOND_LP_INTERVAL,
309 PacketsPerPort(_) => IFLA_BOND_PACKETS_PER_PORT,
310 AdLacpRate(_) => IFLA_BOND_AD_LACP_RATE,
311 AdSelect(_) => IFLA_BOND_AD_SELECT,
312 AdInfo(_) => IFLA_BOND_AD_INFO,
313 AdActorSysPrio(_) => IFLA_BOND_AD_ACTOR_SYS_PRIO,
314 AdUserPortKey(_) => IFLA_BOND_AD_USER_PORT_KEY,
315 AdActorSystem(_) => IFLA_BOND_AD_ACTOR_SYSTEM,
316 TlbDynamicLb(_) => IFLA_BOND_TLB_DYNAMIC_LB,
317 PeerNotifDelay(_) => IFLA_BOND_PEER_NOTIF_DELAY,
318 AdLacpActive(_) => IFLA_BOND_AD_LACP_ACTIVE,
319 MissedMax(_) => IFLA_BOND_MISSED_MAX,
320 NsIp6Target(_) => IFLA_BOND_NS_IP6_TARGET,
321 }
322 }
323}
324
325impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for InfoBond {
326 fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
327 use self::InfoBond::*;
328 let payload = buf.value();
329 Ok(match buf.kind() {
330 IFLA_BOND_MODE => Mode(
331 parse_u8(payload).context("invalid IFLA_BOND_MODE value")?,
332 ),
333 IFLA_BOND_ACTIVE_PORT => ActivePort(
334 parse_u32(payload)
335 .context("invalid IFLA_BOND_ACTIVE_PORT value")?,
336 ),
337 IFLA_BOND_MIIMON => MiiMon(
338 parse_u32(payload).context("invalid IFLA_BOND_MIIMON value")?,
339 ),
340 IFLA_BOND_UPDELAY => UpDelay(
341 parse_u32(payload)
342 .context("invalid IFLA_BOND_UPDELAY value")?,
343 ),
344 IFLA_BOND_DOWNDELAY => DownDelay(
345 parse_u32(payload)
346 .context("invalid IFLA_BOND_DOWNDELAY value")?,
347 ),
348 IFLA_BOND_USE_CARRIER => UseCarrier(
349 parse_u8(payload)
350 .context("invalid IFLA_BOND_USE_CARRIER value")?,
351 ),
352 IFLA_BOND_ARP_INTERVAL => ArpInterval(
353 parse_u32(payload)
354 .context("invalid IFLA_BOND_ARP_INTERVAL value")?,
355 ),
356 IFLA_BOND_ARP_IP_TARGET => {
357 let mut addrs = Vec::<Ipv4Addr>::new();
358 for nla in NlasIterator::new(payload) {
359 let nla =
360 &nla.context("invalid IFLA_BOND_ARP_IP_TARGET value")?;
361 if let Ok(IpAddr::V4(addr)) = parse_ip(nla.value()) {
362 addrs.push(addr);
363 }
364 }
365 ArpIpTarget(addrs)
366 }
367 IFLA_BOND_ARP_VALIDATE => ArpValidate(
368 parse_u32(payload)
369 .context("invalid IFLA_BOND_ARP_VALIDATE value")?,
370 ),
371 IFLA_BOND_ARP_ALL_TARGETS => ArpAllTargets(
372 parse_u32(payload)
373 .context("invalid IFLA_BOND_ARP_ALL_TARGETS value")?,
374 ),
375 IFLA_BOND_PRIMARY => Primary(
376 parse_u32(payload)
377 .context("invalid IFLA_BOND_PRIMARY value")?,
378 ),
379 IFLA_BOND_PRIMARY_RESELECT => PrimaryReselect(
380 parse_u8(payload)
381 .context("invalid IFLA_BOND_PRIMARY_RESELECT value")?,
382 ),
383 IFLA_BOND_FAIL_OVER_MAC => FailOverMac(
384 parse_u8(payload)
385 .context("invalid IFLA_BOND_FAIL_OVER_MAC value")?,
386 ),
387 IFLA_BOND_XMIT_HASH_POLICY => XmitHashPolicy(
388 parse_u8(payload)
389 .context("invalid IFLA_BOND_XMIT_HASH_POLICY value")?,
390 ),
391 IFLA_BOND_RESEND_IGMP => ResendIgmp(
392 parse_u32(payload)
393 .context("invalid IFLA_BOND_RESEND_IGMP value")?,
394 ),
395 IFLA_BOND_NUM_PEER_NOTIF => NumPeerNotif(
396 parse_u8(payload)
397 .context("invalid IFLA_BOND_NUM_PEER_NOTIF value")?,
398 ),
399 IFLA_BOND_ALL_PORTS_ACTIVE => AllPortsActive(
400 parse_u8(payload)
401 .context("invalid IFLA_BOND_ALL_PORTS_ACTIVE value")?,
402 ),
403 IFLA_BOND_MIN_LINKS => MinLinks(
404 parse_u32(payload)
405 .context("invalid IFLA_BOND_MIN_LINKS value")?,
406 ),
407 IFLA_BOND_LP_INTERVAL => LpInterval(
408 parse_u32(payload)
409 .context("invalid IFLA_BOND_LP_INTERVAL value")?,
410 ),
411 IFLA_BOND_PACKETS_PER_PORT => PacketsPerPort(
412 parse_u32(payload)
413 .context("invalid IFLA_BOND_PACKETS_PER_PORT value")?,
414 ),
415 IFLA_BOND_AD_LACP_RATE => AdLacpRate(
416 parse_u8(payload)
417 .context("invalid IFLA_BOND_AD_LACP_RATE value")?,
418 ),
419 IFLA_BOND_AD_SELECT => AdSelect(
420 parse_u8(payload)
421 .context("invalid IFLA_BOND_AD_SELECT value")?,
422 ),
423 IFLA_BOND_AD_INFO => {
424 let mut infos = Vec::new();
425 let err = "failed to parse IFLA_BOND_AD_INFO";
426 for nla in NlasIterator::new(payload) {
427 let nla = &nla.context(err)?;
428 let info = BondAdInfo::parse(nla).context(err)?;
429 infos.push(info);
430 }
431 AdInfo(infos)
432 }
433 IFLA_BOND_AD_ACTOR_SYS_PRIO => AdActorSysPrio(
434 parse_u16(payload)
435 .context("invalid IFLA_BOND_AD_ACTOR_SYS_PRIO value")?,
436 ),
437 IFLA_BOND_AD_USER_PORT_KEY => AdUserPortKey(
438 parse_u16(payload)
439 .context("invalid IFLA_BOND_AD_USER_PORT_KEY value")?,
440 ),
441 IFLA_BOND_AD_ACTOR_SYSTEM => AdActorSystem(
442 parse_mac(payload)
443 .context("invalid IFLA_BOND_AD_ACTOR_SYSTEM value")?,
444 ),
445 IFLA_BOND_TLB_DYNAMIC_LB => TlbDynamicLb(
446 parse_u8(payload)
447 .context("invalid IFLA_BOND_TLB_DYNAMIC_LB value")?,
448 ),
449 IFLA_BOND_PEER_NOTIF_DELAY => PeerNotifDelay(
450 parse_u32(payload)
451 .context("invalid IFLA_BOND_PEER_NOTIF_DELAY value")?,
452 ),
453 IFLA_BOND_AD_LACP_ACTIVE => AdLacpActive(
454 parse_u8(payload)
455 .context("invalid IFLA_BOND_AD_LACP_ACTIVE value")?,
456 ),
457 IFLA_BOND_MISSED_MAX => MissedMax(
458 parse_u8(payload)
459 .context("invalid IFLA_BOND_MISSED_MAX value")?,
460 ),
461 IFLA_BOND_NS_IP6_TARGET => {
462 let mut addrs = Vec::<Ipv6Addr>::new();
463 for nla in NlasIterator::new(payload) {
464 let nla =
465 &nla.context("invalid IFLA_BOND_NS_IP6_TARGET value")?;
466 if let Ok(IpAddr::V6(addr)) = parse_ip(nla.value()) {
467 addrs.push(addr);
468 }
469 }
470 NsIp6Target(addrs)
471 }
472 _ => return Err(format!("unknown NLA type {}", buf.kind()).into()),
473 })
474 }
475}