TCP Loopback Connections with the Same Src/Dest Port

Matt Miller matt at matthewjmiller.net
Wed Jul 17 11:09:08 UTC 2013


Our system is based on FreeBSD 8.1.  In some tests, we were having
issues caused by connections of this form (more details below):

 TCP4      0      0      0/   0/   0    127.0.0.1.665   127.0.0.1.665
 FIN_WAIT_1
 TCP4      0      0      0/   0/   0    127.0.0.1.637   127.0.0.1.637
 FIN_WAIT_1
 TCP4      0      0      0/   0/   0    127.0.0.1.648   127.0.0.1.648
 FIN_WAIT_1

Some questions we had:

- Has anyone else ever seen these same src/dest address/port TCP
connections created?  Does anyone know of a legitimate reason why they
should be allowed?

- If there are no known use cases for this type of connection, does
anyone have more context/insight on the design here: should this type
of inpcb creation be prevented in the kernel or is it the
application's responsibility to ensure it never creates this type of
socket?

For those interested, more details of the issue seen follow.  The
connection seems to get stuck in swi_net sending and receiving pure
FIN/ACKs to itself:

#12 0xffffffff804372ce in ip_output (m=0xffffff0003ccf300,
opt=<optimized out>, ro=0xffffff8020c2b6a0, flags=0, imo=0x0,
inp=0xffffff0019933968) at ../../../../sys/netinet/ip_output.c
#13 0xffffffff804423dc in tcp_output (tp=0xffffff0019de2370) at
../../../../sys/netinet/tcp_output.c
#14 0xffffffff8043ef5d in tcp_do_segment (m=0xffffff0019af1200,
th=0x100200, so=0xffffff011ac59570, tp=0xffffff0019de2370,
drop_hdrlen=52, tlen=0, iptos=0 '\000', ti_locked=3) at
../../../../sys/netinet/tcp_input.c
#15 0xffffffff80440311 in tcp_input (m=0xffffff0019af1200,
off0=<optimized out>) at ../../../../sys/netinet/tcp_input.c
#16 0xffffffff8043530b in ip_input (m=0xffffff0019af1200) at
../../../../sys/netinet/ip_input.c
#17 0xffffffff8040889f in netisr_process_workstream_proto
(proto=<optimized out>, nwsp=<optimized out>) at
../../../../sys/net/netisr.c
#18 swi_net (arg=0xffffffff80f59800) at ../../../../sys/net/netisr.c

swi_net() just continues in this loop, ad nauseam:

 759         while ((bits = nwsp->nws_pendingbits) != 0) {
 760                 while ((prot = ffs(bits)) != 0) {
 761                         prot--;
 762                         bits &= ~(1 << prot);
 763                         (void)netisr_process_workstream_proto(nwsp, prot);
 764                 }
 765         }

The tcp_output() being triggered in tcp_do_segment() in the case is
the one show on line 2303 below:

2212         /*
2213          * In ESTABLISHED state: drop duplicate ACKs; ACK out of range
2214          * ACKs.  If the ack is in the range
2215          *      tp->snd_una < th->th_ack <= tp->snd_max
2216          * then advance tp->snd_una to th->th_ack and drop
2217          * data from the retransmission queue.  If this ACK reflects
2218          * more up to date window information we update our
window information.
2219          */
2220         case TCPS_ESTABLISHED:
2221         case TCPS_FIN_WAIT_1:
2222         case TCPS_FIN_WAIT_2:
2223         case TCPS_CLOSE_WAIT:
2224         case TCPS_CLOSING:
2225         case TCPS_LAST_ACK:
2226                 if (SEQ_GT(th->th_ack, tp->snd_max)) {
2227                         TCPSTAT_INC(tcps_rcvacktoomuch);
2228                         goto dropafterack;
2229                 }
...
2234                 if (SEQ_LEQ(th->th_ack, tp->snd_una)) {
...
2248                         if (tlen == 0 && tiwin == tp->snd_wnd) {
2249                                 TCPSTAT_INC(tcps_rcvdupack);
...
2277                                 if (!tcp_timer_active(tp, TT_REXMT) ||
2278                                     th->th_ack != tp->snd_una)
2279                                         tp->t_dupacks = 0;
2280                                 else if (++tp->t_dupacks >
tcprexmtthresh ||
2281                                     ((V_tcp_do_newreno ||
2282                                       (tp->t_flags & TF_SACK_PERMIT)) &&
2283                                      IN_FASTRECOVERY(tp))) {
2284                                         if ((tp->t_flags &
TF_SACK_PERMIT) &&
2285                                             IN_FASTRECOVERY(tp)) {
2286                                                 int awnd;
2287
2288                                                 /*
2289                                                  * Compute the
amount of data in flight first.
2290                                                  * We can inject
new data into the pipe iff
2291                                                  * we have less
than 1/2 the original window's
2292                                                  * worth of data in flight.
2293                                                  */
2294                                                 awnd =
(tp->snd_nxt - tp->snd_fack) +
2295
tp->sackhint.sack_bytes_rexmit;
2296                                                 if (awnd <
tp->snd_ssthresh) {
2297
tp->snd_cwnd += tp->t_maxseg;
2298                                                         if
(tp->snd_cwnd > tp->snd_ssthresh)
2299
tp->snd_cwnd = tp->snd_ssthresh;
2300                                                 }
2301                                         } else
2302                                                 tp->snd_cwnd +=
tp->t_maxseg;
2303                                         (void) tcp_output(tp);
2304                                         goto drop;

I've noticed that we don't yet have this patch in our code:

http://svnweb.freebsd.org/base?view=revision&revision=239672

Which seems like it could be relevant here to the general case of both
ends of the connection entering FIN_WAIT_1 at the same time and
sending FIN/ACKs repeatedly (though our connections are a bizarre case
of this where both ends of the connection are actually the same
connection).

Thanks,

Matt


More information about the freebsd-net mailing list