Skip to content

Commit 2b6faac

Browse files
committed
bpf/tests/bpf_nat_icmp6: use generated scapy packet fixtures.
These tests whether ICMPv6 PTB messages are correctly revSNAT-ed via nodeport when using bpf masquerade. By switching these to use the scapy generated data we reduce the amount of custom pkt generating code and also naturally add coverage for checksum adjustments on this code path. We also combine the related ipv4 and ipv6 into the same test files to avoid duplication. Signed-off-by: Tom Hadlaw <tom.hadlaw@isovalent.com>
1 parent b0fc6d7 commit 2b6faac

11 files changed

Lines changed: 264 additions & 142 deletions

bpf/tests/bpf_nat_icmp6.h

Lines changed: 155 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
22
/* Copyright Authors of Cilium */
33

4+
#pragma once
5+
46
#include <bpf/ctx/skb.h>
57
#include "common.h"
68
#include "pktgen.h"
79

8-
#define ENABLE_SCTP
9-
#define ENABLE_IPV4
10-
#define ENABLE_IPV6
11-
#define ENABLE_NODEPORT
10+
#define ENABLE_SCTP 1
11+
#define ENABLE_IPV4 1
12+
#define ENABLE_IPV6 1
13+
#define ENABLE_NODEPORT 1
1214
#define ENABLE_MASQUERADE_IPV6 1
1315

14-
#include "lib/bpf_host.h"
16+
#define NODE_ONE6 { .addr = v6_node_one_addr }
17+
#define EXT_IP6 { .addr = v6_ext_node_one_addr }
18+
#define POD_IP6 { .addr = v6_pod_one_addr }
19+
20+
#define POD6_SEC_IDENTITY 112244
1521

1622
#include <bpf/config/node.h>
1723

@@ -22,25 +28,13 @@
2228
#include <lib/nat.h>
2329
#include <lib/time.h>
2430

25-
#include <bpf/helpers.h>
26-
#include <bpf/api.h>
27-
28-
#include <bpf/ctx/skb.h>
29-
#include <bpf/api.h>
30-
#include <bpf/config/node.h>
31-
32-
#define DEBUG
31+
ASSIGN_CONFIG(union v6addr, nat_ipv6_masquerade, { .addr = v6_node_one_addr })
3332

34-
#include <lib/dbg.h>
35-
#include <lib/eps.h>
36-
#include <lib/nat.h>
37-
#include <lib/time.h>
33+
#include "scapy.h"
3834

3935
#include "bpf_nat_tuples.h"
4036

41-
#define NODE_ONE { .addr = v6_node_one_addr }
42-
#define EXT_IP { .addr = v6_ext_node_one_addr }
43-
#define POD_IP { .addr = v6_pod_one_addr }
37+
#include "lib/endpoint.h"
4438

