Skip to content

Commit 3185155

Browse files
committed
tests: add test_unreachable_routes
1 parent cae5674 commit 3185155

File tree

2 files changed

+187
-76
lines changed

2 files changed

+187
-76
lines changed

src/integration_tests/kernel_linux.rs

Lines changed: 154 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,27 @@ use crate::args::Cli;
77
use crate::bgp::flow::Op;
88
use crate::integration_tests::helpers::kernel::linux::{get_nft_stmts, print_ip_route, print_ip_rule, print_nft_chain};
99
use crate::integration_tests::helpers::kernel::rtnl::make_ip_rule_mark;
10-
use crate::kernel::nft::{make_limit, make_meta, make_payload_field, prefix_stmt, range_stmt, ACCEPT, DROP};
10+
use crate::kernel::nft::{
11+
make_limit, make_meta, make_payload_field, mangle_stmt, prefix_stmt, range_stmt, ACCEPT, DROP,
12+
};
13+
use crate::net::IpPrefix;
1114
use clap::Parser;
1215
use itertools::Itertools;
1316
use macro_rules_attribute::apply;
1417
use nftables::expr::Expression::Number;
1518
use nftables::expr::{self, MetaKey};
16-
use nftables::stmt;
1719
use rand::distr::Alphanumeric;
1820
use rand::Rng;
21+
use rtnetlink::packet_route::route::{RouteAttribute, RouteType};
1922
use rtnetlink::{IpVersion, RouteMessageBuilder};
20-
use std::net::{Ipv4Addr, Ipv6Addr};
23+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
2124
use std::time::Duration;
2225
use tokio::select;
2326
use tokio::time::sleep;
2427

