History

Merge branch 'expand-cg_skb-helpers'

Andrey Ignatov says: ==================== v2->v3: - better documentation for bpf_sk_cgroup_id in uapi (Yonghong Song) - save/restore errno in network helpers (Yonghong Song) - cleanup leftover after switching selftest to skeleton (Yonghong Song) - switch from map to skel->bss in selftest (Yonghong Song) v1->v2: - switch selftests to skeleton. This patch set allows a bunch of existing sk lookup and skb cgroup id helpers, and adds two new bpf_sk_{,ancestor_}cgroup_id helpers to be used in cgroup skb programs. It fills the gap to cover a use-case to apply intra-host cgroup-bpf network policy based on a source cgroup a packet comes from. For example, there can be multiple containers A, B, C running on a host. Every such container runs in its own cgroup that can have multiple sub-cgroups. But all these containers can share some IP addresses. At the same time container A wants to have a policy for a server S running in it so that only clients from this same container can connect to S, but not from other containers (such as B, C). Source IP address can't be used to decide whether to allow or deny a packet, but it looks reasonable to filter by cgroup id. The patch set allows to implement the following policy: * when an ingress packet comes to container's cgroup, lookup peer (client) socket this packet comes from; * having peer socket, get its cgroup id; * compare peer cgroup id with self cgroup id and allow packet only if they match, i.e. it comes from same cgroup; * the "sub-cgroup" part of the story can be addressed by getting not direct cgroup id of the peer socket, but ancestor cgroup id on specified level, similar to existing "ancestor" flavors of cgroup id helpers. A newly introduced selftest implements such a policy in its basic form to provide a better idea on the use-case. Patch 1 allows existing sk lookup helpers in cgroup skb. Patch 2 allows skb_ancestor_cgroup_id in cgrou skb. Patch 3 introduces two new helpers to get cgroup id of socket. Patch 4 extends network helpers to use them in the next patch. Patch 5 adds selftest / example of use-case. ====================

Alexei Starovoitov committed 6 years ago

Commit b92d44b2 parents 5b0004d 68e916b7 files · +386 −24Browse files

Unified · Split

7 files
  1. include/uapi/linux/bpf.h +35 −1
  2. net/core/filter.c +61 −9
  3. tools/include/uapi/linux/bpf.h +35 −1
  4. tools/testing/selftests/bpf/network_helpers.c +61 −13
  5. tools/testing/selftests/bpf/network_helpers.h +2 −0
  6. tools/testing/selftests/bpf/prog_tests/cgroup_skb_sk_lookup.c +95 −0
  7. tools/testing/selftests/bpf/progs/cgroup_skb_sk_lookup_kern.c +97 −0
