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 | bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code); | 199 | bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code); |
| 200 | int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen); | 200 | int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen); |
| 201 | int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen); | 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 | enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, | 204 | enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, |
| 203 | const struct sk_buff *skb, unsigned short int family, | 205 | const struct sk_buff *skb, unsigned short int family, |
| 204 | const struct request_sock *req, int l3index, | 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 | return -ENOPROTOOPT; | 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 | #endif | 347 | #endif |
| 334 | 348 | ||
| 335 | #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) | 349 | #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) |
| @@ -133,6 +133,7 @@ enum { · 132 unmodified lines | |||
| 133 | #define TCP_AO_DEL_KEY 39 /* Delete MKT */ | 133 | #define TCP_AO_DEL_KEY 39 /* Delete MKT */ |
| 134 | #define TCP_AO_INFO 40 /* Set/list TCP-AO per-socket options */ | 134 | #define TCP_AO_INFO 40 /* Set/list TCP-AO per-socket options */ |
| 135 | #define TCP_AO_GET_KEYS 41 /* List MKT(s) */ | 135 | #define TCP_AO_GET_KEYS 41 /* List MKT(s) */ |
| 136 | #define TCP_AO_REPAIR 42 /* Get/Set SNEs and ISNs */ | ||
| 136 | 137 | ||
| 137 | #define TCP_REPAIR_ON 1 | 138 | #define TCP_REPAIR_ON 1 |
| 138 | #define TCP_REPAIR_OFF 0 | 139 | #define TCP_REPAIR_OFF 0 |
| @@ -458,6 +459,13 @@ struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET_KEYS) */ · 319 unmodified lines | |||
| 458 | __u64 pkt_bad; /* out: segments that failed verification */ | 459 | __u64 pkt_bad; /* out: segments that failed verification */ |
| 459 | } __attribute__((aligned(8))); | 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 | /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ | 469 | /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ |
| 462 | 470 | ||
| 463 | #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 | 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 | __tcp_sock_set_quickack(sk, val); | 3593 | __tcp_sock_set_quickack(sk, val); |
| 3594 | break; | 3594 | break; |
| 3595 | 3595 | ||
| 3596 | case TCP_AO_REPAIR: | ||
| 3597 | err = tcp_ao_set_repair(sk, optval, optlen); | ||
| 3598 | break; | ||
| 3596 | #ifdef CONFIG_TCP_AO | 3599 | #ifdef CONFIG_TCP_AO |
| 3597 | case TCP_AO_ADD_KEY: | 3600 | case TCP_AO_ADD_KEY: |
| 3598 | case TCP_AO_DEL_KEY: | 3601 | case TCP_AO_DEL_KEY: |
| 3599 | case TCP_AO_INFO: { | 3602 | case TCP_AO_INFO: { |
| 3600 | /* If this is the first TCP-AO setsockopt() on the socket, | 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)) || | 3607 | if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) |
| 3604 | rcu_dereference_protected(tcp_sk(sk)->ao_info, | 3608 | goto ao_parse; |
| 3609 | if (rcu_dereference_protected(tcp_sk(sk)->ao_info, | ||
| 3605 | lockdep_sock_is_held(sk))) | 3610 | lockdep_sock_is_held(sk))) |
| 3606 | err = tp->af_specific->ao_parse(sk, optname, optval, | 3611 | goto ao_parse; |
| 3607 | optlen); | 3612 | if (tp->repair) |
| 3608 | else | 3613 | goto ao_parse; |
| 3609 | err = -EISCONN; | 3614 | err = -EISCONN; |
| 3615 | break; | ||
| 3616 | ao_parse: | ||
| 3617 | err = tp->af_specific->ao_parse(sk, optname, optval, optlen); | ||
| 3610 | break; | 3618 | break; |
| 3611 | } | 3619 | } |
| 3612 | #endif | 3620 | #endif |
| @@ -4284,6 +4292,8 @@ zerocopy_rcv_out: · 671 unmodified lines | |||
| 4284 | return err; | 4292 | return err; |
| 4285 | } | 4293 | } |
| 4286 | #endif | 4294 | #endif |
| 4295 | case TCP_AO_REPAIR: | ||
| 4296 | return tcp_ao_get_repair(sk, optval, optlen); | ||
| 4287 | case TCP_AO_GET_KEYS: | 4297 | case TCP_AO_GET_KEYS: |
| 4288 | case TCP_AO_INFO: { | 4298 | case TCP_AO_INFO: { |
| 4289 | int err; | 4299 | int err; |
| @@ -1490,6 +1490,16 @@ static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) · 1489 unmodified lines | |||
| 1490 | return ERR_PTR(-ESOCKTNOSUPPORT); | 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 | #define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT) | 1503 | #define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT) |
| 1494 | #define TCP_AO_GET_KEYF_VALID (TCP_AO_KEYF_IFINDEX) | 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 | if (ret < 0) | 1681 | if (ret < 0) |
| 1672 | goto err_free_sock; | 1682 | goto err_free_sock; |
| 1673 | 1683 | ||
| 1674 | /* Change this condition if we allow adding keys in states | 1684 | if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) { |
| 1675 | * like close_wait, syn_sent or fin_wait... | ||
| 1676 | */ | ||
| 1677 | if (sk->sk_state == TCP_ESTABLISHED) | ||
| 1678 | tcp_ao_cache_traffic_keys(sk, ao_info, key); | 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 | tcp_ao_link_mkt(ao_info, key); | 1692 | tcp_ao_link_mkt(ao_info, key); |
| 1681 | if (first) { | 1693 | if (first) { |
| @@ -1926,6 +1938,8 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, · 244 unmodified lines | |||
| 1926 | if (IS_ERR(ao_info)) | 1938 | if (IS_ERR(ao_info)) |
| 1927 | return PTR_ERR(ao_info); | 1939 | return PTR_ERR(ao_info); |
| 1928 | if (!ao_info) { | 1940 | if (!ao_info) { |
| 1941 | if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) | ||
| 1942 | return -EINVAL; | ||
| 1929 | ao_info = tcp_ao_alloc_info(GFP_KERNEL); | 1943 | ao_info = tcp_ao_alloc_info(GFP_KERNEL); |
| 1930 | if (!ao_info) | 1944 | if (!ao_info) |
| 1931 | return -ENOMEM; | 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 | return 0; | 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 | } | ||