netlink_packet_route/link/link_info/
bridge.rs

1// SPDX-License-Identifier: MIT
2
3use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4
5use netlink_packet_core::{
6    emit_u16, emit_u16_be, emit_u32, emit_u64, parse_ip, parse_mac, parse_u16,
7    parse_u16_be, parse_u32, parse_u64, parse_u8, DecodeError, DefaultNla,
8    Emitable, ErrorContext, Nla, NlaBuffer, NlasIterator, Parseable,
9    NLA_F_NESTED,
10};
11
12use crate::link::{BridgeBooleanOptions, VlanProtocol};
13
14const IFLA_BR_FORWARD_DELAY: u16 = 1;
15const IFLA_BR_HELLO_TIME: u16 = 2;
16const IFLA_BR_MAX_AGE: u16 = 3;
17const IFLA_BR_AGEING_TIME: u16 = 4;
18const IFLA_BR_STP_STATE: u16 = 5;
19const IFLA_BR_PRIORITY: u16 = 6;
20const IFLA_BR_VLAN_FILTERING: u16 = 7;
21const IFLA_BR_VLAN_PROTOCOL: u16 = 8;
22const IFLA_BR_GROUP_FWD_MASK: u16 = 9;
23const IFLA_BR_ROOT_ID: u16 = 10;
24const IFLA_BR_BRIDGE_ID: u16 = 11;
25const IFLA_BR_ROOT_PORT: u16 = 12;
26const IFLA_BR_ROOT_PATH_COST: u16 = 13;
27const IFLA_BR_TOPOLOGY_CHANGE: u16 = 14;
28const IFLA_BR_TOPOLOGY_CHANGE_DETECTED: u16 = 15;
29const IFLA_BR_HELLO_TIMER: u16 = 16;
30const IFLA_BR_TCN_TIMER: u16 = 17;
31const IFLA_BR_TOPOLOGY_CHANGE_TIMER: u16 = 18;
32const IFLA_BR_GC_TIMER: u16 = 19;
33const IFLA_BR_GROUP_ADDR: u16 = 20;
34const IFLA_BR_FDB_FLUSH: u16 = 21;
35const IFLA_BR_MCAST_ROUTER: u16 = 22;
36const IFLA_BR_MCAST_SNOOPING: u16 = 23;
37const IFLA_BR_MCAST_QUERY_USE_IFADDR: u16 = 24;
38const IFLA_BR_MCAST_QUERIER: u16 = 25;
39const IFLA_BR_MCAST_HASH_ELASTICITY: u16 = 26;
40const IFLA_BR_MCAST_HASH_MAX: u16 = 27;
41const IFLA_BR_MCAST_LAST_MEMBER_CNT: u16 = 28;
42const IFLA_BR_MCAST_STARTUP_QUERY_CNT: u16 = 29;
43const IFLA_BR_MCAST_LAST_MEMBER_INTVL: u16 = 30;
44const IFLA_BR_MCAST_MEMBERSHIP_INTVL: u16 = 31;
45const IFLA_BR_MCAST_QUERIER_INTVL: u16 = 32;
46const IFLA_BR_MCAST_QUERY_INTVL: u16 = 33;
47const IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: u16 = 34;
48const IFLA_BR_MCAST_STARTUP_QUERY_INTVL: u16 = 35;
49const IFLA_BR_NF_CALL_IPTABLES: u16 = 36;
50const IFLA_BR_NF_CALL_IP6TABLES: u16 = 37;
51const IFLA_BR_NF_CALL_ARPTABLES: u16 = 38;
52const IFLA_BR_VLAN_DEFAULT_PVID: u16 = 39;
53// const IFLA_BR_PAD: u16 = 40;
54const IFLA_BR_VLAN_STATS_ENABLED: u16 = 41;
55const IFLA_BR_MCAST_STATS_ENABLED: u16 = 42;
56const IFLA_BR_MCAST_IGMP_VERSION: u16 = 43;
57const IFLA_BR_MCAST_MLD_VERSION: u16 = 44;
58const IFLA_BR_VLAN_STATS_PER_PORT: u16 = 45;
59const IFLA_BR_MULTI_BOOLOPT: u16 = 46;
60const IFLA_BR_MCAST_QUERIER_STATE: u16 = 47;
61const IFLA_BR_FDB_N_LEARNED: u16 = 48;
62const IFLA_BR_FDB_MAX_LEARNED: u16 = 49;
63
64#[derive(Debug, PartialEq, Eq, Clone)]
65#[non_exhaustive]
66pub enum InfoBridge {
67    GroupAddr([u8; 6]),
68    FdbFlush,
69    HelloTimer(u64),
70    TcnTimer(u64),
71    TopologyChangeTimer(u64),
72    GcTimer(u64),
73    MulticastMembershipInterval(u64),
74    MulticastQuerierInterval(u64),
75    MulticastQueryInterval(u64),
76    MulticastQueryResponseInterval(u64),
77    MulticastLastMemberInterval(u64),
78    MulticastStartupQueryInterval(u64),
79    ForwardDelay(u32),
80    HelloTime(u32),
81    MaxAge(u32),
82    AgeingTime(u32),
83    StpState(BridgeStpState),
84    MulticastHashElasticity(u32),
85    MulticastHashMax(u32),
86    MulticastLastMemberCount(u32),
87    MulticastStartupQueryCount(u32),
88    RootPathCost(u32),
89    Priority(u16),
90    VlanProtocol(VlanProtocol),
91    GroupFwdMask(u16),
92    RootId(BridgeId),
93    BridgeId(BridgeId),
94    RootPort(u16),
95    VlanDefaultPvid(u16),
96    VlanFiltering(bool),
97    TopologyChange(u8),
98    TopologyChangeDetected(u8),
99    MulticastRouter(BridgeMulticastRouterType),
100    MulticastSnooping(bool),
101    MulticastQueryUseIfaddr(bool),
102    MulticastQuerier(bool),
103    NfCallIpTables(bool),
104    NfCallIp6Tables(bool),
105    NfCallArpTables(bool),
106    VlanStatsEnabled(bool),
107    MulticastStatsEnabled(bool),
108    MulticastIgmpVersion(u8),
109    MulticastMldVersion(u8),
110    VlanStatsPerPort(bool),
111    MultiBoolOpt(BridgeBooleanOptions),
112    MulticastQuerierState(Vec<BridgeQuerierState>),
113    FdbNLearned(u32),
114    FdbMaxLearned(u32),
115    Other(DefaultNla),
116}
117
118impl Nla for InfoBridge {
119    fn value_len(&self) -> usize {
120        match self {
121            // The existance of FdbFlush means true
122            Self::FdbFlush => 0,
123            Self::HelloTimer(_)
124            | Self::TcnTimer(_)
125            | Self::TopologyChangeTimer(_)
126            | Self::GcTimer(_)
127            | Self::MulticastMembershipInterval(_)
128            | Self::MulticastQuerierInterval(_)
129            | Self::MulticastQueryInterval(_)
130            | Self::MulticastQueryResponseInterval(_)
131            | Self::MulticastLastMemberInterval(_)
132            | Self::MulticastStartupQueryInterval(_) => 8,
133            Self::ForwardDelay(_)
134            | Self::HelloTime(_)
135            | Self::MaxAge(_)
136            | Self::AgeingTime(_)
137            | Self::MulticastHashElasticity(_)
138            | Self::MulticastHashMax(_)
139            | Self::MulticastLastMemberCount(_)
140            | Self::MulticastStartupQueryCount(_)
141            | Self::RootPathCost(_)
142            | Self::FdbNLearned(_)
143            | Self::FdbMaxLearned(_) => 4,
144            Self::Priority(_)
145            | Self::GroupFwdMask(_)
146            | Self::RootPort(_)
147            | Self::VlanDefaultPvid(_) => 2,
148
149            Self::VlanProtocol(_) => 2,
150
151            Self::StpState(_) => 4,
152
153            Self::RootId(_) | Self::BridgeId(_) => 8,
154
155            Self::MultiBoolOpt(v) => v.buffer_len(),
156
157            Self::GroupAddr(_) => 6,
158
159            Self::VlanFiltering(_) => 1,
160            Self::MulticastRouter(_) => 1,
161            Self::TopologyChange(_)
162            | Self::TopologyChangeDetected(_)
163            | Self::MulticastIgmpVersion(_)
164            | Self::MulticastMldVersion(_) => 1,
165
166            Self::MulticastSnooping(_)
167            | Self::NfCallIpTables(_)
168            | Self::NfCallIp6Tables(_)
169            | Self::NfCallArpTables(_)
170            | Self::MulticastStatsEnabled(_)
171            | Self::MulticastQuerier(_)
172            | Self::MulticastQueryUseIfaddr(_)
173            | Self::VlanStatsPerPort(_)
174            | Self::VlanStatsEnabled(_) => 1,
175
176            Self::MulticastQuerierState(nlas) => nlas.as_slice().buffer_len(),
177
178            Self::Other(nla) => nla.value_len(),
179        }
180    }
181
182    fn emit_value(&self, buffer: &mut [u8]) {
183        match self {
184            Self::FdbFlush => (),
185
186            Self::HelloTimer(value)
187            | Self::TcnTimer(value)
188            | Self::TopologyChangeTimer(value)
189            | Self::GcTimer(value)
190            | Self::MulticastMembershipInterval(value)
191            | Self::MulticastQuerierInterval(value)
192            | Self::MulticastQueryInterval(value)
193            | Self::MulticastQueryResponseInterval(value)
194            | Self::MulticastLastMemberInterval(value)
195            | Self::MulticastStartupQueryInterval(value) => {
196                emit_u64(buffer, *value).unwrap()
197            }
198
199            Self::MultiBoolOpt(value) => value.emit(buffer),
200
201            Self::ForwardDelay(value)
202            | Self::HelloTime(value)
203            | Self::MaxAge(value)
204            | Self::AgeingTime(value)
205            | Self::MulticastHashElasticity(value)
206            | Self::MulticastHashMax(value)
207            | Self::MulticastLastMemberCount(value)
208            | Self::MulticastStartupQueryCount(value)
209            | Self::FdbNLearned(value)
210            | Self::FdbMaxLearned(value)
211            | Self::RootPathCost(value) => emit_u32(buffer, *value).unwrap(),
212
213            Self::StpState(value) => {
214                emit_u32(buffer, u32::from(*value)).unwrap()
215            }
216
217            Self::Priority(value)
218            | Self::GroupFwdMask(value)
219            | Self::RootPort(value)
220            | Self::VlanDefaultPvid(value) => emit_u16(buffer, *value).unwrap(),
221
222            Self::VlanProtocol(value) => {
223                emit_u16_be(buffer, (*value).into()).unwrap()
224            }
225
226            Self::RootId(bridge_id) | Self::BridgeId(bridge_id) => {
227                bridge_id.emit(buffer)
228            }
229
230            Self::GroupAddr(value) => buffer.copy_from_slice(&value[..]),
231
232            Self::VlanFiltering(value) => buffer[0] = (*value).into(),
233            Self::TopologyChange(value)
234            | Self::TopologyChangeDetected(value)
235            | Self::MulticastIgmpVersion(value)
236            | Self::MulticastMldVersion(value) => buffer[0] = *value,
237
238            Self::MulticastRouter(value) => buffer[0] = (*value).into(),
239
240            Self::MulticastSnooping(value)
241            | Self::NfCallIpTables(value)
242            | Self::NfCallIp6Tables(value)
243            | Self::NfCallArpTables(value)
244            | Self::MulticastStatsEnabled(value)
245            | Self::MulticastQuerier(value)
246            | Self::MulticastQueryUseIfaddr(value)
247            | Self::VlanStatsPerPort(value)
248            | Self::VlanStatsEnabled(value) => buffer[0] = (*value).into(),
249
250            Self::MulticastQuerierState(nlas) => nlas.as_slice().emit(buffer),
251
252            Self::Other(nla) => nla.emit_value(buffer),
253        }
254    }
255
256    fn kind(&self) -> u16 {
257        match self {
258            Self::GroupAddr(_) => IFLA_BR_GROUP_ADDR,
259            Self::FdbFlush => IFLA_BR_FDB_FLUSH,
260            Self::HelloTimer(_) => IFLA_BR_HELLO_TIMER,
261            Self::TcnTimer(_) => IFLA_BR_TCN_TIMER,
262            Self::TopologyChangeTimer(_) => IFLA_BR_TOPOLOGY_CHANGE_TIMER,
263            Self::GcTimer(_) => IFLA_BR_GC_TIMER,
264            Self::MulticastMembershipInterval(_) => {
265                IFLA_BR_MCAST_MEMBERSHIP_INTVL
266            }
267            Self::MulticastQuerierInterval(_) => IFLA_BR_MCAST_QUERIER_INTVL,
268            Self::MulticastQueryInterval(_) => IFLA_BR_MCAST_QUERY_INTVL,
269            Self::MulticastQueryResponseInterval(_) => {
270                IFLA_BR_MCAST_QUERY_RESPONSE_INTVL
271            }
272            Self::ForwardDelay(_) => IFLA_BR_FORWARD_DELAY,
273            Self::HelloTime(_) => IFLA_BR_HELLO_TIME,
274            Self::MaxAge(_) => IFLA_BR_MAX_AGE,
275            Self::AgeingTime(_) => IFLA_BR_AGEING_TIME,
276            Self::StpState(_) => IFLA_BR_STP_STATE,
277            Self::MulticastHashElasticity(_) => IFLA_BR_MCAST_HASH_ELASTICITY,
278            Self::MulticastHashMax(_) => IFLA_BR_MCAST_HASH_MAX,
279            Self::MulticastLastMemberCount(_) => IFLA_BR_MCAST_LAST_MEMBER_CNT,
280            Self::MulticastStartupQueryCount(_) => {
281                IFLA_BR_MCAST_STARTUP_QUERY_CNT
282            }
283            Self::MulticastLastMemberInterval(_) => {
284                IFLA_BR_MCAST_LAST_MEMBER_INTVL
285            }
286            Self::MulticastStartupQueryInterval(_) => {
287                IFLA_BR_MCAST_STARTUP_QUERY_INTVL
288            }
289            Self::RootPathCost(_) => IFLA_BR_ROOT_PATH_COST,
290            Self::Priority(_) => IFLA_BR_PRIORITY,
291            Self::VlanProtocol(_) => IFLA_BR_VLAN_PROTOCOL,
292            Self::GroupFwdMask(_) => IFLA_BR_GROUP_FWD_MASK,
293            Self::RootId(_) => IFLA_BR_ROOT_ID,
294            Self::BridgeId(_) => IFLA_BR_BRIDGE_ID,
295            Self::RootPort(_) => IFLA_BR_ROOT_PORT,
296            Self::VlanDefaultPvid(_) => IFLA_BR_VLAN_DEFAULT_PVID,
297            Self::VlanFiltering(_) => IFLA_BR_VLAN_FILTERING,
298            Self::TopologyChange(_) => IFLA_BR_TOPOLOGY_CHANGE,
299            Self::TopologyChangeDetected(_) => IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
300            Self::MulticastRouter(_) => IFLA_BR_MCAST_ROUTER,
301            Self::MulticastSnooping(_) => IFLA_BR_MCAST_SNOOPING,
302            Self::MulticastQueryUseIfaddr(_) => IFLA_BR_MCAST_QUERY_USE_IFADDR,
303            Self::MulticastQuerier(_) => IFLA_BR_MCAST_QUERIER,
304            Self::NfCallIpTables(_) => IFLA_BR_NF_CALL_IPTABLES,
305            Self::NfCallIp6Tables(_) => IFLA_BR_NF_CALL_IP6TABLES,
306            Self::NfCallArpTables(_) => IFLA_BR_NF_CALL_ARPTABLES,
307            Self::VlanStatsEnabled(_) => IFLA_BR_VLAN_STATS_ENABLED,
308            Self::MulticastStatsEnabled(_) => IFLA_BR_MCAST_STATS_ENABLED,
309            Self::MulticastIgmpVersion(_) => IFLA_BR_MCAST_IGMP_VERSION,
310            Self::MulticastMldVersion(_) => IFLA_BR_MCAST_MLD_VERSION,
311            Self::VlanStatsPerPort(_) => IFLA_BR_VLAN_STATS_PER_PORT,
312            Self::MultiBoolOpt(_) => IFLA_BR_MULTI_BOOLOPT,
313            Self::MulticastQuerierState(_) => {
314                IFLA_BR_MCAST_QUERIER_STATE | NLA_F_NESTED
315            }
316            Self::FdbNLearned(_) => IFLA_BR_FDB_N_LEARNED,
317            Self::FdbMaxLearned(_) => IFLA_BR_FDB_MAX_LEARNED,
318            Self::Other(nla) => nla.kind(),
319        }
320    }
321}
322
323impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for InfoBridge {
324    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
325        let payload = buf.value();
326        Ok(match buf.kind() {
327            IFLA_BR_FDB_FLUSH => Self::FdbFlush,
328            IFLA_BR_HELLO_TIMER => Self::HelloTimer(
329                parse_u64(payload)
330                    .context("invalid IFLA_BR_HELLO_TIMER value")?,
331            ),
332            IFLA_BR_TCN_TIMER => Self::TcnTimer(
333                parse_u64(payload)
334                    .context("invalid IFLA_BR_TCN_TIMER value")?,
335            ),
336            IFLA_BR_TOPOLOGY_CHANGE_TIMER => Self::TopologyChangeTimer(
337                parse_u64(payload)
338                    .context("invalid IFLA_BR_TOPOLOGY_CHANGE_TIMER value")?,
339            ),
340            IFLA_BR_GC_TIMER => Self::GcTimer(
341                parse_u64(payload).context("invalid IFLA_BR_GC_TIMER value")?,
342            ),
343            IFLA_BR_MCAST_LAST_MEMBER_INTVL => {
344                Self::MulticastLastMemberInterval(
345                    parse_u64(payload).context(
346                        "invalid IFLA_BR_MCAST_LAST_MEMBER_INTVL value",
347                    )?,
348                )
349            }
350            IFLA_BR_MCAST_MEMBERSHIP_INTVL => {
351                Self::MulticastMembershipInterval(
352                    parse_u64(payload).context(
353                        "invalid IFLA_BR_MCAST_MEMBERSHIP_INTVL value",
354                    )?,
355                )
356            }
357            IFLA_BR_MCAST_QUERIER_INTVL => Self::MulticastQuerierInterval(
358                parse_u64(payload)
359                    .context("invalid IFLA_BR_MCAST_QUERIER_INTVL value")?,
360            ),
361            IFLA_BR_MCAST_QUERY_INTVL => Self::MulticastQueryInterval(
362                parse_u64(payload)
363                    .context("invalid IFLA_BR_MCAST_QUERY_INTVL value")?,
364            ),
365            IFLA_BR_MCAST_QUERY_RESPONSE_INTVL => {
366                Self::MulticastQueryResponseInterval(
367                    parse_u64(payload).context(
368                        "invalid IFLA_BR_MCAST_QUERY_RESPONSE_INTVL value",
369                    )?,
370                )
371            }
372            IFLA_BR_MCAST_STARTUP_QUERY_INTVL => {
373                Self::MulticastStartupQueryInterval(
374                    parse_u64(payload).context(
375                        "invalid IFLA_BR_MCAST_STARTUP_QUERY_INTVL value",
376                    )?,
377                )
378            }
379            IFLA_BR_FORWARD_DELAY => Self::ForwardDelay(
380                parse_u32(payload)
381                    .context("invalid IFLA_BR_FORWARD_DELAY value")?,
382            ),
383            IFLA_BR_HELLO_TIME => Self::HelloTime(
384                parse_u32(payload)
385                    .context("invalid IFLA_BR_HELLO_TIME value")?,
386            ),
387            IFLA_BR_MAX_AGE => Self::MaxAge(
388                parse_u32(payload).context("invalid IFLA_BR_MAX_AGE value")?,
389            ),
390            IFLA_BR_AGEING_TIME => Self::AgeingTime(
391                parse_u32(payload)
392                    .context("invalid IFLA_BR_AGEING_TIME value")?,
393            ),
394            IFLA_BR_STP_STATE => Self::StpState(
395                parse_u32(payload)
396                    .context("invalid IFLA_BR_STP_STATE value")?
397                    .into(),
398            ),
399            IFLA_BR_MCAST_HASH_ELASTICITY => Self::MulticastHashElasticity(
400                parse_u32(payload)
401                    .context("invalid IFLA_BR_MCAST_HASH_ELASTICITY value")?,
402            ),
403            IFLA_BR_MCAST_HASH_MAX => Self::MulticastHashMax(
404                parse_u32(payload)
405                    .context("invalid IFLA_BR_MCAST_HASH_MAX value")?,
406            ),
407            IFLA_BR_MCAST_LAST_MEMBER_CNT => Self::MulticastLastMemberCount(
408                parse_u32(payload)
409                    .context("invalid IFLA_BR_MCAST_LAST_MEMBER_CNT value")?,
410            ),
411            IFLA_BR_MCAST_STARTUP_QUERY_CNT => {
412                Self::MulticastStartupQueryCount(
413                    parse_u32(payload).context(
414                        "invalid IFLA_BR_MCAST_STARTUP_QUERY_CNT value",
415                    )?,
416                )
417            }
418            IFLA_BR_ROOT_PATH_COST => Self::RootPathCost(
419                parse_u32(payload)
420                    .context("invalid IFLA_BR_ROOT_PATH_COST value")?,
421            ),
422            IFLA_BR_PRIORITY => Self::Priority(
423                parse_u16(payload).context("invalid IFLA_BR_PRIORITY value")?,
424            ),
425            IFLA_BR_VLAN_PROTOCOL => Self::VlanProtocol(
426                parse_u16_be(payload)
427                    .context("invalid IFLA_BR_VLAN_PROTOCOL value")?
428                    .into(),
429            ),
430            IFLA_BR_GROUP_FWD_MASK => Self::GroupFwdMask(
431                parse_u16(payload)
432                    .context("invalid IFLA_BR_GROUP_FWD_MASK value")?,
433            ),
434            IFLA_BR_ROOT_ID => Self::RootId(
435                BridgeId::parse(&BridgeIdBuffer::new(payload))
436                    .context("invalid IFLA_BR_ROOT_ID value")?,
437            ),
438            IFLA_BR_BRIDGE_ID => Self::BridgeId(
439                BridgeId::parse(&BridgeIdBuffer::new(payload))
440                    .context("invalid IFLA_BR_BRIDGE_ID value")?,
441            ),
442            IFLA_BR_GROUP_ADDR => Self::GroupAddr(
443                parse_mac(payload)
444                    .context("invalid IFLA_BR_GROUP_ADDR value")?,
445            ),
446            IFLA_BR_ROOT_PORT => Self::RootPort(
447                parse_u16(payload)
448                    .context("invalid IFLA_BR_ROOT_PORT value")?,
449            ),
450            IFLA_BR_VLAN_DEFAULT_PVID => Self::VlanDefaultPvid(
451                parse_u16(payload)
452                    .context("invalid IFLA_BR_VLAN_DEFAULT_PVID value")?,
453            ),
454            IFLA_BR_VLAN_FILTERING => Self::VlanFiltering(
455                parse_u8(payload)
456                    .context("invalid IFLA_BR_VLAN_FILTERING value")?
457                    > 0,
458            ),
459            IFLA_BR_TOPOLOGY_CHANGE => Self::TopologyChange(
460                parse_u8(payload)
461                    .context("invalid IFLA_BR_TOPOLOGY_CHANGE value")?,
462            ),
463            IFLA_BR_TOPOLOGY_CHANGE_DETECTED => {
464                Self::TopologyChangeDetected(parse_u8(payload).context(
465                    "invalid IFLA_BR_TOPOLOGY_CHANGE_DETECTED value",
466                )?)
467            }
468            IFLA_BR_MCAST_ROUTER => Self::MulticastRouter(
469                parse_u8(payload)
470                    .context("invalid IFLA_BR_MCAST_ROUTER value")?
471                    .into(),
472            ),
473            IFLA_BR_MCAST_SNOOPING => Self::MulticastSnooping(
474                parse_u8(payload)
475                    .context("invalid IFLA_BR_MCAST_SNOOPING value")?
476                    > 0,
477            ),
478            IFLA_BR_MCAST_QUERY_USE_IFADDR => Self::MulticastQueryUseIfaddr(
479                parse_u8(payload)
480                    .context("invalid IFLA_BR_MCAST_QUERY_USE_IFADDR value")?
481                    > 0,
482            ),
483            IFLA_BR_MCAST_QUERIER => Self::MulticastQuerier(
484                parse_u8(payload)
485                    .context("invalid IFLA_BR_MCAST_QUERIER value")?
486                    > 0,
487            ),
488            IFLA_BR_NF_CALL_IPTABLES => Self::NfCallIpTables(
489                parse_u8(payload)
490                    .context("invalid IFLA_BR_NF_CALL_IPTABLES value")?
491                    > 0,
492            ),
493            IFLA_BR_NF_CALL_IP6TABLES => Self::NfCallIp6Tables(
494                parse_u8(payload)
495                    .context("invalid IFLA_BR_NF_CALL_IP6TABLES value")?
496                    > 0,
497            ),
498            IFLA_BR_NF_CALL_ARPTABLES => Self::NfCallArpTables(
499                parse_u8(payload)
500                    .context("invalid IFLA_BR_NF_CALL_ARPTABLES value")?
501                    > 0,
502            ),
503            IFLA_BR_VLAN_STATS_ENABLED => Self::VlanStatsEnabled(
504                parse_u8(payload)
505                    .context("invalid IFLA_BR_VLAN_STATS_ENABLED value")?
506                    > 0,
507            ),
508            IFLA_BR_MCAST_STATS_ENABLED => Self::MulticastStatsEnabled(
509                parse_u8(payload)
510                    .context("invalid IFLA_BR_MCAST_STATS_ENABLED value")?
511                    > 0,
512            ),
513            IFLA_BR_MCAST_IGMP_VERSION => Self::MulticastIgmpVersion(
514                parse_u8(payload)
515                    .context("invalid IFLA_BR_MCAST_IGMP_VERSION value")?,
516            ),
517            IFLA_BR_MCAST_MLD_VERSION => Self::MulticastMldVersion(
518                parse_u8(payload)
519                    .context("invalid IFLA_BR_MCAST_MLD_VERSION value")?,
520            ),
521            IFLA_BR_VLAN_STATS_PER_PORT => Self::VlanStatsPerPort(
522                parse_u8(payload)
523                    .context("invalid IFLA_BR_VLAN_STATS_PER_PORT value")?
524                    > 0,
525            ),
526            IFLA_BR_MULTI_BOOLOPT => {
527                Self::MultiBoolOpt(BridgeBooleanOptions::parse(payload)?)
528            }
529            IFLA_BR_MCAST_QUERIER_STATE => {
530                let mut v = Vec::new();
531                let err = "failed to parse IFLA_BR_MCAST_QUERIER_STATE";
532                for nla in NlasIterator::new(payload) {
533                    let nla = &nla.context(err)?;
534                    let parsed = BridgeQuerierState::parse(nla).context(err)?;
535                    v.push(parsed);
536                }
537                Self::MulticastQuerierState(v)
538            }
539            IFLA_BR_FDB_N_LEARNED => Self::FdbNLearned(
540                parse_u32(payload)
541                    .context("invalid IFLA_BR_FDB_N_LEARNED value")?,
542            ),
543            IFLA_BR_FDB_MAX_LEARNED => Self::FdbMaxLearned(
544                parse_u32(payload)
545                    .context("invalid IFLA_BR_FDB_MAX_LEARNED value")?,
546            ),
547            _ => Self::Other(DefaultNla::parse(buf).context(
548                "invalid link info bridge NLA value (unknown type)",
549            )?),
550        })
551    }
552}
553
554const BRIDGE_ID_LEN: usize = 8;
555
556#[derive(Debug, PartialEq, Eq, Clone)]
557pub struct BridgeId {
558    pub priority: u16,
559    pub address: [u8; 6],
560}
561
562buffer!(BridgeIdBuffer(BRIDGE_ID_LEN) {
563    priority: (u16, 0..2),
564    address: (slice, 2..BRIDGE_ID_LEN)
565});
566
567impl<T: AsRef<[u8]> + ?Sized> Parseable<BridgeIdBuffer<&T>> for BridgeId {
568    fn parse(buf: &BridgeIdBuffer<&T>) -> Result<Self, DecodeError> {
569        // Priority is encoded in big endian. From kernel's
570        // net/bridge/br_netlink.c br_fill_info():
571        // u16 priority = (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1];
572        Ok(Self {
573            priority: u16::from_be(buf.priority()),
574            address: parse_mac(buf.address())
575                .context("invalid MAC address in BridgeId buffer")?,
576        })
577    }
578}
579
580impl Emitable for BridgeId {
581    fn buffer_len(&self) -> usize {
582        BRIDGE_ID_LEN
583    }
584
585    fn emit(&self, buffer: &mut [u8]) {
586        let mut buffer = BridgeIdBuffer::new(buffer);
587        buffer.set_priority(self.priority.to_be());
588        buffer.address_mut().copy_from_slice(&self.address[..]);
589    }
590}
591
592const BRIDGE_QUERIER_IP_ADDRESS: u16 = 1;
593const BRIDGE_QUERIER_IP_PORT: u16 = 2;
594const BRIDGE_QUERIER_IP_OTHER_TIMER: u16 = 3;
595// const BRIDGE_QUERIER_PAD: u16 = 4;
596const BRIDGE_QUERIER_IPV6_ADDRESS: u16 = 5;
597const BRIDGE_QUERIER_IPV6_PORT: u16 = 6;
598const BRIDGE_QUERIER_IPV6_OTHER_TIMER: u16 = 7;
599
600#[derive(Debug, Clone, Eq, PartialEq)]
601#[non_exhaustive]
602pub enum BridgeQuerierState {
603    Ipv4Address(Ipv4Addr),
604    Ipv4Port(u32),
605    Ipv4OtherTimer(u64),
606    Ipv6Address(Ipv6Addr),
607    Ipv6Port(u32),
608    Ipv6OtherTimer(u64),
609    Other(DefaultNla),
610}
611
612impl Nla for BridgeQuerierState {
613    fn value_len(&self) -> usize {
614        use self::BridgeQuerierState::*;
615        match self {
616            Ipv4Address(_) => 4,
617            Ipv6Address(_) => 16,
618            Ipv4Port(_) | Ipv6Port(_) => 4,
619            Ipv4OtherTimer(_) | Ipv6OtherTimer(_) => 8,
620            Other(nla) => nla.value_len(),
621        }
622    }
623
624    fn kind(&self) -> u16 {
625        use self::BridgeQuerierState::*;
626        match self {
627            Ipv4Address(_) => BRIDGE_QUERIER_IP_ADDRESS,
628            Ipv4Port(_) => BRIDGE_QUERIER_IP_PORT,
629            Ipv4OtherTimer(_) => BRIDGE_QUERIER_IP_OTHER_TIMER,
630            Ipv6Address(_) => BRIDGE_QUERIER_IPV6_ADDRESS,
631            Ipv6Port(_) => BRIDGE_QUERIER_IPV6_PORT,
632            Ipv6OtherTimer(_) => BRIDGE_QUERIER_IPV6_OTHER_TIMER,
633            Other(nla) => nla.kind(),
634        }
635    }
636
637    fn emit_value(&self, buffer: &mut [u8]) {
638        use self::BridgeQuerierState::*;
639        match self {
640            Ipv4Port(d) | Ipv6Port(d) => emit_u32(buffer, *d).unwrap(),
641            Ipv4OtherTimer(d) | Ipv6OtherTimer(d) => {
642                emit_u64(buffer, *d).unwrap()
643            }
644            Ipv4Address(addr) => buffer.copy_from_slice(&addr.octets()),
645            Ipv6Address(addr) => buffer.copy_from_slice(&addr.octets()),
646            Other(nla) => nla.emit_value(buffer),
647        }
648    }
649}
650
651impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
652    for BridgeQuerierState
653{
654    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
655        use self::BridgeQuerierState::*;
656        let payload = buf.value();
657        Ok(match buf.kind() {
658            BRIDGE_QUERIER_IP_ADDRESS => match parse_ip(payload) {
659                Ok(IpAddr::V4(addr)) => Ipv4Address(addr),
660                Ok(v) => {
661                    return Err(DecodeError::from(format!(
662                        "Invalid BRIDGE_QUERIER_IP_ADDRESS, expecting IPv4 \
663                         address, but got {v}"
664                    )))
665                }
666                Err(e) => {
667                    return Err(DecodeError::from(format!(
668                        "Invalid BRIDGE_QUERIER_IP_ADDRESS {e}"
669                    )))
670                }
671            },
672            BRIDGE_QUERIER_IPV6_ADDRESS => match parse_ip(payload) {
673                Ok(IpAddr::V6(addr)) => Ipv6Address(addr),
674                Ok(v) => {
675                    return Err(DecodeError::from(format!(
676                        "Invalid BRIDGE_QUERIER_IPV6_ADDRESS, expecting IPv6 \
677                         address, but got {v}"
678                    )));
679                }
680                Err(e) => {
681                    return Err(DecodeError::from(format!(
682                        "Invalid BRIDGE_QUERIER_IPV6_ADDRESS {e}"
683                    )));
684                }
685            },
686            BRIDGE_QUERIER_IP_PORT => Ipv4Port(
687                parse_u32(payload)
688                    .context("invalid BRIDGE_QUERIER_IP_PORT value")?,
689            ),
690            BRIDGE_QUERIER_IPV6_PORT => Ipv6Port(
691                parse_u32(payload)
692                    .context("invalid BRIDGE_QUERIER_IPV6_PORT value")?,
693            ),
694            BRIDGE_QUERIER_IP_OTHER_TIMER => Ipv4OtherTimer(
695                parse_u64(payload)
696                    .context("invalid BRIDGE_QUERIER_IP_OTHER_TIMER value")?,
697            ),
698            BRIDGE_QUERIER_IPV6_OTHER_TIMER => Ipv6OtherTimer(
699                parse_u64(payload)
700                    .context("invalid BRIDGE_QUERIER_IPV6_OTHER_TIMER value")?,
701            ),
702
703            kind => Other(
704                DefaultNla::parse(buf)
705                    .context(format!("unknown NLA type {kind}"))?,
706            ),
707        })
708    }
709}
710
711const BR_NO_STP: u32 = 0;
712const BR_KERNEL_STP: u32 = 1;
713const BR_USER_STP: u32 = 2;
714
715#[derive(Debug, PartialEq, Eq, Clone, Copy)]
716#[non_exhaustive]
717pub enum BridgeStpState {
718    Disabled,
719    KernelStp,
720    UserStp,
721    Other(u32),
722}
723
724impl From<u32> for BridgeStpState {
725    fn from(d: u32) -> Self {
726        match d {
727            BR_NO_STP => Self::Disabled,
728            BR_KERNEL_STP => Self::KernelStp,
729            BR_USER_STP => Self::UserStp,
730            _ => Self::Other(d),
731        }
732    }
733}
734
735impl From<BridgeStpState> for u32 {
736    fn from(v: BridgeStpState) -> u32 {
737        match v {
738            BridgeStpState::Disabled => BR_NO_STP,
739            BridgeStpState::KernelStp => BR_KERNEL_STP,
740            BridgeStpState::UserStp => BR_USER_STP,
741            BridgeStpState::Other(d) => d,
742        }
743    }
744}
745
746const MDB_RTR_TYPE_DISABLED: u8 = 0;
747const MDB_RTR_TYPE_TEMP_QUERY: u8 = 1;
748const MDB_RTR_TYPE_PERM: u8 = 2;
749const MDB_RTR_TYPE_TEMP: u8 = 3;
750
751#[derive(Debug, PartialEq, Eq, Clone, Copy)]
752#[non_exhaustive]
753pub enum BridgeMulticastRouterType {
754    Disabled,
755    TempQuery,
756    Permanent,
757    Temp,
758    Other(u8),
759}
760
761impl BridgeMulticastRouterType {
762    // iproute is treating Self::TempQuery as auto.
763    #[allow(non_upper_case_globals)]
764    pub const Auto: Self = Self::TempQuery;
765}
766
767impl From<u8> for BridgeMulticastRouterType {
768    fn from(d: u8) -> Self {
769        match d {
770            MDB_RTR_TYPE_DISABLED => Self::Disabled,
771            MDB_RTR_TYPE_TEMP_QUERY => Self::TempQuery,
772            MDB_RTR_TYPE_PERM => Self::Permanent,
773            MDB_RTR_TYPE_TEMP => Self::Temp,
774            _ => Self::Other(d),
775        }
776    }
777}
778
779impl From<BridgeMulticastRouterType> for u8 {
780    fn from(v: BridgeMulticastRouterType) -> u8 {
781        match v {
782            BridgeMulticastRouterType::Disabled => MDB_RTR_TYPE_DISABLED,
783            BridgeMulticastRouterType::TempQuery => MDB_RTR_TYPE_TEMP_QUERY,
784            BridgeMulticastRouterType::Permanent => MDB_RTR_TYPE_PERM,
785            BridgeMulticastRouterType::Temp => MDB_RTR_TYPE_TEMP,
786            BridgeMulticastRouterType::Other(d) => d,
787        }
788    }
789}