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