162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/bpf.h>
362306a36Sopenharmony_ci#include <bpf/bpf_helpers.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_cichar _license[] SEC("license") = "GPL";
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define SOL_CUSTOM			0xdeadbeef
862306a36Sopenharmony_ci#define CUSTOM_INHERIT1			0
962306a36Sopenharmony_ci#define CUSTOM_INHERIT2			1
1062306a36Sopenharmony_ci#define CUSTOM_LISTENER			2
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci__u32 page_size = 0;
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistruct sockopt_inherit {
1562306a36Sopenharmony_ci	__u8 val;
1662306a36Sopenharmony_ci};
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct {
1962306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_SK_STORAGE);
2062306a36Sopenharmony_ci	__uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE);
2162306a36Sopenharmony_ci	__type(key, int);
2262306a36Sopenharmony_ci	__type(value, struct sockopt_inherit);
2362306a36Sopenharmony_ci} cloned1_map SEC(".maps");
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct {
2662306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_SK_STORAGE);
2762306a36Sopenharmony_ci	__uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE);
2862306a36Sopenharmony_ci	__type(key, int);
2962306a36Sopenharmony_ci	__type(value, struct sockopt_inherit);
3062306a36Sopenharmony_ci} cloned2_map SEC(".maps");
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct {
3362306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_SK_STORAGE);
3462306a36Sopenharmony_ci	__uint(map_flags, BPF_F_NO_PREALLOC);
3562306a36Sopenharmony_ci	__type(key, int);
3662306a36Sopenharmony_ci	__type(value, struct sockopt_inherit);
3762306a36Sopenharmony_ci} listener_only_map SEC(".maps");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic __inline struct sockopt_inherit *get_storage(struct bpf_sockopt *ctx)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	if (ctx->optname == CUSTOM_INHERIT1)
4262306a36Sopenharmony_ci		return bpf_sk_storage_get(&cloned1_map, ctx->sk, 0,
4362306a36Sopenharmony_ci					  BPF_SK_STORAGE_GET_F_CREATE);
4462306a36Sopenharmony_ci	else if (ctx->optname == CUSTOM_INHERIT2)
4562306a36Sopenharmony_ci		return bpf_sk_storage_get(&cloned2_map, ctx->sk, 0,
4662306a36Sopenharmony_ci					  BPF_SK_STORAGE_GET_F_CREATE);
4762306a36Sopenharmony_ci	else
4862306a36Sopenharmony_ci		return bpf_sk_storage_get(&listener_only_map, ctx->sk, 0,
4962306a36Sopenharmony_ci					  BPF_SK_STORAGE_GET_F_CREATE);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ciSEC("cgroup/getsockopt")
5362306a36Sopenharmony_ciint _getsockopt(struct bpf_sockopt *ctx)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	__u8 *optval_end = ctx->optval_end;
5662306a36Sopenharmony_ci	struct sockopt_inherit *storage;
5762306a36Sopenharmony_ci	__u8 *optval = ctx->optval;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (ctx->level != SOL_CUSTOM)
6062306a36Sopenharmony_ci		goto out; /* only interested in SOL_CUSTOM */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (optval + 1 > optval_end)
6362306a36Sopenharmony_ci		return 0; /* EPERM, bounds check */
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	storage = get_storage(ctx);
6662306a36Sopenharmony_ci	if (!storage)
6762306a36Sopenharmony_ci		return 0; /* EPERM, couldn't get sk storage */
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	ctx->retval = 0; /* Reset system call return value to zero */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	optval[0] = storage->val;
7262306a36Sopenharmony_ci	ctx->optlen = 1;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return 1;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciout:
7762306a36Sopenharmony_ci	/* optval larger than PAGE_SIZE use kernel's buffer. */
7862306a36Sopenharmony_ci	if (ctx->optlen > page_size)
7962306a36Sopenharmony_ci		ctx->optlen = 0;
8062306a36Sopenharmony_ci	return 1;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciSEC("cgroup/setsockopt")
8462306a36Sopenharmony_ciint _setsockopt(struct bpf_sockopt *ctx)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	__u8 *optval_end = ctx->optval_end;
8762306a36Sopenharmony_ci	struct sockopt_inherit *storage;
8862306a36Sopenharmony_ci	__u8 *optval = ctx->optval;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (ctx->level != SOL_CUSTOM)
9162306a36Sopenharmony_ci		goto out; /* only interested in SOL_CUSTOM */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (optval + 1 > optval_end)
9462306a36Sopenharmony_ci		return 0; /* EPERM, bounds check */
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	storage = get_storage(ctx);
9762306a36Sopenharmony_ci	if (!storage)
9862306a36Sopenharmony_ci		return 0; /* EPERM, couldn't get sk storage */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	storage->val = optval[0];
10162306a36Sopenharmony_ci	ctx->optlen = -1;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return 1;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciout:
10662306a36Sopenharmony_ci	/* optval larger than PAGE_SIZE use kernel's buffer. */
10762306a36Sopenharmony_ci	if (ctx->optlen > page_size)
10862306a36Sopenharmony_ci		ctx->optlen = 0;
10962306a36Sopenharmony_ci	return 1;
11062306a36Sopenharmony_ci}
111