correct linkmtu handling.

Obtained from:	KAME
This commit is contained in:
Hajimu UMEMOTO 2003-10-20 15:27:48 +00:00
parent c3af6250d0
commit 31b3783c8d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=121283
10 changed files with 141 additions and 110 deletions

View File

@ -2711,12 +2711,16 @@ tcp_mss(tp, offer)
if (rt->rt_rmx.rmx_mtu)
mss = rt->rt_rmx.rmx_mtu - min_protoh;
else {
#ifdef INET6
mss = (isipv6 ? IN6_LINKMTU(rt->rt_ifp) : ifp->if_mtu)
- min_protoh;
#else
mss = ifp->if_mtu - min_protoh;
#endif
if (isipv6) {
mss = ND_IFINFO(rt->rt_ifp)->linkmtu - min_protoh;
if (!in6_localaddr(&inp->in6p_faddr))
mss = min(mss, tcp_v6mssdflt);
} else {
mss = ifp->if_mtu - min_protoh;
if (!in_localaddr(inp->inp_faddr))
mss = min(mss, tcp_mssdflt);
}
@ -2834,7 +2838,12 @@ tcp_mssopt(tp)
if (rt == NULL)
return (isipv6 ? tcp_v6mssdflt : tcp_mssdflt);
#ifdef INET6
return (isipv6 ? IN6_LINKMTU(rt->rt_ifp) :
rt->rt_ifp->if_mtu - min_protoh);
#else
return (rt->rt_ifp->if_mtu - min_protoh);
#endif
}

View File

@ -2711,12 +2711,16 @@ tcp_mss(tp, offer)
if (rt->rt_rmx.rmx_mtu)
mss = rt->rt_rmx.rmx_mtu - min_protoh;
else {
#ifdef INET6
mss = (isipv6 ? IN6_LINKMTU(rt->rt_ifp) : ifp->if_mtu)
- min_protoh;
#else
mss = ifp->if_mtu - min_protoh;
#endif
if (isipv6) {
mss = ND_IFINFO(rt->rt_ifp)->linkmtu - min_protoh;
if (!in6_localaddr(&inp->in6p_faddr))
mss = min(mss, tcp_v6mssdflt);
} else {
mss = ifp->if_mtu - min_protoh;
if (!in_localaddr(inp->inp_faddr))
mss = min(mss, tcp_mssdflt);
}
@ -2834,7 +2838,12 @@ tcp_mssopt(tp)
if (rt == NULL)
return (isipv6 ? tcp_v6mssdflt : tcp_mssdflt);
#ifdef INET6
return (isipv6 ? IN6_LINKMTU(rt->rt_ifp) :
rt->rt_ifp->if_mtu - min_protoh);
#else
return (rt->rt_ifp->if_mtu - min_protoh);
#endif
}

View File

@ -2288,9 +2288,12 @@ in6_setmaxmtu()
IFNET_RLOCK();
for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) {
/* this function can be called during ifnet initialization */
if (!ifp->if_afdata[AF_INET6])
continue;
if ((ifp->if_flags & IFF_LOOPBACK) == 0 &&
ND_IFINFO(ifp)->linkmtu > maxmtu)
maxmtu = ND_IFINFO(ifp)->linkmtu;
IN6_LINKMTU(ifp) > maxmtu)
maxmtu = IN6_LINKMTU(ifp);
}
IFNET_RUNLOCK();
if (maxmtu) /* update only when maxmtu is positive */

View File