4539
/*
4640
* Input packet represents a device sending a PKT_TOO_BIG response ICMPv6
@@ -87,81 +81,144 @@
8781
*
8882
* Ref: https://datatracker.ietf.org/doc/html/rfc4443#section-3.2
8983
*/
90-
__always_inline int gen_pmtu_pkt(struct pktgen *builder, int l4_type)
84+
const __u8 icmp6_err_revnat_egress_tcp[] = {
85+
SCAPY_BUF_BYTES(icmp6_err_revnat_egress_tcp)
86+
};
87+
const __u8 icmp6_err_revnat_egress_post_tcp[] = {
88+
SCAPY_BUF_BYTES(icmp6_err_revnat_egress_post_tcp)
89+
};
90+
const __u8 icmp6_err_revnat_egress_udp[] = {
91+
SCAPY_BUF_BYTES(icmp6_err_revnat_egress_udp)
92+
};
93+
const __u8 icmp6_err_revnat_egress_post_udp[] = {
94+
SCAPY_BUF_BYTES(icmp6_err_revnat_egress_post_udp)
95+
};
96+
const __u8 icmp6_err_revnat_full_tcp[] = {
97+
SCAPY_BUF_BYTES(icmp6_err_revnat_full_tcp)
98+
};
99+
const __u8 icmp6_err_revnat_full_tcp_after[] = {
100+
SCAPY_BUF_BYTES(icmp6_err_revnat_full_tcp_after)
101+
};
102+
const __u8 icmp6_err_revnat_full_udp[] = {
103+
SCAPY_BUF_BYTES(icmp6_err_revnat_full_udp)
104+
};
105+
const __u8 icmp6_err_revnat_full_udp_after[] = {
106+
SCAPY_BUF_BYTES(icmp6_err_revnat_full_udp_after)
107+
};
108+
109+
/*
110+
* Push an egressing pod->external TCP packet through to-netdev and let the
111+
* datapath set up nat mappings due to node ip masquerading.
112+
* Exploit the fact that tests are run in alphabetical order to ensure this
113+
* happens before we check the icmp pmtud revSNAT mappings.
114+
*/
115+
PKTGEN(PROG_TYPE, "00_snat_v6_tcp_egress")
116+
int snat_v6_egress_pktgen(struct __ctx_buff *ctx)
91117
{
92-
struct ipv6hdr *inner_l3 = NULL;
93-
struct icmp6hdr *l4 = NULL;
94-
struct tcphdr *inner_l4 = NULL;
95-
struct udphdr *inner_l4_udp = NULL; /* compiler complains if this isn't init to null? */
96-
struct sctphdr *inner_l4_sctp = NULL;
97-
void *data = NULL;
98-
99-
l4 = pktgen__push_ipv6_icmp6_packet(builder,
100-
(__u8 *)mac_one,
101-
(__u8 *)mac_two,
102-
(__u8 *)v6_ext_node_one,
103-
(__u8 *)v6_node_one,
104-
ICMPV6_PKT_TOOBIG);
105-
if (!l4)
106-
return TEST_FAIL;
118+
struct pktgen builder;
107119

108-
inner_l3 = pktgen__push_default_ipv6hdr(builder);
109-
if (!inner_l3)
110-
return TEST_FAIL;
120+
pktgen__init(&builder, ctx);
121+
scapy_push_data(&builder,
122+
icmp6_err_revnat_egress_tcp,
123+
sizeof(icmp6_err_revnat_egress_tcp));
124+
pktgen__finish(&builder);
125+
return TEST_PASS;
126+
}
111127

112-
inner_l3->nexthdr = (__u8)l4_type;
113-
ipv6hdr__set_addrs(inner_l3, (__u8 *)v6_node_one, (__u8 *)v6_ext_node_one);
114-
115-
switch (l4_type) {
116-
case IPPROTO_TCP:
117-
inner_l4 = pktgen__push_default_tcphdr(builder);
118-
if (!inner_l4)
119-
return TEST_FAIL;
120-
/* original source */
121-
inner_l4->dest = 1234;
122-
inner_l4->source = 30001;
123-
break;
124-
case IPPROTO_UDP:
125-
inner_l4_udp = pktgen__push_default_udphdr(builder);
126-
if (!inner_l4_udp)
127-
return TEST_FAIL;
128-
inner_l4_udp->dest = 1234;
129-
inner_l4_udp->source = 30001;
130-
break;
131-
case IPPROTO_SCTP:
132-
inner_l4_sctp = pktgen__push_default_sctphdr(builder);
133-
if (!inner_l4_udp)
134-
return TEST_FAIL;
135-
inner_l4_sctp->dest = 1234;
136-
inner_l4_sctp->source = 30001;
137-
break;
138-
default:
139-
return TEST_FAIL;
140-
}
128+
SETUP(PROG_TYPE, "00_snat_v6_tcp_egress")
129+
int snat_v6_egress_setup(struct __ctx_buff *ctx)
130+
{
131+
union v6addr pod_ip = POD_IP6;
141132

142-
data = pktgen__push_data(builder, default_data, sizeof(default_data));
143-
if (!data)
144-
return TEST_FAIL;
133+
endpoint_v6_add_entry(&pod_ip, 0, 0, 0, POD6_SEC_IDENTITY,
134+
(__u8 *)mac_one, (__u8 *)mac_one);
145135

146-
return TEST_PASS;
136+
return netdev_send_packet(ctx);
147137
}
148138

