Hi,

In tcp_mtudisc() we resend a TCP packet by calling tcp_output() if
the TCP maximum segment size changes.  At least that is what the
code and commit message pretend.

----------------------------
revision 1.78
date: 2004/04/26 18:12:25;  author: frantzen;  state: Exp;  lines: +10 -8;
- allow the user to force the TCP mss below the fail-safe 216 with a low
interface MTU.
- break a tcp_output() -> tcp_mtudisc() -> tcp_output() infinite recursion
when the TCP mss ends up larger than the interface MTU (when the if_mtu is
smaller than the tcp header).  connections will still stall
feedback from itojun@, claudio@ and provos and testing from beck@
----------------------------

But the functions in_rtchange() and in_pcbrtentry() do not touch
t_maxseg.  So orig_maxseg is always equal to tp->t_maxseg and
tcp_output() is not called after changes in t_maxseg.

The function tcp_mss() changes the maximum segment size t_maxseg,
a different value can only be detected after calling it.  Note that
my diff checks whether orig_maxseg is greater than t_maxseg.  For
path MTU discovery it makes only sense to resend a packet immediately
if it becomes smaller and is more likely to fit.

While tobhe@ and I were debugging the endless tcp_mtudisc() recursion
with TCP over UDP encap IPsec, we found the issue in tcp_mtudisc().
PMTU did not always work.  The recursion bug is somewhere else and
the current check does not prevent it.  But let's get a correct
PMTU discovery first.

ok?

bluhm

Index: netinet/tcp_subr.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/tcp_subr.c,v
retrieving revision 1.176
diff -u -p -U4 -r1.176 tcp_subr.c
--- netinet/tcp_subr.c  25 Feb 2021 02:48:21 -0000      1.176
+++ netinet/tcp_subr.c  29 Jun 2021 12:20:46 -0000
@@ -836,30 +836,30 @@ void
 tcp_mtudisc(struct inpcb *inp, int errno)
 {
        struct tcpcb *tp = intotcpcb(inp);
        struct rtentry *rt;
-       int change = 0;
+       int orig_maxseg, change = 0;
 
        if (tp == NULL)
                return;
+       orig_maxseg = tp->t_maxseg;
 
        rt = in_pcbrtentry(inp);
        if (rt != NULL) {
-               int orig_maxseg = tp->t_maxseg;
-
                /*
                 * If this was not a host route, remove and realloc.
                 */
                if ((rt->rt_flags & RTF_HOST) == 0) {
                        in_rtchange(inp, errno);
                        if ((rt = in_pcbrtentry(inp)) == NULL)
                                return;
                }
-               if (orig_maxseg != tp->t_maxseg ||
-                   (rt->rt_locks & RTV_MTU))
+               if (rt->rt_locks & RTV_MTU)
                        change = 1;
        }
        tcp_mss(tp, -1);
+       if (orig_maxseg > tp->t_maxseg)
+               change = 1;
 
        /*
         * Resend unacknowledged packets
         */

Reply via email to