From fe0667b8e961464ee8856011540466f20c2e4af9 Mon Sep 17 00:00:00 2001 From: Miguel Duarte Barroso Date: Tue, 3 Oct 2023 18:29:39 +0200 Subject: [PATCH] macvlan: allow specifying MAC on iface ADD Signed-off-by: Miguel Duarte Barroso --- Cargo.toml | 1 + examples/create_macvlan.rs | 21 ++++++++++++++++----- src/link/add.rs | 16 ++++++++++++---- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a331262..92a3c1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ netlink-proto = { default-features = false, version = "0.11" } nix = { version = "0.26.1", default-features = false, features = ["fs", "mount", "sched", "signal"] } tokio = { version = "1.0.1", features = ["rt"], optional = true} async-global-executor = { version = "2.0.2", optional = true } +macaddr = "1.0" [dev-dependencies] env_logger = "0.10.0" diff --git a/examples/create_macvlan.rs b/examples/create_macvlan.rs index c0810c6..5425a8e 100644 --- a/examples/create_macvlan.rs +++ b/examples/create_macvlan.rs @@ -1,22 +1,31 @@ // SPDX-License-Identifier: MIT use futures::stream::TryStreamExt; +use macaddr::MacAddr; use rtnetlink::{new_connection, Error, Handle}; -use std::env; +use std::{env, str::FromStr}; #[tokio::main] async fn main() -> Result<(), String> { let args: Vec = env::args().collect(); - if args.len() != 2 { + if args.len() != 2 && args.len() != 3 { usage(); return Ok(()); } let link_name = &args[1]; + let mac: Option> = if args.len() == 3 { + let mac_address_arg = (&args[2]).to_string(); + let mac_address = MacAddr::from_str(mac_address_arg.as_str()) + .map_err(|e| format!("{e}"))?; + Some(mac_address.as_bytes().into()) + } else { + None + }; let (connection, handle, _) = new_connection().unwrap(); tokio::spawn(connection); - create_macvlan(handle, link_name.to_string()) + create_macvlan(handle, link_name.to_string(), mac) .await .map_err(|e| format!("{e}")) } @@ -24,6 +33,7 @@ async fn main() -> Result<(), String> { async fn create_macvlan( handle: Handle, link_name: String, + mac_address: Option>, ) -> Result<(), Error> { let mut links = handle.link().get().match_name(link_name.clone()).execute(); if let Some(link) = links.try_next().await? { @@ -31,6 +41,7 @@ async fn create_macvlan( "test_macvlan".into(), link.header.index, 4u32, // bridge mode + mac_address, ); request.execute().await? } else { @@ -42,7 +53,7 @@ async fn create_macvlan( fn usage() { eprintln!( "usage: - cargo run --example create_macvlan -- + cargo run --example create_macvlan -- [mac address] Note that you need to run this program as root. Instead of running cargo as root, build the example normally: @@ -51,6 +62,6 @@ build the example normally: Then find the binary in the target directory: - cd target/debug/examples ; sudo ./create_macvlan " + cd target/debug/examples ; sudo ./create_macvlan [mac address]" ); } diff --git a/src/link/add.rs b/src/link/add.rs index d72b48e..93f3a1d 100644 --- a/src/link/add.rs +++ b/src/link/add.rs @@ -684,14 +684,22 @@ impl LinkAddRequest { /// flags from MACVLAN_MODE (netlink-packet-route/src/rtnl/constants.rs) /// being: _PRIVATE, _VEPA, _BRIDGE, _PASSTHRU, _SOURCE, which can be /// *combined*. - pub fn macvlan(self, name: String, index: u32, mode: u32) -> Self { - self.name(name) + pub fn macvlan( + self, + name: String, + index: u32, + mode: u32, + mac_address: Option>, + ) -> Self { + let mut m = self + .name(name) .link_info( InfoKind::MacVlan, Some(InfoData::MacVlan(vec![InfoMacVlan::Mode(mode)])), ) - .append_nla(Nla::Link(index)) - .up() + .append_nla(Nla::Link(index)); + mac_address.map(|mac| m.message_mut().nlas.push(Nla::Address(mac))); + m.up() } /// Create macvtap on a link.