Skip to content

Commit 7c2ffaf

Browse files
0x7f454c46davem330
authored andcommitted
net/tcp: Calculate TCP-AO traffic keys
Add traffic key calculation the way it's described in RFC5926. Wire it up to tcp_finish_connect() and cache the new keys straight away on already established TCP connections. Co-developed-by: Francesco Ruggeri <fruggeri@arista.com> Signed-off-by: Francesco Ruggeri <fruggeri@arista.com> Co-developed-by: Salam Noureddine <noureddine@arista.com> Signed-off-by: Salam Noureddine <noureddine@arista.com> Signed-off-by: Dmitry Safonov <dima@arista.com> Acked-by: David Ahern <dsahern@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 0aadc73 commit 7c2ffaf

File tree

8 files changed

+314
-2
lines changed

8 files changed

+314
-2
lines changed

include/net/tcp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,6 +2197,9 @@ struct tcp_sock_af_ops {
21972197
struct tcp_ao_key *(*ao_lookup)(const struct sock *sk,
21982198
struct sock *addr_sk,
21992199
int sndid, int rcvid);
2200+
int (*ao_calc_key_sk)(struct tcp_ao_key *mkt, u8 *key,
2201+
const struct sock *sk,
2202+
__be32 sisn, __be32 disn, bool send);
22002203
#endif
22012204
};
22022205

include/net/tcp_ao.h

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,32 @@ struct tcp_ao_info {
8989
};
9090

9191
#ifdef CONFIG_TCP_AO
92+
/* TCP-AO structures and functions */
93+
94+
struct tcp4_ao_context {
95+
__be32 saddr;
96+
__be32 daddr;
97+
__be16 sport;
98+
__be16 dport;
99+
__be32 sisn;
100+
__be32 disn;
101+
};
102+
103+
struct tcp6_ao_context {
104+
struct in6_addr saddr;
105+
struct in6_addr daddr;
106+
__be16 sport;
107+
__be16 dport;
108+
__be32 sisn;
109+
__be32 disn;
110+
};
111+
112+
struct tcp_sigpool;
113+
92114
int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
93115
sockptr_t optval, int optlen);
116+
int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
117+
unsigned int len, struct tcp_sigpool *hp);
94118
void tcp_ao_destroy_sock(struct sock *sk);
95119
struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
96120
const union tcp_ao_addr *addr,
@@ -99,11 +123,22 @@ struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
99123
int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
100124
struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
101125
int sndid, int rcvid);
126+
int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
127+
const struct sock *sk,
128+
__be32 sisn, __be32 disn, bool send);
102129
/* ipv6 specific functions */
103-
int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
130+
int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
131+
const struct sock *sk, __be32 sisn,
132+
__be32 disn, bool send);
104133
struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
105134
struct sock *addr_sk, int sndid, int rcvid);
106-
#else
135+
int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
136+
void tcp_ao_established(struct sock *sk);
137+
void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb);
138+
void tcp_ao_connect_init(struct sock *sk);
139+
140+
#else /* CONFIG_TCP_AO */
141+
107142
static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
108143
const union tcp_ao_addr *addr, int family, int sndid, int rcvid)
109144
{
@@ -113,6 +148,18 @@ static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
113148
static inline void tcp_ao_destroy_sock(struct sock *sk)
114149
{
115150
}
151+
152+
static inline void tcp_ao_established(struct sock *sk)
153+
{
154+
}
155+
156+
static inline void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
157+
{
158+
}
159+
160+
static inline void tcp_ao_connect_init(struct sock *sk)
161+
{
162+
}
116163
#endif
117164

118165
#endif /* _TCP_AO_H */

net/ipv4/tcp_ao.c

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,34 @@
1616
#include <net/tcp.h>
1717
#include <net/ipv6.h>
1818

19+
int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
20+
unsigned int len, struct tcp_sigpool *hp)
21+
{
22+
struct scatterlist sg;
23+
int ret;
24+
25+
if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req),
26+
mkt->key, mkt->keylen))
27+
goto clear_hash;
28+
29+
ret = crypto_ahash_init(hp->req);
30+
if (ret)
31+
goto clear_hash;
32+
33+
sg_init_one(&sg, ctx, len);
34+
ahash_request_set_crypt(hp->req, &sg, key, len);
35+
crypto_ahash_update(hp->req);
36+
37+
ret = crypto_ahash_final(hp->req);
38+
if (ret)
39+
goto clear_hash;
40+
41+
return 0;
42+
clear_hash:
43+
memset(key, 0, tcp_ao_digest_size(mkt));
44+
return 1;
45+
}
46+
1947
/* Optimized version of tcp_ao_do_lookup(): only for sockets for which
2048
* it's known that the keys in ao_info are matching peer's
2149
* family/address/VRF/etc.
@@ -169,6 +197,71 @@ void tcp_ao_destroy_sock(struct sock *sk)
169197
kfree_rcu(ao, rcu);
170198
}
171199

200+
/* 4 tuple and ISNs are expected in NBO */
201+
static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
202+
__be32 saddr, __be32 daddr,
203+
__be16 sport, __be16 dport,
204+
__be32 sisn, __be32 disn)
205+
{
206+
/* See RFC5926 3.1.1 */
207+
struct kdf_input_block {
208+
u8 counter;
209+
u8 label[6];
210+
struct tcp4_ao_context ctx;
211+
__be16 outlen;
212+
} __packed * tmp;
213+
struct tcp_sigpool hp;
214+
int err;
215+
216+
err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp);
217+
if (err)
218+
return err;
219+
220+
tmp = hp.scratch;
221+
tmp->counter = 1;
222+
memcpy(tmp->label, "TCP-AO", 6);
223+
tmp->ctx.saddr = saddr;
224+
tmp->ctx.daddr = daddr;
225+
tmp->ctx.sport = sport;
226+
tmp->ctx.dport = dport;
227+
tmp->ctx.sisn = sisn;
228+
tmp->ctx.disn = disn;
229+
tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */
230+
231+
err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp);
232+
tcp_sigpool_end(&hp);
233+
234+
return err;
235+
}
236+
237+
int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
238+
const struct sock *sk,
239+
__be32 sisn, __be32 disn, bool send)
240+
{
241+
if (send)
242+
return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr,
243+
sk->sk_daddr, htons(sk->sk_num),
244+
sk->sk_dport, sisn, disn);
245+
else
246+
return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr,
247+
sk->sk_rcv_saddr, sk->sk_dport,
248+
htons(sk->sk_num), disn, sisn);
249+
}
250+
251+
static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
252+
const struct sock *sk,
253+
__be32 sisn, __be32 disn, bool send)
254+
{
255+
if (mkt->family == AF_INET)
256+
return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
257+
#if IS_ENABLED(CONFIG_IPV6)
258+
else if (mkt->family == AF_INET6)
259+
return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
260+
#endif
261+
else
262+
return -EOPNOTSUPP;
263+
}
264+
172265
struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
173266
int sndid, int rcvid)
174267
{
@@ -177,6 +270,113 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
177270
return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid);
178271
}
179272

