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>
This commit is contained in:
parent
248411b8cb
commit
faadfaba5e
@ -199,6 +199,8 @@ void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
|
|||||||
bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code);
|
bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code);
|
||||||
int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen);
|
int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen);
|
||||||
int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen);
|
int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen);
|
||||||
|
int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen);
|
||||||
|
int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen);
|
||||||
enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk,
|
enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk,
|
||||||
const struct sk_buff *skb, unsigned short int family,
|
const struct sk_buff *skb, unsigned short int family,
|
||||||
const struct request_sock *req, int l3index,
|
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
|
|||||||
{
|
{
|
||||||
return -ENOPROTOOPT;
|
return -ENOPROTOOPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int tcp_ao_get_repair(struct sock *sk,
|
||||||
|
sockptr_t optval, sockptr_t optlen)
|
||||||
|
{
|
||||||
|
return -ENOPROTOOPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int tcp_ao_set_repair(struct sock *sk,
|
||||||
|
sockptr_t optval, unsigned int optlen)
|
||||||
|
{
|
||||||
|
return -ENOPROTOOPT;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
|
#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
|
||||||
|
@ -133,6 +133,7 @@ enum {
|
|||||||
#define TCP_AO_DEL_KEY 39 /* Delete MKT */
|
#define TCP_AO_DEL_KEY 39 /* Delete MKT */
|
||||||
#define TCP_AO_INFO 40 /* Set/list TCP-AO per-socket options */
|
#define TCP_AO_INFO 40 /* Set/list TCP-AO per-socket options */
|
||||||
#define TCP_AO_GET_KEYS 41 /* List MKT(s) */
|
#define TCP_AO_GET_KEYS 41 /* List MKT(s) */
|
||||||
|
#define TCP_AO_REPAIR 42 /* Get/Set SNEs and ISNs */
|
||||||
|
|
||||||
#define TCP_REPAIR_ON 1
|
#define TCP_REPAIR_ON 1
|
||||||
#define TCP_REPAIR_OFF 0
|
#define TCP_REPAIR_OFF 0
|
||||||
@ -458,6 +459,13 @@ struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET_KEYS) */
|
|||||||
__u64 pkt_bad; /* out: segments that failed verification */
|
__u64 pkt_bad; /* out: segments that failed verification */
|
||||||
} __attribute__((aligned(8)));
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
|
struct tcp_ao_repair { /* {s,g}etsockopt(TCP_AO_REPAIR) */
|
||||||
|
__be32 snt_isn;
|
||||||
|
__be32 rcv_isn;
|
||||||
|
__u32 snd_sne;
|
||||||
|
__u32 rcv_sne;
|
||||||
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
/* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
|
/* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
|
||||||
|
|
||||||
#define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1
|
#define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1
|
||||||
|
@ -3593,20 +3593,28 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname,
|
|||||||
__tcp_sock_set_quickack(sk, val);
|
__tcp_sock_set_quickack(sk, val);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TCP_AO_REPAIR:
|
||||||
|
err = tcp_ao_set_repair(sk, optval, optlen);
|
||||||
|
break;
|
||||||
#ifdef CONFIG_TCP_AO
|
#ifdef CONFIG_TCP_AO
|
||||||
case TCP_AO_ADD_KEY:
|
case TCP_AO_ADD_KEY:
|
||||||
case TCP_AO_DEL_KEY:
|
case TCP_AO_DEL_KEY:
|
||||||
case TCP_AO_INFO: {
|
case TCP_AO_INFO: {
|
||||||
/* If this is the first TCP-AO setsockopt() on the socket,
|
/* If this is the first TCP-AO setsockopt() on the socket,
|
||||||
* sk_state has to be LISTEN or CLOSE
|
* sk_state has to be LISTEN or CLOSE. Allow TCP_REPAIR
|
||||||
|
* in any state.
|
||||||
*/
|
*/
|
||||||
if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) ||
|
if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))
|
||||||
rcu_dereference_protected(tcp_sk(sk)->ao_info,
|
goto ao_parse;
|
||||||
|
if (rcu_dereference_protected(tcp_sk(sk)->ao_info,
|
||||||
lockdep_sock_is_held(sk)))
|
lockdep_sock_is_held(sk)))
|
||||||
err = tp->af_specific->ao_parse(sk, optname, optval,
|
goto ao_parse;
|
||||||
optlen);
|
if (tp->repair)
|
||||||
else
|
goto ao_parse;
|
||||||
err = -EISCONN;
|
err = -EISCONN;
|
||||||
|
break;
|
||||||
|
ao_parse:
|
||||||
|
err = tp->af_specific->ao_parse(sk, optname, optval, optlen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -4284,6 +4292,8 @@ zerocopy_rcv_out:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
case TCP_AO_REPAIR:
|
||||||
|
return tcp_ao_get_repair(sk, optval, optlen);
|
||||||
case TCP_AO_GET_KEYS:
|
case TCP_AO_GET_KEYS:
|
||||||
case TCP_AO_INFO: {
|
case TCP_AO_INFO: {
|
||||||
int err;
|
int err;
|
||||||
|
@ -1490,6 +1490,16 @@ static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk)
|
|||||||
return ERR_PTR(-ESOCKTNOSUPPORT);
|
return ERR_PTR(-ESOCKTNOSUPPORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct tcp_ao_info *getsockopt_ao_info(struct sock *sk)
|
||||||
|
{
|
||||||
|
if (sk_fullsock(sk))
|
||||||
|
return rcu_dereference(tcp_sk(sk)->ao_info);
|
||||||
|
else if (sk->sk_state == TCP_TIME_WAIT)
|
||||||
|
return rcu_dereference(tcp_twsk(sk)->ao_info);
|
||||||
|
|
||||||
|
return ERR_PTR(-ESOCKTNOSUPPORT);
|
||||||
|
}
|
||||||
|
|
||||||
#define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT)
|
#define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT)
|
||||||
#define TCP_AO_GET_KEYF_VALID (TCP_AO_KEYF_IFINDEX)
|
#define TCP_AO_GET_KEYF_VALID (TCP_AO_KEYF_IFINDEX)
|
||||||
|
|
||||||
@ -1671,11 +1681,13 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_free_sock;
|
goto err_free_sock;
|
||||||
|
|
||||||
/* Change this condition if we allow adding keys in states
|
if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) {
|
||||||
* like close_wait, syn_sent or fin_wait...
|
|
||||||
*/
|
|
||||||
if (sk->sk_state == TCP_ESTABLISHED)
|
|
||||||
tcp_ao_cache_traffic_keys(sk, ao_info, key);
|
tcp_ao_cache_traffic_keys(sk, ao_info, key);
|
||||||
|
if (first) {
|
||||||
|
ao_info->current_key = key;
|
||||||
|
ao_info->rnext_key = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tcp_ao_link_mkt(ao_info, key);
|
tcp_ao_link_mkt(ao_info, key);
|
||||||
if (first) {
|
if (first) {
|
||||||
@ -1926,6 +1938,8 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family,
|
|||||||
if (IS_ERR(ao_info))
|
if (IS_ERR(ao_info))
|
||||||
return PTR_ERR(ao_info);
|
return PTR_ERR(ao_info);
|
||||||
if (!ao_info) {
|
if (!ao_info) {
|
||||||
|
if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)))
|
||||||
|
return -EINVAL;
|
||||||
ao_info = tcp_ao_alloc_info(GFP_KERNEL);
|
ao_info = tcp_ao_alloc_info(GFP_KERNEL);
|
||||||
if (!ao_info)
|
if (!ao_info)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -2308,3 +2322,71 @@ int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen)
|
||||||
|
{
|
||||||
|
struct tcp_sock *tp = tcp_sk(sk);
|
||||||
|
struct tcp_ao_repair cmd;
|
||||||
|
struct tcp_ao_key *key;
|
||||||
|
struct tcp_ao_info *ao;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (optlen < sizeof(cmd))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (!tp->repair)
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
ao = setsockopt_ao_info(sk);
|
||||||
|
if (IS_ERR(ao))
|
||||||
|
return PTR_ERR(ao);
|
||||||
|
if (!ao)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
WRITE_ONCE(ao->lisn, cmd.snt_isn);
|
||||||
|
WRITE_ONCE(ao->risn, cmd.rcv_isn);
|
||||||
|
WRITE_ONCE(ao->snd_sne, cmd.snd_sne);
|
||||||
|
WRITE_ONCE(ao->rcv_sne, cmd.rcv_sne);
|
||||||
|
|
||||||
|
hlist_for_each_entry_rcu(key, &ao->head, node)
|
||||||
|
tcp_ao_cache_traffic_keys(sk, ao, key);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen)
|
||||||
|
{
|
||||||
|
struct tcp_sock *tp = tcp_sk(sk);
|
||||||
|
struct tcp_ao_repair opt;
|
||||||
|
struct tcp_ao_info *ao;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (copy_from_sockptr(&len, optlen, sizeof(int)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!tp->repair)
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
ao = getsockopt_ao_info(sk);
|
||||||
|
if (IS_ERR_OR_NULL(ao)) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return ao ? PTR_ERR(ao) : -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
opt.snt_isn = ao->lisn;
|
||||||
|
opt.rcv_isn = ao->risn;
|
||||||
|
opt.snd_sne = READ_ONCE(ao->snd_sne);
|
||||||
|
opt.rcv_sne = READ_ONCE(ao->rcv_sne);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
if (copy_to_sockptr(optval, &opt, min_t(int, len, sizeof(opt))))
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user