]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/plugins/rp-pppoe/plugin.c
pppd: Use a compile test to detect crypt.h (#198)
[ppp.git] / pppd / plugins / rp-pppoe / plugin.c
index 2968e72e93c2fb8936520888f0697b49f74852ae..93c0906119d7240fd60ba483768c3654ef642f5f 100644 (file)
@@ -23,7 +23,7 @@
 ***********************************************************************/
 
 static char const RCSID[] =
-"$Id: plugin.c,v 1.16 2008/06/09 08:34:23 paulus Exp $";
+"$Id: plugin.c,v 1.17 2008/06/15 04:35:50 paulus Exp $";
 
 #define _GNU_SOURCE 1
 #include "pppoe.h"
@@ -46,10 +46,8 @@ static char const RCSID[] =
 #include <unistd.h>
 #include <fcntl.h>
 #include <signal.h>
-#include <net/ethernet.h>
 #include <net/if_arp.h>
 #include <linux/ppp_defs.h>
-#include <linux/if_ppp.h>
 #include <linux/if_pppox.h>
 
 #ifndef _ROOT_PATH
@@ -67,6 +65,11 @@ char *pppd_pppoe_service = NULL;
 static char *acName = NULL;
 static char *existingSession = NULL;
 static int printACNames = 0;
+static char *pppoe_reqd_mac = NULL;
+unsigned char pppoe_reqd_mac_addr[6];
+static char *host_uniq;
+static int pppoe_padi_timeout = PADI_TIMEOUT;
+static int pppoe_padi_attempts = MAX_PADI_ATTEMPTS;
 
 static int PPPoEDevnameHook(char *cmd, char **argv, int doit);
 static option_t Options[] = {
@@ -82,6 +85,14 @@ static option_t Options[] = {
       "Attach to existing session (sessid:macaddr)" },
     { "rp_pppoe_verbose", o_int, &printACNames,
       "Be verbose about discovered access concentrators"},
+    { "pppoe-mac", o_string, &pppoe_reqd_mac,
+      "Only connect to specified MAC address" },
+    { "host-uniq", o_string, &host_uniq,
+      "Set the Host-Uniq to the supplied hex string" },
+    { "pppoe-padi-timeout", o_int, &pppoe_padi_timeout,
+      "Initial timeout for discovery packets in seconds" },
+    { "pppoe-padi-attempts", o_int, &pppoe_padi_attempts,
+      "Number of discovery attempts" },
     { NULL }
 };
 int (*OldDevnameHook)(char *cmd, char **argv, int doit) = NULL;
@@ -104,14 +115,12 @@ PPPOEInitDevice(void)
        novm("PPPoE session data");
     }
     memset(conn, 0, sizeof(PPPoEConnection));
-    conn->acName = acName;
-    conn->serviceName = pppd_pppoe_service;
     conn->ifName = devnam;
     conn->discoverySocket = -1;
     conn->sessionSocket = -1;
-    conn->useHostUniq = 1;
     conn->printACNames = printACNames;
-    conn->discoveryTimeout = PADI_TIMEOUT;
+    conn->discoveryTimeout = pppoe_padi_timeout;
+    conn->discoveryAttempts = pppoe_padi_attempts;
     return 1;
 }
 
@@ -128,7 +137,56 @@ static int
 PPPOEConnectDevice(void)
 {
     struct sockaddr_pppox sp;
+    struct ifreq ifr;
+    int s;
+
+    /* Open session socket before discovery phase, to avoid losing session */
+    /* packets sent by peer just after PADS packet (noted on some Cisco    */
+    /* server equipment).                                                  */
+    /* Opening this socket just before waitForPADS in the discovery()      */
+    /* function would be more appropriate, but it would mess-up the code   */
+    conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE);
+    if (conn->sessionSocket < 0) {
+       error("Failed to create PPPoE socket: %m");
+       return -1;
+    }
+
+    /* 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;
+    }
+    strlcpy(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;
+
+    if (host_uniq) {
+       if (!parseHostUniq(host_uniq, &conn->hostUniq))
+           fatal("Illegal value for host-uniq option");
+    } else {
+       /* if a custom host-uniq is not supplied, use our PID */
+       pid_t pid = getpid();
+       conn->hostUniq.type = htons(TAG_HOST_UNIQ);
+       conn->hostUniq.length = htons(sizeof(pid));
+       memcpy(conn->hostUniq.payload, &pid, sizeof(pid));
+    }
+
+    conn->acName = acName;
+    conn->serviceName = pppd_pppoe_service;
     strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
     if (existingSession) {
        unsigned int mac[ETH_ALEN];
@@ -143,22 +201,18 @@ PPPOEConnectDevice(void)
            conn->peerEth[i] = (unsigned char) mac[i];
        }
     } else {
+       conn->discoverySocket =
+            openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth);
        discovery(conn);
        if (conn->discoveryState != STATE_SESSION) {
            error("Unable to complete PPPoE Discovery");
-           return -1;
+           goto errout;
        }
     }
 
     /* Set PPPoE session-number for further consumption */
     ppp_session_number = ntohs(conn->session);
 
