Skip to main content

netlink/
multicast_groups.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! A module for managing Netlink multicast group memberships.
6//!
7//! A Netlink socket can subscribe to any number of multicast groups, as defined
8//! by the Netlink protocol family that the socket is connected to. There are
9//! two modes of specifying the multicast group memberships. Mode 1 is referred
10//! to as "legacy" throughout this module because it was replaced by mode 2,
11//! referred to as "modern", in Linux 2.6.14.
12//!     Mode 1: Specifying `nl_groups`, a 32 bit bitmask, when binding the
13//!             the socket.
14//!     Mode 2: Setting the `NETLINK_ADD_MEMBERSHIP` or
15//!            `NETLINK_DROP_MEMBERSHIP` socket option.
16//!
17//! Note that both mode 1 and mode 2 are supported (for backwards
18//! compatibility), and the two modes operate over different sets of constants.
19//! The "modern" constants correspond to the index of the set-bit in their
20//! "legacy" counterpart. For example, consider this sample of NETLINK_ROUTE
21//! constants:
22//!     RTNLGRP_LINK:   legacy (1), modern (1),
23//!     RTNLGRP_NOTIFY: legacy (2), modern (2),
24//!     RTNLGRP_NEIGH:  legacy (4), modern (3),
25//!     RTNLGRP_TC:     legacy (8), modern (4),
26//!
27//! The [`MulticastGroupMemberships`] struct exposed by this module tracks the
28//! memberships independently of the mode via which they are set. For example, a
29//! `NETLINK_ROUTE` client could bind to `RTMGRP_IPV6_IFADDR` (256), to start
30//! receiving IPv6 address events, and later set the `NETLINK_DROP_MEMBERSHIP`
31//! socket option to `RTNLGRP_IPV6_IFADDR` (9), to stop receiving events.
32
33use std::marker::PhantomData;
34
35use bit_set::BitSet;
36
37use crate::logging::{log_info, log_warn};
38use crate::protocol_family::NamedNetlinkFamily;
39
40// Safe "as" conversion because u32::BITS (32) will fit into any usize.
41const U32_BITS_USIZE: usize = u32::BITS as usize;
42
43/// A modern (non-legacy) multicast group. Interpreted as a single group.
44#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
45pub struct ModernGroup(pub u32);
46
47impl Into<usize> for ModernGroup {
48    fn into(self) -> usize {
49        let ModernGroup(group) = self;
50        group.try_into().expect("expected usize >= u32")
51    }
52}
53
54/// An error indicating that a modern group has no mapping to a legacy
55/// group.
56#[derive(Debug, PartialEq)]
57pub struct NoMappingFromModernToLegacyGroupError;
58
59impl TryFrom<ModernGroup> for SingleLegacyGroup {
60    type Error = NoMappingFromModernToLegacyGroupError;
61
62    fn try_from(
63        ModernGroup(group): ModernGroup,
64    ) -> Result<SingleLegacyGroup, NoMappingFromModernToLegacyGroupError> {
65        if let Some(group) = 1u32.checked_shl(group) {
66            Ok(SingleLegacyGroup(group))
67        } else {
68            Err(NoMappingFromModernToLegacyGroupError)
69        }
70    }
71}
72
73/// A set of legacy multicast groups. Interpreted as a bit mask, where each set
74/// bit corresponds to a different group membership.
75#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
76pub struct LegacyGroups(pub u32);
77
78/// A single legacy multicast group membership. At most 1 bit is set.
79#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
80pub struct SingleLegacyGroup(u32);
81
82impl SingleLegacyGroup {
83    /// Returns the group number as a `u32`.
84    pub fn inner(&self) -> u32 {
85        let SingleLegacyGroup(inner) = self;
86        *inner
87    }
88}
89
90/// Error returned when attempting to convert a u32 into [`SingleLegacyGroup`].
91#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
92pub struct MultipleBitsSetError;
93
94impl TryFrom<u32> for SingleLegacyGroup {
95    type Error = MultipleBitsSetError;
96    fn try_from(value: u32) -> Result<Self, Self::Error> {
97        (value.count_ones() <= 1).then_some(SingleLegacyGroup(value)).ok_or(MultipleBitsSetError)
98    }
99}
100
101/// Indicates support for a multicast group with respect to a particular
102/// protocol family.
103#[derive(Clone, Copy, Debug, Eq, PartialEq)]
104pub(crate) enum GroupSupport {
105    /// The group is valid for the protocol family and supported.
106    Supported,
107    /// The group is valid for the protocol family but not supported.
108    Unsupported,
109}
110
111/// Error returned when attempting to join an invalid [`ModernGroup`].
112#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
113pub struct InvalidModernGroupError;
114
115/// Multicast group semantics that are specific to a particular protocol family.
116pub(crate) trait MulticastCapableNetlinkFamily {
117    /// Returns whether `group` is supported for this protocol family, or an
118    /// error if it's not a valid group for this family.
119    fn check_support(group: &ModernGroup) -> Result<GroupSupport, InvalidModernGroupError>;
120}
121
122/// Manages the current multicast group memberships of a single connection to
123/// Netlink.
124///
125/// Memberships are stored entirely using the modern set of constants. Legacy
126/// memberships are translated to their modern equivalent before being stored.
127#[derive(Debug)]
128pub(crate) struct MulticastGroupMemberships<F: MulticastCapableNetlinkFamily> {
129    /// Aspects of multicast group memberships that are family specific.
130    family: PhantomData<F>,
131
132    // The current multicast group memberships, stored as modern memberships.
133    // Membership in multicast group "N" is determined by whether the "Nth" bit
134    // of the `BitSet` is set.
135    memberships: BitSet,
136}
137
138#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
139pub(crate) enum Mutation {
140    None,
141    Add(ModernGroup),
142    Del(ModernGroup),
143}
144
145/// Error returned when attempting to join invalid [`LegacyGroups`].
146#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
147pub struct InvalidLegacyGroupsError;
148
149impl<F: MulticastCapableNetlinkFamily + NamedNetlinkFamily> MulticastGroupMemberships<F> {
150    /// Instantiate a new [`MulticastGroupMemberships`].
151    pub(crate) fn new() -> MulticastGroupMemberships<F> {
152        MulticastGroupMemberships { family: PhantomData, memberships: Default::default() }
153    }
154
155    /// Returns `True` if `self` is a member of `group`.
156    pub(crate) fn member_of_group(&self, group: ModernGroup) -> bool {
157        self.memberships.contains(group.into())
158    }
159
160    /// Adds the given multicast group membership.
161    ///
162    /// Returns `true` if the membership was newly added.
163    pub(crate) fn add_membership(
164        &mut self,
165        group: ModernGroup,
166    ) -> Result<bool, InvalidModernGroupError> {
167        let MulticastGroupMemberships { family: _, memberships } = self;
168        if F::check_support(&group)? == GroupSupport::Unsupported {
169            log_info!(
170                "{}: adding membership for valid but unsupported multicast group {:?}",
171                F::NAME,
172                group
173            );
174        }
175        let was_absent = memberships.insert(group.into());
176        return Ok(was_absent);
177    }
178
179    /// Deletes the given multicast group membership.
180    ///
181    /// Returns `true` if the membership was newly removed.
182    pub(crate) fn del_membership(
183        &mut self,
184        group: ModernGroup,
185    ) -> Result<bool, InvalidModernGroupError> {
186        let MulticastGroupMemberships { family: _, memberships } = self;
187        let _ = F::check_support(&group)?;
188        let was_present = memberships.remove(group.into());
189        return Ok(was_present);
190    }
191
192    /// Sets the legacy multicast group memberships.
193    ///
194    /// Legacy memberships are translated into their modern equivalent before
195    /// being written.
196    ///
197    /// Returns the mutations applied.
198    pub(crate) fn set_legacy_memberships(
199        &mut self,
200        LegacyGroups(requested_groups): LegacyGroups,
201    ) -> Result<Vec<Mutation>, InvalidLegacyGroupsError> {
202        crate::logging::log_debug!("set_legacy_memberships");
203        let MulticastGroupMemberships { family: _, memberships } = self;
204
205        let mut mutations = [Mutation::None; U32_BITS_USIZE];
206        // Validate and record all the mutations that will need to be applied.
207        for i in 0..U32_BITS_USIZE {
208            let raw_legacy_group = 1 << i;
209            let legacy_group: SingleLegacyGroup = raw_legacy_group
210                .try_into()
211                .expect("raw_legacy_group unexpectedly had multiple bits set");
212
213            let modern_group = ModernGroup(legacy_group.inner().ilog2() + 1);
214            let support = F::check_support(&modern_group);
215            let is_member_of_group = requested_groups & raw_legacy_group != 0;
216            mutations[i] = match (support, is_member_of_group) {
217                (Ok(support), true) => {
218                    if support == GroupSupport::Unsupported {
219                        log_info!(
220                            "{}: adding membership for valid but unsupported multicast group {:?}",
221                            F::NAME,
222                            modern_group
223                        );
224                    }
225                    Mutation::Add(modern_group)
226                }
227                (Ok(_), false) => Mutation::Del(modern_group),
228                (Err(_), true) => {
229                    log_warn!(
230                        "{}: failed to join legacy groups ({:?}) because of invalid group: {:?}",
231                        F::NAME,
232                        requested_groups,
233                        legacy_group
234                    );
235                    return Err(InvalidLegacyGroupsError);
236                }
237                (Err(_), false) => Mutation::None,
238            };
239        }
240
241        let mut return_mutations = Vec::new();
242        // Apply all of the mutations.
243        for mutation in mutations {
244            match mutation {
245                Mutation::None => {}
246                Mutation::Add(group) => {
247                    crate::logging::log_debug!("mutation: add {group:?}");
248                    let was_absent = memberships.insert(group.into());
249                    if was_absent {
250                        return_mutations.push(Mutation::Add(group));
251                    }
252                }
253                Mutation::Del(group) => {
254                    crate::logging::log_debug!("mutation: del {group:?}");
255                    let was_present = memberships.remove(group.into());
256                    if was_present {
257                        return_mutations.push(Mutation::Del(group));
258                    }
259                }
260            }
261        }
262        Ok(return_mutations)
263    }
264
265    pub(crate) fn iter_groups(&self) -> impl Iterator<Item = ModernGroup> + '_ {
266        self.memberships.into_iter().map(|n: usize| {
267            let n = u32::try_from(n).expect("all ModernGroups fit in u32");
268            ModernGroup(n)
269        })
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use super::*;
276
277    use crate::protocol_family::testutil::{
278        FakeProtocolFamily, INVALID_LEGACY_GROUP, INVALID_MODERN_GROUP, LEGACY_GROUP1,
279        LEGACY_GROUP2, LEGACY_GROUP3, MODERN_GROUP1, MODERN_GROUP2, MODERN_GROUP3,
280    };
281
282    #[test]
283    fn test_single_legacy_groups() {
284        assert_eq!(0.try_into(), Ok(SingleLegacyGroup(0)));
285        assert_eq!(0x00010000.try_into(), Ok(SingleLegacyGroup(0x00010000)));
286        assert_eq!(
287            <u32 as TryInto<SingleLegacyGroup>>::try_into(0x00010100),
288            Err(MultipleBitsSetError {})
289        );
290    }
291
292    #[test]
293    fn test_modern_to_legacy_groups() {
294        assert_eq!(ModernGroup(0).try_into(), Ok(SingleLegacyGroup(0b00000001)));
295        assert_eq!(ModernGroup(4).try_into(), Ok(SingleLegacyGroup(0b00010000)));
296        assert_eq!(
297            ModernGroup(100).try_into(),
298            Err::<SingleLegacyGroup, _>(NoMappingFromModernToLegacyGroupError)
299        );
300    }
301
302    #[test]
303    fn test_add_del_membership() {
304        let mut memberships = MulticastGroupMemberships::<FakeProtocolFamily>::new();
305
306        assert!(!memberships.member_of_group(MODERN_GROUP1));
307        assert!(!memberships.member_of_group(MODERN_GROUP2));
308        assert!(!memberships.member_of_group(MODERN_GROUP3));
309
310        // Add one membership, and verify the others are unaffected.
311        let changed = memberships.add_membership(MODERN_GROUP1).expect("failed to add");
312        assert!(changed, "should have changed group memberships");
313        assert!(memberships.member_of_group(MODERN_GROUP1));
314        assert!(!memberships.member_of_group(MODERN_GROUP2));
315        assert!(!memberships.member_of_group(MODERN_GROUP3));
316        // Add a second & third membership.
317        let changed = memberships.add_membership(MODERN_GROUP2).expect("failed to add");
318        assert!(changed, "should have changed group memberships");
319        let changed = memberships.add_membership(MODERN_GROUP3).expect("failed to add");
320        assert!(changed, "should have changed group memberships");
321        assert!(memberships.member_of_group(MODERN_GROUP1));
322        assert!(memberships.member_of_group(MODERN_GROUP2));
323        assert!(memberships.member_of_group(MODERN_GROUP3));
324        // Remove one membership, and verify the others are unaffected.
325        let changed = memberships.del_membership(MODERN_GROUP1).expect("failed to del");
326        assert!(changed, "should have changed group memberships");
327        assert!(!memberships.member_of_group(MODERN_GROUP1));
328        assert!(memberships.member_of_group(MODERN_GROUP2));
329        assert!(memberships.member_of_group(MODERN_GROUP3));
330        // Remove the second & third membership.
331        let changed = memberships.del_membership(MODERN_GROUP2).expect("failed to del");
332        assert!(changed, "should have changed group memberships");
333        let changed = memberships.del_membership(MODERN_GROUP3).expect("failed to del");
334        assert!(changed, "should have changed group memberships");
335        assert!(!memberships.member_of_group(MODERN_GROUP1));
336        assert!(!memberships.member_of_group(MODERN_GROUP2));
337        assert!(!memberships.member_of_group(MODERN_GROUP3));
338        // Verify Adding/Deleting an invalid group fails.
339        assert_eq!(
340            memberships.add_membership(INVALID_MODERN_GROUP),
341            Err(InvalidModernGroupError {})
342        );
343        assert_eq!(
344            memberships.del_membership(INVALID_MODERN_GROUP),
345            Err(InvalidModernGroupError {})
346        );
347    }
348
349    #[test]
350    fn test_legacy_memberships() {
351        let mut memberships = MulticastGroupMemberships::<FakeProtocolFamily>::new();
352
353        assert!(!memberships.member_of_group(MODERN_GROUP1));
354        assert!(!memberships.member_of_group(MODERN_GROUP2));
355        assert!(!memberships.member_of_group(MODERN_GROUP3));
356
357        // Add one membership and verify the others are unaffected.
358        let mutations = memberships
359            .set_legacy_memberships(LegacyGroups(LEGACY_GROUP1))
360            .expect("failed to set legacy groups");
361        let expected_mutations = [Mutation::Add(MODERN_GROUP1)];
362        assert_eq!(mutations, expected_mutations);
363        assert!(memberships.member_of_group(MODERN_GROUP1));
364        assert!(!memberships.member_of_group(MODERN_GROUP2));
365        assert!(!memberships.member_of_group(MODERN_GROUP3));
366        // Add a second & third membership.
367        let mutations = memberships
368            .set_legacy_memberships(LegacyGroups(LEGACY_GROUP1 | LEGACY_GROUP2 | LEGACY_GROUP3))
369            .expect("failed to set legacy groups");
370        let expected_mutations = [Mutation::Add(MODERN_GROUP2), Mutation::Add(MODERN_GROUP3)];
371        assert_eq!(mutations, expected_mutations);
372        assert!(memberships.member_of_group(MODERN_GROUP1));
373        assert!(memberships.member_of_group(MODERN_GROUP2));
374        assert!(memberships.member_of_group(MODERN_GROUP3));
375        // Remove one membership and verify the others are unaffected.
376        let mutations = memberships
377            .set_legacy_memberships(LegacyGroups(LEGACY_GROUP2 | LEGACY_GROUP3))
378            .expect("failed to set legacy_groups");
379        let expected_mutations = [Mutation::Del(MODERN_GROUP1)];
380        assert_eq!(mutations, expected_mutations);
381        assert!(!memberships.member_of_group(MODERN_GROUP1));
382        assert!(memberships.member_of_group(MODERN_GROUP2));
383        assert!(memberships.member_of_group(MODERN_GROUP3));
384        // Remove the second & third membership.
385        let mutations = memberships
386            .set_legacy_memberships(LegacyGroups(0))
387            .expect("failed to set legacy groups");
388        let expected_mutations = [Mutation::Del(MODERN_GROUP2), Mutation::Del(MODERN_GROUP3)];
389        assert_eq!(mutations, expected_mutations);
390        assert!(!memberships.member_of_group(MODERN_GROUP1));
391        assert!(!memberships.member_of_group(MODERN_GROUP2));
392        assert!(!memberships.member_of_group(MODERN_GROUP3));
393        // Verify that setting an invalid group fails.
394        assert_eq!(
395            memberships.set_legacy_memberships(LegacyGroups(INVALID_LEGACY_GROUP)),
396            Err(InvalidLegacyGroupsError {})
397        );
398    }
399
400    #[test]
401    fn test_legacy_and_modern_memberships() {
402        let mut memberships = MulticastGroupMemberships::<FakeProtocolFamily>::new();
403
404        assert!(!memberships.member_of_group(MODERN_GROUP1));
405        assert!(!memberships.member_of_group(MODERN_GROUP2));
406
407        // Add memberships by their legacy group and drop by their modern group.
408        let _: Vec<Mutation> = memberships
409            .set_legacy_memberships(LegacyGroups(LEGACY_GROUP1 | LEGACY_GROUP2))
410            .expect("failed to set legacy groups");
411        assert!(memberships.member_of_group(MODERN_GROUP1));
412        assert!(memberships.member_of_group(MODERN_GROUP2));
413        let _: bool = memberships.del_membership(MODERN_GROUP1).expect("failed to del");
414        assert!(!memberships.member_of_group(MODERN_GROUP1));
415        assert!(memberships.member_of_group(MODERN_GROUP2));
416        let _: bool = memberships.del_membership(MODERN_GROUP2).expect("failed to del");
417        assert!(!memberships.member_of_group(MODERN_GROUP1));
418        assert!(!memberships.member_of_group(MODERN_GROUP2));
419
420        // Add memberships by their modern group and drop by their legacy group.
421        let _: bool = memberships.add_membership(MODERN_GROUP1).expect("failed to add");
422        let _: bool = memberships.add_membership(MODERN_GROUP2).expect("failed to add");
423        assert!(memberships.member_of_group(MODERN_GROUP1));
424        assert!(memberships.member_of_group(MODERN_GROUP2));
425        let _: Vec<Mutation> = memberships
426            .set_legacy_memberships(LegacyGroups(LEGACY_GROUP2))
427            .expect("failed to set legacy groups");
428        assert!(!memberships.member_of_group(MODERN_GROUP1));
429        assert!(memberships.member_of_group(MODERN_GROUP2));
430        let _: Vec<Mutation> = memberships
431            .set_legacy_memberships(LegacyGroups(0))
432            .expect("failed to set legacy groups");
433        assert!(!memberships.member_of_group(MODERN_GROUP1));
434        assert!(!memberships.member_of_group(MODERN_GROUP2));
435    }
436}