Skip to content

Commit 0de9600

Browse files
Merge pull request #815 from MrSuicideParrot/bridge-vrf-support
Add vrf support for bridges
2 parents d93d03c + 4b6d5ab commit 0de9600

File tree

4 files changed

+104
-1
lines changed

4 files changed

+104
-1
lines changed

src/network/bridge.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use super::{
1919
constants::{
2020
ISOLATE_OPTION_FALSE, ISOLATE_OPTION_STRICT, ISOLATE_OPTION_TRUE,
2121
NO_CONTAINER_INTERFACE_ERROR, OPTION_ISOLATE, OPTION_METRIC, OPTION_MTU,
22-
OPTION_NO_DEFAULT_ROUTE,
22+
OPTION_NO_DEFAULT_ROUTE, OPTION_VRF,
2323
},
2424
core_utils::{self, get_ipam_addresses, join_netns, parse_option, CoreUtils},
2525
driver::{self, DriverInfo},
@@ -50,6 +50,8 @@ struct InternalData {
5050
metric: Option<u32>,
5151
/// if set, no default gateway will be added
5252
no_default_route: bool,
53+
/// sef vrf for bridge
54+
vrf: Option<String>,
5355
// TODO: add vlan
5456
}
5557

@@ -81,6 +83,7 @@ impl driver::NetworkDriver for Bridge<'_> {
8183
let metric: u32 = parse_option(&self.info.network.options, OPTION_METRIC)?.unwrap_or(100);
8284
let no_default_route: bool =
8385
parse_option(&self.info.network.options, OPTION_NO_DEFAULT_ROUTE)?.unwrap_or(false);
86+
let vrf: Option<String> = parse_option(&self.info.network.options, OPTION_VRF)?;
8487

8588
let static_mac = match &self.info.per_network_opts.static_mac {
8689
Some(mac) => Some(CoreUtils::decode_address_from_hex(mac)?),
@@ -96,6 +99,7 @@ impl driver::NetworkDriver for Bridge<'_> {
9699
isolate,
97100
metric: Some(metric),
98101
no_default_route,
102+
vrf,
99103
});
100104
Ok(())
101105
}
@@ -494,6 +498,15 @@ fn create_interfaces(
494498
InfoKind::Bridge,
495499
);
496500
create_link_opts.mtu = data.mtu;
501+
502+
if let Some(vrf_name) = &data.vrf {
503+
let vrf = match host.get_link(netlink::LinkID::Name(vrf_name.to_string())) {
504+
Ok(vrf) => check_link_is_vrf(vrf, vrf_name)?,
505+
Err(err) => return Err(err).wrap("get vrf to set up bridge interface"),
506+
};
507+
create_link_opts.primary_index = vrf.header.index;
508+
}
509+
497510
host.create_link(create_link_opts).wrap("create bridge")?;
498511

499512
if data.ipam.ipv6_enabled {
@@ -672,6 +685,30 @@ fn check_link_is_bridge(msg: LinkMessage, br_name: &str) -> NetavarkResult<LinkM
672685
)))
673686
}
674687

688+
/// make sure the LinkMessage is the kind VRF
689+
fn check_link_is_vrf(msg: LinkMessage, vrf_name: &str) -> NetavarkResult<LinkMessage> {
690+
for nla in msg.nlas.iter() {
691+
if let Nla::Info(info) = nla {
692+
for inf in info.iter() {
693+
if let Info::Kind(kind) = inf {
694+
if *kind == InfoKind::Vrf {
695+
return Ok(msg);
696+
} else {
697+
return Err(NetavarkError::Message(format!(
698+
"vrf {} already exists but is a {:?} interface",
699+
vrf_name, kind
700+
)));
701+
}
702+
}
703+
}
704+
}
705+
}
706+
Err(NetavarkError::Message(format!(
707+
"could not determine namespace link kind for vrf {}",
708+
vrf_name
709+
)))
710+
}
711+
675712
fn remove_link(
676713
host: &mut netlink::Socket,
677714
netns: &mut netlink::Socket,

src/network/constants.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub const OPTION_MODE: &str = "mode";
2121
pub const OPTION_METRIC: &str = "metric";
2222
pub const OPTION_NO_DEFAULT_ROUTE: &str = "no_default_route";
2323
pub const OPTION_BCLIM: &str = "bclim";
24+
pub const OPTION_VRF: &str = "vrf";
2425

2526
/// 100 is the default metric for most Linux networking tools.
2627
pub const DEFAULT_METRIC: u32 = 100;

test/600-bridge-vrf.bats

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env bats -*- bats -*-
2+
#
3+
# bridge driver tests with vrf
4+
#
5+
6+
load helpers
7+
8+
@test vrf - bridge with vrf {
9+
run_in_host_netns ip link add test-vrf type vrf table 10
10+
run_in_host_netns ip link set dev test-vrf up
11+
12+
run_netavark --file ${TESTSDIR}/testfiles/simplebridge-vrf.json setup $(get_container_netns_path)
13+
14+
# check if vrf exists
15+
run_in_host_netns ip -j --details link show podman0
16+
result="$output"
17+
assert_json "$result" ".[].linkinfo.info_slave_kind" "==" "vrf" "Bridge has a vrf set"
18+
assert_json "$result" ".[].master" "==" "test-vrf" "Bridge has the correct vrf set"
19+
}
20+
21+
@test vrf - simple bridge {
22+
run_netavark --file ${TESTSDIR}/testfiles/simplebridge.json setup $(get_container_netns_path)
23+
run_in_host_netns ip -j --details link show podman0
24+
result="$output"
25+
assert_json "$result" ".[].linkinfo.info_slave_kind" "==" "null" "VRF is not set"
26+
}
27+
28+
@test vrf - non existent vrf {
29+
expected_rc=1 run_netavark --file ${TESTSDIR}/testfiles/simplebridge-vrf.json setup $(get_container_netns_path)
30+
result="$output"
31+
assert_json "$result" ".error" "==" "get vrf to set up bridge interface: Netlink error: No such device (os error 19)" "Attempt to set a non existent vrf"
32+
}
33+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"container_id": "6ce776ea58b5",
3+
"container_name": "testcontainer",
4+
"networks": {
5+
"podman": {
6+
"interface_name": "eth0",
7+
"static_ips": [
8+
"10.88.0.2"
9+
]
10+
}
11+
},
12+
"network_info": {
13+
"podman": {
14+
"dns_enabled": false,
15+
"driver": "bridge",
16+
"id": "53ce4390f2adb1681eb1a90ec8b48c49c015e0a8d336c197637e7f65e365fa9e",
17+
"internal": false,
18+
"ipv6_enabled": false,
19+
"name": "podman",
20+
"network_interface": "podman0",
21+
"subnets": [
22+
{
23+
"gateway": "10.88.0.1",
24+
"subnet": "10.88.0.0/16"
25+
}
26+
],
27+
"options": {
28+
"vrf": "test-vrf"
29+
}
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)