On 11/2/21 9:21 PM, Francesco Ruggeri wrote:
On Tue, Nov 2, 2021 at 3:03 AM Leonard Crestez <cdleonard@xxxxxxxxx> wrote:
On 11/1/21 9:22 PM, Francesco Ruggeri wrote:
+/* Compute SNE for a specific packet (by seq). */
+static int compute_packet_sne(struct sock *sk, struct tcp_authopt_info *info,
+ u32 seq, bool input, __be32 *sne)
+{
+ u32 rcv_nxt, snd_nxt;
+
+ // We can't use normal SNE computation before reaching TCP_ESTABLISHED
+ // For TCP_SYN_SENT the dst_isn field is initialized only after we
+ // validate the remote SYN/ACK
+ // For TCP_NEW_SYN_RECV there is no tcp_authopt_info at all
+ if (sk->sk_state == TCP_SYN_SENT ||
+ sk->sk_state == TCP_NEW_SYN_RECV ||
+ sk->sk_state == TCP_LISTEN)
+ return 0;
+
In case of TCP_NEW_SYN_RECV, if our SYNACK had sequence number
0xffffffff, we will receive an ACK sequence number of 0, which
should have sne = 1.
In a somewhat similar corner case, when we receive a SYNACK to
our SYN in tcp_rcv_synsent_state_process, if the SYNACK has
sequence number 0xffffffff, we set tp->rcv_nxt to 0, and we
should set sne to 1.
There may be more similar corner cases related to a wraparound
during the handshake.
Since as you pointed out all we need is "recent" valid <sne, seq>
pairs as reference, rather than relying on rcv_sne being paired
with tp->rcv_nxt (and similarly for snd_sne and tp->snd_nxt),
would it be easier to maintain reference <sne, seq> pairs for send
and receive in tcp_authopt_info, appropriately handle the different
handshake cases and initialize the pairs, and only then track them
in tcp_rcv_nxt_update and tcp_rcv_snd_update?
For TCP_NEW_SYN_RECV there is no struct tcp_authopt_info, only a request
minisock. I think those are deliberately kept small save resources on
SYN floods so I'd rather not increase their size.
For all the handshake cases we can just rely on SNE=0 for ISN and we
already need to keep track of ISNs because they're part of the signature.
Exactly. But the current code, when setting rcv_sne and snd_sne,
always compares the sequence number with the <info->rcv_sne, tp->rcv_nxt>
(or <info->snd_sne, tp->snd_nxt>) pair, where info->rcv_sne and
info->snd_sne are initialized to 0 at the time of info creation.
In other words, the code assumes that rcv_sne always corresponds to
tp->rcv_nxt, and snd_sne to tp->snd_nxt. But that may not be true
when info is created, on account of rollovers during a handshake.
So it is not just a matter of what to use for SNE before info is
created and used, but also how SNEs are initialized in info.
That is why I was suggesting of saving valid <sne, seq> pairs
(initialized with <0, ISN>) in tcp_authopt_info rather than just SNEs,
and then always compare seq to those pairs if info is available.
The pairs could then be updated in tcp_rcv_nxt_update and
tcp_snd_una_update.
You are correct that SNE will be initialized incorrectly if a rollover
happens during the handshake. I think this can be solved by initializing
SNE at the same time as ISN like this:
rcv_sne = compute_sne(0, disn, rcv_nxt);
snd_sne = compute_sne(0, sisn, snd_nxt);
This relies on initial sequence numbers having an extension of zero by
definition. The actual implementation is a bit more complicated but it
only needs to be done when transitioning into ESTABLISHED. I think this
would even work for FASTOPEN where non-zero payload is present in
handshake packets.
The SYN_SEND and SYN_RECV sockets are still special but they're also
special because they have to determine ISNs from the packet itself.
Since those sockets only compute signatures for packets with SYN bit ON
and where SEQ=ISN then SNE is again zero by definition.
I will write tests with client and server-side SEQs equal to 0xFFFFFFFF
to verify because this relies on actual initialization order details.
I think snd_nxt and rcv_nxt are good choices for SNE tracking because
the rest of the TCP state machine controls their advancement. In theory
it's possible to use any received SEQ value but then a very old or
perhaps malicious packet could cause incorrect updates to SNE.
If separate fields were used to track rcv_sne_seq and snd_sne_seq then
you would still need to only advance them for SEQ values which are known
to be valid. Doing so in lockstep with snd_nxt and rcv_nxt would still
make sense.
--
Regards,
Leonard