Skip to content

Commit

Permalink
macvlan: allow specifying MAC on iface ADD
Browse files Browse the repository at this point in the history
Signed-off-by: Miguel Duarte Barroso <[email protected]>
  • Loading branch information
maiqueb committed Oct 3, 2023
1 parent 5e5c688 commit fe0667b
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 9 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
21 changes: 16 additions & 5 deletions examples/create_macvlan.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,47 @@
// 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<String> = 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<Vec<u8>> = 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}"))
}

async fn create_macvlan(
handle: Handle,
link_name: String,
mac_address: Option<Vec<u8>>,
) -> Result<(), Error> {
let mut links = handle.link().get().match_name(link_name.clone()).execute();
if let Some(link) = links.try_next().await? {
let request = handle.link().add().macvlan(
"test_macvlan".into(),
link.header.index,
4u32, // bridge mode
mac_address,
);
request.execute().await?
} else {
Expand All @@ -42,7 +53,7 @@ async fn create_macvlan(
fn usage() {
eprintln!(
"usage:
cargo run --example create_macvlan -- <link name>
cargo run --example create_macvlan -- <link name> [mac address]
Note that you need to run this program as root. Instead of running cargo as root,
build the example normally:
Expand All @@ -51,6 +62,6 @@ build the example normally:
Then find the binary in the target directory:
cd target/debug/examples ; sudo ./create_macvlan <link_name>"
cd target/debug/examples ; sudo ./create_macvlan <link_name> [mac address]"
);
}
16 changes: 12 additions & 4 deletions src/link/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>>,
) -> 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.
Expand Down

0 comments on commit fe0667b

Please sign in to comment.