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_warn;
38
39// Safe "as" conversion because u32::BITS (32) will fit into any usize.
40const U32_BITS_USIZE: usize = u32::BITS as usize;
41
42/// A modern (non-legacy) multicast group. Interpreted as a single group.
43#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
44pub struct ModernGroup(pub u32);
45
46impl Into<usize> for ModernGroup {
47    fn into(self) -> usize {
48        let ModernGroup(group) = self;
49        group.try_into().expect("expected usize >= u32")
50    }
51}
52
53/// An error indicating that a modern group has no mapping to a legacy
54/// group.
55#[derive(Debug)]
56pub struct NoMappingFromModernToLegacyGroupError;
57
58impl TryFrom<ModernGroup> for SingleLegacyGroup {
59    type Error = NoMappingFromModernToLegacyGroupError;
60
61    fn try_from(
62        ModernGroup(group): ModernGroup,
63    ) -> Result<SingleLegacyGroup, NoMappingFromModernToLegacyGroupError> {
64        let group = 1 << group;
65        if group == 0 {
66            Err(NoMappingFromModernToLegacyGroupError)
67        } else {
68            Ok(SingleLegacyGroup(group))
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/// Multicast group semantics that are specific to a particular protocol family.
102pub(crate) trait MulticastCapableNetlinkFamily {
103    /// Returns true if the given [`ModernGroup`] is a valid multicast group.
104    fn is_valid_group(group: &ModernGroup) -> bool;
105}
106
107/// Translate the given legacy group membership to a modern membership.
108///
109/// Returns `None` if the given legacy_group does not exist in this family.
110fn legacy_to_modern<F: MulticastCapableNetlinkFamily>(
111    group: SingleLegacyGroup,
112) -> Option<ModernGroup> {
113    let modern_group = ModernGroup(group.inner().ilog2() + 1);
114    F::is_valid_group(&modern_group).then_some(modern_group)
115}
116
117/// Manages the current multicast group memberships of a single connection to
118/// Netlink.
119///
120/// Memberships are stored entirely using the modern set of constants. Legacy
121/// memberships are translated to their modern equivalent before being stored.
122#[derive(Debug)]
123pub(crate) struct MulticastGroupMemberships<F: MulticastCapableNetlinkFamily> {
124    /// Aspects of multicast group memberships that are family specific.
125    family: PhantomData<F>,
126
127    // The current multicast group memberships, stored as modern memberships.
128    // Membership in multicast group "N" is determined by whether the "Nth" bit
129    // of the `BitSet` is set.
130    memberships: BitSet,
131}
132
133/// Error returned when attempting to join an invalid [`ModernGroup`].
134#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
135pub struct InvalidModernGroupError;
136
137#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
138pub(crate) enum Mutation {
139    None,
140    Add(ModernGroup),
141    Del(ModernGroup),
142}
143
144/// Error returned when attempting to join invalid [`LegacyGroups`].
145#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
146pub struct InvalidLegacyGroupsError;
147
148impl<F: MulticastCapableNetlinkFamily> MulticastGroupMemberships<F> {
149    /// Instantiate a new [`MulticastGroupMemberships`].
150    pub(crate) fn new() -> MulticastGroupMemberships<F> {
151        MulticastGroupMemberships { family: PhantomData, memberships: Default::default() }
152    }
153
154    /// Returns `True` if `self` is a member of `group`.
155    pub(crate) fn member_of_group(&self, group: ModernGroup) -> bool {
156        self.memberships.contains(group.into())
157    }
158
159    /// Adds the given multicast group membership.
160    ///
161    /// Returns `true` if the membership was newly added.
162    pub(crate) fn add_membership(
163        &mut self,
164        group: ModernGroup,
165    ) -> Result<bool, InvalidModernGroupError> {
166        let MulticastGroupMemberships { family: _, memberships } = self;
167        if !F::is_valid_group(&group) {
168            return Err(InvalidModernGroupError);
169        }
170        let was_absent = memberships.insert(group.into());
171        return Ok(was_absent);
172    }
173
174    /// Deletes the given multicast group membership.
175    ///
176    /// Returns `true` if the membership was newly removed.
177    pub(crate) fn del_membership(
178        &mut self,
179        group: ModernGroup,
180    ) -> Result<bool, InvalidModernGroupError> {
181        let MulticastGroupMemberships { family: _, memberships } = self;
182        if !F::is_valid_group(&group) {
183            return Err(InvalidModernGroupError);
184        }
185        let was_present = memberships.remove(group.into());
186        return Ok(was_present);
187    }
188
189    /// Sets the legacy multicast group memberships.
190    ///
191    /// Legacy memberships are translated into their modern equivalent before
192    /// being written.
193    ///
194    /// Returns the mutations applied.
195    pub(crate) fn set_legacy_memberships(
196        &mut self,
197        LegacyGroups(requested_groups): LegacyGroups,
198    ) -> Result<Vec<Mutation>, InvalidLegacyGroupsError> {
199        crate::logging::log_debug!("set_legacy_memberships");
200        let MulticastGroupMemberships { family: _, memberships } = self;
201
202        let mut mutations = [Mutation::None; U32_BITS_USIZE];
203        // Validate and record all the mutations that will need to be applied.
204        for i in 0..U32_BITS_USIZE {
205            let raw_legacy_group = 1 << i;
206            let legacy_group = raw_legacy_group
207                .try_into()
208                .expect("raw_legacy_group unexpectedly had multiple bits set");
209            let modern_group = legacy_to_modern::<F>(legacy_group);
210            let is_member_of_group = requested_groups & raw_legacy_group != 0;
211            mutations[i] = match (modern_group, is_member_of_group) {
212                (Some(modern_group), true) => Mutation::Add(modern_group),
213                (Some(modern_group), false) => Mutation::Del(modern_group),
214                (None, true) => {
215                    log_warn!(
216                        "failed to join legacy groups ({:?}) because of invalid group: {:?}",
217                        requested_groups,
218                        legacy_group
219                    );
220                    return Err(InvalidLegacyGroupsError);
221                }
222                (None, false) => Mutation::None,
223            };
224        }
225
226        let mut return_mutations = Vec::new();
227        // Apply all of the mutations.
228        for mutation in mutations {
229            match mutation {
230                Mutation::None => {}
231                Mutation::Add(group) => {
232                    crate::logging::log_debug!("mutation: add {group:?}");
233                    let was_absent = memberships.insert(group.into());
234                    if was_absent {
235                        return_mutations.push(Mutation::Add(group));
236                    }
237                }
238                Mutation::Del(group) => {
239                    crate::logging::log_debug!("mutation: del {group:?}");
240                    let was_present = memberships.remove(group.into());
241                    if was_present {
242                        return_mutations.push(Mutation::Del(group));
243                    }
244                }
245            }
246        }
247        Ok(return_mutations)
248    }
249
250    pub(crate) fn iter_groups(&self) -> impl Iterator<Item = ModernGroup> + '_ {
251        self.memberships.into_iter().map(|n: usize| {
252            let n = u32::try_from(n).expect("all ModernGroups fit in u32");
253            ModernGroup(n)
254        })
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261
262    use crate::protocol_family::testutil::{
263        FakeProtocolFamily, INVALID_LEGACY_GROUP, INVALID_MODERN_GROUP, LEGACY_GROUP1,
264        LEGACY_GROUP2, LEGACY_GROUP3, MODERN_GROUP1, MODERN_GROUP2, MODERN_GROUP3,
265    };
266
267    #[test]
268    fn test_single_legacy_groups() {
269        assert_eq!(0.try_into(), Ok(SingleLegacyGroup(0)));
270        assert_eq!(0x00010000.try_into(), Ok(SingleLegacyGroup(0x00010000)));
271        assert_eq!(
272            <u32 as TryInto<SingleLegacyGroup>>::try_into(0x00010100),
273            Err(MultipleBitsSetError {})
274        );
275    }
276
277    #[test]
278    fn test_add_del_membership() {
279        let mut memberships = MulticastGroupMemberships::<FakeProtocolFamily>::new();
280
281        assert!(!memberships.member_of_group(MODERN_GROUP1));
282        assert!(!memberships.member_of_group(MODERN_GROUP2));
283        assert!(!memberships.member_of_group(MODERN_GROUP3));
284
285        // Add one membership, and verify the others are unaffected.
286        let changed = memberships.add_membership(MODERN_GROUP1).expect("failed to add");
287        assert!(changed, "should have changed group memberships");
288        assert!(memberships.member_of_group(MODERN_GROUP1));
289        assert!(!memberships.member_of_group(MODERN_GROUP2));
290        assert!(!memberships.member_of_group(MODERN_GROUP3));
291        // Add a second & third membership.
292        let changed = memberships.add_membership(MODERN_GROUP2).expect("failed to add");
293        assert!(changed, "should have changed group memberships");
294        let changed = memberships.add_membership(MODERN_GROUP3).expect("failed to add");
295        assert!(changed, "should have changed group memberships");
296        assert!(memberships.member_of_group(MODERN_GROUP1));
297        assert!(memberships.member_of_group(MODERN_GROUP2));
298        assert!(memberships.member_of_group(MODERN_GROUP3));
299        // Remove one membership, and verify the others are unaffected.
300        let changed = memberships.del_membership(MODERN_GROUP1).expect("failed to del");
301        assert!(changed, "should have changed group memberships");
302        assert!(!memberships.member_of_group(MODERN_GROUP1));
303        assert!(memberships.member_of_group(MODERN_GROUP2));
304        assert!(memberships.member_of_group(MODERN_GROUP3));
305        // Remove the second & third membership.
306        let changed = memberships.del_membership(MODERN_GROUP2).expect("failed to del");
307        assert!(changed, "should have changed group memberships");
308        let changed = memberships.del_membership(MODERN_GROUP3).expect("failed to del");
309        assert!(changed, "should have changed group memberships");
310        assert!(!memberships.member_of_group(MODERN_GROUP1));
311        assert!(!memberships.member_of_group(MODERN_GROUP2));
312        assert!(!memberships.member_of_group(MODERN_GROUP3));
313        // Verify Adding/Deleting an invalid group fails.
314        assert_eq!(
315            memberships.add_membership(INVALID_MODERN_GROUP),
316            Err(InvalidModernGroupError {})
317        );
318        assert_eq!(
319            memberships.del_membership(INVALID_MODERN_GROUP),
320            Err(InvalidModernGroupError {})
321        );
322    }
323
324    #[test]
325    fn test_legacy_memberships() {
326        let mut memberships = MulticastGroupMemberships::<FakeProtocolFamily>::new();
327
328        assert!(!memberships.member_of_group(MODERN_GROUP1));
329        assert!(!memberships.member_of_group(MODERN_GROUP2));
330        assert!(!memberships.member_of_group(MODERN_GROUP3));
331
332        // Add one membership and verify the others are unaffected.
333        let mutations = memberships
334            .set_legacy_memberships(LegacyGroups(LEGACY_GROUP1))
335            .expect("failed to set legacy groups");
336        let expected_mutations = [Mutation::Add(MODERN_GROUP1)];
337        assert_eq!(mutations, expected_mutations);
338        assert!(memberships.member_of_group(MODERN_GROUP1));
339        assert!(!memberships.member_of_group(MODERN_GROUP2));
340        assert!(!memberships.member_of_group(MODERN_GROUP3));
341        // Add a second & third membership.
342        let mutations = memberships
343            .set_legacy_memberships(LegacyGroups(LEGACY_GROUP1 | LEGACY_GROUP2 | LEGACY_GROUP3))
344            .expect("failed to set legacy groups");
345        let expected_mutations = [Mutation::Add(MODERN_GROUP2), Mutation::Add(MODERN_GROUP3)];
346        assert_eq!(mutations, expected_mutations);
347        assert!(memberships.member_of_group(MODERN_GROUP1));
348        assert!(memberships.member_of_group(MODERN_GROUP2));
349        assert!(memberships.member_of_group(MODERN_GROUP3));
350        // Remove one membership and verify the others are unaffected.
351        let mutations = memberships
352            .set_legacy_memberships(LegacyGroups(LEGACY_GROUP2 | LEGACY_GROUP3))
353            .expect("failed to set legacy_groups");
354        let expected_mutations = [Mutation::Del(MODERN_GROUP1)];
355        assert_eq!(mutations, expected_mutations);
356        assert!(!memberships.member_of_group(MODERN_GROUP1));
357        assert!(memberships.member_of_group(MODERN_GROUP2));
358        assert!(memberships.member_of_group(MODERN_GROUP3));
359        // Remove the second & third membership.
360        let mutations = memberships
361            .set_legacy_memberships(LegacyGroups(0))
362            .expect("failed to set legacy groups");
363        let expected_mutations = [Mutation::Del(MODERN_GROUP2), Mutation::Del(MODERN_GROUP3)];
364        assert_eq!(mutations, expected_mutations);
365        assert!(!memberships.member_of_group(MODERN_GROUP1));
366        assert!(!memberships.member_of_group(MODERN_GROUP2));
367        assert!(!memberships.member_of_group(MODERN_GROUP3));
368        // Verify that setting an invalid group fails.
369        assert_eq!(
370            memberships.set_legacy_memberships(LegacyGroups(INVALID_LEGACY_GROUP)),
371            Err(InvalidLegacyGroupsError {})
372        );
373    }
374
375    #[test]
376    fn test_legacy_and_modern_memberships() {
377        let mut memberships = MulticastGroupMemberships::<FakeProtocolFamily>::new();
378
379        assert!(!memberships.member_of_group(MODERN_GROUP1));
380        assert!(!memberships.member_of_group(MODERN_GROUP2));
381
382        // Add memberships by their legacy group and drop by their modern group.
383        let _: Vec<Mutation> = memberships
384            .set_legacy_memberships(LegacyGroups(LEGACY_GROUP1 | LEGACY_GROUP2))
385            .expect("failed to set legacy groups");
386        assert!(memberships.member_of_group(MODERN_GROUP1));
387        assert!(memberships.member_of_group(MODERN_GROUP2));
388        let _: bool = memberships.del_membership(MODERN_GROUP1).expect("failed to del");
389        assert!(!memberships.member_of_group(MODERN_GROUP1));
390        assert!(memberships.member_of_group(MODERN_GROUP2));
391        let _: bool = memberships.del_membership(MODERN_GROUP2).expect("failed to del");
392        assert!(!memberships.member_of_group(MODERN_GROUP1));
393        assert!(!memberships.member_of_group(MODERN_GROUP2));
394
395        // Add memberships by their modern group and drop by their legacy group.
396        let _: bool = memberships.add_membership(MODERN_GROUP1).expect("failed to add");
397        let _: bool = memberships.add_membership(MODERN_GROUP2).expect("failed to add");
398        assert!(memberships.member_of_group(MODERN_GROUP1));
399        assert!(memberships.member_of_group(MODERN_GROUP2));
400        let _: Vec<Mutation> = memberships
401            .set_legacy_memberships(LegacyGroups(LEGACY_GROUP2))
402            .expect("failed to set legacy groups");
403        assert!(!memberships.member_of_group(MODERN_GROUP1));
404        assert!(memberships.member_of_group(MODERN_GROUP2));
405        let _: Vec<Mutation> = memberships
406            .set_legacy_memberships(LegacyGroups(0))
407            .expect("failed to set legacy groups");
408        assert!(!memberships.member_of_group(MODERN_GROUP1));
409        assert!(!memberships.member_of_group(MODERN_GROUP2));
410    }
411}