net/tcp: Wire TCP-AO to request sockets
authorDmitry Safonov <dima@arista.com>
Mon, 23 Oct 2023 19:22:02 +0000 (20:22 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 27 Oct 2023 09:35:45 +0000 (10:35 +0100)
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>
Acked-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
12 files changed:
include/linux/tcp.h
include/net/tcp.h
include/net/tcp_ao.h
net/ipv4/syncookies.c
net/ipv4/tcp_ao.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_minisocks.c
net/ipv4/tcp_output.c
net/ipv6/syncookies.c
net/ipv6/tcp_ao.c
net/ipv6/tcp_ipv6.c

index eec6e7e5312e6985f8dbea71abb8bff1674fdb7c..ec4e9367f5b03be610f5f88621855f3a512604eb 100644 (file)
@@ -166,6 +166,11 @@ 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)
@@ -173,6 +178,19 @@ 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
+}
+
 #define TCP_RMEM_TO_WIN_SCALE 8
 
 struct tcp_sock {
index 17b3ecd2760fe7c453755ec0ee79edde656ce674..d0bea102b52365298744bf64ab944f580ae5151f 100644 (file)
@@ -2216,6 +2216,12 @@ 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);
index 971d7edcda9c71557b443c59179c795b6a26e9ae..d2c1ee8bf7b0c1c5e9f2cd2bc62387efe9acaa89 100644 (file)
@@ -123,6 +123,9 @@ int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
                 sockptr_t optval, int optlen);
 struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao,
                                          int sndid, int rcvid);
+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, struct tcp_sigpool *hp);
 void tcp_ao_destroy_sock(struct sock *sk, bool twsk);
@@ -147,6 +150,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);
@@ -154,11 +162,21 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
 int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp,
                                const struct in6_addr *daddr,
                                const struct in6_addr *saddr, int nbytes);
+int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+                          const struct sk_buff *skb, __be32 sisn, __be32 disn);
 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);
@@ -178,6 +196,12 @@ static inline int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
        return 0;
 }
 
+static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
+                                   struct tcp_request_sock *treq,
+                                   unsigned short int family)
+{
+}
+
 static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
                const union tcp_ao_addr *addr, int family, int sndid, int rcvid)
 {
index c6433436323096fe0e63f89e889687f74b57e387..0681d3e82b115319d2ed017ce80e6004d4567a70 100644 (file)
@@ -400,6 +400,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
        treq->snt_synack        = 0;
        treq->tfo_listener      = false;
 
+       tcp_ao_syncookie(sk, skb, treq, AF_INET);
+
        if (IS_ENABLED(CONFIG_SMC))
                ireq->smc_ok = 0;
 
index 7c4e2f42845ad53281465f17c646fa4860fb6ad2..68d81704e14ef0e5bad09fbe0fdef58a4b297875 100644 (file)
@@ -169,6 +169,23 @@ static void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt)
        hlist_add_head_rcu(&mkt->node, &ao->head);
 }
 
+static 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);
+       tcp_sigpool_get(new_key->tcp_sigpool_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);
@@ -290,6 +307,42 @@ static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
                return -EOPNOTSUPP;
 }
 
+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_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+                                 const struct sk_buff *skb,
+                                 __be32 sisn, __be32 disn)
+{
+       const struct iphdr *iph = ip_hdr(skb);
+       const struct tcphdr *th = tcp_hdr(skb);
+
+       return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr,
+                                 th->source, th->dest, sisn, disn);
+}
+
+static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+                              const struct sk_buff *skb,
+                              __be32 sisn, __be32 disn, int family)
+{
+       if (family == AF_INET)
+               return tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn);
+#if IS_ENABLED(CONFIG_IPV6)
+       else if (family == AF_INET6)
+               return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn);
+#endif
+       return -EAFNOSUPPORT;
+}
+
 static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp,
                                       __be32 daddr, __be32 saddr,
                                       int nbytes)
