Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <string.h>
0003 #include <linux/tcp.h>
0004 #include <linux/bpf.h>
0005 #include <netinet/in.h>
0006 #include <bpf/bpf_helpers.h>
0007 
0008 char _license[] SEC("license") = "GPL";
0009 
0010 int page_size = 0; /* userspace should set it */
0011 
0012 #ifndef SOL_TCP
0013 #define SOL_TCP IPPROTO_TCP
0014 #endif
0015 
0016 #define SOL_CUSTOM          0xdeadbeef
0017 
0018 struct sockopt_sk {
0019     __u8 val;
0020 };
0021 
0022 struct {
0023     __uint(type, BPF_MAP_TYPE_SK_STORAGE);
0024     __uint(map_flags, BPF_F_NO_PREALLOC);
0025     __type(key, int);
0026     __type(value, struct sockopt_sk);
0027 } socket_storage_map SEC(".maps");
0028 
0029 SEC("cgroup/getsockopt")
0030 int _getsockopt(struct bpf_sockopt *ctx)
0031 {
0032     __u8 *optval_end = ctx->optval_end;
0033     __u8 *optval = ctx->optval;
0034     struct sockopt_sk *storage;
0035 
0036     /* Make sure bpf_get_netns_cookie is callable.
0037      */
0038     if (bpf_get_netns_cookie(NULL) == 0)
0039         return 0;
0040 
0041     if (bpf_get_netns_cookie(ctx) == 0)
0042         return 0;
0043 
0044     if (ctx->level == SOL_IP && ctx->optname == IP_TOS) {
0045         /* Not interested in SOL_IP:IP_TOS;
0046          * let next BPF program in the cgroup chain or kernel
0047          * handle it.
0048          */
0049         ctx->optlen = 0; /* bypass optval>PAGE_SIZE */
0050         return 1;
0051     }
0052 
0053     if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
0054         /* Not interested in SOL_SOCKET:SO_SNDBUF;
0055          * let next BPF program in the cgroup chain or kernel
0056          * handle it.
0057          */
0058         return 1;
0059     }
0060 
0061     if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) {
0062         /* Not interested in SOL_TCP:TCP_CONGESTION;
0063          * let next BPF program in the cgroup chain or kernel
0064          * handle it.
0065          */
0066         return 1;
0067     }
0068 
0069     if (ctx->level == SOL_TCP && ctx->optname == TCP_ZEROCOPY_RECEIVE) {
0070         /* Verify that TCP_ZEROCOPY_RECEIVE triggers.
0071          * It has a custom implementation for performance
0072          * reasons.
0073          */
0074 
0075         /* Check that optval contains address (__u64) */
0076         if (optval + sizeof(__u64) > optval_end)
0077             return 0; /* bounds check */
0078 
0079         if (((struct tcp_zerocopy_receive *)optval)->address != 0)
0080             return 0; /* unexpected data */
0081 
0082         return 1;
0083     }
0084 
0085     if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) {
0086         if (optval + 1 > optval_end)
0087             return 0; /* bounds check */
0088 
0089         ctx->retval = 0; /* Reset system call return value to zero */
0090 
0091         /* Always export 0x55 */
0092         optval[0] = 0x55;
0093         ctx->optlen = 1;
0094 
0095         /* Userspace buffer is PAGE_SIZE * 2, but BPF
0096          * program can only see the first PAGE_SIZE
0097          * bytes of data.
0098          */
0099         if (optval_end - optval != page_size)
0100             return 0; /* unexpected data size */
0101 
0102         return 1;
0103     }
0104 
0105     if (ctx->level != SOL_CUSTOM)
0106         return 0; /* deny everything except custom level */
0107 
0108     if (optval + 1 > optval_end)
0109         return 0; /* bounds check */
0110 
0111     storage = bpf_sk_storage_get(&socket_storage_map, ctx->sk, 0,
0112                      BPF_SK_STORAGE_GET_F_CREATE);
0113     if (!storage)
0114         return 0; /* couldn't get sk storage */
0115 
0116     if (!ctx->retval)
0117         return 0; /* kernel should not have handled
0118                * SOL_CUSTOM, something is wrong!
0119                */
0120     ctx->retval = 0; /* Reset system call return value to zero */
0121 
0122     optval[0] = storage->val;
0123     ctx->optlen = 1;
0124 
0125     return 1;
0126 }
0127 
0128 SEC("cgroup/setsockopt")
0129 int _setsockopt(struct bpf_sockopt *ctx)
0130 {
0131     __u8 *optval_end = ctx->optval_end;
0132     __u8 *optval = ctx->optval;
0133     struct sockopt_sk *storage;
0134 
0135     /* Make sure bpf_get_netns_cookie is callable.
0136      */
0137     if (bpf_get_netns_cookie(NULL) == 0)
0138         return 0;
0139 
0140     if (bpf_get_netns_cookie(ctx) == 0)
0141         return 0;
0142 
0143     if (ctx->level == SOL_IP && ctx->optname == IP_TOS) {
0144         /* Not interested in SOL_IP:IP_TOS;
0145          * let next BPF program in the cgroup chain or kernel
0146          * handle it.
0147          */
0148         ctx->optlen = 0; /* bypass optval>PAGE_SIZE */
0149         return 1;
0150     }
0151 
0152     if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
0153         /* Overwrite SO_SNDBUF value */
0154 
0155         if (optval + sizeof(__u32) > optval_end)
0156             return 0; /* bounds check */
0157 
0158         *(__u32 *)optval = 0x55AA;
0159         ctx->optlen = 4;
0160 
0161         return 1;
0162     }
0163 
0164     if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) {
0165         /* Always use cubic */
0166 
0167         if (optval + 5 > optval_end)
0168             return 0; /* bounds check */
0169 
0170         memcpy(optval, "cubic", 5);
0171         ctx->optlen = 5;
0172 
0173         return 1;
0174     }
0175 
0176     if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) {
0177         /* Original optlen is larger than PAGE_SIZE. */
0178         if (ctx->optlen != page_size * 2)
0179             return 0; /* unexpected data size */
0180 
0181         if (optval + 1 > optval_end)
0182             return 0; /* bounds check */
0183 
0184         /* Make sure we can trim the buffer. */
0185         optval[0] = 0;
0186         ctx->optlen = 1;
0187 
0188         /* Usepace buffer is PAGE_SIZE * 2, but BPF
0189          * program can only see the first PAGE_SIZE
0190          * bytes of data.
0191          */
0192         if (optval_end - optval != page_size)
0193             return 0; /* unexpected data size */
0194 
0195         return 1;
0196     }
0197 
0198     if (ctx->level != SOL_CUSTOM)
0199         return 0; /* deny everything except custom level */
0200 
0201     if (optval + 1 > optval_end)
0202         return 0; /* bounds check */
0203 
0204     storage = bpf_sk_storage_get(&socket_storage_map, ctx->sk, 0,
0205                      BPF_SK_STORAGE_GET_F_CREATE);
0206     if (!storage)
0207         return 0; /* couldn't get sk storage */
0208 
0209     storage->val = optval[0];
0210     ctx->optlen = -1; /* BPF has consumed this option, don't call kernel
0211                * setsockopt handler.
0212                */
0213 
0214     return 1;
0215 }