net/tcp: Wire TCP-AO to request sockets
[linux-2.6-microblaze.git] / net / ipv4 / tcp_ipv4.c
index a78112d..2bd56b5 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;