Skip to content

Commit 64382c7

Browse files
0x7f454c46davem330
authored andcommitted
net/tcp: Add TCP-AO SNE support
Add Sequence Number Extension (SNE) for TCP-AO. This is needed to protect long-living TCP-AO connections from replaying attacks after sequence number roll-over, see RFC5925 (6.2). 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 af09a34 commit 64382c7

File tree

6 files changed

+104
-13
lines changed

6 files changed

+104
-13
lines changed

include/net/tcp_ao.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,25 @@ struct tcp_ao_info {
9595
__unused :31;
9696
__be32 lisn;
9797
__be32 risn;
98+
/* Sequence Number Extension (SNE) are upper 4 bytes for SEQ,
99+
* that protect TCP-AO connection from replayed old TCP segments.
100+
* See RFC5925 (6.2).
101+
* In order to get correct SNE, there's a helper tcp_ao_compute_sne().
102+
* It needs SEQ basis to understand whereabouts are lower SEQ numbers.
103+
* According to that basis vector, it can provide incremented SNE
104+
* when SEQ rolls over or provide decremented SNE when there's
105+
* a retransmitted segment from before-rolling over.
106+
* - for request sockets such basis is rcv_isn/snt_isn, which seems
107+
* good enough as it's unexpected to receive 4 Gbytes on reqsk.
108+
* - for full sockets the basis is rcv_nxt/snd_una. snd_una is
109+
* taken instead of snd_nxt as currently it's easier to track
110+
* in tcp_snd_una_update(), rather than updating SNE in all
111+
* WRITE_ONCE(tp->snd_nxt, ...)
112+
* - for time-wait sockets the basis is tw_rcv_nxt/tw_snd_nxt.
113+
* tw_snd_nxt is not expected to change, while tw_rcv_nxt may.
114+
*/
115+
u32 snd_sne;
116+
u32 rcv_sne;
98117
refcount_t refcnt; /* Protects twsk destruction */
99118
struct rcu_head rcu;
100119
};
@@ -147,6 +166,7 @@ enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk,
147166
const struct sk_buff *skb, unsigned short int family,
148167
const struct request_sock *req,
149168
const struct tcp_ao_hdr *aoh);
169+
u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq);
150170
struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
151171
const union tcp_ao_addr *addr,
152172
int family, int sndid, int rcvid);
@@ -156,7 +176,7 @@ int tcp_ao_hash_hdr(unsigned short family, char *ao_hash,
156176
const union tcp_ao_addr *saddr,
157177
const struct tcphdr *th, u32 sne);
158178
int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
159-
const struct tcp_ao_hdr *aoh, int l3index,
179+
const struct tcp_ao_hdr *aoh, int l3index, u32 seq,
160180
struct tcp_ao_key **key, char **traffic_key,
161181
bool *allocated_traffic_key, u8 *keyid, u32 *sne);
162182

net/ipv4/tcp_ao.c

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,21 @@ static int tcp_ao_hash_pseudoheader(unsigned short int family,
401401
return -EAFNOSUPPORT;
402402
}
403403

