summaryrefslogtreecommitdiffstats
path: root/sys/netinet/tcp_output.c
diff options
context:
space:
mode:
authorglebius <glebius@FreeBSD.org>2012-07-16 07:08:34 +0000
committerglebius <glebius@FreeBSD.org>2012-07-16 07:08:34 +0000
commitb5cd2a8e46d79cd3e11ef859da3c19af9b5f539f (patch)
tree61da538ab3c5adfbb712a20314a13469069863d9 /sys/netinet/tcp_output.c
parent8843fcf6f31a321d74db332f1b9596b33b1b51bd (diff)
downloadFreeBSD-src-b5cd2a8e46d79cd3e11ef859da3c19af9b5f539f.zip
FreeBSD-src-b5cd2a8e46d79cd3e11ef859da3c19af9b5f539f.tar.gz
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)
Diffstat (limited to 'sys/netinet/tcp_output.c')
-rw-r--r--sys/netinet/tcp_output.c43
1 files changed, 27 insertions, 16 deletions
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
index 1881c54..9876ef8 100644
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -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:
OpenPOWER on IntegriCloud