-
Notifications
You must be signed in to change notification settings - Fork 128
Support multiple static IPs per subnet #1379
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,6 +62,31 @@ where | |
| Ok(Some(val)) | ||
| } | ||
|
|
||
| fn validate_subnets(network: &types::Network) -> NetavarkResult<()> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you write some rust unit tests for this please. That allows us to cover some more subnet combinations and is faster than dealing with the more expensive integration tests |
||
| let subnets: Vec<&types::Subnet> = network.subnets.iter().flatten().collect(); | ||
| for (i, subnet1) in subnets.iter().enumerate() { | ||
| for subnet2 in subnets.iter().skip(i + 1) { | ||
| // Check for exact duplicate | ||
| if subnet1.subnet == subnet2.subnet { | ||
| return Err(NetavarkError::msg(format!( | ||
| "duplicate subnet defined: {}", | ||
| subnet1.subnet | ||
| ))); | ||
| } | ||
| // Check for overlap (one contains the other) | ||
| if subnet1.subnet.contains(&subnet2.subnet.network()) | ||
| || subnet2.subnet.contains(&subnet1.subnet.network()) | ||
| { | ||
| return Err(NetavarkError::msg(format!( | ||
| "overlapping subnets: {} and {}", | ||
| subnet1.subnet, subnet2.subnet | ||
| ))); | ||
| } | ||
| } | ||
| } | ||
| Ok(()) | ||
| } | ||
|
|
||
| pub fn get_ipam_addresses<'a>( | ||
| per_network_opts: &'a types::PerNetworkOptions, | ||
| network: &'a types::Network, | ||
|
|
@@ -91,8 +116,11 @@ pub fn get_ipam_addresses<'a>( | |
| Some(i) => i, | ||
| }; | ||
|
|
||
| // prepare a vector of static aps with appropriate cidr | ||
| for (idx, subnet) in network.subnets.iter().flatten().enumerate() { | ||
| // check for duplicates and overlaps subnets | ||
| validate_subnets(network)?; | ||
|
|
||
| // prepare a vector of static ips with appropriate cidr | ||
| for subnet in network.subnets.iter().flatten() { | ||
| let subnet_mask_cidr = subnet.subnet.prefix_len(); | ||
| if let Some(gw) = subnet.gateway { | ||
| let gw_net = match ipnet::IpNet::new(gw, subnet_mask_cidr) { | ||
|
|
@@ -112,20 +140,25 @@ pub fn get_ipam_addresses<'a>( | |
| ipv6_enabled = true; | ||
| } | ||
|
|
||
| // Build up response information | ||
| let container_address: ipnet::IpNet = | ||
| match format!("{}/{}", static_ips[idx], subnet_mask_cidr).parse() { | ||
| Ok(i) => i, | ||
| Err(e) => { | ||
| return Err(NetavarkError::SubnetParse(e)); | ||
| } | ||
| }; | ||
| // Add the IP to the address_vector | ||
| container_addresses.push(container_address); | ||
| net_addresses.push(types::NetAddress { | ||
| gateway: subnet.gateway, | ||
| ipnet: container_address, | ||
| }); | ||
| // build up response information - add all static IPs that match this subnet | ||
| for static_ip in static_ips { | ||
| // check if the static IP belongs to this subnet | ||
| if subnet.subnet.contains(static_ip) { | ||
| let container_address: ipnet::IpNet = | ||
| match format!("{}/{}", static_ip, subnet_mask_cidr).parse() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pre existing but it makes no sense to combine this into a string to parse it again, we can create the subnet directly |
||
| Ok(i) => i, | ||
| Err(e) => { | ||
| return Err(NetavarkError::SubnetParse(e)); | ||
| } | ||
| }; | ||
| // add the IP to the address_vector | ||
| container_addresses.push(container_address); | ||
| net_addresses.push(types::NetAddress { | ||
| gateway: subnet.gateway, | ||
| ipnet: container_address, | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let routes: Vec<Route> = match create_route_list(&network.routes) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -389,3 +389,76 @@ EOF | |
| assert_json "$default_route_v6" '.[0].dst' == "default" "Default route was selected" | ||
| assert_json "$default_route_v6" '.[0].metric' == "200" "v6 route metric matches v4" | ||
| } | ||
|
|
||
|
|
||
|
|
||
| @test "macvlan multiple static ips single subnet" { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a ton of duplication between the testfiles here, I would really prefer if we can start deduplicating some of this. i.e. compare test_port_fw function to generate a config dynamically for tests. Right now it duplicates all test files for each driver which is not great. |
||
| # Config file defines a single subnet: | ||
| # - 10.88.0.0/16 | ||
| # Static IPs: 10.88.0.5, 10.88.0.10, 10.88.0.15 | ||
|
|
||
| run_netavark --file ${TESTSDIR}/testfiles/macvlan-multiple-ips.json setup $(get_container_netns_path) | ||
| result="$output" | ||
|
|
||
| assert_json "$result" ".podman.interfaces.eth0.subnets | length" "==" "3" "three ips assigned" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[0].ipnet" "==" "10.88.0.5/16" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[1].ipnet" "==" "10.88.0.10/16" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[2].ipnet" "==" "10.88.0.15/16" | ||
|
|
||
| run_in_container_netns ip addr show eth0 | ||
| assert "$output" "=~" "10.88.0.5/16" | ||
| assert "$output" "=~" "10.88.0.10/16" | ||
| assert "$output" "=~" "10.88.0.15/16" | ||
|
|
||
| run_netavark --file ${TESTSDIR}/testfiles/macvlan-multiple-ips.json teardown $(get_container_netns_path) | ||
| } | ||
|
|
||
| @test "macvlan multiple static ips multiple subnets" { | ||
| # Config file defines two subnets: | ||
| # - 10.88.0.0/16 (gateway: 10.88.0.1) | ||
| # - 10.89.0.0/16 (gateway: 10.89.0.1) | ||
| # Static IPs: 10.88.0.5, 10.89.0.10, 10.88.0.15 | ||
|
|
||
| run_netavark --file ${TESTSDIR}/testfiles/macvlan-multiple-subnets.json setup $(get_container_netns_path) | ||
| result="$output" | ||
|
|
||
| assert_json "$result" ".podman.interfaces.eth0.subnets | length" "==" "3" "three ips assigned" | ||
|
|
||
| assert_json "$result" ".podman.interfaces.eth0.subnets[0].ipnet" "==" "10.88.0.5/16" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[0].gateway" "==" "10.88.0.1" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[1].ipnet" "==" "10.88.0.15/16" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[1].gateway" "==" "10.88.0.1" | ||
|
|
||
| assert_json "$result" ".podman.interfaces.eth0.subnets[2].ipnet" "==" "10.89.0.10/16" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[2].gateway" "==" "10.89.0.1" | ||
|
|
||
|
|
||
| run_in_container_netns ip addr show eth0 | ||
| assert "$output" "=~" "10.88.0.5/16" | ||
| assert "$output" "=~" "10.89.0.10/16" | ||
| assert "$output" "=~" "10.88.0.15/16" | ||
|
|
||
| run_netavark --file ${TESTSDIR}/testfiles/macvlan-multiple-subnets.json teardown $(get_container_netns_path) | ||
| } | ||
|
|
||
| @test "macvlan overlapping subnets error" { | ||
| # Config file defines overlapping subnets: | ||
| # - 10.1.2.0/23 covers 10.1.2.0-10.1.3.255 | ||
| # - 10.1.3.248/30 covers 10.1.3.248-10.1.3.251 (subset of the /23) | ||
| # Static IPs: 10.1.2.5, 10.1.3.249, 10.1.2.100 | ||
|
|
||
| expected_rc=1 run_netavark --file ${TESTSDIR}/testfiles/macvlan-overlapping-subnets.json setup $(get_container_netns_path) | ||
|
|
||
| assert "$output" "=~" "overlapping subnets" "error message should mention overlapping subnets" | ||
| } | ||
|
|
||
| @test "macvlan duplicate subnet definition error" { | ||
| # Config file defines the same subnet twice: | ||
| # - 10.88.0.0/16 (first definition) | ||
| # - 10.88.0.0/16 (duplicate) | ||
| # Static IPs: 10.88.0.5, 10.88.0.10, 10.88.0.15 | ||
|
|
||
| expected_rc=1 run_netavark --file ${TESTSDIR}/testfiles/macvlan-duplicate-subnet.json setup $(get_container_netns_path) | ||
|
|
||
| assert "$output" "=~" "duplicate subnet" "error message should mention duplicate subnet" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -323,3 +323,74 @@ EOF | |
| run_in_container_netns ip -o link show | ||
| assert "${#lines[@]}" == 2 "only two interfaces (lo, eth0) in the netns, the tmp ipvlan interface should be gone" | ||
| } | ||
|
|
||
| @test "ipvlan multiple static ips single subnet" { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will macvlan, too, support it?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it should. Should I add tests for that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, please.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added a macvlan test cases. |
||
| # Config file defines a single subnet: | ||
| # - 10.88.0.0/16 | ||
| # Static IPs: 10.88.0.5, 10.88.0.10, 10.88.0.15 | ||
|
|
||
| run_netavark --file ${TESTSDIR}/testfiles/ipvlan-multiple-ips.json setup $(get_container_netns_path) | ||
| result="$output" | ||
|
|
||
| assert_json "$result" ".podman.interfaces.eth0.subnets | length" "==" "3" "three ips assigned" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[0].ipnet" "==" "10.88.0.5/16" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[1].ipnet" "==" "10.88.0.10/16" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[2].ipnet" "==" "10.88.0.15/16" | ||
|
|
||
| run_in_container_netns ip addr show eth0 | ||
| assert "$output" "=~" "10.88.0.5/16" | ||
| assert "$output" "=~" "10.88.0.10/16" | ||
| assert "$output" "=~" "10.88.0.15/16" | ||
|
|
||
| run_netavark --file ${TESTSDIR}/testfiles/ipvlan-multiple-ips.json teardown $(get_container_netns_path) | ||
| } | ||
|
|
||
| @test "ipvlan multiple static ips multiple subnets" { | ||
| # Config file defines two subnets: | ||
| # - 10.88.0.0/16 (gateway: 10.88.0.1) | ||
| # - 10.89.0.0/16 (gateway: 10.89.0.1) | ||
| # Static IPs: 10.88.0.5, 10.89.0.10, 10.88.0.15 | ||
|
|
||
| run_netavark --file ${TESTSDIR}/testfiles/ipvlan-multiple-subnets.json setup $(get_container_netns_path) | ||
| result="$output" | ||
|
|
||
| assert_json "$result" ".podman.interfaces.eth0.subnets | length" "==" "3" "three ips assigned" | ||
|
|
||
| assert_json "$result" ".podman.interfaces.eth0.subnets[0].ipnet" "==" "10.88.0.5/16" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[0].gateway" "==" "10.88.0.1" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[1].ipnet" "==" "10.88.0.15/16" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[1].gateway" "==" "10.88.0.1" | ||
|
|
||
| assert_json "$result" ".podman.interfaces.eth0.subnets[2].ipnet" "==" "10.89.0.10/16" | ||
| assert_json "$result" ".podman.interfaces.eth0.subnets[2].gateway" "==" "10.89.0.1" | ||
|
|
||
|
|
||
| run_in_container_netns ip addr show eth0 | ||
| assert "$output" "=~" "10.88.0.5/16" | ||
| assert "$output" "=~" "10.89.0.10/16" | ||
| assert "$output" "=~" "10.88.0.15/16" | ||
|
|
||
| run_netavark --file ${TESTSDIR}/testfiles/ipvlan-multiple-subnets.json teardown $(get_container_netns_path) | ||
| } | ||
|
|
||
| @test "ipvlan overlapping subnets error" { | ||
| # Config file defines overlapping subnets: | ||
| # - 10.1.2.0/23 covers 10.1.2.0-10.1.3.255 | ||
| # - 10.1.3.248/30 covers 10.1.3.248-10.1.3.251 (subset of the /23) | ||
| # Static IPs: 10.1.2.5, 10.1.3.249, 10.1.2.100 | ||
|
|
||
| expected_rc=1 run_netavark --file ${TESTSDIR}/testfiles/ipvlan-overlapping-subnets.json setup $(get_container_netns_path) | ||
|
|
||
| assert "$output" "=~" "overlapping subnets" "error message should mention overlapping subnets" | ||
| } | ||
|
|
||
| @test "ipvlan duplicate subnet definition error" { | ||
| # Config file defines the same subnet twice: | ||
| # - 10.88.0.0/16 (first definition) | ||
| # - 10.88.0.0/16 (duplicate) | ||
| # Static IPs: 10.88.0.5, 10.88.0.10, 10.88.0.15 | ||
|
|
||
| expected_rc=1 run_netavark --file ${TESTSDIR}/testfiles/ipvlan-duplicate-subnet.json setup $(get_container_netns_path) | ||
|
|
||
| assert "$output" "=~" "duplicate subnet" "error message should mention duplicate subnet" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| { | ||
| "container_id": "someID", | ||
| "container_name": "someName", | ||
| "networks": { | ||
| "podman": { | ||
| "static_ips": [ | ||
| "10.88.0.5", | ||
| "10.88.0.10", | ||
| "10.88.0.15" | ||
| ], | ||
| "interface_name": "eth0" | ||
| } | ||
| }, | ||
| "network_info": { | ||
| "podman": { | ||
| "name": "podman", | ||
| "id": "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9", | ||
| "driver": "ipvlan", | ||
| "network_interface": "dummy0", | ||
| "subnets": [ | ||
| { | ||
| "subnet": "10.88.0.0/16", | ||
| "gateway": "10.88.0.1" | ||
| }, | ||
| { | ||
| "subnet": "10.88.0.0/16", | ||
| "gateway": "10.88.0.1" | ||
| } | ||
| ], | ||
| "ipv6_enabled": false, | ||
| "internal": false, | ||
| "dns_enabled": false, | ||
| "ipam_options": { | ||
| "driver": "host-local" | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| { | ||
| "container_id": "someID", | ||
| "container_name": "someName", | ||
| "networks": { | ||
| "podman": { | ||
| "static_ips": [ | ||
| "10.88.0.5", | ||
| "10.88.0.10", | ||
| "10.88.0.15" | ||
| ], | ||
| "interface_name": "eth0" | ||
| } | ||
| }, | ||
| "network_info": { | ||
| "podman": { | ||
| "name": "podman", | ||
| "id": "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9", | ||
| "driver": "ipvlan", | ||
| "network_interface": "dummy0", | ||
| "subnets": [ | ||
| { | ||
| "subnet": "10.88.0.0/16", | ||
| "gateway": "10.88.0.1" | ||
| } | ||
| ], | ||
| "ipv6_enabled": false, | ||
| "internal": false, | ||
| "dns_enabled": false, | ||
| "ipam_options": { | ||
| "driver": "host-local" | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| { | ||
| "container_id": "someID", | ||
| "container_name": "someName", | ||
| "networks": { | ||
| "podman": { | ||
| "static_ips": [ | ||
| "10.88.0.5", | ||
| "10.89.0.10", | ||
| "10.88.0.15" | ||
| ], | ||
| "interface_name": "eth0" | ||
| } | ||
| }, | ||
| "network_info": { | ||
| "podman": { | ||
| "name": "podman", | ||
| "id": "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9", | ||
| "driver": "ipvlan", | ||
| "network_interface": "dummy0", | ||
| "subnets": [ | ||
| { | ||
| "subnet": "10.88.0.0/16", | ||
| "gateway": "10.88.0.1" | ||
| }, | ||
| { | ||
| "subnet": "10.89.0.0/16", | ||
| "gateway": "10.89.0.1" | ||
| } | ||
| ], | ||
| "ipv6_enabled": false, | ||
| "internal": false, | ||
| "dns_enabled": false, | ||
| "ipam_options": { | ||
| "driver": "host-local" | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need ability to specify static IP address in CIDR format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you saying that you need multiple static IPs that are not in the same subnet?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multiple static IP addresses that part of multiple smaller subnets of a large configured subnet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain what the exact intention is here? I can't really understand the benefits of setting a subnet different from that of the actual network the interface is connected to, even if that subnet is part of the larger subnet the network uses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree different subnets on a static ip address allocation is not something I like to support.
The way to do that would be multiple --subnet options on network create.