Author: glebius
Date: Mon Jul 16 07:08:34 2012
New Revision: 238516
URL: http://svn.freebsd.org/changeset/base/238516

Log:
  If ip_output() returns EMSGSIZE to tcp_output(), then the latter calls
  tcp_mtudisc(), which in its turn may call tcp_output(). Under certain
  conditions (must admit they are very special) an infinite recursion can
  happen.
  
  To avoid recursion we can pass struct route to ip_output() and obtain
  correct mtu. This allows us not to use tcp_mtudisc() but call tcp_mss_update()
  directly.
  
  PR:           kern/155585
  Submitted by: Andrey Zonov <andrey zonov.org> (original version of patch)

Modified:
  head/sys/netinet/tcp_output.c

Modified: head/sys/netinet/tcp_output.c
==============================================================================
--- head/sys/netinet/tcp_output.c       Mon Jul 16 06:56:46 2012        
(r238515)
+++ head/sys/netinet/tcp_output.c       Mon Jul 16 07:08:34 2012        
(r238516)
@@ -180,7 +180,7 @@ tcp_output(struct tcpcb *tp)
        int idle, sendalot;
        int sack_rxmit, sack_bytes_rxmt;
        struct sackhole *p;
-       int tso;
+       int tso, mtu;
        struct tcpopt to;
 #if 0
        int maxburst = TCP_MAXBURST;
@@ -226,6 +226,7 @@ again:
                tcp_sack_adjust(tp);
        sendalot = 0;
        tso = 0;
+       mtu = 0;
        off = tp->snd_nxt - tp->snd_una;
        sendwin = min(tp->snd_wnd, tp->snd_cwnd);
 
@@ -1209,6 +1210,9 @@ timer:
         */
 #ifdef INET6
        if (isipv6) {
+               struct route_in6 ro;
+
+               bzero(&ro, sizeof(ro));
                /*
                 * we separately set hoplimit for every segment, since the
                 * user might want to change the value via setsockopt.
@@ -1218,10 +1222,13 @@ timer:
                ip6->ip6_hlim = in6_selecthlim(tp->t_inpcb, NULL);
 
                /* TODO: IPv6 IP6TOS_ECT bit on */
-               error = ip6_output(m,
-                           tp->t_inpcb->in6p_outputopts, NULL,
-                           ((so->so_options & SO_DONTROUTE) ?
-                           IP_ROUTETOIF : 0), NULL, NULL, tp->t_inpcb);
+               error = ip6_output(m, tp->t_inpcb->in6p_outputopts, &ro,
+                   ((so->so_options & SO_DONTROUTE) ?  IP_ROUTETOIF : 0),
+                   NULL, NULL, tp->t_inpcb);
+
+               if (error == EMSGSIZE && ro.ro_rt != NULL)
+                       mtu = ro.ro_rt->rt_rmx.rmx_mtu;
+               RO_RTFREE(&ro);
        }
 #endif /* INET6 */
 #if defined(INET) && defined(INET6)
@@ -1229,6 +1236,9 @@ timer:
 #endif
 #ifdef INET
     {
+       struct route ro;
+
+       bzero(&ro, sizeof(ro));
        ip->ip_len = m->m_pkthdr.len;
 #ifdef INET6
        if (tp->t_inpcb->inp_vflag & INP_IPV6PROTO)
@@ -1245,9 +1255,13 @@ timer:
        if (V_path_mtu_discovery && tp->t_maxopd > V_tcp_minmss)
                ip->ip_off |= IP_DF;
 
-       error = ip_output(m, tp->t_inpcb->inp_options, NULL,
+       error = ip_output(m, tp->t_inpcb->inp_options, &ro,
            ((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0), 0,
            tp->t_inpcb);
+
+       if (error == EMSGSIZE && ro.ro_rt != NULL)
+               mtu = ro.ro_rt->rt_rmx.rmx_mtu;
+       RO_RTFREE(&ro);
     }
 #endif /* INET */
        if (error) {
@@ -1294,21 +1308,18 @@ out:
                         * For some reason the interface we used initially
                         * to send segments changed to another or lowered
                         * its MTU.
-                        *
-                        * tcp_mtudisc() will find out the new MTU and as
-                        * its last action, initiate retransmission, so it
-                        * is important to not do so here.
-                        *
                         * If TSO was active we either got an interface
                         * without TSO capabilits or TSO was turned off.
-                        * Disable it for this connection as too and
-                        * immediatly retry with MSS sized segments generated
-                        * by this function.
+                        * If we obtained mtu from ip_output() then update
+                        * it and try again.
                         */
                        if (tso)
                                tp->t_flags &= ~TF_TSO;
-                       tcp_mtudisc(tp->t_inpcb, -1);
-                       return (0);
+                       if (mtu != 0) {
+                               tcp_mss_update(tp, -1, mtu, NULL, NULL);
+                               goto again;
+                       }
+                       return (error);
                case EHOSTDOWN:
                case EHOSTUNREACH:
                case ENETDOWN:
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to