@@ -515,6 +568,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);
+}
+
 struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
                                    int sndid, int rcvid)
 {
@@ -528,7 +591,7 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
                         struct tcp_ao_key **key, char **traffic_key,
                         bool *allocated_traffic_key, u8 *keyid, u32 *sne)
 {
-       struct tcp_ao_key *rnext_key;
+       const struct tcphdr *th = tcp_hdr(skb);
        struct tcp_ao_info *ao_info;
 
        *allocated_traffic_key = false;
@@ -543,23 +606,62 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
                return -ENOTCONN;
 
        if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) {
-               return -1;
+               unsigned int family = READ_ONCE(sk->sk_family);
+               union tcp_ao_addr *addr;
+               __be32 disn, sisn;
 
-       if (sk->sk_state == TCP_TIME_WAIT)
-               ao_info = rcu_dereference(tcp_twsk(sk)->ao_info);
-       else
+               if (sk->sk_state == TCP_NEW_SYN_RECV) {
+                       struct request_sock *req = inet_reqsk(sk);
+
+                       sisn = htonl(tcp_rsk(req)->rcv_isn);
+                       disn = htonl(tcp_rsk(req)->snt_isn);
+                       *sne = 0;
+               } else {
+                       sisn = th->seq;
+                       disn = 0;
+               }
+               if (IS_ENABLED(CONFIG_IPV6) && family == AF_INET6)
+                       addr = (union tcp_md5_addr *)&ipv6_hdr(skb)->saddr;
+               else
+                       addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr;
+#if IS_ENABLED(CONFIG_IPV6)
+               if (family == AF_INET6 && ipv6_addr_v4mapped(&sk->sk_v6_daddr))
+                       family = AF_INET;
+#endif
+
+               sk = sk_const_to_full_sk(sk);
                ao_info = rcu_dereference(tcp_sk(sk)->ao_info);
-       if (!ao_info)
-               return -ENOENT;
+               if (!ao_info)
+                       return -ENOENT;
+               *key = tcp_ao_do_lookup(sk, addr, family, -1, aoh->rnext_keyid);
+               if (!*key)
+                       return -ENOENT;
+               *traffic_key = kmalloc(tcp_ao_digest_size(*key), GFP_ATOMIC);
+               if (!*traffic_key)
+                       return -ENOMEM;
+               *allocated_traffic_key = true;
+               if (tcp_ao_calc_key_skb(*key, *traffic_key, skb,
+                                       sisn, disn, family))
+                       return -1;
+               *keyid = (*key)->rcvid;
+       } else {
+               struct tcp_ao_key *rnext_key;
 
-       *key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1);
-       if (!*key)
-               return -ENOENT;
-       *traffic_key = snd_other_key(*key);
-       rnext_key = READ_ONCE(ao_info->rnext_key);
-       *keyid = rnext_key->rcvid;
-       *sne = 0;
+               if (sk->sk_state == TCP_TIME_WAIT)
+                       ao_info = rcu_dereference(tcp_twsk(sk)->ao_info);
+               else
+                       ao_info = rcu_dereference(tcp_sk(sk)->ao_info);
+               if (!ao_info)
+                       return -ENOENT;
 
+               *key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1);
+               if (!*key)
+                       return -ENOENT;
+               *traffic_key = snd_other_key(*key);
+               rnext_key = READ_ONCE(ao_info->rnext_key);
+               *keyid = rnext_key->rcvid;
+               *sne = 0;
+       }
        return 0;
 }
 
@@ -597,6 +699,46 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
        return 0;
 }
 
