1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2022 Meta
8 #include <linux/stddef.h>
9 #include <linux/pkt_cls.h>
10 #include <linux/if_ether.h>
13 #include <linux/ipv6.h>
14 #include <linux/tcp.h>
15 #include <linux/udp.h>
16 #include <bpf/bpf_helpers.h>
17 #include <bpf/bpf_endian.h>
19 /* veth_src --- veth_src_fwd --- veth_det_fwd --- veth_dst
21 * ns_src | ns_fwd | ns_dst
23 * ns_src and ns_dst: ENDHOST namespace
24 * ns_fwd: Fowarding namespace
27 #define ctx_ptr(field) (void *)(long)(field)
29 #define ip4_src __bpf_htonl(0xac100164) /* 172.16.1.100 */
30 #define ip4_dst __bpf_htonl(0xac100264) /* 172.16.2.100 */
32 #define ip6_src { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
33 0x00, 0x01, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe }
34 #define ip6_dst { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
35 0x00, 0x02, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe }
37 #define v6_equal(a, b) (a.s6_addr32[0] == b.s6_addr32[0] && \
38 a.s6_addr32[1] == b.s6_addr32[1] && \
39 a.s6_addr32[2] == b.s6_addr32[2] && \
40 a.s6_addr32[3] == b.s6_addr32[3])
42 volatile const __u32 IFINDEX_SRC;
43 volatile const __u32 IFINDEX_DST;
45 #define EGRESS_ENDHOST_MAGIC 0x0b9fbeef
46 #define INGRESS_FWDNS_MAGIC 0x1b9fbeef
47 #define EGRESS_FWDNS_MAGIC 0x2b9fbeef
79 __u32 dtimes[__NR_TESTS][__MAX_CNT] = {};
80 __u32 errs[__NR_TESTS][__MAX_CNT] = {};
83 static void inc_dtimes(__u32 idx)
85 if (test < __NR_TESTS)
88 dtimes[UKN_TEST][idx]++;
91 static void inc_errs(__u32 idx)
93 if (test < __NR_TESTS)
96 errs[UKN_TEST][idx]++;
99 static int skb_proto(int type)
104 static int skb_ns(int type)
106 return (type >> 8) & 0xff;
109 static bool fwdns_clear_dtime(void)
111 return test == TCP_IP6_CLEAR_DTIME;
114 static bool bpf_fwd(void)
116 return test < TCP_IP4_RT_FWD;
119 static __u8 get_proto(void)
132 /* -1: parse error: TC_ACT_SHOT
133 * 0: not testing traffic: TC_ACT_OK
134 * >0: first byte is the inet_proto, second byte has the netns
137 static int skb_get_type(struct __sk_buff *skb)
139 __u16 dst_ns_port = __bpf_htons(50000 + test);
140 void *data_end = ctx_ptr(skb->data_end);
141 void *data = ctx_ptr(skb->data);
142 __u8 inet_proto = 0, ns = 0;
143 struct ipv6hdr *ip6h;
150 switch (skb->protocol) {
151 case __bpf_htons(ETH_P_IP):
152 iph = data + sizeof(struct ethhdr);
153 if (iph + 1 > data_end)
155 if (iph->saddr == ip4_src)
157 else if (iph->saddr == ip4_dst)
159 inet_proto = iph->protocol;
162 case __bpf_htons(ETH_P_IPV6):
163 ip6h = data + sizeof(struct ethhdr);
164 if (ip6h + 1 > data_end)
166 if (v6_equal(ip6h->saddr, (struct in6_addr){{ip6_src}}))
168 else if (v6_equal(ip6h->saddr, (struct in6_addr){{ip6_dst}}))
170 inet_proto = ip6h->nexthdr;
177 /* skb is not from src_ns or dst_ns.
178 * skb is not the testing IPPROTO.
180 if (!ns || inet_proto != get_proto())
183 switch (inet_proto) {
186 if (th + 1 > data_end)
193 if (uh + 1 > data_end)
202 /* The skb is the testing traffic */
203 if ((ns == SRC_NS && dport == dst_ns_port) ||
204 (ns == DST_NS && sport == dst_ns_port))
205 return (ns << 8 | inet_proto);
210 /* format: direction@iface@netns
211 * egress@veth_(src|dst)@ns_(src|dst)
214 int egress_host(struct __sk_buff *skb)
218 skb_type = skb_get_type(skb);
224 if (skb_proto(skb_type) == IPPROTO_TCP) {
225 if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO &&
227 inc_dtimes(EGRESS_ENDHOST);
229 inc_errs(EGRESS_ENDHOST);
231 if (skb->tstamp_type == BPF_SKB_TSTAMP_UNSPEC &&
233 inc_dtimes(EGRESS_ENDHOST);
235 inc_errs(EGRESS_ENDHOST);
238 skb->tstamp = EGRESS_ENDHOST_MAGIC;
243 /* ingress@veth_(src|dst)@ns_(src|dst) */
245 int ingress_host(struct __sk_buff *skb)
249 skb_type = skb_get_type(skb);
255 if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO &&
256 skb->tstamp == EGRESS_FWDNS_MAGIC)
257 inc_dtimes(INGRESS_ENDHOST);
259 inc_errs(INGRESS_ENDHOST);
264 /* ingress@veth_(src|dst)_fwd@ns_fwd priority 100 */
266 int ingress_fwdns_prio100(struct __sk_buff *skb)
270 skb_type = skb_get_type(skb);
276 /* delivery_time is only available to the ingress
277 * if the tc-bpf checks the skb->tstamp_type.
279 if (skb->tstamp == EGRESS_ENDHOST_MAGIC)
280 inc_errs(INGRESS_FWDNS_P100);
282 if (fwdns_clear_dtime())
285 return TC_ACT_UNSPEC;
288 /* egress@veth_(src|dst)_fwd@ns_fwd priority 100 */
290 int egress_fwdns_prio100(struct __sk_buff *skb)
294 skb_type = skb_get_type(skb);
300 /* delivery_time is always available to egress even
301 * the tc-bpf did not use the tstamp_type.
303 if (skb->tstamp == INGRESS_FWDNS_MAGIC)
304 inc_dtimes(EGRESS_FWDNS_P100);
306 inc_errs(EGRESS_FWDNS_P100);
308 if (fwdns_clear_dtime())
311 return TC_ACT_UNSPEC;
314 /* ingress@veth_(src|dst)_fwd@ns_fwd priority 101 */
316 int ingress_fwdns_prio101(struct __sk_buff *skb)
318 __u64 expected_dtime = EGRESS_ENDHOST_MAGIC;
321 skb_type = skb_get_type(skb);
322 if (skb_type == -1 || !skb_type)
323 /* Should have handled in prio100 */
326 if (skb_proto(skb_type) == IPPROTO_UDP)
329 if (skb->tstamp_type) {
330 if (fwdns_clear_dtime() ||
331 skb->tstamp_type != BPF_SKB_TSTAMP_DELIVERY_MONO ||
332 skb->tstamp != expected_dtime)
333 inc_errs(INGRESS_FWDNS_P101);
335 inc_dtimes(INGRESS_FWDNS_P101);
337 if (!fwdns_clear_dtime() && expected_dtime)
338 inc_errs(INGRESS_FWDNS_P101);
341 if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO) {
342 skb->tstamp = INGRESS_FWDNS_MAGIC;
344 if (bpf_skb_set_tstamp(skb, INGRESS_FWDNS_MAGIC,
345 BPF_SKB_TSTAMP_DELIVERY_MONO))
347 if (!bpf_skb_set_tstamp(skb, INGRESS_FWDNS_MAGIC,
348 BPF_SKB_TSTAMP_UNSPEC))
352 if (skb_ns(skb_type) == SRC_NS)
354 bpf_redirect_neigh(IFINDEX_DST, NULL, 0, 0) : TC_ACT_OK;
357 bpf_redirect_neigh(IFINDEX_SRC, NULL, 0, 0) : TC_ACT_OK;
360 /* egress@veth_(src|dst)_fwd@ns_fwd priority 101 */
362 int egress_fwdns_prio101(struct __sk_buff *skb)
366 skb_type = skb_get_type(skb);
367 if (skb_type == -1 || !skb_type)
368 /* Should have handled in prio100 */
371 if (skb->tstamp_type) {
372 if (fwdns_clear_dtime() ||
373 skb->tstamp_type != BPF_SKB_TSTAMP_DELIVERY_MONO ||
374 skb->tstamp != INGRESS_FWDNS_MAGIC)
375 inc_errs(EGRESS_FWDNS_P101);
377 inc_dtimes(EGRESS_FWDNS_P101);
379 if (!fwdns_clear_dtime())
380 inc_errs(EGRESS_FWDNS_P101);
383 if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO) {
384 skb->tstamp = EGRESS_FWDNS_MAGIC;
386 if (bpf_skb_set_tstamp(skb, EGRESS_FWDNS_MAGIC,
387 BPF_SKB_TSTAMP_DELIVERY_MONO))
389 if (!bpf_skb_set_tstamp(skb, INGRESS_FWDNS_MAGIC,
390 BPF_SKB_TSTAMP_UNSPEC))
397 char __license[] SEC("license") = "GPL";