2528
#[apply(test_local!)]
2629
async fn test_order() -> anyhow::Result<()> {
27-
let (name, (_g1, _g2, chans, _g3)) = run_kernel_test(0xffff0000, [
30+
let (name, (_g1, _g2, chans, _g3)) = run_kernel_test_bird(0xffff0000, [
2831
"flow4 { dst 10.0.0.0/9; length > 1024; } { bgp_ext_community.add((unknown 0x8006, 0, 0)); }",
2932
"flow4 { dst 10.0.0.0/10; length > 1024; } { bgp_ext_community.add((unknown 0x8006, 0, 0x4c97a25c)); }",
3033
"flow6 { src fdfd::/128; next header 17; } { bgp_ext_community.add((unknown 0x800c, 0, 0)); }",
@@ -69,54 +72,54 @@ async fn test_redirect_to_ip() -> anyhow::Result<()> {
6972
let (conn, handle, _) = rtnetlink::new_connection()?;
7073
tokio::spawn(conn);
7174

72-
let table_index = 0xffff0001;
73-
let dummy_index = create_dummy_link(&handle, "10.128.128.254/24".parse()?).await?;
74-
let (name, (_g1, bird, chans, _g2)) = run_kernel_test(table_index, [
75-
"flow4 { dst 172.20.0.0/16; } { bgp_ext_community.add((unknown 0x800c, 10.128.128.1, 0)); }",
76-
"flow4 { dst 172.21.0.0/16; } { bgp_ext_community.add((unknown 0x800c, 10.128.128.1, 0)); }",
75+
let table_id = 0xffff0001;
76+
let dummy_id = create_dummy_link(&handle, "10.128.128.254/24".parse()?).await?;
77+
let (name, (_g1, bird, chans, _g2)) = run_kernel_test_bird(table_id, [
78+
"flow4 { dst 172.20.0.0/16; } { bgp_ext_community.add((unknown 0x000c, 10.128.128.1, 0)); }",
79+
"flow4 { dst 172.21.0.0/16; } { bgp_ext_community.add((unknown 0x000c, 10.128.128.1, 0)); }",
7780
])
7881
.await?;
7982

8083
print_nft_chain(&name, &name).await?;
8184
print_ip_rule(false).await?;
82-
print_ip_route(false, table_index).await?;
85+
print_ip_route(false, table_id).await?;
8386

8487
let nft_stmts = get_nft_stmts(&name, &name).await?;
8588
let ip_rules = get_ip_rule(&handle, IpVersion::V4).await?;
86-
let ip_routes = get_ip_route(&handle, IpVersion::V4, table_index).await?;
89+
let ip_routes = get_ip_route(&handle, IpVersion::V4, table_id).await?;
8790
close_cli(chans).await;
8891
drop(bird);
89-
remove_link(&handle, dummy_index).await?;
92+
remove_link(&handle, dummy_id).await?;
9093

9194
assert_eq!(nft_stmts, [
9295
vec![
9396
prefix_stmt("daddr", "172.20.0.0/16".parse()?).unwrap(),
94-
stmt::Statement::Mangle(stmt::Mangle { key: make_meta(expr::MetaKey::Mark), value: Number(table_index) }),
97+
mangle_stmt(make_meta(expr::MetaKey::Mark), Number(table_id)),
9598
ACCEPT,
9699
],
97100
vec![
98101
prefix_stmt("daddr", "172.21.0.0/16".parse()?).unwrap(),
99-
stmt::Statement::Mangle(stmt::Mangle { key: make_meta(expr::MetaKey::Mark), value: Number(table_index) }),
102+
mangle_stmt(make_meta(expr::MetaKey::Mark), Number(table_id)),
100103
ACCEPT,
101104
]
102105
]);
103106

104-
let ip_rule_exp = make_ip_rule_mark(IpVersion::V4, 100, table_index, table_index);
107+
let ip_rule_exp = make_ip_rule_mark(IpVersion::V4, 100, table_id, table_id);
105108
println!("> ip rule = {ip_rules:?}");
106109
println!("> exp = {ip_rule_exp:?}");
107110
assert!(ip_rules.contains(&ip_rule_exp));
108111

109112
let mut ip_routes_exp = [
110113
RouteMessageBuilder::<Ipv4Addr>::new()
111-
.table_id(table_index)
114+
.table_id(table_id)
112115
.destination_prefix("172.20.0.0".parse()?, 16)
113-
.output_interface(dummy_index)
116+
.output_interface(dummy_id)
114117
.gateway("10.128.128.1".parse()?)
115118
.build(),
116119
RouteMessageBuilder::<Ipv4Addr>::new()
117-
.table_id(table_index)
120+
.table_id(table_id)
118121
.destination_prefix("172.21.0.0".parse()?, 16)
119-
.output_interface(dummy_index)
122+
.output_interface(dummy_id)
120123
.gateway("10.128.128.1".parse()?)
121124
.build(),
122125
];
@@ -131,54 +134,54 @@ async fn test_redirect_to_ipv6() -> anyhow::Result<()> {
131134
let (conn, handle, _) = rtnetlink::new_connection()?;
132135
tokio::spawn(conn);
133136

134-
let table_index = 0xffff0002;
135-
let dummy_index = create_dummy_link(&handle, "fc64::1/64".parse()?).await?;
136-
let (name, (_g1, exabgp, chans, _g2)) = run_kernel_test_exabgp(table_index, [
137+
let table_id = 0xffff0002;
138+
let dummy_id = create_dummy_link(&handle, "fc64::1/64".parse()?).await?;
139+
let (name, (_g1, exabgp, chans, _g2)) = run_kernel_test_exabgp(table_id, [
137140
"match { destination fc00::/16; } then { redirect-to-nexthop-ietf fc64::ffff; }",
138141
"match { destination fc65:6565::/32; } then { redirect-to-nexthop-ietf fc64::2333; }",
139142
])
140143
.await?;
141144

142145
print_nft_chain(&name, &name).await?;
143146
print_ip_rule(true).await?;
144-
print_ip_route(true, table_index).await?;
147+
print_ip_route(true, table_id).await?;
145148

146149
let nft_stmts = get_nft_stmts(&name, &name).await?;
147150
let ip_rules = get_ip_rule(&handle, IpVersion::V6).await?;
148-
let ip_routes = get_ip_route(&handle, IpVersion::V6, table_index).await?;
151+
let ip_routes = get_ip_route(&handle, IpVersion::V6, table_id).await?;
149152
close_cli(chans).await;
150153
drop(exabgp);
151-
remove_link(&handle, dummy_index).await?;
154+
remove_link(&handle, dummy_id).await?;
152155

153156
assert_eq!(nft_stmts, [
154157
vec![
155158
prefix_stmt("daddr", "fc65:6565::/32".parse()?).unwrap(),
156-
stmt::Statement::Mangle(stmt::Mangle { key: make_meta(expr::MetaKey::Mark), value: Number(table_index) }),
159+
mangle_stmt(make_meta(expr::MetaKey::Mark), Number(table_id)),
157160
ACCEPT,
158161
],
159162
vec![
160163
prefix_stmt("daddr", "fc00::/16".parse()?).unwrap(),
161-
stmt::Statement::Mangle(stmt::Mangle { key: make_meta(expr::MetaKey::Mark), value: Number(table_index) }),
164+
mangle_stmt(make_meta(expr::MetaKey::Mark), Number(table_id)),
162165
ACCEPT,
163166
],
164167
]);
165168

166-
let ip_rule_exp = make_ip_rule_mark(IpVersion::V6, 100, table_index, table_index);
169+
let ip_rule_exp = make_ip_rule_mark(IpVersion::V6, 100, table_id, table_id);
167170
println!("> ip rule = {ip_rules:?}");
168171
println!("> exp = {ip_rule_exp:?}");
169172
assert!(ip_rules.contains(&ip_rule_exp));
170173

171174
let mut ip_routes_exp = [
172175
RouteMessageBuilder::<Ipv6Addr>::new()
173-
.table_id(table_index)
176+
.table_id(table_id)
174177
.destination_prefix("fc00::".parse()?, 16)
175-
.output_interface(dummy_index)
178+
.output_interface(dummy_id)
176179
.gateway("fc64::ffff".parse()?)
177180
.build(),
178181
RouteMessageBuilder::<Ipv6Addr>::new()
179-
.table_id(table_index)
182+
.table_id(table_id)
180183
.destination_prefix("fc65:6565::".parse()?, 32)
181-
.output_interface(dummy_index)
184+
.output_interface(dummy_id)
182185
.gateway("fc64::2333".parse()?)
183186
.build(),
184187
];
@@ -193,54 +196,54 @@ async fn test_ipv4_redirect_to_ipv6() -> anyhow::Result<()> {
193196
let (conn, handle, _) = rtnetlink::new_connection()?;
194197
tokio::spawn(conn);
195198

196-
let table_index = 0xffff0003;
197-
let dummy_index = create_dummy_link(&handle, "fc65::1/64".parse()?).await?;
198-
let (name, (_g1, exabgp, chans, _g2)) = run_kernel_test_exabgp(table_index, [
199+
let table_id = 0xffff0003;
200+
let dummy_id = create_dummy_link(&handle, "fc65::1/64".parse()?).await?;
201+
let (name, (_g1, exabgp, chans, _g2)) = run_kernel_test_exabgp(table_id, [
199202
"match { destination 172.17.254.192/26; } then { redirect-to-nexthop-ietf fc65::ffff; }",
200203
"match { destination 192.0.2.0/27; } then { redirect-to-nexthop-ietf fc65::2333; }",
201204
])
202205
.await?;
203206

204207
print_nft_chain(&name, &name).await?;
205208
print_ip_rule(false).await?;
206-
print_ip_route(false, table_index).await?;
209+
print_ip_route(false, table_id).await?;
207210

208211
let nft_stmts = get_nft_stmts(&name, &name).await?;
209212
let ip_rules = get_ip_rule(&handle, IpVersion::V4).await?;
210-
let ip_routes = get_ip_route(&handle, IpVersion::V4, table_index).await?;
213+
let ip_routes = get_ip_route(&handle, IpVersion::V4, table_id).await?;
211214
close_cli(chans).await;
212215
drop(exabgp);
213-
remove_link(&handle, dummy_index).await?;
216+
remove_link(&handle, dummy_id).await?;
214217

215218
assert_eq!(nft_stmts, [
216219
vec![
217220
prefix_stmt("daddr", "192.0.2.0/27".parse()?).unwrap(),
218-
stmt::Statement::Mangle(stmt::Mangle { key: make_meta(expr::MetaKey::Mark), value: Number(table_index) }),
221+
mangle_stmt(make_meta(expr::MetaKey::Mark), Number(table_id)),
219222
ACCEPT,
220223
],
221224
vec![
222225
prefix_stmt("daddr", "172.17.254.192/26".parse()?).unwrap(),
223-
stmt::Statement::Mangle(stmt::Mangle { key: make_meta(expr::MetaKey::Mark), value: Number(table_index) }),
226+
mangle_stmt(make_meta(expr::MetaKey::Mark), Number(table_id)),
224227
ACCEPT,
225228
],
226229
]);
227230

228-
let ip_rule_exp = make_ip_rule_mark(IpVersion::V4, 100, table_index, table_index);
231+
let ip_rule_exp = make_ip_rule_mark(IpVersion::V4, 100, table_id, table_id);
229232
println!("> ip rule = {ip_rules:?}");
230233
println!("> exp = {ip_rule_exp:?}");
231234
assert!(ip_rules.contains(&ip_rule_exp));
232235

233236
let mut ip_routes_exp = [
234237
RouteMessageBuilder::<Ipv4Addr>::new()
235-
.table_id(table_index)
238+
.table_id(table_id)
236239
.destination_prefix("172.17.254.192".parse()?, 26)
237-
.output_interface(dummy_index)
240+
.output_interface(dummy_id)
238241
.gateway("fc65::ffff".parse()?)
239242
.build(),
240243
RouteMessageBuilder::<Ipv4Addr>::new()
241-
.table_id(table_index)
244+
.table_id(table_id)
242245
.destination_prefix("192.0.2.0".parse()?, 27)
243-
.output_interface(dummy_index)
246+
.output_interface(dummy_id)
244247
.gateway("fc65::2333".parse()?)
245248
.build(),
246249
];
@@ -250,9 +253,114 @@ async fn test_ipv4_redirect_to_ipv6() -> anyhow::Result<()> {
250253
Ok(())
251254
}
252255

256+
#[apply(test_local!)]
257+
async fn test_unreachable_routes() -> anyhow::Result<()> {
258+
let (conn, handle, _) = rtnetlink::new_connection()?;
259+
tokio::spawn(conn);
260+
261+
let table_id = 0xffff0004;
262+
let unreach_prefixes = ["192.0.2.0/24", "fc99::/64"];
263+
let unreach_msgs: Vec<_> = unreach_prefixes
264+
.into_iter()
265+
.map(|p| {
266+
let p = p.parse::<IpPrefix>().unwrap();
267+
RouteMessageBuilder::<IpAddr>::new()
268+
.destination_prefix(p.prefix(), p.len())
269+
.unwrap()
270+
.kind(RouteType::Unreachable)
271+
.build()
272+
})
273+
.collect();
274+
for msg in unreach_msgs.iter().cloned() {
275+
handle.route().add(msg).execute().await?;
276+
}
277+
278+
let (name, (_g1, exabgp, chans, _g2)) = run_kernel_test_exabgp(table_id, [
279+
"match { destination 172.17.254.192/26; } then { redirect-to-nexthop-ietf 192.0.2.128; }",
280+
"match { destination 192.0.2.0/27; } then { redirect-to-nexthop-ietf fc99::2333; }",
281+
"match { destination fc42::/32; } then { redirect-to-nexthop-ietf fc99::6666; }",
282+
])
283+
.await?;
284+
285+
print_nft_chain(&name, &name).await?;
286+
print_ip_rule(false).await?;
287+
print_ip_rule(true).await?;
288+
print_ip_route(false, 254).await?;
289+
print_ip_route(true, 254).await?;
290+
print_ip_route(false, table_id).await?;
291+
print_ip_route(true, table_id).await?;
292+
293+
let nft_stmts = get_nft_stmts(&name, &name).await?;
294+
let ip_rules = [
295+
get_ip_rule(&handle, IpVersion::V4).await?,
296+
get_ip_rule(&handle, IpVersion::V6).await?,
297+
];
298+
let ip_routes = [
299+
get_ip_route(&handle, IpVersion::V4, table_id).await?,
300+
get_ip_route(&handle, IpVersion::V6, table_id).await?,
301+
];
302+
let ip_rules: Vec<_> = ip_rules.into_iter().flatten().collect();
303+
let ip_routes: Vec<_> = ip_routes.into_iter().flatten().collect();
304+
close_cli(chans).await;
305+
drop(exabgp);
306+
for msg in unreach_msgs {
307+
handle.route().del(msg).execute().await?;
308+
}
309+
310+
assert_eq!(nft_stmts, [
311+
vec![
312+
prefix_stmt("daddr", "192.0.2.0/27".parse()?).unwrap(),
313+
mangle_stmt(make_meta(expr::MetaKey::Mark), Number(table_id)),
314+
ACCEPT,
315+
],
316+
vec![
317+
prefix_stmt("daddr", "172.17.254.192/26".parse()?).unwrap(),
318+
mangle_stmt(make_meta(expr::MetaKey::Mark), Number(table_id)),
319+
ACCEPT,
320+
],
321+
vec![
322+
prefix_stmt("daddr", "fc42::/32".parse()?).unwrap(),
323+
mangle_stmt(make_meta(expr::MetaKey::Mark), Number(table_id)),
324+
ACCEPT,
325+
],
326+
]);
327+
328+
let ip_rules_exp: Vec<_> = [IpVersion::V4, IpVersion::V6]
329+
.into_iter()
330+
.map(|x| make_ip_rule_mark(x, 100, table_id, table_id))
331+
.collect();
332+
println!("> ip rule = {ip_rules:?}");
333+
println!("> exp = {ip_rules_exp:?}");
334+
assert!(ip_rules_exp.iter().all(|x| ip_rules.contains(x)));
335+
336+
let ip_routes_exp = ["172.17.254.192/26", "192.0.2.0/27", "fc42::/32"];
337+
let mut ip_routes_exp: Vec<_> = ip_routes_exp
338+
.into_iter()
339+
.map(|x| {
340+
let x = x.parse::<IpPrefix>().unwrap();
341+
let mut msg = RouteMessageBuilder::<IpAddr>::new()
342+
.destination_prefix(x.prefix(), x.len())
343+
.unwrap()
344+
.kind(RouteType::Unreachable)
345+
.table_id(table_id)
346+
.build();
347+
if x.is_ipv6() {
348+
msg.attributes.push(RouteAttribute::Oif(1));
349+
}
350+
msg
351+
})
352+
.collect();
353+
ip_routes_exp.iter_mut().for_each(route_msg_normalize);
354+
assert_eq!(ip_routes, ip_routes_exp);
355+
356+
Ok(())
357+
}
358+
253359
// TODO: test IPv6 offset
360+
// TODO: test prefix overlap
361+
// TODO: test rtnetlink listen to network changes
254362

255-
async fn run_kernel_test(
363+
async fn run_kernel_test_bird(
256364
init_table_id: u32,
257365
flows: impl IntoIterator<Item = &str>,
258366
) -> anyhow::Result<(String, CliGuard)> {

0 commit comments

Comments
 (0)