Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net/tcp: Add TCP_AO_REPAIR

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.

Signed-off-by: Dmitry Safonov <dima@arista.com>
Acked-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Dmitry Safonov and committed by
David S. Miller
faadfaba 248411b8

+125 -11
+14
include/net/tcp_ao.h
··· 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, ··· 329 327 } 330 328 331 329 static inline int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) 330 + { 331 + return -ENOPROTOOPT; 332 + } 333 + 334 + static inline int tcp_ao_get_repair(struct sock *sk, 335 + sockptr_t optval, sockptr_t optlen) 336 + { 337 + return -ENOPROTOOPT; 338 + } 339 + 340 + static inline int tcp_ao_set_repair(struct sock *sk, 341 + sockptr_t optval, unsigned int optlen) 332 342 { 333 343 return -ENOPROTOOPT; 334 344 }
+8
include/uapi/linux/tcp.h
··· 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 ··· 457 456 __s32 ifindex; /* in/out: L3 dev index for VRF */ 458 457 __u64 pkt_good; /* out: verified segments */ 459 458 __u64 pkt_bad; /* out: segments that failed verification */ 459 + } __attribute__((aligned(8))); 460 + 461 + struct tcp_ao_repair { /* {s,g}etsockopt(TCP_AO_REPAIR) */ 462 + __be32 snt_isn; 463 + __be32 rcv_isn; 464 + __u32 snd_sne; 465 + __u32 rcv_sne; 460 466 } __attribute__((aligned(8))); 461 467 462 468 /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
+17 -7
net/ipv4/tcp.c
··· 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 ··· 4292 4284 return err; 4293 4285 } 4294 4286 #endif 4287 + case TCP_AO_REPAIR: 4288 + return tcp_ao_get_repair(sk, optval, optlen); 4295 4289 case TCP_AO_GET_KEYS: 4296 4290 case TCP_AO_INFO: { 4297 4291 int err;
+86 -4
net/ipv4/tcp_ao.c
··· 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 ··· 1681 1671 if (ret < 0) 1682 1672 goto err_free_sock; 1683 1673 1684 - /* Change this condition if we allow adding keys in states 1685 - * like close_wait, syn_sent or fin_wait... 1686 - */ 1687 - if (sk->sk_state == TCP_ESTABLISHED) 1674 + if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) { 1688 1675 tcp_ao_cache_traffic_keys(sk, ao_info, key); 1676 + if (first) { 1677 + ao_info->current_key = key; 1678 + ao_info->rnext_key = key; 1679 + } 1680 + } 1689 1681 1690 1682 tcp_ao_link_mkt(ao_info, key); 1691 1683 if (first) { ··· 1938 1926 if (IS_ERR(ao_info)) 1939 1927 return PTR_ERR(ao_info); 1940 1928 if (!ao_info) { 1929 + if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) 1930 + return -EINVAL; 1941 1931 ao_info = tcp_ao_alloc_info(GFP_KERNEL); 1942 1932 if (!ao_info) 1943 1933 return -ENOMEM; ··· 2322 2308 return 0; 2323 2309 } 2324 2310 2311 + int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen) 2312 + { 2313 + struct tcp_sock *tp = tcp_sk(sk); 2314 + struct tcp_ao_repair cmd; 2315 + struct tcp_ao_key *key; 2316 + struct tcp_ao_info *ao; 2317 + int err; 2318 + 2319 + if (optlen < sizeof(cmd)) 2320 + return -EINVAL; 2321 + 2322 + err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 2323 + if (err) 2324 + return err; 2325 + 2326 + if (!tp->repair) 2327 + return -EPERM; 2328 + 2329 + ao = setsockopt_ao_info(sk); 2330 + if (IS_ERR(ao)) 2331 + return PTR_ERR(ao); 2332 + if (!ao) 2333 + return -ENOENT; 2334 + 2335 + WRITE_ONCE(ao->lisn, cmd.snt_isn); 2336 + WRITE_ONCE(ao->risn, cmd.rcv_isn); 2337 + WRITE_ONCE(ao->snd_sne, cmd.snd_sne); 2338 + WRITE_ONCE(ao->rcv_sne, cmd.rcv_sne); 2339 + 2340 + hlist_for_each_entry_rcu(key, &ao->head, node) 2341 + tcp_ao_cache_traffic_keys(sk, ao, key); 2342 + 2343 + return 0; 2344 + } 2345 + 2346 + int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2347 + { 2348 + struct tcp_sock *tp = tcp_sk(sk); 2349 + struct tcp_ao_repair opt; 2350 + struct tcp_ao_info *ao; 2351 + int len; 2352 + 2353 + if (copy_from_sockptr(&len, optlen, sizeof(int))) 2354 + return -EFAULT; 2355 + 2356 + if (len <= 0) 2357 + return -EINVAL; 2358 + 2359 + if (!tp->repair) 2360 + return -EPERM; 2361 + 2362 + rcu_read_lock(); 2363 + ao = getsockopt_ao_info(sk); 2364 + if (IS_ERR_OR_NULL(ao)) { 2365 + rcu_read_unlock(); 2366 + return ao ? PTR_ERR(ao) : -ENOENT; 2367 + } 2368 + 2369 + opt.snt_isn = ao->lisn; 2370 + opt.rcv_isn = ao->risn; 2371 + opt.snd_sne = READ_ONCE(ao->snd_sne); 2372 + opt.rcv_sne = READ_ONCE(ao->rcv_sne); 2373 + rcu_read_unlock(); 2374 + 2375 + if (copy_to_sockptr(optval, &opt, min_t(int, len, sizeof(opt)))) 2376 + return -EFAULT; 2377 + return 0; 2378 + }