Skip to content

Commit ba7783a

Browse files
0x7f454c46davem330
authored andcommitted
net/tcp: Add AO sign to RST packets
Wire up sending resets to TCP-AO hashing. Co-developed-by: Francesco Ruggeri <[email protected]> Signed-off-by: Francesco Ruggeri <[email protected]> Co-developed-by: Salam Noureddine <[email protected]> Signed-off-by: Salam Noureddine <[email protected]> Signed-off-by: Dmitry Safonov <[email protected]> Acked-by: David Ahern <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f7dca36 commit ba7783a

File tree

5 files changed

+245
-43
lines changed

5 files changed

+245
-43
lines changed

include/net/tcp.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -2258,7 +2258,12 @@ static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops,
22582258

22592259
struct tcp_key {
22602260
union {
2261-
struct tcp_ao_key *ao_key;
2261+
struct {
2262+
struct tcp_ao_key *ao_key;
2263+
char *traffic_key;
2264+
u32 sne;
2265+
u8 rcv_next;
2266+
};
22622267
struct tcp_md5sig_key *md5_key;
22632268
};
22642269
enum {

include/net/tcp_ao.h

+12
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,24 @@ int tcp_ao_hash_skb(unsigned short int family,
120120
const u8 *tkey, int hash_offset, u32 sne);
121121
int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
122122
sockptr_t optval, int optlen);
123+
struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao,
124+
int sndid, int rcvid);
123125
int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
124126
unsigned int len, struct tcp_sigpool *hp);
125127
void tcp_ao_destroy_sock(struct sock *sk);
126128
struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
127129
const union tcp_ao_addr *addr,
128130
int family, int sndid, int rcvid);
131+
int tcp_ao_hash_hdr(unsigned short family, char *ao_hash,
132+
struct tcp_ao_key *key, const u8 *tkey,
133+
const union tcp_ao_addr *daddr,
134+
const union tcp_ao_addr *saddr,
135+
const struct tcphdr *th, u32 sne);
136+
int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
137+
const struct tcp_ao_hdr *aoh, int l3index,
138+
struct tcp_ao_key **key, char **traffic_key,
139+
bool *allocated_traffic_key, u8 *keyid, u32 *sne);
140+
129141
/* ipv4 specific functions */
130142
int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
131143
struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,

net/ipv4/tcp_ao.c

