diff --git a/src/network/core.rs b/src/network/core.rs index fdeb5ce7e..ca92827c1 100644 --- a/src/network/core.rs +++ b/src/network/core.rs @@ -44,6 +44,9 @@ impl Core { let mut response_net_addresses: Vec = Vec::new(); // interfaces map, but we only ever expect one, for response let mut interfaces: HashMap = 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() { @@ -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 => { @@ -154,6 +176,7 @@ impl Core { netns, mtu_config, ipv6_enabled, + vlan_filtering, ) { Ok(addr) => addr, Err(err) => { @@ -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, @@ -185,6 +224,7 @@ impl Core { netns: &str, mtu_config: u32, ipv6_enabled: bool, + vlan_filtering: bool, ) -> Result { //copy subnet masks and gateway ips since we are going to use it later let mut gw_ipaddr_clone = Vec::new(); @@ -197,6 +237,7 @@ impl Core { gw_ipaddr, mtu_config, ipv6_enabled, + vlan_filtering, ) { Ok(_) => (), Err(err) => { diff --git a/src/network/core_utils.rs b/src/network/core_utils.rs index ae7228510..bfacc858c 100644 --- a/src/network/core_utils.rs +++ b/src/network/core_utils.rs @@ -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; @@ -94,6 +99,84 @@ impl CoreUtils { } } + unsafe fn any_as_u8_slice(p: &T) -> &[u8] { + ::std::slice::from_raw_parts((p as *const T) as *const u8, ::std::mem::size_of::()) + } + + #[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 { let (_connection, handle, _) = match rtnetlink::new_connection() { @@ -800,6 +883,7 @@ impl CoreUtils { ips: Vec, mtu: u32, ipv6_enabled: bool, + vlan_filtering: bool, ) -> Result<(), Error> { let (_connection, handle, _) = match rtnetlink::new_connection() { Ok((conn, handle, messages)) => (conn, handle, messages), @@ -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),