diff mbox series

[v2,11/25] tcp: authopt: Implement Sequence Number Extension

Message ID 6097ec24d87efc55962a1bfac9441132f0fc4206.1635784253.git.cdleonard@gmail.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series [v2,01/25] tcp: authopt: Initial support and key management | expand

Checks

Context Check Description
netdev/cover_letter warning Series does not have a cover letter
netdev/fixes_present success Fixes tag not required for -next series
netdev/patch_count fail Series longer than 15 patches (and no cover letter)
netdev/subject_prefix success Link
netdev/cc_maintainers success CCed 6 of 6 maintainers
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 24 this patch: 24
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success No Fixes tag
netdev/checkpatch warning CHECK: Please use a blank line after function/struct/union/enum declarations WARNING: line length of 86 exceeds 80 columns WARNING: line length of 95 exceeds 80 columns WARNING: line length of 96 exceeds 80 columns
netdev/build_allmodconfig_warn success Errors and warnings before: 24 this patch: 24
netdev/header_inline success No static functions without inline keyword in header files
netdev/tree_selection success Guessing tree name failed - patch did not apply, async

Commit Message

Leonard Crestez Nov. 1, 2021, 4:34 p.m. UTC
Add a compute_sne function which finds the value of SNE for a certain
SEQ given an already known "recent" SNE/SEQ. This is implemented using
the standard tcp before/after macro and will work for SEQ values that
are without 2^31 of the SEQ for which we know the SNE.

For updating we advance the value for rcv_sne at the same time as
rcv_nxt and for snd_sne at the same time as snd_nxt. We could track
other values (for example snd_una) but this is good enough and works
very easily for timewait socket.

This implementation is different from RFC suggestions and doesn't
require additional flags. It does pass tests from this draft:
    https://datatracker.ietf.org/doc/draft-touch-sne/

Signed-off-by: Leonard Crestez <cdleonard@gmail.com>
---
 include/net/tcp_authopt.h | 36 ++++++++++++++++++
 net/ipv4/tcp_authopt.c    | 80 ++++++++++++++++++++++++++++++++++++++-
 net/ipv4/tcp_input.c      |  1 +
 net/ipv4/tcp_output.c     |  1 +
 4 files changed, 116 insertions(+), 2 deletions(-)

Comments

