Skip to content

Commit 8dfd4bb

Browse files
nvmmaxKernel Patches Daemon
authored and
Kernel Patches Daemon
committed
bpf: Allow the new syncookie helpers to work with SKBs
This commits allows the new BPF helpers to work in SKB context (in TC BPF programs): bpf_tcp_raw_{gen,check}_syncookie_ipv{4,6}. The sample application and selftest are updated to support the TC mode. It's not the recommended mode of operation, because the SKB is already created at this point, and it's unlikely that the BPF program will provide any substantional speedup compared to regular SYN cookies or synproxy. Signed-off-by: Maxim Mikityanskiy <[email protected]> Reviewed-by: Tariq Toukan <[email protected]>
1 parent 08450b8 commit 8dfd4bb

File tree

4 files changed

+230
-68
lines changed

4 files changed

+230
-68
lines changed

net/core/filter.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7905,6 +7905,16 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
79057905
return &bpf_sk_assign_proto;
79067906
case BPF_FUNC_skb_set_tstamp:
79077907
return &bpf_skb_set_tstamp_proto;
7908+
#ifdef CONFIG_SYN_COOKIES
7909+
case BPF_FUNC_tcp_raw_gen_syncookie_ipv4:
7910+
return &bpf_tcp_raw_gen_syncookie_ipv4_proto;
7911+
case BPF_FUNC_tcp_raw_gen_syncookie_ipv6:
7912+
return &bpf_tcp_raw_gen_syncookie_ipv6_proto;
7913+
case BPF_FUNC_tcp_raw_check_syncookie_ipv4:
7914+
return &bpf_tcp_raw_check_syncookie_ipv4_proto;
7915+
case BPF_FUNC_tcp_raw_check_syncookie_ipv6:
7916+
return &bpf_tcp_raw_check_syncookie_ipv6_proto;
7917+
#endif
79087918
#endif
79097919
default:
79107920
return bpf_sk_base_func_proto(func_id);

tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// SPDX-License-Identifier: GPL-2.0
2+
#define _GNU_SOURCE
23
#include <test_progs.h>
34
#include <network_helpers.h>
45

@@ -7,9 +8,11 @@
78
goto out; \
89
})
910

10-
#define SYS_OUT(cmd) ({ \
11-
FILE *f = popen((cmd), "r"); \
12-
if (!ASSERT_OK_PTR(f, (cmd))) \
11+
#define SYS_OUT(cmd, ...) ({ \
12+
char buf[1024]; \
13+
snprintf(buf, sizeof(buf), (cmd), ##__VA_ARGS__); \
14+
FILE *f = popen(buf, "r"); \
15+
if (!ASSERT_OK_PTR(f, buf)) \
1316
goto out; \
1417
f; \
1518
})
@@ -21,9 +24,10 @@ static bool expect_str(char *buf, size_t size, const char *str)
2124
return !memcmp(buf, str, size);
2225
}
2326

