netlink_packet_route/link/link_info/
bridge_port.rs

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