Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit ccc5780

Browse files
authored
Add basic Conntrack for UDP (#60)
Fixes #45 Signed-off-by: Andrew Stoycos <[email protected]>
1 parent b72a599 commit ccc5780

File tree

21 files changed

+472
-209
lines changed

21 files changed

+472
-209
lines changed

.github/workflows/test.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,13 @@ jobs:
6767
env:
6868
BLIXT_CONTROLPLANE_IMAGE: "ghcr.io/kong/blixt-controlplane:integration-tests"
6969
BLIXT_DATAPLANE_IMAGE: "ghcr.io/kong/blixt-dataplane:integration-tests"
70+
BLIXT_UDP_SERVER_IMAGE: "ghcr.io/kong/blixt-udp-test-server:integration-tests"
71+
72+
## Upload diagnostics if integration test step failed.
73+
- name: upload diagnostics
74+
if: ${{ failure() }}
75+
uses: actions/upload-artifact@v3
76+
with:
77+
name: blixt-integration-test-diag
78+
path: /tmp/ktf-diag*
79+
if-no-files-found: ignore

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ build.image:
138138
.PHONY: build.all.images
139139
build.all.images: build.image
140140
cd dataplane/ && make build.image TAG=$(TAG)
141+
cd tools/udp-test-server && make build.image TAG=$(TAG)
141142

142143
##@ Deployment
143144

config/samples/udproute/kustomization.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ resources:
22
- gateway.yaml
33
- server.yaml
44
- udproute.yaml
5-
#+kubebuilder:scaffold:manifestskustomizesamples
5+
#+kubebuilder:scaffold:manifestskustomizesamples

config/tests/kustomization.yaml renamed to config/tests/blixt/kustomization.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
bases:
2-
- ../default
2+
- ../../default
33

44
images:
55
- name: ghcr.io/kong/blixt-dataplane
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
resources:
2+
- ../../samples/udproute
3+
4+
patchesStrategicMerge:
5+
- |-
6+
apiVersion: apps/v1
7+
kind: Deployment
8+
metadata:
9+
name: blixt-udproute-sample
10+
spec:
11+
template:
12+
spec:
13+
containers:
14+
- name: server
15+
command:
16+
- ./udp-test-server
17+
# --dry-run disables UDP listeners in order to test failures to send
18+
# data, and trigger ICMP port failure responses from the kernel
19+
- --dry-run
20+
21+
images:
22+
- name: ghcr.io/kong/blixt-udp-test-server
23+
newTag: integration-tests
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
bases:
2+
- ../../samples/udproute
3+
4+
images:
5+
- name: ghcr.io/kong/blixt-udp-test-server
6+
newTag: integration-tests

dataplane/ebpf/src/bindings.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* automatically generated by rust-bindgen 0.63.0 */
1+
/* automatically generated by rust-bindgen 0.64.0 */
22

33
#[repr(C)]
44
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
@@ -334,3 +334,31 @@ pub struct udphdr {
334334
pub len: __be16,
335335
pub check: __sum16,
336336
}
337+
#[repr(C)]
338+
#[derive(Copy, Clone)]
339+
pub struct icmphdr {
340+
pub type_: __u8,
341+
pub code: __u8,
342+
pub checksum: __sum16,
343+
pub un: icmphdr__bindgen_ty_1,
344+
}
345+
#[repr(C)]
346+
#[derive(Copy, Clone)]
347+
pub union icmphdr__bindgen_ty_1 {
348+
pub echo: icmphdr__bindgen_ty_1__bindgen_ty_1,
349+
pub gateway: __be32,
350+
pub frag: icmphdr__bindgen_ty_1__bindgen_ty_2,
351+
pub reserved: [__u8; 4usize],
352+
}
353+
#[repr(C)]
354+
#[derive(Copy, Clone)]
355+
pub struct icmphdr__bindgen_ty_1__bindgen_ty_1 {
356+
pub id: __be16,
357+
pub sequence: __be16,
358+
}
359+
#[repr(C)]
360+
#[derive(Copy, Clone)]
361+
pub struct icmphdr__bindgen_ty_1__bindgen_ty_2 {
362+
pub __unused: __be16,
363+
pub mtu: __be16,
364+
}

dataplane/ebpf/src/egress/icmp.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use core::mem;
2+
3+
use aya_bpf::{
4+
bindings::TC_ACT_PIPE,
5+
helpers::bpf_csum_diff,
6+
programs::TcContext,
7+
};
8+
use aya_log_ebpf::info;
9+
10+
use crate::{
11+
bindings::{iphdr, icmphdr},
12+
utils::{csum_fold_helper, ip_from_int, ptr_at, ETH_HDR_LEN, IP_HDR_LEN},
13+
BLIXT_CONNTRACK,
14+
};
15+
16+
const ICMP_HDR_LEN: usize = mem::size_of::<icmphdr>();
17+
const ICMP_PROTO_TYPE_UNREACH: u8 = 3;
18+
19+
pub fn handle_icmp_egress(ctx: TcContext) -> Result<i32, i64> {
20+
let ip_hdr: *mut iphdr = unsafe { ptr_at(&ctx, ETH_HDR_LEN) }?;
21+
22+
let icmp_header_offset = ETH_HDR_LEN + IP_HDR_LEN;
23+
24+
let icmp_hdr: *mut icmphdr = unsafe {
25+
ptr_at(
26+
&ctx,
27+
icmp_header_offset
28+
)?
29+
};
30+
31+
// We only care about redirecting port unreachable messages currently so a
32+
// UDP client can tell when the server is shutdown
33+
if unsafe { (*icmp_hdr).type_ } != ICMP_PROTO_TYPE_UNREACH {
34+
return Ok(TC_ACT_PIPE);
35+
}
36+
37+
let dest_addr = unsafe { (*ip_hdr).daddr };
38+
39+
let new_src = unsafe { BLIXT_CONNTRACK.get(&dest_addr) }.ok_or(TC_ACT_PIPE)?;
40+
41+
let daddr_dot_dec = ip_from_int(unsafe { (*ip_hdr).daddr });
42+
info!(
43+
&ctx,
44+
"Received a ICMP Unreachable packet destined for svc ip: {}.{}.{}.{}",
45+
daddr_dot_dec[0],
46+
daddr_dot_dec[1],
47+
daddr_dot_dec[2],
48+
daddr_dot_dec[3],
49+
);
50+
51+
// redirect icmp unreachable message back to client
52+
unsafe {
53+
(*ip_hdr).saddr = *new_src;
54+
(*ip_hdr).check = 0;
55+
}
56+
57+
let full_cksum = unsafe {
58+
bpf_csum_diff(
59+
mem::MaybeUninit::zeroed().assume_init(),
60+
0,
61+
ip_hdr as *mut u32,
62+
mem::size_of::<iphdr>() as u32,
63+
0
64+
)
65+
} as u64;
66+
unsafe { (*ip_hdr).check = csum_fold_helper(full_cksum) };
67+
68+
// Get inner ipheader since we need to update that as well
69+
let icmp_inner_ip_hdr: *mut iphdr = unsafe { ptr_at(&ctx, icmp_header_offset + ICMP_HDR_LEN)}?;
70+
71+
unsafe {
72+
(*icmp_inner_ip_hdr).daddr = *new_src;
73+
(*icmp_inner_ip_hdr).check = 0;
74+
}
75+
76+
let full_cksum = unsafe {
77+
bpf_csum_diff(
78+
mem::MaybeUninit::zeroed().assume_init(),
79+
0,
80+
icmp_inner_ip_hdr as *mut u32,
81+
mem::size_of::<iphdr>() as u32,
82+
0
83+
)
84+
} as u64;
85+
unsafe { (*icmp_inner_ip_hdr).check = csum_fold_helper(full_cksum) };
86+
87+
unsafe { BLIXT_CONNTRACK.remove(&dest_addr)? };
88+
89+
return Ok(TC_ACT_PIPE);
90+
}

dataplane/ebpf/src/egress/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod icmp;

dataplane/ebpf/src/ingress/udp.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use core::mem;
22

33
use aya_bpf::{
4-
bindings::TC_ACT_OK,
4+
bindings::TC_ACT_PIPE,
55
helpers::{bpf_csum_diff, bpf_redirect_neigh},
66
programs::TcContext,
77
};
@@ -11,6 +11,7 @@ use crate::{
1111
bindings::{iphdr, udphdr},
1212
utils::{csum_fold_helper, ip_from_int, ptr_at, ETH_HDR_LEN, IP_HDR_LEN},
1313
BACKENDS,
14+
BLIXT_CONNTRACK,
1415
};
1516
use common::BackendKey;
1617

@@ -21,12 +22,14 @@ pub fn handle_udp_ingress(ctx: TcContext) -> Result<i32, i64> {
2122

2223
let udp_hdr: *mut udphdr = unsafe { ptr_at(&ctx, udp_header_offset)? };
2324

25+
let original_daddr = unsafe { (*ip_hdr).daddr };
26+
2427
let key = BackendKey {
25-
ip: u32::from_be(unsafe { (*ip_hdr).daddr }),
28+
ip: u32::from_be(original_daddr),
2629
port: (u16::from_be(unsafe { (*udp_hdr).dest })) as u32,
2730
};
2831

29-
let backend = unsafe { BACKENDS.get(&key) }.ok_or(TC_ACT_OK)?;
32+
let backend = unsafe { BACKENDS.get(&key) }.ok_or(TC_ACT_PIPE)?;
3033

3134
let daddr_dot_dec = ip_from_int(unsafe { (*ip_hdr).daddr });
3235
info!(
@@ -40,12 +43,13 @@ pub fn handle_udp_ingress(ctx: TcContext) -> Result<i32, i64> {
4043
);
4144

4245
unsafe {
46+
BLIXT_CONNTRACK.insert(&(*ip_hdr).saddr, &original_daddr, 0 as u64)?;
4347
(*ip_hdr).daddr = backend.daddr.to_be();
44-
}
45-
48+
};
49+
4650
if (ctx.data() + ETH_HDR_LEN + mem::size_of::<iphdr>()) > ctx.data_end() {
4751
info!(&ctx, "Iphdr is out of bounds");
48-
return Ok(TC_ACT_OK);
52+
return Ok(TC_ACT_PIPE);
4953
}
5054

5155
// Calculate l3 cksum

0 commit comments

Comments
 (0)