Skip to content

Commit

Permalink
tests/kernel: finish test_redirect_to_ip
Browse files Browse the repository at this point in the history
  • Loading branch information
hack3ric committed Jan 7, 2025
1 parent 0dea81e commit 6ef171e
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 24 deletions.
62 changes: 53 additions & 9 deletions src/integration_tests/helpers/kernel/rtnl.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
use crate::net::IpWithPrefix;
use futures::TryStreamExt;
use nix::net::if_::if_nametoindex;
use rand::distributions::Alphanumeric;
use rand::Rng;
use rtnetlink::packet_route::link::InfoKind;
use rtnetlink::packet_route::route::{RouteMessage, RouteProtocol};
use rtnetlink::packet_route::rule::RuleAttribute::{self, *};
use rtnetlink::packet_route::rule::{RuleAction, RuleHeader, RuleMessage};
use rtnetlink::packet_route::route::{RouteAttribute, RouteMessage, RouteProtocol};
use rtnetlink::packet_route::rule::{RuleAction, RuleAttribute, RuleHeader, RuleMessage};
use rtnetlink::packet_route::AddressFamily;
use rtnetlink::packet_utils::nla::Nla;
use rtnetlink::packet_utils::Emitable;
use rtnetlink::{Handle, IpVersion, LinkMessageBuilder, LinkUnspec, RouteMessageBuilder};
use std::cmp::Ordering;
use std::net::IpAddr;

