168e916bcSAndrey Ignatov // SPDX-License-Identifier: GPL-2.0
268e916bcSAndrey Ignatov // Copyright (c) 2020 Facebook
368e916bcSAndrey Ignatov 
468e916bcSAndrey Ignatov #include <linux/bpf.h>
568e916bcSAndrey Ignatov #include <bpf/bpf_endian.h>
668e916bcSAndrey Ignatov #include <bpf/bpf_helpers.h>
768e916bcSAndrey Ignatov 
868e916bcSAndrey Ignatov #include <linux/if_ether.h>
968e916bcSAndrey Ignatov #include <linux/in.h>
1068e916bcSAndrey Ignatov #include <linux/in6.h>
1168e916bcSAndrey Ignatov #include <linux/ipv6.h>
1268e916bcSAndrey Ignatov #include <linux/tcp.h>
1368e916bcSAndrey Ignatov 
1468e916bcSAndrey Ignatov #include <sys/types.h>
1568e916bcSAndrey Ignatov #include <sys/socket.h>
1668e916bcSAndrey Ignatov 
1768e916bcSAndrey Ignatov char _license[] SEC("license") = "GPL";
1868e916bcSAndrey Ignatov 
1968e916bcSAndrey Ignatov __u16 g_serv_port = 0;
2068e916bcSAndrey Ignatov 
set_ip(__u32 * dst,const struct in6_addr * src)2168e916bcSAndrey Ignatov static inline void set_ip(__u32 *dst, const struct in6_addr *src)
2268e916bcSAndrey Ignatov {
2368e916bcSAndrey Ignatov 	dst[0] = src->in6_u.u6_addr32[0];
2468e916bcSAndrey Ignatov 	dst[1] = src->in6_u.u6_addr32[1];
2568e916bcSAndrey Ignatov 	dst[2] = src->in6_u.u6_addr32[2];
2668e916bcSAndrey Ignatov 	dst[3] = src->in6_u.u6_addr32[3];
2768e916bcSAndrey Ignatov }
2868e916bcSAndrey Ignatov 
set_tuple(struct bpf_sock_tuple * tuple,const struct ipv6hdr * ip6h,const struct tcphdr * tcph)2968e916bcSAndrey Ignatov static inline void set_tuple(struct bpf_sock_tuple *tuple,
3068e916bcSAndrey Ignatov 			     const struct ipv6hdr *ip6h,
3168e916bcSAndrey Ignatov 			     const struct tcphdr *tcph)
3268e916bcSAndrey Ignatov {
3368e916bcSAndrey Ignatov 	set_ip(tuple->ipv6.saddr, &ip6h->daddr);
3468e916bcSAndrey Ignatov 	set_ip(tuple->ipv6.daddr, &ip6h->saddr);
3568e916bcSAndrey Ignatov 	tuple->ipv6.sport = tcph->dest;
3668e916bcSAndrey Ignatov 	tuple->ipv6.dport = tcph->source;
3768e916bcSAndrey Ignatov }
3868e916bcSAndrey Ignatov 
is_allowed_peer_cg(struct __sk_buff * skb,const struct ipv6hdr * ip6h,const struct tcphdr * tcph)3968e916bcSAndrey Ignatov static inline int is_allowed_peer_cg(struct __sk_buff *skb,
4068e916bcSAndrey Ignatov 				     const struct ipv6hdr *ip6h,
4168e916bcSAndrey Ignatov 				     const struct tcphdr *tcph)
4268e916bcSAndrey Ignatov {
4368e916bcSAndrey Ignatov 	__u64 cgid, acgid, peer_cgid, peer_acgid;
4468e916bcSAndrey Ignatov 	struct bpf_sock_tuple tuple;
4568e916bcSAndrey Ignatov 	size_t tuple_len = sizeof(tuple.ipv6);
4668e916bcSAndrey Ignatov 	struct bpf_sock *peer_sk;
4768e916bcSAndrey Ignatov 
4868e916bcSAndrey Ignatov 	set_tuple(&tuple, ip6h, tcph);
4968e916bcSAndrey Ignatov 
5068e916bcSAndrey Ignatov 	peer_sk = bpf_sk_lookup_tcp(skb, &tuple, tuple_len,
5168e916bcSAndrey Ignatov 				    BPF_F_CURRENT_NETNS, 0);
5268e916bcSAndrey Ignatov 	if (!peer_sk)
5368e916bcSAndrey Ignatov 		return 0;
5468e916bcSAndrey Ignatov 
5568e916bcSAndrey Ignatov 	cgid = bpf_skb_cgroup_id(skb);
5668e916bcSAndrey Ignatov 	peer_cgid = bpf_sk_cgroup_id(peer_sk);
5768e916bcSAndrey Ignatov 
5868e916bcSAndrey Ignatov 	acgid = bpf_skb_ancestor_cgroup_id(skb, 2);
5968e916bcSAndrey Ignatov 	peer_acgid = bpf_sk_ancestor_cgroup_id(peer_sk, 2);
6068e916bcSAndrey Ignatov 
6168e916bcSAndrey Ignatov 	bpf_sk_release(peer_sk);
6268e916bcSAndrey Ignatov 
6368e916bcSAndrey Ignatov 	return cgid && cgid == peer_cgid && acgid && acgid == peer_acgid;
6468e916bcSAndrey Ignatov }
6568e916bcSAndrey Ignatov 
6668e916bcSAndrey Ignatov SEC("cgroup_skb/ingress")
ingress_lookup(struct __sk_buff * skb)6768e916bcSAndrey Ignatov int ingress_lookup(struct __sk_buff *skb)
6868e916bcSAndrey Ignatov {
6968e916bcSAndrey Ignatov 	struct ipv6hdr ip6h;
7068e916bcSAndrey Ignatov 	struct tcphdr tcph;
7168e916bcSAndrey Ignatov 
7268e916bcSAndrey Ignatov 	if (skb->protocol != bpf_htons(ETH_P_IPV6))
7368e916bcSAndrey Ignatov 		return 1;
7468e916bcSAndrey Ignatov 
7568e916bcSAndrey Ignatov 	/* For SYN packets coming to listening socket skb->remote_port will be
7668e916bcSAndrey Ignatov 	 * zero, so IPv6/TCP headers are loaded to identify remote peer
7768e916bcSAndrey Ignatov 	 * instead.
7868e916bcSAndrey Ignatov 	 */
7968e916bcSAndrey Ignatov 	if (bpf_skb_load_bytes(skb, 0, &ip6h, sizeof(ip6h)))
8068e916bcSAndrey Ignatov 		return 1;
8168e916bcSAndrey Ignatov 
8268e916bcSAndrey Ignatov 	if (ip6h.nexthdr != IPPROTO_TCP)
8368e916bcSAndrey Ignatov 		return 1;
8468e916bcSAndrey Ignatov 
8568e916bcSAndrey Ignatov 	if (bpf_skb_load_bytes(skb, sizeof(ip6h), &tcph, sizeof(tcph)))
8668e916bcSAndrey Ignatov 		return 1;
8768e916bcSAndrey Ignatov 
8868e916bcSAndrey Ignatov 	if (!g_serv_port)
8968e916bcSAndrey Ignatov 		return 0;
9068e916bcSAndrey Ignatov 
9168e916bcSAndrey Ignatov 	if (tcph.dest != g_serv_port)
9268e916bcSAndrey Ignatov 		return 1;
9368e916bcSAndrey Ignatov 
9468e916bcSAndrey Ignatov 	return is_allowed_peer_cg(skb, &ip6h, &tcph);
9568e916bcSAndrey Ignatov }
96