Page MenuHomeFreeBSD

D45894.diff
No OneTemporary

D45894.diff

diff --git a/share/man/man4/tcp.4 b/share/man/man4/tcp.4
--- a/share/man/man4/tcp.4
+++ b/share/man/man4/tcp.4
@@ -31,7 +31,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd November 30, 2023
+.Dd July 6, 2024
.Dt TCP 4
.Os
.Sh NAME
@@ -699,6 +699,9 @@
.It Va insecure_syn
Use criteria defined in RFC793 instead of RFC5961 for accepting SYN segments.
Default is false.
+.It Va insecure_ack
+Use criteria defined in RFC793 for validating SEG.ACK.
+Default is false.
.It Va isn_reseed_interval
The interval (in seconds) specifying how often the secret data used in
RFC 1948 initial sequence number calculations should be reseeded.
diff --git a/sys/netinet/in_kdtrace.h b/sys/netinet/in_kdtrace.h
--- a/sys/netinet/in_kdtrace.h
+++ b/sys/netinet/in_kdtrace.h
@@ -330,6 +330,9 @@
SDT_PROBE_DECLARE(mib, tcp, count, tcps_tlpresends);
SDT_PROBE_DECLARE(mib, tcp, count, tcps_tlpresend_bytes);
+
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_rcvghostack);
+SDT_PROBE_DECLARE(mib, tcp, count, tcps_rcvacktooold);
#endif
SDT_PROBE_DECLARE(ip, , , receive);
diff --git a/sys/netinet/in_kdtrace.c b/sys/netinet/in_kdtrace.c
--- a/sys/netinet/in_kdtrace.c
+++ b/sys/netinet/in_kdtrace.c
@@ -339,6 +339,8 @@
MIB_PROBE_TCP(tcps_tlpresends);
MIB_PROBE_TCP(tcps_tlpresend_bytes);
+MIB_PROBE_TCP(tcps_rcvghostack);
+MIB_PROBE_TCP(tcps_rcvacktooold);
#endif
SDT_PROBE_DEFINE6_XLATE(ip, , , receive,
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -202,6 +202,11 @@
&VNET_NAME(tcp_insecure_rst), 0,
"Follow RFC793 instead of RFC5961 criteria for accepting RST packets");
+VNET_DEFINE(int, tcp_insecure_ack) = 0;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, insecure_ack, CTLFLAG_VNET | CTLFLAG_RW,
+ &VNET_NAME(tcp_insecure_ack), 0,
+ "Follow RFC793 criteria for validating SEG.ACK");
+
VNET_DEFINE(int, tcp_recvspace) = 1024*64;
#define V_tcp_recvspace VNET(tcp_recvspace)
SYSCTL_INT(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, CTLFLAG_VNET | CTLFLAG_RW,
@@ -2438,6 +2443,45 @@
/*
* Ack processing.
*/
+ if (SEQ_GEQ(tp->snd_una, tp->iss + (65535 << tp->snd_scale))) {
+ /* Checking SEG.ACK against ISS is definitely redundant. */
+ tp->t_flags2 |= TF2_NO_ISS_CHECK;
+ }
+ if (!V_tcp_insecure_ack) {
+ tcp_seq seq_min;
+ bool ghost_ack_check;
+
+ if (tp->t_flags2 & TF2_NO_ISS_CHECK) {
+ /* Check for too old ACKs (RFC 5961, Section 5.2). */
+ seq_min = tp->snd_una - tp->max_sndwnd;
+ ghost_ack_check = false;
+ } else {
+ if (SEQ_GT(tp->iss + 1, tp->snd_una - tp->max_sndwnd)) {
+ /* Checking for ghost ACKs is stricter. */
+ seq_min = tp->iss + 1;
+ ghost_ack_check = true;
+ } else {
+ /*
+ * Checking for too old ACKs (RFC 5961,
+ * Section 5.2) is stricter.
+ */
+ seq_min = tp->snd_una - tp->max_sndwnd;
+ ghost_ack_check = false;
+ }
+ }
+ if (SEQ_LT(th->th_ack, seq_min)) {
+ if (ghost_ack_check)
+ TCPSTAT_INC(tcps_rcvghostack);
+ else
+ TCPSTAT_INC(tcps_rcvacktooold);
+ /* Send a challenge ACK. */
+ tcp_respond(tp, mtod(m, void *), th, m,
+ tp->rcv_nxt, tp->snd_nxt, TH_ACK);
+ tp->last_ack_sent = tp->rcv_nxt;
+ m = NULL;
+ goto drop;
+ }
+ }
switch (tp->t_state) {
/*
* In SYN_RECEIVED state, the ack ACKs our SYN, so enter
diff --git a/sys/netinet/tcp_stacks/bbr.c b/sys/netinet/tcp_stacks/bbr.c
--- a/sys/netinet/tcp_stacks/bbr.c
+++ b/sys/netinet/tcp_stacks/bbr.c
@@ -7711,6 +7711,43 @@
bbr = (struct tcp_bbr *)tp->t_fb_ptr;
lost = bbr->r_ctl.rc_lost;
nsegs = max(1, m->m_pkthdr.lro_nsegs);
+ if (SEQ_GEQ(tp->snd_una, tp->iss + (65535 << tp->snd_scale))) {
+ /* Checking SEG.ACK against ISS is definitely redundant. */
+ tp->t_flags2 |= TF2_NO_ISS_CHECK;
+ }
+ if (!V_tcp_insecure_ack) {
+ tcp_seq seq_min;
+ bool ghost_ack_check;
+
+ if (tp->t_flags2 & TF2_NO_ISS_CHECK) {
+ /* Check for too old ACKs (RFC 5961, Section 5.2). */
+ seq_min = tp->snd_una - tp->max_sndwnd;
+ ghost_ack_check = false;
+ } else {
+ if (SEQ_GT(tp->iss + 1, tp->snd_una - tp->max_sndwnd)) {
+ /* Checking for ghost ACKs is stricter. */
+ seq_min = tp->iss + 1;
+ ghost_ack_check = true;
+ } else {
+ /*
+ * Checking for too old ACKs (RFC 5961,
+ * Section 5.2) is stricter.
+ */
+ seq_min = tp->snd_una - tp->max_sndwnd;
+ ghost_ack_check = false;
+ }
+ }
+ if (SEQ_LT(th->th_ack, seq_min)) {
+ if (ghost_ack_check)
+ TCPSTAT_INC(tcps_rcvghostack);
+ else
+ TCPSTAT_INC(tcps_rcvacktooold);
+ /* Send challenge ACK. */
+ ctf_do_dropafterack(m, tp, th, thflags, tlen, ret_val);
+ bbr->r_wanted_output = 1;
+ return (1);
+ }
+ }
if (SEQ_GT(th->th_ack, tp->snd_max)) {
ctf_do_dropafterack(m, tp, th, thflags, tlen, ret_val);
bbr->r_wanted_output = 1;
diff --git a/sys/netinet/tcp_stacks/rack.c b/sys/netinet/tcp_stacks/rack.c
--- a/sys/netinet/tcp_stacks/rack.c
+++ b/sys/netinet/tcp_stacks/rack.c
@@ -12472,6 +12472,45 @@
INP_WLOCK_ASSERT(tptoinpcb(tp));
rack = (struct tcp_rack *)tp->t_fb_ptr;
+ if (SEQ_GEQ(tp->snd_una, tp->iss + (65535 << tp->snd_scale))) {
+ /* Checking SEG.ACK against ISS is definitely redundant. */
+ tp->t_flags2 |= TF2_NO_ISS_CHECK;
+ }
+ if (!V_tcp_insecure_ack) {
+ tcp_seq seq_min;
+ bool ghost_ack_check;
+
+ if (tp->t_flags2 & TF2_NO_ISS_CHECK) {
+ /* Check for too old ACKs (RFC 5961, Section 5.2). */
+ seq_min = tp->snd_una - tp->max_sndwnd;
+ ghost_ack_check = false;
+ } else {
+ if (SEQ_GT(tp->iss + 1, tp->snd_una - tp->max_sndwnd)) {
+ /* Checking for ghost ACKs is stricter. */
+ seq_min = tp->iss + 1;
+ ghost_ack_check = true;
+ } else {
+ /*
+ * Checking for too old ACKs (RFC 5961,
+ * Section 5.2) is stricter.
+ */
+ seq_min = tp->snd_una - tp->max_sndwnd;
+ ghost_ack_check = false;
+ }
+ }
+ if (SEQ_LT(th->th_ack, seq_min)) {
+ if (ghost_ack_check)
+ TCPSTAT_INC(tcps_rcvghostack);
+ else
+ TCPSTAT_INC(tcps_rcvacktooold);
+ /* Send challenge ACK. */
+ __ctf_do_dropafterack(m, tp, th, thflags, tlen, ret_val,
+ &rack->r_ctl.challenge_ack_ts,
+ &rack->r_ctl.challenge_ack_cnt);
+ rack->r_wanted_output = 1;
+ return (1);
+ }
+ }
if (SEQ_GT(th->th_ack, tp->snd_max)) {
__ctf_do_dropafterack(m, tp, th, thflags, tlen, ret_val,
&rack->r_ctl.challenge_ack_ts,
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -848,6 +848,7 @@
#define TF2_CANNOT_DO_ECN 0x00080000 /* The stack does not do ECN */
#define TF2_PROC_SACK_PROHIBIT 0x00100000 /* Due to small MSS size do not process sack's */
#define TF2_IPSEC_TSO 0x00200000 /* IPSEC + TSO supported */
+#define TF2_NO_ISS_CHECK 0x00200000 /* Don't check SEG.ACK against ISS */
/*
* Structure to hold TCP options that are only used during segment
@@ -1089,8 +1090,12 @@
uint64_t tcps_tlpresends; /* number of tlp resends */
uint64_t tcps_tlpresend_bytes; /* number of bytes resent by tlp */
+ /* SEG.ACK validation failures */
+ uint64_t tcps_rcvghostack; /* received ACK for data never sent */
+ uint64_t tcps_rcvacktooold; /* received ACK for data too long ago */
- uint64_t _pad[3]; /* 3 TBD placeholder for STABLE */
+
+ uint64_t _pad[1]; /* 1 TBD placeholder for STABLE */
};
#define tcps_rcvmemdrop tcps_rcvreassfull /* compat */
@@ -1280,6 +1285,7 @@
VNET_DECLARE(int, tcp_initcwnd_segments);
VNET_DECLARE(int, tcp_insecure_rst);
VNET_DECLARE(int, tcp_insecure_syn);
+VNET_DECLARE(int, tcp_insecure_ack);
VNET_DECLARE(uint32_t, tcp_map_entries_limit);
VNET_DECLARE(uint32_t, tcp_map_split_limit);
VNET_DECLARE(int, tcp_minmss);
@@ -1327,6 +1333,7 @@
#define V_tcp_initcwnd_segments VNET(tcp_initcwnd_segments)
#define V_tcp_insecure_rst VNET(tcp_insecure_rst)
#define V_tcp_insecure_syn VNET(tcp_insecure_syn)
+#define V_tcp_insecure_ack VNET(tcp_insecure_ack)
#define V_tcp_map_entries_limit VNET(tcp_map_entries_limit)
#define V_tcp_map_split_limit VNET(tcp_map_split_limit)
#define V_tcp_minmss VNET(tcp_minmss)
diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c
--- a/usr.bin/netstat/inet.c
+++ b/usr.bin/netstat/inet.c
@@ -642,8 +642,12 @@
"{N:/UDP tunneled pkt%s}\n");
p(tcps_tunneled_errs, "\t\t{:received-bad-udp-tunneled-pkts/%ju} "
"{N:/UDP tunneled pkt cnt with error%s}\n");
- p(tcps_rcvacktoomuch, "\t\t{:received-acks-for-unsent-data/%ju} "
- "{N:/ack%s for unsent data}\n");
+ p(tcps_rcvacktoomuch, "\t\t{:received-acks-for-data-not-yet-sent/%ju} "
+ "{N:/ack%s for data not yet sent}\n");
+ p(tcps_rcvghostack, "\t\t{:received-acks-for-data-never-been-sent/%ju} "
+ "{N:/ack%s for data never been sent (ghost acks)}\n");
+ p(tcps_rcvacktooold, "\t\t{:received-acks-for-data-being-too-old/%ju} "
+ "{N:/ack%s for data being too old}\n");
p2(tcps_rcvpack, tcps_rcvbyte, "\t\t"
"{:received-in-sequence-packets/%ju} {N:/packet%s} "
"({:received-in-sequence-bytes/%ju} {N:/byte%s}) "

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 17, 7:20 AM (21 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14672411
Default Alt Text
D45894.diff (8 KB)

Event Timeline