+static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family,
+               const struct sock *sk, const struct sk_buff *skb,
+               int sndid, int rcvid)
+{
+       if (family == AF_INET) {
+               const struct iphdr *iph = ip_hdr(skb);
+
+               return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr,
+                               AF_INET, sndid, rcvid);
+       } else {
+               const struct ipv6hdr *iph = ipv6_hdr(skb);
+
+               return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr,
+                               AF_INET6, sndid, rcvid);
+       }
+}
+
+void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
+                     struct tcp_request_sock *treq,
+                     unsigned short int family)
+{
+       const struct tcphdr *th = tcp_hdr(skb);
+       const struct tcp_ao_hdr *aoh;
+       struct tcp_ao_key *key;
+
+       treq->maclen = 0;
+
+       if (tcp_parse_auth_options(th, NULL, &aoh) || !aoh)
+               return;
+
+       key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid);
+       if (!key)
+               /* Key not found, continue without TCP-AO */
+               return;
+
+       treq->ao_rcv_next = aoh->keyid;
+       treq->ao_keyid = aoh->rnext_keyid;
+       treq->maclen = tcp_ao_maclen(key);
+}
+
 static int tcp_ao_cache_traffic_keys(const struct sock *sk,
                                     struct tcp_ao_info *ao,
                                     struct tcp_ao_key *ao_key)
@@ -704,6 +846,100 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
                tcp_ao_cache_traffic_keys(sk, ao, key);
 }
 
+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_key *key, *new_key, *first_key;
+       struct tcp_ao_info *new_ao, *ao;
+       struct hlist_node *key_head;
+       union tcp_ao_addr *addr;
+       bool match = false;
+       int ret = -ENOMEM;
+
+       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);
+       if (!new_ao)
+               return -ENOMEM;
+       new_ao->lisn = htonl(tcp_rsk(req)->snt_isn);
+       new_ao->risn = htonl(tcp_rsk(req)->rcv_isn);
+       new_ao->ao_required = ao->ao_required;
+
+       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 {
+               ret = -EAFNOSUPPORT;
+               goto free_ao;
+       }
+
+       hlist_for_each_entry_rcu(key, &ao->head, node) {
+               if (tcp_ao_key_cmp(key, addr, key->prefixlen, family, -1, -1))
+                       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) {
+               /* RFC5925 (7.4.1) specifies that the TCP-AO status
+                * of a connection is determined on the initial SYN.
+                * At this point the connection was TCP-AO enabled, so
+                * it can't switch to being unsigned if peer's key
+                * disappears on the listening socket.
+                */
+               ret = -EKEYREJECTED;
+               goto free_and_exit;
+       }
+
+       key_head = rcu_dereference(hlist_first_rcu(&new_ao->head));
+       first_key = hlist_entry_safe(key_head, struct tcp_ao_key, node);
+
+       key = tcp_ao_established_key(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_established_key(new_ao, -1, tcp_rsk(req)->ao_rcv_next);
+       if (key)
+               new_ao->rnext_key = key;
+       else
+               new_ao->rnext_key = first_key;
+
+       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, key_head, &new_ao->head, node) {
+               hlist_del(&key->node);
+               tcp_sigpool_release(key->tcp_sigpool_id);
+               atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc);
+               kfree_sensitive(key);
+       }
+free_ao:
+       kfree(new_ao);
+       return ret;
+}
+
 static bool tcp_ao_can_set_current_rnext(struct sock *sk)
 {
        /* There aren't current/rnext keys on TCP_LISTEN sockets */
index fc42b172abf6a3e7620e64f7ed0c1f9a8146c34f..de64c0c2fb6903acbe1c728456c41bbcdac75242 100644 (file)
@@ -7045,6 +7045,10 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
        struct flowi fl;
        u8 syncookies;
 
+#ifdef CONFIG_TCP_AO
+       const struct tcp_ao_hdr *aoh;
+#endif
+
        syncookies = READ_ONCE(net->ipv4.sysctl_tcp_syncookies);
 
        /* TW buckets are converted to open requests without
@@ -7131,6 +7135,17 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
                        inet_rsk(req)->ecn_ok = 0;
        }
 
+#ifdef CONFIG_TCP_AO
+       if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh))
+               goto drop_and_release; /* Invalid TCP options */
+       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;
index a78112d78d0646bdccd36df979fa2999735c57e1..2bd56b57f3c9d0320d262cead0c7e9d4be1c905f 100644 (file)
@@ -1072,13 +1072,47 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
        u32 seq = (sk->sk_state == TCP_LISTEN) ? tcp_rsk(req)->snt_isn + 1 :
                                             tcp_sk(sk)->snd_nxt;
 
-       /* RFC 7323 2.3
-        * The window field (SEG.WND) of every outgoing segment, with the
-        * exception of <SYN> segments, MUST be right-shifted by
-        * Rcv.Wind.Shift bits:
-        */
+#ifdef CONFIG_TCP_AO
+       if (tcp_rsk_used_ao(req)) {
+               const union tcp_md5_addr *addr;
+               const struct tcp_ao_hdr *aoh;
+
+               /* Invalid TCP option size or twice included auth */
+               if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh))
+                       return;
+               if (!aoh)
+                       return;
+
+               addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr;
+               key.ao_key = tcp_ao_do_lookup(sk, addr, AF_INET,
+                                             aoh->rnext_keyid, -1);
+               if (unlikely(!key.ao_key)) {
+                       /* Send ACK with any matching MKT for the peer */
+                       key.ao_key = tcp_ao_do_lookup(sk, addr, AF_INET, -1, -1);
+                       /* Matching key disappeared (user removed the key?)
+                        * let the handshake timeout.
+                        */
+                       if (!key.ao_key) {
+                               net_info_ratelimited("TCP-AO key for (%pI4, %d)->(%pI4, %d) suddenly disappeared, won't ACK new connection\n",
+                                                    addr,
+                                                    ntohs(tcp_hdr(skb)->source),
+                                                    &ip_hdr(skb)->daddr,
+                                                    ntohs(tcp_hdr(skb)->dest));
+                               return;
+                       }
+               }
+               key.traffic_key = kmalloc(tcp_ao_digest_size(key.ao_key), GFP_ATOMIC);
+               if (!key.traffic_key)
+                       return;
+
+               key.type = TCP_KEY_AO;
+               key.rcv_next = aoh->keyid;
+               tcp_v4_ao_calc_key_rsk(key.ao_key, key.traffic_key, req);
+#else
+       if (0) {
+#endif
 #ifdef CONFIG_TCP_MD5SIG
-       if (static_branch_unlikely(&tcp_md5_needed.key)) {
+       } else if (static_branch_unlikely(&tcp_md5_needed.key)) {
                const union tcp_md5_addr *addr;
                int l3index;
 
@@ -1087,8 +1121,14 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
                key.md5_key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET);
                if (key.md5_key)
                        key.type = TCP_KEY_MD5;
-       }
 #endif
