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

netlink add ecmp route #66

Merged
merged 1 commit into from
Nov 5, 2021
Merged
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
105 changes: 68 additions & 37 deletions src/network/core_utils.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
use futures::stream::TryStreamExt;
use futures::StreamExt;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we using this import anywhere. I am not able to see in the diff.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I it need for let mut response = match handle.clone().request(req), do not ask me why.
Also the linter fails with unused imports so we should be safe.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah i see call to request returns a future Stream

use ipnetwork::IpNetwork;
use ipnetwork::Ipv4Network;
use ipnetwork::Ipv6Network;
use libc;
use nix::sched;
use rtnetlink;
use rtnetlink::packet::constants::*;
use rtnetlink::packet::rtnl::link::nlas::Nla;
use rtnetlink::packet::NetlinkPayload;
use rtnetlink::packet::RouteMessage;
use std::fmt::Write;
use std::fs::File;
use std::io::Error;
use std::net::IpAddr;
use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use std::os::unix::prelude::*;
use std::process;
use std::thread;
Expand Down Expand Up @@ -133,45 +140,67 @@ impl CoreUtils {
async fn add_route_v4(
handle: &rtnetlink::Handle,
dest: &Ipv4Network,
gateway: &Ipv4Network,
gateway: &Ipv4Addr,
) -> Result<(), std::io::Error> {
let route = handle.route();
match route
let msg = route
.add()
.v4()
.destination_prefix(dest.ip(), dest.prefix())
.gateway(gateway.ip())
.execute()
.await
{
Ok(_) => Ok(()),
Err(err) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed to add route: {}", err),
)),
}
.gateway(*gateway)
.message_mut()
.to_owned();

CoreUtils::execute_route_msg(handle, msg).await
flouthoc marked this conversation as resolved.
Show resolved Hide resolved
}

async fn add_route_v6(
handle: &rtnetlink::Handle,
dest: &Ipv6Network,
gateway: &Ipv6Network,
gateway: &Ipv6Addr,
) -> Result<(), std::io::Error> {
let route = handle.route();
match route
let msg = route
.add()
.v6()
.destination_prefix(dest.ip(), dest.prefix())
.gateway(gateway.ip())
.execute()
.await
{
Ok(_) => Ok(()),
Err(err) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed to add route: {}", err),
)),
.gateway(*gateway)
.message_mut()
.to_owned();

CoreUtils::execute_route_msg(handle, msg).await
flouthoc marked this conversation as resolved.
Show resolved Hide resolved
}

async fn execute_route_msg(
handle: &rtnetlink::Handle,
msg: RouteMessage,
) -> Result<(), std::io::Error> {
// Note: we do not use .execute because we have to overwrite the request flags
// by default NLM_F_EXCL is set and this throws an error if we try to create multiple default routes
// We need to create a default route for each network because we need to keep the internet connectivity
// after a podman disconnect via the other network.
let mut req =
rtnetlink::packet::NetlinkMessage::from(rtnetlink::packet::RtnlMessage::NewRoute(msg));
req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE;

let mut response = match handle.clone().request(req) {
Ok(res) => res,
Err(err) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed to add route: {}", err),
));
}
};
while let Some(message) = response.next().await {
if let NetlinkPayload::Error(err) = message.payload {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed to add route: {}", err),
));
}
}
Ok(())
}

#[tokio::main]
Expand Down Expand Up @@ -624,7 +653,7 @@ impl CoreUtils {
for gw_ip_add in gw_ip_addrs {
match gw_ip_add.to_string().parse() {
Ok(gateway) => match gateway {
IpNetwork::V4(gateway) => match "0.0.0.0/0".to_string().parse() {
IpAddr::V4(gateway) => match Ipv4Network::new(Ipv4Addr::new(0, 0, 0, 0), 0) {
Ok(dest) => {
if let Err(err) =
CoreUtils::add_route_v4(&handle, &dest, &gateway).await
Expand All @@ -639,21 +668,23 @@ impl CoreUtils {
))
}
},
IpNetwork::V6(gateway) => match "::/0".to_string().parse() {
Ok(dest) => {
if let Err(err) =
CoreUtils::add_route_v6(&handle, &dest, &gateway).await
{
return Err(err);
IpAddr::V6(gateway) => {
match Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 0) {
Ok(dest) => {
if let Err(err) =
CoreUtils::add_route_v6(&handle, &dest, &gateway).await
{
return Err(err);
}
}
Err(err) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed to parse address ::/0: {}", err),
))
}
}
Err(err) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("failed to parse address ::/0: {}", err),
))
}
},
}
},
Err(err) => {
return Err(std::io::Error::new(
Expand Down
58 changes: 58 additions & 0 deletions src/test/config/twoNetworks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"container_id": "2d0fe608dc86d7ba65dcec724a335b618328f08daa62afd2ee7fa9d41f74e5a9",
"container_name": "",
"networks": {
"podman1": {
"static_ips": [
"10.0.0.2"
],
"interface_name": "eth0"
},
"podman2": {
"static_ips": [
"10.1.0.2"
],
"interface_name": "eth1"
}
},
"network_info": {
"podman1": {
"name": "podman1",
"id": "4937f73ac0df011d4f2848d5f83f5c20b707e71a8d98789bbe80d8f64a815e79",
"driver": "bridge",
"network_interface": "podman1",
"created": "2021-11-04T19:08:39.124321192+01:00",
"subnets": [
{
"subnet": "10.0.0.0/24",
"gateway": "10.0.0.1"
}
],
"ipv6_enabled": false,
"internal": false,
"dns_enabled": false,
"ipam_options": {
"driver": "host-local"
}
},
"podman2": {
"name": "podman2",
"id": "488a7d9be4fa72a5b80811bd847aac1d99d1a09060739b4e08687949c957cda8",
"driver": "bridge",
"network_interface": "podman2",
"created": "2021-11-04T19:08:39.124800596+01:00",
"subnets": [
{
"subnet": "10.1.0.0/24",
"gateway": "10.1.0.1"
}
],
"ipv6_enabled": false,
"internal": false,
"dns_enabled": false,
"ipam_options": {
"driver": "host-local"
}
}
}
}