netlink_packet_route/link/link_info/
bridge_port.rs

1// SPDX-License-Identifier: MIT
2use crate::link::{BridgeId, BridgeIdBuffer};
3use anyhow::Context;
4use byteorder::{ByteOrder, NativeEndian};
5use netlink_packet_utils::{
6    nla::{DefaultNla, Nla, NlaBuffer},
7    parsers::{parse_u16, parse_u32, parse_u64, parse_u8},
8    traits::{Emitable, Parseable},
9    DecodeError,
10};
11
12const IFLA_BRPORT_STATE: u16 = 1;
13const IFLA_BRPORT_PRIORITY: u16 = 2;
14const IFLA_BRPORT_COST: u16 = 3;
15const IFLA_BRPORT_MODE: u16 = 4;
16const IFLA_BRPORT_GUARD: u16 = 5;
17const IFLA_BRPORT_PROTECT: u16 = 6;
18const IFLA_BRPORT_FAST_LEAVE: u16 = 7;
19const IFLA_BRPORT_LEARNING: u16 = 8;
20const IFLA_BRPORT_UNICAST_FLOOD: u16 = 9;
21const IFLA_BRPORT_PROXYARP: u16 = 10;
22// only used in one driver, we don't know if its type is stable:
23// const IFLA_BRPORT_LEARNING_SYNC: u16 = 11;
24const IFLA_BRPORT_PROXYARP_WIFI: u16 = 12;
25const IFLA_BRPORT_ROOT_ID: u16 = 13;
26const IFLA_BRPORT_BRIDGE_ID: u16 = 14;
27const IFLA_BRPORT_DESIGNATED_PORT: u16 = 15;
28const IFLA_BRPORT_DESIGNATED_COST: u16 = 16;
29const IFLA_BRPORT_ID: u16 = 17;
30const IFLA_BRPORT_NO: u16 = 18;
31const IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: u16 = 19;
32const IFLA_BRPORT_CONFIG_PENDING: u16 = 20;
33const IFLA_BRPORT_MESSAGE_AGE_TIMER: u16 = 21;
34const IFLA_BRPORT_FORWARD_DELAY_TIMER: u16 = 22;
35const IFLA_BRPORT_HOLD_TIMER: u16 = 23;
36const IFLA_BRPORT_FLUSH: u16 = 24;
37const IFLA_BRPORT_MULTICAST_ROUTER: u16 = 25;
38// const IFLA_BRPORT_PAD: u16 = 26;
39const IFLA_BRPORT_MCAST_FLOOD: u16 = 27;
40const IFLA_BRPORT_MCAST_TO_UCAST: u16 = 28;
41const IFLA_BRPORT_VLAN_TUNNEL: u16 = 29;
42const IFLA_BRPORT_BCAST_FLOOD: u16 = 30;
43const IFLA_BRPORT_GROUP_FWD_MASK: u16 = 31;
44const IFLA_BRPORT_NEIGH_SUPPRESS: u16 = 32;
45const IFLA_BRPORT_ISOLATED: u16 = 33;
46const IFLA_BRPORT_BACKUP_PORT: u16 = 34;
47const IFLA_BRPORT_MRP_RING_OPEN: u16 = 35;
48const IFLA_BRPORT_MRP_IN_OPEN: u16 = 36;
49const IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT: u16 = 37;
50const IFLA_BRPORT_MCAST_EHT_HOSTS_CNT: u16 = 38;
51const IFLA_BRPORT_LOCKED: u16 = 39;
52const IFLA_BRPORT_MAB: u16 = 40;
53const IFLA_BRPORT_MCAST_N_GROUPS: u16 = 41;
54const IFLA_BRPORT_MCAST_MAX_GROUPS: u16 = 42;
55const IFLA_BRPORT_NEIGH_VLAN_SUPPRESS: u16 = 43;
56const IFLA_BRPORT_BACKUP_NHID: u16 = 44;
57
58#[derive(Debug, PartialEq, Eq, Clone)]
59#[non_exhaustive]
60pub enum InfoBridgePort {
61    State(BridgePortState),
62    Priority(u16),
63    Cost(u32),
64    HairpinMode(bool),
65    Guard(bool),
66    Protect(bool),
67    FastLeave(bool),
68    Learning(bool),
69    UnicastFlood(bool),
70    ProxyARP(bool),
71    ProxyARPWifi(bool),
72    RootId(BridgeId),
73    BridgeId(BridgeId),
74    DesignatedPort(u16),
75    DesignatedCost(u16),
76    PortId(u16),
77    PortNumber(u16),
78    TopologyChangeAck(bool),
79    ConfigPending(bool),
80    MessageAgeTimer(u64),
81    ForwardDelayTimer(u64),
82    HoldTimer(u64),
83    Flush,
84    MulticastRouter(BridgePortMulticastRouter),
85    MulticastFlood(bool),
86    MulticastToUnicast(bool),
87    VlanTunnel(bool),
88    BroadcastFlood(bool),
89    GroupFwdMask(u16),
90    NeighSupress(bool),
91    Isolated(bool),
92    BackupPort(u32),
93    MrpRingOpen(bool),
94    MrpInOpen(bool),
95    MulticastEhtHostsLimit(u32),
96    MulticastEhtHostsCnt(u32),
97    Locked(bool),
98    Mab(bool),
99    MulticastNGroups(u32),
100    MulticastMaxGroups(u32),
101    NeighVlanSupress(bool),
102    BackupNextHopId(u32),
103    Other(DefaultNla),
104}
105
106impl Nla for InfoBridgePort {
107    fn value_len(&self) -> usize {
108        match self {
109            InfoBridgePort::Flush => 0,
110            InfoBridgePort::State(_)
111            | InfoBridgePort::HairpinMode(_)
112            | InfoBridgePort::Guard(_)
113            | InfoBridgePort::Protect(_)
114            | InfoBridgePort::FastLeave(_)
115            | InfoBridgePort::Learning(_)
116            | InfoBridgePort::UnicastFlood(_)
117            | InfoBridgePort::ProxyARP(_)
118            | InfoBridgePort::ProxyARPWifi(_)
119            | InfoBridgePort::TopologyChangeAck(_)
120            | InfoBridgePort::ConfigPending(_)
121            | InfoBridgePort::MulticastRouter(_)
122            | InfoBridgePort::MulticastFlood(_)
123            | InfoBridgePort::MulticastToUnicast(_)
124            | InfoBridgePort::VlanTunnel(_)
125            | InfoBridgePort::BroadcastFlood(_)
126            | InfoBridgePort::NeighSupress(_)
127            | InfoBridgePort::Isolated(_)
128            | InfoBridgePort::MrpRingOpen(_)
129            | InfoBridgePort::MrpInOpen(_)
130            | InfoBridgePort::Locked(_)
131            | InfoBridgePort::Mab(_)
132            | InfoBridgePort::NeighVlanSupress(_) => 1,
133            InfoBridgePort::Priority(_)
134            | InfoBridgePort::DesignatedPort(_)
135            | InfoBridgePort::DesignatedCost(_)
136            | InfoBridgePort::PortId(_)
137            | InfoBridgePort::PortNumber(_)
138            | InfoBridgePort::GroupFwdMask(_) => 2,
139            InfoBridgePort::Cost(_)
140            | InfoBridgePort::BackupPort(_)
141            | InfoBridgePort::MulticastEhtHostsLimit(_)
142            | InfoBridgePort::MulticastEhtHostsCnt(_)
143            | InfoBridgePort::MulticastNGroups(_)
144            | InfoBridgePort::MulticastMaxGroups(_)
145            | InfoBridgePort::BackupNextHopId(_) => 4,
146            InfoBridgePort::RootId(_)
147            | InfoBridgePort::BridgeId(_)
148            | InfoBridgePort::MessageAgeTimer(_)
149            | InfoBridgePort::ForwardDelayTimer(_)
150            | InfoBridgePort::HoldTimer(_) => 8,
151            InfoBridgePort::Other(nla) => nla.value_len(),
152        }
153    }
154
155    fn emit_value(&self, buffer: &mut [u8]) {
156        match self {
157            InfoBridgePort::Flush => (),
158            InfoBridgePort::HairpinMode(value)
159            | InfoBridgePort::Guard(value)
160            | InfoBridgePort::Protect(value)
161            | InfoBridgePort::FastLeave(value)
162            | InfoBridgePort::Learning(value)
163            | InfoBridgePort::UnicastFlood(value)
164            | InfoBridgePort::ProxyARP(value)
165            | InfoBridgePort::TopologyChangeAck(value)
166            | InfoBridgePort::ConfigPending(value)
167            | InfoBridgePort::ProxyARPWifi(value)
168            | InfoBridgePort::MulticastFlood(value)
169            | InfoBridgePort::MulticastToUnicast(value)
170            | InfoBridgePort::VlanTunnel(value)
171            | InfoBridgePort::BroadcastFlood(value)
172            | InfoBridgePort::NeighSupress(value)
173            | InfoBridgePort::Isolated(value)
174            | InfoBridgePort::MrpRingOpen(value)
175            | InfoBridgePort::MrpInOpen(value)
176            | InfoBridgePort::Locked(value)
177            | InfoBridgePort::Mab(value)
178            | InfoBridgePort::NeighVlanSupress(value) => {
179                buffer[0] = if *value { 1 } else { 0 }
180            }
181            InfoBridgePort::Priority(value)
182            | InfoBridgePort::DesignatedPort(value)
183            | InfoBridgePort::DesignatedCost(value)
184            | InfoBridgePort::PortId(value)
185            | InfoBridgePort::PortNumber(value)
186            | InfoBridgePort::GroupFwdMask(value) => {
187                NativeEndian::write_u16(buffer, *value)
188            }
189            InfoBridgePort::Cost(value)
190            | InfoBridgePort::BackupPort(value)
191            | InfoBridgePort::MulticastEhtHostsLimit(value)
192            | InfoBridgePort::MulticastEhtHostsCnt(value)
193            | InfoBridgePort::MulticastNGroups(value)
194            | InfoBridgePort::MulticastMaxGroups(value)
195            | InfoBridgePort::BackupNextHopId(value) => {
196                NativeEndian::write_u32(buffer, *value)
197            }
198            InfoBridgePort::MessageAgeTimer(value)
199            | InfoBridgePort::ForwardDelayTimer(value)
200            | InfoBridgePort::HoldTimer(value) => {
201                NativeEndian::write_u64(buffer, *value)
202            }
203            InfoBridgePort::RootId(bridge_id)
204            | InfoBridgePort::BridgeId(bridge_id) => bridge_id.emit(buffer),
205            InfoBridgePort::State(state) => buffer[0] = (*state).into(),
206            InfoBridgePort::MulticastRouter(mcast_router) => {
207                buffer[0] = (*mcast_router).into()
208            }
209            InfoBridgePort::Other(nla) => nla.emit_value(buffer),
210        }
211    }
212
213    fn kind(&self) -> u16 {
214        match self {
215            InfoBridgePort::State(_) => IFLA_BRPORT_STATE,
216            InfoBridgePort::Priority(_) => IFLA_BRPORT_PRIORITY,
217            InfoBridgePort::Cost(_) => IFLA_BRPORT_COST,
218            InfoBridgePort::HairpinMode(_) => IFLA_BRPORT_MODE,
219            InfoBridgePort::Guard(_) => IFLA_BRPORT_GUARD,
220            InfoBridgePort::Protect(_) => IFLA_BRPORT_PROTECT,
221            InfoBridgePort::FastLeave(_) => IFLA_BRPORT_FAST_LEAVE,
222            InfoBridgePort::Learning(_) => IFLA_BRPORT_LEARNING,
223            InfoBridgePort::UnicastFlood(_) => IFLA_BRPORT_UNICAST_FLOOD,
224            InfoBridgePort::ProxyARP(_) => IFLA_BRPORT_PROXYARP,
225            InfoBridgePort::ProxyARPWifi(_) => IFLA_BRPORT_PROXYARP_WIFI,
226            InfoBridgePort::RootId(_) => IFLA_BRPORT_ROOT_ID,
227            InfoBridgePort::BridgeId(_) => IFLA_BRPORT_BRIDGE_ID,
228            InfoBridgePort::DesignatedPort(_) => IFLA_BRPORT_DESIGNATED_PORT,
229            InfoBridgePort::DesignatedCost(_) => IFLA_BRPORT_DESIGNATED_COST,
230            InfoBridgePort::PortId(_) => IFLA_BRPORT_ID,
231            InfoBridgePort::PortNumber(_) => IFLA_BRPORT_NO,
232            InfoBridgePort::TopologyChangeAck(_) => {
233                IFLA_BRPORT_TOPOLOGY_CHANGE_ACK
234            }
235            InfoBridgePort::ConfigPending(_) => IFLA_BRPORT_CONFIG_PENDING,
236            InfoBridgePort::MessageAgeTimer(_) => IFLA_BRPORT_MESSAGE_AGE_TIMER,
237            InfoBridgePort::ForwardDelayTimer(_) => {
238                IFLA_BRPORT_FORWARD_DELAY_TIMER
239            }
240            InfoBridgePort::HoldTimer(_) => IFLA_BRPORT_HOLD_TIMER,
241            InfoBridgePort::Flush => IFLA_BRPORT_FLUSH,
242            InfoBridgePort::MulticastRouter(_) => IFLA_BRPORT_MULTICAST_ROUTER,
243            InfoBridgePort::MulticastFlood(_) => IFLA_BRPORT_MCAST_FLOOD,
244            InfoBridgePort::MulticastToUnicast(_) => IFLA_BRPORT_MCAST_TO_UCAST,
245            InfoBridgePort::VlanTunnel(_) => IFLA_BRPORT_VLAN_TUNNEL,
246            InfoBridgePort::BroadcastFlood(_) => IFLA_BRPORT_BCAST_FLOOD,
247            InfoBridgePort::GroupFwdMask(_) => IFLA_BRPORT_GROUP_FWD_MASK,
248            InfoBridgePort::NeighSupress(_) => IFLA_BRPORT_NEIGH_SUPPRESS,
249            InfoBridgePort::Isolated(_) => IFLA_BRPORT_ISOLATED,
250            InfoBridgePort::BackupPort(_) => IFLA_BRPORT_BACKUP_PORT,
251            InfoBridgePort::MrpRingOpen(_) => IFLA_BRPORT_MRP_RING_OPEN,
252            InfoBridgePort::MrpInOpen(_) => IFLA_BRPORT_MRP_IN_OPEN,
253            InfoBridgePort::MulticastEhtHostsLimit(_) => {
254                IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT
255            }
256            InfoBridgePort::MulticastEhtHostsCnt(_) => {
257                IFLA_BRPORT_MCAST_EHT_HOSTS_CNT
258            }
259            InfoBridgePort::Locked(_) => IFLA_BRPORT_LOCKED,
260            InfoBridgePort::Mab(_) => IFLA_BRPORT_MAB,
261            InfoBridgePort::MulticastNGroups(_) => IFLA_BRPORT_MCAST_N_GROUPS,
262            InfoBridgePort::MulticastMaxGroups(_) => {
263                IFLA_BRPORT_MCAST_MAX_GROUPS
264            }
265            InfoBridgePort::NeighVlanSupress(_) => {
266                IFLA_BRPORT_NEIGH_VLAN_SUPPRESS
267            }
268            InfoBridgePort::BackupNextHopId(_) => IFLA_BRPORT_BACKUP_NHID,
269            InfoBridgePort::Other(nla) => nla.kind(),
270        }
271    }
272}
273
274impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
275    for InfoBridgePort
276{
277    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
278        let payload = buf.value();
279
280        Ok(match buf.kind() {
281            IFLA_BRPORT_STATE => InfoBridgePort::State(
282                parse_u8(payload)
283                    .with_context(|| {
284                        format!("invalid IFLA_BRPORT_STATE {payload:?}")
285                    })?
286                    .into(),
287            ),
288            IFLA_BRPORT_PRIORITY => {
289                InfoBridgePort::Priority(parse_u16(payload).with_context(
290                    || format!("invalid IFLA_BRPORT_PRIORITY {payload:?}"),
291                )?)
292            }
293            IFLA_BRPORT_COST => {
294                InfoBridgePort::Cost(parse_u32(payload).with_context(|| {
295                    format!("invalid IFLA_BRPORT_COST {payload:?}")
296                })?)
297            }
298            IFLA_BRPORT_MODE => InfoBridgePort::HairpinMode(
299                parse_u8(payload).with_context(|| {
300                    format!("invalid IFLA_BRPORT_MODE {payload:?}")
301                })? > 0,
302            ),
303            IFLA_BRPORT_GUARD => InfoBridgePort::Guard(
304                parse_u8(payload).with_context(|| {
305                    format!("invalid IFLA_BRPORT_GUARD {payload:?}")
306                })? > 0,
307            ),
308            IFLA_BRPORT_PROTECT => InfoBridgePort::Protect(
309                parse_u8(payload).with_context(|| {
310                    format!("invalid IFLA_BRPORT_PROTECT {payload:?}")
311                })? > 0,
312            ),
313            IFLA_BRPORT_FAST_LEAVE => InfoBridgePort::FastLeave(
314                parse_u8(payload).with_context(|| {
315                    format!(
316                        "invalid IFLA_BRPORT_FAST_LEAVE {payload:?}"
317                    )
318                })? > 0,
319            ),
320            IFLA_BRPORT_LEARNING => InfoBridgePort::Learning(
321                parse_u8(payload).with_context(|| {
322                    format!("invalid IFLA_BRPORT_LEARNING {payload:?}")
323                })? > 0,
324            ),
325            IFLA_BRPORT_UNICAST_FLOOD => InfoBridgePort::UnicastFlood(
326                parse_u8(payload).with_context(|| {
327                    format!("invalid IFLA_BRPORT_UNICAST_FLOOD {payload:?}")
328                })? > 0,
329            ),
330            IFLA_BRPORT_PROXYARP => InfoBridgePort::ProxyARP(
331                parse_u8(payload).with_context(|| {
332                    format!("invalid IFLA_BRPORT_PROXYARP {payload:?}")
333                })? > 0,
334            ),
335            IFLA_BRPORT_PROXYARP_WIFI => InfoBridgePort::ProxyARPWifi(
336                parse_u8(payload).with_context(|| {
337                    format!("invalid IFLA_BRPORT_PROXYARP_WIFI {payload:?}")
338                })? > 0,
339            ),
340            IFLA_BRPORT_ROOT_ID => Self::RootId(
341                BridgeId::parse(&BridgeIdBuffer::new(payload)).with_context(|| {
342                    format!("invalid IFLA_BRPORT_ROOT_ID {payload:?}")
343                })?,
344            ),
345            IFLA_BRPORT_BRIDGE_ID => Self::BridgeId(
346                BridgeId::parse(&BridgeIdBuffer::new(payload)).with_context(|| {
347                    format!("invalid IFLA_BRPORT_BRIDGE_ID {payload:?}")
348                })?,
349            ),
350            IFLA_BRPORT_DESIGNATED_PORT => InfoBridgePort::DesignatedPort(
351                parse_u16(payload).with_context(|| {
352                    format!("invalid IFLA_BRPORT_DESIGNATED_PORT {payload:?}")
353                })?,
354            ),
355            IFLA_BRPORT_DESIGNATED_COST => InfoBridgePort::DesignatedCost(
356                parse_u16(payload).with_context(|| {
357                    format!("invalid IFLA_BRPORT_DESIGNATED_COST {payload:?}")
358                })?,
359            ),
360            IFLA_BRPORT_ID => {
361                InfoBridgePort::PortId(parse_u16(payload).with_context(|| {
362                    format!("invalid IFLA_BRPORT_ID {payload:?}")
363                })?)
364            }
365            IFLA_BRPORT_NO => {
366                InfoBridgePort::PortNumber(parse_u16(payload).with_context(|| {
367                    format!("invalid IFLA_BRPORT_NO {payload:?}")
368                })?)
369            }
370            IFLA_BRPORT_TOPOLOGY_CHANGE_ACK => {
371                InfoBridgePort::TopologyChangeAck(
372                    parse_u8(payload).with_context(|| {
373                        format!(
374                            "invalid IFLA_BRPORT_TOPOLOGY_CHANGE_ACK {payload:?}"
375                        )
376                    })? > 0,
377                )
378            }
379            IFLA_BRPORT_CONFIG_PENDING => InfoBridgePort::ConfigPending(
380                parse_u8(payload).with_context(|| {
381                    format!("invalid IFLA_BRPORT_CONFIG_PENDING {payload:?}")
382                })? > 0,
383            ),
384            IFLA_BRPORT_MESSAGE_AGE_TIMER => InfoBridgePort::MessageAgeTimer(
385                parse_u64(payload).with_context(|| {
386                    format!("invalid IFLA_BRPORT_MESSAGE_AGE_TIMER {payload:?}")
387                })?,
388            ),
389            IFLA_BRPORT_FORWARD_DELAY_TIMER => {
390                InfoBridgePort::ForwardDelayTimer(
391                    parse_u64(payload).with_context(|| {
392                        format!(
393                            "invalid IFLA_BRPORT_FORWARD_DELAY_TIMER {payload:?}"
394                        )
395                    })?,
396                )
397            }
398            IFLA_BRPORT_HOLD_TIMER => {
399                InfoBridgePort::HoldTimer(parse_u64(payload).with_context(
400                    || format!("invalid IFLA_BRPORT_HOLD_TIMER {payload:?}"),
401                )?)
402            }
403            IFLA_BRPORT_FLUSH => InfoBridgePort::Flush,
404            IFLA_BRPORT_MULTICAST_ROUTER => InfoBridgePort::MulticastRouter(
405                parse_u8(payload)
406                    .with_context(|| {
407                        format!(
408                            "invalid IFLA_BRPORT_MULTICAST_ROUTER {payload:?}"
409                        )
410                    })?
411                    .into(),
412            ),
413            IFLA_BRPORT_MCAST_FLOOD => InfoBridgePort::MulticastFlood(
414                parse_u8(payload).with_context(|| {
415                    format!("invalid IFLA_BRPORT_MCAST_FLOOD {payload:?}")
416                })? > 0,
417            ),
418            IFLA_BRPORT_MCAST_TO_UCAST => InfoBridgePort::MulticastToUnicast(
419                parse_u8(payload).with_context(|| {
420                    format!("invalid IFLA_BRPORT_MCAST_TO_UCAST {payload:?}")
421                })? > 0,
422            ),
423            IFLA_BRPORT_VLAN_TUNNEL => InfoBridgePort::VlanTunnel(
424                parse_u8(payload).with_context(|| {
425                    format!("invalid IFLA_BRPORT_VLAN_TUNNEL {payload:?}")
426                })? > 0,
427            ),
428            IFLA_BRPORT_BCAST_FLOOD => InfoBridgePort::BroadcastFlood(
429                parse_u8(payload).with_context(|| {
430                    format!("invalid IFLA_BRPORT_BCAST_FLOOD {payload:?}")
431                })? > 0,
432            ),
433            IFLA_BRPORT_GROUP_FWD_MASK => InfoBridgePort::GroupFwdMask(
434                parse_u16(payload).with_context(|| {
435                    format!("invalid IFLA_BRPORT_GROUP_FWD_MASK {payload:?}")
436                })?,
437            ),
438            IFLA_BRPORT_NEIGH_SUPPRESS => InfoBridgePort::NeighSupress(
439                parse_u8(payload).with_context(|| {
440                    format!("invalid IFLA_BRPORT_NEIGH_SUPPRESS {payload:?}")
441                })? > 0,
442            ),
443            IFLA_BRPORT_ISOLATED => InfoBridgePort::Isolated(
444                parse_u8(payload).with_context(|| {
445                    format!("invalid IFLA_BRPORT_ISOLATED {payload:?}")
446                })? > 0,
447            ),
448            IFLA_BRPORT_BACKUP_PORT => {
449                InfoBridgePort::BackupPort(parse_u32(payload).with_context(
450                    || format!("invalid IFLA_BRPORT_BACKUP_PORT {payload:?}"),
451                )?)
452            }
453            IFLA_BRPORT_MRP_RING_OPEN => InfoBridgePort::MrpRingOpen(
454                parse_u8(payload).with_context(|| {
455                    format!("invalid IFLA_BRPORT_MRP_RING_OPEN {payload:?}")
456                })? > 0,
457            ),
458            IFLA_BRPORT_MRP_IN_OPEN => InfoBridgePort::MrpInOpen(
459                parse_u8(payload).with_context(|| {
460                    format!("invalid IFLA_BRPORT_MRP_IN_OPEN {payload:?}")
461                })? > 0,
462            ),
463            IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT => {
464                InfoBridgePort::MulticastEhtHostsLimit(
465                    parse_u32(payload).with_context(|| {
466                        format!(
467                            "invalid IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT {payload:?}"
468                        )
469                    })?,
470                )
471            }
472            IFLA_BRPORT_MCAST_EHT_HOSTS_CNT => {
473                InfoBridgePort::MulticastEhtHostsCnt(
474                    parse_u32(payload).with_context(|| {
475                        format!(
476                            "invalid IFLA_BRPORT_MCAST_EHT_HOSTS_CNT {payload:?}"
477                        )
478                    })?
479                )
480            }
481            IFLA_BRPORT_LOCKED => InfoBridgePort::Locked(
482                parse_u8(payload).with_context(|| {
483                    format!("invalid IFLA_BRPORT_LOCKED {payload:?}")
484                })? > 0,
485            ),
486            IFLA_BRPORT_MAB => InfoBridgePort::Mab(
487                parse_u8(payload).with_context(|| {
488                    format!("invalid IFLA_BRPORT_MAB {payload:?}")
489                })? > 0,
490            ),
491            IFLA_BRPORT_MCAST_N_GROUPS => InfoBridgePort::MulticastNGroups(
492                parse_u32(payload).with_context(|| {
493                    format!("invalid IFLA_BRPORT_MCAST_N_GROUPS {payload:?}")
494                })?,
495            ),
496            IFLA_BRPORT_MCAST_MAX_GROUPS => InfoBridgePort::MulticastMaxGroups(
497                parse_u32(payload).with_context(|| {
498                    format!("invalid IFLA_BRPORT_MCAST_MAX_GROUPS {payload:?}")
499                })?,
500            ),
501            IFLA_BRPORT_NEIGH_VLAN_SUPPRESS => InfoBridgePort::NeighVlanSupress(
502                parse_u8(payload).with_context(|| {
503                    format!(
504                        "invalid IFLA_BRPORT_NEIGH_VLAN_SUPPRESS {payload:?}"
505                    )
506                })? > 0,
507            ),
508            IFLA_BRPORT_BACKUP_NHID => InfoBridgePort::BackupNextHopId(
509                parse_u32(payload).with_context(|| {
510                    format!("invalid IFLA_BRPORT_BACKUP_NHID {payload:?}")
511                })?,
512            ),
513            kind => InfoBridgePort::Other(
514                DefaultNla::parse(buf).with_context(|| {
515                    format!(
516                        "failed to parse bridge port NLA of type '{kind}' into DefaultNla"
517                    )
518                })?,
519            ),
520        })
521    }
522}
523
524const BR_STATE_DISABLED: u8 = 0;
525const BR_STATE_LISTENING: u8 = 1;
526const BR_STATE_LEARNING: u8 = 2;
527const BR_STATE_FORWARDING: u8 = 3;
528const BR_STATE_BLOCKING: u8 = 4;
529
530#[derive(Debug, Clone, Copy, Eq, PartialEq)]
531#[non_exhaustive]
532pub enum BridgePortState {
533    Disabled,
534    Listening,
535    Learning,
536    Forwarding,
537    Blocking,
538    Other(u8),
539}
540
541impl From<u8> for BridgePortState {
542    fn from(value: u8) -> Self {
543        match value {
544            BR_STATE_DISABLED => BridgePortState::Disabled,
545            BR_STATE_LISTENING => BridgePortState::Listening,
546            BR_STATE_LEARNING => BridgePortState::Learning,
547            BR_STATE_FORWARDING => BridgePortState::Forwarding,
548            BR_STATE_BLOCKING => BridgePortState::Blocking,
549            _ => BridgePortState::Other(value),
550        }
551    }
552}
553
554impl From<BridgePortState> for u8 {
555    fn from(value: BridgePortState) -> Self {
556        match value {
557            BridgePortState::Disabled => BR_STATE_DISABLED,
558            BridgePortState::Listening => BR_STATE_LISTENING,
559            BridgePortState::Learning => BR_STATE_LEARNING,
560            BridgePortState::Forwarding => BR_STATE_FORWARDING,
561            BridgePortState::Blocking => BR_STATE_BLOCKING,
562            BridgePortState::Other(v) => v,
563        }
564    }
565}
566
567const MDB_RTR_TYPE_DISABLED: u8 = 0;
568const MDB_RTR_TYPE_TEMP_QUERY: u8 = 1;
569const MDB_RTR_TYPE_PERM: u8 = 2;
570const MDB_RTR_TYPE_TEMP: u8 = 3;
571
572#[derive(Debug, Clone, Copy, Eq, PartialEq)]
573#[non_exhaustive]
574pub enum BridgePortMulticastRouter {
575    Disabled,
576    TempQuery,
577    Perm,
578    Temp,
579    Other(u8),
580}
581
582impl From<u8> for BridgePortMulticastRouter {
583    fn from(value: u8) -> Self {
584        match value {
585            MDB_RTR_TYPE_DISABLED => BridgePortMulticastRouter::Disabled,
586            MDB_RTR_TYPE_TEMP_QUERY => BridgePortMulticastRouter::TempQuery,
587            MDB_RTR_TYPE_PERM => BridgePortMulticastRouter::Perm,
588            MDB_RTR_TYPE_TEMP => BridgePortMulticastRouter::Temp,
589            _ => BridgePortMulticastRouter::Other(value),
590        }
591    }
592}
593
594impl From<BridgePortMulticastRouter> for u8 {
595    fn from(value: BridgePortMulticastRouter) -> Self {
596        match value {
597            BridgePortMulticastRouter::Disabled => MDB_RTR_TYPE_DISABLED,
598            BridgePortMulticastRouter::TempQuery => MDB_RTR_TYPE_TEMP_QUERY,
599            BridgePortMulticastRouter::Perm => MDB_RTR_TYPE_PERM,
600            BridgePortMulticastRouter::Temp => MDB_RTR_TYPE_TEMP,
601            BridgePortMulticastRouter::Other(v) => v,
602        }
603    }
604}