Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Copyright (c) 2020 Facebook */
0003 
0004 #include <stddef.h>
0005 #include <errno.h>
0006 #include <stdbool.h>
0007 #include <sys/types.h>
0008 #include <sys/socket.h>
0009 #include <linux/ipv6.h>
0010 #include <linux/tcp.h>
0011 #include <linux/socket.h>
0012 #include <linux/bpf.h>
0013 #include <linux/types.h>
0014 #include <bpf/bpf_helpers.h>
0015 #include <bpf/bpf_endian.h>
0016 #define BPF_PROG_TEST_TCP_HDR_OPTIONS
0017 #include "test_tcp_hdr_options.h"
0018 
0019 __u16 last_addr16_n = __bpf_htons(1);
0020 __u16 active_lport_n = 0;
0021 __u16 active_lport_h = 0;
0022 __u16 passive_lport_n = 0;
0023 __u16 passive_lport_h = 0;
0024 
0025 /* options received at passive side */
0026 unsigned int nr_pure_ack = 0;
0027 unsigned int nr_data = 0;
0028 unsigned int nr_syn = 0;
0029 unsigned int nr_fin = 0;
0030 
0031 /* Check the header received from the active side */
0032 static int __check_active_hdr_in(struct bpf_sock_ops *skops, bool check_syn)
0033 {
0034     union {
0035         struct tcphdr th;
0036         struct ipv6hdr ip6;
0037         struct tcp_exprm_opt exprm_opt;
0038         struct tcp_opt reg_opt;
0039         __u8 data[100]; /* IPv6 (40) + Max TCP hdr (60) */
0040     } hdr = {};
0041     __u64 load_flags = check_syn ? BPF_LOAD_HDR_OPT_TCP_SYN : 0;
0042     struct tcphdr *pth;
0043     int ret;
0044 
0045     hdr.reg_opt.kind = 0xB9;
0046 
0047     /* The option is 4 bytes long instead of 2 bytes */
0048     ret = bpf_load_hdr_opt(skops, &hdr.reg_opt, 2, load_flags);
0049     if (ret != -ENOSPC)
0050         RET_CG_ERR(ret);
0051 
0052     /* Test searching magic with regular kind */
0053     hdr.reg_opt.len = 4;
0054     ret = bpf_load_hdr_opt(skops, &hdr.reg_opt, sizeof(hdr.reg_opt),
0055                    load_flags);
0056     if (ret != -EINVAL)
0057         RET_CG_ERR(ret);
0058 
0059     hdr.reg_opt.len = 0;
0060     ret = bpf_load_hdr_opt(skops, &hdr.reg_opt, sizeof(hdr.reg_opt),
0061                    load_flags);
0062     if (ret != 4 || hdr.reg_opt.len != 4 || hdr.reg_opt.kind != 0xB9 ||
0063         hdr.reg_opt.data[0] != 0xfa || hdr.reg_opt.data[1] != 0xce)
0064         RET_CG_ERR(ret);
0065 
0066     /* Test searching experimental option with invalid kind length */
0067     hdr.exprm_opt.kind = TCPOPT_EXP;
0068     hdr.exprm_opt.len = 5;
0069     hdr.exprm_opt.magic = 0;
0070     ret = bpf_load_hdr_opt(skops, &hdr.exprm_opt, sizeof(hdr.exprm_opt),
0071                    load_flags);
0072     if (ret != -EINVAL)
0073         RET_CG_ERR(ret);
0074 
0075     /* Test searching experimental option with 0 magic value */
0076     hdr.exprm_opt.len = 4;
0077     ret = bpf_load_hdr_opt(skops, &hdr.exprm_opt, sizeof(hdr.exprm_opt),
0078                    load_flags);
0079     if (ret != -ENOMSG)
0080         RET_CG_ERR(ret);
0081 
0082     hdr.exprm_opt.magic = __bpf_htons(0xeB9F);
0083     ret = bpf_load_hdr_opt(skops, &hdr.exprm_opt, sizeof(hdr.exprm_opt),
0084                    load_flags);
0085     if (ret != 4 || hdr.exprm_opt.len != 4 ||
0086         hdr.exprm_opt.kind != TCPOPT_EXP ||
0087         hdr.exprm_opt.magic != __bpf_htons(0xeB9F))
0088         RET_CG_ERR(ret);
0089 
0090     if (!check_syn)
0091         return CG_OK;
0092 
0093     /* Test loading from skops->syn_skb if sk_state == TCP_NEW_SYN_RECV
0094      *
0095      * Test loading from tp->saved_syn for other sk_state.
0096      */
0097     ret = bpf_getsockopt(skops, SOL_TCP, TCP_BPF_SYN_IP, &hdr.ip6,
0098                  sizeof(hdr.ip6));
0099     if (ret != -ENOSPC)
0100         RET_CG_ERR(ret);
0101 
0102     if (hdr.ip6.saddr.s6_addr16[7] != last_addr16_n ||
0103         hdr.ip6.daddr.s6_addr16[7] != last_addr16_n)
0104         RET_CG_ERR(0);
0105 
0106     ret = bpf_getsockopt(skops, SOL_TCP, TCP_BPF_SYN_IP, &hdr, sizeof(hdr));
0107     if (ret < 0)
0108         RET_CG_ERR(ret);
0109 
0110     pth = (struct tcphdr *)(&hdr.ip6 + 1);
0111     if (pth->dest != passive_lport_n || pth->source != active_lport_n)
0112         RET_CG_ERR(0);
0113 
0114     ret = bpf_getsockopt(skops, SOL_TCP, TCP_BPF_SYN, &hdr, sizeof(hdr));
0115     if (ret < 0)
0116         RET_CG_ERR(ret);
0117 
0118     if (hdr.th.dest != passive_lport_n || hdr.th.source != active_lport_n)
0119         RET_CG_ERR(0);
0120 
0121     return CG_OK;
0122 }
0123 
0124 static int check_active_syn_in(struct bpf_sock_ops *skops)
0125 {
0126     return __check_active_hdr_in(skops, true);
0127 }
0128 
0129 static int check_active_hdr_in(struct bpf_sock_ops *skops)
0130 {
0131     struct tcphdr *th;
0132 
0133     if (__check_active_hdr_in(skops, false) == CG_ERR)
0134         return CG_ERR;
0135 
0136     th = skops->skb_data;
0137     if (th + 1 > skops->skb_data_end)
0138         RET_CG_ERR(0);
0139 
0140     if (tcp_hdrlen(th) < skops->skb_len)
0141         nr_data++;
0142 
0143     if (th->fin)
0144         nr_fin++;
0145 
0146     if (th->ack && !th->fin && tcp_hdrlen(th) == skops->skb_len)
0147         nr_pure_ack++;
0148 
0149     return CG_OK;
0150 }
0151 
0152 static int active_opt_len(struct bpf_sock_ops *skops)
0153 {
0154     int err;
0155 
0156     /* Reserve more than enough to allow the -EEXIST test in
0157      * the write_active_opt().
0158      */
0159     err = bpf_reserve_hdr_opt(skops, 12, 0);
0160     if (err)
0161         RET_CG_ERR(err);
0162 
0163     return CG_OK;
0164 }
0165 
0166 static int write_active_opt(struct bpf_sock_ops *skops)
0167 {
0168     struct tcp_exprm_opt exprm_opt = {};
0169     struct tcp_opt win_scale_opt = {};
0170     struct tcp_opt reg_opt = {};
0171     struct tcphdr *th;
0172     int err, ret;
0173 
0174     exprm_opt.kind = TCPOPT_EXP;
0175     exprm_opt.len = 4;
0176     exprm_opt.magic = __bpf_htons(0xeB9F);
0177 
0178     reg_opt.kind = 0xB9;
0179     reg_opt.len = 4;
0180     reg_opt.data[0] = 0xfa;
0181     reg_opt.data[1] = 0xce;
0182 
0183     win_scale_opt.kind = TCPOPT_WINDOW;
0184 
0185     err = bpf_store_hdr_opt(skops, &exprm_opt, sizeof(exprm_opt), 0);
0186     if (err)
0187         RET_CG_ERR(err);
0188 
0189     /* Store the same exprm option */
0190     err = bpf_store_hdr_opt(skops, &exprm_opt, sizeof(exprm_opt), 0);
0191     if (err != -EEXIST)
0192         RET_CG_ERR(err);
0193 
0194     err = bpf_store_hdr_opt(skops, &reg_opt, sizeof(reg_opt), 0);
0195     if (err)
0196         RET_CG_ERR(err);
0197     err = bpf_store_hdr_opt(skops, &reg_opt, sizeof(reg_opt), 0);
0198     if (err != -EEXIST)
0199         RET_CG_ERR(err);
0200 
0201     /* Check the option has been written and can be searched */
0202     ret = bpf_load_hdr_opt(skops, &exprm_opt, sizeof(exprm_opt), 0);
0203     if (ret != 4 || exprm_opt.len != 4 || exprm_opt.kind != TCPOPT_EXP ||
0204         exprm_opt.magic != __bpf_htons(0xeB9F))
0205         RET_CG_ERR(ret);
0206 
0207     reg_opt.len = 0;
0208     ret = bpf_load_hdr_opt(skops, &reg_opt, sizeof(reg_opt), 0);
0209     if (ret != 4 || reg_opt.len != 4 || reg_opt.kind != 0xB9 ||
0210         reg_opt.data[0] != 0xfa || reg_opt.data[1] != 0xce)
0211         RET_CG_ERR(ret);
0212 
0213     th = skops->skb_data;
0214     if (th + 1 > skops->skb_data_end)
0215         RET_CG_ERR(0);
0216 
0217     if (th->syn) {
0218         active_lport_h = skops->local_port;
0219         active_lport_n = th->source;
0220 
0221         /* Search the win scale option written by kernel
0222          * in the SYN packet.
0223          */
0224         ret = bpf_load_hdr_opt(skops, &win_scale_opt,
0225                        sizeof(win_scale_opt), 0);
0226         if (ret != 3 || win_scale_opt.len != 3 ||
0227             win_scale_opt.kind != TCPOPT_WINDOW)
0228             RET_CG_ERR(ret);
0229 
0230         /* Write the win scale option that kernel
0231          * has already written.
0232          */
0233         err = bpf_store_hdr_opt(skops, &win_scale_opt,
0234                     sizeof(win_scale_opt), 0);
0235         if (err != -EEXIST)
0236             RET_CG_ERR(err);
0237     }
0238 
0239     return CG_OK;
0240 }
0241 
0242 static int handle_hdr_opt_len(struct bpf_sock_ops *skops)
0243 {
0244     __u8 tcp_flags = skops_tcp_flags(skops);
0245 
0246     if ((tcp_flags & TCPHDR_SYNACK) == TCPHDR_SYNACK)
0247         /* Check the SYN from bpf_sock_ops_kern->syn_skb */
0248         return check_active_syn_in(skops);
0249 
0250     /* Passive side should have cleared the write hdr cb by now */
0251     if (skops->local_port == passive_lport_h)
0252         RET_CG_ERR(0);
0253 
0254     return active_opt_len(skops);
0255 }
0256 
0257 static int handle_write_hdr_opt(struct bpf_sock_ops *skops)
0258 {
0259     if (skops->local_port == passive_lport_h)
0260         RET_CG_ERR(0);
0261 
0262     return write_active_opt(skops);
0263 }
0264 
0265 static int handle_parse_hdr(struct bpf_sock_ops *skops)
0266 {
0267     /* Passive side is not writing any non-standard/unknown
0268      * option, so the active side should never be called.
0269      */
0270     if (skops->local_port == active_lport_h)
0271         RET_CG_ERR(0);
0272 
0273     return check_active_hdr_in(skops);
0274 }
0275 
0276 static int handle_passive_estab(struct bpf_sock_ops *skops)
0277 {
0278     int err;
0279 
0280     /* No more write hdr cb */
0281     bpf_sock_ops_cb_flags_set(skops,
0282                   skops->bpf_sock_ops_cb_flags &
0283                   ~BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG);
0284 
0285     /* Recheck the SYN but check the tp->saved_syn this time */
0286     err = check_active_syn_in(skops);
0287     if (err == CG_ERR)
0288         return err;
0289 
0290     nr_syn++;
0291 
0292     /* The ack has header option written by the active side also */
0293     return check_active_hdr_in(skops);
0294 }
0295 
0296 SEC("sockops")
0297 int misc_estab(struct bpf_sock_ops *skops)
0298 {
0299     int true_val = 1;
0300 
0301     switch (skops->op) {
0302     case BPF_SOCK_OPS_TCP_LISTEN_CB:
0303         passive_lport_h = skops->local_port;
0304         passive_lport_n = __bpf_htons(passive_lport_h);
0305         bpf_setsockopt(skops, SOL_TCP, TCP_SAVE_SYN,
0306                    &true_val, sizeof(true_val));
0307         set_hdr_cb_flags(skops, 0);
0308         break;
0309     case BPF_SOCK_OPS_TCP_CONNECT_CB:
0310         set_hdr_cb_flags(skops, 0);
0311         break;
0312     case BPF_SOCK_OPS_PARSE_HDR_OPT_CB:
0313         return handle_parse_hdr(skops);
0314     case BPF_SOCK_OPS_HDR_OPT_LEN_CB:
0315         return handle_hdr_opt_len(skops);
0316     case BPF_SOCK_OPS_WRITE_HDR_OPT_CB:
0317         return handle_write_hdr_opt(skops);
0318     case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
0319         return handle_passive_estab(skops);
0320     }
0321 
0322     return CG_OK;
0323 }
0324 
0325 char _license[] SEC("license") = "GPL";