/
af_xdp_kern.c
193 lines (159 loc) · 4.91 KB
/
af_xdp_kern.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h> /* bpf_core_type_id_local */
#include "xdp/parsing_helpers.h"
#include "af_xdp_kern_shared.h"
struct {
__uint(type, BPF_MAP_TYPE_XSKMAP);
__uint(max_entries, MAX_AF_SOCKS);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
} xsks_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__type(key, __u32);
__type(value, __u32);
__uint(max_entries, MAX_AF_SOCKS);
} xdp_stats_map SEC(".maps");
/*
* The xdp_hints_xxx struct's are stored in the XDP 'data_meta' area,
* which is located just in-front-of the raw packet payload data.
*
* Explaining the struct attribute's:
* ----------------------------------
* The struct must be 4 byte aligned (kernel requirement), which here
* is enforced by the struct __attribute__((aligned(4))).
*
* To avoid any C-struct padding attribute "packed" is used.
*
* NOTICE: Do NOT define __attribute__((preserve_access_index)) here,
* as libbpf will try to find a matching kernel data-structure,
* e.g. it will cause BPF-prog loading step to fail (with invalid func
* unknown#195896080 which is 0xbad2310 in hex for "bad relo").
*/
struct xdp_hints_mark {
__u32 mark;
__u32 btf_id;
} __attribute__((aligned(4))) __attribute__((packed));
struct xdp_hints_rx_time {
__u64 rx_ktime;
__u32 xdp_rx_cpu;
__u32 btf_id;
} __attribute__((aligned(4))) __attribute__((packed));
int meta_add_rx_time(struct xdp_md *ctx)
{
struct xdp_hints_rx_time *meta;
void *data;
int err;
/* Reserve space in-front of data pointer for our meta info.
* (Notice drivers not supporting data_meta will fail here!)
*/
err = bpf_xdp_adjust_meta(ctx, -(int)sizeof(*meta));
if (err)
return -1;
/* Notice: Kernel-side verifier requires that loading of
* ctx->data MUST happen _after_ helper bpf_xdp_adjust_meta(),
* as pkt-data pointers are invalidated. Helpers that require
* this are determined/marked by bpf_helper_changes_pkt_data()
*/
data = (void *)(unsigned long)ctx->data;
meta = (void *)(unsigned long)ctx->data_meta;
if (meta + 1 > data) /* Verify meta area is accessible */
return -2;
meta->rx_ktime = bpf_ktime_get_ns();
meta->xdp_rx_cpu = bpf_get_smp_processor_id();
/* Userspace can identify struct used by BTF id */
meta->btf_id = bpf_core_type_id_local(struct xdp_hints_rx_time);
return 0;
}
int meta_add_mark(struct xdp_md *ctx, __u32 mark)
{
struct xdp_hints_mark *meta;
void *data;
int err;
/* Reserve space in-front of data pointer for our meta info */
err = bpf_xdp_adjust_meta(ctx, -(int)sizeof(*meta));
if (err)
return -1;
data = (void *)(unsigned long)ctx->data;
meta = (void *)(unsigned long)ctx->data_meta;
if (meta + 1 > data) /* Verify meta area is accessible */
return -2;
meta->mark = mark;
meta->btf_id = bpf_core_type_id_local(struct xdp_hints_mark);
return 0;
}
/* Neighbor Discovery in IPv6 - Values taken from RFC4861 */
#define NDP_R_SOL 133 /* Router Solicitation */
#define NDP_R_ADV 134 /* Router Advertisement */
#define NDP_SOL 135 /* Neighbor Solicitation */
#define NDP_ADV 136 /* Neighbor Advertisement */
#define NDP_REDIR 137 /* Redirect Message */
int parse_pkt__is_ARP_or_NDP(struct xdp_md *ctx)
{
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct hdr_cursor nh = { .pos = data };
struct ethhdr *eth;
int eth_type;
eth_type = parse_ethhdr(&nh, data_end, ð);
if (eth_type < 0)
return -1;
if (eth_type == bpf_htons(ETH_P_ARP))
return 1;
if (eth_type == bpf_htons(ETH_P_IPV6)) {
struct ipv6hdr *ip6h;
int ip_type = parse_ip6hdr(&nh, data_end, &ip6h);
if (ip_type < 0)
return -1;
if (ip_type == IPPROTO_ICMPV6) {
struct icmp6hdr *icmp6hdr;
int icmp6_type = parse_icmp6hdr(&nh, data_end, &icmp6hdr);
if (icmp6_type < 0)
return -1;
if (icmp6_type >= NDP_R_SOL &&
icmp6_type <= NDP_REDIR)
return 1;
}
}
return 0;
}
SEC("xdp_sock")
int xdp_sock_prog(struct xdp_md *ctx)
{
int index = ctx->rx_queue_index;
__u32 *pkt_count;
int err, ret;
pkt_count = bpf_map_lookup_elem(&xdp_stats_map, &index);
if (!pkt_count)
return XDP_ABORTED;
__u64 cnt = (*pkt_count)++;
// if (cnt == 0) {
// if (bpf_ktime_get_ns() == 42)
// return XDP_ABORTED;
// cnt++;
// }
/* Notice how two different xdp_hints meta-data are used */
if ((cnt % 2) == 0) {
err = meta_add_rx_time(ctx);
if (err < 0)
return XDP_ABORTED;
} else {
err = meta_add_mark(ctx, 42);
if (err < 0)
return XDP_DROP;
}
/* Let network stack handle ARP and IPv6 Neigh Solicitation */
ret = parse_pkt__is_ARP_or_NDP(ctx);
if (ret < 0)
return XDP_ABORTED;
if (ret == 1)
return XDP_PASS;
/* A set entry here means that the correspnding queue_id
* has an active AF_XDP socket bound to it. */
if (bpf_map_lookup_elem(&xsks_map, &index))
return bpf_redirect_map(&xsks_map, index, 0);
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";