Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions examples/set_bridge_vlan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: MIT

use futures_util::stream::TryStreamExt;
use rtnetlink::{
new_connection,
packet_route::{
link::{BridgeVlanInfoFlags, LinkAttribute, LinkExtentMask},
AddressFamily,
},
Handle, LinkBridge, LinkBridgeVlan, LinkDummy, LinkUnspec,
};

async fn create_bridge_and_get_index(handle: &Handle) -> Result<u32, String> {
handle
.link()
.add(
LinkBridge::new("my-bridge0")
.vlan_filtering(true)
.up()
.build(),
)
.execute()
.await
.map_err(|e| format!("{e}"))?;

let mut bridge_links = handle
.link()
.get()
.match_name("my-bridge0".to_string())
.execute();
if let Some(bridge_link) =
bridge_links.try_next().await.map_err(|e| format!("{e}"))?
{
Ok(bridge_link.header.index)
} else {
Err("failed to find my-bridge0".into())
}
}

async fn create_dummy_and_attach_to_bridge(
handle: &Handle,
bridge_index: u32,
) -> Result<u32, rtnetlink::Error> {
handle
.link()
.add(LinkDummy::new("my-dummy0").build())
.execute()
.await?;

handle
.link()
.set(
LinkUnspec::new_with_name("my-dummy0")
.controller(bridge_index)
.down()
.build(),
)
.execute()
.await?;

let mut dummy_links = handle
.link()
.get()
.match_name("my-dummy0".to_string())
.execute();
if let Some(dummy_link) = dummy_links.try_next().await? {
Ok(dummy_link.header.index)
} else {
panic!("failed to find my-dummy0")
}
}