+100-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
4848
* it's known that the keys in ao_info are matching peer's
4949
* family/address/VRF/etc.
5050
*/
51-
static struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao,
52-
int sndid, int rcvid)
51+
struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao,
52+
int sndid, int rcvid)
5353
{
5454
struct tcp_ao_key *key;
5555

@@ -369,6 +369,66 @@ static int tcp_ao_hash_header(struct tcp_sigpool *hp,
369369
return err;
370370
}
371371

372+
int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash,
373+
struct tcp_ao_key *key, const u8 *tkey,
374+
const union tcp_ao_addr *daddr,
375+
const union tcp_ao_addr *saddr,
376+
const struct tcphdr *th, u32 sne)
377+
{
378+
int tkey_len = tcp_ao_digest_size(key);
379+
int hash_offset = ao_hash - (char *)th;
380+
struct tcp_sigpool hp;
381+
void *hash_buf = NULL;
382+
383+
hash_buf = kmalloc(tkey_len, GFP_ATOMIC);
384+
if (!hash_buf)
385+
goto clear_hash_noput;
386+
387+
if (tcp_sigpool_start(key->tcp_sigpool_id, &hp))
388+
goto clear_hash_noput;
389+
390+
if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len))
391+
goto clear_hash;
392+
393+
if (crypto_ahash_init(hp.req))
394+
goto clear_hash;
395+
396+
if (tcp_ao_hash_sne(&hp, sne))
397+
goto clear_hash;
398+
if (family == AF_INET) {
399+
if (tcp_v4_ao_hash_pseudoheader(&hp, daddr->a4.s_addr,
400+
saddr->a4.s_addr, th->doff * 4))
401+
goto clear_hash;
402+
#if IS_ENABLED(CONFIG_IPV6)
403+
} else if (family == AF_INET6) {
404+
if (tcp_v6_ao_hash_pseudoheader(&hp, &daddr->a6,
405+
&saddr->a6, th->doff * 4))
406+
goto clear_hash;
407+
#endif
408+
} else {
409+
WARN_ON_ONCE(1);
410+
goto clear_hash;
411+
}
412+
if (tcp_ao_hash_header(&hp, th, false,
413+
ao_hash, hash_offset, tcp_ao_maclen(key)))
414+
goto clear_hash;
415+
ahash_request_set_crypt(hp.req, NULL, hash_buf, 0);
416+
if (crypto_ahash_final(hp.req))
417+
goto clear_hash;
418+
419+
memcpy(ao_hash, hash_buf, tcp_ao_maclen(key));
420+
tcp_sigpool_end(&hp);
421+
kfree(hash_buf);
422+
return 0;
423+
424+
clear_hash:
425+
tcp_sigpool_end(&hp);
426+
clear_hash_noput:
427+
memset(ao_hash, 0, tcp_ao_maclen(key));
428+
kfree(hash_buf);
429+
return 1;
430+
}
431+
372432
int tcp_ao_hash_skb(unsigned short int family,
373433
char *ao_hash, struct tcp_ao_key *key,
374434
const struct sock *sk, const struct sk_buff *skb,
@@ -435,6 +495,44 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
435495
return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid);
436496
}
437497

498+
int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
499+
const struct tcp_ao_hdr *aoh, int l3index,
500+
struct tcp_ao_key **key, char **traffic_key,
501+
bool *allocated_traffic_key, u8 *keyid, u32 *sne)
502+
{
503+
struct tcp_ao_key *rnext_key;
504+
struct tcp_ao_info *ao_info;
505+
506+
*allocated_traffic_key = false;
507+
/* If there's no socket - than initial sisn/disn are unknown.
508+
* Drop the segment. RFC5925 (7.7) advises to require graceful
509+
* restart [RFC4724]. Alternatively, the RFC5925 advises to
510+
* save/restore traffic keys before/after reboot.
511+
* Linux TCP-AO support provides TCP_AO_ADD_KEY and TCP_AO_REPAIR
512+
* options to restore a socket post-reboot.
513+
*/
514+
if (!sk)
515+
return -ENOTCONN;
516+
517+
if ((1 << sk->sk_state) &
518+
(TCPF_LISTEN | TCPF_NEW_SYN_RECV | TCPF_TIME_WAIT))
519+
return -1;
520+
521+
ao_info = rcu_dereference(tcp_sk(sk)->ao_info);
522+
if (!ao_info)
523+
return -ENOENT;
524+
525+
*key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1);
526+
if (!*key)
527+
return -ENOENT;
528+
*traffic_key = snd_other_key(*key);
529+
rnext_key = READ_ONCE(ao_info->rnext_key);
530+
*keyid = rnext_key->rcvid;
531+
*sne = 0;
532+
533+
return 0;
534+
}
535+
438536
int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
439537
struct tcp_ao_key *key, struct tcphdr *th,
440538
__u8 *hash_location)

net/ipv4/tcp_ipv4.c

+56-13
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,52 @@ void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb)
657657
}
658658
EXPORT_SYMBOL(tcp_v4_send_check);
659659

