1 // SPDX-License-Identifier: GPL-2.0
3 #include <netinet/in.h>
4 #include <netinet/tcp.h>
6 #include <bpf/bpf_helpers.h>
8 char _license
[] SEC("license") = "GPL";
9 __u32 _version
SEC("version") = 1;
12 #define PAGE_SIZE 4096
15 #define SOL_CUSTOM 0xdeadbeef
22 __uint(type
, BPF_MAP_TYPE_SK_STORAGE
);
23 __uint(map_flags
, BPF_F_NO_PREALLOC
);
25 __type(value
, struct sockopt_sk
);
26 } socket_storage_map
SEC(".maps");
28 SEC("cgroup/getsockopt")
29 int _getsockopt(struct bpf_sockopt
*ctx
)
31 __u8
*optval_end
= ctx
->optval_end
;
32 __u8
*optval
= ctx
->optval
;
33 struct sockopt_sk
*storage
;
35 if (ctx
->level
== SOL_IP
&& ctx
->optname
== IP_TOS
) {
36 /* Not interested in SOL_IP:IP_TOS;
37 * let next BPF program in the cgroup chain or kernel
40 ctx
->optlen
= 0; /* bypass optval>PAGE_SIZE */
44 if (ctx
->level
== SOL_SOCKET
&& ctx
->optname
== SO_SNDBUF
) {
45 /* Not interested in SOL_SOCKET:SO_SNDBUF;
46 * let next BPF program in the cgroup chain or kernel
52 if (ctx
->level
== SOL_TCP
&& ctx
->optname
== TCP_CONGESTION
) {
53 /* Not interested in SOL_TCP:TCP_CONGESTION;
54 * let next BPF program in the cgroup chain or kernel
60 if (ctx
->level
== SOL_IP
&& ctx
->optname
== IP_FREEBIND
) {
61 if (optval
+ 1 > optval_end
)
62 return 0; /* EPERM, bounds check */
64 ctx
->retval
= 0; /* Reset system call return value to zero */
66 /* Always export 0x55 */
70 /* Userspace buffer is PAGE_SIZE * 2, but BPF
71 * program can only see the first PAGE_SIZE
74 if (optval_end
- optval
!= PAGE_SIZE
)
75 return 0; /* EPERM, unexpected data size */
80 if (ctx
->level
!= SOL_CUSTOM
)
81 return 0; /* EPERM, deny everything except custom level */
83 if (optval
+ 1 > optval_end
)
84 return 0; /* EPERM, bounds check */
86 storage
= bpf_sk_storage_get(&socket_storage_map
, ctx
->sk
, 0,
87 BPF_SK_STORAGE_GET_F_CREATE
);
89 return 0; /* EPERM, couldn't get sk storage */
92 return 0; /* EPERM, kernel should not have handled
93 * SOL_CUSTOM, something is wrong!
95 ctx
->retval
= 0; /* Reset system call return value to zero */
97 optval
[0] = storage
->val
;
103 SEC("cgroup/setsockopt")
104 int _setsockopt(struct bpf_sockopt
*ctx
)
106 __u8
*optval_end
= ctx
->optval_end
;
107 __u8
*optval
= ctx
->optval
;
108 struct sockopt_sk
*storage
;
110 if (ctx
->level
== SOL_IP
&& ctx
->optname
== IP_TOS
) {
111 /* Not interested in SOL_IP:IP_TOS;
112 * let next BPF program in the cgroup chain or kernel
115 ctx
->optlen
= 0; /* bypass optval>PAGE_SIZE */
119 if (ctx
->level
== SOL_SOCKET
&& ctx
->optname
== SO_SNDBUF
) {
120 /* Overwrite SO_SNDBUF value */
122 if (optval
+ sizeof(__u32
) > optval_end
)
123 return 0; /* EPERM, bounds check */
125 *(__u32
*)optval
= 0x55AA;
131 if (ctx
->level
== SOL_TCP
&& ctx
->optname
== TCP_CONGESTION
) {
132 /* Always use cubic */
134 if (optval
+ 5 > optval_end
)
135 return 0; /* EPERM, bounds check */
137 memcpy(optval
, "cubic", 5);
143 if (ctx
->level
== SOL_IP
&& ctx
->optname
== IP_FREEBIND
) {
144 /* Original optlen is larger than PAGE_SIZE. */
145 if (ctx
->optlen
!= PAGE_SIZE
* 2)
146 return 0; /* EPERM, unexpected data size */
148 if (optval
+ 1 > optval_end
)
149 return 0; /* EPERM, bounds check */
151 /* Make sure we can trim the buffer. */
155 /* Usepace buffer is PAGE_SIZE * 2, but BPF
156 * program can only see the first PAGE_SIZE
159 if (optval_end
- optval
!= PAGE_SIZE
)
160 return 0; /* EPERM, unexpected data size */
165 if (ctx
->level
!= SOL_CUSTOM
)
166 return 0; /* EPERM, deny everything except custom level */
168 if (optval
+ 1 > optval_end
)
169 return 0; /* EPERM, bounds check */
171 storage
= bpf_sk_storage_get(&socket_storage_map
, ctx
->sk
, 0,
172 BPF_SK_STORAGE_GET_F_CREATE
);
174 return 0; /* EPERM, couldn't get sk storage */
176 storage
->val
= optval
[0];
177 ctx
->optlen
= -1; /* BPF has consumed this option, don't call kernel
178 * setsockopt handler.