]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/plugins/rp-pppoe/discovery.c
pppd: Use a compile test to detect crypt.h (#198)
[ppp.git] / pppd / plugins / rp-pppoe / discovery.c
index dc6148933248aab84869c7e652a52dcd93ce211e..23089df550771f2a795c1e54b8d197b6bdc5e492 100644 (file)
@@ -9,11 +9,13 @@
 ***********************************************************************/
 
 static char const RCSID[] =
-"$Id: discovery.c,v 1.5 2008/06/09 08:34:23 paulus Exp $";
+"$Id: discovery.c,v 1.6 2008/06/15 04:35:50 paulus Exp $";
 
 #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>
@@ -38,6 +40,30 @@ static char const RCSID[] =
 
 #include <signal.h>
 
+/* Calculate time remaining until *exp, return 0 if now >= *exp */
+static int time_left(struct timeval *diff, struct timeval *exp)
+{
+    struct timeval now;
+
+    if (get_time(&now) < 0) {
+       error("get_time: %m");
+       return 0;
+    }
+
+    if (now.tv_sec > exp->tv_sec
+       || (now.tv_sec == exp->tv_sec && now.tv_usec >= exp->tv_usec))
+       return 0;
+
+    diff->tv_sec = exp->tv_sec - now.tv_sec;
+    diff->tv_usec = exp->tv_usec - now.tv_usec;
+    if (diff->tv_usec < 0) {
+       diff->tv_usec += 1000000;
+       --diff->tv_sec;
+    }
+
+    return 1;
+}
+
 /**********************************************************************
 *%FUNCTION: parseForHostUniq
 *%ARGUMENTS:
@@ -54,14 +80,10 @@ static void
 parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data,
                 void *extra)
 {
-    int *val = (int *) extra;
-    if (type == TAG_HOST_UNIQ && len == sizeof(pid_t)) {
-       pid_t tmp;
-       memcpy(&tmp, data, len);
-       if (tmp == getpid()) {
-           *val = 1;
-       }
-    }
+    PPPoETag *tag = extra;
+
+    if (type == TAG_HOST_UNIQ && len == ntohs(tag->length))
+       tag->length = memcmp(data, tag->payload, len);
 }
 
 /**********************************************************************
@@ -78,16 +100,16 @@ parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data,
 static int
 packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet)
 {
-    int forMe = 0;
+    PPPoETag hostUniq = conn->hostUniq;
 
     /* If packet is not directed to our MAC address, forget it */
     if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0;
 
     /* If we're not using the Host-Unique tag, then accept the packet */
-    if (!conn->useHostUniq) return 1;
+    if (!conn->hostUniq.length) return 1;
 
-    parsePacket(packet, parseForHostUniq, &forMe);
-    return forMe;
+    parsePacket(packet, parseForHostUniq, &hostUniq);
+    return !hostUniq.length;
 }
 
 /**********************************************************************
@@ -110,6 +132,7 @@ parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data,
 {
     struct PacketCriteria *pc = (struct PacketCriteria *) extra;
     PPPoEConnection *conn = pc->conn;
+    UINT16_t mru;
     int i;
 
     switch(type) {
@@ -140,6 +163,19 @@ parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data,
        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;
@@ -172,10 +208,24 @@ parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data,
              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;
@@ -247,16 +297,25 @@ sendPADI(PPPoEConnection *conn)
     }
 
     /* If we're using Host-Uniq, copy it over */
-    if (conn->useHostUniq) {
-       PPPoETag hostUniq;
-       pid_t pid = getpid();
-       hostUniq.type = htons(TAG_HOST_UNIQ);
-       hostUniq.length = htons(sizeof(pid));
-       memcpy(hostUniq.payload, &pid, sizeof(pid));
-       CHECK_ROOM(cursor, packet.payload, sizeof(pid) + TAG_HDR_SIZE);
-       memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
-       cursor += sizeof(pid) + TAG_HDR_SIZE;
-       plen += sizeof(pid) + TAG_HDR_SIZE;
+    if (conn->hostUniq.length) {
+       int len = ntohs(conn->hostUniq.length);
+       CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE);
+       memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE);
+       cursor += len + TAG_HDR_SIZE;
+       plen += len + 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);
@@ -274,12 +333,14 @@ sendPADI(PPPoEConnection *conn)
 *%DESCRIPTION:
 * Waits for a PADO packet and copies useful information
 ***********************************************************************/