+       }
+
+       /* RFC 7323 2.3
+        * The window field (SEG.WND) of every outgoing segment, with the
+        * exception of <SYN> segments, MUST be right-shifted by
+        * Rcv.Wind.Shift bits:
+        */
        tcp_v4_send_ack(sk, skb, seq,
                        tcp_rsk(req)->rcv_nxt,
                        req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale,
@@ -1098,6 +1138,8 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
                        inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0,
                        ip_hdr(skb)->tos,
                        READ_ONCE(tcp_rsk(req)->txhash));
+       if (tcp_key_is_ao(&key))
+               kfree(key.traffic_key);
 }
 
 /*
@@ -1636,6 +1678,10 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
        .req_md5_lookup =       tcp_v4_md5_lookup,
        .calc_md5_hash  =       tcp_v4_md5_hash_skb,
 #endif
+#ifdef CONFIG_TCP_AO
+       .ao_lookup      =       tcp_v4_ao_lookup_rsk,
+       .ao_calc_key    =       tcp_v4_ao_calc_key_rsk,
+#endif
 #ifdef CONFIG_SYN_COOKIES
        .cookie_init_seq =      cookie_v4_init_sequence,
 #endif
@@ -1737,12 +1783,16 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
        /* Copy over the MD5 key from the original socket */
        addr = (union tcp_md5_addr *)&newinet->inet_daddr;
        key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET);
