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 | |
16 | static 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 | |
52 | int 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 | |
64 | int 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 | |
78 | int 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 | |
90 | struct 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 | |
102 | struct 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 | |
115 | int (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 | |
134 | int 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 | |
142 | int 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 | |
148 | int 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); |
165 | out: |
166 | kfree(objp: hash_buf); |
167 | return err; |
168 | } |
169 | |