149-
int snat_v6_insert_ct_nat(__u8 proto)
139+
/*
140+
* Add a check prior to moving onto actual icmp pmtu icmp testing
141+
* to quickly fail if priror steps did not setup expected nat state
142+
*/
143+
CHECK(PROG_TYPE, "00_snat_v6_tcp_egress")
144+
int snat_v6_egress_check(const struct __ctx_buff *ctx)
150145
{
151-
struct ipv6_nat_entry entry = {
152-
.to_daddr = POD_IP,
146+
test_init();
147+
148+
ASSERT_CTX_BUF_OFF("snat_v6_egress", "Ether", ctx, sizeof(__u32),
149+
icmp6_err_revnat_egress_post_tcp,
150+
sizeof(icmp6_err_revnat_egress_post_tcp));
151+
152+
union v6addr pod_ip = POD_IP6;
153+
struct ipv6_ct_tuple tuple = {
154+
.daddr = NODE_ONE6,
155+
.saddr = EXT_IP6,
156+
.dport = tcp_src_two,
157+
.sport = tcp_dst_one,
158+
.nexthdr = IPPROTO_TCP,
159+
.flags = NAT_DIR_INGRESS,
153160
};
154-
entry.to_sport = 0;
155-
entry.to_dport = 20;
161+
struct ipv6_nat_entry *entry = map_lookup_elem(&cilium_snat_v6_external,
162+
&tuple);
163+
164+
if (!entry)
165+
test_fatal("no revSNAT entry created by to-netdev");
166+
if (!ipv6_addr_equals(&entry->to_daddr, &pod_ip))
167+
test_fatal("revSNAT entry to_daddr is not the pod IP");
168+
if (entry->to_dport != tcp_src_two)
169+
test_fatal("revSNAT entry to_dport is not the pod source port");
170+
171+
test_finish();
172+
}
173+
174+
PKTGEN(PROG_TYPE, "01_snat_v6_udp_egress")
175+
int snat_v6_egress_udp_pktgen(struct __ctx_buff *ctx)
176+
{
177+
struct pktgen builder;
178+
179+
pktgen__init(&builder, ctx);
180+
scapy_push_data(&builder,
181+
icmp6_err_revnat_egress_udp,
182+
sizeof(icmp6_err_revnat_egress_udp));
183+
pktgen__finish(&builder);
184+
return TEST_PASS;
185+
}
186+
187+
SETUP(PROG_TYPE, "01_snat_v6_udp_egress")
188+
int snat_v6_egress_udp_setup(struct __ctx_buff *ctx)
189+
{
190+
return netdev_send_packet(ctx);
191+
}
192+
193+
CHECK(PROG_TYPE, "01_snat_v6_udp_egress")
194+
int snat_v6_egress_udp_check(const struct __ctx_buff *ctx)
195+
{
196+
test_init();
197+
198+
ASSERT_CTX_BUF_OFF("snat_v6_egress_udp", "Ether", ctx, sizeof(__u32),
199+
icmp6_err_revnat_egress_post_udp,
200+
sizeof(icmp6_err_revnat_egress_post_udp));
201+
202+
union v6addr pod_ip = POD_IP6;
156203
struct ipv6_ct_tuple tuple = {
157-
.daddr = NODE_ONE,
158-
.saddr = EXT_IP,
159-
.dport = 30001, /* SNAT remapped port */
160-
.sport = 1234,
161-
.nexthdr = proto,
162-
.flags = TUPLE_F_IN,
204+
.daddr = NODE_ONE6,
205+
.saddr = EXT_IP6,
206+
.dport = tcp_src_two,
207+
.sport = tcp_dst_one,
208+
.nexthdr = IPPROTO_UDP,
209+
.flags = NAT_DIR_INGRESS,
163210
};
164-
return map_update_elem(&cilium_snat_v6_external, &tuple, &entry, BPF_ANY);
211+
struct ipv6_nat_entry *entry = map_lookup_elem(&cilium_snat_v6_external,
212+
&tuple);
213+
214+
if (!entry)
215+
test_fatal("no revSNAT entry created by to-netdev");
216+
if (!ipv6_addr_equals(&entry->to_daddr, &pod_ip))
217+
test_fatal("revSNAT entry to_daddr is not the pod IP");
218+
if (entry->to_dport != tcp_src_two)
219+
test_fatal("revSNAT entry to_dport is not the pod source port");
220+
221+
test_finish();
165222
}
166223

167224
int do_icmp6_pkt_too_big_check(const struct __ctx_buff *ctx)
@@ -200,9 +257,9 @@ int do_icmp6_pkt_too_big_check(const struct __ctx_buff *ctx)
200257
l4 = (void *)(data + sizeof(__u32) +
201258
sizeof(struct ethhdr) + sizeof(struct ipv6hdr) +
202259
sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr));
203-
if (*((__u16 *)l4) != 20)
260+
if (*((__u16 *)l4) != bpf_htons(20))
204261
return TEST_FAIL;
205-
if (*((__u16 *)(l4 + sizeof(__u16))) != 1234)
262+
if (*((__u16 *)(l4 + sizeof(__u16))) != bpf_htons(1234))
206263
return TEST_FAIL;
207264
return 0;
208265
}
@@ -213,28 +270,26 @@ int snat_v6_pmtu_pktgen(struct __ctx_buff *ctx)
213270
struct pktgen builder;
214271

