1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2020 Facebook |
3 | |
4 | #include <linux/bpf.h> |
5 | #include <bpf/bpf_endian.h> |
6 | #include <bpf/bpf_helpers.h> |
7 | |
8 | #include <linux/if_ether.h> |
9 | #include <linux/in.h> |
10 | #include <linux/in6.h> |
11 | #include <linux/ipv6.h> |
12 | #include <linux/tcp.h> |
13 | |
14 | #include <sys/types.h> |
15 | #include <sys/socket.h> |
16 | |
17 | char _license[] SEC("license" ) = "GPL" ; |
18 | |
19 | __u16 g_serv_port = 0; |
20 | |
21 | static inline void set_ip(__u32 *dst, const struct in6_addr *src) |
22 | { |
23 | dst[0] = src->in6_u.u6_addr32[0]; |
24 | dst[1] = src->in6_u.u6_addr32[1]; |
25 | dst[2] = src->in6_u.u6_addr32[2]; |
26 | dst[3] = src->in6_u.u6_addr32[3]; |
27 | } |
28 | |
29 | static inline void set_tuple(struct bpf_sock_tuple *tuple, |
30 | const struct ipv6hdr *ip6h, |
31 | const struct tcphdr *tcph) |
32 | { |
33 | set_ip(dst: tuple->ipv6.saddr, src: &ip6h->daddr); |
34 | set_ip(dst: tuple->ipv6.daddr, src: &ip6h->saddr); |
35 | tuple->ipv6.sport = tcph->dest; |
36 | tuple->ipv6.dport = tcph->source; |
37 | } |
38 | |
39 | static inline int is_allowed_peer_cg(struct __sk_buff *skb, |
40 | const struct ipv6hdr *ip6h, |
41 | const struct tcphdr *tcph) |
42 | { |
43 | __u64 cgid, acgid, peer_cgid, peer_acgid; |
44 | struct bpf_sock_tuple tuple; |
45 | size_t tuple_len = sizeof(tuple.ipv6); |
46 | struct bpf_sock *peer_sk; |
47 | |
48 | set_tuple(tuple: &tuple, ip6h, tcph); |
49 | |
50 | peer_sk = bpf_sk_lookup_tcp(skb, &tuple, tuple_len, |
51 | BPF_F_CURRENT_NETNS, 0); |
52 | if (!peer_sk) |
53 | return 0; |
54 | |
55 | cgid = bpf_skb_cgroup_id(skb); |
56 | peer_cgid = bpf_sk_cgroup_id(peer_sk); |
57 | |
58 | acgid = bpf_skb_ancestor_cgroup_id(skb, 2); |
59 | peer_acgid = bpf_sk_ancestor_cgroup_id(peer_sk, 2); |
60 | |
61 | bpf_sk_release(peer_sk); |
62 | |
63 | return cgid && cgid == peer_cgid && acgid && acgid == peer_acgid; |
64 | } |
65 | |
66 | SEC("cgroup_skb/ingress" ) |
67 | int ingress_lookup(struct __sk_buff *skb) |
68 | { |
69 | struct ipv6hdr ip6h; |
70 | struct tcphdr tcph; |
71 | |
72 | if (skb->protocol != bpf_htons(ETH_P_IPV6)) |
73 | return 1; |
74 | |
75 | /* For SYN packets coming to listening socket skb->remote_port will be |
76 | * zero, so IPv6/TCP headers are loaded to identify remote peer |
77 | * instead. |
78 | */ |
79 | if (bpf_skb_load_bytes(skb, 0, &ip6h, sizeof(ip6h))) |
80 | return 1; |
81 | |
82 | if (ip6h.nexthdr != IPPROTO_TCP) |
83 | return 1; |
84 | |
85 | if (bpf_skb_load_bytes(skb, sizeof(ip6h), &tcph, sizeof(tcph))) |
86 | return 1; |
87 | |
88 | if (!g_serv_port) |
89 | return 0; |
90 | |
91 | if (tcph.dest != g_serv_port) |
92 | return 1; |
93 | |
94 | return is_allowed_peer_cg(skb, ip6h: &ip6h, tcph: &tcph); |
95 | } |
96 | |