]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet/tcp_timewait.c
add -n option to suppress clearing the build tree and add -DNO_CLEAN
[FreeBSD/FreeBSD.git] / sys / netinet / tcp_timewait.c
1 /*-
2  * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  *      @(#)tcp_subr.c  8.2 (Berkeley) 5/24/95
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include "opt_inet.h"
36 #include "opt_inet6.h"
37 #include "opt_mac.h"
38 #include "opt_tcpdebug.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/callout.h>
43 #include <sys/kernel.h>
44 #include <sys/sysctl.h>
45 #include <sys/malloc.h>
46 #include <sys/mbuf.h>
47 #include <sys/priv.h>
48 #include <sys/proc.h>
49 #include <sys/socket.h>
50 #include <sys/socketvar.h>
51 #include <sys/protosw.h>
52 #include <sys/random.h>
53 #include <sys/vimage.h>
54
55 #include <vm/uma.h>
56
57 #include <net/route.h>
58 #include <net/if.h>
59
60 #include <netinet/in.h>
61 #include <netinet/in_systm.h>
62 #include <netinet/ip.h>
63 #ifdef INET6
64 #include <netinet/ip6.h>
65 #endif
66 #include <netinet/in_pcb.h>
67 #ifdef INET6
68 #include <netinet6/in6_pcb.h>
69 #endif
70 #include <netinet/in_var.h>
71 #include <netinet/ip_var.h>
72 #ifdef INET6
73 #include <netinet6/ip6_var.h>
74 #include <netinet6/scope6_var.h>
75 #include <netinet6/nd6.h>
76 #endif
77 #include <netinet/ip_icmp.h>
78 #include <netinet/tcp.h>
79 #include <netinet/tcp_fsm.h>
80 #include <netinet/tcp_seq.h>
81 #include <netinet/tcp_timer.h>
82 #include <netinet/tcp_var.h>
83 #ifdef INET6
84 #include <netinet6/tcp6_var.h>
85 #endif
86 #include <netinet/tcpip.h>
87 #ifdef TCPDEBUG
88 #include <netinet/tcp_debug.h>
89 #endif
90 #include <netinet6/ip6protosw.h>
91
92 #include <machine/in_cksum.h>
93
94 #include <security/mac/mac_framework.h>
95
96 static uma_zone_t tcptw_zone;
97 static int      maxtcptw;
98
99 /*
100  * The timed wait queue contains references to each of the TCP sessions
101  * currently in the TIME_WAIT state.  The queue pointers, including the
102  * queue pointers in each tcptw structure, are protected using the global
103  * tcbinfo lock, which must be held over queue iteration and modification.
104  */
105 static TAILQ_HEAD(, tcptw)      twq_2msl;
106
107 static void     tcp_tw_2msl_reset(struct tcptw *, int);
108 static void     tcp_tw_2msl_stop(struct tcptw *);
109
110 static int
111 tcptw_auto_size(void)
112 {
113         INIT_VNET_INET(curvnet);
114         int halfrange;
115
116         /*
117          * Max out at half the ephemeral port range so that TIME_WAIT
118          * sockets don't tie up too many ephemeral ports.
119          */
120         if (V_ipport_lastauto > V_ipport_firstauto)
121                 halfrange = (V_ipport_lastauto - V_ipport_firstauto) / 2;
122         else
123                 halfrange = (V_ipport_firstauto - V_ipport_lastauto) / 2;
124         /* Protect against goofy port ranges smaller than 32. */
125         return (imin(imax(halfrange, 32), maxsockets / 5));
126 }
127
128 static int
129 sysctl_maxtcptw(SYSCTL_HANDLER_ARGS)
130 {
131         int error, new;
132
133         if (maxtcptw == 0)
134                 new = tcptw_auto_size();
135         else
136                 new = maxtcptw;
137         error = sysctl_handle_int(oidp, &new, 0, req);
138         if (error == 0 && req->newptr)
139                 if (new >= 32) {
140                         maxtcptw = new;
141                         uma_zone_set_max(tcptw_zone, maxtcptw);
142                 }
143         return (error);
144 }
145
146 SYSCTL_PROC(_net_inet_tcp, OID_AUTO, maxtcptw, CTLTYPE_INT|CTLFLAG_RW,
147     &maxtcptw, 0, sysctl_maxtcptw, "IU",
148     "Maximum number of compressed TCP TIME_WAIT entries");
149
150 static int      nolocaltimewait = 0;
151 SYSCTL_INT(_net_inet_tcp, OID_AUTO, nolocaltimewait, CTLFLAG_RW,
152     &nolocaltimewait, 0,
153     "Do not create compressed TCP TIME_WAIT entries for local connections");
154
155 void
156 tcp_tw_zone_change(void)
157 {
158
159         if (maxtcptw == 0)
160                 uma_zone_set_max(tcptw_zone, tcptw_auto_size());
161 }
162
163 void
164 tcp_tw_init(void)
165 {
166         INIT_VNET_INET(curvnet);
167
168         tcptw_zone = uma_zcreate("tcptw", sizeof(struct tcptw),
169             NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
170         TUNABLE_INT_FETCH("net.inet.tcp.maxtcptw", &maxtcptw);
171         if (maxtcptw == 0)
172                 uma_zone_set_max(tcptw_zone, tcptw_auto_size());
173         else
174                 uma_zone_set_max(tcptw_zone, maxtcptw);
175         TAILQ_INIT(&V_twq_2msl);
176 }
177
178 /*
179  * Move a TCP connection into TIME_WAIT state.
180  *    tcbinfo is locked.
181  *    inp is locked, and is unlocked before returning.
182  */
183 void
184 tcp_twstart(struct tcpcb *tp)
185 {
186 #if defined(INVARIANTS) || defined(INVARIANT_SUPPORT)
187         INIT_VNET_INET(tp->t_vnet);
188 #endif
189         struct tcptw *tw;
190         struct inpcb *inp = tp->t_inpcb;
191         int acknow;
192         struct socket *so;
193
194         INP_INFO_WLOCK_ASSERT(&V_tcbinfo);      /* tcp_tw_2msl_reset(). */
195         INP_WLOCK_ASSERT(inp);
196
197         if (V_nolocaltimewait && in_localip(inp->inp_faddr)) {
198                 tp = tcp_close(tp);
199                 if (tp != NULL)
200                         INP_WUNLOCK(inp);
201                 return;
202         }
203
204         tw = uma_zalloc(tcptw_zone, M_NOWAIT);
205         if (tw == NULL) {
206                 tw = tcp_tw_2msl_scan(1);
207                 if (tw == NULL) {
208                         tp = tcp_close(tp);
209                         if (tp != NULL)
210                                 INP_WUNLOCK(inp);
211                         return;
212                 }
213         }
214         tw->tw_inpcb = inp;
215
216         /*
217          * Recover last window size sent.
218          */
219         tw->last_win = (tp->rcv_adv - tp->rcv_nxt) >> tp->rcv_scale;
220
221         /*
222          * Set t_recent if timestamps are used on the connection.
223          */
224         if ((tp->t_flags & (TF_REQ_TSTMP|TF_RCVD_TSTMP|TF_NOOPT)) ==
225             (TF_REQ_TSTMP|TF_RCVD_TSTMP)) {
226                 tw->t_recent = tp->ts_recent;
227                 tw->ts_offset = tp->ts_offset;
228         } else {
229                 tw->t_recent = 0;
230                 tw->ts_offset = 0;
231         }
232
233         tw->snd_nxt = tp->snd_nxt;
234         tw->rcv_nxt = tp->rcv_nxt;
235         tw->iss     = tp->iss;
236         tw->irs     = tp->irs;
237         tw->t_starttime = tp->t_starttime;
238         tw->tw_time = 0;
239
240 /* XXX
241  * If this code will
242  * be used for fin-wait-2 state also, then we may need
243  * a ts_recent from the last segment.
244  */
245         acknow = tp->t_flags & TF_ACKNOW;
246
247         /*
248          * First, discard tcpcb state, which includes stopping its timers and
249          * freeing it.  tcp_discardcb() used to also release the inpcb, but
250          * that work is now done in the caller.
251          *
252          * Note: soisdisconnected() call used to be made in tcp_discardcb(),
253          * and might not be needed here any longer.
254          */
255         tcp_discardcb(tp);
256         so = inp->inp_socket;
257         soisdisconnected(so);
258         tw->tw_cred = crhold(so->so_cred);
259         SOCK_LOCK(so);
260         tw->tw_so_options = so->so_options;
261         SOCK_UNLOCK(so);
262         if (acknow)
263                 tcp_twrespond(tw, TH_ACK);
264         inp->inp_ppcb = tw;
265         inp->inp_vflag |= INP_TIMEWAIT;
266         tcp_tw_2msl_reset(tw, 0);
267
268         /*
269          * If the inpcb owns the sole reference to the socket, then we can
270          * detach and free the socket as it is not needed in time wait.
271          */
272         if (inp->inp_vflag & INP_SOCKREF) {
273                 KASSERT(so->so_state & SS_PROTOREF,
274                     ("tcp_twstart: !SS_PROTOREF"));
275                 inp->inp_vflag &= ~INP_SOCKREF;
276                 INP_WUNLOCK(inp);
277                 ACCEPT_LOCK();
278                 SOCK_LOCK(so);
279                 so->so_state &= ~SS_PROTOREF;
280                 sofree(so);
281         } else
282                 INP_WUNLOCK(inp);
283 }
284
285 #if 0
286 /*
287  * The appromixate rate of ISN increase of Microsoft TCP stacks;
288  * the actual rate is slightly higher due to the addition of
289  * random positive increments.
290  *
291  * Most other new OSes use semi-randomized ISN values, so we
292  * do not need to worry about them.
293  */
294 #define MS_ISN_BYTES_PER_SECOND         250000
295
296 /*
297  * Determine if the ISN we will generate has advanced beyond the last
298  * sequence number used by the previous connection.  If so, indicate
299  * that it is safe to recycle this tw socket by returning 1.
300  */
301 int
302 tcp_twrecycleable(struct tcptw *tw)
303 {
304         INIT_VNET_INET(curvnet);
305         tcp_seq new_iss = tw->iss;
306         tcp_seq new_irs = tw->irs;
307
308         INP_INFO_WLOCK_ASSERT(&V_tcbinfo);
309         new_iss += (ticks - tw->t_starttime) * (ISN_BYTES_PER_SECOND / hz);
310         new_irs += (ticks - tw->t_starttime) * (MS_ISN_BYTES_PER_SECOND / hz);
311
312         if (SEQ_GT(new_iss, tw->snd_nxt) && SEQ_GT(new_irs, tw->rcv_nxt))
313                 return (1);
314         else
315                 return (0);
316 }
317 #endif
318
319 /*
320  * Returns 1 if the TIME_WAIT state was killed and we should start over,
321  * looking for a pcb in the listen state.  Returns 0 otherwise.
322  */
323 int
324 tcp_twcheck(struct inpcb *inp, struct tcpopt *to, struct tcphdr *th,
325     struct mbuf *m, int tlen)
326 {
327 #if defined(INVARIANTS) || defined(INVARIANT_SUPPORT)
328         INIT_VNET_INET(curvnet);
329 #endif
330         struct tcptw *tw;
331         int thflags;
332         tcp_seq seq;
333 #ifdef INET6
334         int isipv6 = (mtod(m, struct ip *)->ip_v == 6) ? 1 : 0;
335 #else
336         const int isipv6 = 0;
337 #endif
338
339         /* tcbinfo lock required for tcp_twclose(), tcp_tw_2msl_reset(). */
340         INP_INFO_WLOCK_ASSERT(&V_tcbinfo);
341         INP_WLOCK_ASSERT(inp);
342
343         /*
344          * XXXRW: Time wait state for inpcb has been recycled, but inpcb is
345          * still present.  This is undesirable, but temporarily necessary
346          * until we work out how to handle inpcb's who's timewait state has
347          * been removed.
348          */
349         tw = intotw(inp);
350         if (tw == NULL)
351                 goto drop;
352
353         thflags = th->th_flags;
354
355         /*
356          * NOTE: for FIN_WAIT_2 (to be added later),
357          * must validate sequence number before accepting RST
358          */
359
360         /*
361          * If the segment contains RST:
362          *      Drop the segment - see Stevens, vol. 2, p. 964 and
363          *      RFC 1337.
364          */
365         if (thflags & TH_RST)
366                 goto drop;
367
368 #if 0
369 /* PAWS not needed at the moment */
370         /*
371          * RFC 1323 PAWS: If we have a timestamp reply on this segment
372          * and it's less than ts_recent, drop it.
373          */
374         if ((to.to_flags & TOF_TS) != 0 && tp->ts_recent &&
375             TSTMP_LT(to.to_tsval, tp->ts_recent)) {
376                 if ((thflags & TH_ACK) == 0)
377                         goto drop;
378                 goto ack;
379         }
380         /*
381          * ts_recent is never updated because we never accept new segments.
382          */
383 #endif
384
385         /*
386          * If a new connection request is received
387          * while in TIME_WAIT, drop the old connection
388          * and start over if the sequence numbers
389          * are above the previous ones.
390          */
391         if ((thflags & TH_SYN) && SEQ_GT(th->th_seq, tw->rcv_nxt)) {
392                 tcp_twclose(tw, 0);
393                 return (1);
394         }
395
396         /*
397          * Drop the the segment if it does not contain an ACK.
398          */
399         if ((thflags & TH_ACK) == 0)
400                 goto drop;
401
402         /*
403          * Reset the 2MSL timer if this is a duplicate FIN.
404          */
405         if (thflags & TH_FIN) {
406                 seq = th->th_seq + tlen + (thflags & TH_SYN ? 1 : 0);
407                 if (seq + 1 == tw->rcv_nxt)
408                         tcp_tw_2msl_reset(tw, 1);
409         }
410
411         /*
412          * Acknowledge the segment if it has data or is not a duplicate ACK.
413          */
414         if (thflags != TH_ACK || tlen != 0 ||
415             th->th_seq != tw->rcv_nxt || th->th_ack != tw->snd_nxt)
416                 tcp_twrespond(tw, TH_ACK);
417         goto drop;
418
419         /*
420          * Generate a RST, dropping incoming segment.
421          * Make ACK acceptable to originator of segment.
422          * Don't bother to respond if destination was broadcast/multicast.
423          */
424         if (m->m_flags & (M_BCAST|M_MCAST))
425                 goto drop;
426         if (isipv6) {
427 #ifdef INET6
428                 struct ip6_hdr *ip6;
429
430                 /* IPv6 anycast check is done at tcp6_input() */
431                 ip6 = mtod(m, struct ip6_hdr *);
432                 if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
433                     IN6_IS_ADDR_MULTICAST(&ip6->ip6_src))
434                         goto drop;
435 #endif
436         } else {
437                 struct ip *ip;
438
439                 ip = mtod(m, struct ip *);
440                 if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
441                     IN_MULTICAST(ntohl(ip->ip_src.s_addr)) ||
442                     ip->ip_src.s_addr == htonl(INADDR_BROADCAST) ||
443                     in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif))
444                         goto drop;
445         }
446         if (thflags & TH_ACK) {
447                 tcp_respond(NULL,
448                     mtod(m, void *), th, m, 0, th->th_ack, TH_RST);
449         } else {
450                 seq = th->th_seq + (thflags & TH_SYN ? 1 : 0);
451                 tcp_respond(NULL,
452                     mtod(m, void *), th, m, seq, 0, TH_RST|TH_ACK);
453         }
454         INP_WUNLOCK(inp);
455         return (0);
456
457 drop:
458         INP_WUNLOCK(inp);
459         m_freem(m);
460         return (0);
461 }
462
463 void
464 tcp_twclose(struct tcptw *tw, int reuse)
465 {
466         INIT_VNET_INET(curvnet);
467         struct socket *so;
468         struct inpcb *inp;
469
470         /*
471          * At this point, we are in one of two situations:
472          *
473          * (1) We have no socket, just an inpcb<->twtcp pair.  We can free
474          *     all state.
475          *
476          * (2) We have a socket -- if we own a reference, release it and
477          *     notify the socket layer.
478          */
479         inp = tw->tw_inpcb;
480         KASSERT((inp->inp_vflag & INP_TIMEWAIT), ("tcp_twclose: !timewait"));
481         KASSERT(intotw(inp) == tw, ("tcp_twclose: inp_ppcb != tw"));
482         INP_INFO_WLOCK_ASSERT(&V_tcbinfo);      /* tcp_tw_2msl_stop(). */
483         INP_WLOCK_ASSERT(inp);
484
485         tw->tw_inpcb = NULL;
486         tcp_tw_2msl_stop(tw);
487         inp->inp_ppcb = NULL;
488         in_pcbdrop(inp);
489
490         so = inp->inp_socket;
491         if (so != NULL) {
492                 /*
493                  * If there's a socket, handle two cases: first, we own a
494                  * strong reference, which we will now release, or we don't
495                  * in which case another reference exists (XXXRW: think
496                  * about this more), and we don't need to take action.
497                  */
498                 if (inp->inp_vflag & INP_SOCKREF) {
499                         inp->inp_vflag &= ~INP_SOCKREF;
500                         INP_WUNLOCK(inp);
501                         ACCEPT_LOCK();
502                         SOCK_LOCK(so);
503                         KASSERT(so->so_state & SS_PROTOREF,
504                             ("tcp_twclose: INP_SOCKREF && !SS_PROTOREF"));
505                         so->so_state &= ~SS_PROTOREF;
506                         sofree(so);
507                 } else {
508                         /*
509                          * If we don't own the only reference, the socket and
510                          * inpcb need to be left around to be handled by
511                          * tcp_usr_detach() later.
512                          */
513                         INP_WUNLOCK(inp);
514                 }
515         } else {
516 #ifdef INET6
517                 if (inp->inp_vflag & INP_IPV6PROTO)
518                         in6_pcbfree(inp);
519                 else
520 #endif
521                         in_pcbfree(inp);
522         }
523         V_tcpstat.tcps_closed++;
524         crfree(tw->tw_cred);
525         tw->tw_cred = NULL;
526         if (reuse)
527                 return;
528         uma_zfree(tcptw_zone, tw);
529 }
530
531 int
532 tcp_twrespond(struct tcptw *tw, int flags)
533 {
534         INIT_VNET_INET(curvnet);
535         struct inpcb *inp = tw->tw_inpcb;
536         struct tcphdr *th;
537         struct mbuf *m;
538         struct ip *ip = NULL;
539         u_int hdrlen, optlen;
540         int error;
541         struct tcpopt to;
542 #ifdef INET6
543         struct ip6_hdr *ip6 = NULL;
544         int isipv6 = inp->inp_inc.inc_isipv6;
545 #endif
546
547         INP_WLOCK_ASSERT(inp);
548
549         m = m_gethdr(M_DONTWAIT, MT_DATA);
550         if (m == NULL)
551                 return (ENOBUFS);
552         m->m_data += max_linkhdr;
553
554 #ifdef MAC
555         mac_inpcb_create_mbuf(inp, m);
556 #endif
557
558 #ifdef INET6
559         if (isipv6) {
560                 hdrlen = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
561                 ip6 = mtod(m, struct ip6_hdr *);
562                 th = (struct tcphdr *)(ip6 + 1);
563                 tcpip_fillheaders(inp, ip6, th);
564         } else
565 #endif
566         {
567                 hdrlen = sizeof(struct tcpiphdr);
568                 ip = mtod(m, struct ip *);
569                 th = (struct tcphdr *)(ip + 1);
570                 tcpip_fillheaders(inp, ip, th);
571         }
572         to.to_flags = 0;
573
574         /*
575          * Send a timestamp and echo-reply if both our side and our peer
576          * have sent timestamps in our SYN's and this is not a RST.
577          */
578         if (tw->t_recent && flags == TH_ACK) {
579                 to.to_flags |= TOF_TS;
580                 to.to_tsval = ticks + tw->ts_offset;
581                 to.to_tsecr = tw->t_recent;
582         }
583         optlen = tcp_addoptions(&to, (u_char *)(th + 1));
584
585         m->m_len = hdrlen + optlen;
586         m->m_pkthdr.len = m->m_len;
587
588         KASSERT(max_linkhdr + m->m_len <= MHLEN, ("tcptw: mbuf too small"));
589
590         th->th_seq = htonl(tw->snd_nxt);
591         th->th_ack = htonl(tw->rcv_nxt);
592         th->th_off = (sizeof(struct tcphdr) + optlen) >> 2;
593         th->th_flags = flags;
594         th->th_win = htons(tw->last_win);
595
596 #ifdef INET6
597         if (isipv6) {
598                 th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(struct ip6_hdr),
599                     sizeof(struct tcphdr) + optlen);
600                 ip6->ip6_hlim = in6_selecthlim(inp, NULL);
601                 error = ip6_output(m, inp->in6p_outputopts, NULL,
602                     (tw->tw_so_options & SO_DONTROUTE), NULL, NULL, inp);
603         } else
604 #endif
605         {
606                 th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
607                     htons(sizeof(struct tcphdr) + optlen + IPPROTO_TCP));
608                 m->m_pkthdr.csum_flags = CSUM_TCP;
609                 m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
610                 ip->ip_len = m->m_pkthdr.len;
611                 if (V_path_mtu_discovery)
612                         ip->ip_off |= IP_DF;
613                 error = ip_output(m, inp->inp_options, NULL,
614                     ((tw->tw_so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0),
615                     NULL, inp);
616         }
617         if (flags & TH_ACK)
618                 V_tcpstat.tcps_sndacks++;
619         else
620                 V_tcpstat.tcps_sndctrl++;
621         V_tcpstat.tcps_sndtotal++;
622         return (error);
623 }
624
625 static void
626 tcp_tw_2msl_reset(struct tcptw *tw, int rearm)
627 {
628         INIT_VNET_INET(curvnet);
629
630         INP_INFO_WLOCK_ASSERT(&V_tcbinfo);
631         INP_WLOCK_ASSERT(tw->tw_inpcb);
632         if (rearm)
633                 TAILQ_REMOVE(&V_twq_2msl, tw, tw_2msl);
634         tw->tw_time = ticks + 2 * tcp_msl;
635         TAILQ_INSERT_TAIL(&V_twq_2msl, tw, tw_2msl);
636 }
637
638 static void
639 tcp_tw_2msl_stop(struct tcptw *tw)
640 {
641         INIT_VNET_INET(curvnet);
642
643         INP_INFO_WLOCK_ASSERT(&V_tcbinfo);
644         TAILQ_REMOVE(&V_twq_2msl, tw, tw_2msl);
645 }
646
647 struct tcptw *
648 tcp_tw_2msl_scan(int reuse)
649 {
650         INIT_VNET_INET(curvnet);
651         struct tcptw *tw;
652
653         INP_INFO_WLOCK_ASSERT(&V_tcbinfo);
654         for (;;) {
655                 tw = TAILQ_FIRST(&V_twq_2msl);
656                 if (tw == NULL || (!reuse && tw->tw_time > ticks))
657                         break;
658                 INP_WLOCK(tw->tw_inpcb);
659                 tcp_twclose(tw, reuse);
660                 if (reuse)
661                         return (tw);
662         }
663         return (NULL);
664 }