215272
pktgen__init(&builder, ctx);
216-
gen_pmtu_pkt(&builder, IPPROTO_TCP);
273+
scapy_push_data(&builder,
274+
icmp6_err_revnat_full_tcp,
275+
sizeof(icmp6_err_revnat_full_tcp));
217276
pktgen__finish(&builder);
218277
return TEST_PASS;
219278
}
220279

221280
SETUP(PROG_TYPE, "snat_v6_tcp_pmtu")
222281
int snat_v6_pmtu_setup(struct __ctx_buff *ctx)
223282
{
224-
int ret;
225-
226-
ret = snat_v6_insert_ct_nat(IPPROTO_TCP);
227-
if (ret < 0)
228-
return TEST_FAIL;
229-
230283
return netdev_receive_packet(ctx);
231284
}
232285

233286
CHECK(PROG_TYPE, "snat_v6_tcp_pmtu")
234287
int snat_v6_pmtu_check(const struct __ctx_buff *ctx)
235288
{
236289
test_init();
237-
assert(do_icmp6_pkt_too_big_check(ctx) == 0);
290+
ASSERT_CTX_BUF_OFF("snat_v6_tcp_pmtu", "Ether", ctx, sizeof(__u32),
291+
icmp6_err_revnat_full_tcp_after,
292+
sizeof(icmp6_err_revnat_full_tcp_after));
238293
test_finish();
239294

240295
return 0;
@@ -246,28 +301,26 @@ int snat_v6_pmtu_udp_pktgen(struct __ctx_buff *ctx)
246301
struct pktgen builder;
247302

248303
pktgen__init(&builder, ctx);
249-
gen_pmtu_pkt(&builder, IPPROTO_UDP);
304+
scapy_push_data(&builder,
305+
icmp6_err_revnat_full_udp,
306+
sizeof(icmp6_err_revnat_full_udp));
250307
pktgen__finish(&builder);
251308
return TEST_PASS;
252309
}
253310

254311
SETUP(PROG_TYPE, "snat_v6_udp_pmtu")
255312
int snat_v6_pmtu_udp_setup(struct __ctx_buff *ctx)
256313
{
257-
int ret;
258-
259-
ret = snat_v6_insert_ct_nat(IPPROTO_UDP);
260-
if (ret < 0)
261-
return TEST_FAIL;
262-
263314
return netdev_receive_packet(ctx);
264315
}
265316

266317
CHECK(PROG_TYPE, "snat_v6_udp_pmtu")
267318
int snat_v6_pmtu_udp_check(const struct __ctx_buff *ctx)
268319
{
269320
test_init();
270-
assert(do_icmp6_pkt_too_big_check(ctx) == 0);
321+
ASSERT_CTX_BUF_OFF("snat_v6_udp_pmtu", "Ether", ctx, sizeof(__u32),
322+
icmp6_err_revnat_full_udp_after,
323+
sizeof(icmp6_err_revnat_full_udp_after));
271324
test_finish();
272325

273326
return 0;

0 commit comments

Comments
 (0)