}
/* Do some sanity checks on packet */
- if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */
+ if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */
error("Invalid PPPoE packet length (%u)", len);
return -1;
}
case TAG_RELAY_SESSION_ID:
printer(arg, "relay-session-id");
break;
+ case TAG_PPP_MAX_PAYLOAD:
+ printer(arg, "PPP-max-payload");
+ break;
case TAG_SERVICE_NAME_ERROR:
printer(arg, "service-name-error");
text = 1;
#define _GNU_SOURCE 1
#include "pppoe.h"
#include "pppd/pppd.h"
+#include "pppd/fsm.h"
+#include "pppd/lcp.h"
#include <string.h>
#include <stdlib.h>
{
struct PacketCriteria *pc = (struct PacketCriteria *) extra;
PPPoEConnection *conn = pc->conn;
+ UINT16_t mru;
int i;
switch(type) {
conn->relayId.length = htons(len);
memcpy(conn->relayId.payload, data, len);
break;
+ case TAG_PPP_MAX_PAYLOAD:
+ if (len == sizeof(mru)) {
+ memcpy(&mru, data, sizeof(mru));
+ mru = ntohs(mru);
+ if (mru >= ETH_PPPOE_MTU) {
+ if (lcp_allowoptions[0].mru > mru)
+ lcp_allowoptions[0].mru = mru;
+ if (lcp_wantoptions[0].mru > mru)
+ lcp_wantoptions[0].mru = mru;
+ conn->seenMaxPayload = 1;
+ }
+ }
+ break;
case TAG_SERVICE_NAME_ERROR:
error("PADO: Service-Name-Error: %.*s", (int) len, data);
conn->error = 1;
void *extra)
{
PPPoEConnection *conn = (PPPoEConnection *) extra;
+ UINT16_t mru;
switch(type) {
case TAG_SERVICE_NAME:
dbglog("PADS: Service-Name: '%.*s'", (int) len, data);
break;
+ case TAG_PPP_MAX_PAYLOAD:
+ if (len == sizeof(mru)) {
+ memcpy(&mru, data, sizeof(mru));
+ mru = ntohs(mru);
+ if (mru >= ETH_PPPOE_MTU) {
+ if (lcp_allowoptions[0].mru > mru)
+ lcp_allowoptions[0].mru = mru;
+ if (lcp_wantoptions[0].mru > mru)
+ lcp_wantoptions[0].mru = mru;
+ conn->seenMaxPayload = 1;
+ }
+ }
+ break;
case TAG_SERVICE_NAME_ERROR:
error("PADS: Service-Name-Error: %.*s", (int) len, data);
conn->error = 1;
plen += sizeof(pid) + TAG_HDR_SIZE;
}
+ /* Add our maximum MTU/MRU */
+ if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) {
+ PPPoETag maxPayload;
+ UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru));
+ maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
+ maxPayload.length = htons(sizeof(mru));
+ memcpy(maxPayload.payload, &mru, sizeof(mru));
+ CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE);
+ memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
+ cursor += sizeof(mru) + TAG_HDR_SIZE;
+ plen += sizeof(mru) + TAG_HDR_SIZE;
+ }
+
packet.length = htons(plen);
sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
pc.seenACName = 0;
pc.seenServiceName = 0;
+ conn->seenMaxPayload = 0;
conn->error = 0;
do {
plen += sizeof(pid) + TAG_HDR_SIZE;
}
+ /* Add our maximum MTU/MRU */
+ if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) {
+ PPPoETag maxPayload;
+ UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru));
+ maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD);
+ maxPayload.length = htons(sizeof(mru));
+ memcpy(maxPayload.payload, &mru, sizeof(mru));
+ CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE);
+ memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE);
+ cursor += sizeof(mru) + TAG_HDR_SIZE;
+ plen += sizeof(mru) + TAG_HDR_SIZE;
+ }
+
/* Copy cookie and relay-ID if needed */
if (conn->cookie.type) {
CHECK_ROOM(cursor, packet.payload,
timeout *= 2;
} while (conn->discoveryState == STATE_SENT_PADR);
+ if (!conn->seenMaxPayload) {
+ /* RFC 4638: MUST limit MTU/MRU to 1492 */
+ if (lcp_allowoptions[0].mru > ETH_PPPOE_MTU)
+ lcp_allowoptions[0].mru = ETH_PPPOE_MTU;
+ if (lcp_wantoptions[0].mru > ETH_PPPOE_MTU)
+ lcp_wantoptions[0].mru = ETH_PPPOE_MTU;
+ }
+
/* We're done. */
conn->discoveryState = STATE_SESSION;
return;
PPPOEConnectDevice(void)
{
struct sockaddr_pppox sp;
+ struct ifreq ifr;
+ int s;
+
+ /* Restore configuration */
+ lcp_allowoptions[0].mru = conn->mtu;
+ lcp_wantoptions[0].mru = conn->mru;
+
+ /* Update maximum MRU */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ error("Can't get MTU for %s: %m", conn->ifName);
+ goto errout;
+ }
+ strncpy(ifr.ifr_name, conn->ifName, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFMTU, &ifr) < 0) {
+ error("Can't get MTU for %s: %m", conn->ifName);
+ close(s);
+ goto errout;
+ }
+ close(s);
+
+ if (lcp_allowoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD)
+ lcp_allowoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
+ if (lcp_wantoptions[0].mru > ifr.ifr_mtu - TOTAL_OVERHEAD)
+ lcp_wantoptions[0].mru = ifr.ifr_mtu - TOTAL_OVERHEAD;
conn->acName = acName;
conn->serviceName = pppd_pppoe_service;
error("Failed to create PPPoE socket: %m");
goto errout;
}
+
sp.sa_family = AF_PPPOX;
sp.sa_protocol = PX_PROTO_OE;
sp.sa_addr.pppoe.sid = conn->session;
if (lcp_wantoptions[0].mru > MAX_PPPOE_MTU)
lcp_wantoptions[0].mru = MAX_PPPOE_MTU;
+ /* Save configuration */
+ conn->mtu = lcp_allowoptions[0].mru;
+ conn->mru = lcp_wantoptions[0].mru;
+
ccp_allowoptions[0].deflate = 0;
ccp_wantoptions[0].deflate = 0;
}
/* Do some sanity checks on packet */
- if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */
+ if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */
fprintf(stderr, "Invalid PPPoE packet length (%u)\n", len);
return -1;
}
#define TAG_AC_COOKIE 0x0104
#define TAG_VENDOR_SPECIFIC 0x0105
#define TAG_RELAY_SESSION_ID 0x0110
+#define TAG_PPP_MAX_PAYLOAD 0x0120
#define TAG_SERVICE_NAME_ERROR 0x0201
#define TAG_AC_SYSTEM_ERROR 0x0202
#define TAG_GENERIC_ERROR 0x0203
#define IPV4ALEN 4
#define SMALLBUF 256
+/* There are other fixed-size buffers preventing
+ this from being increased to 16110. The buffer
+ sizes would need to be properly de-coupled from
+ the default MRU. For now, getting up to 1500 is
+ enough. */
+#define ETH_JUMBO_LEN 1508
+
/* A PPPoE Packet, including Ethernet headers */
typedef struct PPPoEPacketStruct {
struct ethhdr ethHdr; /* Ethernet header */
unsigned int code:8; /* PPPoE code */
unsigned int session:16; /* PPPoE session */
unsigned int length:16; /* Payload length */
- unsigned char payload[ETH_DATA_LEN]; /* A bit of room to spare */
+ unsigned char payload[ETH_JUMBO_LEN]; /* A bit of room to spare */
} PPPoEPacket;
#define PPPOE_VER(vt) ((vt) >> 4)
/* Header size of a PPPoE packet */
#define PPPOE_OVERHEAD 6 /* type, code, session, length */
#define HDR_SIZE (sizeof(struct ethhdr) + PPPOE_OVERHEAD)
-#define MAX_PPPOE_PAYLOAD (ETH_DATA_LEN - PPPOE_OVERHEAD)
-#define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - 2)
+#define MAX_PPPOE_PAYLOAD (ETH_JUMBO_LEN - PPPOE_OVERHEAD)
+#define PPP_OVERHEAD 2 /* protocol */
+#define MAX_PPPOE_MTU (MAX_PPPOE_PAYLOAD - PPP_OVERHEAD)
+#define TOTAL_OVERHEAD (PPPOE_OVERHEAD + PPP_OVERHEAD)
+#define ETH_PPPOE_MTU (ETH_DATA_LEN - TOTAL_OVERHEAD)
/* PPPoE Tag */
typedef struct PPPoETagStruct {
unsigned int type:16; /* tag type */
unsigned int length:16; /* Length of payload */
- unsigned char payload[ETH_DATA_LEN]; /* A LOT of room to spare */
+ unsigned char payload[ETH_JUMBO_LEN]; /* A LOT of room to spare */
} PPPoETag;
/* Header size of a PPPoE tag */
#define TAG_HDR_SIZE 4
int error; /* Error packet received */
int debug; /* Set to log packets sent and received */
int discoveryTimeout; /* Timeout for discovery packets */
+ int seenMaxPayload;
+ int mtu; /* Stored MTU */
+ int mru; /* Stored MRU */
} PPPoEConnection;
/* Structure used to determine acceptable PADO or PADS packet */