404+
u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq)
405+
{
406+
u32 sne = next_sne;
407+
408+
if (before(seq, next_seq)) {
409+
if (seq > next_seq)
410+
sne--;
411+
} else {
412+
if (seq < next_seq)
413+
sne++;
414+
}
415+
416+
return sne;
417+
}
418+
404419
/* tcp_ao_hash_sne(struct tcp_sigpool *hp)
405420
* @hp - used for hashing
406421
* @sne - sne value
@@ -611,7 +626,7 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
611626
}
612627

613628
int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
614-
const struct tcp_ao_hdr *aoh, int l3index,
629+
const struct tcp_ao_hdr *aoh, int l3index, u32 seq,
615630
struct tcp_ao_key **key, char **traffic_key,
616631
bool *allocated_traffic_key, u8 *keyid, u32 *sne)
617632
{
@@ -639,7 +654,7 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
639654

640655
sisn = htonl(tcp_rsk(req)->rcv_isn);
641656
disn = htonl(tcp_rsk(req)->snt_isn);
642-
*sne = 0;
657+
*sne = tcp_ao_compute_sne(0, tcp_rsk(req)->snt_isn, seq);
643658
} else {
644659
sisn = th->seq;
645660
disn = 0;
@@ -670,11 +685,15 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
670685
*keyid = (*key)->rcvid;
671686
} else {
672687
struct tcp_ao_key *rnext_key;
688+
u32 snd_basis;
673689

674-
if (sk->sk_state == TCP_TIME_WAIT)
690+
if (sk->sk_state == TCP_TIME_WAIT) {
675691
ao_info = rcu_dereference(tcp_twsk(sk)->ao_info);
676-
else
692+
snd_basis = tcp_twsk(sk)->tw_snd_nxt;
693+
} else {
677694
ao_info = rcu_dereference(tcp_sk(sk)->ao_info);
695+
snd_basis = tcp_sk(sk)->snd_una;
696+
}
678697
if (!ao_info)
679698
return -ENOENT;
680699

@@ -684,7 +703,8 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
684703
*traffic_key = snd_other_key(*key);
685704
rnext_key = READ_ONCE(ao_info->rnext_key);
686705
*keyid = rnext_key->rcvid;
687-
*sne = 0;
706+
*sne = tcp_ao_compute_sne(READ_ONCE(ao_info->snd_sne),
707+
snd_basis, seq);
688708
}
689709
return 0;
690710
}
@@ -698,6 +718,7 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
698718
struct tcp_ao_info *ao;
699719
void *tkey_buf = NULL;
700720
u8 *traffic_key;
721+
u32 sne;
701722

702723
ao = rcu_dereference_protected(tcp_sk(sk)->ao_info,
703724
lockdep_sock_is_held(sk));
@@ -717,8 +738,10 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
717738
tp->af_specific->ao_calc_key_sk(key, traffic_key,
718739
sk, ao->lisn, disn, true);
719740
}
741+
sne = tcp_ao_compute_sne(READ_ONCE(ao->snd_sne), READ_ONCE(tp->snd_una),
742+
ntohl(th->seq));
720743
tp->af_specific->calc_ao_hash(hash_location, key, sk, skb, traffic_key,
721-
hash_location - (u8 *)th, 0);
744+
hash_location - (u8 *)th, sne);
722745
kfree(tkey_buf);
723746
return 0;
724747
}
@@ -846,7 +869,8 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
846869
if (unlikely(th->syn && !th->ack))
847870
goto verify_hash;
848871

849-
sne = 0;
872+
sne = tcp_ao_compute_sne(info->rcv_sne, tcp_sk(sk)->rcv_nxt,
873+
ntohl(th->seq));
850874
/* Established socket, traffic key are cached */
851875
traffic_key = rcv_other_key(key);
852876
err = tcp_ao_verify_hash(sk, skb, family, info, aoh, key,
@@ -881,14 +905,16 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
881905
if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) {
882906
/* Make the initial syn the likely case here */
883907
if (unlikely(req)) {
884-
sne = 0;
908+
sne = tcp_ao_compute_sne(0, tcp_rsk(req)->rcv_isn,
909+
ntohl(th->seq));
885910
sisn = htonl(tcp_rsk(req)->rcv_isn);
886911
disn = htonl(tcp_rsk(req)->snt_isn);
887912
} else if (unlikely(th->ack && !th->syn)) {
888913
/* Possible syncookie packet */
889914
sisn = htonl(ntohl(th->seq) - 1);
890915
disn = htonl(ntohl(th->ack_seq) - 1);
891-
sne = 0;
916+
sne = tcp_ao_compute_sne(0, ntohl(sisn),
917+
ntohl(th->seq));
892918
} else if (unlikely(!th->syn)) {
893919
/* no way to figure out initial sisn/disn - drop */
894920
return SKB_DROP_REASON_TCP_FLAGS;
@@ -986,6 +1012,7 @@ void tcp_ao_connect_init(struct sock *sk)
9861012
tp->tcp_header_len += tcp_ao_len(key);
9871013

9881014
ao_info->lisn = htonl(tp->write_seq);
1015+
ao_info->snd_sne = 0;
9891016
} else {
9901017
/* Can't happen: tcp_connect() verifies that there's
9911018
* at least one tcp-ao key that matches the remote peer.
@@ -1021,6 +1048,7 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
10211048
return;
10221049

10231050
WRITE_ONCE(ao->risn, tcp_hdr(skb)->seq);
1051+
ao->rcv_sne = 0;
10241052

10251053
hlist_for_each_entry_rcu(key, &ao->head, node)
10261054
tcp_ao_cache_traffic_keys(sk, ao, key);

net/ipv4/tcp_input.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3576,19 +3576,37 @@ static inline bool tcp_may_update_window(const struct tcp_sock *tp,
35763576
static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack)
35773577
{
35783578
u32 delta = ack - tp->snd_una;
3579+
#ifdef CONFIG_TCP_AO
3580+
struct tcp_ao_info *ao;
3581+
#endif
35793582

35803583
sock_owned_by_me((struct sock *)tp);
35813584
tp->bytes_acked += delta;
3585+
#ifdef CONFIG_TCP_AO
3586+
ao = rcu_dereference_protected(tp->ao_info,
3587+
lockdep_sock_is_held((struct sock *)tp));
3588+
if (ao && ack < tp->snd_una)
3589+
ao->snd_sne++;
3590+
#endif
35823591
tp->snd_una = ack;
35833592
}
35843593

35853594
/* If we update tp->rcv_nxt, also update tp->bytes_received */
35863595
static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq)
35873596
{
35883597
u32 delta = seq - tp->rcv_nxt;
3598+
#ifdef CONFIG_TCP_AO
3599+
struct tcp_ao_info *ao;
3600+
#endif
35893601

35903602
sock_owned_by_me((struct sock *)tp);
35913603
tp->bytes_received += delta;
3604+
#ifdef CONFIG_TCP_AO
3605+
ao = rcu_dereference_protected(tp->ao_info,
3606+
lockdep_sock_is_held((struct sock *)tp));
3607+
if (ao && seq < tp->rcv_nxt)
3608+
ao->rcv_sne++;
3609+
#endif
35923610
WRITE_ONCE(tp->rcv_nxt, seq);
35933611
}
35943612

@@ -6456,6 +6474,16 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
64566474
* simultaneous connect with crossed SYNs.
64576475
* Particularly, it can be connect to self.
64586476
*/
6477+
#ifdef CONFIG_TCP_AO
6478+
struct tcp_ao_info *ao;
6479+
6480+
ao = rcu_dereference_protected(tp->ao_info,
6481+
lockdep_sock_is_held(sk));
6482+
if (ao) {
6483+
WRITE_ONCE(ao->risn, th->seq);
6484+
ao->rcv_sne = 0;
6485+
}
6486+
#endif
64596487
tcp_set_state(sk, TCP_SYN_RECV);
64606488

64616489
if (tp->rx_opt.saw_tstamp) {

net/ipv4/tcp_ipv4.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ static bool tcp_v4_ao_sign_reset(const struct sock *sk, struct sk_buff *skb,
676676
u8 keyid;
677677

678678
rcu_read_lock();
679-
if (tcp_ao_prepare_reset(sk, skb, aoh, l3index,
679+
if (tcp_ao_prepare_reset(sk, skb, aoh, l3index, ntohl(reply->seq),
680680
&key, &traffic_key, &allocated_traffic_key,
681681
&keyid, &ao_sne))
682682
goto out;
@@ -1034,6 +1034,7 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
10341034
struct tcp_ao_key *rnext_key;
10351035

10361036
key.traffic_key = snd_other_key(key.ao_key);
1037+
key.sne = READ_ONCE(ao_info->snd_sne);
10371038
rnext_key = READ_ONCE(ao_info->rnext_key);
10381039
key.rcv_next = rnext_key->rcvid;
10391040
key.type = TCP_KEY_AO;

net/ipv4/tcp_minisocks.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ tcp_timewait_check_oow_rate_limit(struct inet_timewait_sock *tw,
5151
return TCP_TW_SUCCESS;
5252
}
5353

54+
static void twsk_rcv_nxt_update(struct tcp_timewait_sock *tcptw, u32 seq)
55+
{
56+
#ifdef CONFIG_TCP_AO
57+
struct tcp_ao_info *ao;
58+
59+
ao = rcu_dereference(tcptw->ao_info);
60+
if (unlikely(ao && seq < tcptw->tw_rcv_nxt))
61+
WRITE_ONCE(ao->rcv_sne, ao->rcv_sne + 1);
62+
#endif
63+
tcptw->tw_rcv_nxt = seq;
64+
}
65+
5466
/*
5567
* * Main purpose of TIME-WAIT state is to close connection gracefully,
5668
* when one of ends sits in LAST-ACK or CLOSING retransmitting FIN
@@ -136,7 +148,8 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
136148

137149
/* FIN arrived, enter true time-wait state. */
138150
tw->tw_substate = TCP_TIME_WAIT;
139-
tcptw->tw_rcv_nxt = TCP_SKB_CB(skb)->end_seq;
151+
twsk_rcv_nxt_update(tcptw, TCP_SKB_CB(skb)->end_seq);
152+
140153
if (tmp_opt.saw_tstamp) {
141154
tcptw->tw_ts_recent_stamp = ktime_get_seconds();
142155
tcptw->tw_ts_recent = tmp_opt.rcv_tsval;

net/ipv6/tcp_ipv6.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
10901090
int l3index;
10911091

10921092
l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0;
1093-
if (tcp_ao_prepare_reset(sk, skb, aoh, l3index,
1093+
if (tcp_ao_prepare_reset(sk, skb, aoh, l3index, seq,
10941094
&key.ao_key, &key.traffic_key,
10951095
&allocated_traffic_key,
10961096
&key.rcv_next, &key.sne))
@@ -1167,6 +1167,7 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
11671167
/* rcv_next switches to our rcv_next */
11681168
rnext_key = READ_ONCE(ao_info->rnext_key);
11691169
key.rcv_next = rnext_key->rcvid;
1170+
key.sne = READ_ONCE(ao_info->snd_sne);
11701171
key.type = TCP_KEY_AO;
11711172
#else
11721173
if (0) {

0 commit comments

Comments
 (0)