xref: /linux/net/ipv4/tcp_ao.c (revision 7c2ffaf21bd67f73d21560995ce17eaf5fc1d37f)
14954f17dSDmitry Safonov // SPDX-License-Identifier: GPL-2.0-or-later
24954f17dSDmitry Safonov /*
34954f17dSDmitry Safonov  * INET		An implementation of the TCP Authentication Option (TCP-AO).
44954f17dSDmitry Safonov  *		See RFC5925.
54954f17dSDmitry Safonov  *
64954f17dSDmitry Safonov  * Authors:	Dmitry Safonov <dima@arista.com>
74954f17dSDmitry Safonov  *		Francesco Ruggeri <fruggeri@arista.com>
84954f17dSDmitry Safonov  *		Salam Noureddine <noureddine@arista.com>
94954f17dSDmitry Safonov  */
104954f17dSDmitry Safonov #define pr_fmt(fmt) "TCP: " fmt
114954f17dSDmitry Safonov 
124954f17dSDmitry Safonov #include <crypto/hash.h>
134954f17dSDmitry Safonov #include <linux/inetdevice.h>
144954f17dSDmitry Safonov #include <linux/tcp.h>
154954f17dSDmitry Safonov 
164954f17dSDmitry Safonov #include <net/tcp.h>
174954f17dSDmitry Safonov #include <net/ipv6.h>
184954f17dSDmitry Safonov 
19*7c2ffaf2SDmitry Safonov int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
20*7c2ffaf2SDmitry Safonov 			    unsigned int len, struct tcp_sigpool *hp)
21*7c2ffaf2SDmitry Safonov {
22*7c2ffaf2SDmitry Safonov 	struct scatterlist sg;
23*7c2ffaf2SDmitry Safonov 	int ret;
24*7c2ffaf2SDmitry Safonov 
25*7c2ffaf2SDmitry Safonov 	if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req),
26*7c2ffaf2SDmitry Safonov 				mkt->key, mkt->keylen))
27*7c2ffaf2SDmitry Safonov 		goto clear_hash;
28*7c2ffaf2SDmitry Safonov 
29*7c2ffaf2SDmitry Safonov 	ret = crypto_ahash_init(hp->req);
30*7c2ffaf2SDmitry Safonov 	if (ret)
31*7c2ffaf2SDmitry Safonov 		goto clear_hash;
32*7c2ffaf2SDmitry Safonov 
33*7c2ffaf2SDmitry Safonov 	sg_init_one(&sg, ctx, len);
34*7c2ffaf2SDmitry Safonov 	ahash_request_set_crypt(hp->req, &sg, key, len);
35*7c2ffaf2SDmitry Safonov 	crypto_ahash_update(hp->req);
36*7c2ffaf2SDmitry Safonov 
37*7c2ffaf2SDmitry Safonov 	ret = crypto_ahash_final(hp->req);
38*7c2ffaf2SDmitry Safonov 	if (ret)
39*7c2ffaf2SDmitry Safonov 		goto clear_hash;
40*7c2ffaf2SDmitry Safonov 
41*7c2ffaf2SDmitry Safonov 	return 0;
42*7c2ffaf2SDmitry Safonov clear_hash:
43*7c2ffaf2SDmitry Safonov 	memset(key, 0, tcp_ao_digest_size(mkt));
44*7c2ffaf2SDmitry Safonov 	return 1;
45*7c2ffaf2SDmitry Safonov }
46*7c2ffaf2SDmitry Safonov 
474954f17dSDmitry Safonov /* Optimized version of tcp_ao_do_lookup(): only for sockets for which
484954f17dSDmitry Safonov  * it's known that the keys in ao_info are matching peer's
494954f17dSDmitry Safonov  * family/address/VRF/etc.
504954f17dSDmitry Safonov  */
514954f17dSDmitry Safonov static struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao,
524954f17dSDmitry Safonov 						 int sndid, int rcvid)
534954f17dSDmitry Safonov {
544954f17dSDmitry Safonov 	struct tcp_ao_key *key;
554954f17dSDmitry Safonov 
564954f17dSDmitry Safonov 	hlist_for_each_entry_rcu(key, &ao->head, node) {
574954f17dSDmitry Safonov 		if ((sndid >= 0 && key->sndid != sndid) ||
584954f17dSDmitry Safonov 		    (rcvid >= 0 && key->rcvid != rcvid))
594954f17dSDmitry Safonov 			continue;
604954f17dSDmitry Safonov 		return key;
614954f17dSDmitry Safonov 	}
624954f17dSDmitry Safonov 
634954f17dSDmitry Safonov 	return NULL;
644954f17dSDmitry Safonov }
654954f17dSDmitry Safonov 
664954f17dSDmitry Safonov static int ipv4_prefix_cmp(const struct in_addr *addr1,
674954f17dSDmitry Safonov 			   const struct in_addr *addr2,
684954f17dSDmitry Safonov 			   unsigned int prefixlen)
694954f17dSDmitry Safonov {
704954f17dSDmitry Safonov 	__be32 mask = inet_make_mask(prefixlen);
714954f17dSDmitry Safonov 	__be32 a1 = addr1->s_addr & mask;
724954f17dSDmitry Safonov 	__be32 a2 = addr2->s_addr & mask;
734954f17dSDmitry Safonov 
744954f17dSDmitry Safonov 	if (a1 == a2)
754954f17dSDmitry Safonov 		return 0;
764954f17dSDmitry Safonov 	return memcmp(&a1, &a2, sizeof(a1));
774954f17dSDmitry Safonov }
784954f17dSDmitry Safonov 
794954f17dSDmitry Safonov static int __tcp_ao_key_cmp(const struct tcp_ao_key *key,
804954f17dSDmitry Safonov 			    const union tcp_ao_addr *addr, u8 prefixlen,
814954f17dSDmitry Safonov 			    int family, int sndid, int rcvid)
824954f17dSDmitry Safonov {
834954f17dSDmitry Safonov 	if (sndid >= 0 && key->sndid != sndid)
844954f17dSDmitry Safonov 		return (key->sndid > sndid) ? 1 : -1;
854954f17dSDmitry Safonov 	if (rcvid >= 0 && key->rcvid != rcvid)
864954f17dSDmitry Safonov 		return (key->rcvid > rcvid) ? 1 : -1;
874954f17dSDmitry Safonov 
884954f17dSDmitry Safonov 	if (family == AF_UNSPEC)
894954f17dSDmitry Safonov 		return 0;
904954f17dSDmitry Safonov 	if (key->family != family)
914954f17dSDmitry Safonov 		return (key->family > family) ? 1 : -1;
924954f17dSDmitry Safonov 
934954f17dSDmitry Safonov 	if (family == AF_INET) {
944954f17dSDmitry Safonov 		if (ntohl(key->addr.a4.s_addr) == INADDR_ANY)
954954f17dSDmitry Safonov 			return 0;
964954f17dSDmitry Safonov 		if (ntohl(addr->a4.s_addr) == INADDR_ANY)
974954f17dSDmitry Safonov 			return 0;
984954f17dSDmitry Safonov 		return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen);
994954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6)
1004954f17dSDmitry Safonov 	} else {
1014954f17dSDmitry Safonov 		if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6))
1024954f17dSDmitry Safonov 			return 0;
1034954f17dSDmitry Safonov 		if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen))
1044954f17dSDmitry Safonov 			return 0;
1054954f17dSDmitry Safonov 		return memcmp(&key->addr.a6, &addr->a6, sizeof(addr->a6));
1064954f17dSDmitry Safonov #endif
1074954f17dSDmitry Safonov 	}
1084954f17dSDmitry Safonov 	return -1;
1094954f17dSDmitry Safonov }
1104954f17dSDmitry Safonov 
1114954f17dSDmitry Safonov static int tcp_ao_key_cmp(const struct tcp_ao_key *key,
1124954f17dSDmitry Safonov 			  const union tcp_ao_addr *addr, u8 prefixlen,
1134954f17dSDmitry Safonov 			  int family, int sndid, int rcvid)
1144954f17dSDmitry Safonov {
1154954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6)
1164954f17dSDmitry Safonov 	if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) {
1174954f17dSDmitry Safonov 		__be32 addr4 = addr->a6.s6_addr32[3];
1184954f17dSDmitry Safonov 
1194954f17dSDmitry Safonov 		return __tcp_ao_key_cmp(key, (union tcp_ao_addr *)&addr4,
1204954f17dSDmitry Safonov 					prefixlen, AF_INET, sndid, rcvid);
1214954f17dSDmitry Safonov 	}
1224954f17dSDmitry Safonov #endif
1234954f17dSDmitry Safonov 	return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid);
1244954f17dSDmitry Safonov }
1254954f17dSDmitry Safonov 
1264954f17dSDmitry Safonov static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk,
1274954f17dSDmitry Safonov 		const union tcp_ao_addr *addr, int family, u8 prefix,
1284954f17dSDmitry Safonov 		int sndid, int rcvid)
1294954f17dSDmitry Safonov {
1304954f17dSDmitry Safonov 	struct tcp_ao_key *key;
1314954f17dSDmitry Safonov 	struct tcp_ao_info *ao;
1324954f17dSDmitry Safonov 
1334954f17dSDmitry Safonov 	ao = rcu_dereference_check(tcp_sk(sk)->ao_info,
1344954f17dSDmitry Safonov 				   lockdep_sock_is_held(sk));
1354954f17dSDmitry Safonov 	if (!ao)
1364954f17dSDmitry Safonov 		return NULL;
1374954f17dSDmitry Safonov 
1384954f17dSDmitry Safonov 	hlist_for_each_entry_rcu(key, &ao->head, node) {
1394954f17dSDmitry Safonov 		u8 prefixlen = min(prefix, key->prefixlen);
1404954f17dSDmitry Safonov 
1414954f17dSDmitry Safonov 		if (!tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid))
1424954f17dSDmitry Safonov 			return key;
1434954f17dSDmitry Safonov 	}
1444954f17dSDmitry Safonov 	return NULL;
1454954f17dSDmitry Safonov }
1464954f17dSDmitry Safonov 
1470aadc739SDmitry Safonov struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
1480aadc739SDmitry Safonov 				    const union tcp_ao_addr *addr,
1490aadc739SDmitry Safonov 				    int family, int sndid, int rcvid)
1500aadc739SDmitry Safonov {
1510aadc739SDmitry Safonov 	return __tcp_ao_do_lookup(sk, addr, family, U8_MAX, sndid, rcvid);
1520aadc739SDmitry Safonov }
1530aadc739SDmitry Safonov 
1544954f17dSDmitry Safonov static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags)
1554954f17dSDmitry Safonov {
1564954f17dSDmitry Safonov 	struct tcp_ao_info *ao;
1574954f17dSDmitry Safonov 
1584954f17dSDmitry Safonov 	ao = kzalloc(sizeof(*ao), flags);
1594954f17dSDmitry Safonov 	if (!ao)
1604954f17dSDmitry Safonov 		return NULL;
1614954f17dSDmitry Safonov 	INIT_HLIST_HEAD(&ao->head);
1624954f17dSDmitry Safonov 
1634954f17dSDmitry Safonov 	return ao;
1644954f17dSDmitry Safonov }
1654954f17dSDmitry Safonov 
1664954f17dSDmitry Safonov static void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt)
1674954f17dSDmitry Safonov {
1684954f17dSDmitry Safonov 	hlist_add_head_rcu(&mkt->node, &ao->head);
1694954f17dSDmitry Safonov }
1704954f17dSDmitry Safonov 
1714954f17dSDmitry Safonov static void tcp_ao_key_free_rcu(struct rcu_head *head)
1724954f17dSDmitry Safonov {
1734954f17dSDmitry Safonov 	struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu);
1744954f17dSDmitry Safonov 
1754954f17dSDmitry Safonov 	tcp_sigpool_release(key->tcp_sigpool_id);
1764954f17dSDmitry Safonov 	kfree_sensitive(key);
1774954f17dSDmitry Safonov }
1784954f17dSDmitry Safonov 
1794954f17dSDmitry Safonov void tcp_ao_destroy_sock(struct sock *sk)
1804954f17dSDmitry Safonov {
1814954f17dSDmitry Safonov 	struct tcp_ao_info *ao;
1824954f17dSDmitry Safonov 	struct tcp_ao_key *key;
1834954f17dSDmitry Safonov 	struct hlist_node *n;
1844954f17dSDmitry Safonov 
1854954f17dSDmitry Safonov 	ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1);
1864954f17dSDmitry Safonov 	tcp_sk(sk)->ao_info = NULL;
1874954f17dSDmitry Safonov 
1884954f17dSDmitry Safonov 	if (!ao)
1894954f17dSDmitry Safonov 		return;
1904954f17dSDmitry Safonov 
1914954f17dSDmitry Safonov 	hlist_for_each_entry_safe(key, n, &ao->head, node) {
1924954f17dSDmitry Safonov 		hlist_del_rcu(&key->node);
1934954f17dSDmitry Safonov 		atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
1944954f17dSDmitry Safonov 		call_rcu(&key->rcu, tcp_ao_key_free_rcu);
1954954f17dSDmitry Safonov 	}
1964954f17dSDmitry Safonov 
1974954f17dSDmitry Safonov 	kfree_rcu(ao, rcu);
1984954f17dSDmitry Safonov }
1994954f17dSDmitry Safonov 
200*7c2ffaf2SDmitry Safonov /* 4 tuple and ISNs are expected in NBO */
201*7c2ffaf2SDmitry Safonov static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
202*7c2ffaf2SDmitry Safonov 			      __be32 saddr, __be32 daddr,
203*7c2ffaf2SDmitry Safonov 			      __be16 sport, __be16 dport,
204*7c2ffaf2SDmitry Safonov 			      __be32 sisn,  __be32 disn)
205*7c2ffaf2SDmitry Safonov {
206*7c2ffaf2SDmitry Safonov 	/* See RFC5926 3.1.1 */
207*7c2ffaf2SDmitry Safonov 	struct kdf_input_block {
208*7c2ffaf2SDmitry Safonov 		u8                      counter;
209*7c2ffaf2SDmitry Safonov 		u8                      label[6];
210*7c2ffaf2SDmitry Safonov 		struct tcp4_ao_context	ctx;
211*7c2ffaf2SDmitry Safonov 		__be16                  outlen;
212*7c2ffaf2SDmitry Safonov 	} __packed * tmp;
213*7c2ffaf2SDmitry Safonov 	struct tcp_sigpool hp;
214*7c2ffaf2SDmitry Safonov 	int err;
215*7c2ffaf2SDmitry Safonov 
216*7c2ffaf2SDmitry Safonov 	err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp);
217*7c2ffaf2SDmitry Safonov 	if (err)
218*7c2ffaf2SDmitry Safonov 		return err;
219*7c2ffaf2SDmitry Safonov 
220*7c2ffaf2SDmitry Safonov 	tmp = hp.scratch;
221*7c2ffaf2SDmitry Safonov 	tmp->counter	= 1;
222*7c2ffaf2SDmitry Safonov 	memcpy(tmp->label, "TCP-AO", 6);
223*7c2ffaf2SDmitry Safonov 	tmp->ctx.saddr	= saddr;
224*7c2ffaf2SDmitry Safonov 	tmp->ctx.daddr	= daddr;
225*7c2ffaf2SDmitry Safonov 	tmp->ctx.sport	= sport;
226*7c2ffaf2SDmitry Safonov 	tmp->ctx.dport	= dport;
227*7c2ffaf2SDmitry Safonov 	tmp->ctx.sisn	= sisn;
228*7c2ffaf2SDmitry Safonov 	tmp->ctx.disn	= disn;
229*7c2ffaf2SDmitry Safonov 	tmp->outlen	= htons(tcp_ao_digest_size(mkt) * 8); /* in bits */
230*7c2ffaf2SDmitry Safonov 
231*7c2ffaf2SDmitry Safonov 	err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp);
232*7c2ffaf2SDmitry Safonov 	tcp_sigpool_end(&hp);
233*7c2ffaf2SDmitry Safonov 
234*7c2ffaf2SDmitry Safonov 	return err;
235*7c2ffaf2SDmitry Safonov }
236*7c2ffaf2SDmitry Safonov 
237*7c2ffaf2SDmitry Safonov int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
238*7c2ffaf2SDmitry Safonov 			  const struct sock *sk,
239*7c2ffaf2SDmitry Safonov 			  __be32 sisn, __be32 disn, bool send)
240*7c2ffaf2SDmitry Safonov {
241*7c2ffaf2SDmitry Safonov 	if (send)
242*7c2ffaf2SDmitry Safonov 		return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr,
243*7c2ffaf2SDmitry Safonov 					  sk->sk_daddr, htons(sk->sk_num),
244*7c2ffaf2SDmitry Safonov 					  sk->sk_dport, sisn, disn);
245*7c2ffaf2SDmitry Safonov 	else
246*7c2ffaf2SDmitry Safonov 		return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr,
247*7c2ffaf2SDmitry Safonov 					  sk->sk_rcv_saddr, sk->sk_dport,
248*7c2ffaf2SDmitry Safonov 					  htons(sk->sk_num), disn, sisn);
249*7c2ffaf2SDmitry Safonov }
250*7c2ffaf2SDmitry Safonov 
251*7c2ffaf2SDmitry Safonov static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
252*7c2ffaf2SDmitry Safonov 			      const struct sock *sk,
253*7c2ffaf2SDmitry Safonov 			      __be32 sisn, __be32 disn, bool send)
254*7c2ffaf2SDmitry Safonov {
255*7c2ffaf2SDmitry Safonov 	if (mkt->family == AF_INET)
256*7c2ffaf2SDmitry Safonov 		return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
257*7c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6)
258*7c2ffaf2SDmitry Safonov 	else if (mkt->family == AF_INET6)
259*7c2ffaf2SDmitry Safonov 		return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
260*7c2ffaf2SDmitry Safonov #endif
261*7c2ffaf2SDmitry Safonov 	else
262*7c2ffaf2SDmitry Safonov 		return -EOPNOTSUPP;
263*7c2ffaf2SDmitry Safonov }
264*7c2ffaf2SDmitry Safonov 
2650aadc739SDmitry Safonov struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
2660aadc739SDmitry Safonov 				    int sndid, int rcvid)
2670aadc739SDmitry Safonov {
2680aadc739SDmitry Safonov 	union tcp_ao_addr *addr = (union tcp_ao_addr *)&addr_sk->sk_daddr;
2690aadc739SDmitry Safonov 
2700aadc739SDmitry Safonov 	return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid);
2710aadc739SDmitry Safonov }
2720aadc739SDmitry Safonov 
273*7c2ffaf2SDmitry Safonov static int tcp_ao_cache_traffic_keys(const struct sock *sk,
274*7c2ffaf2SDmitry Safonov 				     struct tcp_ao_info *ao,
275*7c2ffaf2SDmitry Safonov 				     struct tcp_ao_key *ao_key)
276*7c2ffaf2SDmitry Safonov {
277*7c2ffaf2SDmitry Safonov 	u8 *traffic_key = snd_other_key(ao_key);
278*7c2ffaf2SDmitry Safonov 	int ret;
279*7c2ffaf2SDmitry Safonov 
280*7c2ffaf2SDmitry Safonov 	ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk,
281*7c2ffaf2SDmitry Safonov 				 ao->lisn, ao->risn, true);
282*7c2ffaf2SDmitry Safonov 	if (ret)
283*7c2ffaf2SDmitry Safonov 		return ret;
284*7c2ffaf2SDmitry Safonov 
285*7c2ffaf2SDmitry Safonov 	traffic_key = rcv_other_key(ao_key);
286*7c2ffaf2SDmitry Safonov 	ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk,
287*7c2ffaf2SDmitry Safonov 				 ao->lisn, ao->risn, false);
288*7c2ffaf2SDmitry Safonov 	return ret;
289*7c2ffaf2SDmitry Safonov }
290*7c2ffaf2SDmitry Safonov 
291*7c2ffaf2SDmitry Safonov void tcp_ao_connect_init(struct sock *sk)
292*7c2ffaf2SDmitry Safonov {
293*7c2ffaf2SDmitry Safonov 	struct tcp_sock *tp = tcp_sk(sk);
294*7c2ffaf2SDmitry Safonov 	struct tcp_ao_info *ao_info;
295*7c2ffaf2SDmitry Safonov 	union tcp_ao_addr *addr;
296*7c2ffaf2SDmitry Safonov 	struct tcp_ao_key *key;
297*7c2ffaf2SDmitry Safonov 	int family;
298*7c2ffaf2SDmitry Safonov 
299*7c2ffaf2SDmitry Safonov 	ao_info = rcu_dereference_protected(tp->ao_info,
300*7c2ffaf2SDmitry Safonov 					    lockdep_sock_is_held(sk));
301*7c2ffaf2SDmitry Safonov 	if (!ao_info)
302*7c2ffaf2SDmitry Safonov 		return;
303*7c2ffaf2SDmitry Safonov 
304*7c2ffaf2SDmitry Safonov 	/* Remove all keys that don't match the peer */
305*7c2ffaf2SDmitry Safonov 	family = sk->sk_family;
306*7c2ffaf2SDmitry Safonov 	if (family == AF_INET)
307*7c2ffaf2SDmitry Safonov 		addr = (union tcp_ao_addr *)&sk->sk_daddr;
308*7c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6)
309*7c2ffaf2SDmitry Safonov 	else if (family == AF_INET6)
310*7c2ffaf2SDmitry Safonov 		addr = (union tcp_ao_addr *)&sk->sk_v6_daddr;
311*7c2ffaf2SDmitry Safonov #endif
312*7c2ffaf2SDmitry Safonov 	else
313*7c2ffaf2SDmitry Safonov 		return;
314*7c2ffaf2SDmitry Safonov 
315*7c2ffaf2SDmitry Safonov 	hlist_for_each_entry_rcu(key, &ao_info->head, node) {
316*7c2ffaf2SDmitry Safonov 		if (!tcp_ao_key_cmp(key, addr, key->prefixlen, family, -1, -1))
317*7c2ffaf2SDmitry Safonov 			continue;
318*7c2ffaf2SDmitry Safonov 
319*7c2ffaf2SDmitry Safonov 		if (key == ao_info->current_key)
320*7c2ffaf2SDmitry Safonov 			ao_info->current_key = NULL;
321*7c2ffaf2SDmitry Safonov 		if (key == ao_info->rnext_key)
322*7c2ffaf2SDmitry Safonov 			ao_info->rnext_key = NULL;
323*7c2ffaf2SDmitry Safonov 		hlist_del_rcu(&key->node);
324*7c2ffaf2SDmitry Safonov 		atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
325*7c2ffaf2SDmitry Safonov 		call_rcu(&key->rcu, tcp_ao_key_free_rcu);
326*7c2ffaf2SDmitry Safonov 	}
327*7c2ffaf2SDmitry Safonov 
328*7c2ffaf2SDmitry Safonov 	key = tp->af_specific->ao_lookup(sk, sk, -1, -1);
329*7c2ffaf2SDmitry Safonov 	if (key) {
330*7c2ffaf2SDmitry Safonov 		/* if current_key or rnext_key were not provided,
331*7c2ffaf2SDmitry Safonov 		 * use the first key matching the peer
332*7c2ffaf2SDmitry Safonov 		 */
333*7c2ffaf2SDmitry Safonov 		if (!ao_info->current_key)
334*7c2ffaf2SDmitry Safonov 			ao_info->current_key = key;
335*7c2ffaf2SDmitry Safonov 		if (!ao_info->rnext_key)
336*7c2ffaf2SDmitry Safonov 			ao_info->rnext_key = key;
337*7c2ffaf2SDmitry Safonov 		tp->tcp_header_len += tcp_ao_len(key);
338*7c2ffaf2SDmitry Safonov 
339*7c2ffaf2SDmitry Safonov 		ao_info->lisn = htonl(tp->write_seq);
340*7c2ffaf2SDmitry Safonov 	} else {
341*7c2ffaf2SDmitry Safonov 		/* Can't happen: tcp_connect() verifies that there's
342*7c2ffaf2SDmitry Safonov 		 * at least one tcp-ao key that matches the remote peer.
343*7c2ffaf2SDmitry Safonov 		 */
344*7c2ffaf2SDmitry Safonov 		WARN_ON_ONCE(1);
345*7c2ffaf2SDmitry Safonov 		rcu_assign_pointer(tp->ao_info, NULL);
346*7c2ffaf2SDmitry Safonov 		kfree(ao_info);
347*7c2ffaf2SDmitry Safonov 	}
348*7c2ffaf2SDmitry Safonov }
349*7c2ffaf2SDmitry Safonov 
350*7c2ffaf2SDmitry Safonov void tcp_ao_established(struct sock *sk)
351*7c2ffaf2SDmitry Safonov {
352*7c2ffaf2SDmitry Safonov 	struct tcp_ao_info *ao;
353*7c2ffaf2SDmitry Safonov 	struct tcp_ao_key *key;
354*7c2ffaf2SDmitry Safonov 
355*7c2ffaf2SDmitry Safonov 	ao = rcu_dereference_protected(tcp_sk(sk)->ao_info,
356*7c2ffaf2SDmitry Safonov 				       lockdep_sock_is_held(sk));
357*7c2ffaf2SDmitry Safonov 	if (!ao)
358*7c2ffaf2SDmitry Safonov 		return;
359*7c2ffaf2SDmitry Safonov 
360*7c2ffaf2SDmitry Safonov 	hlist_for_each_entry_rcu(key, &ao->head, node)
361*7c2ffaf2SDmitry Safonov 		tcp_ao_cache_traffic_keys(sk, ao, key);
362*7c2ffaf2SDmitry Safonov }
363*7c2ffaf2SDmitry Safonov 
364*7c2ffaf2SDmitry Safonov void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
365*7c2ffaf2SDmitry Safonov {
366*7c2ffaf2SDmitry Safonov 	struct tcp_ao_info *ao;
367*7c2ffaf2SDmitry Safonov 	struct tcp_ao_key *key;
368*7c2ffaf2SDmitry Safonov 
369*7c2ffaf2SDmitry Safonov 	ao = rcu_dereference_protected(tcp_sk(sk)->ao_info,
370*7c2ffaf2SDmitry Safonov 				       lockdep_sock_is_held(sk));
371*7c2ffaf2SDmitry Safonov 	if (!ao)
372*7c2ffaf2SDmitry Safonov 		return;
373*7c2ffaf2SDmitry Safonov 
374*7c2ffaf2SDmitry Safonov 	WRITE_ONCE(ao->risn, tcp_hdr(skb)->seq);
375*7c2ffaf2SDmitry Safonov 
376*7c2ffaf2SDmitry Safonov 	hlist_for_each_entry_rcu(key, &ao->head, node)
377*7c2ffaf2SDmitry Safonov 		tcp_ao_cache_traffic_keys(sk, ao, key);
378*7c2ffaf2SDmitry Safonov }
379*7c2ffaf2SDmitry Safonov 
3804954f17dSDmitry Safonov static bool tcp_ao_can_set_current_rnext(struct sock *sk)
3814954f17dSDmitry Safonov {
3824954f17dSDmitry Safonov 	/* There aren't current/rnext keys on TCP_LISTEN sockets */
3834954f17dSDmitry Safonov 	if (sk->sk_state == TCP_LISTEN)
3844954f17dSDmitry Safonov 		return false;
3854954f17dSDmitry Safonov 	return true;
3864954f17dSDmitry Safonov }
3874954f17dSDmitry Safonov 
3884954f17dSDmitry Safonov static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao_add *cmd,
3894954f17dSDmitry Safonov 			      union tcp_ao_addr **addr)
3904954f17dSDmitry Safonov {
3914954f17dSDmitry Safonov 	struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->addr;
3924954f17dSDmitry Safonov 	struct inet_sock *inet = inet_sk(sk);
3934954f17dSDmitry Safonov 
3944954f17dSDmitry Safonov 	if (sin->sin_family != AF_INET)
3954954f17dSDmitry Safonov 		return -EINVAL;
3964954f17dSDmitry Safonov 
3974954f17dSDmitry Safonov 	/* Currently matching is not performed on port (or port ranges) */
3984954f17dSDmitry Safonov 	if (sin->sin_port != 0)
3994954f17dSDmitry Safonov 		return -EINVAL;
4004954f17dSDmitry Safonov 
4014954f17dSDmitry Safonov 	/* Check prefix and trailing 0's in addr */
4024954f17dSDmitry Safonov 	if (cmd->prefix != 0) {
4034954f17dSDmitry Safonov 		__be32 mask;
4044954f17dSDmitry Safonov 
4054954f17dSDmitry Safonov 		if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY)
4064954f17dSDmitry Safonov 			return -EINVAL;
4074954f17dSDmitry Safonov 		if (cmd->prefix > 32)
4084954f17dSDmitry Safonov 			return -EINVAL;
4094954f17dSDmitry Safonov 
4104954f17dSDmitry Safonov 		mask = inet_make_mask(cmd->prefix);
4114954f17dSDmitry Safonov 		if (sin->sin_addr.s_addr & ~mask)
4124954f17dSDmitry Safonov 			return -EINVAL;
4134954f17dSDmitry Safonov 
4144954f17dSDmitry Safonov 		/* Check that MKT address is consistent with socket */
4154954f17dSDmitry Safonov 		if (ntohl(inet->inet_daddr) != INADDR_ANY &&
4164954f17dSDmitry Safonov 		    (inet->inet_daddr & mask) != sin->sin_addr.s_addr)
4174954f17dSDmitry Safonov 			return -EINVAL;
4184954f17dSDmitry Safonov 	} else {
4194954f17dSDmitry Safonov 		if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY)
4204954f17dSDmitry Safonov 			return -EINVAL;
4214954f17dSDmitry Safonov 	}
4224954f17dSDmitry Safonov 
4234954f17dSDmitry Safonov 	*addr = (union tcp_ao_addr *)&sin->sin_addr;
4244954f17dSDmitry Safonov 	return 0;
4254954f17dSDmitry Safonov }
4264954f17dSDmitry Safonov 
4274954f17dSDmitry Safonov static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key)
4284954f17dSDmitry Safonov {
4294954f17dSDmitry Safonov 	unsigned int syn_tcp_option_space;
4304954f17dSDmitry Safonov 	bool is_kdf_aes_128_cmac = false;
4314954f17dSDmitry Safonov 	struct crypto_ahash *tfm;
4324954f17dSDmitry Safonov 	struct tcp_sigpool hp;
4334954f17dSDmitry Safonov 	void *tmp_key = NULL;
4344954f17dSDmitry Safonov 	int err;
4354954f17dSDmitry Safonov 
4364954f17dSDmitry Safonov 	/* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */
4374954f17dSDmitry Safonov 	if (!strcmp("cmac(aes128)", cmd->alg_name)) {
4384954f17dSDmitry Safonov 		strscpy(cmd->alg_name, "cmac(aes)", sizeof(cmd->alg_name));
4394954f17dSDmitry Safonov 		is_kdf_aes_128_cmac = (cmd->keylen != 16);
4404954f17dSDmitry Safonov 		tmp_key = kmalloc(cmd->keylen, GFP_KERNEL);
4414954f17dSDmitry Safonov 		if (!tmp_key)
4424954f17dSDmitry Safonov 			return -ENOMEM;
4434954f17dSDmitry Safonov 	}
4444954f17dSDmitry Safonov 
4454954f17dSDmitry Safonov 	key->maclen = cmd->maclen ?: 12; /* 12 is the default in RFC5925 */
4464954f17dSDmitry Safonov 
4474954f17dSDmitry Safonov 	/* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss
4484954f17dSDmitry Safonov 	 *					- tstamp - wscale - sackperm),
4494954f17dSDmitry Safonov 	 * see tcp_syn_options(), tcp_synack_options(), commit 33ad798c924b.
4504954f17dSDmitry Safonov 	 *
4514954f17dSDmitry Safonov 	 * In order to allow D-SACK with TCP-AO, the header size should be:
4524954f17dSDmitry Safonov 	 * (MAX_TCP_OPTION_SPACE - TCPOLEN_TSTAMP_ALIGNED
4534954f17dSDmitry Safonov 	 *			- TCPOLEN_SACK_BASE_ALIGNED
4544954f17dSDmitry Safonov 	 *			- 2 * TCPOLEN_SACK_PERBLOCK) = 8 (maclen = 4),
4554954f17dSDmitry Safonov 	 * see tcp_established_options().
4564954f17dSDmitry Safonov 	 *
4574954f17dSDmitry Safonov 	 * RFC5925, 2.2:
4584954f17dSDmitry Safonov 	 * Typical MACs are 96-128 bits (12-16 bytes), but any length
4594954f17dSDmitry Safonov 	 * that fits in the header of the segment being authenticated
4604954f17dSDmitry Safonov 	 * is allowed.
4614954f17dSDmitry Safonov 	 *
4624954f17dSDmitry Safonov 	 * RFC5925, 7.6:
4634954f17dSDmitry Safonov 	 * TCP-AO continues to consume 16 bytes in non-SYN segments,
4644954f17dSDmitry Safonov 	 * leaving a total of 24 bytes for other options, of which
4654954f17dSDmitry Safonov 	 * the timestamp consumes 10.  This leaves 14 bytes, of which 10
4664954f17dSDmitry Safonov 	 * are used for a single SACK block. When two SACK blocks are used,
4674954f17dSDmitry Safonov 	 * such as to handle D-SACK, a smaller TCP-AO MAC would be required
4684954f17dSDmitry Safonov 	 * to make room for the additional SACK block (i.e., to leave 18
4694954f17dSDmitry Safonov 	 * bytes for the D-SACK variant of the SACK option) [RFC2883].
4704954f17dSDmitry Safonov 	 * Note that D-SACK is not supportable in TCP MD5 in the presence
4714954f17dSDmitry Safonov 	 * of timestamps, because TCP MD5’s MAC length is fixed and too
4724954f17dSDmitry Safonov 	 * large to leave sufficient option space.
4734954f17dSDmitry Safonov 	 */
4744954f17dSDmitry Safonov 	syn_tcp_option_space = MAX_TCP_OPTION_SPACE;
4754954f17dSDmitry Safonov 	syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED;
4764954f17dSDmitry Safonov 	syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED;
4774954f17dSDmitry Safonov 	syn_tcp_option_space -= TCPOLEN_SACKPERM_ALIGNED;
4784954f17dSDmitry Safonov 	if (tcp_ao_len(key) > syn_tcp_option_space) {
4794954f17dSDmitry Safonov 		err = -EMSGSIZE;
4804954f17dSDmitry Safonov 		goto err_kfree;
4814954f17dSDmitry Safonov 	}
4824954f17dSDmitry Safonov 
4834954f17dSDmitry Safonov 	key->keylen = cmd->keylen;
4844954f17dSDmitry Safonov 	memcpy(key->key, cmd->key, cmd->keylen);
4854954f17dSDmitry Safonov 
4864954f17dSDmitry Safonov 	err = tcp_sigpool_start(key->tcp_sigpool_id, &hp);
4874954f17dSDmitry Safonov 	if (err)
4884954f17dSDmitry Safonov 		goto err_kfree;
4894954f17dSDmitry Safonov 
4904954f17dSDmitry Safonov 	tfm = crypto_ahash_reqtfm(hp.req);
4914954f17dSDmitry Safonov 	if (is_kdf_aes_128_cmac) {
4924954f17dSDmitry Safonov 		void *scratch = hp.scratch;
4934954f17dSDmitry Safonov 		struct scatterlist sg;
4944954f17dSDmitry Safonov 
4954954f17dSDmitry Safonov 		memcpy(tmp_key, cmd->key, cmd->keylen);
4964954f17dSDmitry Safonov 		sg_init_one(&sg, tmp_key, cmd->keylen);
4974954f17dSDmitry Safonov 
4984954f17dSDmitry Safonov 		/* Using zero-key of 16 bytes as described in RFC5926 */
4994954f17dSDmitry Safonov 		memset(scratch, 0, 16);
5004954f17dSDmitry Safonov 		err = crypto_ahash_setkey(tfm, scratch, 16);
5014954f17dSDmitry Safonov 		if (err)
5024954f17dSDmitry Safonov 			goto err_pool_end;
5034954f17dSDmitry Safonov 
5044954f17dSDmitry Safonov 		err = crypto_ahash_init(hp.req);
5054954f17dSDmitry Safonov 		if (err)
5064954f17dSDmitry Safonov 			goto err_pool_end;
5074954f17dSDmitry Safonov 
5084954f17dSDmitry Safonov 		ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen);
5094954f17dSDmitry Safonov 		err = crypto_ahash_update(hp.req);
5104954f17dSDmitry Safonov 		if (err)
5114954f17dSDmitry Safonov 			goto err_pool_end;
5124954f17dSDmitry Safonov 
5134954f17dSDmitry Safonov 		err |= crypto_ahash_final(hp.req);
5144954f17dSDmitry Safonov 		if (err)
5154954f17dSDmitry Safonov 			goto err_pool_end;
5164954f17dSDmitry Safonov 		key->keylen = 16;
5174954f17dSDmitry Safonov 	}
5184954f17dSDmitry Safonov 
5194954f17dSDmitry Safonov 	err = crypto_ahash_setkey(tfm, key->key, key->keylen);
5204954f17dSDmitry Safonov 	if (err)
5214954f17dSDmitry Safonov 		goto err_pool_end;
5224954f17dSDmitry Safonov 
5234954f17dSDmitry Safonov 	tcp_sigpool_end(&hp);
5244954f17dSDmitry Safonov 	kfree_sensitive(tmp_key);
5254954f17dSDmitry Safonov 
5264954f17dSDmitry Safonov 	if (tcp_ao_maclen(key) > key->digest_size)
5274954f17dSDmitry Safonov 		return -EINVAL;
5284954f17dSDmitry Safonov 
5294954f17dSDmitry Safonov 	return 0;
5304954f17dSDmitry Safonov 
5314954f17dSDmitry Safonov err_pool_end:
5324954f17dSDmitry Safonov 	tcp_sigpool_end(&hp);
5334954f17dSDmitry Safonov err_kfree:
5344954f17dSDmitry Safonov 	kfree_sensitive(tmp_key);
5354954f17dSDmitry Safonov 	return err;
5364954f17dSDmitry Safonov }
5374954f17dSDmitry Safonov 
5384954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6)
5394954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd,
5404954f17dSDmitry Safonov 			      union tcp_ao_addr **paddr,
5414954f17dSDmitry Safonov 			      unsigned short int *family)
5424954f17dSDmitry Safonov {
5434954f17dSDmitry Safonov 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->addr;
5444954f17dSDmitry Safonov 	struct in6_addr *addr = &sin6->sin6_addr;
5454954f17dSDmitry Safonov 	u8 prefix = cmd->prefix;
5464954f17dSDmitry Safonov 
5474954f17dSDmitry Safonov 	if (sin6->sin6_family != AF_INET6)
5484954f17dSDmitry Safonov 		return -EINVAL;
5494954f17dSDmitry Safonov 
5504954f17dSDmitry Safonov 	/* Currently matching is not performed on port (or port ranges) */
5514954f17dSDmitry Safonov 	if (sin6->sin6_port != 0)
5524954f17dSDmitry Safonov 		return -EINVAL;
5534954f17dSDmitry Safonov 
5544954f17dSDmitry Safonov 	/* Check prefix and trailing 0's in addr */
5554954f17dSDmitry Safonov 	if (cmd->prefix != 0 && ipv6_addr_v4mapped(addr)) {
5564954f17dSDmitry Safonov 		__be32 addr4 = addr->s6_addr32[3];
5574954f17dSDmitry Safonov 		__be32 mask;
5584954f17dSDmitry Safonov 
5594954f17dSDmitry Safonov 		if (prefix > 32 || ntohl(addr4) == INADDR_ANY)
5604954f17dSDmitry Safonov 			return -EINVAL;
5614954f17dSDmitry Safonov 
5624954f17dSDmitry Safonov 		mask = inet_make_mask(prefix);
5634954f17dSDmitry Safonov 		if (addr4 & ~mask)
5644954f17dSDmitry Safonov 			return -EINVAL;
5654954f17dSDmitry Safonov 
5664954f17dSDmitry Safonov 		/* Check that MKT address is consistent with socket */
5674954f17dSDmitry Safonov 		if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
5684954f17dSDmitry Safonov 			__be32 daddr4 = sk->sk_v6_daddr.s6_addr32[3];
5694954f17dSDmitry Safonov 
5704954f17dSDmitry Safonov 			if (!ipv6_addr_v4mapped(&sk->sk_v6_daddr))
5714954f17dSDmitry Safonov 				return -EINVAL;
5724954f17dSDmitry Safonov 			if ((daddr4 & mask) != addr4)
5734954f17dSDmitry Safonov 				return -EINVAL;
5744954f17dSDmitry Safonov 		}
5754954f17dSDmitry Safonov 
5764954f17dSDmitry Safonov 		*paddr = (union tcp_ao_addr *)&addr->s6_addr32[3];
5774954f17dSDmitry Safonov 		*family = AF_INET;
5784954f17dSDmitry Safonov 		return 0;
5794954f17dSDmitry Safonov 	} else if (cmd->prefix != 0) {
5804954f17dSDmitry Safonov 		struct in6_addr pfx;
5814954f17dSDmitry Safonov 
5824954f17dSDmitry Safonov 		if (ipv6_addr_any(addr) || prefix > 128)
5834954f17dSDmitry Safonov 			return -EINVAL;
5844954f17dSDmitry Safonov 
5854954f17dSDmitry Safonov 		ipv6_addr_prefix(&pfx, addr, prefix);
5864954f17dSDmitry Safonov 		if (ipv6_addr_cmp(&pfx, addr))
5874954f17dSDmitry Safonov 			return -EINVAL;
5884954f17dSDmitry Safonov 
5894954f17dSDmitry Safonov 		/* Check that MKT address is consistent with socket */
5904954f17dSDmitry Safonov 		if (!ipv6_addr_any(&sk->sk_v6_daddr) &&
5914954f17dSDmitry Safonov 		    !ipv6_prefix_equal(&sk->sk_v6_daddr, addr, prefix))
5924954f17dSDmitry Safonov 
5934954f17dSDmitry Safonov 			return -EINVAL;
5944954f17dSDmitry Safonov 	} else {
5954954f17dSDmitry Safonov 		if (!ipv6_addr_any(addr))
5964954f17dSDmitry Safonov 			return -EINVAL;
5974954f17dSDmitry Safonov 	}
5984954f17dSDmitry Safonov 
5994954f17dSDmitry Safonov 	*paddr = (union tcp_ao_addr *)addr;
6004954f17dSDmitry Safonov 	return 0;
6014954f17dSDmitry Safonov }
6024954f17dSDmitry Safonov #else
6034954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd,
6044954f17dSDmitry Safonov 			      union tcp_ao_addr **paddr,
6054954f17dSDmitry Safonov 			      unsigned short int *family)
6064954f17dSDmitry Safonov {
6074954f17dSDmitry Safonov 	return -EOPNOTSUPP;
6084954f17dSDmitry Safonov }
6094954f17dSDmitry Safonov #endif
6104954f17dSDmitry Safonov 
6114954f17dSDmitry Safonov static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk)
6124954f17dSDmitry Safonov {
6134954f17dSDmitry Safonov 	if (sk_fullsock(sk)) {
6144954f17dSDmitry Safonov 		return rcu_dereference_protected(tcp_sk(sk)->ao_info,
6154954f17dSDmitry Safonov 						 lockdep_sock_is_held(sk));
6164954f17dSDmitry Safonov 	}
6174954f17dSDmitry Safonov 	return ERR_PTR(-ESOCKTNOSUPPORT);
6184954f17dSDmitry Safonov }
6194954f17dSDmitry Safonov 
6204954f17dSDmitry Safonov #define TCP_AO_KEYF_ALL		(0)
6214954f17dSDmitry Safonov 
6224954f17dSDmitry Safonov static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk,
6234954f17dSDmitry Safonov 					   struct tcp_ao_add *cmd)
6244954f17dSDmitry Safonov {
6254954f17dSDmitry Safonov 	const char *algo = cmd->alg_name;
6264954f17dSDmitry Safonov 	unsigned int digest_size;
6274954f17dSDmitry Safonov 	struct crypto_ahash *tfm;
6284954f17dSDmitry Safonov 	struct tcp_ao_key *key;
6294954f17dSDmitry Safonov 	struct tcp_sigpool hp;
6304954f17dSDmitry Safonov 	int err, pool_id;
6314954f17dSDmitry Safonov 	size_t size;
6324954f17dSDmitry Safonov 
6334954f17dSDmitry Safonov 	/* Force null-termination of alg_name */
6344954f17dSDmitry Safonov 	cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] = '\0';
6354954f17dSDmitry Safonov 
6364954f17dSDmitry Safonov 	/* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */
6374954f17dSDmitry Safonov 	if (!strcmp("cmac(aes128)", algo))
6384954f17dSDmitry Safonov 		algo = "cmac(aes)";
6394954f17dSDmitry Safonov 
6404954f17dSDmitry Safonov 	/* Full TCP header (th->doff << 2) should fit into scratch area,
6414954f17dSDmitry Safonov 	 * see tcp_ao_hash_header().
6424954f17dSDmitry Safonov 	 */
6434954f17dSDmitry Safonov 	pool_id = tcp_sigpool_alloc_ahash(algo, 60);
6444954f17dSDmitry Safonov 	if (pool_id < 0)
6454954f17dSDmitry Safonov 		return ERR_PTR(pool_id);
6464954f17dSDmitry Safonov 
6474954f17dSDmitry Safonov 	err = tcp_sigpool_start(pool_id, &hp);
6484954f17dSDmitry Safonov 	if (err)
6494954f17dSDmitry Safonov 		goto err_free_pool;
6504954f17dSDmitry Safonov 
6514954f17dSDmitry Safonov 	tfm = crypto_ahash_reqtfm(hp.req);
6524954f17dSDmitry Safonov 	if (crypto_ahash_alignmask(tfm) > TCP_AO_KEY_ALIGN) {
6534954f17dSDmitry Safonov 		err = -EOPNOTSUPP;
6544954f17dSDmitry Safonov 		goto err_pool_end;
6554954f17dSDmitry Safonov 	}
6564954f17dSDmitry Safonov 	digest_size = crypto_ahash_digestsize(tfm);
6574954f17dSDmitry Safonov 	tcp_sigpool_end(&hp);
6584954f17dSDmitry Safonov 
6594954f17dSDmitry Safonov 	size = sizeof(struct tcp_ao_key) + (digest_size << 1);
6604954f17dSDmitry Safonov 	key = sock_kmalloc(sk, size, GFP_KERNEL);
6614954f17dSDmitry Safonov 	if (!key) {
6624954f17dSDmitry Safonov 		err = -ENOMEM;
6634954f17dSDmitry Safonov 		goto err_free_pool;
6644954f17dSDmitry Safonov 	}
6654954f17dSDmitry Safonov 
6664954f17dSDmitry Safonov 	key->tcp_sigpool_id = pool_id;
6674954f17dSDmitry Safonov 	key->digest_size = digest_size;
6684954f17dSDmitry Safonov 	return key;
6694954f17dSDmitry Safonov 
6704954f17dSDmitry Safonov err_pool_end:
6714954f17dSDmitry Safonov 	tcp_sigpool_end(&hp);
6724954f17dSDmitry Safonov err_free_pool:
6734954f17dSDmitry Safonov 	tcp_sigpool_release(pool_id);
6744954f17dSDmitry Safonov 	return ERR_PTR(err);
6754954f17dSDmitry Safonov }
6764954f17dSDmitry Safonov 
6774954f17dSDmitry Safonov static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
6784954f17dSDmitry Safonov 			  sockptr_t optval, int optlen)
6794954f17dSDmitry Safonov {
6804954f17dSDmitry Safonov 	struct tcp_ao_info *ao_info;
6814954f17dSDmitry Safonov 	union tcp_ao_addr *addr;
6824954f17dSDmitry Safonov 	struct tcp_ao_key *key;
6834954f17dSDmitry Safonov 	struct tcp_ao_add cmd;
6844954f17dSDmitry Safonov 	bool first = false;
6854954f17dSDmitry Safonov 	int ret;
6864954f17dSDmitry Safonov 
6874954f17dSDmitry Safonov 	if (optlen < sizeof(cmd))
6884954f17dSDmitry Safonov 		return -EINVAL;
6894954f17dSDmitry Safonov 
6904954f17dSDmitry Safonov 	ret = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen);
6914954f17dSDmitry Safonov 	if (ret)
6924954f17dSDmitry Safonov 		return ret;
6934954f17dSDmitry Safonov 
6944954f17dSDmitry Safonov 	if (cmd.keylen > TCP_AO_MAXKEYLEN)
6954954f17dSDmitry Safonov 		return -EINVAL;
6964954f17dSDmitry Safonov 
6974954f17dSDmitry Safonov 	if (cmd.reserved != 0 || cmd.reserved2 != 0)
6984954f17dSDmitry Safonov 		return -EINVAL;
6994954f17dSDmitry Safonov 
7004954f17dSDmitry Safonov 	if (family == AF_INET)
7014954f17dSDmitry Safonov 		ret = tcp_ao_verify_ipv4(sk, &cmd, &addr);
7024954f17dSDmitry Safonov 	else
7034954f17dSDmitry Safonov 		ret = tcp_ao_verify_ipv6(sk, &cmd, &addr, &family);
7044954f17dSDmitry Safonov 	if (ret)
7054954f17dSDmitry Safonov 		return ret;
7064954f17dSDmitry Safonov 
7074954f17dSDmitry Safonov 	if (cmd.keyflags & ~TCP_AO_KEYF_ALL)
7084954f17dSDmitry Safonov 		return -EINVAL;
7094954f17dSDmitry Safonov 
7104954f17dSDmitry Safonov 	if (cmd.set_current || cmd.set_rnext) {
7114954f17dSDmitry Safonov 		if (!tcp_ao_can_set_current_rnext(sk))
7124954f17dSDmitry Safonov 			return -EINVAL;
7134954f17dSDmitry Safonov 	}
7144954f17dSDmitry Safonov 
7150aadc739SDmitry Safonov 	/* Don't allow keys for peers that have a matching TCP-MD5 key */
7160aadc739SDmitry Safonov 	if (tcp_md5_do_lookup_any_l3index(sk, addr, family))
7170aadc739SDmitry Safonov 		return -EKEYREJECTED;
7180aadc739SDmitry Safonov 
7194954f17dSDmitry Safonov 	ao_info = setsockopt_ao_info(sk);
7204954f17dSDmitry Safonov 	if (IS_ERR(ao_info))
7214954f17dSDmitry Safonov 		return PTR_ERR(ao_info);
7224954f17dSDmitry Safonov 
7234954f17dSDmitry Safonov 	if (!ao_info) {
7244954f17dSDmitry Safonov 		ao_info = tcp_ao_alloc_info(GFP_KERNEL);
7254954f17dSDmitry Safonov 		if (!ao_info)
7264954f17dSDmitry Safonov 			return -ENOMEM;
7274954f17dSDmitry Safonov 		first = true;
7284954f17dSDmitry Safonov 	} else {
7294954f17dSDmitry Safonov 		/* Check that neither RecvID nor SendID match any
7304954f17dSDmitry Safonov 		 * existing key for the peer, RFC5925 3.1:
7314954f17dSDmitry Safonov 		 * > The IDs of MKTs MUST NOT overlap where their
7324954f17dSDmitry Safonov 		 * > TCP connection identifiers overlap.
7334954f17dSDmitry Safonov 		 */
7344954f17dSDmitry Safonov 		if (__tcp_ao_do_lookup(sk, addr, family,
7354954f17dSDmitry Safonov 				       cmd.prefix, -1, cmd.rcvid))
7364954f17dSDmitry Safonov 			return -EEXIST;
7374954f17dSDmitry Safonov 		if (__tcp_ao_do_lookup(sk, addr, family,
7384954f17dSDmitry Safonov 				       cmd.prefix, cmd.sndid, -1))
7394954f17dSDmitry Safonov 			return -EEXIST;
7404954f17dSDmitry Safonov 	}
7414954f17dSDmitry Safonov 
7424954f17dSDmitry Safonov 	key = tcp_ao_key_alloc(sk, &cmd);
7434954f17dSDmitry Safonov 	if (IS_ERR(key)) {
7444954f17dSDmitry Safonov 		ret = PTR_ERR(key);
7454954f17dSDmitry Safonov 		goto err_free_ao;
7464954f17dSDmitry Safonov 	}
7474954f17dSDmitry Safonov 
7484954f17dSDmitry Safonov 	INIT_HLIST_NODE(&key->node);
7494954f17dSDmitry Safonov 	memcpy(&key->addr, addr, (family == AF_INET) ? sizeof(struct in_addr) :
7504954f17dSDmitry Safonov 						       sizeof(struct in6_addr));
7514954f17dSDmitry Safonov 	key->prefixlen	= cmd.prefix;
7524954f17dSDmitry Safonov 	key->family	= family;
7534954f17dSDmitry Safonov 	key->keyflags	= cmd.keyflags;
7544954f17dSDmitry Safonov 	key->sndid	= cmd.sndid;
7554954f17dSDmitry Safonov 	key->rcvid	= cmd.rcvid;
7564954f17dSDmitry Safonov 
7574954f17dSDmitry Safonov 	ret = tcp_ao_parse_crypto(&cmd, key);
7584954f17dSDmitry Safonov 	if (ret < 0)
7594954f17dSDmitry Safonov 		goto err_free_sock;
7604954f17dSDmitry Safonov 
761*7c2ffaf2SDmitry Safonov 	/* Change this condition if we allow adding keys in states
762*7c2ffaf2SDmitry Safonov 	 * like close_wait, syn_sent or fin_wait...
763*7c2ffaf2SDmitry Safonov 	 */
764*7c2ffaf2SDmitry Safonov 	if (sk->sk_state == TCP_ESTABLISHED)
765*7c2ffaf2SDmitry Safonov 		tcp_ao_cache_traffic_keys(sk, ao_info, key);
766*7c2ffaf2SDmitry Safonov 
7674954f17dSDmitry Safonov 	tcp_ao_link_mkt(ao_info, key);
7684954f17dSDmitry Safonov 	if (first) {
7694954f17dSDmitry Safonov 		sk_gso_disable(sk);
7704954f17dSDmitry Safonov 		rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info);
7714954f17dSDmitry Safonov 	}
7724954f17dSDmitry Safonov 
7734954f17dSDmitry Safonov 	if (cmd.set_current)
7744954f17dSDmitry Safonov 		WRITE_ONCE(ao_info->current_key, key);
7754954f17dSDmitry Safonov 	if (cmd.set_rnext)
7764954f17dSDmitry Safonov 		WRITE_ONCE(ao_info->rnext_key, key);
7774954f17dSDmitry Safonov 	return 0;
7784954f17dSDmitry Safonov 
7794954f17dSDmitry Safonov err_free_sock:
7804954f17dSDmitry Safonov 	atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
7814954f17dSDmitry Safonov 	tcp_sigpool_release(key->tcp_sigpool_id);
7824954f17dSDmitry Safonov 	kfree_sensitive(key);
7834954f17dSDmitry Safonov err_free_ao:
7844954f17dSDmitry Safonov 	if (first)
7854954f17dSDmitry Safonov 		kfree(ao_info);
7864954f17dSDmitry Safonov 	return ret;
7874954f17dSDmitry Safonov }
7884954f17dSDmitry Safonov 
7894954f17dSDmitry Safonov static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info,
7904954f17dSDmitry Safonov 			     struct tcp_ao_key *key,
7914954f17dSDmitry Safonov 			     struct tcp_ao_key *new_current,
7924954f17dSDmitry Safonov 			     struct tcp_ao_key *new_rnext)
7934954f17dSDmitry Safonov {
7944954f17dSDmitry Safonov 	int err;
7954954f17dSDmitry Safonov 
7964954f17dSDmitry Safonov 	hlist_del_rcu(&key->node);
7974954f17dSDmitry Safonov 
7984954f17dSDmitry Safonov 	/* At this moment another CPU could have looked this key up
7994954f17dSDmitry Safonov 	 * while it was unlinked from the list. Wait for RCU grace period,
8004954f17dSDmitry Safonov 	 * after which the key is off-list and can't be looked up again;
8014954f17dSDmitry Safonov 	 * the rx path [just before RCU came] might have used it and set it
8024954f17dSDmitry Safonov 	 * as current_key (very unlikely).
8034954f17dSDmitry Safonov 	 */
8044954f17dSDmitry Safonov 	synchronize_rcu();
8054954f17dSDmitry Safonov 	if (new_current)
8064954f17dSDmitry Safonov 		WRITE_ONCE(ao_info->current_key, new_current);
8074954f17dSDmitry Safonov 	if (new_rnext)
8084954f17dSDmitry Safonov 		WRITE_ONCE(ao_info->rnext_key, new_rnext);
8094954f17dSDmitry Safonov 
8104954f17dSDmitry Safonov 	if (unlikely(READ_ONCE(ao_info->current_key) == key ||
8114954f17dSDmitry Safonov 		     READ_ONCE(ao_info->rnext_key) == key)) {
8124954f17dSDmitry Safonov 		err = -EBUSY;
8134954f17dSDmitry Safonov 		goto add_key;
8144954f17dSDmitry Safonov 	}
8154954f17dSDmitry Safonov 
8164954f17dSDmitry Safonov 	atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
8174954f17dSDmitry Safonov 	call_rcu(&key->rcu, tcp_ao_key_free_rcu);
8184954f17dSDmitry Safonov 
8194954f17dSDmitry Safonov 	return 0;
8204954f17dSDmitry Safonov add_key:
8214954f17dSDmitry Safonov 	hlist_add_head_rcu(&key->node, &ao_info->head);
8224954f17dSDmitry Safonov 	return err;
8234954f17dSDmitry Safonov }
8244954f17dSDmitry Safonov 
8254954f17dSDmitry Safonov static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family,
8264954f17dSDmitry Safonov 			  sockptr_t optval, int optlen)
8274954f17dSDmitry Safonov {
8284954f17dSDmitry Safonov 	struct tcp_ao_key *key, *new_current = NULL, *new_rnext = NULL;
8294954f17dSDmitry Safonov 	struct tcp_ao_info *ao_info;
8304954f17dSDmitry Safonov 	union tcp_ao_addr *addr;
8314954f17dSDmitry Safonov 	struct tcp_ao_del cmd;
8324954f17dSDmitry Safonov 	int addr_len;
8334954f17dSDmitry Safonov 	__u8 prefix;
8344954f17dSDmitry Safonov 	u16 port;
8354954f17dSDmitry Safonov 	int err;
8364954f17dSDmitry Safonov 
8374954f17dSDmitry Safonov 	if (optlen < sizeof(cmd))
8384954f17dSDmitry Safonov 		return -EINVAL;
8394954f17dSDmitry Safonov 
8404954f17dSDmitry Safonov 	err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen);
8414954f17dSDmitry Safonov 	if (err)
8424954f17dSDmitry Safonov 		return err;
8434954f17dSDmitry Safonov 
8444954f17dSDmitry Safonov 	if (cmd.reserved != 0 || cmd.reserved2 != 0)
8454954f17dSDmitry Safonov 		return -EINVAL;
8464954f17dSDmitry Safonov 
8474954f17dSDmitry Safonov 	if (cmd.set_current || cmd.set_rnext) {
8484954f17dSDmitry Safonov 		if (!tcp_ao_can_set_current_rnext(sk))
8494954f17dSDmitry Safonov 			return -EINVAL;
8504954f17dSDmitry Safonov 	}
8514954f17dSDmitry Safonov 
8524954f17dSDmitry Safonov 	ao_info = setsockopt_ao_info(sk);
8534954f17dSDmitry Safonov 	if (IS_ERR(ao_info))
8544954f17dSDmitry Safonov 		return PTR_ERR(ao_info);
8554954f17dSDmitry Safonov 	if (!ao_info)
8564954f17dSDmitry Safonov 		return -ENOENT;
8574954f17dSDmitry Safonov 
8584954f17dSDmitry Safonov 	/* For sockets in TCP_CLOSED it's possible set keys that aren't
8594954f17dSDmitry Safonov 	 * matching the future peer (address/VRF/etc),
8604954f17dSDmitry Safonov 	 * tcp_ao_connect_init() will choose a correct matching MKT
8614954f17dSDmitry Safonov 	 * if there's any.
8624954f17dSDmitry Safonov 	 */
8634954f17dSDmitry Safonov 	if (cmd.set_current) {
8644954f17dSDmitry Safonov 		new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1);
8654954f17dSDmitry Safonov 		if (!new_current)
8664954f17dSDmitry Safonov 			return -ENOENT;
8674954f17dSDmitry Safonov 	}
8684954f17dSDmitry Safonov 	if (cmd.set_rnext) {
8694954f17dSDmitry Safonov 		new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext);
8704954f17dSDmitry Safonov 		if (!new_rnext)
8714954f17dSDmitry Safonov 			return -ENOENT;
8724954f17dSDmitry Safonov 	}
8734954f17dSDmitry Safonov 
8744954f17dSDmitry Safonov 	if (family == AF_INET) {
8754954f17dSDmitry Safonov 		struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.addr;
8764954f17dSDmitry Safonov 
8774954f17dSDmitry Safonov 		addr = (union tcp_ao_addr *)&sin->sin_addr;
8784954f17dSDmitry Safonov 		addr_len = sizeof(struct in_addr);
8794954f17dSDmitry Safonov 		port = ntohs(sin->sin_port);
8804954f17dSDmitry Safonov 	} else {
8814954f17dSDmitry Safonov 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.addr;
8824954f17dSDmitry Safonov 		struct in6_addr *addr6 = &sin6->sin6_addr;
8834954f17dSDmitry Safonov 
8844954f17dSDmitry Safonov 		if (ipv6_addr_v4mapped(addr6)) {
8854954f17dSDmitry Safonov 			addr = (union tcp_ao_addr *)&addr6->s6_addr32[3];
8864954f17dSDmitry Safonov 			addr_len = sizeof(struct in_addr);
8874954f17dSDmitry Safonov 			family = AF_INET;
8884954f17dSDmitry Safonov 		} else {
8894954f17dSDmitry Safonov 			addr = (union tcp_ao_addr *)addr6;
8904954f17dSDmitry Safonov 			addr_len = sizeof(struct in6_addr);
8914954f17dSDmitry Safonov 		}
8924954f17dSDmitry Safonov 		port = ntohs(sin6->sin6_port);
8934954f17dSDmitry Safonov 	}
8944954f17dSDmitry Safonov 	prefix = cmd.prefix;
8954954f17dSDmitry Safonov 
8964954f17dSDmitry Safonov 	/* Currently matching is not performed on port (or port ranges) */
8974954f17dSDmitry Safonov 	if (port != 0)
8984954f17dSDmitry Safonov 		return -EINVAL;
8994954f17dSDmitry Safonov 
9004954f17dSDmitry Safonov 	/* We could choose random present key here for current/rnext
9014954f17dSDmitry Safonov 	 * but that's less predictable. Let's be strict and don't
9024954f17dSDmitry Safonov 	 * allow removing a key that's in use. RFC5925 doesn't
9034954f17dSDmitry Safonov 	 * specify how-to coordinate key removal, but says:
9044954f17dSDmitry Safonov 	 * "It is presumed that an MKT affecting a particular
9054954f17dSDmitry Safonov 	 * connection cannot be destroyed during an active connection"
9064954f17dSDmitry Safonov 	 */
9074954f17dSDmitry Safonov 	hlist_for_each_entry_rcu(key, &ao_info->head, node) {
9084954f17dSDmitry Safonov 		if (cmd.sndid != key->sndid ||
9094954f17dSDmitry Safonov 		    cmd.rcvid != key->rcvid)
9104954f17dSDmitry Safonov 			continue;
9114954f17dSDmitry Safonov 
9124954f17dSDmitry Safonov 		if (family != key->family ||
9134954f17dSDmitry Safonov 		    prefix != key->prefixlen ||
9144954f17dSDmitry Safonov 		    memcmp(addr, &key->addr, addr_len))
9154954f17dSDmitry Safonov 			continue;
9164954f17dSDmitry Safonov 
9174954f17dSDmitry Safonov 		if (key == new_current || key == new_rnext)
9184954f17dSDmitry Safonov 			continue;
9194954f17dSDmitry Safonov 
9204954f17dSDmitry Safonov 		return tcp_ao_delete_key(sk, ao_info, key,
9214954f17dSDmitry Safonov 					  new_current, new_rnext);
9224954f17dSDmitry Safonov 	}
9234954f17dSDmitry Safonov 	return -ENOENT;
9244954f17dSDmitry Safonov }
9254954f17dSDmitry Safonov 
9260aadc739SDmitry Safonov /* cmd.ao_required makes a socket TCP-AO only.
9270aadc739SDmitry Safonov  * Don't allow any md5 keys for any l3intf on the socket together with it.
9280aadc739SDmitry Safonov  * Restricting it early in setsockopt() removes a check for
9290aadc739SDmitry Safonov  * ao_info->ao_required on inbound tcp segment fast-path.
9300aadc739SDmitry Safonov  */
9310aadc739SDmitry Safonov static int tcp_ao_required_verify(struct sock *sk)
9320aadc739SDmitry Safonov {
9330aadc739SDmitry Safonov #ifdef CONFIG_TCP_MD5SIG
9340aadc739SDmitry Safonov 	const struct tcp_md5sig_info *md5sig;
9350aadc739SDmitry Safonov 
9360aadc739SDmitry Safonov 	if (!static_branch_unlikely(&tcp_md5_needed.key))
9370aadc739SDmitry Safonov 		return 0;
9380aadc739SDmitry Safonov 
9390aadc739SDmitry Safonov 	md5sig = rcu_dereference_check(tcp_sk(sk)->md5sig_info,
9400aadc739SDmitry Safonov 				       lockdep_sock_is_held(sk));
9410aadc739SDmitry Safonov 	if (!md5sig)
9420aadc739SDmitry Safonov 		return 0;
9430aadc739SDmitry Safonov 
9440aadc739SDmitry Safonov 	if (rcu_dereference_check(hlist_first_rcu(&md5sig->head),
9450aadc739SDmitry Safonov 				  lockdep_sock_is_held(sk)))
9460aadc739SDmitry Safonov 		return 1;
9470aadc739SDmitry Safonov #endif
9480aadc739SDmitry Safonov 	return 0;
9490aadc739SDmitry Safonov }
9500aadc739SDmitry Safonov 
9514954f17dSDmitry Safonov static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family,
9524954f17dSDmitry Safonov 			   sockptr_t optval, int optlen)
9534954f17dSDmitry Safonov {
9544954f17dSDmitry Safonov 	struct tcp_ao_key *new_current = NULL, *new_rnext = NULL;
9554954f17dSDmitry Safonov 	struct tcp_ao_info *ao_info;
9564954f17dSDmitry Safonov 	struct tcp_ao_info_opt cmd;
9574954f17dSDmitry Safonov 	bool first = false;
9584954f17dSDmitry Safonov 	int err;
9594954f17dSDmitry Safonov 
9604954f17dSDmitry Safonov 	if (optlen < sizeof(cmd))
9614954f17dSDmitry Safonov 		return -EINVAL;
9624954f17dSDmitry Safonov 
9634954f17dSDmitry Safonov 	err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen);
9644954f17dSDmitry Safonov 	if (err)
9654954f17dSDmitry Safonov 		return err;
9664954f17dSDmitry Safonov 
9674954f17dSDmitry Safonov 	if (cmd.set_current || cmd.set_rnext) {
9684954f17dSDmitry Safonov 		if (!tcp_ao_can_set_current_rnext(sk))
9694954f17dSDmitry Safonov 			return -EINVAL;
9704954f17dSDmitry Safonov 	}
9714954f17dSDmitry Safonov 
9724954f17dSDmitry Safonov 	if (cmd.reserved != 0)
9734954f17dSDmitry Safonov 		return -EINVAL;
9744954f17dSDmitry Safonov 
9754954f17dSDmitry Safonov 	ao_info = setsockopt_ao_info(sk);
9764954f17dSDmitry Safonov 	if (IS_ERR(ao_info))
9774954f17dSDmitry Safonov 		return PTR_ERR(ao_info);
9784954f17dSDmitry Safonov 	if (!ao_info) {
9794954f17dSDmitry Safonov 		ao_info = tcp_ao_alloc_info(GFP_KERNEL);
9804954f17dSDmitry Safonov 		if (!ao_info)
9814954f17dSDmitry Safonov 			return -ENOMEM;
9824954f17dSDmitry Safonov 		first = true;
9834954f17dSDmitry Safonov 	}
9844954f17dSDmitry Safonov 
9850aadc739SDmitry Safonov 	if (cmd.ao_required && tcp_ao_required_verify(sk))
9860aadc739SDmitry Safonov 		return -EKEYREJECTED;
9870aadc739SDmitry Safonov 
9884954f17dSDmitry Safonov 	/* For sockets in TCP_CLOSED it's possible set keys that aren't
9894954f17dSDmitry Safonov 	 * matching the future peer (address/port/VRF/etc),
9904954f17dSDmitry Safonov 	 * tcp_ao_connect_init() will choose a correct matching MKT
9914954f17dSDmitry Safonov 	 * if there's any.
9924954f17dSDmitry Safonov 	 */
9934954f17dSDmitry Safonov 	if (cmd.set_current) {
9944954f17dSDmitry Safonov 		new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1);
9954954f17dSDmitry Safonov 		if (!new_current) {
9964954f17dSDmitry Safonov 			err = -ENOENT;
9974954f17dSDmitry Safonov 			goto out;
9984954f17dSDmitry Safonov 		}
9994954f17dSDmitry Safonov 	}
10004954f17dSDmitry Safonov 	if (cmd.set_rnext) {
10014954f17dSDmitry Safonov 		new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext);
10024954f17dSDmitry Safonov 		if (!new_rnext) {
10034954f17dSDmitry Safonov 			err = -ENOENT;
10044954f17dSDmitry Safonov 			goto out;
10054954f17dSDmitry Safonov 		}
10064954f17dSDmitry Safonov 	}
10074954f17dSDmitry Safonov 
10084954f17dSDmitry Safonov 	ao_info->ao_required = cmd.ao_required;
10094954f17dSDmitry Safonov 	if (new_current)
10104954f17dSDmitry Safonov 		WRITE_ONCE(ao_info->current_key, new_current);
10114954f17dSDmitry Safonov 	if (new_rnext)
10124954f17dSDmitry Safonov 		WRITE_ONCE(ao_info->rnext_key, new_rnext);
10134954f17dSDmitry Safonov 	if (first) {
10144954f17dSDmitry Safonov 		sk_gso_disable(sk);
10154954f17dSDmitry Safonov 		rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info);
10164954f17dSDmitry Safonov 	}
10174954f17dSDmitry Safonov 	return 0;
10184954f17dSDmitry Safonov out:
10194954f17dSDmitry Safonov 	if (first)
10204954f17dSDmitry Safonov 		kfree(ao_info);
10214954f17dSDmitry Safonov 	return err;
10224954f17dSDmitry Safonov }
10234954f17dSDmitry Safonov 
10244954f17dSDmitry Safonov int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
10254954f17dSDmitry Safonov 		 sockptr_t optval, int optlen)
10264954f17dSDmitry Safonov {
10274954f17dSDmitry Safonov 	if (WARN_ON_ONCE(family != AF_INET && family != AF_INET6))
10284954f17dSDmitry Safonov 		return -EAFNOSUPPORT;
10294954f17dSDmitry Safonov 
10304954f17dSDmitry Safonov 	switch (cmd) {
10314954f17dSDmitry Safonov 	case TCP_AO_ADD_KEY:
10324954f17dSDmitry Safonov 		return tcp_ao_add_cmd(sk, family, optval, optlen);
10334954f17dSDmitry Safonov 	case TCP_AO_DEL_KEY:
10344954f17dSDmitry Safonov 		return tcp_ao_del_cmd(sk, family, optval, optlen);
10354954f17dSDmitry Safonov 	case TCP_AO_INFO:
10364954f17dSDmitry Safonov 		return tcp_ao_info_cmd(sk, family, optval, optlen);
10374954f17dSDmitry Safonov 	default:
10384954f17dSDmitry Safonov 		WARN_ON_ONCE(1);
10394954f17dSDmitry Safonov 		return -EINVAL;
10404954f17dSDmitry Safonov 	}
10414954f17dSDmitry Safonov }
10424954f17dSDmitry Safonov 
10434954f17dSDmitry Safonov int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen)
10444954f17dSDmitry Safonov {
10454954f17dSDmitry Safonov 	return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen);
10464954f17dSDmitry Safonov }
10474954f17dSDmitry Safonov 
1048