0001
0002
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
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
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];
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
0048 ret = bpf_load_hdr_opt(skops, &hdr.reg_opt, 2, load_flags);
0049 if (ret != -ENOSPC)
0050 RET_CG_ERR(ret);
0051
0052
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
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
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
0094
0095
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
0157
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
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, ®_opt, sizeof(reg_opt), 0);
0195 if (err)
0196 RET_CG_ERR(err);
0197 err = bpf_store_hdr_opt(skops, ®_opt, sizeof(reg_opt), 0);
0198 if (err != -EEXIST)
0199 RET_CG_ERR(err);
0200
0201
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, ®_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
0222
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
0231
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
0248 return check_active_syn_in(skops);
0249
0250
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
0268
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
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
0286 err = check_active_syn_in(skops);
0287 if (err == CG_ERR)
0288 return err;
0289
0290 nr_syn++;
0291
0292
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";