1*5eaa5fbbSAlice Ryhl // SPDX-License-Identifier: GPL-2.0 2*5eaa5fbbSAlice Ryhl 3*5eaa5fbbSAlice Ryhl // Copyright (C) 2026 Google LLC. 4*5eaa5fbbSAlice Ryhl 5*5eaa5fbbSAlice Ryhl //! Rust support for generic netlink. 6*5eaa5fbbSAlice Ryhl //! 7*5eaa5fbbSAlice Ryhl //! Currently only supports exposing multicast groups. 8*5eaa5fbbSAlice Ryhl //! 9*5eaa5fbbSAlice Ryhl //! C header: [`include/net/genetlink.h`](srctree/include/net/genetlink.h) 10*5eaa5fbbSAlice Ryhl 11*5eaa5fbbSAlice Ryhl use kernel::{ 12*5eaa5fbbSAlice Ryhl alloc::{self, AllocError}, 13*5eaa5fbbSAlice Ryhl error::to_result, 14*5eaa5fbbSAlice Ryhl prelude::*, 15*5eaa5fbbSAlice Ryhl transmute::AsBytes, 16*5eaa5fbbSAlice Ryhl types::Opaque, 17*5eaa5fbbSAlice Ryhl ThisModule, 18*5eaa5fbbSAlice Ryhl }; 19*5eaa5fbbSAlice Ryhl 20*5eaa5fbbSAlice Ryhl use core::{ 21*5eaa5fbbSAlice Ryhl mem::ManuallyDrop, 22*5eaa5fbbSAlice Ryhl ptr::NonNull, // 23*5eaa5fbbSAlice Ryhl }; 24*5eaa5fbbSAlice Ryhl 25*5eaa5fbbSAlice Ryhl /// The default netlink message size. 26*5eaa5fbbSAlice Ryhl pub const GENLMSG_DEFAULT_SIZE: usize = bindings::GENLMSG_DEFAULT_SIZE; 27*5eaa5fbbSAlice Ryhl 28*5eaa5fbbSAlice Ryhl /// A wrapper around `struct sk_buff` for generic netlink messages. 29*5eaa5fbbSAlice Ryhl /// 30*5eaa5fbbSAlice Ryhl /// This type is intended to be specific for buffers used with netlink only, and other usecases for 31*5eaa5fbbSAlice Ryhl /// `struct sk_buff` are out-of-scope for this abstraction. 32*5eaa5fbbSAlice Ryhl /// 33*5eaa5fbbSAlice Ryhl /// # Invariants 34*5eaa5fbbSAlice Ryhl /// 35*5eaa5fbbSAlice Ryhl /// The pointer has ownership over a valid `sk_buff`. 36*5eaa5fbbSAlice Ryhl pub struct NetlinkSkBuff { 37*5eaa5fbbSAlice Ryhl skb: NonNull<kernel::bindings::sk_buff>, 38*5eaa5fbbSAlice Ryhl } 39*5eaa5fbbSAlice Ryhl 40*5eaa5fbbSAlice Ryhl impl NetlinkSkBuff { 41*5eaa5fbbSAlice Ryhl /// Creates a new `NetlinkSkBuff` with the given size. 42*5eaa5fbbSAlice Ryhl pub fn new(size: usize, flags: alloc::Flags) -> Result<NetlinkSkBuff, AllocError> { 43*5eaa5fbbSAlice Ryhl // SAFETY: `genlmsg_new` only requires its arguments to be valid integers. 44*5eaa5fbbSAlice Ryhl let skb = unsafe { bindings::genlmsg_new(size, flags.as_raw()) }; 45*5eaa5fbbSAlice Ryhl let skb = NonNull::new(skb).ok_or(AllocError)?; 46*5eaa5fbbSAlice Ryhl Ok(NetlinkSkBuff { skb }) 47*5eaa5fbbSAlice Ryhl } 48*5eaa5fbbSAlice Ryhl 49*5eaa5fbbSAlice Ryhl /// Puts a generic netlink header into the `NetlinkSkBuff`. 50*5eaa5fbbSAlice Ryhl pub fn genlmsg_put( 51*5eaa5fbbSAlice Ryhl self, 52*5eaa5fbbSAlice Ryhl portid: u32, 53*5eaa5fbbSAlice Ryhl seq: u32, 54*5eaa5fbbSAlice Ryhl family: &'static Family, 55*5eaa5fbbSAlice Ryhl cmd: u8, 56*5eaa5fbbSAlice Ryhl ) -> Result<GenlMsg, AllocError> { 57*5eaa5fbbSAlice Ryhl let skb = self.skb.as_ptr(); 58*5eaa5fbbSAlice Ryhl // SAFETY: The skb and family pointers are valid. 59*5eaa5fbbSAlice Ryhl let hdr = unsafe { bindings::genlmsg_put(skb, portid, seq, family.as_raw(), 0, cmd) }; 60*5eaa5fbbSAlice Ryhl let hdr = NonNull::new(hdr).ok_or(AllocError)?; 61*5eaa5fbbSAlice Ryhl Ok(GenlMsg { skb: self, hdr }) 62*5eaa5fbbSAlice Ryhl } 63*5eaa5fbbSAlice Ryhl } 64*5eaa5fbbSAlice Ryhl 65*5eaa5fbbSAlice Ryhl impl Drop for NetlinkSkBuff { 66*5eaa5fbbSAlice Ryhl fn drop(&mut self) { 67*5eaa5fbbSAlice Ryhl // SAFETY: We have ownership over the `sk_buff`, so we may free it. 68*5eaa5fbbSAlice Ryhl unsafe { bindings::nlmsg_free(self.skb.as_ptr()) } 69*5eaa5fbbSAlice Ryhl } 70*5eaa5fbbSAlice Ryhl } 71*5eaa5fbbSAlice Ryhl 72*5eaa5fbbSAlice Ryhl /// A generic netlink message being constructed. 73*5eaa5fbbSAlice Ryhl /// 74*5eaa5fbbSAlice Ryhl /// # Invariants 75*5eaa5fbbSAlice Ryhl /// 76*5eaa5fbbSAlice Ryhl /// `hdr` references the header in this netlink message. 77*5eaa5fbbSAlice Ryhl pub struct GenlMsg { 78*5eaa5fbbSAlice Ryhl skb: NetlinkSkBuff, 79*5eaa5fbbSAlice Ryhl hdr: NonNull<c_void>, 80*5eaa5fbbSAlice Ryhl } 81*5eaa5fbbSAlice Ryhl 82*5eaa5fbbSAlice Ryhl impl GenlMsg { 83*5eaa5fbbSAlice Ryhl /// Puts an attribute into the message. 84*5eaa5fbbSAlice Ryhl #[inline] 85*5eaa5fbbSAlice Ryhl fn put<T>(&mut self, attrtype: c_int, value: &T) -> Result 86*5eaa5fbbSAlice Ryhl where 87*5eaa5fbbSAlice Ryhl T: ?Sized + AsBytes, 88*5eaa5fbbSAlice Ryhl { 89*5eaa5fbbSAlice Ryhl let skb = self.skb.skb.as_ptr(); 90*5eaa5fbbSAlice Ryhl let len = size_of_val(value); 91*5eaa5fbbSAlice Ryhl let ptr = core::ptr::from_ref(value).cast::<c_void>(); 92*5eaa5fbbSAlice Ryhl // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and the provided value is 93*5eaa5fbbSAlice Ryhl // readable and initialized for its `size_of` bytes. 94*5eaa5fbbSAlice Ryhl to_result(unsafe { bindings::nla_put(skb, attrtype, len as c_int, ptr) }) 95*5eaa5fbbSAlice Ryhl } 96*5eaa5fbbSAlice Ryhl 97*5eaa5fbbSAlice Ryhl /// Puts a `u32` attribute into the message. 98*5eaa5fbbSAlice Ryhl #[inline] 99*5eaa5fbbSAlice Ryhl pub fn put_u32(&mut self, attrtype: c_int, value: u32) -> Result { 100*5eaa5fbbSAlice Ryhl self.put(attrtype, &value) 101*5eaa5fbbSAlice Ryhl } 102*5eaa5fbbSAlice Ryhl 103*5eaa5fbbSAlice Ryhl /// Puts a string attribute into the message. 104*5eaa5fbbSAlice Ryhl #[inline] 105*5eaa5fbbSAlice Ryhl pub fn put_string(&mut self, attrtype: c_int, value: &CStr) -> Result { 106*5eaa5fbbSAlice Ryhl self.put(attrtype, value.to_bytes_with_nul()) 107*5eaa5fbbSAlice Ryhl } 108*5eaa5fbbSAlice Ryhl 109*5eaa5fbbSAlice Ryhl /// Puts a flag attribute into the message. 110*5eaa5fbbSAlice Ryhl #[inline] 111*5eaa5fbbSAlice Ryhl pub fn put_flag(&mut self, attrtype: c_int) -> Result { 112*5eaa5fbbSAlice Ryhl let skb = self.skb.skb.as_ptr(); 113*5eaa5fbbSAlice Ryhl // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and a null pointer is valid 114*5eaa5fbbSAlice Ryhl // when the length is zero. 115*5eaa5fbbSAlice Ryhl to_result(unsafe { bindings::nla_put(skb, attrtype, 0, core::ptr::null()) }) 116*5eaa5fbbSAlice Ryhl } 117*5eaa5fbbSAlice Ryhl 118*5eaa5fbbSAlice Ryhl /// Sends the generic netlink message as a multicast message. 119*5eaa5fbbSAlice Ryhl #[inline] 120*5eaa5fbbSAlice Ryhl pub fn multicast( 121*5eaa5fbbSAlice Ryhl self, 122*5eaa5fbbSAlice Ryhl family: &'static Family, 123*5eaa5fbbSAlice Ryhl portid: u32, 124*5eaa5fbbSAlice Ryhl group: u32, 125*5eaa5fbbSAlice Ryhl flags: alloc::Flags, 126*5eaa5fbbSAlice Ryhl ) -> Result { 127*5eaa5fbbSAlice Ryhl let me = ManuallyDrop::new(self); 128*5eaa5fbbSAlice Ryhl // SAFETY: The `skb` and `family` pointers are valid. We pass ownership of the `skb` to 129*5eaa5fbbSAlice Ryhl // `genlmsg_multicast` by not dropping `self`. 130*5eaa5fbbSAlice Ryhl unsafe { 131*5eaa5fbbSAlice Ryhl bindings::genlmsg_end(me.skb.skb.as_ptr(), me.hdr.as_ptr()); 132*5eaa5fbbSAlice Ryhl to_result(bindings::genlmsg_multicast( 133*5eaa5fbbSAlice Ryhl family.as_raw(), 134*5eaa5fbbSAlice Ryhl me.skb.skb.as_ptr(), 135*5eaa5fbbSAlice Ryhl portid, 136*5eaa5fbbSAlice Ryhl group, 137*5eaa5fbbSAlice Ryhl flags.as_raw(), 138*5eaa5fbbSAlice Ryhl )) 139*5eaa5fbbSAlice Ryhl } 140*5eaa5fbbSAlice Ryhl } 141*5eaa5fbbSAlice Ryhl } 142*5eaa5fbbSAlice Ryhl impl Drop for GenlMsg { 143*5eaa5fbbSAlice Ryhl fn drop(&mut self) { 144*5eaa5fbbSAlice Ryhl // SAFETY: The `hdr` pointer references the header of this generic netlink message. 145*5eaa5fbbSAlice Ryhl unsafe { bindings::genlmsg_cancel(self.skb.skb.as_ptr(), self.hdr.as_ptr()) }; 146*5eaa5fbbSAlice Ryhl } 147*5eaa5fbbSAlice Ryhl } 148*5eaa5fbbSAlice Ryhl 149*5eaa5fbbSAlice Ryhl /// Flags for a generic netlink family. 150*5eaa5fbbSAlice Ryhl struct FamilyFlags { 151*5eaa5fbbSAlice Ryhl /// Whether the family supports network namespaces. 152*5eaa5fbbSAlice Ryhl netnsok: bool, 153*5eaa5fbbSAlice Ryhl /// Whether the family supports parallel operations. 154*5eaa5fbbSAlice Ryhl parallel_ops: bool, 155*5eaa5fbbSAlice Ryhl } 156*5eaa5fbbSAlice Ryhl 157*5eaa5fbbSAlice Ryhl impl FamilyFlags { 158*5eaa5fbbSAlice Ryhl /// Converts the flags to the bitfield representation used by `genl_family`. 159*5eaa5fbbSAlice Ryhl const fn into_bitfield(self) -> bindings::__BindgenBitfieldUnit<[u8; 1]> { 160*5eaa5fbbSAlice Ryhl // The below shifts are verified correct by test_family_flags_bitfield() below. 161*5eaa5fbbSAlice Ryhl // 162*5eaa5fbbSAlice Ryhl // Although bindgen generates helpers to change bitfields based on the C headers, these 163*5eaa5fbbSAlice Ryhl // helpers unfortunately can't be used in const context. Since `Family` needs to be filled 164*5eaa5fbbSAlice Ryhl // out at build-time, we use this helper instead. 165*5eaa5fbbSAlice Ryhl let mut bits = 0; 166*5eaa5fbbSAlice Ryhl if self.netnsok { 167*5eaa5fbbSAlice Ryhl bits |= 1 << 0; 168*5eaa5fbbSAlice Ryhl } 169*5eaa5fbbSAlice Ryhl if self.parallel_ops { 170*5eaa5fbbSAlice Ryhl bits |= 1 << 1; 171*5eaa5fbbSAlice Ryhl } 172*5eaa5fbbSAlice Ryhl // Convert from little endian to the target's endianness. 173*5eaa5fbbSAlice Ryhl bits = u8::from_le(bits); 174*5eaa5fbbSAlice Ryhl // SAFETY: This bitfield is represented as an u8. 175*5eaa5fbbSAlice Ryhl unsafe { core::mem::transmute::<u8, bindings::__BindgenBitfieldUnit<[u8; 1]>>(bits) } 176*5eaa5fbbSAlice Ryhl } 177*5eaa5fbbSAlice Ryhl } 178*5eaa5fbbSAlice Ryhl 179*5eaa5fbbSAlice Ryhl /// A generic netlink family. 180*5eaa5fbbSAlice Ryhl #[repr(transparent)] 181*5eaa5fbbSAlice Ryhl pub struct Family { 182*5eaa5fbbSAlice Ryhl inner: Opaque<bindings::genl_family>, 183*5eaa5fbbSAlice Ryhl } 184*5eaa5fbbSAlice Ryhl 185*5eaa5fbbSAlice Ryhl // SAFETY: The `Family` type is thread safe. 186*5eaa5fbbSAlice Ryhl unsafe impl Sync for Family {} 187*5eaa5fbbSAlice Ryhl 188*5eaa5fbbSAlice Ryhl impl Family { 189*5eaa5fbbSAlice Ryhl /// Creates a new `Family` instance. 190*5eaa5fbbSAlice Ryhl /// 191*5eaa5fbbSAlice Ryhl /// Intended to be used from const context only. Will panic if provided with invalid arguments. 192*5eaa5fbbSAlice Ryhl /// 193*5eaa5fbbSAlice Ryhl /// The name must be a nul-terminated string, but it is taken as `&[u8]` so that it can be used 194*5eaa5fbbSAlice Ryhl /// more conveniently with the strings generated by bindgen. 195*5eaa5fbbSAlice Ryhl pub const fn const_new( 196*5eaa5fbbSAlice Ryhl module: &ThisModule, 197*5eaa5fbbSAlice Ryhl name: &[u8], 198*5eaa5fbbSAlice Ryhl version: u32, 199*5eaa5fbbSAlice Ryhl mcgrps: &'static [MulticastGroup], 200*5eaa5fbbSAlice Ryhl ) -> Family { 201*5eaa5fbbSAlice Ryhl let n_mcgrps = mcgrps.len() as u8; 202*5eaa5fbbSAlice Ryhl if n_mcgrps as usize != mcgrps.len() { 203*5eaa5fbbSAlice Ryhl panic!("too many mcgrps"); 204*5eaa5fbbSAlice Ryhl } 205*5eaa5fbbSAlice Ryhl let mut genl_family = bindings::genl_family { 206*5eaa5fbbSAlice Ryhl version, 207*5eaa5fbbSAlice Ryhl _bitfield_1: FamilyFlags { 208*5eaa5fbbSAlice Ryhl netnsok: true, 209*5eaa5fbbSAlice Ryhl parallel_ops: true, 210*5eaa5fbbSAlice Ryhl } 211*5eaa5fbbSAlice Ryhl .into_bitfield(), 212*5eaa5fbbSAlice Ryhl module: module.as_ptr(), 213*5eaa5fbbSAlice Ryhl mcgrps: mcgrps.as_ptr().cast(), 214*5eaa5fbbSAlice Ryhl n_mcgrps, 215*5eaa5fbbSAlice Ryhl ..pin_init::zeroed() 216*5eaa5fbbSAlice Ryhl }; 217*5eaa5fbbSAlice Ryhl if CStr::from_bytes_with_nul(name).is_err() { 218*5eaa5fbbSAlice Ryhl panic!("genl_family name not nul-terminated"); 219*5eaa5fbbSAlice Ryhl } 220*5eaa5fbbSAlice Ryhl if genl_family.name.len() < name.len() { 221*5eaa5fbbSAlice Ryhl panic!("genl_family name too long"); 222*5eaa5fbbSAlice Ryhl } 223*5eaa5fbbSAlice Ryhl let mut i = 0; 224*5eaa5fbbSAlice Ryhl while i < name.len() { 225*5eaa5fbbSAlice Ryhl genl_family.name[i] = name[i]; 226*5eaa5fbbSAlice Ryhl i += 1; 227*5eaa5fbbSAlice Ryhl } 228*5eaa5fbbSAlice Ryhl Family { 229*5eaa5fbbSAlice Ryhl inner: Opaque::new(genl_family), 230*5eaa5fbbSAlice Ryhl } 231*5eaa5fbbSAlice Ryhl } 232*5eaa5fbbSAlice Ryhl 233*5eaa5fbbSAlice Ryhl /// Checks if there are any listeners for the given multicast group. 234*5eaa5fbbSAlice Ryhl pub fn has_listeners(&self, group: u32) -> bool { 235*5eaa5fbbSAlice Ryhl // SAFETY: The family and init_net pointers are valid. 236*5eaa5fbbSAlice Ryhl unsafe { 237*5eaa5fbbSAlice Ryhl bindings::genl_has_listeners(self.as_raw(), &raw mut bindings::init_net, group) != 0 238*5eaa5fbbSAlice Ryhl } 239*5eaa5fbbSAlice Ryhl } 240*5eaa5fbbSAlice Ryhl 241*5eaa5fbbSAlice Ryhl /// Returns a raw pointer to the underlying `genl_family` structure. 242*5eaa5fbbSAlice Ryhl pub fn as_raw(&self) -> *mut bindings::genl_family { 243*5eaa5fbbSAlice Ryhl self.inner.get() 244*5eaa5fbbSAlice Ryhl } 245*5eaa5fbbSAlice Ryhl } 246*5eaa5fbbSAlice Ryhl 247*5eaa5fbbSAlice Ryhl /// A generic netlink multicast group. 248*5eaa5fbbSAlice Ryhl #[repr(transparent)] 249*5eaa5fbbSAlice Ryhl pub struct MulticastGroup { 250*5eaa5fbbSAlice Ryhl // No Opaque because fully immutable 251*5eaa5fbbSAlice Ryhl group: bindings::genl_multicast_group, 252*5eaa5fbbSAlice Ryhl } 253*5eaa5fbbSAlice Ryhl 254*5eaa5fbbSAlice Ryhl // SAFETY: Pure data so thread safe. 255*5eaa5fbbSAlice Ryhl unsafe impl Sync for MulticastGroup {} 256*5eaa5fbbSAlice Ryhl 257*5eaa5fbbSAlice Ryhl impl MulticastGroup { 258*5eaa5fbbSAlice Ryhl /// Creates a new `MulticastGroup` instance. 259*5eaa5fbbSAlice Ryhl /// 260*5eaa5fbbSAlice Ryhl /// Intended to be used from const context only. Will panic if provided with invalid arguments. 261*5eaa5fbbSAlice Ryhl pub const fn const_new(name: &CStr) -> MulticastGroup { 262*5eaa5fbbSAlice Ryhl let mut group: bindings::genl_multicast_group = pin_init::zeroed(); 263*5eaa5fbbSAlice Ryhl 264*5eaa5fbbSAlice Ryhl let name = name.to_bytes_with_nul(); 265*5eaa5fbbSAlice Ryhl if group.name.len() < name.len() { 266*5eaa5fbbSAlice Ryhl panic!("genl_multicast_group name too long"); 267*5eaa5fbbSAlice Ryhl } 268*5eaa5fbbSAlice Ryhl let mut i = 0; 269*5eaa5fbbSAlice Ryhl while i < name.len() { 270*5eaa5fbbSAlice Ryhl group.name[i] = name[i]; 271*5eaa5fbbSAlice Ryhl i += 1; 272*5eaa5fbbSAlice Ryhl } 273*5eaa5fbbSAlice Ryhl 274*5eaa5fbbSAlice Ryhl MulticastGroup { group } 275*5eaa5fbbSAlice Ryhl } 276*5eaa5fbbSAlice Ryhl } 277*5eaa5fbbSAlice Ryhl 278*5eaa5fbbSAlice Ryhl /// A registration of a generic netlink family. 279*5eaa5fbbSAlice Ryhl /// 280*5eaa5fbbSAlice Ryhl /// This type represents the registration of a [`Family`]. When an instance of this type is 281*5eaa5fbbSAlice Ryhl /// dropped, its respective generic netlink family will be unregistered from the system. 282*5eaa5fbbSAlice Ryhl /// 283*5eaa5fbbSAlice Ryhl /// # Invariants 284*5eaa5fbbSAlice Ryhl /// 285*5eaa5fbbSAlice Ryhl /// `self.family` always holds a valid reference to an initialized and registered [`Family`]. 286*5eaa5fbbSAlice Ryhl pub struct Registration { 287*5eaa5fbbSAlice Ryhl family: &'static Family, 288*5eaa5fbbSAlice Ryhl } 289*5eaa5fbbSAlice Ryhl 290*5eaa5fbbSAlice Ryhl impl Family { 291*5eaa5fbbSAlice Ryhl /// Registers the generic netlink family with the kernel. 292*5eaa5fbbSAlice Ryhl pub fn register(&'static self) -> Result<Registration> { 293*5eaa5fbbSAlice Ryhl // SAFETY: `self.as_raw()` is a valid pointer to a `genl_family` struct. 294*5eaa5fbbSAlice Ryhl // The `genl_family` struct is static, so it will outlive the registration. 295*5eaa5fbbSAlice Ryhl to_result(unsafe { bindings::genl_register_family(self.as_raw()) })?; 296*5eaa5fbbSAlice Ryhl Ok(Registration { family: self }) 297*5eaa5fbbSAlice Ryhl } 298*5eaa5fbbSAlice Ryhl } 299*5eaa5fbbSAlice Ryhl 300*5eaa5fbbSAlice Ryhl impl Drop for Registration { 301*5eaa5fbbSAlice Ryhl fn drop(&mut self) { 302*5eaa5fbbSAlice Ryhl // SAFETY: `self.family.as_raw()` is a valid pointer to a registered `genl_family` struct. 303*5eaa5fbbSAlice Ryhl // The `Registration` struct ensures that `genl_unregister_family` is called exactly once 304*5eaa5fbbSAlice Ryhl // for this family when it goes out of scope. 305*5eaa5fbbSAlice Ryhl unsafe { bindings::genl_unregister_family(self.family.as_raw()) }; 306*5eaa5fbbSAlice Ryhl } 307*5eaa5fbbSAlice Ryhl } 308*5eaa5fbbSAlice Ryhl 309*5eaa5fbbSAlice Ryhl #[macros::kunit_tests(rust_netlink)] 310*5eaa5fbbSAlice Ryhl mod tests { 311*5eaa5fbbSAlice Ryhl use super::*; 312*5eaa5fbbSAlice Ryhl 313*5eaa5fbbSAlice Ryhl #[test] 314*5eaa5fbbSAlice Ryhl fn test_family_flags_bitfield() { 315*5eaa5fbbSAlice Ryhl for netnsok in [false, true] { 316*5eaa5fbbSAlice Ryhl for parallel_ops in [false, true] { 317*5eaa5fbbSAlice Ryhl let mut b_fam = bindings::genl_family { 318*5eaa5fbbSAlice Ryhl ..Default::default() 319*5eaa5fbbSAlice Ryhl }; 320*5eaa5fbbSAlice Ryhl b_fam.set_netnsok(if netnsok { 1 } else { 0 }); 321*5eaa5fbbSAlice Ryhl b_fam.set_parallel_ops(if parallel_ops { 1 } else { 0 }); 322*5eaa5fbbSAlice Ryhl 323*5eaa5fbbSAlice Ryhl let c_bitfield = FamilyFlags { 324*5eaa5fbbSAlice Ryhl netnsok, 325*5eaa5fbbSAlice Ryhl parallel_ops, 326*5eaa5fbbSAlice Ryhl } 327*5eaa5fbbSAlice Ryhl .into_bitfield(); 328*5eaa5fbbSAlice Ryhl 329*5eaa5fbbSAlice Ryhl // SAFETY: The bit field is stored as u8. 330*5eaa5fbbSAlice Ryhl let b_val: u8 = unsafe { core::mem::transmute(b_fam._bitfield_1) }; 331*5eaa5fbbSAlice Ryhl // SAFETY: The bit field is stored as u8. 332*5eaa5fbbSAlice Ryhl let c_val: u8 = unsafe { core::mem::transmute(c_bitfield) }; 333*5eaa5fbbSAlice Ryhl assert_eq!(b_val, c_val); 334*5eaa5fbbSAlice Ryhl } 335*5eaa5fbbSAlice Ryhl } 336*5eaa5fbbSAlice Ryhl } 337*5eaa5fbbSAlice Ryhl } 338