If the underlying link's MTU is less than PP_MTU (e.g. PPPoE), set our

MRU to the link's MTU and initiate an MRU negotiation with the peer.

This is useful when the PPP session is bridged from Ethernet to ATM
by an ADSL modem (such as the Linksys AM200). Unless we negotiate the
lower MRU, the peer is unaware that 1500-byte packets will not make
it umolested across the link (the Linksys AM200 silently truncates them
to 1498 bytes, creating a nice PMTU blackhole).

Note that the PPP RFC says peers MUST accept 1500 byte packets,
regardless of the negotiated MRU, so most ISPs which use PPPoA will
probably still send 1500-byte packets. However, I persuaded my ISP
(Andrews and Arnold) to modify their software to generate an ICMP error
"fragment needed" for packets with IP.DF set which are larger than the
negotiated MRU. They will still forward non-IP.DF packets, with the
associated truncation, but at least my PMTU troubles have gone.
This commit is contained in:
scw 2007-06-23 08:45:25 +00:00
parent 616f645161
commit 2346befecc
1 changed files with 24 additions and 10 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_spppsubr.c,v 1.104 2007/03/04 06:03:16 christos Exp $ */
/* $NetBSD: if_spppsubr.c,v 1.105 2007/06/23 08:45:25 scw Exp $ */
/*
* Synchronous PPP/Cisco link level subroutines.
@ -41,7 +41,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_spppsubr.c,v 1.104 2007/03/04 06:03:16 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: if_spppsubr.c,v 1.105 2007/06/23 08:45:25 scw Exp $");
#include "opt_inet.h"
#include "opt_ipx.h"
@ -1075,7 +1075,8 @@ sppp_ioctl(struct ifnet *ifp, u_long cmd, void *data)
#define ifr_mtu ifr_metric
#endif
case SIOCSIFMTU:
if (ifr->ifr_mtu < 128 || ifr->ifr_mtu > sp->lcp.their_mru) {
if (ifr->ifr_mtu < PPP_MINMRU ||
ifr->ifr_mtu > sp->lcp.their_mru) {
error = EINVAL;
break;
}
@ -1085,7 +1086,8 @@ sppp_ioctl(struct ifnet *ifp, u_long cmd, void *data)
#endif
#ifdef SLIOCSETMTU
case SLIOCSETMTU:
if (*(short *)data < 128 || *(short *)data > sp->lcp.their_mru)
if (*(short *)data < PPP_MINMRU ||
*(short *)data > sp->lcp.their_mru)
{
error = EINVAL;
break;
@ -2007,7 +2009,12 @@ sppp_lcp_init(struct sppp *sp)
sp->pp_seq[IDX_LCP] = 0;
sp->pp_rseq[IDX_LCP] = 0;
sp->lcp.protos = 0;
sp->lcp.mru = sp->lcp.their_mru = PP_MTU;
if (sp->pp_if.if_mtu < PP_MTU) {
sp->lcp.mru = sp->pp_if.if_mtu;
sp->lcp.opts |= (1 << LCP_OPT_MRU);
} else
sp->lcp.mru = PP_MTU;
sp->lcp.their_mru = PP_MTU;
/*
* Initialize counters and timeout values. Note that we don't
@ -2394,11 +2401,18 @@ sppp_lcp_RCN_rej(struct sppp *sp, struct lcp_header *h, int len)
break;
case LCP_OPT_MRU:
/*
* Should not be rejected anyway, since we only
* negotiate a MRU if explicitly requested by
* peer.
* We try to negotiate a lower MRU if the underlying
* link's MTU is less than PP_MTU (e.g. PPPoE). If the
* peer rejects this lower rate, fallback to the
* default.
*/
if (debug) {
addlog("%s: warning: peer rejected our MRU of "
"%ld bytes. Defaulting to %d bytes\n",
ifp->if_xname, sp->lcp.mru, PP_MTU);
}
sp->lcp.opts &= ~(1 << LCP_OPT_MRU);
sp->lcp.mru = PP_MTU;
break;
case LCP_OPT_AUTH_PROTO:
/*
@ -2494,8 +2508,8 @@ sppp_lcp_RCN_nak(struct sppp *sp, struct lcp_header *h, int len)
u_int mru = p[2] * 256 + p[3];
if (debug)
addlog(" %d", mru);
if (mru < PP_MTU || mru > PP_MAX_MRU)
mru = PP_MTU;
if (mru < PPP_MINMRU || mru > sp->pp_if.if_mtu)
mru = sp->pp_if.if_mtu;
sp->lcp.mru = mru;
sp->lcp.opts |= (1 << LCP_OPT_MRU);
}