@ -94,6 +94,7 @@
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/nd6.h>
#include <netinet/tcp.h>
#include <netinet/tcp_seq.h>
@ -149,7 +150,7 @@ in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head,
if (!rt->rt_rmx.rmx_mtu && !(rt->rt_rmx.rmx_locks & RTV_MTU)
&& rt->rt_ifp)
rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
rt->rt_rmx.rmx_mtu = IN6_LINKMTU(rt->rt_ifp);
ret = rn_addroute(v_arg, n_arg, head, treenodes);
if (ret == NULL && rt->rt_flags & RTF_HOST) {

View File

@ -383,7 +383,7 @@ ip6_forward(m, srcrt)
return;
}
if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) {
if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) {
in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
if (mcopy) {
u_long mtu;
@ -393,7 +393,7 @@ ip6_forward(m, srcrt)
size_t ipsechdrsiz;
#endif
mtu = rt->rt_ifp->if_mtu;
mtu = IN6_LINKMTU(rt->rt_ifp);
#ifdef IPSEC
/*
* When we do IPsec tunnel ingress, we need to play

View File

@ -113,6 +113,7 @@
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet6/ip6_mroute.h>
#include <netinet6/pim6.h>
#include <netinet6/pim6_var.h>
@ -1454,6 +1455,7 @@ phyint_send(ip6, mifp, m)
static struct route_in6 ro;
struct in6_multi *in6m;
struct sockaddr_in6 *dst6;
u_long linkmtu;
/*
* Make a new reference to the packet; make sure that
@ -1513,7 +1515,8 @@ phyint_send(ip6, mifp, m)
* Put the packet into the sending queue of the outgoing interface
* if it would fit in the MTU of the interface.
*/
if (mb_copy->m_pkthdr.len <= ifp->if_mtu || ifp->if_mtu < IPV6_MMTU) {
linkmtu = IN6_LINKMTU(ifp);
if (mb_copy->m_pkthdr.len <= linkmtu || linkmtu < IPV6_MMTU) {
dst6->sin6_len = sizeof(struct sockaddr_in6);
dst6->sin6_family = AF_INET6;
dst6->sin6_addr = ip6->ip6_dst;
@ -1530,7 +1533,7 @@ phyint_send(ip6, mifp, m)
#endif
} else {
#ifdef MULTICAST_PMTUD
icmp6_error(mb_copy, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu);
icmp6_error(mb_copy, ICMP6_PACKET_TOO_BIG, 0, linkmtu);
#else
#ifdef MRT6DEBUG
if (mrt6debug & DEBUG_XMIT)

View File

@ -137,6 +137,8 @@ static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int,
struct ip6_frag **));
static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t));
static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *));
static int ip6_getpmtu __P((struct route_in6 *, struct route_in6 *,
struct ifnet *, struct in6_addr *, u_long *));
/*
@ -771,51 +773,9 @@ skip_ipsec2:;
if (ifpp)
*ifpp = ifp;
/*
* Determine path MTU.
*/
if (ro_pmtu != ro) {
/* The first hop and the final destination may differ. */
struct sockaddr_in6 *sin6_fin =
(struct sockaddr_in6 *)&ro_pmtu->ro_dst;
if (ro_pmtu->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
!IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr,
&finaldst))) {
RTFREE(ro_pmtu->ro_rt);
ro_pmtu->ro_rt = (struct rtentry *)0;
}
if (ro_pmtu->ro_rt == 0) {
bzero(sin6_fin, sizeof(*sin6_fin));
sin6_fin->sin6_family = AF_INET6;
sin6_fin->sin6_len = sizeof(struct sockaddr_in6);
sin6_fin->sin6_addr = finaldst;
rtalloc((struct route *)ro_pmtu);
}
}
if (ro_pmtu->ro_rt != NULL) {
u_int32_t ifmtu = ND_IFINFO(ifp)->linkmtu;
mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
if (mtu > ifmtu || mtu == 0) {
/*
* The MTU on the route is larger than the MTU on
* the interface! This shouldn't happen, unless the
* MTU of the interface has been changed after the
* interface was brought up. Change the MTU in the
* route to match the interface MTU (as long as the
* field isn't locked).
*
* if MTU on the route is 0, we need to fix the MTU.
* this case happens with path MTU discovery timeouts.
*/
mtu = ifmtu;
if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0)
ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */
}
} else {
mtu = ND_IFINFO(ifp)->linkmtu;
}
/* Determine path MTU. */
if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu)) != 0)
goto bad;
/*
* advanced API (IPV6_USE_MIN_MTU) overrides mtu setting
@ -1300,6 +1260,69 @@ ip6_insertfraghdr(m0, m, hlen, frghdrp)
return (0);
}
static int
ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup)
struct route_in6 *ro_pmtu, *ro;
struct ifnet *ifp;
struct in6_addr *dst;
u_long *mtup;
{
u_int32_t mtu = 0;
int error = 0;
/*
* Determine path MTU.
*/
if (ro_pmtu != ro) {
/* The first hop and the final destination may differ. */
struct sockaddr_in6 *sa6_dst =
(struct sockaddr_in6 *)&ro_pmtu->ro_dst;
if (ro_pmtu->ro_rt &&
((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
!IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) {
RTFREE(ro_pmtu->ro_rt);
ro_pmtu->ro_rt = (struct rtentry *)NULL;
}
if (ro_pmtu->ro_rt == NULL) {
bzero(sa6_dst, sizeof(*sa6_dst));
sa6_dst->sin6_family = AF_INET6;
sa6_dst->sin6_len = sizeof(struct sockaddr_in6);
sa6_dst->sin6_addr = *dst;
rtalloc((struct route *)ro_pmtu);
}
}
if (ro_pmtu->ro_rt) {
u_int32_t ifmtu;
if (ifp == NULL)
ifp = ro_pmtu->ro_rt->rt_ifp;
ifmtu = IN6_LINKMTU(ifp);
mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
if (mtu == 0)
mtu = ifmtu;
else if (mtu > ifmtu || mtu == 0) {
/*
* The MTU on the route is larger than the MTU on
* the interface! This shouldn't happen, unless the
* MTU of the interface has been changed after the
* interface was brought up. Change the MTU in the
* route to match the interface MTU (as long as the
* field isn't locked).
*/
mtu = ifmtu;
if (!(ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU))
ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu;
}
} else if (ifp) {
mtu = IN6_LINKMTU(ifp);
} else
error = EHOSTUNREACH; /* XXX */
*mtup = mtu;
return (error);
}
/*
* IP6 socket option processing.
*/

View File

@ -51,6 +51,7 @@
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_arc.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/if_atm.h>
@ -149,12 +150,10 @@ nd6_ifattach(ifp)
nd->initialized = 1;
nd->linkmtu = ifnet_byindex(ifp->if_index)->if_mtu;
nd->chlim = IPV6_DEFHLIM;
nd->basereachable = REACHABLE_TIME;
nd->reachable = ND_COMPUTE_RTIME(nd->basereachable);
nd->retrans = RETRANS_TIMER;
nd->receivedra = 0;
/*
* Note that the default value of ip6_accept_rtadv is 0, which means
* we won't accept RAs by default even if we set ND6_IFF_ACCEPT_RTADV
@ -194,21 +193,19 @@ nd6_setmtu0(ifp, ndi)
struct ifnet *ifp;
struct nd_ifinfo *ndi;
{
u_long oldmaxmtu;
u_long oldlinkmtu;
u_int32_t omaxmtu;
oldmaxmtu = ndi->maxmtu;
oldlinkmtu = ndi->linkmtu;
omaxmtu = ndi->maxmtu;
switch (ifp->if_type) {
case IFT_ARCNET: /* XXX MTU handling needs more work */
ndi->maxmtu = MIN(60480, ifp->if_mtu);
case IFT_ARCNET:
ndi->maxmtu = MIN(ARC_PHDS_MAXMTU, ifp->if_mtu); /* RFC2497 */
break;
case IFT_ETHER:
ndi->maxmtu = MIN(ETHERMTU, ifp->if_mtu);
break;
case IFT_FDDI:
ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu);
ndi->maxmtu = MIN(FDDIIPMTU, ifp->if_mtu); /* RFC2467 */
break;
case IFT_ATM:
ndi->maxmtu = MIN(ATMMTU, ifp->if_mtu);
@ -229,29 +226,21 @@ nd6_setmtu0(ifp, ndi)
break;
}
if (oldmaxmtu != ndi->maxmtu) {
/*
* If the ND level MTU is not set yet, or if the maxmtu
* is reset to a smaller value than the ND level MTU,
* also reset the ND level MTU.
*/
if (ndi->linkmtu == 0 ||
ndi->maxmtu < ndi->linkmtu) {
ndi->linkmtu = ndi->maxmtu;
/* also adjust in6_maxmtu if necessary. */
if (oldlinkmtu == 0) {
/*
* XXX: the case analysis is grotty, but
* it is not efficient to call in6_setmaxmtu()
* here when we are during the initialization
* procedure.
*/
if (in6_maxmtu < ndi->linkmtu)
in6_maxmtu = ndi->linkmtu;
} else
in6_setmaxmtu();
}
/*
* Decreasing the interface MTU under IPV6 minimum MTU may cause
* undesirable situation. We thus notify the operator of the change
* explicitly. The check for omaxmtu is necessary to restrict the
* log to the case of changing the MTU, not initializing it.
*/
if (omaxmtu >= IPV6_MMTU && ndi->maxmtu < IPV6_MMTU) {
log(LOG_NOTICE, "nd6_setmtu0: "
"new link MTU on %s (%lu) is too small for IPv6\n",
if_name(ifp), (unsigned long)ndi->maxmtu);
}
if (ndi->maxmtu > in6_maxmtu)
in6_setmaxmtu(); /* check all interfaces just in case */
#undef MIN
}
@ -1449,7 +1438,7 @@ nd6_ioctl(cmd, data, ifp)
case OSIOCGIFINFO_IN6:
/* XXX: old ndp(8) assumes a positive value for linkmtu. */
bzero(&ndi->ndi, sizeof(ndi->ndi));
ndi->ndi.linkmtu = ND_IFINFO(ifp)->linkmtu;
ndi->ndi.linkmtu = IN6_LINKMTU(ifp);
ndi->ndi.maxmtu = ND_IFINFO(ifp)->maxmtu;
ndi->ndi.basereachable = ND_IFINFO(ifp)->basereachable;
ndi->ndi.reachable = ND_IFINFO(ifp)->reachable;
@ -1457,10 +1446,10 @@ nd6_ioctl(cmd, data, ifp)
ndi->ndi.flags = ND_IFINFO(ifp)->flags;
ndi->ndi.recalctm = ND_IFINFO(ifp)->recalctm;
ndi->ndi.chlim = ND_IFINFO(ifp)->chlim;
ndi->ndi.receivedra = ND_IFINFO(ifp)->receivedra;
break;
case SIOCGIFINFO_IN6:
ndi->ndi = *ND_IFINFO(ifp);
ndi->ndi.linkmtu = IN6_LINKMTU(ifp);
break;
case SIOCSIFINFO_FLAGS:
ND_IFINFO(ifp)->flags = ndi->ndi.flags;