async fn set_bridge_vlan(
handle: &Handle,
port_index: u32,
) -> Result<(), rtnetlink::Error> {
let message = LinkBridgeVlan::new(port_index)
.vlan(10, BridgeVlanInfoFlags::Pvid)
.vlan_range_start(20, BridgeVlanInfoFlags::empty())
.vlan_range_end(30, BridgeVlanInfoFlags::empty())
.build();

handle.link().set(message).execute().await?;
Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (connection, handle, _) = new_connection().unwrap();
tokio::spawn(connection);

let bridge_index = create_bridge_and_get_index(&handle).await?;

let port_index =
create_dummy_and_attach_to_bridge(&handle, bridge_index).await?;
set_bridge_vlan(&handle, port_index).await?;

let mut dump_link = handle
.link()
.get()
.set_filter_mask(
AddressFamily::Bridge,
vec![LinkExtentMask::BrvlanCompressed],
)
.execute();
while let Some(link_msg) = dump_link.try_next().await? {
// With set_filter_mask(), we cannot use match_name to filter due to
// linux kernel limitation.
if !link_msg.attributes.as_slice().iter().any(
|a| matches!(a, LinkAttribute::IfName(name) if name == "my-dummy0"),
) {
continue;
}
for nla in link_msg.attributes {
if let LinkAttribute::AfSpecBridge(i) = &nla {
println!("{:?}", i);
return Ok(());
}
}
}
Err("failed to find bridge vlan info for my-dummy0".into())
}
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ pub use crate::{
handle::Handle,
link::{
LinkAddRequest, LinkBond, LinkBondPort, LinkBridge, LinkBridgePort,
LinkDelPropRequest, LinkDelRequest, LinkDummy, LinkGetRequest,
LinkHandle, LinkMacSec, LinkMacVlan, LinkMacVtap, LinkMessageBuilder,
LinkNetkit, LinkSetRequest, LinkUnspec, LinkVeth, LinkVlan, LinkVrf,
LinkVxlan, LinkWireguard, LinkXfrm, QosMapping,
LinkBridgeVlan, LinkDelPropRequest, LinkDelRequest, LinkDummy,
LinkGetRequest, LinkHandle, LinkMacSec, LinkMacVlan, LinkMacVtap,
LinkMessageBuilder, LinkNetkit, LinkSetRequest, LinkUnspec, LinkVeth,
LinkVlan, LinkVrf, LinkVxlan, LinkWireguard, LinkXfrm, QosMapping,
},
multicast::MulticastGroup,
neighbour::{
Expand Down
61 changes: 61 additions & 0 deletions src/link/bridge_vlan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT

use crate::{
packet_route::{
link::{
AfSpecBridge, BridgeVlanInfo, BridgeVlanInfoFlags, LinkAttribute,
},
AddressFamily,
},
LinkMessageBuilder,
};

#[derive(Debug)]
pub struct LinkBridgeVlan;

impl LinkBridgeVlan {
pub fn new(port_index: u32) -> LinkMessageBuilder<Self> {
LinkMessageBuilder::<LinkBridgeVlan>::default()
.index(port_index)
.interface_family(AddressFamily::Bridge)
}
}

impl LinkMessageBuilder<LinkBridgeVlan> {
/// Append arbitrary [LinkAttribute::AfSpecBridge]
pub fn append_af_spec(self, spec: AfSpecBridge) -> Self {
let mut ret = self;

for attr in ret.extra_attriutes.iter_mut() {
if let LinkAttribute::AfSpecBridge(specs) = attr {
specs.push(spec);
return ret;
}
}

ret.append_extra_attribute(LinkAttribute::AfSpecBridge(vec![spec]))
}

pub fn vlan(self, vid: u16, flags: BridgeVlanInfoFlags) -> Self {
self.append_af_spec(AfSpecBridge::VlanInfo(BridgeVlanInfo {
vid,
flags,
}))
}

/// Helper function by adding [BridgeVlanInfoFlags::RangeBegin]
/// automatically to `flags`
pub fn vlan_range_start(
self,
vid: u16,
flags: BridgeVlanInfoFlags,
) -> Self {
self.vlan(vid, flags | BridgeVlanInfoFlags::RangeBegin)
}

/// Helper function by adding [BridgeVlanInfoFlags::RangeEnd]
/// automatically to `flags`
pub fn vlan_range_end(self, vid: u16, flags: BridgeVlanInfoFlags) -> Self {
self.vlan(vid, flags | BridgeVlanInfoFlags::RangeEnd)
}
}
17 changes: 13 additions & 4 deletions src/link/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

use std::{marker::PhantomData, os::fd::RawFd};

use crate::packet_route::link::{
InfoData, InfoKind, InfoPortData, InfoPortKind, LinkAttribute, LinkFlags,
LinkHeader, LinkInfo, LinkMessage,
use crate::packet_route::{
link::{
InfoData, InfoKind, InfoPortData, InfoPortKind, LinkAttribute,
LinkFlags, LinkHeader, LinkInfo, LinkMessage,
},
AddressFamily,
};

/// Generic interface without interface type
Expand Down Expand Up @@ -57,7 +60,7 @@ pub struct LinkMessageBuilder<T> {
pub(crate) info_data: Option<InfoData>,
pub(crate) port_kind: Option<InfoPortKind>,
pub(crate) port_data: Option<InfoPortData>,
extra_attriutes: Vec<LinkAttribute>,
pub(crate) extra_attriutes: Vec<LinkAttribute>,
_phantom: PhantomData<T>,
}

Expand Down Expand Up @@ -164,6 +167,12 @@ impl<T> LinkMessageBuilder<T> {
ret
}

pub fn interface_family(self, family: AddressFamily) -> Self {
let mut ret = self;
ret.header.interface_family = family;
ret
}

/// Define the hardware address of the link when creating it (equivalent to
/// `ip link add NAME address ADDRESS`)
pub fn address(self, address: Vec<u8>) -> Self {
Expand Down
9 changes: 9 additions & 0 deletions src/link/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ mod bond;
mod bond_port;
mod bridge;
mod bridge_port;
#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "android"))]
mod bridge_vlan;
mod builder;
mod del;
mod dummy;
Expand Down Expand Up @@ -52,3 +54,10 @@ pub use self::{

#[cfg(test)]
mod test;

#[cfg(any(
target_os = "linux",
target_os = "fuchsia",
target_os = "android"
))]
pub use self::bridge_vlan::LinkBridgeVlan;