pub async fn create_dummy_link(handle: &Handle, name: &str, addr: IpWithPrefix) -> anyhow::Result<()> {
pub async fn create_dummy_link(handle: &Handle, addr: IpWithPrefix) -> anyhow::Result<u32> {
let name: String = "dummy_"
.chars()
.chain(rand::thread_rng().sample_iter(&Alphanumeric).take(8).map(char::from))
.collect();
let link_msg = LinkMessageBuilder::<LinkUnspec>::new_with_info_kind(InfoKind::Dummy)
.name(name.into())
.name(name.clone())
.up()
.build();
handle.link().add(link_msg).execute().await?;
let index = if_nametoindex(name)?;
let index = if_nametoindex(&*name)?;
handle.address().add(index, addr.addr(), addr.prefix_len()).execute().await?;
Ok(index)
}

pub async fn remove_link(handle: &Handle, index: u32) -> anyhow::Result<()> {
handle.link().del(index).execute().await?;
Ok(())
}

pub async fn get_ip_rule(handle: &Handle, ip_version: IpVersion) -> anyhow::Result<Vec<RuleMessage>> {
use RuleAttribute::*;

let mut buf = Vec::new();
let mut stream = handle.rule().get(ip_version).execute();
while let Some(mut msg) = stream.try_next().await? {
Expand All @@ -47,6 +59,8 @@ pub async fn get_ip_rule(handle: &Handle, ip_version: IpVersion) -> anyhow::Resu
}

pub fn make_ip_rule_mark(ip_version: IpVersion, prio: u32, mark: u32, table: u32) -> RuleMessage {
use RuleAttribute::*;

let mut msg = RuleMessage::default();
msg.header = RuleHeader {
family: match ip_version {
Expand Down Expand Up @@ -86,9 +100,39 @@ pub async fn get_ip_route(handle: &Handle, ip_version: IpVersion, table: u32) ->
IpVersion::V6 => AddressFamily::Inet6,
};
let mut buf = Vec::new();
let mut stream = handle.route().get(msg).dump(false).execute();
while let Some(msg) = stream.try_next().await? {
buf.push(msg);
let mut stream = handle.route().get(msg).execute();
while let Some(mut msg) = stream.try_next().await? {
if msg.header.table as u32 == table || msg.attributes.contains(&RouteAttribute::Table(table)) {
route_msg_normalize(&mut msg);
buf.push(msg);
}
}
Ok(buf)
}

pub fn route_msg_normalize(msg: &mut RouteMessage) {
for attr in &msg.attributes {
if let RouteAttribute::Table(table) = attr {
if *table > 0xff {
msg.header.table = 0;
}
}
}
msg.attributes.sort_by(route_attr_sort);
}

fn route_attr_sort(a: &RouteAttribute, b: &RouteAttribute) -> Ordering {
match a.kind().cmp(&b.kind()) {
Ordering::Equal => {}
ord => return ord,
}
let (al, bl) = (a.value_len(), b.value_len());
match al.cmp(&bl) {
Ordering::Equal => {}
ord => return ord,
}
let (mut abuf, mut bbuf) = (vec![0; al], vec![0; bl]);
a.emit(&mut abuf);
b.emit(&mut bbuf);
abuf.cmp(&bbuf)
}
43 changes: 28 additions & 15 deletions src/integration_tests/kernel_linux.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::helpers::bird::ensure_bird_2;
use super::helpers::cli::{close_cli, run_cli_with_bird, CliGuard};
use super::helpers::kernel::rtnl::{create_dummy_link, get_ip_route, get_ip_rule};
use super::helpers::kernel::rtnl::{create_dummy_link, get_ip_route, get_ip_rule, remove_link, route_msg_normalize};
use super::helpers::kernel::{ensure_loopback_up, ensure_root, pick_port};
use super::{TestEvent, BIRD_CONFIG_1};
use crate::args::Cli;
Expand All @@ -15,7 +15,8 @@ use nftables::expr::{self, MetaKey};
use nftables::stmt;
use rand::distributions::Alphanumeric;
use rand::Rng;
use rtnetlink::IpVersion;
use rtnetlink::{IpVersion, RouteMessageBuilder};
use std::net::IpAddr;
use std::time::Duration;
use tokio::select;
use tokio::time::sleep;
Expand Down Expand Up @@ -66,33 +67,45 @@ async fn test_order() -> anyhow::Result<()> {
async fn test_redirect_to_ip() -> anyhow::Result<()> {
let (conn, handle, _) = rtnetlink::new_connection()?;
tokio::spawn(conn);
create_dummy_link(&handle, "dummy_flow", "10.128.128.254/24".parse()?).await?;

let (name, (_g1, _g2, chans, _g3)) =
run_kernel_test(["flow4 { dst 172.20.0.0/16; } { bgp_ext_community.add((unknown 0x800c, 1.1.1.1, 0)); }"]).await?;
let dummy_index = create_dummy_link(&handle, "10.128.128.254/24".parse()?).await?;
let (name, (_g1, bird, chans, _g2)) =
run_kernel_test(["flow4 { dst 172.20.0.0/16; } { bgp_ext_community.add((unknown 0x800c, 10.128.128.1, 0)); }"])
.await?;

print_nft_chain(&name, &name).await?;
print_ip_rule().await?;
print_ip_route(10000).await?;

let ip_rule = get_ip_rule(&handle, IpVersion::V4).await?;
let ip_route = get_ip_route(&handle, IpVersion::V4, 10000).await?;
let ip_rules = get_ip_rule(&handle, IpVersion::V4).await?;
let ip_routes = get_ip_route(&handle, IpVersion::V4, 10000).await?;
let nft_stmts = get_nft_stmts(&name, &name).await?;
close_cli(chans).await;
drop(bird);
remove_link(&handle, dummy_index).await?;

let ip_rule_exp = make_ip_rule_mark(IpVersion::V4, 100, 10000, 10000);
println!("ip rule = {ip_rule:?}");
println!("exp = {ip_rule_exp:?}");
assert!(ip_rule.contains(&ip_rule_exp));

// let ip_route_exp =
println!("ip route show table 10000 = {ip_route:?}");
let table_index = 10000;

assert_eq!(nft_stmts, [vec![
prefix_stmt("daddr", "172.20.0.0/16".parse()?).unwrap(),
stmt::Statement::Mangle(stmt::Mangle { key: make_meta(expr::MetaKey::Mark), value: Number(10000) }),
stmt::Statement::Mangle(stmt::Mangle { key: make_meta(expr::MetaKey::Mark), value: Number(table_index) }),
ACCEPT,
]]);

let ip_rule_exp = make_ip_rule_mark(IpVersion::V4, 100, table_index, table_index);
println!("> ip rule = {ip_rules:?}");
println!("> exp = {ip_rule_exp:?}");
assert!(ip_rules.contains(&ip_rule_exp));

let mut ip_route_exp = RouteMessageBuilder::<IpAddr>::new()
.table_id(table_index)
.destination_prefix("172.20.0.0".parse()?, 16)?
.output_interface(dummy_index)
.gateway("10.128.128.1".parse()?)?
.build();
route_msg_normalize(&mut ip_route_exp);
assert_eq!(ip_routes, [ip_route_exp]);

Ok(())
}

Expand Down

0 comments on commit 6ef171e

Please sign in to comment.