-    /* Make the session socket */
-    conn->sessionSocket = socket(AF_PPPOX, SOCK_STREAM, PX_PROTO_OE);
-    if (conn->sessionSocket < 0) {
-       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;
@@ -188,7 +242,6 @@ PPPOEConnectDevice(void)
     if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
                sizeof(struct sockaddr_pppox)) < 0) {
        error("Failed to connect PPPoE socket: %d %m", errno);
-       close(conn->sessionSocket);
        goto errout;
     }
 
@@ -200,6 +253,7 @@ PPPOEConnectDevice(void)
        close(conn->discoverySocket);
        conn->discoverySocket = -1;
     }
+    close(conn->sessionSocket);
     return -1;
 }
 
@@ -235,19 +289,22 @@ PPPOEDisconnectDevice(void)
     memcpy(sp.sa_addr.pppoe.dev, conn->ifName, IFNAMSIZ);
     memcpy(sp.sa_addr.pppoe.remote, conn->peerEth, ETH_ALEN);
     if (connect(conn->sessionSocket, (struct sockaddr *) &sp,
-               sizeof(struct sockaddr_pppox)) < 0)
+               sizeof(struct sockaddr_pppox)) < 0 && errno != EALREADY)
        error("Failed to disconnect PPPoE socket: %d %m", errno);
     close(conn->sessionSocket);
-    /* don't send PADT?? */
-    if (conn->discoverySocket >= 0)
+    if (conn->discoverySocket >= 0) {
+        sendPADT(conn, NULL);
        close(conn->discoverySocket);
+    }
 }
 
 static void
 PPPOEDeviceOptions(void)
 {
-    char buf[256];
-    snprintf(buf, 256, _PATH_ETHOPT "%s", devnam);
+    char buf[MAXPATHLEN];
+
+    strlcpy(buf, _PATH_ETHOPT, MAXPATHLEN);
+    strlcat(buf, devnam, MAXPATHLEN);
     if (!options_from_file(buf, 0, 0, 1))
        exit(EXIT_OPTION_ERROR);
 
@@ -291,7 +348,7 @@ PPPoEDevnameHook(char *cmd, char **argv, int doit)
 
     /* Try getting interface index */
     if (r) {
-       strncpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name));
+       strlcpy(ifr.ifr_name, cmd, sizeof(ifr.ifr_name));
        if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
            r = 0;
        } else {
@@ -310,7 +367,7 @@ PPPoEDevnameHook(char *cmd, char **argv, int doit)
     /* Close socket */
     close(fd);
     if (r && doit) {
-       strncpy(devnam, cmd, sizeof(devnam));
+       strlcpy(devnam, cmd, sizeof(devnam));
        if (the_channel != &pppoe_channel) {
 
            the_channel = &pppoe_channel;
@@ -348,6 +405,21 @@ plugin_init(void)
 
 void pppoe_check_options(void)
 {
+    unsigned int mac[6];
+    int i;
+
+    if (pppoe_reqd_mac != NULL) {
+       if (sscanf(pppoe_reqd_mac, "%x:%x:%x:%x:%x:%x",
+                  &mac[0], &mac[1], &mac[2], &mac[3],
+                  &mac[4], &mac[5]) != 6) {
+           option_error("cannot parse pppoe-mac option value");
+           exit(EXIT_OPTION_ERROR);
+       }
+       for (i = 0; i < 6; ++i)
+           conn->req_peer_mac[i] = mac[i];
+       conn->req_peer = 1;
+    }
+
     lcp_allowoptions[0].neg_accompression = 0;
     lcp_wantoptions[0].neg_accompression = 0;
 
@@ -362,6 +434,10 @@ void pppoe_check_options(void)
     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;