Francesco Ruggeri Nov. 1, 2021, 7:22 p.m. UTC | #1
> +/* 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?

>  static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq)
>  {
>         u32 delta = seq - tp->rcv_nxt;
>
>         sock_owned_by_me((struct sock *)tp);
> +       tcp_authopt_update_rcv_sne(tp, seq);
>         tp->bytes_received += delta;
>         WRITE_ONCE(tp->rcv_nxt, seq);
>  }
>

Since rcv_sne and tp->rcv_nxt are not updated atomically, could
there ever be a case where a reader might use the new sne with
the old rcv_nxt?

Francesco
Eric Dumazet Nov. 1, 2021, 8:54 p.m. UTC | #2
On 11/1/21 9:34 AM, Leonard Crestez wrote:
> Add a compute_sne function which finds the value of SNE for a certain
> SEQ given an already known "recent" SNE/SEQ. This is implemented using
> the standard tcp before/after macro and will work for SEQ values that
> are without 2^31 of the SEQ for which we know the SNE.

>  }
> +void __tcp_authopt_update_rcv_sne(struct tcp_sock *tp, struct tcp_authopt_info *info, u32 seq);
> +static inline void tcp_authopt_update_rcv_sne(struct tcp_sock *tp, u32 seq)
> +{
> +	struct tcp_authopt_info *info;
> +
> +	if (static_branch_unlikely(&tcp_authopt_needed)) {
> +		rcu_read_lock();
> +		info = rcu_dereference(tp->authopt_info);
> +		if (info)
> +			__tcp_authopt_update_rcv_sne(tp, info, seq);
> +		rcu_read_unlock();
> +	}
> +}
> +void __tcp_authopt_update_snd_sne(struct tcp_sock *tp, struct tcp_authopt_info *info, u32 seq);
> +static inline void tcp_authopt_update_snd_sne(struct tcp_sock *tp, u32 seq)
> +{
> +	struct tcp_authopt_info *info;
> +
> +	if (static_branch_unlikely(&tcp_authopt_needed)) {
> +		rcu_read_lock();
> +		info = rcu_dereference(tp->authopt_info);
> +		if (info)
> +			__tcp_authopt_update_snd_sne(tp, info, seq);
> +		rcu_read_unlock();
> +	}
> +}
>

I would think callers of these helpers own socket lock,
so no rcu_read_lock()/unlock() should be needed.

Perhaps instead
rcu_dereference_protected(tp->authopt_info, lockdep_sock_is_held(sk));
Leonard Crestez Nov. 2, 2021, 9:50 a.m. UTC | #3
On 11/1/21 10:54 PM, Eric Dumazet wrote:
> On 11/1/21 9:34 AM, Leonard Crestez wrote:
>> Add a compute_sne function which finds the value of SNE for a certain
>> SEQ given an already known "recent" SNE/SEQ. This is implemented using
>> the standard tcp before/after macro and will work for SEQ values that
>> are without 2^31 of the SEQ for which we know the SNE.
> 
>>   }
>> +void __tcp_authopt_update_rcv_sne(struct tcp_sock *tp, struct tcp_authopt_info *info, u32 seq);
>> +static inline void tcp_authopt_update_rcv_sne(struct tcp_sock *tp, u32 seq)
>> +{
>> +	struct tcp_authopt_info *info;
>> +
>> +	if (static_branch_unlikely(&tcp_authopt_needed)) {
>> +		rcu_read_lock();
>> +		info = rcu_dereference(tp->authopt_info);
>> +		if (info)
>> +			__tcp_authopt_update_rcv_sne(tp, info, seq);
>> +		rcu_read_unlock();
>> +	}
>> +}
>> +void __tcp_authopt_update_snd_sne(struct tcp_sock *tp, struct tcp_authopt_info *info, u32 seq);
>> +static inline void tcp_authopt_update_snd_sne(struct tcp_sock *tp, u32 seq)
>> +{
>> +	struct tcp_authopt_info *info;
>> +
>> +	if (static_branch_unlikely(&tcp_authopt_needed)) {
>> +		rcu_read_lock();
>> +		info = rcu_dereference(tp->authopt_info);
>> +		if (info)
>> +			__tcp_authopt_update_snd_sne(tp, info, seq);
>> +		rcu_read_unlock();
>> +	}
>> +}
>>
> 
> I would think callers of these helpers own socket lock,
> so no rcu_read_lock()/unlock() should be needed.
> 
> Perhaps instead
> rcu_dereference_protected(tp->authopt_info, lockdep_sock_is_held(sk));

Yes, all the callers hold the socket lock and replacing rcu_read_lock 
doesn't trigger any RCU warnings.

--
Regards,
Leonard
Leonard Crestez Nov. 2, 2021, 10:03 a.m. UTC | #4
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.

I'll need to test handshake seq 0xFFFFFFFF deliberately, you're right 
that it can fail.

>>   static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq)
>>   {
>>          u32 delta = seq - tp->rcv_nxt;
>>
>>          sock_owned_by_me((struct sock *)tp);
>> +       tcp_authopt_update_rcv_sne(tp, seq);
>>          tp->bytes_received += delta;
>>          WRITE_ONCE(tp->rcv_nxt, seq);
>>   }
>>
> 
> Since rcv_sne and tp->rcv_nxt are not updated atomically, could
> there ever be a case where a reader might use the new sne with
> the old rcv_nxt?

As far as I understand if all of the read and writes to SNE happen under 
the socket lock it should be fine. I don't know why WRITE_ONCE is used 
here, maybe somebody else wants to read rcv_nxt outside the socket lock? 
That doesn't matter for SNE.

I think the only case would be sending ipv4 RSTs outside the socket.

--
Regards,
Leonard
Francesco Ruggeri Nov. 2, 2021, 7:21 p.m. UTC | #5
On Tue, Nov 2, 2021 at 3:03 AM Leonard Crestez <cdleonard@gmail.com> 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.

Regards,
Francesco
Leonard Crestez Nov. 3, 2021, 10:01 p.m. UTC | #6
On 11/2/21 9:21 PM, Francesco Ruggeri wrote:
> On Tue, Nov 2, 2021 at 3:03 AM Leonard Crestez <cdleonard@gmail.com> 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
diff mbox series

Patch

diff --git a/include/net/tcp_authopt.h b/include/net/tcp_authopt.h
index a505db1dd67b..7360bda20f97 100644
--- a/include/net/tcp_authopt.h
+++ b/include/net/tcp_authopt.h
@@ -62,10 +62,14 @@  struct tcp_authopt_info {
 	u32 flags;
 	/** @src_isn: Local Initial Sequence Number */
 	u32 src_isn;
 	/** @dst_isn: Remote Initial Sequence Number */
 	u32 dst_isn;
