Skip to content

Commit

Permalink
net/tcp: Wire TCP-AO to request sockets
Browse files Browse the repository at this point in the history
Now when the new request socket is created from the listening socket,
it's recorded what MKT was used by the peer. tcp_rsk_used_ao() is
a new helper for checking if TCP-AO option was used to create the
request socket.
tcp_ao_copy_all_matching() will copy all keys that match the peer on the
request socket, as well as preparing them for the usage (creating
traffic keys).

Co-developed-by: Francesco Ruggeri <fruggeri@arista.com>
Signed-off-by: Francesco Ruggeri <fruggeri@arista.com>
Co-developed-by: Salam Noureddine <noureddine@arista.com>
Signed-off-by: Salam Noureddine <noureddine@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
  • Loading branch information
0x7f454c46 authored and intel-lab-lkp committed Sep 23, 2022
1 parent efcce1d commit 714d0dc
Show file tree
Hide file tree
Showing 10 changed files with 379 additions and 30 deletions.
18 changes: 18 additions & 0 deletions include/linux/tcp.h
Expand Up @@ -165,13 +165,31 @@ struct tcp_request_sock {
* after data-in-SYN.
*/
u8 syn_tos;
#ifdef CONFIG_TCP_AO
u8 ao_keyid;
u8 ao_rcv_next;
u8 maclen;
#endif
};

static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)
{
return (struct tcp_request_sock *)req;
}

static inline bool tcp_rsk_used_ao(const struct request_sock *req)
{
/* The real length of MAC is saved in the request socket,
* signing anything with zero-length makes no sense, so here is
* a little hack..
*/
#ifndef CONFIG_TCP_AO
return false;
#else
return tcp_rsk(req)->maclen != 0;
#endif
}

struct tcp_sock {
/* inet_connection_sock has to be the first member of tcp_sock */
struct inet_connection_sock inet_conn;
Expand Down
7 changes: 7 additions & 0 deletions include/net/tcp.h
Expand Up @@ -2103,6 +2103,13 @@ struct tcp_request_sock_ops {
const struct sock *sk,
const struct sk_buff *skb);
#endif
#ifdef CONFIG_TCP_AO
struct tcp_ao_key *(*ao_lookup)(const struct sock *sk,
struct request_sock *req,
int sndid, int rcvid);
int (*ao_calc_key)(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *sk);
#endif
#ifdef CONFIG_SYN_COOKIES
__u32 (*cookie_init_seq)(const struct sk_buff *skb,
__u16 *mss);
Expand Down
16 changes: 16 additions & 0 deletions include/net/tcp_ao.h
Expand Up @@ -119,6 +119,9 @@ int tcp_ao_hash_skb(unsigned short int family,
int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
sockptr_t optval, int optlen);
struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid);
int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk,
struct request_sock *req, struct sk_buff *skb,
int family);
int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
unsigned int len);
void tcp_ao_destroy_sock(struct sock *sk, bool twsk);
Expand All @@ -142,6 +145,11 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk,
__be32 sisn, __be32 disn, bool send);
int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req);
struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
struct request_sock *req,
int sndid, int rcvid);
int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
const struct sock *sk, const struct sk_buff *skb,
const u8 *tkey, int hash_offset, u32 sne);
Expand All @@ -152,9 +160,17 @@ int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp,
int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk, __be32 sisn,
__be32 disn, bool send);
int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req);
struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk,
const struct in6_addr *addr,
int sndid, int rcvid);
struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
struct sock *addr_sk,
int sndid, int rcvid);
struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
struct request_sock *req,
int sndid, int rcvid);
int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
const struct sock *sk, const struct sk_buff *skb,
const u8 *tkey, int hash_offset, u32 sne);
Expand Down
141 changes: 141 additions & 0 deletions net/ipv4/tcp_ao.c
Expand Up @@ -52,6 +52,21 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
return 1;
}

struct tcp_ao_key *tcp_ao_do_lookup_keyid(struct tcp_ao_info *ao,
int sndid, int rcvid)
{
struct tcp_ao_key *key;

hlist_for_each_entry_rcu(key, &ao->head, node) {
if ((sndid >= 0 && key->sndid != sndid) ||
(rcvid >= 0 && key->rcvid != rcvid))
continue;
return key;
}

return NULL;
}

static struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid)
{
struct tcp_sock *tp = tcp_sk(sk);
Expand Down Expand Up @@ -194,6 +209,22 @@ static void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt)
hlist_add_head_rcu(&mkt->node, &ao->head);
}

struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, struct tcp_ao_key *key)
{
struct tcp_ao_key *new_key;

new_key = sock_kmalloc(sk, tcp_ao_sizeof_key(key),
GFP_ATOMIC);
if (!new_key)
return NULL;

*new_key = *key;
INIT_HLIST_NODE(&new_key->node);
crypto_pool_add(new_key->crypto_pool_id);

return new_key;
}

static void tcp_ao_key_free_rcu(struct rcu_head *head)
{
struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu);
Expand Down Expand Up @@ -291,6 +322,18 @@ int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
htons(sk->sk_num), disn, sisn);
}

int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req)
{
struct inet_request_sock *ireq = inet_rsk(req);

return tcp_v4_ao_calc_key(mkt, key,
ireq->ir_loc_addr, ireq->ir_rmt_addr,
htons(ireq->ir_num), ireq->ir_rmt_port,
htonl(tcp_rsk(req)->snt_isn),
htonl(tcp_rsk(req)->rcv_isn));
}