-       if (key) {
+       if (key && !tcp_rsk_used_ao(req)) {
                if (tcp_md5_key_copy(newsk, addr, AF_INET, 32, l3index, key))
                        goto put_and_exit;
                sk_gso_disable(newsk);
        }
 #endif
+#ifdef CONFIG_TCP_AO
+       if (tcp_ao_copy_all_matching(sk, newsk, req, skb, AF_INET))
+               goto put_and_exit; /* OOM, release back memory */
+#endif
 
        if (__inet_inherit_port(sk, newsk) < 0)
                goto put_and_exit;
index 6810cf65a322e1a9bef17e671fe3af66411609e9..8d941a6e066b2a981fd0f5a89f46727ac5e7f272 100644 (file)
@@ -506,6 +506,9 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
        const struct tcp_sock *oldtp;
        struct tcp_sock *newtp;
        u32 seq;
+#ifdef CONFIG_TCP_AO
+       struct tcp_ao_key *ao_key;
+#endif
 
        if (!newsk)
                return NULL;
@@ -594,6 +597,13 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
 #ifdef CONFIG_TCP_MD5SIG
        newtp->md5sig_info = NULL;      /*XXX*/
 #endif
+#ifdef CONFIG_TCP_AO
+       newtp->ao_info = NULL;
+       ao_key = treq->af_specific->ao_lookup(sk, req,
+                               tcp_rsk(req)->ao_keyid, -1);
+       if (ao_key)
+               newtp->tcp_header_len += tcp_ao_len(ao_key);
+ #endif
        if (skb->len >= TCP_MSS_DEFAULT + newtp->tcp_header_len)
                newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len;
        newtp->rx_opt.mss_clamp = req->mss;
index de546313a214cd0de92dac8ec7f51ca610f73161..d44ef88025fb299274e68e8514db1c2207959e7e 100644 (file)
@@ -615,6 +615,7 @@ static void bpf_skops_write_hdr_opt(struct sock *sk, struct sk_buff *skb,
  * (but it may well be that other scenarios fail similarly).
  */
 static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
+                             const struct tcp_request_sock *tcprsk,
                              struct tcp_out_options *opts,
                              struct tcp_key *key)
 {
@@ -629,20 +630,28 @@ static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
                ptr += 4;
        } else if (tcp_key_is_ao(key)) {
 #ifdef CONFIG_TCP_AO
-               struct tcp_ao_key *rnext_key;
-               struct tcp_ao_info *ao_info;
-               u8 maclen;
+               u8 maclen = tcp_ao_maclen(key->ao_key);
 
-               ao_info = rcu_dereference_check(tp->ao_info,
+               if (tcprsk) {
+                       u8 aolen = maclen + sizeof(struct tcp_ao_hdr);
+
+                       *ptr++ = htonl((TCPOPT_AO << 24) | (aolen << 16) |
+                                      (tcprsk->ao_keyid << 8) |
+                                      (tcprsk->ao_rcv_next));
+               } else {
+                       struct tcp_ao_key *rnext_key;
+                       struct tcp_ao_info *ao_info;
+
+                       ao_info = rcu_dereference_check(tp->ao_info,
                                lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk));
-               rnext_key = READ_ONCE(ao_info->rnext_key);
-               if (WARN_ON_ONCE(!rnext_key))
-                       goto out_ao;
-               maclen = tcp_ao_maclen(key->ao_key);
-               *ptr++ = htonl((TCPOPT_AO << 24) |
-                               (tcp_ao_len(key->ao_key) << 16) |
-                               (key->ao_key->sndid << 8) |
-                               (rnext_key->rcvid));
+                       rnext_key = READ_ONCE(ao_info->rnext_key);
+                       if (WARN_ON_ONCE(!rnext_key))
+                               goto out_ao;
+                       *ptr++ = htonl((TCPOPT_AO << 24) |
+                                      (tcp_ao_len(key->ao_key) << 16) |
+                                      (key->ao_key->sndid << 8) |
+                                      (rnext_key->rcvid));
+               }
                opts->hash_location = (__u8 *)ptr;
                ptr += maclen / sizeof(*ptr);
                if (unlikely(maclen % sizeof(*ptr))) {
@@ -1386,7 +1395,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
                th->window      = htons(min(tp->rcv_wnd, 65535U));
        }
 
-       tcp_options_write(th, tp, &opts, &key);
+       tcp_options_write(th, tp, NULL, &opts, &key);
 
        if (tcp_key_is_md5(&key)) {
 #ifdef CONFIG_TCP_MD5SIG
@@ -3729,7 +3738,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
 
        /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
        th->window = htons(min(req->rsk_rcv_wnd, 65535U));
-       tcp_options_write(th, NULL, &opts, &key);
+       tcp_options_write(th, NULL, NULL, &opts, &key);
        th->doff = (tcp_header_size >> 2);
        TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS);
 
index 5014aa663452763b9895b2da0579367bfb3d0430..ad7a8caa7b2ac4b8493e1e12d06142d5c4b72d15 100644 (file)
@@ -214,6 +214,8 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
        treq->snt_isn = cookie;
        treq->ts_off = 0;
        treq->txhash = net_tx_rndhash();
+       tcp_ao_syncookie(sk, skb, treq, AF_INET6);
+
        if (IS_ENABLED(CONFIG_SMC))
                ireq->smc_ok = 0;
 
index d08735b6f3c5b00b6e96a51cf048b769b9150d5a..c9a6fa84f6ce2aa8756f9b77dd3ad29cdf0430c5 100644 (file)
@@ -49,6 +49,17 @@ static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
        return err;
 }
 
+int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+                          const struct sk_buff *skb,
+                          __be32 sisn, __be32 disn)
+{
+       const struct ipv6hdr *iph = ipv6_hdr(skb);
+       const struct tcphdr *th = tcp_hdr(skb);
+
+       return tcp_v6_ao_calc_key(mkt, key, &iph->saddr, &iph->daddr,
+                                th->source, th->dest, sisn, disn);
+}
+
 int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
                          const struct sock *sk, __be32 sisn,
                          __be32 disn, bool send)