+	/** @rcv_sne: Recv-side Sequence Number Extension tracking tcp_sock.rcv_nxt */
+	u32 rcv_sne;
+	/** @snd_sne: Send-side Sequence Number Extension tracking tcp_sock.snd_nxt */
+	u32 snd_sne;
 };
 
 #ifdef CONFIG_TCP_AUTHOPT
 extern int sysctl_tcp_authopt;
 DECLARE_STATIC_KEY_FALSE(tcp_authopt_needed);
@@ -143,10 +147,36 @@  static inline int tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb
 			return __tcp_authopt_inbound_check(sk, skb, info);
 	}
 
 	return 0;
 }
+void __tcp_authopt_update_rcv_sne(struct tcp_sock *tp, struct tcp_authopt_info *info, u32 seq);
+static inline void tcp_authopt_update_rcv_sne(struct tcp_sock *tp, u32 seq)
+{
+	struct tcp_authopt_info *info;
+
+	if (static_branch_unlikely(&tcp_authopt_needed)) {
+		rcu_read_lock();
+		info = rcu_dereference(tp->authopt_info);
+		if (info)
+			__tcp_authopt_update_rcv_sne(tp, info, seq);
+		rcu_read_unlock();
+	}
+}
+void __tcp_authopt_update_snd_sne(struct tcp_sock *tp, struct tcp_authopt_info *info, u32 seq);
+static inline void tcp_authopt_update_snd_sne(struct tcp_sock *tp, u32 seq)
+{
+	struct tcp_authopt_info *info;
+
+	if (static_branch_unlikely(&tcp_authopt_needed)) {
+		rcu_read_lock();
+		info = rcu_dereference(tp->authopt_info);
+		if (info)
+			__tcp_authopt_update_snd_sne(tp, info, seq);
+		rcu_read_unlock();
+	}
+}
 #else
 static inline int tcp_set_authopt(struct sock *sk, sockptr_t optval, unsigned int optlen)
 {
 	return -ENOPROTOOPT;
 }
@@ -185,8 +215,14 @@  static inline void tcp_authopt_time_wait(
 }
 static inline int tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb)
 {
 	return 0;
 }
+static inline void tcp_authopt_update_rcv_sne(struct tcp_sock *tp, u32 seq)
+{
+}
+static inline void tcp_authopt_update_snd_sne(struct tcp_sock *tp, u32 seq)
+{
+}
 #endif
 
 #endif /* _LINUX_TCP_AUTHOPT_H */
diff --git a/net/ipv4/tcp_authopt.c b/net/ipv4/tcp_authopt.c
index 7c49dcce7d24..a48b741c83e4 100644
--- a/net/ipv4/tcp_authopt.c
+++ b/net/ipv4/tcp_authopt.c
@@ -968,10 +968,84 @@  static int skb_shash_frags(struct shash_desc *desc,
 	}
 
 	return 0;
 }
 