static int tcp_v4_ao_hash_pseudoheader(struct crypto_pool_ahash *hp,
__be32 daddr, __be32 saddr,
int nbytes)
Expand Down Expand Up @@ -560,6 +603,16 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
tkey, hash_offset, sne);
}

struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
struct request_sock *req,
int sndid, int rcvid)
{
union tcp_ao_addr *addr =
(union tcp_ao_addr *)&inet_rsk(req)->ir_rmt_addr;

return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid, 0);
}

struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
int sndid, int rcvid)
{
Expand Down Expand Up @@ -665,6 +718,94 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
}
}

int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk,
struct request_sock *req, struct sk_buff *skb,
int family)
{
struct tcp_ao_info *ao;
struct tcp_ao_info *new_ao;
struct tcp_ao_key *key, *new_key, *first_key;
struct hlist_node *n;
union tcp_ao_addr *addr;
bool match = false;

ao = rcu_dereference(tcp_sk(sk)->ao_info);
if (!ao)
return 0;

/* New socket without TCP-AO on it */
if (!tcp_rsk_used_ao(req))
return 0;

new_ao = tcp_ao_alloc_info(GFP_ATOMIC, ao);
if (!new_ao)
return -ENOMEM;
new_ao->lisn = htonl(tcp_rsk(req)->snt_isn);
new_ao->risn = htonl(tcp_rsk(req)->rcv_isn);

if (family == AF_INET)
addr = (union tcp_ao_addr *)&newsk->sk_daddr;
#if IS_ENABLED(CONFIG_IPV6)
else if (family == AF_INET6)
addr = (union tcp_ao_addr *)&newsk->sk_v6_daddr;
#endif
else
return -EOPNOTSUPP;

hlist_for_each_entry_rcu(key, &ao->head, node) {
if (tcp_ao_key_cmp(key, addr, key->prefixlen, family,
-1, -1, 0))
continue;

new_key = tcp_ao_copy_key(newsk, key);
if (!new_key)
goto free_and_exit;

tcp_ao_cache_traffic_keys(newsk, new_ao, new_key);
tcp_ao_link_mkt(new_ao, new_key);
match = true;
}

if (match) {
struct hlist_node *key_head;

key_head = rcu_dereference(hlist_first_rcu(&new_ao->head));
first_key = hlist_entry_safe(key_head, struct tcp_ao_key, node);

/* set current_key */
key = tcp_ao_do_lookup_keyid(new_ao, tcp_rsk(req)->ao_keyid, -1);
if (key)
new_ao->current_key = key;
else
new_ao->current_key = first_key;

/* set rnext_key */
key = tcp_ao_do_lookup_keyid(new_ao, -1, tcp_rsk(req)->ao_rcv_next);
if (key)
new_ao->rnext_key = key;
else
new_ao->rnext_key = first_key;

new_ao->snd_sne_seq = tcp_rsk(req)->snt_isn;
new_ao->rcv_sne_seq = tcp_rsk(req)->rcv_isn;

sk_gso_disable(newsk);
rcu_assign_pointer(tcp_sk(newsk)->ao_info, new_ao);
}

return 0;

free_and_exit:
hlist_for_each_entry_safe(key, n, &new_ao->head, node) {
hlist_del(&key->node);
crypto_pool_release(key->crypto_pool_id);
atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc);
kfree(key);
}
kfree(new_ao);
return -ENOMEM;
}

static int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags,
u8 tcpa_sndid, u8 tcpa_rcvid)
{
Expand Down
19 changes: 18 additions & 1 deletion net/ipv4/tcp_input.c
Expand Up @@ -6933,14 +6933,22 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
struct dst_entry *dst;
struct flowi fl;
u8 syncookies;
const struct tcp_ao_hdr *aoh = NULL;

#ifdef CONFIG_TCP_AO
/* TODO: Add an option to require TCP-AO signature */
/* Packet was already validated in tcp_v[46]_rcv */
if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh))
goto drop; /* Invalid TCP options */
#endif

syncookies = READ_ONCE(net->ipv4.sysctl_tcp_syncookies);

/* TW buckets are converted to open requests without
* limitations, they conserve resources and peer is
* evidently real one.
*/
if ((syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) && !isn) {
if (!aoh && (syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) && !isn) {
want_cookie = tcp_syn_flood_action(sk, rsk_ops->slab_name);
if (!want_cookie)
goto drop;
Expand Down Expand Up @@ -7019,6 +7027,15 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
inet_rsk(req)->ecn_ok = 0;
}

#ifdef CONFIG_TCP_AO
if (aoh) {
tcp_rsk(req)->maclen = aoh->length - sizeof(struct tcp_ao_hdr);
tcp_rsk(req)->ao_rcv_next = aoh->keyid;
tcp_rsk(req)->ao_keyid = aoh->rnext_keyid;
} else {
tcp_rsk(req)->maclen = 0;
}
#endif
tcp_rsk(req)->snt_isn = isn;
tcp_rsk(req)->txhash = net_tx_rndhash();
tcp_rsk(req)->syn_tos = TCP_SKB_CB(skb)->ip_dsfield;
Expand Down

0 comments on commit 714d0dc

Please sign in to comment.