Linux Audio

Check our new training course

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