Add TCP_AO_REPAIR setsockopt(), getsockopt(). They let a user to repair TCP-AO ISNs/SNEs. Also let the user hack around when (tp->repair) is on and add ao_info on a socket in any supported state. As SNEs now can be read/written at any moment, use WRITE_ONCE()/READ_ONCE() to set/read them.
Unified · Split
| @@ -199,6 +199,8 @@ void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); · 198 unmodified lines | ||
| 199 | 199 | bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code); |
| 200 | 200 | int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen); |
| 201 | 201 | int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen); |
| 202 | +int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen); | |
| 203 | +int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen); | |
| 202 | 204 | enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, |
| 203 | 205 | const struct sk_buff *skb, unsigned short int family, |
| 204 | 206 | const struct request_sock *req, int l3index, |
| @@ -330,6 +332,18 @@ static inline int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockpt · 125 unmodified lines | ||
| 330 | 332 | { |
| 331 | 333 | return -ENOPROTOOPT; |
| 332 | 334 | } |
| 335 | + | |
| 336 | +static inline int tcp_ao_get_repair(struct sock *sk, | |
| 337 | + sockptr_t optval, sockptr_t optlen) | |
| 338 | +{ | |
| 339 | + return -ENOPROTOOPT; | |
| 340 | +} | |
| 341 | + | |
| 342 | +static inline int tcp_ao_set_repair(struct sock *sk, | |
| 343 | + sockptr_t optval, unsigned int optlen) | |
| 344 | +{ | |
| 345 | + return -ENOPROTOOPT; | |
| 346 | +} | |
| 333 | 347 | #endif |
| 334 | 348 | |
| 335 | 349 | #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) |
| @@ -133,6 +133,7 @@ enum { · 132 unmodified lines | ||
| 133 | 133 | #define TCP_AO_DEL_KEY 39 /* Delete MKT */ |
| 134 | 134 | #define TCP_AO_INFO 40 /* Set/list TCP-AO per-socket options */ |
| 135 | 135 | #define TCP_AO_GET_KEYS 41 /* List MKT(s) */ |
| 136 | +#define TCP_AO_REPAIR 42 /* Get/Set SNEs and ISNs */ | |
| 136 | 137 | |
| 137 | 138 | #define TCP_REPAIR_ON 1 |
| 138 | 139 | #define TCP_REPAIR_OFF 0 |
| @@ -458,6 +459,13 @@ struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET_KEYS) */ · 319 unmodified lines | ||
| 458 | 459 | __u64 pkt_bad; /* out: segments that failed verification */ |
| 459 | 460 | } __attribute__((aligned(8))); |
| 460 | 461 | |
| 462 | +struct tcp_ao_repair { /* {s,g}etsockopt(TCP_AO_REPAIR) */ | |
| 463 | + __be32 snt_isn; | |
| 464 | + __be32 rcv_isn; | |
| 465 | + __u32 snd_sne; | |
| 466 | + __u32 rcv_sne; | |
| 467 | +} __attribute__((aligned(8))); | |
| 468 | + | |
| 461 | 469 | /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ |
| 462 | 470 | |
| 463 | 471 | #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 |
| @@ -3593,20 +3593,28 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname, · 3592 unmodified lines | ||
| 3593 | 3593 | __tcp_sock_set_quickack(sk, val); |
| 3594 | 3594 | break; |
| 3595 | 3595 | |
| 3596 | + case TCP_AO_REPAIR: | |
| 3597 | + err = tcp_ao_set_repair(sk, optval, optlen); | |
| 3598 | + break; | |
| 3596 | 3599 | #ifdef CONFIG_TCP_AO |
| 3597 | 3600 | case TCP_AO_ADD_KEY: |
| 3598 | 3601 | case TCP_AO_DEL_KEY: |
| 3599 | 3602 | case TCP_AO_INFO: { |
| 3600 | 3603 | /* If this is the first TCP-AO setsockopt() on the socket, |
| 3601 | − * sk_state has to be LISTEN or CLOSE | |
| 3604 | + * sk_state has to be LISTEN or CLOSE. Allow TCP_REPAIR | |
| 3605 | + * in any state. | |
| 3602 | 3606 | */ |
| 3603 | − if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) || | |
| 3604 | − rcu_dereference_protected(tcp_sk(sk)->ao_info, | |
| 3607 | + if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) | |
| 3608 | + goto ao_parse; | |
| 3609 | + if (rcu_dereference_protected(tcp_sk(sk)->ao_info, | |
| 3605 | 3610 | lockdep_sock_is_held(sk))) |
| 3606 | − err = tp->af_specific->ao_parse(sk, optname, optval, | |
| 3607 | − optlen); | |
| 3608 | − else | |
| 3609 | − err = -EISCONN; | |
| 3611 | + goto ao_parse; | |
| 3612 | + if (tp->repair) | |
| 3613 | + goto ao_parse; | |
| 3614 | + err = -EISCONN; | |
| 3615 | + break; | |
| 3616 | +ao_parse: | |
| 3617 | + err = tp->af_specific->ao_parse(sk, optname, optval, optlen); | |
| 3610 | 3618 | break; |
| 3611 | 3619 | } |
| 3612 | 3620 | #endif |
| @@ -4284,6 +4292,8 @@ zerocopy_rcv_out: · 671 unmodified lines | ||
| 4284 | 4292 | return err; |
| 4285 | 4293 | } |
| 4286 | 4294 | #endif |
| 4295 | + case TCP_AO_REPAIR: | |
| 4296 | + return tcp_ao_get_repair(sk, optval, optlen); | |
| 4287 | 4297 | case TCP_AO_GET_KEYS: |
| 4288 | 4298 | case TCP_AO_INFO: { |
| 4289 | 4299 | int err; |
| @@ -1490,6 +1490,16 @@ static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) · 1489 unmodified lines | ||
| 1490 | 1490 | return ERR_PTR(-ESOCKTNOSUPPORT); |
| 1491 | 1491 | } |
| 1492 | 1492 | |
| 1493 | +static struct tcp_ao_info *getsockopt_ao_info(struct sock *sk) | |
| 1494 | +{ | |
| 1495 | + if (sk_fullsock(sk)) | |
| 1496 | + return rcu_dereference(tcp_sk(sk)->ao_info); | |
| 1497 | + else if (sk->sk_state == TCP_TIME_WAIT) | |
| 1498 | + return rcu_dereference(tcp_twsk(sk)->ao_info); | |
| 1499 | + | |
| 1500 | + return ERR_PTR(-ESOCKTNOSUPPORT); | |
| 1501 | +} | |
| 1502 | + | |
| 1493 | 1503 | #define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT) |
| 1494 | 1504 | #define TCP_AO_GET_KEYF_VALID (TCP_AO_KEYF_IFINDEX) |
| 1495 | 1505 | |
| @@ -1671,11 +1681,13 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, · 175 unmodified lines | ||
| 1671 | 1681 | if (ret < 0) |
| 1672 | 1682 | goto err_free_sock; |
| 1673 | 1683 | |
| 1674 | − /* Change this condition if we allow adding keys in states | |
| 1675 | − * like close_wait, syn_sent or fin_wait... | |
| 1676 | − */ | |
| 1677 | − if (sk->sk_state == TCP_ESTABLISHED) | |
| 1684 | + if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) { | |
| 1678 | 1685 | tcp_ao_cache_traffic_keys(sk, ao_info, key); |
| 1686 | + if (first) { | |
| 1687 | + ao_info->current_key = key; | |
| 1688 | + ao_info->rnext_key = key; | |
| 1689 | + } | |
| 1690 | + } | |
| 1679 | 1691 | |
| 1680 | 1692 | tcp_ao_link_mkt(ao_info, key); |
| 1681 | 1693 | if (first) { |
| @@ -1926,6 +1938,8 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, · 244 unmodified lines | ||
| 1926 | 1938 | if (IS_ERR(ao_info)) |
| 1927 | 1939 | return PTR_ERR(ao_info); |
| 1928 | 1940 | if (!ao_info) { |
| 1941 | + if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) | |
| 1942 | + return -EINVAL; | |
| 1929 | 1943 | ao_info = tcp_ao_alloc_info(GFP_KERNEL); |
| 1930 | 1944 | if (!ao_info) |
| 1931 | 1945 | return -ENOMEM; |
| @@ -2308,3 +2322,71 @@ int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) · 376 unmodified lines | ||
| 2308 | 2322 | return 0; |
| 2309 | 2323 | } |
| 2310 | 2324 | |
| 2325 | +int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen) | |
| 2326 | +{ | |
| 2327 | + struct tcp_sock *tp = tcp_sk(sk); | |
| 2328 | + struct tcp_ao_repair cmd; | |
| 2329 | + struct tcp_ao_key *key; | |
| 2330 | + struct tcp_ao_info *ao; | |
| 2331 | + int err; | |
| 2332 | + | |
| 2333 | + if (optlen < sizeof(cmd)) | |
| 2334 | + return -EINVAL; | |
| 2335 | + | |
| 2336 | + err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); | |
| 2337 | + if (err) | |
| 2338 | + return err; | |
| 2339 | + | |
| 2340 | + if (!tp->repair) | |
| 2341 | + return -EPERM; | |
| 2342 | + | |
| 2343 | + ao = setsockopt_ao_info(sk); | |
| 2344 | + if (IS_ERR(ao)) | |
| 2345 | + return PTR_ERR(ao); | |
| 2346 | + if (!ao) | |
| 2347 | + return -ENOENT; | |
| 2348 | + | |
| 2349 | + WRITE_ONCE(ao->lisn, cmd.snt_isn); | |
| 2350 | + WRITE_ONCE(ao->risn, cmd.rcv_isn); | |
| 2351 | + WRITE_ONCE(ao->snd_sne, cmd.snd_sne); | |
| 2352 | + WRITE_ONCE(ao->rcv_sne, cmd.rcv_sne); | |
| 2353 | + | |
| 2354 | + hlist_for_each_entry_rcu(key, &ao->head, node) | |
| 2355 | + tcp_ao_cache_traffic_keys(sk, ao, key); | |
| 2356 | + | |
| 2357 | + return 0; | |
| 2358 | +} | |
| 2359 | + | |
| 2360 | +int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen) | |
| 2361 | +{ | |
| 2362 | + struct tcp_sock *tp = tcp_sk(sk); | |
| 2363 | + struct tcp_ao_repair opt; | |
| 2364 | + struct tcp_ao_info *ao; | |
| 2365 | + int len; | |
| 2366 | + | |
| 2367 | + if (copy_from_sockptr(&len, optlen, sizeof(int))) | |
| 2368 | + return -EFAULT; | |
| 2369 | + | |
| 2370 | + if (len <= 0) | |
| 2371 | + return -EINVAL; | |
| 2372 | + | |
| 2373 | + if (!tp->repair) | |
| 2374 | + return -EPERM; | |
| 2375 | + | |
| 2376 | + rcu_read_lock(); | |
| 2377 | + ao = getsockopt_ao_info(sk); | |
| 2378 | + if (IS_ERR_OR_NULL(ao)) { | |
| 2379 | + rcu_read_unlock(); | |
| 2380 | + return ao ? PTR_ERR(ao) : -ENOENT; | |
| 2381 | + } | |
| 2382 | + | |
| 2383 | + opt.snt_isn = ao->lisn; | |
| 2384 | + opt.rcv_isn = ao->risn; | |
| 2385 | + opt.snd_sne = READ_ONCE(ao->snd_sne); | |
| 2386 | + opt.rcv_sne = READ_ONCE(ao->rcv_sne); | |
| 2387 | + rcu_read_unlock(); | |
| 2388 | + | |
| 2389 | + if (copy_to_sockptr(optval, &opt, min_t(int, len, sizeof(opt)))) | |
| 2390 | + return -EFAULT; | |
| 2391 | + return 0; | |
| 2392 | +} | |