View File

@ -79,7 +79,6 @@ struct nd_ifinfo {
u_int32_t flags; /* Flags */
int recalctm; /* BaseReacable re-calculation timer */
u_int8_t chlim; /* CurHopLimit */
u_int8_t receivedra;
u_int8_t initialized; /* Flag to see the entry is initialized */
/* the following 3 members are for privacy extension for addrconf */
u_int8_t randomseed0[8]; /* upper 64 bits of MD5 digest */

View File

@ -364,38 +364,33 @@ nd6_ra_input(m, off, icmp6len)
* MTU
*/
if (ndopts.nd_opts_mtu && ndopts.nd_opts_mtu->nd_opt_mtu_len == 1) {
u_int32_t mtu;
u_long mtu;
u_long maxmtu;
mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
mtu = (u_long)ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
/* lower bound */
if (mtu < IPV6_MMTU) {
nd6log((LOG_INFO, "nd6_ra_input: bogus mtu option "
"mtu=%d sent from %s, ignoring\n",
"mtu=%lu sent from %s, ignoring\n",
mtu, ip6_sprintf(&ip6->ip6_src)));
goto skip;
}
/* upper bound */
if (ndi->maxmtu) {
if (mtu <= ndi->maxmtu) {
int change = (ndi->linkmtu != mtu);
maxmtu = (ndi->maxmtu && ndi->maxmtu < ifp->if_mtu)
? ndi->maxmtu : ifp->if_mtu;
if (mtu <= maxmtu) {
int change = (ndi->linkmtu != mtu);
ndi->linkmtu = mtu;
if (change) /* in6_maxmtu may change */
in6_setmaxmtu();
} else {
nd6log((LOG_INFO, "nd6_ra_input: bogus mtu "
"mtu=%d sent from %s; "
"exceeds maxmtu %d, ignoring\n",
mtu, ip6_sprintf(&ip6->ip6_src),
ndi->maxmtu));
}
ndi->linkmtu = mtu;
if (change) /* in6_maxmtu may change */
in6_setmaxmtu();
} else {
nd6log((LOG_INFO, "nd6_ra_input: mtu option "
"mtu=%d sent from %s; maxmtu unknown, "
"ignoring\n",
mtu, ip6_sprintf(&ip6->ip6_src)));
nd6log((LOG_INFO, "nd6_ra_input: bogus mtu "
"mtu=%lu sent from %s; "
"exceeds maxmtu %lu, ignoring\n",
mtu, ip6_sprintf(&ip6->ip6_src), maxmtu));
}
}