-static void
+void
 waitForPADO(PPPoEConnection *conn, int timeout)
 {
     fd_set readable;
     int r;
     struct timeval tv;
+    struct timeval expire_at;
+
     PPPoEPacket packet;
     int len;
 
@@ -289,25 +350,33 @@ waitForPADO(PPPoEConnection *conn, int timeout)
     pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
     pc.seenACName    = 0;
     pc.seenServiceName = 0;
+    conn->seenMaxPayload = 0;
     conn->error = 0;
 
+    if (get_time(&expire_at) < 0) {
+       error("get_time (waitForPADO): %m");
+       return;
+    }
+    expire_at.tv_sec += timeout;
+
     do {
        if (BPF_BUFFER_IS_EMPTY) {
-           tv.tv_sec = timeout;
-           tv.tv_usec = 0;
+           if (!time_left(&tv, &expire_at))
+               return;         /* Timed out */
 
            FD_ZERO(&readable);
            FD_SET(conn->discoverySocket, &readable);
 
            while(1) {
                r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
-               if (r >= 0 || errno != EINTR) break;
+               if (r >= 0 || errno != EINTR || got_sigterm) break;
            }
            if (r < 0) {
                error("select (waitForPADO): %m");
                return;
            }
-           if (r == 0) return;        /* Timed out */
+           if (r == 0)
+               return;         /* Timed out */
        }
 
        /* Get the packet */
@@ -333,6 +402,11 @@ waitForPADO(PPPoEConnection *conn, int timeout)
                error("Ignoring PADO packet from non-unicast MAC address");
                continue;
            }
+           if (conn->req_peer
+               && memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) {
+               warn("Ignoring PADO packet from wrong MAC address");
+               continue;
+           }
            if (parsePacket(&packet, parsePADOTags, &pc) < 0)
                return;
            if (conn->error)
@@ -396,16 +470,25 @@ sendPADR(PPPoEConnection *conn)
     cursor += namelen + TAG_HDR_SIZE;
 
     /* If we're using Host-Uniq, copy it over */
-    if (conn->useHostUniq) {
-       PPPoETag hostUniq;
-       pid_t pid = getpid();
-       hostUniq.type = htons(TAG_HOST_UNIQ);
-       hostUniq.length = htons(sizeof(pid));
-       memcpy(hostUniq.payload, &pid, sizeof(pid));
-       CHECK_ROOM(cursor, packet.payload, sizeof(pid)+TAG_HDR_SIZE);
-       memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
-       cursor += sizeof(pid) + TAG_HDR_SIZE;
-       plen += sizeof(pid) + TAG_HDR_SIZE;
+    if (conn->hostUniq.length) {
+       int len = ntohs(conn->hostUniq.length);
+       CHECK_ROOM(cursor, packet.payload, len+TAG_HDR_SIZE);
+       memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE);
+       cursor += len + TAG_HDR_SIZE;
+       plen += len + 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 */
@@ -445,27 +528,36 @@ waitForPADS(PPPoEConnection *conn, int timeout)
     fd_set readable;
     int r;
     struct timeval tv;
+    struct timeval expire_at;
+
     PPPoEPacket packet;
     int len;
 
+    if (get_time(&expire_at) < 0) {
+       error("get_time (waitForPADS): %m");
+       return;
+    }
+    expire_at.tv_sec += timeout;
+
     conn->error = 0;
     do {
        if (BPF_BUFFER_IS_EMPTY) {
-           tv.tv_sec = timeout;
-           tv.tv_usec = 0;
+           if (!time_left(&tv, &expire_at))
+               return;         /* Timed out */
 
            FD_ZERO(&readable);
            FD_SET(conn->discoverySocket, &readable);
 
            while(1) {
                r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
-               if (r >= 0 || errno != EINTR) break;
+               if (r >= 0 || errno != EINTR || got_sigterm) break;
            }
            if (r < 0) {
                error("select (waitForPADS): %m");
                return;
            }
-           if (r == 0) return;
+           if (r == 0)
+               return;         /* Timed out */
        }
 
        /* Get the packet */
@@ -528,12 +620,9 @@ discovery(PPPoEConnection *conn)
     int padrAttempts = 0;
     int timeout = conn->discoveryTimeout;
 
-    conn->discoverySocket =
-       openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
-
     do {
        padiAttempts++;
-       if (padiAttempts > MAX_PADI_ATTEMPTS) {
+       if (got_sigterm || padiAttempts > conn->discoveryAttempts) {
            warn("Timeout waiting for PADO packets");
            close(conn->discoverySocket);
            conn->discoverySocket = -1;
@@ -549,7 +638,7 @@ discovery(PPPoEConnection *conn)
     timeout = conn->discoveryTimeout;
     do {
        padrAttempts++;
-       if (padrAttempts > MAX_PADI_ATTEMPTS) {
+       if (got_sigterm || padrAttempts > conn->discoveryAttempts) {
            warn("Timeout waiting for PADS packets");
            close(conn->discoverySocket);
            conn->discoverySocket = -1;
@@ -561,7 +650,17 @@ discovery(PPPoEConnection *conn)
        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. */
+    close(conn->discoverySocket);
+    conn->discoverySocket = -1;
     conn->discoveryState = STATE_SESSION;
     return;
 }