660+
#define REPLY_OPTIONS_LEN (MAX_TCP_OPTION_SPACE / sizeof(__be32))
661+
662+
static bool tcp_v4_ao_sign_reset(const struct sock *sk, struct sk_buff *skb,
663+
const struct tcp_ao_hdr *aoh,
664+
struct ip_reply_arg *arg, struct tcphdr *reply,
665+
__be32 reply_options[REPLY_OPTIONS_LEN])
666+
{
667+
#ifdef CONFIG_TCP_AO
668+
int sdif = tcp_v4_sdif(skb);
669+
int dif = inet_iif(skb);
670+
int l3index = sdif ? dif : 0;
671+
bool allocated_traffic_key;
672+
struct tcp_ao_key *key;
673+
char *traffic_key;
674+
bool drop = true;
675+
u32 ao_sne = 0;
676+
u8 keyid;
677+
678+
rcu_read_lock();
679+
if (tcp_ao_prepare_reset(sk, skb, aoh, l3index,
680+
&key, &traffic_key, &allocated_traffic_key,
681+
&keyid, &ao_sne))
682+
goto out;
683+
684+
reply_options[0] = htonl((TCPOPT_AO << 24) | (tcp_ao_len(key) << 16) |
685+
(aoh->rnext_keyid << 8) | keyid);
686+
arg->iov[0].iov_len += round_up(tcp_ao_len(key), 4);
687+
reply->doff = arg->iov[0].iov_len / 4;
688+
689+
if (tcp_ao_hash_hdr(AF_INET, (char *)&reply_options[1],
690+
key, traffic_key,
691+
(union tcp_ao_addr *)&ip_hdr(skb)->saddr,
692+
(union tcp_ao_addr *)&ip_hdr(skb)->daddr,
693+
reply, ao_sne))
694+
goto out;
695+
drop = false;
696+
out:
697+
rcu_read_unlock();
698+
if (allocated_traffic_key)
699+
kfree(traffic_key);
700+
return drop;
701+
#else
702+
return true;
703+
#endif
704+
}
705+
660706
/*
661707
* This routine will send an RST to the other tcp.
662708
*
@@ -670,28 +716,21 @@ EXPORT_SYMBOL(tcp_v4_send_check);
670716
* Exception: precedence violation. We do not implement it in any case.
671717
*/
672718

673-
#ifdef CONFIG_TCP_AO
674-
#define OPTION_BYTES MAX_TCP_OPTION_SPACE
675-
#elif defined(CONFIG_TCP_MD5SIG)
676-
#define OPTION_BYTES TCPOLEN_MD5SIG_ALIGNED
677-
#else
678-
#define OPTION_BYTES sizeof(__be32)
679-
#endif
680-
681719
static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
682720
{
683721
const struct tcphdr *th = tcp_hdr(skb);
684722
struct {
685723
struct tcphdr th;
686-
__be32 opt[OPTION_BYTES / sizeof(__be32)];
724+
__be32 opt[REPLY_OPTIONS_LEN];
687725
} rep;
726+
const __u8 *md5_hash_location = NULL;
727+
const struct tcp_ao_hdr *aoh;
688728
struct ip_reply_arg arg;
689729
#ifdef CONFIG_TCP_MD5SIG
690-
const __u8 *md5_hash_location = NULL;
691730
struct tcp_md5sig_key *key = NULL;
692731
unsigned char newhash[16];
693-
int genhash;
694732
struct sock *sk1 = NULL;
733+
int genhash;
695734
#endif
696735
u64 transmit_time = 0;
697736
struct sock *ctl_sk;
@@ -728,11 +767,15 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
728767
arg.iov[0].iov_len = sizeof(rep.th);
729768

730769
net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
731-
#ifdef CONFIG_TCP_MD5SIG
770+
732771
/* Invalid TCP option size or twice included auth */
733-
if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, NULL))
772+
if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, &aoh))
734773
return;
735774

775+
if (aoh && tcp_v4_ao_sign_reset(sk, skb, aoh, &arg, &rep.th, rep.opt))
776+
return;
777+
778+
#ifdef CONFIG_TCP_MD5SIG
736779
rcu_read_lock();
737780
if (sk && sk_fullsock(sk)) {
738781
const union tcp_md5_addr *addr;

0 commit comments

Comments
 (0)