+/* compute_sne - Calculate Sequence Number Extension
+ *
+ * Give old upper/lower 32bit values and a new lower 32bit value determine the
+ * new value of the upper 32 bit. The new sequence number can be 2^31 before or
+ * after prev_seq but TCP window scaling should limit this further.
+ *
+ * For correct accounting the stored SNE value should be only updated together
+ * with the SEQ.
+ */
+static u32 compute_sne(u32 sne, u32 prev_seq, u32 seq)
+{
+	if (before(seq, prev_seq)) {
+		if (seq > prev_seq)
+			--sne;
+	} else {
+		if (seq < prev_seq)
+			++sne;
+	}
+
+	return sne;
+}
+
+/* Update rcv_sne, must be called immediately before rcv_nxt update */
+void __tcp_authopt_update_rcv_sne(struct tcp_sock *tp,
+				  struct tcp_authopt_info *info, u32 seq)
+{
+	info->rcv_sne = compute_sne(info->rcv_sne, tp->rcv_nxt, seq);
+}
+
+/* Update snd_sne, must be called immediately before snd_nxt update */
+void __tcp_authopt_update_snd_sne(struct tcp_sock *tp,
+				  struct tcp_authopt_info *info, u32 seq)
+{
+	info->snd_sne = compute_sne(info->snd_sne, tp->snd_nxt, seq);
+}
+
+/* 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;
+
+	if (sk->sk_state == TCP_TIME_WAIT) {
+		rcv_nxt = tcp_twsk(sk)->tw_rcv_nxt;
+		snd_nxt = tcp_twsk(sk)->tw_snd_nxt;
+	} else {
+		if (WARN_ONCE(!sk_fullsock(sk),
+			      "unexpected minisock sk=%p state=%d", sk,
+			      sk->sk_state))
+			return -EINVAL;
+		rcv_nxt = tcp_sk(sk)->rcv_nxt;
+		snd_nxt = tcp_sk(sk)->snd_nxt;
+	}
+
+	if (WARN_ONCE(!info, "unexpected missing info for sk=%p sk_state=%d", sk, sk->sk_state))
+		return -EINVAL;
+
+	if (input)
+		*sne = htonl(compute_sne(info->rcv_sne, rcv_nxt, seq));
+	else
+		*sne = htonl(compute_sne(info->snd_sne, snd_nxt, seq));
+
+	return 0;
+}
+
 static int tcp_authopt_hash_packet(struct crypto_shash *tfm,
 				   struct sock *sk,
 				   struct sk_buff *skb,
 				   struct tcp_authopt_info *info,
 				   bool input,
@@ -979,14 +1053,16 @@  static int tcp_authopt_hash_packet(struct crypto_shash *tfm,
 				   bool include_options,
 				   u8 *macbuf)
 {
 	struct tcphdr *th = tcp_hdr(skb);
 	SHASH_DESC_ON_STACK(desc, tfm);
+	__be32 sne = 0;
 	int err;
 
-	/* NOTE: SNE unimplemented */
-	__be32 sne = 0;
+	err = compute_packet_sne(sk, info, ntohl(th->seq), input, &sne);
+	if (err)
+		return err;
 
 	desc->tfm = tfm;
 	err = crypto_shash_init(desc);
 	if (err)
 		return err;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 5dcde6e74bfc..0ac74e621b4e 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3517,10 +3517,11 @@  static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack)
 static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq)
 {
 	u32 delta = seq - tp->rcv_nxt;
 
 	sock_owned_by_me((struct sock *)tp);
+	tcp_authopt_update_rcv_sne(tp, seq);
 	tp->bytes_received += delta;
 	WRITE_ONCE(tp->rcv_nxt, seq);
 }
 
 /* Update our send window.
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 1e5acc5a38cf..ea53c24747b9 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -67,10 +67,11 @@  static void tcp_event_new_data_sent(struct sock *sk, struct sk_buff *skb)
 {
 	struct inet_connection_sock *icsk = inet_csk(sk);
 	struct tcp_sock *tp = tcp_sk(sk);
 	unsigned int prior_packets = tp->packets_out;
 
+	tcp_authopt_update_snd_sne(tp, TCP_SKB_CB(skb)->end_seq);
 	WRITE_ONCE(tp->snd_nxt, TCP_SKB_CB(skb)->end_seq);
 
 	__skb_unlink(skb, &sk->sk_write_queue);
 	tcp_rbtree_insert(&sk->tcp_rtx_queue, skb);