Address concerns about CPU usage while doing TCP reassembly.

Currently, the per-queue limit is a function of the receive buffer
size and the MSS.  In certain cases (such as connections with large
receive buffers), the per-queue segment limit can be quite large.
Because we process segments as a linked list, large queues may not
perform acceptably.

The better long-term solution is to make the queue more efficient.
But, in the short-term, we can provide a way for a system
administrator to set the maximum queue size.

We set the default queue limit to 100.  This is an effort to balance
performance with a sane resource limit.  Depending on their
environment, goals, etc., an administrator may choose to modify this
limit in either direction.

Reviewed by:	jhb
Approved by:	so
Security:	FreeBSD-SA-18:08.tcp
Security:	CVE-2018-6922
This commit is contained in:
Jonathan T. Looney 2018-08-06 17:36:57 +00:00
parent 7b7dbfa9b3
commit 95a914f631
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=337384
2 changed files with 25 additions and 1 deletions

View File

@ -445,6 +445,20 @@ no reseeding will occur.
Reseeding should not be necessary, and will break
.Dv TIME_WAIT
recycling for a few minutes.
.It Va reass.cursegments
The current total number of segments present in all reassembly queues.
.It Va reass.maxsegments
The maximum limit on the total number of segments across all reassembly
queues.
The limit can be adjusted as a tunable.
.It Va reass.maxqueuelen
The maximum number of segments allowed in each reassembly queue.
By default, the system chooses a limit based on each TCP connection's
receive buffer size and maximum segment size (MSS).
The actual limit applied to a session's reassembly queue will be the lower of
the system-calculated automatic limit and the user-specified
.Va reass.maxqueuelen
limit.
.It Va rexmit_min , rexmit_slop
Adjust the retransmit timer calculation for
.Tn TCP .

View File

@ -91,6 +91,11 @@ SYSCTL_UMA_CUR(_net_inet_tcp_reass, OID_AUTO, cursegments, 0,
&tcp_reass_zone,
"Global number of TCP Segments currently in Reassembly Queue");
static u_int tcp_reass_maxqueuelen = 100;
SYSCTL_UINT(_net_inet_tcp_reass, OID_AUTO, maxqueuelen, CTLFLAG_RWTUN,
&tcp_reass_maxqueuelen, 0,
"Maximum number of TCP Segments per Reassembly Queue");
/* Initialize TCP reassembly queue */
static void
tcp_reass_zone_change(void *tag)
@ -170,6 +175,10 @@ tcp_reass(struct tcpcb *tp, struct tcphdr *th, int *tlenp, struct mbuf *m)
* socket receive buffer determines our advertised window and grows
* automatically when socket buffer autotuning is enabled. Use it as the
* basis for our queue limit.
*
* However, allow the user to specify a ceiling for the number of
* segments in each queue.
*
* Always let the missing segment through which caused this queue.
* NB: Access to the socket buffer is left intentionally unlocked as we
* can tolerate stale information here.
@ -180,7 +189,8 @@ tcp_reass(struct tcpcb *tp, struct tcphdr *th, int *tlenp, struct mbuf *m)
* is understood.
*/
if ((th->th_seq != tp->rcv_nxt || !TCPS_HAVEESTABLISHED(tp->t_state)) &&
tp->t_segqlen >= (so->so_rcv.sb_hiwat / tp->t_maxseg) + 1) {
tp->t_segqlen >= min((so->so_rcv.sb_hiwat / tp->t_maxseg) + 1,
tcp_reass_maxqueuelen)) {
TCPSTAT_INC(tcps_rcvreassfull);
*tlenp = 0;
if ((s = tcp_log_addrs(&tp->t_inpcb->inp_inc, th, NULL, NULL))) {