1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * INET An implementation of the TCP Authentication Option (TCP-AO).
4 * See RFC5925.
5 *
6 * Authors: Dmitry Safonov <dima@arista.com>
7 * Francesco Ruggeri <fruggeri@arista.com>
8 * Salam Noureddine <noureddine@arista.com>
9 */
10#include <crypto/hash.h>
11#include <linux/tcp.h>
12
13#include <net/tcp.h>
14#include <net/ipv6.h>
15
16static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
17 const struct in6_addr *saddr,
18 const struct in6_addr *daddr,
19 __be16 sport, __be16 dport,
20 __be32 sisn, __be32 disn)
21{
22 struct kdf_input_block {
23 u8 counter;
24 u8 label[6];
25 struct tcp6_ao_context ctx;
26 __be16 outlen;
27 } __packed * tmp;
28 struct tcp_sigpool hp;
29 int err;
30
31 err = tcp_sigpool_start(id: mkt->tcp_sigpool_id, c: &hp);
32 if (err)
33 return err;
34
35 tmp = hp.scratch;
36 tmp->counter = 1;
37 memcpy(tmp->label, "TCP-AO", 6);
38 tmp->ctx.saddr = *saddr;
39 tmp->ctx.daddr = *daddr;
40 tmp->ctx.sport = sport;
41 tmp->ctx.dport = dport;
42 tmp->ctx.sisn = sisn;
43 tmp->ctx.disn = disn;
44 tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */
45
46 err = tcp_ao_calc_traffic_key(mkt, key, ctx: tmp, len: sizeof(*tmp), hp: &hp);
47 tcp_sigpool_end(c: &hp);
48
49 return err;
50}
51
52int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
53 const struct sk_buff *skb,
54 __be32 sisn, __be32 disn)
55{
56 const struct ipv6hdr *iph = ipv6_hdr(skb);
57 const struct tcphdr *th = tcp_hdr(skb);
58
59 return tcp_v6_ao_calc_key(mkt, key, saddr: &iph->saddr,
60 daddr: &iph->daddr, sport: th->source,
61 dport: th->dest, sisn, disn);
62}
63
64int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
65 const struct sock *sk, __be32 sisn,
66 __be32 disn, bool send)
67{
68 if (send)
69 return tcp_v6_ao_calc_key(mkt, key, saddr: &sk->sk_v6_rcv_saddr,
70 daddr: &sk->sk_v6_daddr, htons(sk->sk_num),
71 dport: sk->sk_dport, sisn, disn);
72 else
73 return tcp_v6_ao_calc_key(mkt, key, saddr: &sk->sk_v6_daddr,
74 daddr: &sk->sk_v6_rcv_saddr, sport: sk->sk_dport,
75 htons(sk->sk_num), sisn: disn, disn: sisn);
76}
77
78int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
79 struct request_sock *req)
80{
81 struct inet_request_sock *ireq = inet_rsk(sk: req);
82
83 return tcp_v6_ao_calc_key(mkt, key,
84 saddr: &ireq->ir_v6_loc_addr, daddr: &ireq->ir_v6_rmt_addr,
85 htons(ireq->ir_num), dport: ireq->ir_rmt_port,
86 htonl(tcp_rsk(req)->snt_isn),
87 htonl(tcp_rsk(req)->rcv_isn));
88}
89
90struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
91 struct sock *addr_sk,
92 int sndid, int rcvid)
93{
94 int l3index = l3mdev_master_ifindex_by_index(net: sock_net(sk),
95 ifindex: addr_sk->sk_bound_dev_if);
96 struct in6_addr *addr = &addr_sk->sk_v6_daddr;
97
98 return tcp_ao_do_lookup(sk, l3index, addr: (union tcp_ao_addr *)addr,
99 AF_INET6, sndid, rcvid);
100}
101
102struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
103 struct request_sock *req,
104 int sndid, int rcvid)
105{
106 struct inet_request_sock *ireq = inet_rsk(sk: req);
107 struct in6_addr *addr = &ireq->ir_v6_rmt_addr;
108 int l3index;
109
110 l3index = l3mdev_master_ifindex_by_index(net: sock_net(sk), ifindex: ireq->ir_iif);
111 return tcp_ao_do_lookup(sk, l3index, addr: (union tcp_ao_addr *)addr,
112 AF_INET6, sndid, rcvid);
113}
114
115int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp,
116 const struct in6_addr *daddr,
117 const struct in6_addr *saddr, int nbytes)
118{
119 struct tcp6_pseudohdr *bp;
120 struct scatterlist sg;
121
122 bp = hp->scratch;
123 /* 1. TCP pseudo-header (RFC2460) */
124 bp->saddr = *saddr;
125 bp->daddr = *daddr;
126 bp->len = cpu_to_be32(nbytes);
127 bp->protocol = cpu_to_be32(IPPROTO_TCP);
128
129 sg_init_one(&sg, bp, sizeof(*bp));
130 ahash_request_set_crypt(req: hp->req, src: &sg, NULL, nbytes: sizeof(*bp));
131 return crypto_ahash_update(req: hp->req);
132}
133
134int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
135 const struct sock *sk, const struct sk_buff *skb,
136 const u8 *tkey, int hash_offset, u32 sne)
137{
138 return tcp_ao_hash_skb(AF_INET6, ao_hash, key, sk, skb, tkey,
139 hash_offset, sne);
140}
141
142int tcp_v6_parse_ao(struct sock *sk, int cmd,
143 sockptr_t optval, int optlen)
144{
145 return tcp_parse_ao(sk, cmd, AF_INET6, optval, optlen);
146}
147
148int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
149 struct request_sock *req, const struct sk_buff *skb,
150 int hash_offset, u32 sne)
151{
152 void *hash_buf = NULL;
153 int err;
154
155 hash_buf = kmalloc(size: tcp_ao_digest_size(key: ao_key), GFP_ATOMIC);
156 if (!hash_buf)
157 return -ENOMEM;
158
159 err = tcp_v6_ao_calc_key_rsk(mkt: ao_key, key: hash_buf, req);
160 if (err)
161 goto out;
162
163 err = tcp_ao_hash_skb(AF_INET6, ao_hash, key: ao_key, sk: req_to_sk(req), skb,
164 tkey: hash_buf, hash_offset, sne);
165out:
166 kfree(objp: hash_buf);
167 return err;
168}
169

source code of linux/net/ipv6/tcp_ao.c