1 // SPDX-License-Identifier: GPL-2.0
7 #include <linux/stddef.h>
8 #include <linux/pkt_cls.h>
9 #include <linux/if_ether.h>
12 #include <linux/ipv6.h>
14 #include <bpf/bpf_helpers.h>
15 #include <bpf/bpf_endian.h>
18 # define ctx_ptr(field) (void *)(long)(field)
24 static __always_inline
int fill_fib_params_v4(struct __sk_buff
*skb
,
25 struct bpf_fib_lookup
*fib_params
)
27 void *data_end
= ctx_ptr(skb
->data_end
);
28 void *data
= ctx_ptr(skb
->data
);
31 if (data
+ sizeof(struct ethhdr
) > data_end
)
34 ip4h
= (struct iphdr
*)(data
+ sizeof(struct ethhdr
));
35 if ((void *)(ip4h
+ 1) > data_end
)
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
;
50 static __always_inline
int fill_fib_params_v6(struct __sk_buff
*skb
,
51 struct bpf_fib_lookup
*fib_params
)
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
);
59 if (data
+ sizeof(struct ethhdr
) > data_end
)
62 ip6h
= (struct ipv6hdr
*)(data
+ sizeof(struct ethhdr
));
63 if ((void *)(ip6h
+ 1) > data_end
)
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
);
78 SEC("chk_egress") int tc_chk(struct __sk_buff
*skb
)
80 void *data_end
= ctx_ptr(skb
->data_end
);
81 void *data
= ctx_ptr(skb
->data
);
84 if (data
+ sizeof(struct ethhdr
) > data_end
)
87 return !raw
[0] && !raw
[1] && !raw
[2] ? TC_ACT_SHOT
: TC_ACT_OK
;
90 static __always_inline
int tc_redir(struct __sk_buff
*skb
)
92 struct bpf_fib_lookup fib_params
= { .ifindex
= skb
->ingress_ifindex
};
93 __u8 zero
[ETH_ALEN
* 2];
96 switch (skb
->protocol
) {
97 case __bpf_constant_htons(ETH_P_IP
):
98 ret
= fill_fib_params_v4(skb
, &fib_params
);
100 case __bpf_constant_htons(ETH_P_IPV6
):
101 ret
= fill_fib_params_v6(skb
, &fib_params
);
108 ret
= bpf_fib_lookup(skb
, &fib_params
, sizeof(fib_params
), 0);
109 if (ret
== BPF_FIB_LKUP_RET_NOT_FWDED
|| ret
< 0)
112 __builtin_memset(&zero
, 0, sizeof(zero
));
113 if (bpf_skb_store_bytes(skb
, 0, &zero
, sizeof(zero
), 0) < 0)
116 if (ret
== BPF_FIB_LKUP_RET_NO_NEIGH
) {
117 struct bpf_redir_neigh nh_params
= {};
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
));
123 return bpf_redirect_neigh(fib_params
.ifindex
, &nh_params
,
124 sizeof(nh_params
), 0);
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
);
130 if (eth
+ 1 > data_end
)
133 __builtin_memcpy(eth
->h_dest
, fib_params
.dmac
, ETH_ALEN
);
134 __builtin_memcpy(eth
->h_source
, fib_params
.smac
, ETH_ALEN
);
136 return bpf_redirect(fib_params
.ifindex
, 0);
142 /* these are identical, but keep them separate for compatibility with the
143 * section names expected by test_tc_redirect.sh
145 SEC("dst_ingress") int tc_dst(struct __sk_buff
*skb
)
147 return tc_redir(skb
);
150 SEC("src_ingress") int tc_src(struct __sk_buff
*skb
)
152 return tc_redir(skb
);
155 char __license
[] SEC("license") = "GPL";