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

core: add support for vlan tag and vlan_filtering for bridge driver #146

Open
wants to merge 1 commit 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
41 changes: 41 additions & 0 deletions src/network/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ impl Core {
let mut response_net_addresses: Vec<NetAddress> = Vec::new();
// interfaces map, but we only ever expect one, for response
let mut interfaces: HashMap<String, types::NetInterface> = HashMap::new();
// any vlan id specified with bridge for tagging.
let mut vlan_id: u16 = 0;
let mut vlan_filtering: bool = false;
// mtu to configure, 0 means it was not set do nothing.
let mut mtu_config: u32 = 0;
if let Some(options_map) = network.options.as_ref() {
Expand All @@ -62,6 +65,25 @@ impl Core {
}
}

if let Some(options_map) = network.options.as_ref() {
if let Some(vlan) = options_map.get("vlan") {
match vlan.parse() {
Ok(vlan) => {
vlan_id = vlan;
if vlan_id != 0 {
vlan_filtering = true;
}
}
Err(err) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("unable to parse vlan_id: {}", err),
))
}
}
}
}

let container_veth_name: String = per_network_opts.interface_name.to_owned();
let static_ips = match per_network_opts.static_ips.as_ref() {
None => {
Expand Down Expand Up @@ -154,6 +176,7 @@ impl Core {
netns,
mtu_config,
ipv6_enabled,
vlan_filtering,
) {
Ok(addr) => addr,
Err(err) => {
Expand All @@ -163,6 +186,22 @@ impl Core {
))
}
};

if vlan_filtering {
//Wait for: https://github.com/little-dude/netlink/pull/222
//For a l2:
//Configure the vlan tag on the veth host side.
// flags must not be 100u16 instead use constants from upstream
if let Err(er) =
core_utils::CoreUtils::set_bridge_vlan(&host_veth_name, vlan_id, 100u16)
{
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed assigning bridge vlan tag {}", er),
));
}
}

debug!("Container veth mac: {:?}", container_veth_mac);
let interface = types::NetInterface {
mac_address: container_veth_mac,
Expand All @@ -185,6 +224,7 @@ impl Core {
netns: &str,
mtu_config: u32,
ipv6_enabled: bool,
vlan_filtering: bool,
) -> Result<String, std::io::Error> {
//copy subnet masks and gateway ips since we are going to use it later
let mut gw_ipaddr_clone = Vec::new();
Expand All @@ -197,6 +237,7 @@ impl Core {
gw_ipaddr,
mtu_config,
ipv6_enabled,
vlan_filtering,
) {
Ok(_) => (),
Err(err) => {
Expand Down
101 changes: 94 additions & 7 deletions src/network/core_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ use futures::stream::TryStreamExt;
use futures::StreamExt;
use libc;
use log::{debug, error};
use netlink_packet_route::link::nlas::AfSpecBridge;
use netlink_packet_route::link::nlas::Info;
use netlink_packet_route::link::nlas::InfoBridge;
use netlink_packet_route::link::nlas::InfoData;
use netlink_packet_route::link::nlas::InfoKind;
use nix::sched;
use rand::Rng;
use rtnetlink;
Expand Down Expand Up @@ -94,6 +99,84 @@ impl CoreUtils {
}
}

unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
::std::slice::from_raw_parts((p as *const T) as *const u8, ::std::mem::size_of::<T>())
}

#[tokio::main]
pub async fn set_bridge_vlan(
link_name: &str,
vlan_id: u16,
flags: u16,
) -> Result<(), std::io::Error> {
let (_connection, handle, _) = match rtnetlink::new_connection() {
Ok((conn, handle, messages)) => (conn, handle, messages),
Err(err) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed to connect: {}", err),
))
}
};

tokio::spawn(_connection);

let mut links = handle
.link()
.get()
.set_name_filter(link_name.to_string())
.execute();
match links.try_next().await {
Ok(Some(link)) => {
// locally scoped struct since we dont need it anywhere else
struct BridgeVlanInfo {
id: u16,
data: u16,
}

let mut request = handle.link().set(link.header.index);
request.message_mut().header.interface_family = AF_BRIDGE as u8;
request.message_mut().header.flags = 0;
let my_struct = BridgeVlanInfo {
id: vlan_id,
data: flags, //accept as arugment
};
let bytes: &[u8] = unsafe { CoreUtils::any_as_u8_slice(&my_struct) };

let bridge_flags = AfSpecBridge::Flags(BRIDGE_FLAGS_SELF);
let bridge_vlan_info = AfSpecBridge::VlanInfo(bytes.to_vec());
request
.message_mut()
.nlas
.push(Nla::AfSpecBridge(vec![bridge_flags, bridge_vlan_info]));

if let Err(err) = request.execute().await {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"failed to tag link with bridge vlan info {}: {}",
&link_name, err
),
));
}

return Ok(());
}
Err(err) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Unable to get link by name {}: {}", link_name, err),
));
}
Ok(None) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("No link found with name {}", link_name),
))
}
}
}

#[tokio::main]
pub async fn get_default_route_interface() -> Result<String, std::io::Error> {
let (_connection, handle, _) = match rtnetlink::new_connection() {
Expand Down Expand Up @@ -800,6 +883,7 @@ impl CoreUtils {
ips: Vec<ipnet::IpNet>,
mtu: u32,
ipv6_enabled: bool,
vlan_filtering: bool,
) -> Result<(), Error> {
let (_connection, handle, _) = match rtnetlink::new_connection() {
Ok((conn, handle, messages)) => (conn, handle, messages),
Expand All @@ -824,13 +908,16 @@ impl CoreUtils {
// I am unable to decipher how I can get get the link mode.
Ok(Some(_)) => (),
Ok(None) => {
if let Err(err) = handle
.link()
.add()
.bridge(ifname.to_string())
.execute()
.await
{
let mut request = handle.link().add().bridge(ifname.to_string());
if vlan_filtering {
let mut link_info_nlas = vec![Info::Kind(InfoKind::Bridge)];
let data = Some(InfoData::Bridge(vec![InfoBridge::VlanFiltering(1u8)]));
if let Some(data) = data {
link_info_nlas.push(Info::Data(data));
}
request.message_mut().nlas.push(Nla::Info(link_info_nlas));
}
if let Err(err) = request.execute().await {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed to create a bridge interface {}: {}", &ifname, err),
Expand Down