Hi All,

The following is tested with:

Stock 5.0 as client & server.
These modification to 5.0 client & server.
Recent pppoe(4) patches for RFC 4638 support applied to 5.0 as the client.

It appears to be broken in with -current from a few days ago + these mods as the server; although if I run ktrace on the server's spawned ppp(8) then it appears to work just after the ktrace attaches...

To test pppoe(8) changes I use the following configuration files:

/etc/ppp/ppp.secret:
user1 secret123

/etc/ppp/ppp.conf:
default:
 set log Phase Chat LCP IPCP CCP tun command
 disable ipv6cp

pppoe1:
 set device "!/usr/sbin/pppoe -i em1"
 set mtu max 1492
 set mru max 1492
 set speed sync
 disable acfcomp protocomp
 deny acfcomp
 set authname user1
 set authkey secret123

pppoe2:
 set device "!/usr/sbin/pppoe -i em1 -m 1500"
 set mtu max 1500
 set mru max 1500
 set speed sync
 disable acfcomp protocomp
 deny acfcomp
 set authname user1
 set authkey secret123

pppoes1:
 set mtu max 1492
 set mru max 1492
 set speed sync
 disable acfcomp protocomp
 deny acfcomp
 set ifaddr 10.0.0.1 10.0.1.1-10.255.255.254 255.255.255.255
 enable pap

pppoes2:
 set mtu max 1500
 set mru max 1500
 set speed sync
 disable acfcomp protocomp
 deny acfcomp
 set ifaddr 10.0.0.1 10.0.1.1-10.255.255.254 255.255.255.255
 enable pap


And, for example, I will bring em1 up on the client and server with:
# ifconfig em1 mtu 2000 up

then go through the various combinations of
server# pppoe -p pppoes1 -i em1 -s
client# ppp pppoe1
ppp> open
...

and
server# pppoe -p pppoes2 -i em1 -m 1500 -s
client# ppp pppoe2
ppp> open
...


For testing the recent pppoe(4) modifications as the client I used:
# ifconfig em1 mtu 1500 up
# ifconfig pppoe0 0.0.0.0 0.0.0.1 netmask 255.255.255.255 \
pppoedev em1 authproto pap authname user1 authkey secret123

and subsequently:
# ifconfig em1 mtu 1508 up
# ifconfig pppoe0 down
# ifconfig pppoe0 mtu 1500 up

with both:
server# pppoe -p pppoes1 -i em1 -s
and
server# pppoe -p pppoes2 -i em1 -m 1500 -s

-- ben


Index: client.c
===================================================================
RCS file: /cvs/src/usr.sbin/pppoe/client.c,v
retrieving revision 1.23
diff -u -p -r1.23 client.c
--- client.c    4 Jul 2011 16:29:35 -0000       1.23
+++ client.c    29 Jan 2012 13:59:40 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: client.c,v 1.23 2011/07/04 16:29:35 sthen Exp $       */
+/*     $OpenBSD: client.c,v 1.24 2011/11/05 09:20:36 yasuoka Exp $     */

 /*
* Copyright (c) 2000 Network Security Technologies, Inc. http://www.netsec.net
@@ -28,10 +28,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/uio.h>
-#include <sys/time.h>
 #include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/param.h>
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_types.h>
@@ -41,10 +38,7 @@
 #include <net/ppp_defs.h>
 #include <errno.h>
 #include <string.h>
-#include <err.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <sysexits.h>
 #include <stdlib.h>
 #include <signal.h>

@@ -56,6 +50,7 @@

 u_int32_t client_cookie;
 u_int16_t client_sessionid;
+u_int16_t client_mtu;
 int pppfd, client_state;

 static int getpackets(int, u_int8_t *, u_int8_t *, struct ether_addr *,
@@ -86,6 +81,7 @@ client_mode(int bfd, u_int8_t *sysname,
        pppfd = -1;
        client_sessionid = 0xffff;
        client_state = -1;
+       client_mtu = 0;

        r = send_padi(bfd, myea, srvname);
        if (r <= 0)
@@ -150,13 +146,14 @@ client_mode(int bfd, u_int8_t *sysname,
 static int
 send_padi(int fd, struct ether_addr *ea, u_int8_t *srv)
 {
-       struct iovec iov[10];
+       struct iovec iov[12];
        struct pppoe_header ph = {
                PPPOE_VERTYPE(1, 1),
                PPPOE_CODE_PADI, 0, 0
        };
-       struct pppoe_tag thost, tserv;
+       struct pppoe_tag thost, tserv, tmaxpayload;
        u_int16_t etype = htons(ETHERTYPE_PPPOEDISC);
+       u_int16_t maxpayload;
        int i = 0;

        /* ether_header */
