162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (c) 2018 Facebook
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <stdio.h>
562306a36Sopenharmony_ci#include <unistd.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <arpa/inet.h>
862306a36Sopenharmony_ci#include <sys/types.h>
962306a36Sopenharmony_ci#include <sys/socket.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/filter.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <bpf/bpf.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "cgroup_helpers.h"
1662306a36Sopenharmony_ci#include <bpf/bpf_endian.h>
1762306a36Sopenharmony_ci#include "bpf_util.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define CG_PATH		"/foo"
2062306a36Sopenharmony_ci#define MAX_INSNS	512
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cichar bpf_log_buf[BPF_LOG_BUF_SIZE];
2362306a36Sopenharmony_cistatic bool verbose = false;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct sock_test {
2662306a36Sopenharmony_ci	const char *descr;
2762306a36Sopenharmony_ci	/* BPF prog properties */
2862306a36Sopenharmony_ci	struct bpf_insn	insns[MAX_INSNS];
2962306a36Sopenharmony_ci	enum bpf_attach_type expected_attach_type;
3062306a36Sopenharmony_ci	enum bpf_attach_type attach_type;
3162306a36Sopenharmony_ci	/* Socket properties */
3262306a36Sopenharmony_ci	int domain;
3362306a36Sopenharmony_ci	int type;
3462306a36Sopenharmony_ci	/* Endpoint to bind() to */
3562306a36Sopenharmony_ci	const char *ip;
3662306a36Sopenharmony_ci	unsigned short port;
3762306a36Sopenharmony_ci	unsigned short port_retry;
3862306a36Sopenharmony_ci	/* Expected test result */
3962306a36Sopenharmony_ci	enum {
4062306a36Sopenharmony_ci		LOAD_REJECT,
4162306a36Sopenharmony_ci		ATTACH_REJECT,
4262306a36Sopenharmony_ci		BIND_REJECT,
4362306a36Sopenharmony_ci		SUCCESS,
4462306a36Sopenharmony_ci		RETRY_SUCCESS,
4562306a36Sopenharmony_ci		RETRY_REJECT
4662306a36Sopenharmony_ci	} result;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic struct sock_test tests[] = {
5062306a36Sopenharmony_ci	{
5162306a36Sopenharmony_ci		.descr = "bind4 load with invalid access: src_ip6",
5262306a36Sopenharmony_ci		.insns = {
5362306a36Sopenharmony_ci			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
5462306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
5562306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_ip6[0])),
5662306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
5762306a36Sopenharmony_ci			BPF_EXIT_INSN(),
5862306a36Sopenharmony_ci		},
5962306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
6062306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET4_POST_BIND,
6162306a36Sopenharmony_ci		.result = LOAD_REJECT,
6262306a36Sopenharmony_ci	},
6362306a36Sopenharmony_ci	{
6462306a36Sopenharmony_ci		.descr = "bind4 load with invalid access: mark",
6562306a36Sopenharmony_ci		.insns = {
6662306a36Sopenharmony_ci			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
6762306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
6862306a36Sopenharmony_ci				    offsetof(struct bpf_sock, mark)),
6962306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
7062306a36Sopenharmony_ci			BPF_EXIT_INSN(),
7162306a36Sopenharmony_ci		},
7262306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
7362306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET4_POST_BIND,
7462306a36Sopenharmony_ci		.result = LOAD_REJECT,
7562306a36Sopenharmony_ci	},
7662306a36Sopenharmony_ci	{
7762306a36Sopenharmony_ci		.descr = "bind6 load with invalid access: src_ip4",
7862306a36Sopenharmony_ci		.insns = {
7962306a36Sopenharmony_ci			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
8062306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
8162306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_ip4)),
8262306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
8362306a36Sopenharmony_ci			BPF_EXIT_INSN(),
8462306a36Sopenharmony_ci		},
8562306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
8662306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET6_POST_BIND,
8762306a36Sopenharmony_ci		.result = LOAD_REJECT,
8862306a36Sopenharmony_ci	},
8962306a36Sopenharmony_ci	{
9062306a36Sopenharmony_ci		.descr = "sock_create load with invalid access: src_port",
9162306a36Sopenharmony_ci		.insns = {
9262306a36Sopenharmony_ci			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
9362306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
9462306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_port)),
9562306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
9662306a36Sopenharmony_ci			BPF_EXIT_INSN(),
9762306a36Sopenharmony_ci		},
9862306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
9962306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
10062306a36Sopenharmony_ci		.result = LOAD_REJECT,
10162306a36Sopenharmony_ci	},
10262306a36Sopenharmony_ci	{
10362306a36Sopenharmony_ci		.descr = "sock_create load w/o expected_attach_type (compat mode)",
10462306a36Sopenharmony_ci		.insns = {
10562306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
10662306a36Sopenharmony_ci			BPF_EXIT_INSN(),
10762306a36Sopenharmony_ci		},
10862306a36Sopenharmony_ci		.expected_attach_type = 0,
10962306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
11062306a36Sopenharmony_ci		.domain = AF_INET,
11162306a36Sopenharmony_ci		.type = SOCK_STREAM,
11262306a36Sopenharmony_ci		.ip = "127.0.0.1",
11362306a36Sopenharmony_ci		.port = 8097,
11462306a36Sopenharmony_ci		.result = SUCCESS,
11562306a36Sopenharmony_ci	},
11662306a36Sopenharmony_ci	{
11762306a36Sopenharmony_ci		.descr = "sock_create load w/ expected_attach_type",
11862306a36Sopenharmony_ci		.insns = {
11962306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
12062306a36Sopenharmony_ci			BPF_EXIT_INSN(),
12162306a36Sopenharmony_ci		},
12262306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
12362306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
12462306a36Sopenharmony_ci		.domain = AF_INET,
12562306a36Sopenharmony_ci		.type = SOCK_STREAM,
12662306a36Sopenharmony_ci		.ip = "127.0.0.1",
12762306a36Sopenharmony_ci		.port = 8097,
12862306a36Sopenharmony_ci		.result = SUCCESS,
12962306a36Sopenharmony_ci	},
13062306a36Sopenharmony_ci	{
13162306a36Sopenharmony_ci		.descr = "attach type mismatch bind4 vs bind6",
13262306a36Sopenharmony_ci		.insns = {
13362306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
13462306a36Sopenharmony_ci			BPF_EXIT_INSN(),
13562306a36Sopenharmony_ci		},
13662306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
13762306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET6_POST_BIND,
13862306a36Sopenharmony_ci		.result = ATTACH_REJECT,
13962306a36Sopenharmony_ci	},
14062306a36Sopenharmony_ci	{
14162306a36Sopenharmony_ci		.descr = "attach type mismatch bind6 vs bind4",
14262306a36Sopenharmony_ci		.insns = {
14362306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
14462306a36Sopenharmony_ci			BPF_EXIT_INSN(),
14562306a36Sopenharmony_ci		},
14662306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
14762306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET4_POST_BIND,
14862306a36Sopenharmony_ci		.result = ATTACH_REJECT,
14962306a36Sopenharmony_ci	},
15062306a36Sopenharmony_ci	{
15162306a36Sopenharmony_ci		.descr = "attach type mismatch default vs bind4",
15262306a36Sopenharmony_ci		.insns = {
15362306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
15462306a36Sopenharmony_ci			BPF_EXIT_INSN(),
15562306a36Sopenharmony_ci		},
15662306a36Sopenharmony_ci		.expected_attach_type = 0,
15762306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET4_POST_BIND,
15862306a36Sopenharmony_ci		.result = ATTACH_REJECT,
15962306a36Sopenharmony_ci	},
16062306a36Sopenharmony_ci	{
16162306a36Sopenharmony_ci		.descr = "attach type mismatch bind6 vs sock_create",
16262306a36Sopenharmony_ci		.insns = {
16362306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
16462306a36Sopenharmony_ci			BPF_EXIT_INSN(),
16562306a36Sopenharmony_ci		},
16662306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
16762306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,
16862306a36Sopenharmony_ci		.result = ATTACH_REJECT,
16962306a36Sopenharmony_ci	},
17062306a36Sopenharmony_ci	{
17162306a36Sopenharmony_ci		.descr = "bind4 reject all",
17262306a36Sopenharmony_ci		.insns = {
17362306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 0),
17462306a36Sopenharmony_ci			BPF_EXIT_INSN(),
17562306a36Sopenharmony_ci		},
17662306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
17762306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET4_POST_BIND,
17862306a36Sopenharmony_ci		.domain = AF_INET,
17962306a36Sopenharmony_ci		.type = SOCK_STREAM,
18062306a36Sopenharmony_ci		.ip = "0.0.0.0",
18162306a36Sopenharmony_ci		.result = BIND_REJECT,
18262306a36Sopenharmony_ci	},
18362306a36Sopenharmony_ci	{
18462306a36Sopenharmony_ci		.descr = "bind6 reject all",
18562306a36Sopenharmony_ci		.insns = {
18662306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 0),
18762306a36Sopenharmony_ci			BPF_EXIT_INSN(),
18862306a36Sopenharmony_ci		},
18962306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
19062306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET6_POST_BIND,
19162306a36Sopenharmony_ci		.domain = AF_INET6,
19262306a36Sopenharmony_ci		.type = SOCK_STREAM,
19362306a36Sopenharmony_ci		.ip = "::",
19462306a36Sopenharmony_ci		.result = BIND_REJECT,
19562306a36Sopenharmony_ci	},
19662306a36Sopenharmony_ci	{
19762306a36Sopenharmony_ci		.descr = "bind6 deny specific IP & port",
19862306a36Sopenharmony_ci		.insns = {
19962306a36Sopenharmony_ci			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci			/* if (ip == expected && port == expected) */
20262306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
20362306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_ip6[3])),
20462306a36Sopenharmony_ci			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
20562306a36Sopenharmony_ci				    __bpf_constant_ntohl(0x00000001), 4),
20662306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
20762306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_port)),
20862306a36Sopenharmony_ci			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci			/* return DENY; */
21162306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 0),
21262306a36Sopenharmony_ci			BPF_JMP_A(1),
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci			/* else return ALLOW; */
21562306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
21662306a36Sopenharmony_ci			BPF_EXIT_INSN(),
21762306a36Sopenharmony_ci		},
21862306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
21962306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET6_POST_BIND,
22062306a36Sopenharmony_ci		.domain = AF_INET6,
22162306a36Sopenharmony_ci		.type = SOCK_STREAM,
22262306a36Sopenharmony_ci		.ip = "::1",
22362306a36Sopenharmony_ci		.port = 8193,
22462306a36Sopenharmony_ci		.result = BIND_REJECT,
22562306a36Sopenharmony_ci	},
22662306a36Sopenharmony_ci	{
22762306a36Sopenharmony_ci		.descr = "bind4 allow specific IP & port",
22862306a36Sopenharmony_ci		.insns = {
22962306a36Sopenharmony_ci			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci			/* if (ip == expected && port == expected) */
23262306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
23362306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_ip4)),
23462306a36Sopenharmony_ci			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
23562306a36Sopenharmony_ci				    __bpf_constant_ntohl(0x7F000001), 4),
23662306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
23762306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_port)),
23862306a36Sopenharmony_ci			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci			/* return ALLOW; */
24162306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
24262306a36Sopenharmony_ci			BPF_JMP_A(1),
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci			/* else return DENY; */
24562306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 0),
24662306a36Sopenharmony_ci			BPF_EXIT_INSN(),
24762306a36Sopenharmony_ci		},
24862306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
24962306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET4_POST_BIND,
25062306a36Sopenharmony_ci		.domain = AF_INET,
25162306a36Sopenharmony_ci		.type = SOCK_STREAM,
25262306a36Sopenharmony_ci		.ip = "127.0.0.1",
25362306a36Sopenharmony_ci		.port = 4098,
25462306a36Sopenharmony_ci		.result = SUCCESS,
25562306a36Sopenharmony_ci	},
25662306a36Sopenharmony_ci	{
25762306a36Sopenharmony_ci		.descr = "bind4 deny specific IP & port of TCP, and retry",
25862306a36Sopenharmony_ci		.insns = {
25962306a36Sopenharmony_ci			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci			/* if (ip == expected && port == expected) */
26262306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
26362306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_ip4)),
26462306a36Sopenharmony_ci			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
26562306a36Sopenharmony_ci				    __bpf_constant_ntohl(0x7F000001), 4),
26662306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
26762306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_port)),
26862306a36Sopenharmony_ci			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci			/* return DENY; */
27162306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 0),
27262306a36Sopenharmony_ci			BPF_JMP_A(1),
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci			/* else return ALLOW; */
27562306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
27662306a36Sopenharmony_ci			BPF_EXIT_INSN(),
27762306a36Sopenharmony_ci		},
27862306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
27962306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET4_POST_BIND,
28062306a36Sopenharmony_ci		.domain = AF_INET,
28162306a36Sopenharmony_ci		.type = SOCK_STREAM,
28262306a36Sopenharmony_ci		.ip = "127.0.0.1",
28362306a36Sopenharmony_ci		.port = 4098,
28462306a36Sopenharmony_ci		.port_retry = 5000,
28562306a36Sopenharmony_ci		.result = RETRY_SUCCESS,
28662306a36Sopenharmony_ci	},
28762306a36Sopenharmony_ci	{
28862306a36Sopenharmony_ci		.descr = "bind4 deny specific IP & port of UDP, and retry",
28962306a36Sopenharmony_ci		.insns = {
29062306a36Sopenharmony_ci			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci			/* if (ip == expected && port == expected) */
29362306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
29462306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_ip4)),
29562306a36Sopenharmony_ci			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
29662306a36Sopenharmony_ci				    __bpf_constant_ntohl(0x7F000001), 4),
29762306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
29862306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_port)),
29962306a36Sopenharmony_ci			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci			/* return DENY; */
30262306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 0),
30362306a36Sopenharmony_ci			BPF_JMP_A(1),
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci			/* else return ALLOW; */
30662306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
30762306a36Sopenharmony_ci			BPF_EXIT_INSN(),
30862306a36Sopenharmony_ci		},
30962306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
31062306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET4_POST_BIND,
31162306a36Sopenharmony_ci		.domain = AF_INET,
31262306a36Sopenharmony_ci		.type = SOCK_DGRAM,
31362306a36Sopenharmony_ci		.ip = "127.0.0.1",
31462306a36Sopenharmony_ci		.port = 4098,
31562306a36Sopenharmony_ci		.port_retry = 5000,
31662306a36Sopenharmony_ci		.result = RETRY_SUCCESS,
31762306a36Sopenharmony_ci	},
31862306a36Sopenharmony_ci	{
31962306a36Sopenharmony_ci		.descr = "bind6 deny specific IP & port, and retry",
32062306a36Sopenharmony_ci		.insns = {
32162306a36Sopenharmony_ci			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci			/* if (ip == expected && port == expected) */
32462306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
32562306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_ip6[3])),
32662306a36Sopenharmony_ci			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
32762306a36Sopenharmony_ci				    __bpf_constant_ntohl(0x00000001), 4),
32862306a36Sopenharmony_ci			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
32962306a36Sopenharmony_ci				    offsetof(struct bpf_sock, src_port)),
33062306a36Sopenharmony_ci			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			/* return DENY; */
33362306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 0),
33462306a36Sopenharmony_ci			BPF_JMP_A(1),
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci			/* else return ALLOW; */
33762306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
33862306a36Sopenharmony_ci			BPF_EXIT_INSN(),
33962306a36Sopenharmony_ci		},
34062306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
34162306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET6_POST_BIND,
34262306a36Sopenharmony_ci		.domain = AF_INET6,
34362306a36Sopenharmony_ci		.type = SOCK_STREAM,
34462306a36Sopenharmony_ci		.ip = "::1",
34562306a36Sopenharmony_ci		.port = 8193,
34662306a36Sopenharmony_ci		.port_retry = 9000,
34762306a36Sopenharmony_ci		.result = RETRY_SUCCESS,
34862306a36Sopenharmony_ci	},
34962306a36Sopenharmony_ci	{
35062306a36Sopenharmony_ci		.descr = "bind4 allow all",
35162306a36Sopenharmony_ci		.insns = {
35262306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
35362306a36Sopenharmony_ci			BPF_EXIT_INSN(),
35462306a36Sopenharmony_ci		},
35562306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
35662306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET4_POST_BIND,
35762306a36Sopenharmony_ci		.domain = AF_INET,
35862306a36Sopenharmony_ci		.type = SOCK_STREAM,
35962306a36Sopenharmony_ci		.ip = "0.0.0.0",
36062306a36Sopenharmony_ci		.result = SUCCESS,
36162306a36Sopenharmony_ci	},
36262306a36Sopenharmony_ci	{
36362306a36Sopenharmony_ci		.descr = "bind6 allow all",
36462306a36Sopenharmony_ci		.insns = {
36562306a36Sopenharmony_ci			BPF_MOV64_IMM(BPF_REG_0, 1),
36662306a36Sopenharmony_ci			BPF_EXIT_INSN(),
36762306a36Sopenharmony_ci		},
36862306a36Sopenharmony_ci		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
36962306a36Sopenharmony_ci		.attach_type = BPF_CGROUP_INET6_POST_BIND,
37062306a36Sopenharmony_ci		.domain = AF_INET6,
37162306a36Sopenharmony_ci		.type = SOCK_STREAM,
37262306a36Sopenharmony_ci		.ip = "::",
37362306a36Sopenharmony_ci		.result = SUCCESS,
37462306a36Sopenharmony_ci	},
37562306a36Sopenharmony_ci};
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic size_t probe_prog_length(const struct bpf_insn *fp)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	size_t len;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	for (len = MAX_INSNS - 1; len > 0; --len)
38262306a36Sopenharmony_ci		if (fp[len].code != 0 || fp[len].imm != 0)
38362306a36Sopenharmony_ci			break;
38462306a36Sopenharmony_ci	return len + 1;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic int load_sock_prog(const struct bpf_insn *prog,
38862306a36Sopenharmony_ci			  enum bpf_attach_type attach_type)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	LIBBPF_OPTS(bpf_prog_load_opts, opts);
39162306a36Sopenharmony_ci	int ret, insn_cnt;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	insn_cnt = probe_prog_length(prog);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	opts.expected_attach_type = attach_type;
39662306a36Sopenharmony_ci	opts.log_buf = bpf_log_buf;
39762306a36Sopenharmony_ci	opts.log_size = BPF_LOG_BUF_SIZE;
39862306a36Sopenharmony_ci	opts.log_level = 2;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", prog, insn_cnt, &opts);
40162306a36Sopenharmony_ci	if (verbose && ret < 0)
40262306a36Sopenharmony_ci		fprintf(stderr, "%s\n", bpf_log_buf);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return ret;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic int attach_sock_prog(int cgfd, int progfd,
40862306a36Sopenharmony_ci			    enum bpf_attach_type attach_type)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int bind_sock(int domain, int type, const char *ip,
41462306a36Sopenharmony_ci		     unsigned short port, unsigned short port_retry)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct sockaddr_storage addr;
41762306a36Sopenharmony_ci	struct sockaddr_in6 *addr6;
41862306a36Sopenharmony_ci	struct sockaddr_in *addr4;
41962306a36Sopenharmony_ci	int sockfd = -1;
42062306a36Sopenharmony_ci	socklen_t len;
42162306a36Sopenharmony_ci	int res = SUCCESS;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	sockfd = socket(domain, type, 0);
42462306a36Sopenharmony_ci	if (sockfd < 0)
42562306a36Sopenharmony_ci		goto err;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	memset(&addr, 0, sizeof(addr));
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (domain == AF_INET) {
43062306a36Sopenharmony_ci		len = sizeof(struct sockaddr_in);
43162306a36Sopenharmony_ci		addr4 = (struct sockaddr_in *)&addr;
43262306a36Sopenharmony_ci		addr4->sin_family = domain;
43362306a36Sopenharmony_ci		addr4->sin_port = htons(port);
43462306a36Sopenharmony_ci		if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)
43562306a36Sopenharmony_ci			goto err;
43662306a36Sopenharmony_ci	} else if (domain == AF_INET6) {
43762306a36Sopenharmony_ci		len = sizeof(struct sockaddr_in6);
43862306a36Sopenharmony_ci		addr6 = (struct sockaddr_in6 *)&addr;
43962306a36Sopenharmony_ci		addr6->sin6_family = domain;
44062306a36Sopenharmony_ci		addr6->sin6_port = htons(port);
44162306a36Sopenharmony_ci		if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)
44262306a36Sopenharmony_ci			goto err;
44362306a36Sopenharmony_ci	} else {
44462306a36Sopenharmony_ci		goto err;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {
44862306a36Sopenharmony_ci		/* sys_bind() may fail for different reasons, errno has to be
44962306a36Sopenharmony_ci		 * checked to confirm that BPF program rejected it.
45062306a36Sopenharmony_ci		 */
45162306a36Sopenharmony_ci		if (errno != EPERM)
45262306a36Sopenharmony_ci			goto err;
45362306a36Sopenharmony_ci		if (port_retry)
45462306a36Sopenharmony_ci			goto retry;
45562306a36Sopenharmony_ci		res = BIND_REJECT;
45662306a36Sopenharmony_ci		goto out;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	goto out;
46062306a36Sopenharmony_ciretry:
46162306a36Sopenharmony_ci	if (domain == AF_INET)
46262306a36Sopenharmony_ci		addr4->sin_port = htons(port_retry);
46362306a36Sopenharmony_ci	else
46462306a36Sopenharmony_ci		addr6->sin6_port = htons(port_retry);
46562306a36Sopenharmony_ci	if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {
46662306a36Sopenharmony_ci		if (errno != EPERM)
46762306a36Sopenharmony_ci			goto err;
46862306a36Sopenharmony_ci		res = RETRY_REJECT;
46962306a36Sopenharmony_ci	} else {
47062306a36Sopenharmony_ci		res = RETRY_SUCCESS;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci	goto out;
47362306a36Sopenharmony_cierr:
47462306a36Sopenharmony_ci	res = -1;
47562306a36Sopenharmony_ciout:
47662306a36Sopenharmony_ci	close(sockfd);
47762306a36Sopenharmony_ci	return res;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic int run_test_case(int cgfd, const struct sock_test *test)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	int progfd = -1;
48362306a36Sopenharmony_ci	int err = 0;
48462306a36Sopenharmony_ci	int res;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	printf("Test case: %s .. ", test->descr);
48762306a36Sopenharmony_ci	progfd = load_sock_prog(test->insns, test->expected_attach_type);
48862306a36Sopenharmony_ci	if (progfd < 0) {
48962306a36Sopenharmony_ci		if (test->result == LOAD_REJECT)
49062306a36Sopenharmony_ci			goto out;
49162306a36Sopenharmony_ci		else
49262306a36Sopenharmony_ci			goto err;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (attach_sock_prog(cgfd, progfd, test->attach_type) < 0) {
49662306a36Sopenharmony_ci		if (test->result == ATTACH_REJECT)
49762306a36Sopenharmony_ci			goto out;
49862306a36Sopenharmony_ci		else
49962306a36Sopenharmony_ci			goto err;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	res = bind_sock(test->domain, test->type, test->ip, test->port,
50362306a36Sopenharmony_ci			test->port_retry);
50462306a36Sopenharmony_ci	if (res > 0 && test->result == res)
50562306a36Sopenharmony_ci		goto out;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cierr:
50862306a36Sopenharmony_ci	err = -1;
50962306a36Sopenharmony_ciout:
51062306a36Sopenharmony_ci	/* Detaching w/o checking return code: best effort attempt. */
51162306a36Sopenharmony_ci	if (progfd != -1)
51262306a36Sopenharmony_ci		bpf_prog_detach(cgfd, test->attach_type);
51362306a36Sopenharmony_ci	close(progfd);
51462306a36Sopenharmony_ci	printf("[%s]\n", err ? "FAIL" : "PASS");
51562306a36Sopenharmony_ci	return err;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int run_tests(int cgfd)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	int passes = 0;
52162306a36Sopenharmony_ci	int fails = 0;
52262306a36Sopenharmony_ci	int i;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tests); ++i) {
52562306a36Sopenharmony_ci		if (run_test_case(cgfd, &tests[i]))
52662306a36Sopenharmony_ci			++fails;
52762306a36Sopenharmony_ci		else
52862306a36Sopenharmony_ci			++passes;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci	printf("Summary: %d PASSED, %d FAILED\n", passes, fails);
53162306a36Sopenharmony_ci	return fails ? -1 : 0;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ciint main(int argc, char **argv)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	int cgfd = -1;
53762306a36Sopenharmony_ci	int err = 0;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	cgfd = cgroup_setup_and_join(CG_PATH);
54062306a36Sopenharmony_ci	if (cgfd < 0)
54162306a36Sopenharmony_ci		goto err;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/* Use libbpf 1.0 API mode */
54462306a36Sopenharmony_ci	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (run_tests(cgfd))
54762306a36Sopenharmony_ci		goto err;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	goto out;
55062306a36Sopenharmony_cierr:
55162306a36Sopenharmony_ci	err = -1;
55262306a36Sopenharmony_ciout:
55362306a36Sopenharmony_ci	close(cgfd);
55462306a36Sopenharmony_ci	cleanup_cgroup_environment();
55562306a36Sopenharmony_ci	return err;
55662306a36Sopenharmony_ci}
557