1adfd272cSToke Høiland-Jørgensen // SPDX-License-Identifier: GPL-2.0
2adfd272cSToke Høiland-Jørgensen #include <stdint.h>
3adfd272cSToke Høiland-Jørgensen #include <stdbool.h>
4adfd272cSToke Høiland-Jørgensen #include <stddef.h>
5adfd272cSToke Høiland-Jørgensen 
6adfd272cSToke Høiland-Jørgensen #include <linux/bpf.h>
7adfd272cSToke Høiland-Jørgensen #include <linux/stddef.h>
8adfd272cSToke Høiland-Jørgensen #include <linux/pkt_cls.h>
9adfd272cSToke Høiland-Jørgensen #include <linux/if_ether.h>
10adfd272cSToke Høiland-Jørgensen #include <linux/in.h>
11adfd272cSToke Høiland-Jørgensen #include <linux/ip.h>
12adfd272cSToke Høiland-Jørgensen #include <linux/ipv6.h>
13adfd272cSToke Høiland-Jørgensen 
14adfd272cSToke Høiland-Jørgensen #include <bpf/bpf_helpers.h>
15adfd272cSToke Høiland-Jørgensen #include <bpf/bpf_endian.h>
16adfd272cSToke Høiland-Jørgensen 
17adfd272cSToke Høiland-Jørgensen #ifndef ctx_ptr
18adfd272cSToke Høiland-Jørgensen # define ctx_ptr(field)		(void *)(long)(field)
19adfd272cSToke Høiland-Jørgensen #endif
20adfd272cSToke Høiland-Jørgensen 
21adfd272cSToke Høiland-Jørgensen #define AF_INET 2
22adfd272cSToke Høiland-Jørgensen #define AF_INET6 10
23adfd272cSToke Høiland-Jørgensen 
fill_fib_params_v4(struct __sk_buff * skb,struct bpf_fib_lookup * fib_params)24adfd272cSToke Høiland-Jørgensen static __always_inline int fill_fib_params_v4(struct __sk_buff *skb,
25adfd272cSToke Høiland-Jørgensen 					      struct bpf_fib_lookup *fib_params)
26adfd272cSToke Høiland-Jørgensen {
27adfd272cSToke Høiland-Jørgensen 	void *data_end = ctx_ptr(skb->data_end);
28adfd272cSToke Høiland-Jørgensen 	void *data = ctx_ptr(skb->data);
29adfd272cSToke Høiland-Jørgensen 	struct iphdr *ip4h;
30adfd272cSToke Høiland-Jørgensen 
31adfd272cSToke Høiland-Jørgensen 	if (data + sizeof(struct ethhdr) > data_end)
32adfd272cSToke Høiland-Jørgensen 		return -1;
33adfd272cSToke Høiland-Jørgensen 
34adfd272cSToke Høiland-Jørgensen 	ip4h = (struct iphdr *)(data + sizeof(struct ethhdr));
35adfd272cSToke Høiland-Jørgensen 	if ((void *)(ip4h + 1) > data_end)
36adfd272cSToke Høiland-Jørgensen 		return -1;
37adfd272cSToke Høiland-Jørgensen 
38adfd272cSToke Høiland-Jørgensen 	fib_params->family = AF_INET;
39adfd272cSToke Høiland-Jørgensen 	fib_params->tos = ip4h->tos;
40adfd272cSToke Høiland-Jørgensen 	fib_params->l4_protocol = ip4h->protocol;
41adfd272cSToke Høiland-Jørgensen 	fib_params->sport = 0;
42adfd272cSToke Høiland-Jørgensen 	fib_params->dport = 0;
43adfd272cSToke Høiland-Jørgensen 	fib_params->tot_len = bpf_ntohs(ip4h->tot_len);
44adfd272cSToke Høiland-Jørgensen 	fib_params->ipv4_src = ip4h->saddr;
45adfd272cSToke Høiland-Jørgensen 	fib_params->ipv4_dst = ip4h->daddr;
46adfd272cSToke Høiland-Jørgensen 
47adfd272cSToke Høiland-Jørgensen 	return 0;
48adfd272cSToke Høiland-Jørgensen }
49adfd272cSToke Høiland-Jørgensen 
fill_fib_params_v6(struct __sk_buff * skb,struct bpf_fib_lookup * fib_params)50adfd272cSToke Høiland-Jørgensen static __always_inline int fill_fib_params_v6(struct __sk_buff *skb,
51adfd272cSToke Høiland-Jørgensen 					      struct bpf_fib_lookup *fib_params)
52adfd272cSToke Høiland-Jørgensen {
53adfd272cSToke Høiland-Jørgensen 	struct in6_addr *src = (struct in6_addr *)fib_params->ipv6_src;
54adfd272cSToke Høiland-Jørgensen 	struct in6_addr *dst = (struct in6_addr *)fib_params->ipv6_dst;
55adfd272cSToke Høiland-Jørgensen 	void *data_end = ctx_ptr(skb->data_end);
56adfd272cSToke Høiland-Jørgensen 	void *data = ctx_ptr(skb->data);
57adfd272cSToke Høiland-Jørgensen 	struct ipv6hdr *ip6h;
58adfd272cSToke Høiland-Jørgensen 
59adfd272cSToke Høiland-Jørgensen 	if (data + sizeof(struct ethhdr) > data_end)
60adfd272cSToke Høiland-Jørgensen 		return -1;
61adfd272cSToke Høiland-Jørgensen 
62adfd272cSToke Høiland-Jørgensen 	ip6h = (struct ipv6hdr *)(data + sizeof(struct ethhdr));
63adfd272cSToke Høiland-Jørgensen 	if ((void *)(ip6h + 1) > data_end)
64adfd272cSToke Høiland-Jørgensen 		return -1;
65adfd272cSToke Høiland-Jørgensen 
66adfd272cSToke Høiland-Jørgensen 	fib_params->family = AF_INET6;
67adfd272cSToke Høiland-Jørgensen 	fib_params->flowinfo = 0;
68adfd272cSToke Høiland-Jørgensen 	fib_params->l4_protocol = ip6h->nexthdr;
69adfd272cSToke Høiland-Jørgensen 	fib_params->sport = 0;
70adfd272cSToke Høiland-Jørgensen 	fib_params->dport = 0;
71adfd272cSToke Høiland-Jørgensen 	fib_params->tot_len = bpf_ntohs(ip6h->payload_len);
72adfd272cSToke Høiland-Jørgensen 	*src = ip6h->saddr;
73adfd272cSToke Høiland-Jørgensen 	*dst = ip6h->daddr;
74adfd272cSToke Høiland-Jørgensen 
75adfd272cSToke Høiland-Jørgensen 	return 0;
76adfd272cSToke Høiland-Jørgensen }
77adfd272cSToke Høiland-Jørgensen 
78*c22bdd28SAndrii Nakryiko SEC("tc")
tc_chk(struct __sk_buff * skb)79096eccdeSJussi Maki int tc_chk(struct __sk_buff *skb)
80adfd272cSToke Høiland-Jørgensen {
81adfd272cSToke Høiland-Jørgensen 	void *data_end = ctx_ptr(skb->data_end);
82adfd272cSToke Høiland-Jørgensen 	void *data = ctx_ptr(skb->data);
83adfd272cSToke Høiland-Jørgensen 	__u32 *raw = data;
84adfd272cSToke Høiland-Jørgensen 
85adfd272cSToke Høiland-Jørgensen 	if (data + sizeof(struct ethhdr) > data_end)
86adfd272cSToke Høiland-Jørgensen 		return TC_ACT_SHOT;
87adfd272cSToke Høiland-Jørgensen 
88adfd272cSToke Høiland-Jørgensen 	return !raw[0] && !raw[1] && !raw[2] ? TC_ACT_SHOT : TC_ACT_OK;
89adfd272cSToke Høiland-Jørgensen }
90adfd272cSToke Høiland-Jørgensen 
tc_redir(struct __sk_buff * skb)91adfd272cSToke Høiland-Jørgensen static __always_inline int tc_redir(struct __sk_buff *skb)
92adfd272cSToke Høiland-Jørgensen {
93adfd272cSToke Høiland-Jørgensen 	struct bpf_fib_lookup fib_params = { .ifindex = skb->ingress_ifindex };
94adfd272cSToke Høiland-Jørgensen 	__u8 zero[ETH_ALEN * 2];
95adfd272cSToke Høiland-Jørgensen 	int ret = -1;
96adfd272cSToke Høiland-Jørgensen 
97adfd272cSToke Høiland-Jørgensen 	switch (skb->protocol) {
98adfd272cSToke Høiland-Jørgensen 	case __bpf_constant_htons(ETH_P_IP):
99adfd272cSToke Høiland-Jørgensen 		ret = fill_fib_params_v4(skb, &fib_params);
100adfd272cSToke Høiland-Jørgensen 		break;
101adfd272cSToke Høiland-Jørgensen 	case __bpf_constant_htons(ETH_P_IPV6):
102adfd272cSToke Høiland-Jørgensen 		ret = fill_fib_params_v6(skb, &fib_params);
103adfd272cSToke Høiland-Jørgensen 		break;
104adfd272cSToke Høiland-Jørgensen 	}
105adfd272cSToke Høiland-Jørgensen 
106adfd272cSToke Høiland-Jørgensen 	if (ret)
107adfd272cSToke Høiland-Jørgensen 		return TC_ACT_OK;
108adfd272cSToke Høiland-Jørgensen 
109adfd272cSToke Høiland-Jørgensen 	ret = bpf_fib_lookup(skb, &fib_params, sizeof(fib_params), 0);
110adfd272cSToke Høiland-Jørgensen 	if (ret == BPF_FIB_LKUP_RET_NOT_FWDED || ret < 0)
111adfd272cSToke Høiland-Jørgensen 		return TC_ACT_OK;
112adfd272cSToke Høiland-Jørgensen 
113adfd272cSToke Høiland-Jørgensen 	__builtin_memset(&zero, 0, sizeof(zero));
114adfd272cSToke Høiland-Jørgensen 	if (bpf_skb_store_bytes(skb, 0, &zero, sizeof(zero), 0) < 0)
115adfd272cSToke Høiland-Jørgensen 		return TC_ACT_SHOT;
116adfd272cSToke Høiland-Jørgensen 
117adfd272cSToke Høiland-Jørgensen 	if (ret == BPF_FIB_LKUP_RET_NO_NEIGH) {
118adfd272cSToke Høiland-Jørgensen 		struct bpf_redir_neigh nh_params = {};
119adfd272cSToke Høiland-Jørgensen 
120adfd272cSToke Høiland-Jørgensen 		nh_params.nh_family = fib_params.family;
121adfd272cSToke Høiland-Jørgensen 		__builtin_memcpy(&nh_params.ipv6_nh, &fib_params.ipv6_dst,
122adfd272cSToke Høiland-Jørgensen 				 sizeof(nh_params.ipv6_nh));
123adfd272cSToke Høiland-Jørgensen 
124adfd272cSToke Høiland-Jørgensen 		return bpf_redirect_neigh(fib_params.ifindex, &nh_params,
125adfd272cSToke Høiland-Jørgensen 					  sizeof(nh_params), 0);
126adfd272cSToke Høiland-Jørgensen 
127adfd272cSToke Høiland-Jørgensen 	} else if (ret == BPF_FIB_LKUP_RET_SUCCESS) {
128adfd272cSToke Høiland-Jørgensen 		void *data_end = ctx_ptr(skb->data_end);
129adfd272cSToke Høiland-Jørgensen 		struct ethhdr *eth = ctx_ptr(skb->data);
130adfd272cSToke Høiland-Jørgensen 
131adfd272cSToke Høiland-Jørgensen 		if (eth + 1 > data_end)
132adfd272cSToke Høiland-Jørgensen 			return TC_ACT_SHOT;
133adfd272cSToke Høiland-Jørgensen 
134adfd272cSToke Høiland-Jørgensen 		__builtin_memcpy(eth->h_dest, fib_params.dmac, ETH_ALEN);
135adfd272cSToke Høiland-Jørgensen 		__builtin_memcpy(eth->h_source, fib_params.smac, ETH_ALEN);
136adfd272cSToke Høiland-Jørgensen 
137adfd272cSToke Høiland-Jørgensen 		return bpf_redirect(fib_params.ifindex, 0);
138adfd272cSToke Høiland-Jørgensen 	}
139adfd272cSToke Høiland-Jørgensen 
140adfd272cSToke Høiland-Jørgensen 	return TC_ACT_SHOT;
141adfd272cSToke Høiland-Jørgensen }
142adfd272cSToke Høiland-Jørgensen 
143adfd272cSToke Høiland-Jørgensen /* these are identical, but keep them separate for compatibility with the
144adfd272cSToke Høiland-Jørgensen  * section names expected by test_tc_redirect.sh
145adfd272cSToke Høiland-Jørgensen  */
146*c22bdd28SAndrii Nakryiko SEC("tc")
tc_dst(struct __sk_buff * skb)147096eccdeSJussi Maki int tc_dst(struct __sk_buff *skb)
148adfd272cSToke Høiland-Jørgensen {
149adfd272cSToke Høiland-Jørgensen 	return tc_redir(skb);
150adfd272cSToke Høiland-Jørgensen }
151adfd272cSToke Høiland-Jørgensen 
152*c22bdd28SAndrii Nakryiko SEC("tc")
tc_src(struct __sk_buff * skb)153096eccdeSJussi Maki int tc_src(struct __sk_buff *skb)
154adfd272cSToke Høiland-Jørgensen {
155adfd272cSToke Høiland-Jørgensen 	return tc_redir(skb);
156adfd272cSToke Høiland-Jørgensen }
157adfd272cSToke Høiland-Jørgensen 
158adfd272cSToke Høiland-Jørgensen char __license[] SEC("license") = "GPL";
159