1c803475fSMartin KaFai Lau // SPDX-License-Identifier: GPL-2.0
2c803475fSMartin KaFai Lau // Copyright (c) 2022 Meta
3c803475fSMartin KaFai Lau 
4c803475fSMartin KaFai Lau #include <stddef.h>
5c803475fSMartin KaFai Lau #include <stdint.h>
6c803475fSMartin KaFai Lau #include <stdbool.h>
7c803475fSMartin KaFai Lau #include <linux/bpf.h>
8c803475fSMartin KaFai Lau #include <linux/stddef.h>
9c803475fSMartin KaFai Lau #include <linux/pkt_cls.h>
10c803475fSMartin KaFai Lau #include <linux/if_ether.h>
11c803475fSMartin KaFai Lau #include <linux/in.h>
12c803475fSMartin KaFai Lau #include <linux/ip.h>
13c803475fSMartin KaFai Lau #include <linux/ipv6.h>
14e6ff92f4SMartin KaFai Lau #include <linux/tcp.h>
15e6ff92f4SMartin KaFai Lau #include <linux/udp.h>
16c803475fSMartin KaFai Lau #include <bpf/bpf_helpers.h>
17c803475fSMartin KaFai Lau #include <bpf/bpf_endian.h>
18c803475fSMartin KaFai Lau 
19c803475fSMartin KaFai Lau /* veth_src --- veth_src_fwd --- veth_det_fwd --- veth_dst
20c803475fSMartin KaFai Lau  *           |                                 |
21c803475fSMartin KaFai Lau  *  ns_src   |              ns_fwd             |   ns_dst
22c803475fSMartin KaFai Lau  *
23c803475fSMartin KaFai Lau  * ns_src and ns_dst: ENDHOST namespace
24c803475fSMartin KaFai Lau  *            ns_fwd: Fowarding namespace
25c803475fSMartin KaFai Lau  */
26c803475fSMartin KaFai Lau 
27c803475fSMartin KaFai Lau #define ctx_ptr(field)		(void *)(long)(field)
28c803475fSMartin KaFai Lau 
29c803475fSMartin KaFai Lau #define ip4_src			__bpf_htonl(0xac100164) /* 172.16.1.100 */
30c803475fSMartin KaFai Lau #define ip4_dst			__bpf_htonl(0xac100264) /* 172.16.2.100 */
31c803475fSMartin KaFai Lau 
32c803475fSMartin KaFai Lau #define ip6_src			{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
33c803475fSMartin KaFai Lau 				  0x00, 0x01, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe }
34c803475fSMartin KaFai Lau #define ip6_dst			{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
35c803475fSMartin KaFai Lau 				  0x00, 0x02, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe }
36c803475fSMartin KaFai Lau 
37c803475fSMartin KaFai Lau #define v6_equal(a, b)		(a.s6_addr32[0] == b.s6_addr32[0] && \
38c803475fSMartin KaFai Lau 				 a.s6_addr32[1] == b.s6_addr32[1] && \
39c803475fSMartin KaFai Lau 				 a.s6_addr32[2] == b.s6_addr32[2] && \
40c803475fSMartin KaFai Lau 				 a.s6_addr32[3] == b.s6_addr32[3])
41c803475fSMartin KaFai Lau 
42c803475fSMartin KaFai Lau volatile const __u32 IFINDEX_SRC;
43c803475fSMartin KaFai Lau volatile const __u32 IFINDEX_DST;
44c803475fSMartin KaFai Lau 
45c803475fSMartin KaFai Lau #define EGRESS_ENDHOST_MAGIC	0x0b9fbeef
46c803475fSMartin KaFai Lau #define INGRESS_FWDNS_MAGIC	0x1b9fbeef
47c803475fSMartin KaFai Lau #define EGRESS_FWDNS_MAGIC	0x2b9fbeef
48c803475fSMartin KaFai Lau 
49c803475fSMartin KaFai Lau enum {
50c803475fSMartin KaFai Lau 	INGRESS_FWDNS_P100,
51c803475fSMartin KaFai Lau 	INGRESS_FWDNS_P101,
52c803475fSMartin KaFai Lau 	EGRESS_FWDNS_P100,
53c803475fSMartin KaFai Lau 	EGRESS_FWDNS_P101,
54c803475fSMartin KaFai Lau 	INGRESS_ENDHOST,
55c803475fSMartin KaFai Lau 	EGRESS_ENDHOST,
56c803475fSMartin KaFai Lau 	SET_DTIME,
57c803475fSMartin KaFai Lau 	__MAX_CNT,
58c803475fSMartin KaFai Lau };
59c803475fSMartin KaFai Lau 
60c803475fSMartin KaFai Lau enum {
61c803475fSMartin KaFai Lau 	TCP_IP6_CLEAR_DTIME,
62c803475fSMartin KaFai Lau 	TCP_IP4,
63c803475fSMartin KaFai Lau 	TCP_IP6,
64c803475fSMartin KaFai Lau 	UDP_IP4,
65c803475fSMartin KaFai Lau 	UDP_IP6,
66c803475fSMartin KaFai Lau 	TCP_IP4_RT_FWD,
67c803475fSMartin KaFai Lau 	TCP_IP6_RT_FWD,
68c803475fSMartin KaFai Lau 	UDP_IP4_RT_FWD,
69c803475fSMartin KaFai Lau 	UDP_IP6_RT_FWD,
70c803475fSMartin KaFai Lau 	UKN_TEST,
71c803475fSMartin KaFai Lau 	__NR_TESTS,
72c803475fSMartin KaFai Lau };
73c803475fSMartin KaFai Lau 
74c803475fSMartin KaFai Lau enum {
75c803475fSMartin KaFai Lau 	SRC_NS = 1,
76c803475fSMartin KaFai Lau 	DST_NS,
77c803475fSMartin KaFai Lau };
78c803475fSMartin KaFai Lau 
79c803475fSMartin KaFai Lau __u32 dtimes[__NR_TESTS][__MAX_CNT] = {};
80c803475fSMartin KaFai Lau __u32 errs[__NR_TESTS][__MAX_CNT] = {};
81c803475fSMartin KaFai Lau __u32 test = 0;
82c803475fSMartin KaFai Lau 
inc_dtimes(__u32 idx)83c803475fSMartin KaFai Lau static void inc_dtimes(__u32 idx)
84c803475fSMartin KaFai Lau {
85c803475fSMartin KaFai Lau 	if (test < __NR_TESTS)
86c803475fSMartin KaFai Lau 		dtimes[test][idx]++;
87c803475fSMartin KaFai Lau 	else
88c803475fSMartin KaFai Lau 		dtimes[UKN_TEST][idx]++;
89c803475fSMartin KaFai Lau }
90c803475fSMartin KaFai Lau 
inc_errs(__u32 idx)91c803475fSMartin KaFai Lau static void inc_errs(__u32 idx)
92c803475fSMartin KaFai Lau {
93c803475fSMartin KaFai Lau 	if (test < __NR_TESTS)
94c803475fSMartin KaFai Lau 		errs[test][idx]++;
95c803475fSMartin KaFai Lau 	else
96c803475fSMartin KaFai Lau 		errs[UKN_TEST][idx]++;
97c803475fSMartin KaFai Lau }
98c803475fSMartin KaFai Lau 
skb_proto(int type)99c803475fSMartin KaFai Lau static int skb_proto(int type)
100c803475fSMartin KaFai Lau {
101c803475fSMartin KaFai Lau 	return type & 0xff;
102c803475fSMartin KaFai Lau }
103c803475fSMartin KaFai Lau 
skb_ns(int type)104c803475fSMartin KaFai Lau static int skb_ns(int type)
105c803475fSMartin KaFai Lau {
106c803475fSMartin KaFai Lau 	return (type >> 8) & 0xff;
107c803475fSMartin KaFai Lau }
108c803475fSMartin KaFai Lau 
fwdns_clear_dtime(void)109c803475fSMartin KaFai Lau static bool fwdns_clear_dtime(void)
110c803475fSMartin KaFai Lau {
111c803475fSMartin KaFai Lau 	return test == TCP_IP6_CLEAR_DTIME;
112c803475fSMartin KaFai Lau }
113c803475fSMartin KaFai Lau 
bpf_fwd(void)114c803475fSMartin KaFai Lau static bool bpf_fwd(void)
115c803475fSMartin KaFai Lau {
116c803475fSMartin KaFai Lau 	return test < TCP_IP4_RT_FWD;
117c803475fSMartin KaFai Lau }
118c803475fSMartin KaFai Lau 
get_proto(void)119e6ff92f4SMartin KaFai Lau static __u8 get_proto(void)
120e6ff92f4SMartin KaFai Lau {
121e6ff92f4SMartin KaFai Lau 	switch (test) {
122e6ff92f4SMartin KaFai Lau 	case UDP_IP4:
123e6ff92f4SMartin KaFai Lau 	case UDP_IP6:
124e6ff92f4SMartin KaFai Lau 	case UDP_IP4_RT_FWD:
125e6ff92f4SMartin KaFai Lau 	case UDP_IP6_RT_FWD:
126e6ff92f4SMartin KaFai Lau 		return IPPROTO_UDP;
127e6ff92f4SMartin KaFai Lau 	default:
128e6ff92f4SMartin KaFai Lau 		return IPPROTO_TCP;
129e6ff92f4SMartin KaFai Lau 	}
130e6ff92f4SMartin KaFai Lau }
131e6ff92f4SMartin KaFai Lau 
132c803475fSMartin KaFai Lau /* -1: parse error: TC_ACT_SHOT
133c803475fSMartin KaFai Lau  *  0: not testing traffic: TC_ACT_OK
134c803475fSMartin KaFai Lau  * >0: first byte is the inet_proto, second byte has the netns
135c803475fSMartin KaFai Lau  *     of the sender
136c803475fSMartin KaFai Lau  */
skb_get_type(struct __sk_buff * skb)137c803475fSMartin KaFai Lau static int skb_get_type(struct __sk_buff *skb)
138c803475fSMartin KaFai Lau {
139e6ff92f4SMartin KaFai Lau 	__u16 dst_ns_port = __bpf_htons(50000 + test);
140c803475fSMartin KaFai Lau 	void *data_end = ctx_ptr(skb->data_end);
141c803475fSMartin KaFai Lau 	void *data = ctx_ptr(skb->data);
142c803475fSMartin KaFai Lau 	__u8 inet_proto = 0, ns = 0;
143c803475fSMartin KaFai Lau 	struct ipv6hdr *ip6h;
144e6ff92f4SMartin KaFai Lau 	__u16 sport, dport;
145c803475fSMartin KaFai Lau 	struct iphdr *iph;
146e6ff92f4SMartin KaFai Lau 	struct tcphdr *th;
147e6ff92f4SMartin KaFai Lau 	struct udphdr *uh;
148e6ff92f4SMartin KaFai Lau 	void *trans;
149c803475fSMartin KaFai Lau 
150c803475fSMartin KaFai Lau 	switch (skb->protocol) {
151c803475fSMartin KaFai Lau 	case __bpf_htons(ETH_P_IP):
152c803475fSMartin KaFai Lau 		iph = data + sizeof(struct ethhdr);
153c803475fSMartin KaFai Lau 		if (iph + 1 > data_end)
154c803475fSMartin KaFai Lau 			return -1;
155c803475fSMartin KaFai Lau 		if (iph->saddr == ip4_src)
156c803475fSMartin KaFai Lau 			ns = SRC_NS;
157c803475fSMartin KaFai Lau 		else if (iph->saddr == ip4_dst)
158c803475fSMartin KaFai Lau 			ns = DST_NS;
159c803475fSMartin KaFai Lau 		inet_proto = iph->protocol;
160e6ff92f4SMartin KaFai Lau 		trans = iph + 1;
161c803475fSMartin KaFai Lau 		break;
162c803475fSMartin KaFai Lau 	case __bpf_htons(ETH_P_IPV6):
163c803475fSMartin KaFai Lau 		ip6h = data + sizeof(struct ethhdr);
164c803475fSMartin KaFai Lau 		if (ip6h + 1 > data_end)
165c803475fSMartin KaFai Lau 			return -1;
166*c8ed6685SAndrii Nakryiko 		if (v6_equal(ip6h->saddr, (struct in6_addr){{ip6_src}}))
167c803475fSMartin KaFai Lau 			ns = SRC_NS;
168*c8ed6685SAndrii Nakryiko 		else if (v6_equal(ip6h->saddr, (struct in6_addr){{ip6_dst}}))
169c803475fSMartin KaFai Lau 			ns = DST_NS;
170c803475fSMartin KaFai Lau 		inet_proto = ip6h->nexthdr;
171e6ff92f4SMartin KaFai Lau 		trans = ip6h + 1;
172c803475fSMartin KaFai Lau 		break;
173c803475fSMartin KaFai Lau 	default:
174c803475fSMartin KaFai Lau 		return 0;
175c803475fSMartin KaFai Lau 	}
176c803475fSMartin KaFai Lau 
177e6ff92f4SMartin KaFai Lau 	/* skb is not from src_ns or dst_ns.
178e6ff92f4SMartin KaFai Lau 	 * skb is not the testing IPPROTO.
179e6ff92f4SMartin KaFai Lau 	 */
180e6ff92f4SMartin KaFai Lau 	if (!ns || inet_proto != get_proto())
181c803475fSMartin KaFai Lau 		return 0;
182c803475fSMartin KaFai Lau 
183e6ff92f4SMartin KaFai Lau 	switch (inet_proto) {
184e6ff92f4SMartin KaFai Lau 	case IPPROTO_TCP:
185e6ff92f4SMartin KaFai Lau 		th = trans;
186e6ff92f4SMartin KaFai Lau 		if (th + 1 > data_end)
187e6ff92f4SMartin KaFai Lau 			return -1;
188e6ff92f4SMartin KaFai Lau 		sport = th->source;
189e6ff92f4SMartin KaFai Lau 		dport = th->dest;
190e6ff92f4SMartin KaFai Lau 		break;
191e6ff92f4SMartin KaFai Lau 	case IPPROTO_UDP:
192e6ff92f4SMartin KaFai Lau 		uh = trans;
193e6ff92f4SMartin KaFai Lau 		if (uh + 1 > data_end)
194e6ff92f4SMartin KaFai Lau 			return -1;
195e6ff92f4SMartin KaFai Lau 		sport = uh->source;
196e6ff92f4SMartin KaFai Lau 		dport = uh->dest;
197e6ff92f4SMartin KaFai Lau 		break;
198e6ff92f4SMartin KaFai Lau 	default:
199e6ff92f4SMartin KaFai Lau 		return 0;
200e6ff92f4SMartin KaFai Lau 	}
201e6ff92f4SMartin KaFai Lau 
202e6ff92f4SMartin KaFai Lau 	/* The skb is the testing traffic */
203e6ff92f4SMartin KaFai Lau 	if ((ns == SRC_NS && dport == dst_ns_port) ||
204e6ff92f4SMartin KaFai Lau 	    (ns == DST_NS && sport == dst_ns_port))
205c803475fSMartin KaFai Lau 		return (ns << 8 | inet_proto);
206e6ff92f4SMartin KaFai Lau 
207e6ff92f4SMartin KaFai Lau 	return 0;
208c803475fSMartin KaFai Lau }
209c803475fSMartin KaFai Lau 
210c803475fSMartin KaFai Lau /* format: direction@iface@netns
211c803475fSMartin KaFai Lau  * egress@veth_(src|dst)@ns_(src|dst)
212c803475fSMartin KaFai Lau  */
213c803475fSMartin KaFai Lau SEC("tc")
egress_host(struct __sk_buff * skb)214c803475fSMartin KaFai Lau int egress_host(struct __sk_buff *skb)
215c803475fSMartin KaFai Lau {
216c803475fSMartin KaFai Lau 	int skb_type;
217c803475fSMartin KaFai Lau 
218c803475fSMartin KaFai Lau 	skb_type = skb_get_type(skb);
219c803475fSMartin KaFai Lau 	if (skb_type == -1)
220c803475fSMartin KaFai Lau 		return TC_ACT_SHOT;
221c803475fSMartin KaFai Lau 	if (!skb_type)
222c803475fSMartin KaFai Lau 		return TC_ACT_OK;
223c803475fSMartin KaFai Lau 
224c803475fSMartin KaFai Lau 	if (skb_proto(skb_type) == IPPROTO_TCP) {
2253daf0896SMartin KaFai Lau 		if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO &&
226c803475fSMartin KaFai Lau 		    skb->tstamp)
227c803475fSMartin KaFai Lau 			inc_dtimes(EGRESS_ENDHOST);
228c803475fSMartin KaFai Lau 		else
229c803475fSMartin KaFai Lau 			inc_errs(EGRESS_ENDHOST);
230c803475fSMartin KaFai Lau 	} else {
2313daf0896SMartin KaFai Lau 		if (skb->tstamp_type == BPF_SKB_TSTAMP_UNSPEC &&
232c803475fSMartin KaFai Lau 		    skb->tstamp)
233c803475fSMartin KaFai Lau 			inc_dtimes(EGRESS_ENDHOST);
234c803475fSMartin KaFai Lau 		else
235c803475fSMartin KaFai Lau 			inc_errs(EGRESS_ENDHOST);
236c803475fSMartin KaFai Lau 	}
237c803475fSMartin KaFai Lau 
238c803475fSMartin KaFai Lau 	skb->tstamp = EGRESS_ENDHOST_MAGIC;
239c803475fSMartin KaFai Lau 
240c803475fSMartin KaFai Lau 	return TC_ACT_OK;
241c803475fSMartin KaFai Lau }
242c803475fSMartin KaFai Lau 
243c803475fSMartin KaFai Lau /* ingress@veth_(src|dst)@ns_(src|dst) */
244c803475fSMartin KaFai Lau SEC("tc")
ingress_host(struct __sk_buff * skb)245c803475fSMartin KaFai Lau int ingress_host(struct __sk_buff *skb)
246c803475fSMartin KaFai Lau {
247c803475fSMartin KaFai Lau 	int skb_type;
248c803475fSMartin KaFai Lau 
249c803475fSMartin KaFai Lau 	skb_type = skb_get_type(skb);
250c803475fSMartin KaFai Lau 	if (skb_type == -1)
251c803475fSMartin KaFai Lau 		return TC_ACT_SHOT;
252c803475fSMartin KaFai Lau 	if (!skb_type)
253c803475fSMartin KaFai Lau 		return TC_ACT_OK;
254c803475fSMartin KaFai Lau 
2553daf0896SMartin KaFai Lau 	if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO &&
256c803475fSMartin KaFai Lau 	    skb->tstamp == EGRESS_FWDNS_MAGIC)
257c803475fSMartin KaFai Lau 		inc_dtimes(INGRESS_ENDHOST);
258c803475fSMartin KaFai Lau 	else
259c803475fSMartin KaFai Lau 		inc_errs(INGRESS_ENDHOST);
260c803475fSMartin KaFai Lau 
261c803475fSMartin KaFai Lau 	return TC_ACT_OK;
262c803475fSMartin KaFai Lau }
263c803475fSMartin KaFai Lau 
264c803475fSMartin KaFai Lau /* ingress@veth_(src|dst)_fwd@ns_fwd priority 100 */
265c803475fSMartin KaFai Lau SEC("tc")
ingress_fwdns_prio100(struct __sk_buff * skb)266c803475fSMartin KaFai Lau int ingress_fwdns_prio100(struct __sk_buff *skb)
267c803475fSMartin KaFai Lau {
268c803475fSMartin KaFai Lau 	int skb_type;
269c803475fSMartin KaFai Lau 
270c803475fSMartin KaFai Lau 	skb_type = skb_get_type(skb);
271c803475fSMartin KaFai Lau 	if (skb_type == -1)
272c803475fSMartin KaFai Lau 		return TC_ACT_SHOT;
273c803475fSMartin KaFai Lau 	if (!skb_type)
274c803475fSMartin KaFai Lau 		return TC_ACT_OK;
275c803475fSMartin KaFai Lau 
276c803475fSMartin KaFai Lau 	/* delivery_time is only available to the ingress
2773daf0896SMartin KaFai Lau 	 * if the tc-bpf checks the skb->tstamp_type.
278c803475fSMartin KaFai Lau 	 */
279c803475fSMartin KaFai Lau 	if (skb->tstamp == EGRESS_ENDHOST_MAGIC)
280c803475fSMartin KaFai Lau 		inc_errs(INGRESS_FWDNS_P100);
281c803475fSMartin KaFai Lau 
282c803475fSMartin KaFai Lau 	if (fwdns_clear_dtime())
283c803475fSMartin KaFai Lau 		skb->tstamp = 0;
284c803475fSMartin KaFai Lau 
285c803475fSMartin KaFai Lau 	return TC_ACT_UNSPEC;
286c803475fSMartin KaFai Lau }
287c803475fSMartin KaFai Lau 
288c803475fSMartin KaFai Lau /* egress@veth_(src|dst)_fwd@ns_fwd priority 100 */
289c803475fSMartin KaFai Lau SEC("tc")
egress_fwdns_prio100(struct __sk_buff * skb)290c803475fSMartin KaFai Lau int egress_fwdns_prio100(struct __sk_buff *skb)
291c803475fSMartin KaFai Lau {
292c803475fSMartin KaFai Lau 	int skb_type;
293c803475fSMartin KaFai Lau 
294c803475fSMartin KaFai Lau 	skb_type = skb_get_type(skb);
295c803475fSMartin KaFai Lau 	if (skb_type == -1)
296c803475fSMartin KaFai Lau 		return TC_ACT_SHOT;
297c803475fSMartin KaFai Lau 	if (!skb_type)
298c803475fSMartin KaFai Lau 		return TC_ACT_OK;
299c803475fSMartin KaFai Lau 
300c803475fSMartin KaFai Lau 	/* delivery_time is always available to egress even
3013daf0896SMartin KaFai Lau 	 * the tc-bpf did not use the tstamp_type.
302c803475fSMartin KaFai Lau 	 */
303c803475fSMartin KaFai Lau 	if (skb->tstamp == INGRESS_FWDNS_MAGIC)
304c803475fSMartin KaFai Lau 		inc_dtimes(EGRESS_FWDNS_P100);
305c803475fSMartin KaFai Lau 	else
306c803475fSMartin KaFai Lau 		inc_errs(EGRESS_FWDNS_P100);
307c803475fSMartin KaFai Lau 
308c803475fSMartin KaFai Lau 	if (fwdns_clear_dtime())
309c803475fSMartin KaFai Lau 		skb->tstamp = 0;
310c803475fSMartin KaFai Lau 
311c803475fSMartin KaFai Lau 	return TC_ACT_UNSPEC;
312c803475fSMartin KaFai Lau }
313c803475fSMartin KaFai Lau 
314c803475fSMartin KaFai Lau /* ingress@veth_(src|dst)_fwd@ns_fwd priority 101 */
315c803475fSMartin KaFai Lau SEC("tc")
ingress_fwdns_prio101(struct __sk_buff * skb)316c803475fSMartin KaFai Lau int ingress_fwdns_prio101(struct __sk_buff *skb)
317c803475fSMartin KaFai Lau {
318c803475fSMartin KaFai Lau 	__u64 expected_dtime = EGRESS_ENDHOST_MAGIC;
319c803475fSMartin KaFai Lau 	int skb_type;
320c803475fSMartin KaFai Lau 
321c803475fSMartin KaFai Lau 	skb_type = skb_get_type(skb);
322c803475fSMartin KaFai Lau 	if (skb_type == -1 || !skb_type)
323c803475fSMartin KaFai Lau 		/* Should have handled in prio100 */
324c803475fSMartin KaFai Lau 		return TC_ACT_SHOT;
325c803475fSMartin KaFai Lau 
326c803475fSMartin KaFai Lau 	if (skb_proto(skb_type) == IPPROTO_UDP)
327c803475fSMartin KaFai Lau 		expected_dtime = 0;
328c803475fSMartin KaFai Lau 
3293daf0896SMartin KaFai Lau 	if (skb->tstamp_type) {
330c803475fSMartin KaFai Lau 		if (fwdns_clear_dtime() ||
3313daf0896SMartin KaFai Lau 		    skb->tstamp_type != BPF_SKB_TSTAMP_DELIVERY_MONO ||
332c803475fSMartin KaFai Lau 		    skb->tstamp != expected_dtime)
333c803475fSMartin KaFai Lau 			inc_errs(INGRESS_FWDNS_P101);
334c803475fSMartin KaFai Lau 		else
335c803475fSMartin KaFai Lau 			inc_dtimes(INGRESS_FWDNS_P101);
336c803475fSMartin KaFai Lau 	} else {
337c803475fSMartin KaFai Lau 		if (!fwdns_clear_dtime() && expected_dtime)
338c803475fSMartin KaFai Lau 			inc_errs(INGRESS_FWDNS_P101);
339c803475fSMartin KaFai Lau 	}
340c803475fSMartin KaFai Lau 
3413daf0896SMartin KaFai Lau 	if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO) {
342c803475fSMartin KaFai Lau 		skb->tstamp = INGRESS_FWDNS_MAGIC;
343c803475fSMartin KaFai Lau 	} else {
3443daf0896SMartin KaFai Lau 		if (bpf_skb_set_tstamp(skb, INGRESS_FWDNS_MAGIC,
3453daf0896SMartin KaFai Lau 				       BPF_SKB_TSTAMP_DELIVERY_MONO))
346c803475fSMartin KaFai Lau 			inc_errs(SET_DTIME);
3473daf0896SMartin KaFai Lau 		if (!bpf_skb_set_tstamp(skb, INGRESS_FWDNS_MAGIC,
3483daf0896SMartin KaFai Lau 					BPF_SKB_TSTAMP_UNSPEC))
349c803475fSMartin KaFai Lau 			inc_errs(SET_DTIME);
350c803475fSMartin KaFai Lau 	}
351c803475fSMartin KaFai Lau 
352c803475fSMartin KaFai Lau 	if (skb_ns(skb_type) == SRC_NS)
353c803475fSMartin KaFai Lau 		return bpf_fwd() ?
354c803475fSMartin KaFai Lau 			bpf_redirect_neigh(IFINDEX_DST, NULL, 0, 0) : TC_ACT_OK;
355c803475fSMartin KaFai Lau 	else
356c803475fSMartin KaFai Lau 		return bpf_fwd() ?
357c803475fSMartin KaFai Lau 			bpf_redirect_neigh(IFINDEX_SRC, NULL, 0, 0) : TC_ACT_OK;
358c803475fSMartin KaFai Lau }
359c803475fSMartin KaFai Lau 
360c803475fSMartin KaFai Lau /* egress@veth_(src|dst)_fwd@ns_fwd priority 101 */
361c803475fSMartin KaFai Lau SEC("tc")
egress_fwdns_prio101(struct __sk_buff * skb)362c803475fSMartin KaFai Lau int egress_fwdns_prio101(struct __sk_buff *skb)
363c803475fSMartin KaFai Lau {
364c803475fSMartin KaFai Lau 	int skb_type;
365c803475fSMartin KaFai Lau 
366c803475fSMartin KaFai Lau 	skb_type = skb_get_type(skb);
367c803475fSMartin KaFai Lau 	if (skb_type == -1 || !skb_type)
368c803475fSMartin KaFai Lau 		/* Should have handled in prio100 */
369c803475fSMartin KaFai Lau 		return TC_ACT_SHOT;
370c803475fSMartin KaFai Lau 
3713daf0896SMartin KaFai Lau 	if (skb->tstamp_type) {
372c803475fSMartin KaFai Lau 		if (fwdns_clear_dtime() ||
3733daf0896SMartin KaFai Lau 		    skb->tstamp_type != BPF_SKB_TSTAMP_DELIVERY_MONO ||
374c803475fSMartin KaFai Lau 		    skb->tstamp != INGRESS_FWDNS_MAGIC)
375c803475fSMartin KaFai Lau 			inc_errs(EGRESS_FWDNS_P101);
376c803475fSMartin KaFai Lau 		else
377c803475fSMartin KaFai Lau 			inc_dtimes(EGRESS_FWDNS_P101);
378c803475fSMartin KaFai Lau 	} else {
379c803475fSMartin KaFai Lau 		if (!fwdns_clear_dtime())
380c803475fSMartin KaFai Lau 			inc_errs(EGRESS_FWDNS_P101);
381c803475fSMartin KaFai Lau 	}
382c803475fSMartin KaFai Lau 
3833daf0896SMartin KaFai Lau 	if (skb->tstamp_type == BPF_SKB_TSTAMP_DELIVERY_MONO) {
384c803475fSMartin KaFai Lau 		skb->tstamp = EGRESS_FWDNS_MAGIC;
385c803475fSMartin KaFai Lau 	} else {
3863daf0896SMartin KaFai Lau 		if (bpf_skb_set_tstamp(skb, EGRESS_FWDNS_MAGIC,
3873daf0896SMartin KaFai Lau 				       BPF_SKB_TSTAMP_DELIVERY_MONO))
388c803475fSMartin KaFai Lau 			inc_errs(SET_DTIME);
3893daf0896SMartin KaFai Lau 		if (!bpf_skb_set_tstamp(skb, INGRESS_FWDNS_MAGIC,
3903daf0896SMartin KaFai Lau 					BPF_SKB_TSTAMP_UNSPEC))
391c803475fSMartin KaFai Lau 			inc_errs(SET_DTIME);
392c803475fSMartin KaFai Lau 	}
393c803475fSMartin KaFai Lau 
394c803475fSMartin KaFai Lau 	return TC_ACT_OK;
395c803475fSMartin KaFai Lau }
396c803475fSMartin KaFai Lau 
397c803475fSMartin KaFai Lau char __license[] SEC("license") = "GPL";
398