netlink_packet_route/rtnl/link/nlas/
bridge.rs

1// SPDX-License-Identifier: MIT
2
3use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4
5use anyhow::Context;
6use byteorder::{BigEndian, ByteOrder, NativeEndian};
7use netlink_packet_utils::{
8    nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED},
9    parsers::{
10        parse_ip, parse_mac, parse_u16, parse_u16_be, parse_u32, parse_u64,
11        parse_u8,
12    },
13    traits::{Emitable, Parseable},
14    DecodeError,
15};
16
17use crate::constants::*;
18
19const BRIDGE_QUERIER_IP_ADDRESS: u16 = 1;
20const BRIDGE_QUERIER_IP_PORT: u16 = 2;
21const BRIDGE_QUERIER_IP_OTHER_TIMER: u16 = 3;
22// const BRIDGE_QUERIER_PAD: u16 = 4;
23const BRIDGE_QUERIER_IPV6_ADDRESS: u16 = 5;
24const BRIDGE_QUERIER_IPV6_PORT: u16 = 6;
25const BRIDGE_QUERIER_IPV6_OTHER_TIMER: u16 = 7;
26
27#[derive(Debug, PartialEq, Eq, Clone)]
28#[non_exhaustive]
29pub enum InfoBridge {
30    Unspec(Vec<u8>),
31    GroupAddr([u8; 6]),
32    // FIXME: what type is this? putting Vec<u8> for now but it might
33    // be a boolean actually
34    FdbFlush(Vec<u8>),
35    Pad(Vec<u8>),
36    HelloTimer(u64),
37    TcnTimer(u64),
38    TopologyChangeTimer(u64),
39    GcTimer(u64),
40    MulticastMembershipInterval(u64),
41    MulticastQuerierInterval(u64),
42    MulticastQueryInterval(u64),
43    MulticastQueryResponseInterval(u64),
44    MulticastLastMemberInterval(u64),
45    MulticastStartupQueryInterval(u64),
46    ForwardDelay(u32),
47    HelloTime(u32),
48    MaxAge(u32),
49    AgeingTime(u32),
50    StpState(u32),
51    MulticastHashElasticity(u32),
52    MulticastHashMax(u32),
53    MulticastLastMemberCount(u32),
54    MulticastStartupQueryCount(u32),
55    RootPathCost(u32),
56    Priority(u16),
57    VlanProtocol(u16),
58    GroupFwdMask(u16),
59    RootId((u16, [u8; 6])),
60    BridgeId((u16, [u8; 6])),
61    RootPort(u16),
62    VlanDefaultPvid(u16),
63    VlanFiltering(u8),
64    TopologyChange(u8),
65    TopologyChangeDetected(u8),
66    MulticastRouter(u8),
67    MulticastSnooping(u8),
68    MulticastQueryUseIfaddr(u8),
69    MulticastQuerier(u8),
70    NfCallIpTables(u8),
71    NfCallIp6Tables(u8),
72    NfCallArpTables(u8),
73    VlanStatsEnabled(u8),
74    MulticastStatsEnabled(u8),
75    MulticastIgmpVersion(u8),
76    MulticastMldVersion(u8),
77    VlanStatsPerHost(u8),
78    MultiBoolOpt(u64),
79    MulticastQuerierState(Vec<BridgeQuerierState>),
80    Other(DefaultNla),
81}
82
83impl Nla for InfoBridge {
84    #[rustfmt::skip]
85    fn value_len(&self) -> usize {
86        use self::InfoBridge::*;
87        match self {
88            Unspec(bytes)
89                | FdbFlush(bytes)
90                | Pad(bytes)
91                => bytes.len(),
92            HelloTimer(_)
93                | TcnTimer(_)
94                | TopologyChangeTimer(_)
95                | GcTimer(_)
96                | MulticastMembershipInterval(_)
97                | MulticastQuerierInterval(_)
98                | MulticastQueryInterval(_)
99                | MulticastQueryResponseInterval(_)
100                | MulticastLastMemberInterval(_)
101                | MulticastStartupQueryInterval(_)
102                => 8,
103            ForwardDelay(_)
104                | HelloTime(_)
105                | MaxAge(_)
106                | AgeingTime(_)
107                | StpState(_)
108                | MulticastHashElasticity(_)
109                | MulticastHashMax(_)
110                | MulticastLastMemberCount(_)
111                | MulticastStartupQueryCount(_)
112                | RootPathCost(_)
113                => 4,
114            Priority(_)
115                | VlanProtocol(_)
116                | GroupFwdMask(_)
117                | RootPort(_)
118                | VlanDefaultPvid(_)
119                => 2,
120
121            RootId(_)
122                | BridgeId(_)
123                | MultiBoolOpt(_)
124                => 8,
125
126            GroupAddr(_) => 6,
127
128            VlanFiltering(_)
129                | TopologyChange(_)
130                | TopologyChangeDetected(_)
131                | MulticastRouter(_)
132                | MulticastSnooping(_)
133                | MulticastQueryUseIfaddr(_)
134                | MulticastQuerier(_)
135                | NfCallIpTables(_)
136                | NfCallIp6Tables(_)
137                | NfCallArpTables(_)
138                | VlanStatsEnabled(_)
139                | MulticastStatsEnabled(_)
140                | MulticastIgmpVersion(_)
141                | MulticastMldVersion(_)
142                | VlanStatsPerHost(_)
143                => 1,
144
145            MulticastQuerierState(ref nlas) => nlas.as_slice().buffer_len(),
146
147            Other(nla)
148                => nla.value_len(),
149        }
150    }
151
152    #[rustfmt::skip]
153    fn emit_value(&self, buffer: &mut [u8]) {
154        use self::InfoBridge::*;
155        match self {
156            Unspec(ref bytes)
157                | FdbFlush(ref bytes)
158                | Pad(ref bytes)
159                => buffer.copy_from_slice(bytes),
160
161            HelloTimer(ref value)
162                | TcnTimer(ref value)
163                | TopologyChangeTimer(ref value)
164                | GcTimer(ref value)
165                | MulticastMembershipInterval(ref value)
166                | MulticastQuerierInterval(ref value)
167                | MulticastQueryInterval(ref value)
168                | MulticastQueryResponseInterval(ref value)
169                | MulticastLastMemberInterval(ref value)
170                | MulticastStartupQueryInterval(ref value)
171                | MultiBoolOpt(ref value)
172                => NativeEndian::write_u64(buffer, *value),
173
174            ForwardDelay(ref value)
175                | HelloTime(ref value)
176                | MaxAge(ref value)
177                | AgeingTime(ref value)
178                | StpState(ref value)
179                | MulticastHashElasticity(ref value)
180                | MulticastHashMax(ref value)
181                | MulticastLastMemberCount(ref value)
182                | MulticastStartupQueryCount(ref value)
183                | RootPathCost(ref value)
184                => NativeEndian::write_u32(buffer, *value),
185
186            Priority(ref value)
187                | GroupFwdMask(ref value)
188                | RootPort(ref value)
189                | VlanDefaultPvid(ref value)
190                => NativeEndian::write_u16(buffer, *value),
191
192            VlanProtocol(ref value)
193                => BigEndian::write_u16(buffer, *value),
194
195            RootId((ref priority, ref address))
196                | BridgeId((ref priority, ref address))
197                => {
198                    NativeEndian::write_u16(buffer, *priority);
199                    buffer[2..].copy_from_slice(&address[..]);
200                }
201
202            GroupAddr(ref value) => buffer.copy_from_slice(&value[..]),
203
204            VlanFiltering(ref value)
205                | TopologyChange(ref value)
206                | TopologyChangeDetected(ref value)
207                | MulticastRouter(ref value)
208                | MulticastSnooping(ref value)
209                | MulticastQueryUseIfaddr(ref value)
210                | MulticastQuerier(ref value)
211                | NfCallIpTables(ref value)
212                | NfCallIp6Tables(ref value)
213                | NfCallArpTables(ref value)
214                | VlanStatsEnabled(ref value)
215                | MulticastStatsEnabled(ref value)
216                | MulticastIgmpVersion(ref value)
217                | MulticastMldVersion(ref value)
218                | VlanStatsPerHost(ref value)
219                => buffer[0] = *value,
220
221            MulticastQuerierState(ref nlas) => nlas.as_slice().emit(buffer),
222
223            Other(nla)
224                => nla.emit_value(buffer),
225        }
226    }
227
228    fn kind(&self) -> u16 {
229        use self::InfoBridge::*;
230        match self {
231            Unspec(_) => IFLA_BR_UNSPEC,
232            GroupAddr(_) => IFLA_BR_GROUP_ADDR,
233            FdbFlush(_) => IFLA_BR_FDB_FLUSH,
234            Pad(_) => IFLA_BR_PAD,
235            HelloTimer(_) => IFLA_BR_HELLO_TIMER,
236            TcnTimer(_) => IFLA_BR_TCN_TIMER,
237            TopologyChangeTimer(_) => IFLA_BR_TOPOLOGY_CHANGE_TIMER,
238            GcTimer(_) => IFLA_BR_GC_TIMER,
239            MulticastMembershipInterval(_) => IFLA_BR_MCAST_MEMBERSHIP_INTVL,
240            MulticastQuerierInterval(_) => IFLA_BR_MCAST_QUERIER_INTVL,
241            MulticastQueryInterval(_) => IFLA_BR_MCAST_QUERY_INTVL,
242            MulticastQueryResponseInterval(_) => {
243                IFLA_BR_MCAST_QUERY_RESPONSE_INTVL
244            }
245            ForwardDelay(_) => IFLA_BR_FORWARD_DELAY,
246            HelloTime(_) => IFLA_BR_HELLO_TIME,
247            MaxAge(_) => IFLA_BR_MAX_AGE,
248            AgeingTime(_) => IFLA_BR_AGEING_TIME,
249            StpState(_) => IFLA_BR_STP_STATE,
250            MulticastHashElasticity(_) => IFLA_BR_MCAST_HASH_ELASTICITY,
251            MulticastHashMax(_) => IFLA_BR_MCAST_HASH_MAX,
252            MulticastLastMemberCount(_) => IFLA_BR_MCAST_LAST_MEMBER_CNT,
253            MulticastStartupQueryCount(_) => IFLA_BR_MCAST_STARTUP_QUERY_CNT,
254            MulticastLastMemberInterval(_) => IFLA_BR_MCAST_LAST_MEMBER_INTVL,
255            MulticastStartupQueryInterval(_) => {
256                IFLA_BR_MCAST_STARTUP_QUERY_INTVL
257            }
258            RootPathCost(_) => IFLA_BR_ROOT_PATH_COST,
259            Priority(_) => IFLA_BR_PRIORITY,
260            VlanProtocol(_) => IFLA_BR_VLAN_PROTOCOL,
261            GroupFwdMask(_) => IFLA_BR_GROUP_FWD_MASK,
262            RootId(_) => IFLA_BR_ROOT_ID,
263            BridgeId(_) => IFLA_BR_BRIDGE_ID,
264            RootPort(_) => IFLA_BR_ROOT_PORT,
265            VlanDefaultPvid(_) => IFLA_BR_VLAN_DEFAULT_PVID,
266            VlanFiltering(_) => IFLA_BR_VLAN_FILTERING,
267            TopologyChange(_) => IFLA_BR_TOPOLOGY_CHANGE,
268            TopologyChangeDetected(_) => IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
269            MulticastRouter(_) => IFLA_BR_MCAST_ROUTER,
270            MulticastSnooping(_) => IFLA_BR_MCAST_SNOOPING,
271            MulticastQueryUseIfaddr(_) => IFLA_BR_MCAST_QUERY_USE_IFADDR,
272            MulticastQuerier(_) => IFLA_BR_MCAST_QUERIER,
273            NfCallIpTables(_) => IFLA_BR_NF_CALL_IPTABLES,
274            NfCallIp6Tables(_) => IFLA_BR_NF_CALL_IP6TABLES,
275            NfCallArpTables(_) => IFLA_BR_NF_CALL_ARPTABLES,
276            VlanStatsEnabled(_) => IFLA_BR_VLAN_STATS_ENABLED,
277            MulticastStatsEnabled(_) => IFLA_BR_MCAST_STATS_ENABLED,
278            MulticastIgmpVersion(_) => IFLA_BR_MCAST_IGMP_VERSION,
279            MulticastMldVersion(_) => IFLA_BR_MCAST_MLD_VERSION,
280            VlanStatsPerHost(_) => IFLA_BR_VLAN_STATS_PER_PORT,
281            MultiBoolOpt(_) => IFLA_BR_MULTI_BOOLOPT,
282            MulticastQuerierState(_) => {
283                IFLA_BR_MCAST_QUERIER_STATE | NLA_F_NESTED
284            }
285            Other(nla) => nla.kind(),
286        }
287    }
288}
289
290impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for InfoBridge {
291    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
292        use self::InfoBridge::*;
293        let payload = buf.value();
294        Ok(match buf.kind() {
295            IFLA_BR_UNSPEC => Unspec(payload.to_vec()),
296            IFLA_BR_FDB_FLUSH => FdbFlush(payload.to_vec()),
297            IFLA_BR_PAD => Pad(payload.to_vec()),
298            IFLA_BR_HELLO_TIMER => HelloTimer(
299                parse_u64(payload)
300                    .context("invalid IFLA_BR_HELLO_TIMER value")?,
301            ),
302            IFLA_BR_TCN_TIMER => TcnTimer(
303                parse_u64(payload)
304                    .context("invalid IFLA_BR_TCN_TIMER value")?,
305            ),
306            IFLA_BR_TOPOLOGY_CHANGE_TIMER => TopologyChangeTimer(
307                parse_u64(payload)
308                    .context("invalid IFLA_BR_TOPOLOGY_CHANGE_TIMER value")?,
309            ),
310            IFLA_BR_GC_TIMER => GcTimer(
311                parse_u64(payload).context("invalid IFLA_BR_GC_TIMER value")?,
312            ),
313            IFLA_BR_MCAST_LAST_MEMBER_INTVL => MulticastLastMemberInterval(
314                parse_u64(payload)
315                    .context("invalid IFLA_BR_MCAST_LAST_MEMBER_INTVL value")?,
316            ),
317            IFLA_BR_MCAST_MEMBERSHIP_INTVL => MulticastMembershipInterval(
318                parse_u64(payload)
319                    .context("invalid IFLA_BR_MCAST_MEMBERSHIP_INTVL value")?,
320            ),
321            IFLA_BR_MCAST_QUERIER_INTVL => MulticastQuerierInterval(
322                parse_u64(payload)
323                    .context("invalid IFLA_BR_MCAST_QUERIER_INTVL value")?,
324            ),
325            IFLA_BR_MCAST_QUERY_INTVL => MulticastQueryInterval(
326                parse_u64(payload)
327                    .context("invalid IFLA_BR_MCAST_QUERY_INTVL value")?,
328            ),
329            IFLA_BR_MCAST_QUERY_RESPONSE_INTVL => {
330                MulticastQueryResponseInterval(parse_u64(payload).context(
331                    "invalid IFLA_BR_MCAST_QUERY_RESPONSE_INTVL value",
332                )?)
333            }
334            IFLA_BR_MCAST_STARTUP_QUERY_INTVL => {
335                MulticastStartupQueryInterval(parse_u64(payload).context(
336                    "invalid IFLA_BR_MCAST_STARTUP_QUERY_INTVL value",
337                )?)
338            }
339            IFLA_BR_FORWARD_DELAY => ForwardDelay(
340                parse_u32(payload)
341                    .context("invalid IFLA_BR_FORWARD_DELAY value")?,
342            ),
343            IFLA_BR_HELLO_TIME => HelloTime(
344                parse_u32(payload)
345                    .context("invalid IFLA_BR_HELLO_TIME value")?,
346            ),
347            IFLA_BR_MAX_AGE => MaxAge(
348                parse_u32(payload).context("invalid IFLA_BR_MAX_AGE value")?,
349            ),
350            IFLA_BR_AGEING_TIME => AgeingTime(
351                parse_u32(payload)
352                    .context("invalid IFLA_BR_AGEING_TIME value")?,
353            ),
354            IFLA_BR_STP_STATE => StpState(
355                parse_u32(payload)
356                    .context("invalid IFLA_BR_STP_STATE value")?,
357            ),
358            IFLA_BR_MCAST_HASH_ELASTICITY => MulticastHashElasticity(
359                parse_u32(payload)
360                    .context("invalid IFLA_BR_MCAST_HASH_ELASTICITY value")?,
361            ),
362            IFLA_BR_MCAST_HASH_MAX => MulticastHashMax(
363                parse_u32(payload)
364                    .context("invalid IFLA_BR_MCAST_HASH_MAX value")?,
365            ),
366            IFLA_BR_MCAST_LAST_MEMBER_CNT => MulticastLastMemberCount(
367                parse_u32(payload)
368                    .context("invalid IFLA_BR_MCAST_LAST_MEMBER_CNT value")?,
369            ),
370            IFLA_BR_MCAST_STARTUP_QUERY_CNT => MulticastStartupQueryCount(
371                parse_u32(payload)
372                    .context("invalid IFLA_BR_MCAST_STARTUP_QUERY_CNT value")?,
373            ),
374            IFLA_BR_ROOT_PATH_COST => RootPathCost(
375                parse_u32(payload)
376                    .context("invalid IFLA_BR_ROOT_PATH_COST value")?,
377            ),
378            IFLA_BR_PRIORITY => Priority(
379                parse_u16(payload).context("invalid IFLA_BR_PRIORITY value")?,
380            ),
381            IFLA_BR_VLAN_PROTOCOL => VlanProtocol(
382                parse_u16_be(payload)
383                    .context("invalid IFLA_BR_VLAN_PROTOCOL value")?,
384            ),
385            IFLA_BR_GROUP_FWD_MASK => GroupFwdMask(
386                parse_u16(payload)
387                    .context("invalid IFLA_BR_GROUP_FWD_MASK value")?,
388            ),
389            IFLA_BR_ROOT_ID | IFLA_BR_BRIDGE_ID => {
390                if payload.len() != 8 {
391                    return Err(
392                        "invalid IFLA_BR_ROOT_ID or IFLA_BR_BRIDGE_ID value"
393                            .into(),
394                    );
395                }
396
397                let priority = NativeEndian::read_u16(&payload[..2]);
398                let address = parse_mac(&payload[2..]).context(
399                    "invalid IFLA_BR_ROOT_ID or IFLA_BR_BRIDGE_ID value",
400                )?;
401
402                match buf.kind() {
403                    IFLA_BR_ROOT_ID => RootId((priority, address)),
404                    IFLA_BR_BRIDGE_ID => BridgeId((priority, address)),
405                    _ => unreachable!(),
406                }
407            }
408            IFLA_BR_GROUP_ADDR => GroupAddr(
409                parse_mac(payload)
410                    .context("invalid IFLA_BR_GROUP_ADDR value")?,
411            ),
412            IFLA_BR_ROOT_PORT => RootPort(
413                parse_u16(payload)
414                    .context("invalid IFLA_BR_ROOT_PORT value")?,
415            ),
416            IFLA_BR_VLAN_DEFAULT_PVID => VlanDefaultPvid(
417                parse_u16(payload)
418                    .context("invalid IFLA_BR_VLAN_DEFAULT_PVID value")?,
419            ),
420            IFLA_BR_VLAN_FILTERING => VlanFiltering(
421                parse_u8(payload)
422                    .context("invalid IFLA_BR_VLAN_FILTERING value")?,
423            ),
424            IFLA_BR_TOPOLOGY_CHANGE => TopologyChange(
425                parse_u8(payload)
426                    .context("invalid IFLA_BR_TOPOLOGY_CHANGE value")?,
427            ),
428            IFLA_BR_TOPOLOGY_CHANGE_DETECTED => {
429                TopologyChangeDetected(parse_u8(payload).context(
430                    "invalid IFLA_BR_TOPOLOGY_CHANGE_DETECTED value",
431                )?)
432            }
433            IFLA_BR_MCAST_ROUTER => MulticastRouter(
434                parse_u8(payload)
435                    .context("invalid IFLA_BR_MCAST_ROUTER value")?,
436            ),
437            IFLA_BR_MCAST_SNOOPING => MulticastSnooping(
438                parse_u8(payload)
439                    .context("invalid IFLA_BR_MCAST_SNOOPING value")?,
440            ),
441            IFLA_BR_MCAST_QUERY_USE_IFADDR => MulticastQueryUseIfaddr(
442                parse_u8(payload)
443                    .context("invalid IFLA_BR_MCAST_QUERY_USE_IFADDR value")?,
444            ),
445            IFLA_BR_MCAST_QUERIER => MulticastQuerier(
446                parse_u8(payload)
447                    .context("invalid IFLA_BR_MCAST_QUERIER value")?,
448            ),
449            IFLA_BR_NF_CALL_IPTABLES => NfCallIpTables(
450                parse_u8(payload)
451                    .context("invalid IFLA_BR_NF_CALL_IPTABLES value")?,
452            ),
453            IFLA_BR_NF_CALL_IP6TABLES => NfCallIp6Tables(
454                parse_u8(payload)
455                    .context("invalid IFLA_BR_NF_CALL_IP6TABLES value")?,
456            ),
457            IFLA_BR_NF_CALL_ARPTABLES => NfCallArpTables(
458                parse_u8(payload)
459                    .context("invalid IFLA_BR_NF_CALL_ARPTABLES value")?,
460            ),
461            IFLA_BR_VLAN_STATS_ENABLED => VlanStatsEnabled(
462                parse_u8(payload)
463                    .context("invalid IFLA_BR_VLAN_STATS_ENABLED value")?,
464            ),
465            IFLA_BR_MCAST_STATS_ENABLED => MulticastStatsEnabled(
466                parse_u8(payload)
467                    .context("invalid IFLA_BR_MCAST_STATS_ENABLED value")?,
468            ),
469            IFLA_BR_MCAST_IGMP_VERSION => MulticastIgmpVersion(
470                parse_u8(payload)
471                    .context("invalid IFLA_BR_MCAST_IGMP_VERSION value")?,
472            ),
473            IFLA_BR_MCAST_MLD_VERSION => MulticastMldVersion(
474                parse_u8(payload)
475                    .context("invalid IFLA_BR_MCAST_MLD_VERSION value")?,
476            ),
477            IFLA_BR_VLAN_STATS_PER_PORT => VlanStatsPerHost(
478                parse_u8(payload)
479                    .context("invalid IFLA_BR_VLAN_STATS_PER_PORT value")?,
480            ),
481            IFLA_BR_MULTI_BOOLOPT => MultiBoolOpt(
482                parse_u64(payload)
483                    .context("invalid IFLA_BR_MULTI_BOOLOPT value")?,
484            ),
485            IFLA_BR_MCAST_QUERIER_STATE => {
486                let mut v = Vec::new();
487                let err = "failed to parse IFLA_BR_MCAST_QUERIER_STATE";
488                for nla in NlasIterator::new(payload) {
489                    let nla = &nla.context(err)?;
490                    let parsed = BridgeQuerierState::parse(nla).context(err)?;
491                    v.push(parsed);
492                }
493                MulticastQuerierState(v)
494            }
495            _ => Other(DefaultNla::parse(buf).context(
496                "invalid link info bridge NLA value (unknown type)",
497            )?),
498        })
499    }
500}
501
502#[derive(Debug, Clone, Eq, PartialEq)]
503#[non_exhaustive]
504pub enum BridgeQuerierState {
505    Ipv4Address(Ipv4Addr),
506    Ipv4Port(u32),
507    Ipv4OtherTimer(u64),
508    Ipv6Address(Ipv6Addr),
509    Ipv6Port(u32),
510    Ipv6OtherTimer(u64),
511    Other(DefaultNla),
512}
513
514impl Nla for BridgeQuerierState {
515    fn value_len(&self) -> usize {
516        use self::BridgeQuerierState::*;
517        match self {
518            Ipv4Address(_) => 4,
519            Ipv6Address(_) => 16,
520            Ipv4Port(_) | Ipv6Port(_) => 4,
521            Ipv4OtherTimer(_) | Ipv6OtherTimer(_) => 8,
522            Other(nla) => nla.value_len(),
523        }
524    }
525
526    fn kind(&self) -> u16 {
527        use self::BridgeQuerierState::*;
528        match self {
529            Ipv4Address(_) => BRIDGE_QUERIER_IP_ADDRESS,
530            Ipv4Port(_) => BRIDGE_QUERIER_IP_PORT,
531            Ipv4OtherTimer(_) => BRIDGE_QUERIER_IP_OTHER_TIMER,
532            Ipv6Address(_) => BRIDGE_QUERIER_IPV6_ADDRESS,
533            Ipv6Port(_) => BRIDGE_QUERIER_IPV6_PORT,
534            Ipv6OtherTimer(_) => BRIDGE_QUERIER_IPV6_OTHER_TIMER,
535            Other(nla) => nla.kind(),
536        }
537    }
538
539    fn emit_value(&self, buffer: &mut [u8]) {
540        use self::BridgeQuerierState::*;
541        match self {
542            Ipv4Port(d) | Ipv6Port(d) => NativeEndian::write_u32(buffer, *d),
543            Ipv4OtherTimer(d) | Ipv6OtherTimer(d) => {
544                NativeEndian::write_u64(buffer, *d)
545            }
546            Ipv4Address(addr) => buffer.copy_from_slice(&addr.octets()),
547            Ipv6Address(addr) => buffer.copy_from_slice(&addr.octets()),
548            Other(nla) => nla.emit_value(buffer),
549        }
550    }
551}
552
553impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
554    for BridgeQuerierState
555{
556    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
557        use self::BridgeQuerierState::*;
558        let payload = buf.value();
559        Ok(match buf.kind() {
560            BRIDGE_QUERIER_IP_ADDRESS => match parse_ip(payload) {
561                Ok(IpAddr::V4(addr)) => Ipv4Address(addr),
562                Ok(v) => {
563                    return Err(DecodeError::from(format!(
564                        "Invalid BRIDGE_QUERIER_IP_ADDRESS, \
565                        expecting IPv4 address, but got {v}"
566                    )))
567                }
568                Err(e) => {
569                    return Err(DecodeError::from(format!(
570                        "Invalid BRIDGE_QUERIER_IP_ADDRESS {e}"
571                    )))
572                }
573            },
574            BRIDGE_QUERIER_IPV6_ADDRESS => match parse_ip(payload) {
575                Ok(IpAddr::V6(addr)) => Ipv6Address(addr),
576                Ok(v) => {
577                    return Err(DecodeError::from(format!(
578                        "Invalid BRIDGE_QUERIER_IPV6_ADDRESS, \
579                        expecting IPv6 address, but got {v}"
580                    )));
581                }
582                Err(e) => {
583                    return Err(DecodeError::from(format!(
584                        "Invalid BRIDGE_QUERIER_IPV6_ADDRESS {e}"
585                    )));
586                }
587            },
588            BRIDGE_QUERIER_IP_PORT => Ipv4Port(
589                parse_u32(payload)
590                    .context("invalid BRIDGE_QUERIER_IP_PORT value")?,
591            ),
592            BRIDGE_QUERIER_IPV6_PORT => Ipv6Port(
593                parse_u32(payload)
594                    .context("invalid BRIDGE_QUERIER_IPV6_PORT value")?,
595            ),
596            BRIDGE_QUERIER_IP_OTHER_TIMER => Ipv4OtherTimer(
597                parse_u64(payload)
598                    .context("invalid BRIDGE_QUERIER_IP_OTHER_TIMER value")?,
599            ),
600            BRIDGE_QUERIER_IPV6_OTHER_TIMER => Ipv6OtherTimer(
601                parse_u64(payload)
602                    .context("invalid BRIDGE_QUERIER_IPV6_OTHER_TIMER value")?,
603            ),
604
605            kind => Other(
606                DefaultNla::parse(buf)
607                    .context(format!("unknown NLA type {kind}"))?,
608            ),
609        })
610    }
611}
612
613#[cfg(test)]
614mod tests {
615    use netlink_packet_utils::{
616        nla::NlaBuffer,
617        traits::{Emitable, Parseable},
618    };
619
620    use super::{BridgeQuerierState, InfoBridge};
621
622    #[rustfmt::skip]
623    // This is capture of nlmon of `ip -d link show br0` after:
624    //      ip link set br0 type bridge mcast_snooping 1
625    //      ip link set br0 type bridge mcast_querier 1
626    //      ip link set br0 type bridge mcast_stats_enabled 1
627    const BR_MCAST_QUERIER_STATE_DUMP: [u8; 32] = [
628        0x20, 0x00,                     // len: 32
629        0x2f, 0x80,                     // IFLA_BR_MCAST_QUERIER_STATE | NLA_F_NESTED
630        0x08, 0x00,                     // len: 8
631        0x01, 0x00,                     // BRIDGE_QUERIER_IP_ADDRESS
632        0x00, 0x00, 0x00, 0x00,         // 0.0.0.0
633        0x14, 0x00,                     // len: 20
634        0x05, 0x00,                     // BRIDGE_QUERIER_IPV6_ADDRESS
635        0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
636        0x02, 0x23, 0x45, 0xff, 0xfe, 0x67, 0x89, 0x1c, // fe80::223:45ff:fe67:891c
637    ];
638
639    #[test]
640    fn test_br_multicast_querier_state_parse() {
641        let expected = vec![
642            BridgeQuerierState::Ipv4Address("0.0.0.0".parse().unwrap()),
643            BridgeQuerierState::Ipv6Address(
644                "fe80::223:45ff:fe67:891c".parse().unwrap(),
645            ),
646        ];
647        let nla =
648            NlaBuffer::new_checked(&BR_MCAST_QUERIER_STATE_DUMP[..]).unwrap();
649        let parsed = if let InfoBridge::MulticastQuerierState(s) =
650            InfoBridge::parse(&nla).unwrap()
651        {
652            s
653        } else {
654            panic!("Failed for parse IFLA_BR_MCAST_QUERIER_STATE")
655        };
656        assert_eq!(parsed, expected);
657    }
658
659    #[test]
660    fn test_br_multicast_querier_state_emit() {
661        let mut expected = [0u8; 32];
662        InfoBridge::MulticastQuerierState(vec![
663            BridgeQuerierState::Ipv4Address("0.0.0.0".parse().unwrap()),
664            BridgeQuerierState::Ipv6Address(
665                "fe80::223:45ff:fe67:891c".parse().unwrap(),
666            ),
667        ])
668        .emit(&mut expected);
669
670        assert_eq!(expected, BR_MCAST_QUERIER_STATE_DUMP);
671    }
672}