Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(link): add support of creating ipvlan and ipvtap. #62

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
68 changes: 68 additions & 0 deletions examples/create_ipvlan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT

use futures::stream::TryStreamExt;
use netlink_packet_route::link::IpVlanMode;
use rtnetlink::{new_connection, Error, Handle};
use std::env;

#[tokio::main]
async fn main() -> Result<(), String> {
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
usage();
return Ok(());
}
let link_name = &args[1];
let mode_str = &args[2];
let mode = match mode_str.as_str() {
"l2" => IpVlanMode::L2,
"l3" => IpVlanMode::L3,
"l3s" => IpVlanMode::L3S,
_ => {
usage();
return Ok(());
}
};

let (connection, handle, _) = new_connection().unwrap();
tokio::spawn(connection);

create_ipvlan(handle, link_name.to_string(), mode)
.await
.map_err(|e| format!("{e}"))
}

async fn create_ipvlan(
handle: Handle,
link_name: String,
mode: IpVlanMode,
) -> 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().ipvlan(
"test_ipvlan".into(),
link.header.index,
mode,
);
request.execute().await?
} else {
println!("no link {link_name} found");
}
Ok(())
}

fn usage() {
eprintln!(
"usage:
cargo run --example create_ipvlan -- <link_name> <ipvlan_mode>
ipvlan_mode can be one of the following:
l2: L2 mode
l3: L3 mode
l3s: L3S mode
Note that you need to run this program as root. Instead of running cargo as root,
build the example normally:
cargo build --example create_ipvlan
Then find the binary in the target directory:
cd target/debug/examples ; sudo ./create_ipvlan <link_name> <ipvlan_mode>"
);
}
73 changes: 73 additions & 0 deletions examples/create_ipvtap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: MIT

use futures::stream::TryStreamExt;
use netlink_packet_route::link::IpVtapMode;
use rtnetlink::{new_connection, Error, Handle};
use std::env;

#[tokio::main]
async fn main() -> Result<(), String> {
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
usage();
return Ok(());
}
let link_name = &args[1];
let mode_str = &args[2];
let mode = match mode_str.as_str() {
"l2" => IpVtapMode::L2,
"l3" => IpVtapMode::L3,
"l3s" => IpVtapMode::L3S,
_ => {
usage();
return Ok(());
}
};

let (connection, handle, _) = new_connection().unwrap();
tokio::spawn(connection);

create_ipvtap(handle, link_name.to_string(), mode)
.await
.map_err(|e| format!("{e}"))
}

async fn create_ipvtap(
handle: Handle,
link_name: String,
mode: IpVtapMode,
) -> 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().ipvtap(
"test_ipvtap".into(),
link.header.index,
mode,
);
request.execute().await?
} else {
println!("no link {link_name} found");
}
Ok(())
}

fn usage() {
eprintln!(
"usage:
cargo run --example create_ipvtap -- <link_name> <ipvtap_mode>

ipvtap_mode can be one of the following:
l2: L2 mode
l3: L3 mode
l3s: L3S mode

Note that you need to run this program as root. Instead of running cargo as root,
build the example normally:

cargo build --example create_ipvtap

Then find the binary in the target directory:

cd target/debug/examples ; sudo ./create_ipvtap <link_name> <ipvtap_mode>"
);
}
38 changes: 34 additions & 4 deletions src/link/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ use netlink_packet_core::{

use netlink_packet_route::{
link::{
BondMode, InfoBond, InfoData, InfoKind, InfoMacVlan, InfoMacVtap,
InfoVeth, InfoVlan, InfoVrf, InfoVxlan, InfoXfrm, LinkAttribute,
LinkFlags, LinkInfo, LinkMessage, MacVlanMode, MacVtapMode,
VlanQosMapping,
BondMode, InfoBond, InfoData, InfoIpVlan, InfoIpVtap, InfoKind,
InfoMacVlan, InfoMacVtap, InfoVeth, InfoVlan, InfoVrf, InfoVxlan,
InfoXfrm, IpVlanMode, IpVtapMode, LinkAttribute, LinkFlags, LinkInfo,
LinkMessage, MacVlanMode, MacVtapMode, VlanQosMapping,
},
RouteNetlinkMessage,
};
Expand Down Expand Up @@ -714,6 +714,36 @@ impl LinkAddRequest {
.up()
}

/// Create ipvlan on a link.
/// This is equivalent to `ip link add name NAME link LINK type ipvlan mode
/// IPVLAN_MODE`, but instead of specifying a link name (`LINK`), we
/// specify a link index. The mode is an integer consisting of
/// ipvlan modes. 0 is L2, 1 is L3, 2 is L3S.
pub fn ipvlan(self, name: String, index: u32, mode: IpVlanMode) -> Self {
self.name(name)
.link_info(
InfoKind::IpVlan,
Some(InfoData::IpVlan(vec![InfoIpVlan::Mode(mode)])),
)
.append_nla(LinkAttribute::Link(index))
.up()
}

/// Create ipvtap on a link.
/// This is equivalent to `ip link add name NAME link LINK type ipvtap mode
/// IPVTAP_MODE`, but instead of specifying a link name (`LINK`), we
/// specify a link index. The mode is an integer consisting of
/// ipvtap modes. 0 is L2, 1 is L3, 2 is L3S.
pub fn ipvtap(self, name: String, index: u32, mode: IpVtapMode) -> Self {
self.name(name)
.link_info(
InfoKind::IpVtap,
Some(InfoData::IpVtap(vec![InfoIpVtap::Mode(mode)])),
)
.append_nla(LinkAttribute::Link(index))
.up()
}

/// Create a VxLAN
/// This is equivalent to `ip link add name NAME type vxlan id VNI`,
/// it returns a VxlanAddRequest to further customize the vxlan
Expand Down