int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
 struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
                                    int sndid, int rcvid);
+int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *mkt,
+                         struct request_sock *req, const struct sk_buff *skb,
+                         int hash_offset, u32 sne);
 int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
                          const struct sock *sk,
                          __be32 sisn, __be32 disn, bool send);
                       const struct sock *sk, const struct sk_buff *skb,
                       const u8 *tkey, int hash_offset, u32 sne);
 int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
+int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
+                         struct request_sock *req, const struct sk_buff *skb,
+                         int hash_offset, u32 sne);
 void tcp_ao_established(struct sock *sk);
 void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb);
 void tcp_ao_connect_init(struct sock *sk);
 
                                       struct request_sock *req,
                                       unsigned int mss, struct sk_buff *skb,
                                       struct tcp_out_options *opts,
-                                      const struct tcp_md5sig_key *md5,
+                                      const struct tcp_key *key,
                                       struct tcp_fastopen_cookie *foc,
                                       enum tcp_synack_type synack_type,
                                       struct sk_buff *syn_skb)
        struct inet_request_sock *ireq = inet_rsk(req);
        unsigned int remaining = MAX_TCP_OPTION_SPACE;
 
-#ifdef CONFIG_TCP_MD5SIG
-       if (md5) {
+       if (tcp_key_is_md5(key)) {
                opts->options |= OPTION_MD5;
                remaining -= TCPOLEN_MD5SIG_ALIGNED;
 
                 */
                if (synack_type != TCP_SYNACK_COOKIE)
                        ireq->tstamp_ok &= !ireq->sack_ok;
+       } else if (tcp_key_is_ao(key)) {
+               opts->options |= OPTION_AO;
+               remaining -= tcp_ao_len(key->ao_key);
+               ireq->tstamp_ok &= !ireq->sack_ok;
        }
-#endif
 
        /* We always send an MSS option. */
        opts->mss = mss;
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        const struct tcp_sock *tp = tcp_sk(sk);
-       struct tcp_md5sig_key *md5 = NULL;
        struct tcp_out_options opts;
        struct tcp_key key = {};
        struct sk_buff *skb;
                        tcp_rsk(req)->snt_synack = tcp_skb_timestamp_us(skb);
        }
 
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
        rcu_read_lock();
-       md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk, req_to_sk(req));
-       if (md5)
-               key.type = TCP_KEY_MD5;
 #endif
+       if (tcp_rsk_used_ao(req)) {
+#ifdef CONFIG_TCP_AO
+               struct tcp_ao_key *ao_key = NULL;
+               u8 maclen = tcp_rsk(req)->maclen;
+               u8 keyid = tcp_rsk(req)->ao_keyid;
+
+               ao_key = tcp_sk(sk)->af_specific->ao_lookup(sk, req_to_sk(req),
+                                                           keyid, -1);
+               /* If there is no matching key - avoid sending anything,
+                * especially usigned segments. It could try harder and lookup
+                * for another peer-matching key, but the peer has requested
+                * ao_keyid (RFC5925 RNextKeyID), so let's keep it simple here.
+                */
+               if (unlikely(!ao_key || tcp_ao_maclen(ao_key) != maclen)) {
+                       u8 key_maclen = ao_key ? tcp_ao_maclen(ao_key) : 0;
+
+                       rcu_read_unlock();
+                       kfree_skb(skb);
+                       net_warn_ratelimited("TCP-AO: the keyid %u with maclen %u|%u from SYN packet is not present - not sending SYNACK\n",
+                                            keyid, maclen, key_maclen);
+                       return NULL;
+               }
+               key.ao_key = ao_key;
+               key.type = TCP_KEY_AO;
+#endif
+       } else {
+#ifdef CONFIG_TCP_MD5SIG
+               key.md5_key = tcp_rsk(req)->af_specific->req_md5_lookup(sk,
+                                       req_to_sk(req));
+               if (key.md5_key)
+                       key.type = TCP_KEY_MD5;
+#endif
+       }
        skb_set_hash(skb, READ_ONCE(tcp_rsk(req)->txhash), PKT_HASH_TYPE_L4);
        /* bpf program will be interested in the tcp_flags */
        TCP_SKB_CB(skb)->tcp_flags = TCPHDR_SYN | TCPHDR_ACK;
-       tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5,
-                                            foc, synack_type,
-                                            syn_skb) + sizeof(*th);
+       tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts,
+                                            &key, foc, synack_type, syn_skb)
+                                       + sizeof(*th);
 
        skb_push(skb, tcp_header_size);
        skb_reset_transport_header(skb);
 
        /* 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, NULL, &opts, &key);
+       tcp_options_write(th, NULL, tcp_rsk(req), &opts, &key);
        th->doff = (tcp_header_size >> 2);
        TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS);
 
-#ifdef CONFIG_TCP_MD5SIG
        /* Okay, we have all we need - do the md5 hash if needed */
-       if (md5)
+       if (tcp_key_is_md5(&key)) {
+#ifdef CONFIG_TCP_MD5SIG
                tcp_rsk(req)->af_specific->calc_md5_hash(opts.hash_location,
-                                              md5, req_to_sk(req), skb);
+                                       key.md5_key, req_to_sk(req), skb);
+#endif
+       } else if (tcp_key_is_ao(&key)) {
+#ifdef CONFIG_TCP_AO
+               tcp_rsk(req)->af_specific->ao_synack_hash(opts.hash_location,
+                                       key.ao_key, req, skb,
+                                       opts.hash_location - (u8 *)th, 0);
+#endif
+       }
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
        rcu_read_unlock();
 #endif