273+
static int tcp_ao_cache_traffic_keys(const struct sock *sk,
274+
struct tcp_ao_info *ao,
275+
struct tcp_ao_key *ao_key)
276+
{
277+
u8 *traffic_key = snd_other_key(ao_key);
278+
int ret;
279+
280+
ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk,
281+
ao->lisn, ao->risn, true);
282+
if (ret)
283+
return ret;
284+
285+
traffic_key = rcv_other_key(ao_key);
286+
ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk,
287+
ao->lisn, ao->risn, false);
288+
return ret;
289+
}
290+
291+
void tcp_ao_connect_init(struct sock *sk)
292+
{
293+
struct tcp_sock *tp = tcp_sk(sk);
294+
struct tcp_ao_info *ao_info;
295+
union tcp_ao_addr *addr;
296+
struct tcp_ao_key *key;
297+
int family;
298+
299+
ao_info = rcu_dereference_protected(tp->ao_info,
300+
lockdep_sock_is_held(sk));
301+
if (!ao_info)
302+
return;
303+
304+
/* Remove all keys that don't match the peer */
305+
family = sk->sk_family;
306+
if (family == AF_INET)
307+
addr = (union tcp_ao_addr *)&sk->sk_daddr;
308+
#if IS_ENABLED(CONFIG_IPV6)
309+
else if (family == AF_INET6)
310+
addr = (union tcp_ao_addr *)&sk->sk_v6_daddr;
311+
#endif
312+
else
313+
return;
314+
315+
hlist_for_each_entry_rcu(key, &ao_info->head, node) {
316+
if (!tcp_ao_key_cmp(key, addr, key->prefixlen, family, -1, -1))
317+
continue;
318+
319+
if (key == ao_info->current_key)
320+
ao_info->current_key = NULL;
321+
if (key == ao_info->rnext_key)
322+
ao_info->rnext_key = NULL;
323+
hlist_del_rcu(&key->node);
324+
atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
325+
call_rcu(&key->rcu, tcp_ao_key_free_rcu);
326+
}
327+
328+
key = tp->af_specific->ao_lookup(sk, sk, -1, -1);
329+
if (key) {
330+
/* if current_key or rnext_key were not provided,
331+
* use the first key matching the peer
332+
*/
333+
if (!ao_info->current_key)
334+
ao_info->current_key = key;
335+
if (!ao_info->rnext_key)
336+
ao_info->rnext_key = key;
337+
tp->tcp_header_len += tcp_ao_len(key);
338+
339+
ao_info->lisn = htonl(tp->write_seq);
340+
} else {
341+
/* Can't happen: tcp_connect() verifies that there's
342+
* at least one tcp-ao key that matches the remote peer.
343+
*/
344+
WARN_ON_ONCE(1);
345+
rcu_assign_pointer(tp->ao_info, NULL);
346+
kfree(ao_info);
347+
}
348+
}
349+
350+
void tcp_ao_established(struct sock *sk)
351+
{
352+
struct tcp_ao_info *ao;
353+
struct tcp_ao_key *key;
354+
355+
ao = rcu_dereference_protected(tcp_sk(sk)->ao_info,
356+
lockdep_sock_is_held(sk));
357+
if (!ao)
358+
return;
359+
360+
hlist_for_each_entry_rcu(key, &ao->head, node)
361+
tcp_ao_cache_traffic_keys(sk, ao, key);
362+
}
363+
364+
void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
365+
{
366+
struct tcp_ao_info *ao;
367+
struct tcp_ao_key *key;
368+
369+
ao = rcu_dereference_protected(tcp_sk(sk)->ao_info,
370+
lockdep_sock_is_held(sk));
371+
if (!ao)
372+
return;
373+
374+
WRITE_ONCE(ao->risn, tcp_hdr(skb)->seq);
375+
376+
hlist_for_each_entry_rcu(key, &ao->head, node)
377+
tcp_ao_cache_traffic_keys(sk, ao, key);
378+
}
379+
180380
static bool tcp_ao_can_set_current_rnext(struct sock *sk)
181381
{
182382
/* There aren't current/rnext keys on TCP_LISTEN sockets */
@@ -558,6 +758,12 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
558758
if (ret < 0)
559759
goto err_free_sock;
560760

761+
/* Change this condition if we allow adding keys in states
762+
* like close_wait, syn_sent or fin_wait...
763+
*/
764+
if (sk->sk_state == TCP_ESTABLISHED)
765+
tcp_ao_cache_traffic_keys(sk, ao_info, key);
766+
561767
tcp_ao_link_mkt(ao_info, key);
562768
if (first) {
563769
sk_gso_disable(sk);

net/ipv4/tcp_input.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6151,6 +6151,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb)
61516151
struct tcp_sock *tp = tcp_sk(sk);
61526152
struct inet_connection_sock *icsk = inet_csk(sk);
61536153

6154+
tcp_ao_finish_connect(sk, skb);
61546155
tcp_set_state(sk, TCP_ESTABLISHED);
61556156
icsk->icsk_ack.lrcvtime = tcp_jiffies32;
61566157

@@ -6648,6 +6649,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
66486649
skb);
66496650
WRITE_ONCE(tp->copied_seq, tp->rcv_nxt);
66506651
}
6652+
tcp_ao_established(sk);
66516653
smp_mb();
66526654
tcp_set_state(sk, TCP_ESTABLISHED);
66536655
sk->sk_state_change(sk);

net/ipv4/tcp_ipv4.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2288,6 +2288,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
22882288
#ifdef CONFIG_TCP_AO
22892289
.ao_lookup = tcp_v4_ao_lookup,
22902290
.ao_parse = tcp_v4_parse_ao,
2291+
.ao_calc_key_sk = tcp_v4_ao_calc_key_sk,
22912292
#endif
22922293
};
22932294
#endif

net/ipv4/tcp_output.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3749,6 +3749,8 @@ static void tcp_connect_init(struct sock *sk)
37493749
if (READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_timestamps))
37503750
tp->tcp_header_len += TCPOLEN_TSTAMP_ALIGNED;
37513751

3752+
tcp_ao_connect_init(sk);
3753+
37523754
/* If user gave his TCP_MAXSEG, record it to clamp */
37533755
if (tp->rx_opt.user_mss)
37543756
tp->rx_opt.mss_clamp = tp->rx_opt.user_mss;

0 commit comments

Comments
 (0)