24-
void test_xdp_synproxy(void)
27+
static void test_synproxy(bool xdp)
2528
{
2629
int server_fd = -1, client_fd = -1, accept_fd = -1;
30+
char *prog_id, *prog_id_end;
2731
struct nstoken *ns = NULL;
2832
FILE *ctrl_file = NULL;
2933
char buf[1024];
@@ -39,8 +43,9 @@ void test_xdp_synproxy(void)
3943
// When checksum offload is enabled, the XDP program sees wrong
4044
// checksums and drops packets.
4145
SYS("ethtool -K tmp0 tx off");
42-
// Workaround required for veth.
43-
SYS("ip link set tmp0 xdp object xdp_dummy.o section xdp 2> /dev/null");
46+
if (xdp)
47+
// Workaround required for veth.
48+
SYS("ip link set tmp0 xdp object xdp_dummy.o section xdp 2> /dev/null");
4449

4550
ns = open_netns("synproxy");
4651
if (!ASSERT_OK_PTR(ns, "setns"))
@@ -60,14 +65,34 @@ void test_xdp_synproxy(void)
6065
SYS("iptables -t filter -A INPUT \
6166
-i tmp1 -m state --state INVALID -j DROP");
6267

63-
ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --ports 8080 --single \
64-
--mss4 1460 --mss6 1440 --wscale 7 --ttl 64");
68+
ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --ports 8080 \
69+
--single --mss4 1460 --mss6 1440 \
70+
--wscale 7 --ttl 64%s", xdp ? "" : " --tc");
6571
size = fread(buf, 1, sizeof(buf), ctrl_file);
6672
pclose(ctrl_file);
6773
if (!ASSERT_TRUE(expect_str(buf, size, "Total SYNACKs generated: 0\n"),
6874
"initial SYNACKs"))
6975
goto out;
7076

77+
if (!xdp) {
78+
ctrl_file = SYS_OUT("tc filter show dev tmp1 ingress");
79+
size = fread(buf, 1, sizeof(buf), ctrl_file);
80+
pclose(ctrl_file);
81+
prog_id = memmem(buf, size, " id ", 4);
82+
if (!ASSERT_OK_PTR(prog_id, "find prog id"))
83+
goto out;
84+
prog_id += 4;
85+
if (!ASSERT_LT(prog_id, buf + size, "find prog id begin"))
86+
goto out;
87+
prog_id_end = prog_id;
88+
while (prog_id_end < buf + size && *prog_id_end >= '0' &&
89+
*prog_id_end <= '9')
90+
prog_id_end++;
91+
if (!ASSERT_LT(prog_id_end, buf + size, "find prog id end"))
92+
goto out;
93+
*prog_id_end = '\0';
94+
}
95+
7196
server_fd = start_server(AF_INET, SOCK_STREAM, "198.18.0.2", 8080, 0);
7297
if (!ASSERT_GE(server_fd, 0, "start_server"))
7398
goto out;
@@ -87,7 +112,11 @@ void test_xdp_synproxy(void)
87112
if (!ASSERT_OK_PTR(ns, "setns"))
88113
goto out;
89114

90-
ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --single");
115+
if (xdp)
116+
ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --single");
117+
else
118+
ctrl_file = SYS_OUT("./xdp_synproxy --prog %s --single",
119+
prog_id);
91120
size = fread(buf, 1, sizeof(buf), ctrl_file);
92121
pclose(ctrl_file);
93122
if (!ASSERT_TRUE(expect_str(buf, size, "Total SYNACKs generated: 1\n"),
@@ -107,3 +136,9 @@ void test_xdp_synproxy(void)
107136
system("ip link del tmp0");
108137
system("ip netns del synproxy");
109138
}
139+
140+
void test_xdp_synproxy(void)
141+
{
142+
test_synproxy(true);
143+
test_synproxy(false);
144+
}

tools/testing/selftests/bpf/progs/xdp_synproxy_kern.c

Lines changed: 105 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#include <bpf/bpf_endian.h>
88
#include <asm/errno.h>
99

10+
#define TC_ACT_OK 0
11+
#define TC_ACT_SHOT 2
12+
1013
#define NSEC_PER_SEC 1000000000L
1114

1215
#define ETH_ALEN 6
@@ -80,6 +83,12 @@ extern struct nf_conn *bpf_xdp_ct_lookup(struct xdp_md *xdp_ctx,
8083
struct bpf_ct_opts *opts,
8184
__u32 len_opts) __ksym;
8285

86+
extern struct nf_conn *bpf_skb_ct_lookup(struct __sk_buff *skb_ctx,
87+
struct bpf_sock_tuple *bpf_tuple,
88+
u32 len_tuple,
89+
struct bpf_ct_opts *opts,
90+
u32 len_opts) __ksym;
91+
8392
extern void bpf_ct_release(struct nf_conn *ct) __ksym;
8493

8594
static __always_inline void swap_eth_addr(__u8 *a, __u8 *b)
@@ -380,7 +389,7 @@ static __always_inline int tcp_dissect(void *data, void *data_end,
380389
return XDP_TX;
381390
}
382391

383-
static __always_inline int tcp_lookup(struct xdp_md *ctx, struct header_pointers *hdr)
392+
static __always_inline int tcp_lookup(void *ctx, struct header_pointers *hdr, bool xdp)
384393
{
385394
struct bpf_ct_opts ct_lookup_opts = {
386395
.netns_id = BPF_F_CURRENT_NETNS,
@@ -410,7 +419,10 @@ static __always_inline int tcp_lookup(struct xdp_md *ctx, struct header_pointers
410419
// The verifier can't track that either ipv4 or ipv6 is not NULL.
411420
return XDP_ABORTED;
412421
}
413-
ct = bpf_xdp_ct_lookup(ctx, &tup, tup_size, &ct_lookup_opts, sizeof(ct_lookup_opts));
422+
if (xdp)
423+
ct = bpf_xdp_ct_lookup(ctx, &tup, tup_size, &ct_lookup_opts, sizeof(ct_lookup_opts));
424+
else
425+
ct = bpf_skb_ct_lookup(ctx, &tup, tup_size, &ct_lookup_opts, sizeof(ct_lookup_opts));
414426
if (ct) {
415427
unsigned long status = ct->status;
416428

@@ -523,8 +535,9 @@ static __always_inline void tcpv6_gen_synack(struct header_pointers *hdr,
523535
}
524536

525537
static __always_inline int syncookie_handle_syn(struct header_pointers *hdr,
526-
struct xdp_md *ctx,
527-
void *data, void *data_end)
538+
void *ctx,
539+
void *data, void *data_end,
540+
bool xdp)
528541
{
529542
__u32 old_pkt_size, new_pkt_size;
530543
// Unlike clang 10, clang 11 and 12 generate code that doesn't pass the
@@ -656,8 +669,13 @@ static __always_inline int syncookie_handle_syn(struct header_pointers *hdr,
656669
// Set the new packet size.
657670
old_pkt_size = data_end - data;
658671
new_pkt_size = sizeof(*hdr->eth) + ip_len + hdr->tcp->doff * 4;
659-
if (bpf_xdp_adjust_tail(ctx, new_pkt_size - old_pkt_size))
660-
return XDP_ABORTED;
672+
if (xdp) {
673+
if (bpf_xdp_adjust_tail(ctx, new_pkt_size - old_pkt_size))
674+
return XDP_ABORTED;
675+
} else {
676+
if (bpf_skb_change_tail(ctx, new_pkt_size, 0))
677+
return XDP_ABORTED;
678+
}
661679

662680
values_inc_synacks();
663681

@@ -683,68 +701,119 @@ static __always_inline int syncookie_handle_ack(struct header_pointers *hdr)
683701
return XDP_PASS;
684702
}
685703

686-
SEC("xdp/syncookie")
687-
int syncookie_xdp(struct xdp_md *ctx)
704+
static __always_inline int syncookie_part1(void *ctx, void *data, void *data_end,
705+
struct header_pointers *hdr, bool xdp)
688706
{
689-
void *data_end = (void *)(long)ctx->data_end;
690-
void *data = (void *)(long)ctx->data;
691-
struct header_pointers hdr;
692-
__s64 value;
693-
int ret;
694-
695707
struct bpf_ct_opts ct_lookup_opts = {
696708
.netns_id = BPF_F_CURRENT_NETNS,
697709
.l4proto = IPPROTO_TCP,
698710
};
711+
int ret;
699712

700-
ret = tcp_dissect(data, data_end, &hdr);
713+
ret = tcp_dissect(data, data_end, hdr);
701714
if (ret != XDP_TX)
702715
return ret;
703716

704-
ret = tcp_lookup(ctx, &hdr);
717+
ret = tcp_lookup(ctx, hdr, xdp);
705718
if (ret != XDP_TX)
706719
return ret;
707720

708721
// Packet is TCP and doesn't belong to an established connection.
709722

710-
if ((hdr.tcp->syn ^ hdr.tcp->ack) != 1)
723+
if ((hdr->tcp->syn ^ hdr->tcp->ack) != 1)
711724
return XDP_DROP;
712725

713-
// Grow the TCP header to TCP_MAXLEN to be able to pass any hdr.tcp_len
726+
// Grow the TCP header to TCP_MAXLEN to be able to pass any hdr->tcp_len
714727
// to bpf_tcp_raw_gen_syncookie_ipv{4,6} and pass the verifier.
715-
if (bpf_xdp_adjust_tail(ctx, TCP_MAXLEN - hdr.tcp_len))
716-
return XDP_ABORTED;
728+
if (xdp) {
729+
if (bpf_xdp_adjust_tail(ctx, TCP_MAXLEN - hdr->tcp_len))
730+
return XDP_ABORTED;
731+
} else {
732+
// Without volatile the verifier throws this error:
733+
// R9 32-bit pointer arithmetic prohibited
734+
volatile u64 old_len = data_end - data;
717735

718-
data_end = (void *)(long)ctx->data_end;
719-
data = (void *)(long)ctx->data;
736+
if (bpf_skb_change_tail(ctx, old_len + TCP_MAXLEN - hdr->tcp_len, 0))
737+
return XDP_ABORTED;
738+
}
739+
740+
return XDP_TX;
741+
}
720742

721-
if (hdr.ipv4) {
722-
hdr.eth = data;
723-
hdr.ipv4 = (void *)hdr.eth + sizeof(*hdr.eth);
743+
static __always_inline int syncookie_part2(void *ctx, void *data, void *data_end,
744+
struct header_pointers *hdr, bool xdp)
745+
{
746+
if (hdr->ipv4) {
747+
hdr->eth = data;
748+
hdr->ipv4 = (void *)hdr->eth + sizeof(*hdr->eth);
724749
// IPV4_MAXLEN is needed when calculating checksum.
725750
// At least sizeof(struct iphdr) is needed here to access ihl.
726-
if ((void *)hdr.ipv4 + IPV4_MAXLEN > data_end)
751+
if ((void *)hdr->ipv4 + IPV4_MAXLEN > data_end)
727752
return XDP_ABORTED;
728-
hdr.tcp = (void *)hdr.ipv4 + hdr.ipv4->ihl * 4;
729-
} else if (hdr.ipv6) {
730-
hdr.eth = data;
731-
hdr.ipv6 = (void *)hdr.eth + sizeof(*hdr.eth);
732-
hdr.tcp = (void *)hdr.ipv6 + sizeof(*hdr.ipv6);
753+
hdr->tcp = (void *)hdr->ipv4 + hdr->ipv4->ihl * 4;
754+
} else if (hdr->ipv6) {
755+
hdr->eth = data;
756+
hdr->ipv6 = (void *)hdr->eth + sizeof(*hdr->eth);
757+
hdr->tcp = (void *)hdr->ipv6 + sizeof(*hdr->ipv6);
733758
} else {
734759
return XDP_ABORTED;
735760
}
736761

737-
if ((void *)hdr.tcp + TCP_MAXLEN > data_end)
762+
if ((void *)hdr->tcp + TCP_MAXLEN > data_end)
738763
return XDP_ABORTED;
739764

740765
// We run out of registers, tcp_len gets spilled to the stack, and the
741766
// verifier forgets its min and max values checked above in tcp_dissect.
742-
hdr.tcp_len = hdr.tcp->doff * 4;
743-
if (hdr.tcp_len < sizeof(*hdr.tcp))
767+
hdr->tcp_len = hdr->tcp->doff * 4;
768+
if (hdr->tcp_len < sizeof(*hdr->tcp))
744769
return XDP_ABORTED;
745770

746-
return hdr.tcp->syn ? syncookie_handle_syn(&hdr, ctx, data, data_end) :
747-
syncookie_handle_ack(&hdr);
771+
return hdr->tcp->syn ? syncookie_handle_syn(hdr, ctx, data, data_end, xdp) :
772+
syncookie_handle_ack(hdr);
773+
}
774+
775+
SEC("xdp/syncookie")
776+
int syncookie_xdp(struct xdp_md *ctx)
777+
{
778+
void *data_end = (void *)(long)ctx->data_end;
779+
void *data = (void *)(long)ctx->data;
780+
struct header_pointers hdr;
781+
int ret;
782+
783+
ret = syncookie_part1(ctx, data, data_end, &hdr, true);
784+
if (ret != XDP_TX)
785+
return ret;
786+
787+
data_end = (void *)(long)ctx->data_end;
788+
data = (void *)(long)ctx->data;
789+
790+
return syncookie_part2(ctx, data, data_end, &hdr, true);
791+
}
792+
793+
SEC("tc")
794+
int syncookie_tc(struct __sk_buff *skb)
795+
{
796+
void *data_end = (void *)(long)skb->data_end;
797+
void *data = (void *)(long)skb->data;
798+
struct header_pointers hdr;
799+
int ret;
800+
801+
ret = syncookie_part1(skb, data, data_end, &hdr, false);
802+
if (ret != XDP_TX)
803+
return ret == XDP_PASS ? TC_ACT_OK : TC_ACT_SHOT;
804+
805+
data_end = (void *)(long)skb->data_end;
806+
data = (void *)(long)skb->data;
807+
808+
ret = syncookie_part2(skb, data, data_end, &hdr, false);
809+
switch (ret) {
810+
case XDP_PASS:
811+
return TC_ACT_OK;
812+
case XDP_TX:
813+
return bpf_redirect(skb->ifindex, 0);
814+
default:
815+
return TC_ACT_SHOT;
816+
}
748817
}
749818

750819
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)