@@ -63,9 +74,21 @@ int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
                                          htons(sk->sk_num), disn, sisn);
 }
 
-static struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk,
-                                             const struct in6_addr *addr,
-                                             int sndid, int rcvid)
+int tcp_v6_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_v6_ao_calc_key(mkt, key,
+                       &ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr,
+                       htons(ireq->ir_num), ireq->ir_rmt_port,
+                       htonl(tcp_rsk(req)->snt_isn),
+                       htonl(tcp_rsk(req)->rcv_isn));
+}
+
+struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk,
+                                      const struct in6_addr *addr,
+                                      int sndid, int rcvid)
 {
        return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)addr, AF_INET6,
                                sndid, rcvid);
@@ -80,6 +103,15 @@ struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
        return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid);
 }
 
+struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
+                                       struct request_sock *req,
+                                       int sndid, int rcvid)
+{
+       struct in6_addr *addr = &inet_rsk(req)->ir_v6_rmt_addr;
+
+       return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid);
+}
+
 int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp,
                                const struct in6_addr *daddr,
                                const struct in6_addr *saddr, int nbytes)
index e6c3617976f07f7ac18f9b2eea18a3d0e254fdfe..b08b177847da3f071cdf8d2bb7305722d8f07a16 100644 (file)
@@ -836,6 +836,10 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
        .req_md5_lookup =       tcp_v6_md5_lookup,
        .calc_md5_hash  =       tcp_v6_md5_hash_skb,
 #endif
+#ifdef CONFIG_TCP_AO
+       .ao_lookup      =       tcp_v6_ao_lookup_rsk,
+       .ao_calc_key    =       tcp_v6_ao_calc_key_rsk,
+#endif
 #ifdef CONFIG_SYN_COOKIES
        .cookie_init_seq =      cookie_v6_init_sequence,
 #endif