@@ -196,6 +193,21 @@ send_padi(int fd, struct ether_addr *ea,
        }
        tserv.len = htons(tserv.len);

+       /* ppp-max-payload tag (optional) */
+       if (rfc_4638_mtu > PPPOE_MIN_MTU) {
+               tmaxpayload.type = htons(PPPOE_TAG_PPP_MAX_PAYLOAD);
+               tmaxpayload.len = htons(sizeof(maxpayload));
+               tmaxpayload.val = (u_int8_t*) &maxpayload;
+               maxpayload = htons(rfc_4638_mtu);
+               iov[i].iov_base = &tmaxpayload;
+               iov[i++].iov_len = sizeof(tmaxpayload.len) + 
sizeof(tmaxpayload.type);
+               iov[i].iov_base = &maxpayload;
+               iov[i++].iov_len = sizeof(maxpayload);
+               ph.len += sizeof(tmaxpayload.len) + sizeof(tmaxpayload.type) +
+sizeof(maxpayload);
+       }
+
+
        ph.len = htons(ph.len);

        client_state = STATE_EXPECT_PADO;
@@ -208,9 +220,10 @@ send_padr(int bfd, u_int8_t *srv, struct
     struct ether_addr *rmea, struct ether_header *eh,
     struct pppoe_header *ph, struct tag_list *tl)
 {
-       struct iovec iov[12];
+       struct iovec iov[14];
        u_int16_t etype = htons(ETHERTYPE_PPPOEDISC);
-       struct pppoe_tag hutag, svtag;
+       struct pppoe_tag hutag, svtag, mptag;
+       u_int16_t maxpayload = htons(client_mtu);
        struct tag_node *n;
        int idx = 0, slen;

@@ -251,6 +264,18 @@ send_padr(int bfd, u_int8_t *srv, struct
                iov[idx++].iov_len = slen;
        }

+       /* PPP-Max-Payload */
+       if (client_mtu > 0) {
+               mptag.type = htons(PPPOE_TAG_PPP_MAX_PAYLOAD);
+               mptag.len = htons(sizeof(maxpayload));
+               iov[idx].iov_base = &mptag;
+               iov[idx++].iov_len = sizeof(mptag.type) + sizeof(mptag.len);
+               iov[idx].iov_base = &maxpayload;
+               iov[idx++].iov_len = sizeof(maxpayload);
+               ph->len += sizeof(mptag.type) + sizeof(mptag.len) + 
sizeof(maxpayload);
+       }
+
+
        n = tag_lookup(tl, PPPOE_TAG_RELAY_SESSION, 0);
        if (n != NULL) {
                iov[idx].iov_base = &n->type;
@@ -413,6 +438,18 @@ recv_pado(int bfd, u_int8_t *srv, struct
        if (bcmp(n->val, &client_cookie, sizeof(client_cookie)))
                goto out;

+       n = tag_lookup(&tl, PPPOE_TAG_PPP_MAX_PAYLOAD, 0);
+       if (n != NULL) {
+               client_mtu = n->val[1] | (n->val[0] << 8);
+               if (client_mtu > rfc_4638_mtu) {
+                       client_mtu = rfc_4638_mtu;
+               } else if (client_mtu < PPPOE_MIN_MTU) {
+                       client_mtu = PPPOE_MIN_MTU;
+               }
+       } else {
+               client_mtu = 0;
+       }
+
        r = 0;
        slen = (srv == NULL) ? 0 : strlen((char *)srv);
        while ((n = tag_lookup(&tl, PPPOE_TAG_SERVICE_NAME, r)) != NULL) {
@@ -468,6 +505,18 @@ recv_pads(int bfd, u_int8_t *srv, u_int8
                goto out;
        if (bcmp(n->val, &client_cookie, sizeof(client_cookie)))
                goto out;
+
+       n = tag_lookup(&tl, PPPOE_TAG_PPP_MAX_PAYLOAD, 0);
+       if (n != NULL && n->len == sizeof(client_mtu)) {
+               client_mtu = n->val[1] | (n->val[0] << 8);
+               if (client_mtu > rfc_4638_mtu ||
+                   client_mtu < PPPOE_MIN_MTU) {
+                       /* error; fallback */
+                       client_mtu = 0;
+               }
+       } else {
+               client_mtu = 0;
+       }

        if (ph->sessionid == 0) {
                timer_clr();
Index: common.c
===================================================================
RCS file: /cvs/src/usr.sbin/pppoe/common.c,v
retrieving revision 1.12
diff -u -p -r1.12 common.c
--- common.c    6 May 2004 20:29:04 -0000       1.12
+++ common.c    29 Jan 2012 13:59:41 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: common.c,v 1.12 2004/05/06 20:29:04 deraadt Exp $     */
+/*     $OpenBSD: common.c,v 1.13 2011/11/05 09:20:36 yasuoka Exp $     */

 /*
* Copyright (c) 2000 Network Security Technologies, Inc. http://www.netsec.net
@@ -28,10 +28,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/uio.h>
-#include <sys/time.h>
 #include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_types.h>
@@ -41,10 +38,8 @@
 #include <net/bpf.h>
 #include <errno.h>
 #include <string.h>
-#include <err.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <sysexits.h>
 #include <stdlib.h>
 #include <md5.h>

@@ -143,7 +138,7 @@ ppp_to_bpf(int bfd, int pppfd, struct et
        int r;

        if (pktbuf == NULL) {
-               pktbuf = (u_int8_t *)malloc(PPPOE_MTU);
+               pktbuf = (u_int8_t *)malloc(rfc_4638_mtu + PPPOE_MTU_DIFF);
                if (pktbuf == NULL)
                        return (-1);
        }
@@ -151,7 +146,7 @@ ppp_to_bpf(int bfd, int pppfd, struct et
        iov[0].iov_base = trash;
        iov[0].iov_len = 2;
        iov[1].iov_base = pktbuf;
-       iov[1].iov_len = PPPOE_MTU;
+       iov[1].iov_len = rfc_4638_mtu + PPPOE_MTU_DIFF;
        r = readv(pppfd, iov, 2);
        if (r <= 0)
                return (-1);
Index: debug.c
===================================================================
RCS file: /cvs/src/usr.sbin/pppoe/debug.c,v
retrieving revision 1.2
diff -u -p -r1.2 debug.c
--- debug.c     4 Jun 2003 04:46:13 -0000       1.2
+++ debug.c     29 Jan 2012 13:59:41 -0000
@@ -156,6 +156,9 @@ debug_packet(u_int8_t *pkt, int len)
                case PPPOE_TAG_RELAY_SESSION:
                        printf("relay-session");
                        break;
+               case PPPOE_TAG_PPP_MAX_PAYLOAD:
+                       printf("ppp-max-payload");
+                       break;
                case PPPOE_TAG_SERVICE_NAME_ERROR:
                        printf("service-name-error");
                        break;
Index: pppoe.8
===================================================================
RCS file: /cvs/src/usr.sbin/pppoe/pppoe.8,v
retrieving revision 1.24
diff -u -p -r1.24 pppoe.8
--- pppoe.8     20 Apr 2011 12:53:50 -0000      1.24
+++ pppoe.8     29 Jan 2012 13:59:41 -0000
@@ -37,6 +37,7 @@
 .Op Fl i Ar interface
 .Op Fl n Ar service
 .Op Fl p Ar system
+.Op Fl m Ar mtu
 .Sh DESCRIPTION
 The
 .Nm pppoe
@@ -67,6 +68,14 @@ This argument is passed, uninterpreted,
 It can be used to specify the configuration data to be used for
 PPP Over Ethernet connections.
 This option is only used in server mode.
+.It Fl m Ar mtu
+Attempt to negotiate a higher MTU, using RFC 4638.  It is your
+responsibility to ensure that the ethernet
+.Ar interface
+supports jumbo frames and is configured with an MTU at
+least 8 bytes greater than this or 1500, and that
+.Xr ppp 8
+is also configured to support the same maximum MTU/MRU.
 .It Fl s
 If this option is specified,
 .Nm
@@ -223,6 +232,13 @@ to adjust the maximum segment size on ou
 .%T A Method for Transmitting PPP Over Ethernet (PPPoE)
 .%A L. Mamakos, et al.
 .Re
+.Rs
+.%R RFC 4638
+.%T Accommodating a Maximum Transit Unit/Maximum Receive Unit
+(MTU/MRU) Greater Than 1492 in the Point-to-Point Protocol over
+Ethernet (PPPoE)
+.%A P. Arberg, et al.
+.Re
 .Sh HISTORY
 This implementation of
 .Nm pppoe
@@ -238,6 +254,11 @@ program was written by
 of
 .An Network Security Technologies, Inc.\&
 .Aq http://www.netsec.net .
+.Sh CAVEATS
+RFC 4638 negotiation is only aware of the MTU configured on the endpoints,
+but not the maximum MTU supported on the path between them.
+If the path cannot pass the larger Ethernet frames, negotiation will succeed
+but the connection will not function correctly.
 .Sh BUGS
 This software runs completely in user mode.
 As such it will have much more overhead than a kernel implementation.
Index: pppoe.c
===================================================================
RCS file: /cvs/src/usr.sbin/pppoe/pppoe.c,v
retrieving revision 1.18
diff -u -p -r1.18 pppoe.c
--- pppoe.c     30 Apr 2011 18:47:48 -0000      1.18
+++ pppoe.c     29 Jan 2012 13:59:41 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pppoe.c,v 1.18 2011/04/30 18:47:48 nicm Exp $ */
+/*     $OpenBSD: pppoe.c,v 1.19 2011/11/05 09:20:36 yasuoka Exp $      */

 /*
* Copyright (c) 2000 Network Security Technologies, Inc. http://www.netsec.net
@@ -27,8 +27,6 @@

 #include <stdio.h>
 #include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/time.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <sys/wait.h>
@@ -46,12 +44,13 @@
 #include <pwd.h>
 #include <unistd.h>
 #include <sysexits.h>
-#include <stdlib.h>
 #include <signal.h>
 #include <ifaddrs.h>
+#include <stdlib.h>

 #include "pppoe.h"

+u_int16_t rfc_4638_mtu = PPPOE_MIN_MTU;
 int option_verbose = 0;
 u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

@@ -75,10 +74,12 @@ main(int argc, char **argv)
        int bpffd, smode = 0, c;
        struct passwd *pw;

+       rfc_4638_mtu = PPPOE_MIN_MTU;
+
        if ((pw = getpwnam("_ppp")) == NULL)
                err(EX_CONFIG, "getpwnam(\"_ppp\")");

-       while ((c = getopt(argc, argv, "svi:n:p:")) != -1) {
+       while ((c = getopt(argc, argv, "svm:i:n:p:")) != -1) {
                switch (c) {
                case 'i':
                        if (ifname != NULL) {
@@ -94,6 +95,11 @@ main(int argc, char **argv)
                        }
                        srvname = (u_int8_t *)optarg;
                        break;
+               case 'm':
+                       rfc_4638_mtu = (u_int16_t) atoi(optarg);
+                       if (rfc_4638_mtu < PPPOE_MIN_MTU)
+                               rfc_4638_mtu = PPPOE_MIN_MTU;
+                       break;
                case 'p':
                        if (sysname != NULL) {
                                usage();
@@ -415,6 +421,8 @@ getifhwaddr(char *ifnhint, char *ifnambu
 {
        struct sockaddr_dl *dl;
        struct ifaddrs *ifap, *ifa;
+       struct ifreq ifr;
+       int fd;

        if (getifaddrs(&ifap) != 0) {
                perror("getifaddrs");
@@ -445,6 +453,34 @@ getifhwaddr(char *ifnhint, char *ifnambu
                        freeifaddrs(ifap);
                        return (-1);
                }
+
+               /* check and reduce interface MTU per RFC 4638 */
+               if (rfc_4638_mtu > PPPOE_MIN_MTU) {
+                       fd = socket(AF_INET, SOCK_DGRAM, 0);
+                       if (fd == -1) {
+                               rfc_4638_mtu = PPPOE_MIN_MTU;
+                               goto choose;    
+                       }
+                       strlcpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ);
+                       if (ioctl(fd, SIOCGIFMTU, &ifr) == -1) {
+                               close(fd);
+                               rfc_4638_mtu = PPPOE_MIN_MTU;
+                               goto choose;    
+                       }
+                       close(fd);
+                       if (ifr.ifr_mtu < ETHERMTU) {
+                               fprintf(stderr, "interface %s mtu %d too low\n",
+                                       ifa->ifa_name, ifr.ifr_mtu);
+                               freeifaddrs(ifap);
+                               return (-1);
+                       }
+                       ifr.ifr_mtu -= 8;
+                       if (rfc_4638_mtu > ifr.ifr_mtu) {
+                               rfc_4638_mtu = ifr.ifr_mtu;
+                       }
+               }
+
+choose:
                bcopy(dl->sdl_data + dl->sdl_nlen, ea, sizeof(*ea));
                strlcpy(ifnambuf, ifa->ifa_name, IFNAMSIZ);
                freeifaddrs(ifap);
@@ -464,7 +500,7 @@ usage(void)
        extern char *__progname;

        fprintf(stderr,
-           "usage: %s [-sv] [-i interface] [-n service] [-p system]\n",
+           "usage: %s [-sv] [-i interface] [-n service] [-p system] [-m 
mtu]\n",
            __progname);
 }

Index: pppoe.h
===================================================================
RCS file: /cvs/src/usr.sbin/pppoe/pppoe.h,v
retrieving revision 1.9
diff -u -p -r1.9 pppoe.h
--- pppoe.h     4 Jul 2011 16:29:35 -0000       1.9
+++ pppoe.h     29 Jan 2012 13:59:41 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pppoe.h,v 1.9 2011/07/04 16:29:35 sthen Exp $ */
+/*     $OpenBSD: pppoe.h,v 1.10 2011/10/15 02:05:07 yasuoka Exp $      */

 /*
* Copyright (c) 2000 Network Security Technologies, Inc. http://www.netsec.net
@@ -44,6 +44,8 @@ struct pppoe_header {
        u_int16_t len;          /* PPPoE payload length */
 };
 #define        PPPOE_MTU               (ETHERMTU - sizeof(struct pppoe_header))
+#define PPPOE_MTU_DIFF         2
+#define        PPPOE_MIN_MTU           (PPPOE_MTU - PPPOE_MTU_DIFF)

 #define        PPPOE_VER_S             0       /* Version shift */
 #define        PPPOE_VER_M             0x0f    /* Version mask */
@@ -76,10 +78,12 @@ struct pppoe_tag {
 #define        PPPOE_TAG_AC_COOKIE             0x0104  /* Access Concentratr 
Cookie */
 #define        PPPOE_TAG_VENDOR_SPEC           0x0105  /* Vendor Specific */
 #define        PPPOE_TAG_RELAY_SESSION         0x0110  /* Relay Session Id */
+#define        PPPOE_TAG_PPP_MAX_PAYLOAD       0x0120  /* RFC 4638 
PPP-Max-Payload */
 #define        PPPOE_TAG_SERVICE_NAME_ERROR    0x0201  /* Service Name Error */
 #define        PPPOE_TAG_AC_SYSTEM_ERROR       0x0202  /* Acc. Concentrator 
Error */
 #define        PPPOE_TAG_GENERIC_ERROR         0x0203  /* Generic Error */

+extern u_int16_t rfc_4638_mtu; /* maximum value of mtu */
 extern int option_verbose;
 extern u_char etherbroadcastaddr[];

@@ -110,7 +114,7 @@ struct pppoe_session {
        struct ether_addr s_ea;         /* remote ethernet mac */
        u_int16_t s_id;                 /* session id */
        int s_fd;                       /* ttyfd */
-       int s_first;
+       u_int16_t s_mtu;                /* negotiated mtu */
 };

 struct pppoe_session_master {
Index: server.c
===================================================================
RCS file: /cvs/src/usr.sbin/pppoe/server.c,v
retrieving revision 1.13
diff -u -p -r1.13 server.c
--- server.c    3 Sep 2007 14:26:54 -0000       1.13
+++ server.c    29 Jan 2012 13:59:41 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server.c,v 1.13 2007/09/03 14:26:54 deraadt Exp $     */
+/*     $OpenBSD: server.c,v 1.15 2011/11/05 09:20:36 yasuoka Exp $     */

 /*
* Copyright (c) 2000 Network Security Technologies, Inc. http://www.netsec.net
@@ -25,12 +25,9 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */

-#include <stdio.h>
 #include <sys/types.h>
 #include <sys/uio.h>
-#include <sys/time.h>
 #include <sys/socket.h>
-#include <sys/ioctl.h>
 #include <sys/param.h>
 #include <net/if.h>
 #include <net/if_dl.h>
@@ -38,11 +35,9 @@
 #include <netinet/in.h>
 #include <netinet/if_ether.h>
 #include <net/bpf.h>
-#include <net/ppp_defs.h>
 #include <errno.h>
 #include <string.h>
 #include <err.h>
-#include <fcntl.h>
 #include <unistd.h>
 #include <sysexits.h>
 #include <stdlib.h>
@@ -65,13 +60,17 @@ static void recv_padt(int, struct ether_
     struct ether_header *, struct pppoe_header *, u_long, u_int8_t *);

 static void send_pado(int, struct ether_addr *,
-    struct ether_header *, struct pppoe_header *, u_long, u_int8_t *);
+    struct ether_header *, struct pppoe_header *, u_long, u_int8_t *,
+    u_int16_t);
 static void send_pads(int, u_int8_t *, struct ether_addr *,
-    struct ether_header *, struct pppoe_header *, u_long, u_int8_t *);
+    struct ether_header *, struct pppoe_header *, u_long, u_int8_t *,
+    u_int16_t);
 static void key_gen(void);
 static u_int8_t *key_make(u_int8_t *, int, u_int8_t *, int);
 static int key_cmp(u_int8_t *, int, u_int8_t *, int, u_int8_t *, int);

+static u_long scrub_pkt(u_long, u_int8_t *);
+
 void
 server_mode(int bpffd, u_int8_t *sysname, u_int8_t *srvname,
     struct ether_addr *ea)
@@ -112,7 +111,7 @@ server_mode(int bpffd, u_int8_t *sysname
                        if (errno == EINTR)
                                continue;
                        err(EX_IOERR, "select");
-                       break;
+                       /* NOTREACHED */
                }
                if (n == 0)
                        continue;
@@ -278,6 +277,8 @@ recv_padi(int bpffd, struct ether_addr *
     struct pppoe_header *ph, u_long pktlen, u_int8_t *pktbuf)
 {
        struct tag_list tl;
+       struct tag_node *n;
+       u_int16_t mtu = 0;

        if (ph->sessionid != 0)
                return;
@@ -291,7 +292,17 @@ recv_padi(int bpffd, struct ether_addr *
        if (tag_lookup(&tl, PPPOE_TAG_SERVICE_NAME, 1) != NULL)
                goto out;

-       send_pado(bpffd, ea, eh, ph, pktlen, pktbuf);
+       n = tag_lookup(&tl, PPPOE_TAG_PPP_MAX_PAYLOAD, 0);
+       if (n != NULL && n->len == sizeof(mtu)) {
+               mtu = n->val[1] | (n->val[0] << 8);
+               if (mtu < PPPOE_MIN_MTU) {
+                       mtu = PPPOE_MIN_MTU;
+               } else if (mtu > rfc_4638_mtu) {
+                       mtu = rfc_4638_mtu;
+               }
+       }
+
+       send_pado(bpffd, ea, eh, ph, pktlen, pktbuf, mtu);

 out:
        tag_destroy(&tl);
@@ -299,12 +310,15 @@ out:

 static void
 send_pado(int bpffd, struct ether_addr *ea, struct ether_header *eh,
-    struct pppoe_header *ph, u_long pktlen, u_int8_t *pktbuf)
+    struct pppoe_header *ph, u_long pktlen, u_int8_t *pktbuf,
+       u_int16_t offer_mtu)
 {
-       struct pppoe_tag ktag, htag;
+       struct pppoe_tag ktag, htag, tmaxpayload, teol;
+       u_int16_t maxpayload;
+       u_long newlen;
        u_int8_t hn[MAXHOSTNAMELEN];
        u_int8_t *k = NULL;
-       struct iovec v[7];
+       struct iovec v[10];
        int idx = 0;

        memcpy(&eh->ether_dhost[0], &eh->ether_shost[0], ETHER_ADDR_LEN);
@@ -315,7 +329,13 @@ send_pado(int bpffd, struct ether_addr *
        ph->code = PPPOE_CODE_PADO;
        v[idx].iov_base = ph; v[idx].iov_len = sizeof(*ph); idx++;

-       v[idx].iov_base = pktbuf; v[idx].iov_len = pktlen; idx++;
+       /* Scrub unknown tags from the packet before returning them */
+       if ((newlen = scrub_pkt(pktlen, pktbuf)) == 0)
+               return;
+       if (newlen != pktlen)
+               ph->len -= pktlen - newlen;
+
+       v[idx].iov_base = pktbuf; v[idx].iov_len = newlen; idx++;

        if (gethostname((char *)hn, sizeof(hn)) < 0)
                return;
@@ -343,6 +363,28 @@ send_pado(int bpffd, struct ether_addr *
        ph->len += sizeof(ktag.len) + sizeof(ktag.type) + COOKIE_LEN;
        ktag.len = htons(COOKIE_LEN);

+       if (offer_mtu > 0) {
+                tmaxpayload.type = htons(PPPOE_TAG_PPP_MAX_PAYLOAD);
+                tmaxpayload.len = sizeof(maxpayload);
+                tmaxpayload.val = (u_int8_t*) &maxpayload;
+                maxpayload = htons(offer_mtu);
+                v[idx].iov_base = &tmaxpayload;
+ v[idx].iov_len = sizeof(tmaxpayload.len) + sizeof(tmaxpayload.type);
+               idx++;
+                v[idx].iov_base = &maxpayload;
+                v[idx].iov_len = sizeof(maxpayload);
+               idx++;
+ ph->len += sizeof(tmaxpayload.len) + sizeof(tmaxpayload.type) + sizeof(maxpayload);
+               tmaxpayload.len = htons(tmaxpayload.len);
+       }
+
+       teol.type = htons(PPPOE_TAG_END_OF_LIST);
+       teol.len = 0;
+       v[idx].iov_base = &teol;
+       v[idx].iov_len = sizeof(teol.type) + sizeof(teol.len);
+       ph->len += v[idx].iov_len;
+       idx++;
+
        ph->len = htons(ph->len);

        writev(bpffd, v, idx);
@@ -358,6 +400,7 @@ recv_padr(int bpffd, u_int8_t *sysname,
 {
        struct tag_list tl;
        struct tag_node *n;
+       u_int16_t mtu = 0;

        if (ph->sessionid != 0)
                return;
@@ -373,7 +416,16 @@ recv_padr(int bpffd, u_int8_t *sysname,
            ac_cookie_key, sizeof(ac_cookie_key)))
                return;

-       send_pads(bpffd, sysname, ea, eh, ph, pktlen, pktbuf);
+       n = tag_lookup(&tl, PPPOE_TAG_PPP_MAX_PAYLOAD, 0);
+       if (n != NULL && n->len == sizeof(mtu)) {
+               mtu = n->val[1] | (n->val[0] << 8);
+               if (mtu > rfc_4638_mtu || mtu < PPPOE_MIN_MTU) {
+                       /* error; fallback */
+                        mtu = PPPOE_MIN_MTU;
+               }
+       }
+
+       send_pads(bpffd, sysname, ea, eh, ph, pktlen, pktbuf, mtu);

        tag_destroy(&tl);
 }
@@ -381,12 +433,14 @@ recv_padr(int bpffd, u_int8_t *sysname,
 static void
 send_pads(int bpffd, u_int8_t *sysname, struct ether_addr *ea,
     struct ether_header *eh, struct pppoe_header *ph,
-    u_long pktlen, u_int8_t *pktbuf)
+    u_long pktlen, u_int8_t *pktbuf, u_int16_t final_mtu)
 {
+       u_long newlen;
        u_int8_t hn[MAXHOSTNAMELEN];
-       struct iovec v[16];
+       u_int16_t maxpayload;
+       struct iovec v[19];
        struct pppoe_session *s;
-       struct pppoe_tag htag;
+       struct pppoe_tag htag, tmaxpayload, teol;
        int idx = 0;

        s = session_new((struct ether_addr *)&eh->ether_shost[0]);
@@ -404,7 +458,13 @@ send_pads(int bpffd, u_int8_t *sysname,
                return;
        v[idx].iov_base = ph; v[idx].iov_len = sizeof(*ph); idx++;

-       v[idx].iov_base = pktbuf; v[idx].iov_len = pktlen; idx++;
+       /* Scrub unknown tags from the packet before returning them */
+       if ((newlen = scrub_pkt(pktlen, pktbuf)) == 0)
+               return;
+       if (newlen != pktlen)
+               ph->len -= pktlen - newlen;
+
+       v[idx].iov_base = pktbuf; v[idx].iov_len = newlen; idx++;

        htag.len = strlen((char *)hn);
        htag.type = htons(PPPOE_TAG_AC_NAME);
@@ -416,6 +476,29 @@ send_pads(int bpffd, u_int8_t *sysname,
        ph->len += sizeof(htag.len) + sizeof(htag.type) + htag.len;
        htag.len = htons(htag.len);

+        if (final_mtu > 0) {
+               s->s_mtu = final_mtu;
+                tmaxpayload.type = htons(PPPOE_TAG_PPP_MAX_PAYLOAD);
+                tmaxpayload.len = sizeof(maxpayload);
+                tmaxpayload.val = (u_int8_t*) &maxpayload;
+                maxpayload = htons(final_mtu);
+                v[idx].iov_base = &tmaxpayload;
+ v[idx].iov_len = sizeof(tmaxpayload.len) + sizeof(tmaxpayload.type);
+               idx++;
+                v[idx].iov_base = &maxpayload;
+                v[idx].iov_len = sizeof(maxpayload);
+               idx++;
+ ph->len += sizeof(tmaxpayload.len) + sizeof(tmaxpayload.type) + sizeof(maxpayload);
+               tmaxpayload.len = htons(tmaxpayload.len);
+        }
+
+        teol.type = htons(PPPOE_TAG_END_OF_LIST);
+        teol.len = 0;
+        v[idx].iov_base = &teol;
+        v[idx].iov_len = sizeof(teol.type) + sizeof(teol.len);
+        ph->len += v[idx].iov_len;
+        idx++;
+
        ph->len = htons(ph->len);

        writev(bpffd, v, idx);
@@ -446,4 +529,62 @@ recv_padt(int bpffd, struct ether_addr *

 out:
        tag_destroy(&tl);
+}
+
+static u_long
+scrub_pkt(u_long pktlen, u_int8_t *pkt)
+{
+       u_long           newlen = 0;
+       u_int16_t        ttype, tlen;
+       u_int8_t        *p = pkt;
+
+       while (pktlen != 0) {
+               if (pktlen < sizeof(u_int16_t))
+                       break;
+               ttype = pkt[1] | (pkt[0] << 8);
+               pkt += sizeof(u_int16_t);
+               pktlen -= sizeof(u_int16_t);
+
+               if (pktlen < sizeof(u_int16_t))
+                       break;
+               tlen = pkt[1] | (pkt[0] << 8);
+               pkt += sizeof(u_int16_t);
+               pktlen -= sizeof(u_int16_t);
+
+               if (pktlen < tlen)
+                       break;
+
+               pkt += tlen;
+               pktlen -= tlen;
+
+               switch (ttype) {
+
+                       /* Echo these tags back to the client */
+               case PPPOE_TAG_SERVICE_NAME:
+               case PPPOE_TAG_AC_NAME:
+               case PPPOE_TAG_HOST_UNIQ:
+               case PPPOE_TAG_AC_COOKIE:
+               case PPPOE_TAG_VENDOR_SPEC:
+               case PPPOE_TAG_RELAY_SESSION:
+               case PPPOE_TAG_SERVICE_NAME_ERROR:
+               case PPPOE_TAG_AC_SYSTEM_ERROR:
+               case PPPOE_TAG_GENERIC_ERROR:
+                       p = pkt;
+                       newlen += sizeof(u_int16_t) + sizeof(u_int16_t) + tlen;
+                       break;
+
+                       /* Scrub these */
+               case PPPOE_TAG_END_OF_LIST:
+               case PPPOE_TAG_PPP_MAX_PAYLOAD:
+               default:
+                       if (pktlen != 0)
+                               bcopy(pkt, p, pktlen);
+                       pkt = p;
+                       break;
+               }
+       }
+
+       if (pktlen != 0)
+               return (0);
+       return (newlen);
 }
Index: session.c
===================================================================
RCS file: /cvs/src/usr.sbin/pppoe/session.c,v
retrieving revision 1.3
diff -u -p -r1.3 session.c
--- session.c   6 May 2004 20:29:04 -0000       1.3
+++ session.c   29 Jan 2012 13:59:41 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: session.c,v 1.3 2004/05/06 20:29:04 deraadt Exp $     */
+/*     $OpenBSD: session.c,v 1.5 2011/11/05 09:20:36 yasuoka Exp $     */

 /*
* Copyright (c) 2000 Network Security Technologies, Inc. http://www.netsec.net
@@ -25,24 +25,15 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */

-#include <stdio.h>
 #include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/time.h>
 #include <sys/socket.h>
-#include <sys/ioctl.h>
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_types.h>
 #include <netinet/in.h>
 #include <netinet/if_ether.h>
-#include <net/bpf.h>
-#include <errno.h>
 #include <string.h>
-#include <err.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <sysexits.h>
 #include <stdlib.h>

 #include "pppoe.h"
@@ -91,8 +82,8 @@ session_new(struct ether_addr *ea)

        s->s_id = id;
        s->s_fd = -1;
-       s->s_first = 1;
        memcpy(&s->s_ea, ea, ETHER_ADDR_LEN);
+       s->s_mtu = PPPOE_MIN_MTU;
        LIST_INSERT_HEAD(&session_master.sm_sessions, s, s_next);

        return (s);
Index: tag.c
===================================================================
RCS file: /cvs/src/usr.sbin/pppoe/tag.c,v
retrieving revision 1.4
diff -u -p -r1.4 tag.c
--- tag.c       4 Jul 2011 16:29:35 -0000       1.4
+++ tag.c       29 Jan 2012 13:59:41 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: tag.c,v 1.4 2011/07/04 16:29:35 sthen Exp $   */
+/*     $OpenBSD: tag.c,v 1.5 2011/11/05 09:20:36 yasuoka Exp $ */

 /*
* Copyright (c) 2000 Network Security Technologies, Inc. http://www.netsec.net
@@ -25,24 +25,14 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */

-#include <stdio.h>
 #include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/time.h>
 #include <sys/socket.h>
-#include <sys/ioctl.h>
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_types.h>
 #include <netinet/in.h>
 #include <netinet/if_ether.h>
-#include <net/bpf.h>
-#include <errno.h>
 #include <string.h>
-#include <err.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sysexits.h>
 #include <stdlib.h>

 #include "pppoe.h"

Reply via email to