include/uapi/linux/bpf.h+35−1
@@ -3121,6 +3121,38 @@ union bpf_attr { · 3120 unmodified lines
3121 * 0 on success, or a negative error in case of failure:3121 * 0 on success, or a negative error in case of failure:
3122 *3122 *
3123 * **-EOVERFLOW** if an overflow happened: The same object will be tried again.3123 * **-EOVERFLOW** if an overflow happened: The same object will be tried again.
3124 *
3125 * u64 bpf_sk_cgroup_id(struct bpf_sock *sk)
3126 * Description
3127 * Return the cgroup v2 id of the socket *sk*.
3128 *
3129 * *sk* must be a non-**NULL** pointer to a full socket, e.g. one
3130 * returned from **bpf_sk_lookup_xxx**\ (),
3131 * **bpf_sk_fullsock**\ (), etc. The format of returned id is
3132 * same as in **bpf_skb_cgroup_id**\ ().
3133 *
3134 * This helper is available only if the kernel was compiled with
3135 * the **CONFIG_SOCK_CGROUP_DATA** configuration option.
3136 * Return
3137 * The id is returned or 0 in case the id could not be retrieved.
3138 *
3139 * u64 bpf_sk_ancestor_cgroup_id(struct bpf_sock *sk, int ancestor_level)
3140 * Description
3141 * Return id of cgroup v2 that is ancestor of cgroup associated
3142 * with the *sk* at the *ancestor_level*. The root cgroup is at
3143 * *ancestor_level* zero and each step down the hierarchy
3144 * increments the level. If *ancestor_level* == level of cgroup
3145 * associated with *sk*, then return value will be same as that
3146 * of **bpf_sk_cgroup_id**\ ().
3147 *
3148 * The helper is useful to implement policies based on cgroups
3149 * that are upper in hierarchy than immediate cgroup associated
3150 * with *sk*.
3151 *
3152 * The format of returned id and helper limitations are same as in
3153 * **bpf_sk_cgroup_id**\ ().
3154 * Return
3155 * The id is returned or 0 in case the id could not be retrieved.
3124 */3156 */
3125#define __BPF_FUNC_MAPPER(FN) \3157#define __BPF_FUNC_MAPPER(FN) \
3126 FN(unspec), \3158 FN(unspec), \
@@ -3250,7 +3282,9 @@ union bpf_attr { · 123 unmodified lines
3250 FN(sk_assign), \3282 FN(sk_assign), \
3251 FN(ktime_get_boot_ns), \3283 FN(ktime_get_boot_ns), \
3252 FN(seq_printf), \3284 FN(seq_printf), \
3253 FN(seq_write),3285 FN(seq_write), \
3286 FN(sk_cgroup_id), \
3287 FN(sk_ancestor_cgroup_id),
3254 3288
3255/* integer value in 'imm' field of BPF_CALL instruction selects which helper3289/* integer value in 'imm' field of BPF_CALL instruction selects which helper
3256 * function eBPF program intends to call3290 * function eBPF program intends to call
net/core/filter.c+61−9
@@ -4003,16 +4003,22 @@ static const struct bpf_func_proto bpf_skb_under_cgroup_proto = { · 4002 unmodified lines
4003};4003};
4004 4004
4005#ifdef CONFIG_SOCK_CGROUP_DATA4005#ifdef CONFIG_SOCK_CGROUP_DATA
4006static inline u64 __bpf_sk_cgroup_id(struct sock *sk)
4007{
4008 struct cgroup *cgrp;
4009
4010 cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
4011 return cgroup_id(cgrp);
4012}
4013
4006BPF_CALL_1(bpf_skb_cgroup_id, const struct sk_buff *, skb)4014BPF_CALL_1(bpf_skb_cgroup_id, const struct sk_buff *, skb)
4007{4015{
4008 struct sock *sk = skb_to_full_sk(skb);4016 struct sock *sk = skb_to_full_sk(skb);
4009 struct cgroup *cgrp;
4010 4017
4011 if (!sk || !sk_fullsock(sk))4018 if (!sk || !sk_fullsock(sk))
4012 return 0;4019 return 0;
4013 4020
4014 cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);4021 return __bpf_sk_cgroup_id(sk);
4015 return cgroup_id(cgrp);
4016}4022}
4017 4023
4018static const struct bpf_func_proto bpf_skb_cgroup_id_proto = {4024static const struct bpf_func_proto bpf_skb_cgroup_id_proto = {
@@ -4022,16 +4028,12 @@ static const struct bpf_func_proto bpf_skb_cgroup_id_proto = { · 3 unmodified lines
4022 .arg1_type = ARG_PTR_TO_CTX,4028 .arg1_type = ARG_PTR_TO_CTX,
4023};4029};
4024 4030
4025BPF_CALL_2(bpf_skb_ancestor_cgroup_id, const struct sk_buff *, skb, int,4031static inline u64 __bpf_sk_ancestor_cgroup_id(struct sock *sk,
4026 ancestor_level)4032 int ancestor_level)
4027{4033{
4028 struct sock *sk = skb_to_full_sk(skb);
4029 struct cgroup *ancestor;4034 struct cgroup *ancestor;
4030 struct cgroup *cgrp;4035 struct cgroup *cgrp;
4031 4036
4032 if (!sk || !sk_fullsock(sk))
4033 return 0;
4034
4035 cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);4037 cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
4036 ancestor = cgroup_ancestor(cgrp, ancestor_level);4038 ancestor = cgroup_ancestor(cgrp, ancestor_level);
4037 if (!ancestor)4039 if (!ancestor)
@@ -4040,6 +4042,17 @@ BPF_CALL_2(bpf_skb_ancestor_cgroup_id, const struct sk_buff *, skb, int, · 2 unmodified lines
4040 return cgroup_id(ancestor);4042 return cgroup_id(ancestor);
4041}4043}
4042 4044
4045BPF_CALL_2(bpf_skb_ancestor_cgroup_id, const struct sk_buff *, skb, int,
4046 ancestor_level)
4047{
4048 struct sock *sk = skb_to_full_sk(skb);
4049
4050 if (!sk || !sk_fullsock(sk))
4051 return 0;
4052
4053 return __bpf_sk_ancestor_cgroup_id(sk, ancestor_level);
4054}
4055
4043static const struct bpf_func_proto bpf_skb_ancestor_cgroup_id_proto = {4056static const struct bpf_func_proto bpf_skb_ancestor_cgroup_id_proto = {
4044 .func = bpf_skb_ancestor_cgroup_id,4057 .func = bpf_skb_ancestor_cgroup_id,
4045 .gpl_only = false,4058 .gpl_only = false,
@@ -4047,6 +4060,31 @@ static const struct bpf_func_proto bpf_skb_ancestor_cgroup_id_proto = { · 1 unmodified line
4047 .arg1_type = ARG_PTR_TO_CTX,4060 .arg1_type = ARG_PTR_TO_CTX,
4048 .arg2_type = ARG_ANYTHING,4061 .arg2_type = ARG_ANYTHING,
4049};4062};
4063
4064BPF_CALL_1(bpf_sk_cgroup_id, struct sock *, sk)
4065{
4066 return __bpf_sk_cgroup_id(sk);
4067}
4068
4069static const struct bpf_func_proto bpf_sk_cgroup_id_proto = {
4070 .func = bpf_sk_cgroup_id,
4071 .gpl_only = false,
4072 .ret_type = RET_INTEGER,
4073 .arg1_type = ARG_PTR_TO_SOCKET,
4074};
4075
4076BPF_CALL_2(bpf_sk_ancestor_cgroup_id, struct sock *, sk, int, ancestor_level)
4077{
4078 return __bpf_sk_ancestor_cgroup_id(sk, ancestor_level);
4079}
4080
4081static const struct bpf_func_proto bpf_sk_ancestor_cgroup_id_proto = {
4082 .func = bpf_sk_ancestor_cgroup_id,
4083 .gpl_only = false,
4084 .ret_type = RET_INTEGER,
4085 .arg1_type = ARG_PTR_TO_SOCKET,
4086 .arg2_type = ARG_ANYTHING,
4087};
4050#endif4088#endif
4051 4089
4052static unsigned long bpf_xdp_copy(void *dst_buff, const void *src_buff,4090static unsigned long bpf_xdp_copy(void *dst_buff, const void *src_buff,
@@ -6157,8 +6195,22 @@ cg_skb_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 2104 unmodified lines
6157#ifdef CONFIG_SOCK_CGROUP_DATA6195#ifdef CONFIG_SOCK_CGROUP_DATA
6158 case BPF_FUNC_skb_cgroup_id:6196 case BPF_FUNC_skb_cgroup_id:
6159 return &bpf_skb_cgroup_id_proto;6197 return &bpf_skb_cgroup_id_proto;
6198 case BPF_FUNC_skb_ancestor_cgroup_id:
6199 return &bpf_skb_ancestor_cgroup_id_proto;
6200 case BPF_FUNC_sk_cgroup_id:
6201 return &bpf_sk_cgroup_id_proto;
6202 case BPF_FUNC_sk_ancestor_cgroup_id:
6203 return &bpf_sk_ancestor_cgroup_id_proto;
6160#endif6204#endif
6161#ifdef CONFIG_INET6205#ifdef CONFIG_INET
6206 case BPF_FUNC_sk_lookup_tcp:
6207 return &bpf_sk_lookup_tcp_proto;
6208 case BPF_FUNC_sk_lookup_udp:
6209 return &bpf_sk_lookup_udp_proto;
6210 case BPF_FUNC_sk_release:
6211 return &bpf_sk_release_proto;
6212 case BPF_FUNC_skc_lookup_tcp:
6213 return &bpf_skc_lookup_tcp_proto;
6162 case BPF_FUNC_tcp_sock:6214 case BPF_FUNC_tcp_sock:
6163 return &bpf_tcp_sock_proto;6215 return &bpf_tcp_sock_proto;
6164 case BPF_FUNC_get_listener_sock:6216 case BPF_FUNC_get_listener_sock:
tools/include/uapi/linux/bpf.h+35−1
@@ -3121,6 +3121,38 @@ union bpf_attr { · 3120 unmodified lines
3121 * 0 on success, or a negative error in case of failure:3121 * 0 on success, or a negative error in case of failure:
3122 *3122 *
3123 * **-EOVERFLOW** if an overflow happened: The same object will be tried again.3123 * **-EOVERFLOW** if an overflow happened: The same object will be tried again.
3124 *
3125 * u64 bpf_sk_cgroup_id(struct bpf_sock *sk)
3126 * Description
3127 * Return the cgroup v2 id of the socket *sk*.
3128 *
3129 * *sk* must be a non-**NULL** pointer to a full socket, e.g. one
3130 * returned from **bpf_sk_lookup_xxx**\ (),
3131 * **bpf_sk_fullsock**\ (), etc. The format of returned id is
3132 * same as in **bpf_skb_cgroup_id**\ ().
3133 *
3134 * This helper is available only if the kernel was compiled with
3135 * the **CONFIG_SOCK_CGROUP_DATA** configuration option.
3136 * Return
3137 * The id is returned or 0 in case the id could not be retrieved.
3138 *
3139 * u64 bpf_sk_ancestor_cgroup_id(struct bpf_sock *sk, int ancestor_level)
3140 * Description
3141 * Return id of cgroup v2 that is ancestor of cgroup associated
3142 * with the *sk* at the *ancestor_level*. The root cgroup is at
3143 * *ancestor_level* zero and each step down the hierarchy
3144 * increments the level. If *ancestor_level* == level of cgroup
3145 * associated with *sk*, then return value will be same as that
3146 * of **bpf_sk_cgroup_id**\ ().
3147 *
3148 * The helper is useful to implement policies based on cgroups
3149 * that are upper in hierarchy than immediate cgroup associated
3150 * with *sk*.
3151 *
3152 * The format of returned id and helper limitations are same as in
3153 * **bpf_sk_cgroup_id**\ ().
3154 * Return
3155 * The id is returned or 0 in case the id could not be retrieved.
3124 */3156 */
3125#define __BPF_FUNC_MAPPER(FN) \3157#define __BPF_FUNC_MAPPER(FN) \
3126 FN(unspec), \3158 FN(unspec), \
@@ -3250,7 +3282,9 @@ union bpf_attr { · 123 unmodified lines
3250 FN(sk_assign), \3282 FN(sk_assign), \
3251 FN(ktime_get_boot_ns), \3283 FN(ktime_get_boot_ns), \
3252 FN(seq_printf), \3284 FN(seq_printf), \
3253 FN(seq_write),3285 FN(seq_write), \
3286 FN(sk_cgroup_id), \
3287 FN(sk_ancestor_cgroup_id),
3254 3288
3255/* integer value in 'imm' field of BPF_CALL instruction selects which helper3289/* integer value in 'imm' field of BPF_CALL instruction selects which helper
3256 * function eBPF program intends to call3290 * function eBPF program intends to call
tools/testing/selftests/bpf/network_helpers.c+61−13
@@ -4,10 +4,14 @@ · 3 unmodified lines
4#include <stdio.h>4#include <stdio.h>
5#include <string.h>5#include <string.h>
6#include <unistd.h>6#include <unistd.h>
7
8#include <sys/epoll.h>
9
7#include <linux/err.h>10#include <linux/err.h>
8#include <linux/in.h>11#include <linux/in.h>
9#include <linux/in6.h>12#include <linux/in6.h>
10 13
14#include "bpf_util.h"
11#include "network_helpers.h"15#include "network_helpers.h"
12 16
13#define clean_errno() (errno == 0 ? "None" : strerror(errno))17#define clean_errno() (errno == 0 ? "None" : strerror(errno))
@@ -77,9 +81,7 @@ static const size_t timeo_optlen = sizeof(timeo_sec); · 63 unmodified lines
77 81
78int connect_to_fd(int family, int type, int server_fd)82int connect_to_fd(int family, int type, int server_fd)
79{83{
80 struct sockaddr_storage addr;84 int fd, save_errno;
81 socklen_t len = sizeof(addr);
82 int fd;
83 85
84 fd = socket(family, type, 0);86 fd = socket(family, type, 0);
85 if (fd < 0) {87 if (fd < 0) {
@@ -87,24 +89,70 @@ int connect_to_fd(int family, int type, int server_fd) · 1 unmodified line
87 return -1;89 return -1;
88 }90 }
89 91
90 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo_sec, timeo_optlen)) {92 if (connect_fd_to_fd(fd, server_fd) < 0 && errno != EINPROGRESS) {
93 save_errno = errno;
94 close(fd);
95 errno = save_errno;
96 return -1;
97 }
98
99 return fd;
100}
101
102int connect_fd_to_fd(int client_fd, int server_fd)
103{
104 struct sockaddr_storage addr;
105 socklen_t len = sizeof(addr);
106 int save_errno;
107
108 if (setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &timeo_sec,
109 timeo_optlen)) {
91 log_err("Failed to set SO_RCVTIMEO");110 log_err("Failed to set SO_RCVTIMEO");
92 goto out;111 return -1;
93 }112 }
94 113
95 if (getsockname(server_fd, (struct sockaddr *)&addr, &len)) {114 if (getsockname(server_fd, (struct sockaddr *)&addr, &len)) {
96 log_err("Failed to get server addr");115 log_err("Failed to get server addr");
97 goto out;116 return -1;
98 }117 }
99 118
100 if (connect(fd, (const struct sockaddr *)&addr, len) < 0) {119 if (connect(client_fd, (const struct sockaddr *)&addr, len) < 0) {
101 log_err("Fail to connect to server with family %d", family);120 if (errno != EINPROGRESS) {
102 goto out;121 save_errno = errno;
122 log_err("Failed to connect to server");
123 errno = save_errno;
124 }
125 return -1;
103 }126 }
104 127
105 return fd;128 return 0;
129}
130
131int connect_wait(int fd)
132{
133 struct epoll_event ev = {}, events[2];
134 int timeout_ms = 1000;
135 int efd, nfd;
136
137 efd = epoll_create1(EPOLL_CLOEXEC);
138 if (efd < 0) {
139 log_err("Failed to open epoll fd");
140 return -1;
141 }
142
143 ev.events = EPOLLRDHUP | EPOLLOUT;
144 ev.data.fd = fd;
145
146 if (epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev) < 0) {
147 log_err("Failed to register fd=%d on epoll fd=%d", fd, efd);
148 close(efd);
149 return -1;
150 }
151
152 nfd = epoll_wait(efd, events, ARRAY_SIZE(events), timeout_ms);
153 if (nfd < 0)
154 log_err("Failed to wait for I/O event on epoll fd=%d", efd);
106 155
107out:156 close(efd);
108 close(fd);157 return nfd;
109 return -1;
110}158}
tools/testing/selftests/bpf/network_helpers.h+2−0
@@ -35,5 +35,7 @@ extern struct ipv6_packet pkt_v6; · 34 unmodified lines
35 35
36int start_server(int family, int type);36int start_server(int family, int type);
37int connect_to_fd(int family, int type, int server_fd);37int connect_to_fd(int family, int type, int server_fd);
38int connect_fd_to_fd(int client_fd, int server_fd);
39int connect_wait(int client_fd);
38 40
39#endif41#endif
tools/testing/selftests/bpf/prog_tests/cgroup_skb_sk_lookup.c+95−0
@@ -0,0 +1,95 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2020 Facebook
3
4#include <test_progs.h>
5
6#include "network_helpers.h"
7#include "cgroup_skb_sk_lookup_kern.skel.h"
8
9static void run_lookup_test(__u16 *g_serv_port, int out_sk)
10{
11 int serv_sk = -1, in_sk = -1, serv_in_sk = -1, err;
12 struct sockaddr_in6 addr = {};
13 socklen_t addr_len = sizeof(addr);
14 __u32 duration = 0;
15
16 serv_sk = start_server(AF_INET6, SOCK_STREAM);
17 if (CHECK(serv_sk < 0, "start_server", "failed to start server\n"))
18 return;
19
20 err = getsockname(serv_sk, (struct sockaddr *)&addr, &addr_len);
21 if (CHECK(err, "getsockname", "errno %d\n", errno))
22 goto cleanup;
23
24 *g_serv_port = addr.sin6_port;
25
26 /* Client outside of test cgroup should fail to connect by timeout. */
27 err = connect_fd_to_fd(out_sk, serv_sk);
28 if (CHECK(!err || errno != EINPROGRESS, "connect_fd_to_fd",
29 "unexpected result err %d errno %d\n", err, errno))
30 goto cleanup;
31
32 err = connect_wait(out_sk);
33 if (CHECK(err, "connect_wait", "unexpected result %d\n", err))
34 goto cleanup;
35
36 /* Client inside test cgroup should connect just fine. */
37 in_sk = connect_to_fd(AF_INET6, SOCK_STREAM, serv_sk);
38 if (CHECK(in_sk < 0, "connect_to_fd", "errno %d\n", errno))
39 goto cleanup;
40
41 serv_in_sk = accept(serv_sk, NULL, NULL);
42 if (CHECK(serv_in_sk < 0, "accept", "errno %d\n", errno))
43 goto cleanup;
44
45cleanup:
46 close(serv_in_sk);
47 close(in_sk);
48 close(serv_sk);
49}
50
51static void run_cgroup_bpf_test(const char *cg_path, int out_sk)
52{
53 struct cgroup_skb_sk_lookup_kern *skel;
54 struct bpf_link *link;
55 __u32 duration = 0;
56 int cgfd = -1;
57
58 skel = cgroup_skb_sk_lookup_kern__open_and_load();
59 if (CHECK(!skel, "skel_open_load", "open_load failed\n"))
60 return;
61
62 cgfd = test__join_cgroup(cg_path);
63 if (CHECK(cgfd < 0, "cgroup_join", "cgroup setup failed\n"))
64 goto cleanup;
65
66 link = bpf_program__attach_cgroup(skel->progs.ingress_lookup, cgfd);
67 if (CHECK(IS_ERR(link), "cgroup_attach", "err: %ld\n", PTR_ERR(link)))
68 goto cleanup;
69
70 run_lookup_test(&skel->bss->g_serv_port, out_sk);
71
72 bpf_link__destroy(link);
73
74cleanup:
75 close(cgfd);
76 cgroup_skb_sk_lookup_kern__destroy(skel);
77}
78
79void test_cgroup_skb_sk_lookup(void)
80{
81 const char *cg_path = "/foo";
82 int out_sk;
83
84 /* Create a socket before joining testing cgroup so that its cgroup id
85 * differs from that of testing cgroup. Moving selftests process to
86 * testing cgroup won't change cgroup id of an already created socket.
87 */
88 out_sk = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
89 if (CHECK_FAIL(out_sk < 0))
90 return;
91
92 run_cgroup_bpf_test(cg_path, out_sk);
93
94 close(out_sk);
95}
tools/testing/selftests/bpf/progs/cgroup_skb_sk_lookup_kern.c+97−0
@@ -0,0 +1,97 @@
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
17int _version SEC("version") = 1;
18char _license[] SEC("license") = "GPL";
19
20__u16 g_serv_port = 0;
21
22static inline void set_ip(__u32 *dst, const struct in6_addr *src)
23{
24 dst[0] = src->in6_u.u6_addr32[0];
25 dst[1] = src->in6_u.u6_addr32[1];
26 dst[2] = src->in6_u.u6_addr32[2];
27 dst[3] = src->in6_u.u6_addr32[3];
28}
29
30static inline void set_tuple(struct bpf_sock_tuple *tuple,
31 const struct ipv6hdr *ip6h,
32 const struct tcphdr *tcph)
33{
34 set_ip(tuple->ipv6.saddr, &ip6h->daddr);
35 set_ip(tuple->ipv6.daddr, &ip6h->saddr);
36 tuple->ipv6.sport = tcph->dest;
37 tuple->ipv6.dport = tcph->source;
38}
39
40static inline int is_allowed_peer_cg(struct __sk_buff *skb,
41 const struct ipv6hdr *ip6h,
42 const struct tcphdr *tcph)
43{
44 __u64 cgid, acgid, peer_cgid, peer_acgid;
45 struct bpf_sock_tuple tuple;
46 size_t tuple_len = sizeof(tuple.ipv6);
47 struct bpf_sock *peer_sk;
48
49 set_tuple(&tuple, ip6h, tcph);
50
51 peer_sk = bpf_sk_lookup_tcp(skb, &tuple, tuple_len,
52 BPF_F_CURRENT_NETNS, 0);
53 if (!peer_sk)
54 return 0;
55
56 cgid = bpf_skb_cgroup_id(skb);
57 peer_cgid = bpf_sk_cgroup_id(peer_sk);
58
59 acgid = bpf_skb_ancestor_cgroup_id(skb, 2);
60 peer_acgid = bpf_sk_ancestor_cgroup_id(peer_sk, 2);
61
62 bpf_sk_release(peer_sk);
63
64 return cgid && cgid == peer_cgid && acgid && acgid == peer_acgid;
65}
66
67SEC("cgroup_skb/ingress")
68int ingress_lookup(struct __sk_buff *skb)
69{
70 __u32 serv_port_key = 0;
71 struct ipv6hdr ip6h;
72 struct tcphdr tcph;
73
74 if (skb->protocol != bpf_htons(ETH_P_IPV6))
75 return 1;
76
77 /* For SYN packets coming to listening socket skb->remote_port will be
78 * zero, so IPv6/TCP headers are loaded to identify remote peer
79 * instead.
80 */
81 if (bpf_skb_load_bytes(skb, 0, &ip6h, sizeof(ip6h)))
82 return 1;
83
84 if (ip6h.nexthdr != IPPROTO_TCP)
85 return 1;
86
87 if (bpf_skb_load_bytes(skb, sizeof(ip6h), &tcph, sizeof(tcph)))
88 return 1;
89
90 if (!g_serv_port)
91 return 0;
92
93 if (tcph.dest != g_serv_port)
94 return 1;
95
96 return is_allowed_peer_cg(skb, &ip6h, &tcph);
97}