@@ -1192,16 +1196,54 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
 {
        struct tcp_key key = {};
 
+#ifdef CONFIG_TCP_AO
+       if (tcp_rsk_used_ao(req)) {
+               const struct in6_addr *addr = &ipv6_hdr(skb)->saddr;
+               const struct tcp_ao_hdr *aoh;
+               int l3index;
+
+               l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0;
+               /* Invalid TCP option size or twice included auth */
+               if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh))
+                       return;
+               if (!aoh)
+                       return;
+               key.ao_key = tcp_v6_ao_do_lookup(sk, addr, aoh->rnext_keyid, -1);
+               if (unlikely(!key.ao_key)) {
+                       /* Send ACK with any matching MKT for the peer */
+                       key.ao_key = tcp_v6_ao_do_lookup(sk, addr, -1, -1);
+                       /* Matching key disappeared (user removed the key?)
+                        * let the handshake timeout.
+                        */
+                       if (!key.ao_key) {
+                               net_info_ratelimited("TCP-AO key for (%pI6, %d)->(%pI6, %d) suddenly disappeared, won't ACK new connection\n",
+                                                    addr,
+                                                    ntohs(tcp_hdr(skb)->source),
+                                                    &ipv6_hdr(skb)->daddr,
+                                                    ntohs(tcp_hdr(skb)->dest));
+                               return;
+                       }
+               }
+               key.traffic_key = kmalloc(tcp_ao_digest_size(key.ao_key), GFP_ATOMIC);
+               if (!key.traffic_key)
+                       return;
+
+               key.type = TCP_KEY_AO;
+               key.rcv_next = aoh->keyid;
+               tcp_v6_ao_calc_key_rsk(key.ao_key, key.traffic_key, req);
+#else
+       if (0) {
+#endif
 #ifdef CONFIG_TCP_MD5SIG
-       if (static_branch_unlikely(&tcp_md5_needed.key)) {
+       } else if (static_branch_unlikely(&tcp_md5_needed.key)) {
                int l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0;
 
                key.md5_key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr,
                                                   l3index);
                if (key.md5_key)
                        key.type = TCP_KEY_MD5;
-       }
 #endif
+       }
 
        /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
         * sk->sk_state == TCP_SYN_RECV -> for Fast Open.
@@ -1220,6 +1262,8 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
                        &key, ipv6_get_dsfield(ipv6_hdr(skb)), 0,
                        READ_ONCE(sk->sk_priority),
                        READ_ONCE(tcp_rsk(req)->txhash));
+       if (tcp_key_is_ao(&key))
+               kfree(key.traffic_key);
 }
 
 
@@ -1449,19 +1493,26 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
 #ifdef CONFIG_TCP_MD5SIG
        l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif);
 
-       /* Copy over the MD5 key from the original socket */
-       key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr, l3index);
-       if (key) {
-               const union tcp_md5_addr *addr;
-
-               addr = (union tcp_md5_addr *)&newsk->sk_v6_daddr;
-               if (tcp_md5_key_copy(newsk, addr, AF_INET6, 128, l3index, key)) {
-                       inet_csk_prepare_forced_close(newsk);
-                       tcp_done(newsk);
-                       goto out;
+       if (!tcp_rsk_used_ao(req)) {
+               /* Copy over the MD5 key from the original socket */
+               key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr, l3index);
+               if (key) {
+                       const union tcp_md5_addr *addr;
+
+                       addr = (union tcp_md5_addr *)&newsk->sk_v6_daddr;
+                       if (tcp_md5_key_copy(newsk, addr, AF_INET6, 128, l3index, key)) {
+                               inet_csk_prepare_forced_close(newsk);
+                               tcp_done(newsk);
+                               goto out;
+                       }
                }
        }
 #endif
+#ifdef CONFIG_TCP_AO
+       /* Copy over tcp_ao_info if any */
+       if (tcp_ao_copy_all_matching(sk, newsk, req, skb, AF_INET6))
+               goto out; /* OOM */
+#endif
 
        if (__inet_inherit_port(sk, newsk) < 0) {
                inet_csk_prepare_forced_close(newsk);