]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/sys-linux.c
Remove the requirement that redistributions in binary form reproduce
[ppp.git] / pppd / sys-linux.c
index 06c5f7fdc457d48402912669f1b8732523675df4..26cadc6e7c797778ca45ca5d80b6eed8f8489b5b 100644 (file)
@@ -2,20 +2,71 @@
  * sys-linux.c - System-dependent procedures for setting up
  * PPP interfaces on Linux systems
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Copyright (c) 1994-2004 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from main.c and pppd.h, which are:
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #include <sys/ioctl.h>
 #include "pppd.h"
 #include "fsm.h"
 #include "ipcp.h"
-#include "patchlevel.h"
 
 #ifdef IPX_CHANGE
 #include "ipxcp.h"
 #endif
 #endif /* IPX_CHANGE */
 
+#ifdef PPP_FILTER
+#include <pcap-bpf.h>
+#include <linux/filter.h>
+#endif /* PPP_FILTER */
+
 #ifdef LOCKLIB
 #include <sys/locks.h>
 #endif
@@ -104,7 +159,7 @@ struct in6_ifreq {
 
 #define IN6_LLADDR_FROM_EUI64(sin6, eui64) do {                        \
        memset(&sin6.s6_addr, 0, sizeof(struct in6_addr));      \
-       sin6.s6_addr16[0] = htons(0xfe80);                      \
+       sin6.s6_addr16[0] = htons(0xfe80);                      \
        eui64_copy(eui64, sin6.s6_addr32[2]);                   \
        } while (0)
 
@@ -123,7 +178,15 @@ static int master_fd = -1;
 #ifdef INET6
 static int sock6_fd = -1;
 #endif /* INET6 */
-static int ppp_dev_fd = -1;    /* fd for /dev/ppp (new style driver) */
+
+/*
+ * For the old-style kernel driver, this is the same as ppp_fd.
+ * For the new-style driver, it is the fd of an instance of /dev/ppp
+ * which is attached to the ppp unit and is used for controlling it.
+ */
+int ppp_dev_fd = -1;           /* fd for /dev/ppp (new style driver) */
+
+static int chindex;            /* channel index (new style driver) */
 
 static fd_set in_fds;          /* set of fds that wait_input waits for */
 static int max_in_fd;          /* highest fd set in in_fds */
@@ -136,7 +199,7 @@ static int driver_is_old       = 0;
 static int restore_term        = 0;    /* 1 => we've munged the terminal */
 static struct termios inittermios;     /* Initial TTY termios */
 
-static int new_style_driver = 0;
+int new_style_driver = 0;
 
 static char loop_name[20];
 static unsigned char inbuf[512]; /* buffer for chars read from loopback */
@@ -148,6 +211,7 @@ static char proxy_arp_dev[16];              /* Device for proxy arp entry */
 static u_int32_t our_old_addr;         /* for detecting address changes */
 static int     dynaddr_set;            /* 1 if ip_dynaddr set */
 static int     looped;                 /* 1 if using loop */
+static int     link_mtu;               /* mtu for the link (not bundle) */
 
 static struct utsname utsname; /* for the kernel version */
 static int kernel_version;
@@ -162,11 +226,9 @@ static int kernel_version;
 #define SIN_ADDR(x)    (((struct sockaddr_in *) (&(x)))->sin_addr.s_addr)
 
 /* Prototypes for procedures local to this file. */
-static int get_flags (int fd);
-static void set_flags (int fd, int flags);
+static int modify_flags(int fd, int clear_bits, int set_bits);
 static int translate_speed (int bps);
 static int baud_rate_of (int speed);
-static char *path_to_route (void);
 static void close_route_table (void);
 static int open_route_table (void);
 static int read_route_table (struct rtentry *rt);
@@ -176,6 +238,7 @@ static int get_ether_addr (u_int32_t ipaddr, struct sockaddr *hwaddr,
 static void decode_version (char *buf, int *version, int *mod, int *patch);
 static int set_kdebugflag(int level);
 static int ppp_registered(void);
+static int make_ppp_unit(void);
 
 extern u_char  inpacket_buf[]; /* borrowed from main.c */
 
@@ -216,36 +279,26 @@ static int still_ppp(void)
        return 0;
 }
 
-/********************************************************************
- *
- * Functions to read and set the flags value in the device driver
+/*
+ * modify_flags - set and clear flag bits controlling the kernel
+ * PPP driver.
  */
+static int modify_flags(int fd, int clear_bits, int set_bits)
+{
+       int flags;
 
-static int get_flags (int fd)
-{    
-    int flags;
-
-    if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &flags) < 0) {
-       if ( ok_error (errno) )
-           flags = 0;
-       else
-           fatal("ioctl(PPPIOCGFLAGS): %m");
-    }
-
-    SYSDEBUG ((LOG_DEBUG, "get flags = %x\n", flags));
-    return flags;
-}
-
-/********************************************************************/
+       if (ioctl(fd, PPPIOCGFLAGS, &flags) == -1)
+               goto err;
+       flags = (flags & ~clear_bits) | set_bits;
+       if (ioctl(fd, PPPIOCSFLAGS, &flags) == -1)
+               goto err;
 
-static void set_flags (int fd, int flags)
-{    
-    SYSDEBUG ((LOG_DEBUG, "set flags = %x\n", flags));
+       return 0;
 
-    if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &flags) < 0) {
-       if (! ok_error (errno) )
-           fatal("ioctl(PPPIOCSFLAGS, %x): %m", flags, errno);
-    }
+ err:
+       if (errno != EIO)
+               error("Failed to set PPP kernel option flags: %m");
+       return -1;
 }
 
 /********************************************************************
@@ -255,24 +308,6 @@ static void set_flags (int fd, int flags)
 
 void sys_init(void)
 {
-    int osmaj, osmin, ospatch;
-    int flags;
-
-    openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
-    setlogmask(LOG_UPTO(LOG_INFO));
-    if (debug)
-       setlogmask(LOG_UPTO(LOG_DEBUG));
-
-    if (new_style_driver) {
-       ppp_dev_fd = open("/dev/ppp", O_RDWR);
-       if (ppp_dev_fd < 0)
-           fatal("Couldn't open /dev/ppp: %m");
-       flags = fcntl(ppp_dev_fd, F_GETFL);
-       if (flags == -1
-           || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1)
-           warn("Couldn't set /dev/ppp to nonblock: %m");
-    }
-
     /* Get an internet socket for doing socket ioctls. */
     sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
     if (sock_fd < 0)
@@ -281,16 +316,11 @@ void sys_init(void)
 #ifdef INET6
     sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0);
     if (sock6_fd < 0)
-       fatal("Couldn't create IPv6 socket: %m(%d)", errno);
+       sock6_fd = -errno;      /* save errno for later */
 #endif
 
     FD_ZERO(&in_fds);
     max_in_fd = 0;
-
-    uname(&utsname);
-    osmaj = osmin = ospatch = 0;
-    sscanf(utsname.release, "%d.%d.%d", &osmaj, &osmin, &ospatch);
-    kernel_version = KVERSION(osmaj, osmin, ospatch);
 }
 
 /********************************************************************
@@ -326,15 +356,18 @@ void sys_cleanup(void)
 void
 sys_close(void)
 {
-    if (new_style_driver)
+    if (new_style_driver && ppp_dev_fd >= 0)
        close(ppp_dev_fd);
     if (sock_fd >= 0)
        close(sock_fd);
+#ifdef INET6
+    if (sock6_fd >= 0)
+       close(sock6_fd);
+#endif
     if (slave_fd >= 0)
        close(slave_fd);
     if (master_fd >= 0)
        close(master_fd);
-    closelog();
 }
 
 /********************************************************************
@@ -344,9 +377,11 @@ sys_close(void)
 
 static int set_kdebugflag (int requested_level)
 {
+    if (ppp_dev_fd < 0)
+       return 1;
     if (ioctl(ppp_dev_fd, PPPIOCSDEBUG, &requested_level) < 0) {
        if ( ! ok_error (errno) )
-           error("ioctl(PPPIOCSDEBUG): %m");
+           error("ioctl(PPPIOCSDEBUG): %m (line %d)", __LINE__);
        return (0);
     }
     SYSDEBUG ((LOG_INFO, "set kernel debugging level to %d",
@@ -356,117 +391,186 @@ static int set_kdebugflag (int requested_level)
 
 /********************************************************************
  *
- * establish_ppp - Turn the serial port into a ppp interface.
+ * tty_establish_ppp - Turn the serial port into a ppp interface.
  */
 
-int establish_ppp (int tty_fd)
+int tty_establish_ppp (int tty_fd)
 {
-    int x;
+    int ret_fd;
 
-/*
- * The current PPP device will be the tty file.
- */
-    set_ppp_fd (tty_fd);
 /*
  * Ensure that the tty device is in exclusive mode.
  */
     if (ioctl(tty_fd, TIOCEXCL, 0) < 0) {
        if ( ! ok_error ( errno ))
-           warn("ioctl(TIOCEXCL): %m");
+           warn("Couldn't make tty exclusive: %m");
     }
 /*
  * Demand mode - prime the old ppp device to relinquish the unit.
  */
     if (!new_style_driver && looped
-       && ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0)
-       fatal("ioctl(transfer ppp unit): %m(%d)", errno);
+       && ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) {
+       error("ioctl(transfer ppp unit): %m, line %d", __LINE__);
+       return -1;
+    }
 /*
  * Set the current tty to the PPP discpline
  */
+
+#ifndef N_SYNC_PPP
+#define N_SYNC_PPP 14
+#endif
+    ppp_disc = (new_style_driver && sync_serial)? N_SYNC_PPP: N_PPP;
     if (ioctl(tty_fd, TIOCSETD, &ppp_disc) < 0) {
-       if ( ! ok_error (errno) )
-           fatal("ioctl(TIOCSETD): %m(%d)", errno);
+       if ( ! ok_error (errno) ) {
+           error("Couldn't set tty to PPP discipline: %m");
+           return -1;
+       }
     }
-/*
- * Find out which interface we were given.
+
+    ret_fd = generic_establish_ppp(tty_fd);
+
+#define SC_RCVB        (SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)
+#define SC_LOGB        (SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \
+                | SC_LOG_FLUSH)
+
+    if (ret_fd >= 0) {
+       modify_flags(ppp_fd, SC_RCVB | SC_LOGB,
+                    (kdebugflag * SC_DEBUG) & SC_LOGB);
+    } else {
+       if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
+           warn("Couldn't reset tty to normal line discipline: %m");
+    }
+
+    return ret_fd;
+}
+
+/********************************************************************
+ *
+ * generic_establish_ppp - Turn the fd into a ppp interface.
  */
+int generic_establish_ppp (int fd)
+{
+    int x;
+
     if (new_style_driver) {
-       if (!looped) {
-           /* allocate ourselves a ppp unit */
+       int flags;
+
+       /* Open an instance of /dev/ppp and connect the channel to it */
+       if (ioctl(fd, PPPIOCGCHAN, &chindex) == -1) {
+           error("Couldn't get channel number: %m");
+           goto err;
+       }
+       dbglog("using channel %d", chindex);
+       fd = open("/dev/ppp", O_RDWR);
+       if (fd < 0) {
+           error("Couldn't reopen /dev/ppp: %m");
+           goto err;
+       }
+       (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+       if (ioctl(fd, PPPIOCATTCHAN, &chindex) < 0) {
+           error("Couldn't attach to channel %d: %m", chindex);
+           goto err_close;
+       }
+       flags = fcntl(fd, F_GETFL);
+       if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+           warn("Couldn't set /dev/ppp (channel) to nonblock: %m");
+       set_ppp_fd(fd);
+
+       if (!looped)
            ifunit = -1;
-           if (ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit) < 0)
-               fatal("Couldn't create new ppp unit: %m");
-           set_kdebugflag(kdebugflag);
-       } else {
-           set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) & ~SC_LOOP_TRAFFIC);
+       if (!looped && !multilink) {
+           /*
+            * Create a new PPP unit.
+            */
+           if (make_ppp_unit() < 0)
+               goto err_close;
        }
-       if (ioctl(tty_fd, PPPIOCATTACH, &ifunit) < 0) {
-           if (errno == EIO)
-               return -1;
-           fatal("Couldn't attach tty to PPP unit %d: %m", ifunit);
+
+       if (looped)
+           modify_flags(ppp_dev_fd, SC_LOOP_TRAFFIC, 0);
+
+       if (!multilink) {
+           add_fd(ppp_dev_fd);
+           if (ioctl(fd, PPPIOCCONNECT, &ifunit) < 0) {
+               error("Couldn't attach to PPP unit %d: %m", ifunit);
+               goto err_close;
+           }
        }
+
     } else {
-       if (ioctl(tty_fd, PPPIOCGUNIT, &x) < 0) {       
-           if ( ! ok_error (errno))
-               fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
+       /*
+        * Old-style driver: find out which interface we were given.
+        */
+       set_ppp_fd (fd);
+       if (ioctl(fd, PPPIOCGUNIT, &x) < 0) {
+           if (ok_error (errno))
+               goto err;
+           fatal("ioctl(PPPIOCGUNIT): %m (line %d)", __LINE__);
        }
        /* Check that we got the same unit again. */
        if (looped && x != ifunit)
            fatal("transfer_ppp failed: wanted unit %d, got %d", ifunit, x);
        ifunit = x;
+
+       /*
+        * Fetch the initial file flags and reset blocking mode on the file.
+        */
+       initfdflags = fcntl(fd, F_GETFL);
+       if (initfdflags == -1 ||
+           fcntl(fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+           if ( ! ok_error (errno))
+               warn("Couldn't set device to non-blocking mode: %m");
+       }
     }
 
-/*
- * Enable debug in the driver if requested.
- */
+    /*
    * Enable debug in the driver if requested.
    */
     if (!looped)
        set_kdebugflag (kdebugflag);
-    looped = 0;
 
-    set_flags(tty_fd, get_flags(tty_fd) & ~(SC_RCV_B7_0 | SC_RCV_B7_1 |
-                                           SC_RCV_EVNP | SC_RCV_ODDP));
+    looped = 0;
 
     SYSDEBUG ((LOG_NOTICE, "Using version %d.%d.%d of PPP driver",
            driver_version, driver_modification, driver_patch));
 
-/*
- * Fetch the initial file flags and reset blocking mode on the file.
- */
-    initfdflags = fcntl(tty_fd, F_GETFL);
-    if (initfdflags == -1 ||
-       fcntl(tty_fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
-       if ( ! ok_error (errno))
-           warn("Couldn't set device to non-blocking mode: %m");
-    }
+    return ppp_fd;
 
-    return ppp_dev_fd;
+ err_close:
+    close(fd);
+ err:
+    return -1;
 }
 
 /********************************************************************
  *
- * disestablish_ppp - Restore the serial port to normal operation.
+ * tty_disestablish_ppp - Restore the serial port to normal operation.
  * This shouldn't call die() because it's called from die().
  */
 
-void disestablish_ppp(int tty_fd)
+void tty_disestablish_ppp(int tty_fd)
 {
     if (!hungup) {
 /*
  * Flush the tty output buffer so that the TIOCSETD doesn't hang.
  */
        if (tcflush(tty_fd, TCIOFLUSH) < 0)
+       {
            warn("tcflush failed: %m");
+           goto flushfailed;
+       }
 /*
  * Restore the previous line discipline
  */
        if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0) {
            if ( ! ok_error (errno))
-               error("ioctl(TIOCSETD, N_TTY): %m");
+               error("ioctl(TIOCSETD, N_TTY): %m (line %d)", __LINE__);
        }
-       
+
        if (ioctl(tty_fd, TIOCNXCL, 0) < 0) {
            if ( ! ok_error (errno))
-               warn("ioctl(TIOCNXCL): %m(%d)", errno);
+               warn("ioctl(TIOCNXCL): %m (line %d)", __LINE__);
        }
 
        /* Reset non-blocking mode on fd. */
@@ -475,28 +579,145 @@ void disestablish_ppp(int tty_fd)
                warn("Couldn't restore device fd flags: %m");
        }
     }
+flushfailed:
     initfdflags = -1;
-    if (new_style_driver && !looped) {
-       if (ioctl(ppp_dev_fd, PPPIOCDETACH) < 0) {
-           if (errno == ENOTTY) {
-               /* first version of new driver didn't have PPPIOCDETACH */
-               int flags;
 
-               close(ppp_dev_fd);
-               ppp_dev_fd = open("/dev/ppp", O_RDWR);
-               if (ppp_dev_fd < 0)
-                   fatal("Couldn't reopen /dev/ppp: %m");
-               flags = fcntl(ppp_dev_fd, F_GETFL);
-               if (flags == -1
-                   || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1)
-                   warn("Couldn't set /dev/ppp to nonblock: %m");
-           } else
-               error("Couldn't release PPP unit: %m");
+    generic_disestablish_ppp(tty_fd);
+}
+
+/********************************************************************
+ *
+ * generic_disestablish_ppp - Restore device components to normal
+ * operation, and reconnect the ppp unit to the loopback if in demand
+ * mode.  This shouldn't call die() because it's called from die().
+ */
+void generic_disestablish_ppp(int dev_fd)
+{
+    if (new_style_driver) {
+       close(ppp_fd);
+       ppp_fd = -1;
+       if (demand) {
+           modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC);
+           looped = 1;
+       } else if (ppp_dev_fd >= 0) {
+           close(ppp_dev_fd);
+           remove_fd(ppp_dev_fd);
+           ppp_dev_fd = -1;
        }
-       set_ppp_fd(-1);
+    } else {
+       /* old-style driver */
+       if (demand)
+           set_ppp_fd(slave_fd);
+       else
+           ppp_dev_fd = -1;
     }
 }
 
+/*
+ * make_ppp_unit - make a new ppp unit for ppp_dev_fd.
+ * Assumes new_style_driver.
+ */
+static int make_ppp_unit()
+{
+       int x, flags;
+
+       if (ppp_dev_fd >= 0) {
+               dbglog("in make_ppp_unit, already had /dev/ppp open?");
+               close(ppp_dev_fd);
+       }
+       ppp_dev_fd = open("/dev/ppp", O_RDWR);
+       if (ppp_dev_fd < 0)
+               fatal("Couldn't open /dev/ppp: %m");
+       flags = fcntl(ppp_dev_fd, F_GETFL);
+       if (flags == -1
+           || fcntl(ppp_dev_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+               warn("Couldn't set /dev/ppp to nonblock: %m");
+
+       ifunit = req_unit;
+       x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+       if (x < 0 && req_unit >= 0 && errno == EEXIST) {
+               warn("Couldn't allocate PPP unit %d as it is already in use", req_unit);
+               ifunit = -1;
+               x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
+       }
+       if (x < 0)
+               error("Couldn't create new ppp unit: %m");
+       return x;
+}
+
+/*
+ * cfg_bundle - configure the existing bundle.
+ * Used in demand mode.
+ */
+void cfg_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+       if (!new_style_driver)
+               return;
+
+       /* set the mrru, mtu and flags */
+       if (ioctl(ppp_dev_fd, PPPIOCSMRRU, &mrru) < 0)
+               error("Couldn't set MRRU: %m");
+
+       modify_flags(ppp_dev_fd, SC_MP_SHORTSEQ|SC_MP_XSHORTSEQ|SC_MULTILINK,
+                    ((rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0)
+                     | (mrru? SC_MULTILINK: 0)));
+
+       /* connect up the channel */
+       if (ioctl(ppp_fd, PPPIOCCONNECT, &ifunit) < 0)
+               fatal("Couldn't attach to PPP unit %d: %m", ifunit);
+       add_fd(ppp_dev_fd);
+}
+
+/*
+ * make_new_bundle - create a new PPP unit (i.e. a bundle)
+ * and connect our channel to it.  This should only get called
+ * if `multilink' was set at the time establish_ppp was called.
+ * In demand mode this uses our existing bundle instead of making
+ * a new one.
+ */
+void make_new_bundle(int mrru, int mtru, int rssn, int tssn)
+{
+       if (!new_style_driver)
+               return;
+
+       /* make us a ppp unit */
+       if (make_ppp_unit() < 0)
+               die(1);
+
+       /* set the mrru and flags */
+       cfg_bundle(mrru, mtru, rssn, tssn);
+}
+
+/*
+ * bundle_attach - attach our link to a given PPP unit.
+ * We assume the unit is controlled by another pppd.
+ */
+int bundle_attach(int ifnum)
+{
+       int master_fd;
+
+       if (!new_style_driver)
+               return -1;
+
+       master_fd = open("/dev/ppp", O_RDWR);
+       if (master_fd < 0)
+               fatal("Couldn't open /dev/ppp: %m");
+       if (ioctl(master_fd, PPPIOCATTACH, &ifnum) < 0) {
+               if (errno == ENXIO) {
+                       close(master_fd);
+                       return 0;       /* doesn't still exist */
+               }
+               fatal("Couldn't attach to interface unit %d: %m\n", ifnum);
+       }
+       if (ioctl(ppp_fd, PPPIOCCONNECT, &ifnum) < 0)
+               fatal("Couldn't connect to interface unit %d: %m", ifnum);
+       modify_flags(master_fd, 0, SC_MULTILINK);
+       close(master_fd);
+
+       ifunit = ifnum;
+       return 1;
+}
+
 /********************************************************************
  *
  * clean_check - Fetch the flags for the device and generate
@@ -514,20 +735,20 @@ void clean_check(void)
            case SC_RCV_B7_0:
                s = "all had bit 7 set to 1";
                break;
-               
+
            case SC_RCV_B7_1:
                s = "all had bit 7 set to 0";
                break;
-               
+
            case SC_RCV_EVNP:
                s = "all had odd parity";
                break;
-               
+
            case SC_RCV_ODDP:
                s = "all had even parity";
                break;
            }
-           
+
            if (s != NULL) {
                warn("Receive serial link is not 8-bit clean:");
                warn("Problem: %s", s);
@@ -535,7 +756,7 @@ void clean_check(void)
        }
     }
 }
-       
+
 
 /*
  * List of valid speeds.
@@ -601,6 +822,9 @@ struct speed {
 #ifdef B57600
     { 57600, B57600 },
 #endif
+#ifdef B76800
+    { 76800, B76800 },
+#endif
 #ifdef B115200
     { 115200, B115200 },
 #endif
@@ -615,6 +839,9 @@ struct speed {
 #endif
 #ifdef B460800
     { 460800, B460800 },
+#endif
+#ifdef B921600
+    { 921600, B921600 },
 #endif
     { 0, 0 }
 };
@@ -646,7 +873,7 @@ static int translate_speed (int bps)
 static int baud_rate_of (int speed)
 {
     struct speed *speedp;
-    
+
     if (speed != 0) {
        for (speedp = speeds; speedp->speed_int; speedp++) {
            if (speed == speedp->speed_val)
@@ -671,13 +898,13 @@ void set_up_tty(int tty_fd, int local)
     setdtr(tty_fd, 1);
     if (tcgetattr(tty_fd, &tios) < 0) {
        if (!ok_error(errno))
-           fatal("tcgetattr: %m(%d)", errno);
+           fatal("tcgetattr: %m (line %d)", __LINE__);
        return;
     }
-    
+
     if (!restore_term)
        inittermios = tios;
-    
+
     tios.c_cflag     &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
     tios.c_cflag     |= CS8 | CREAD | HUPCL;
 
@@ -686,7 +913,7 @@ void set_up_tty(int tty_fd, int local)
     tios.c_lflag      = 0;
     tios.c_cc[VMIN]   = 1;
     tios.c_cc[VTIME]  = 0;
-    
+
     if (local || !modem)
        tios.c_cflag ^= (CLOCAL | HUPCL);
 
@@ -708,7 +935,7 @@ void set_up_tty(int tty_fd, int local)
     default:
        break;
     }
-    
+
     speed = translate_speed(inspeed);
     if (speed) {
        cfsetospeed (&tios, speed);
@@ -724,10 +951,10 @@ void set_up_tty(int tty_fd, int local)
            fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
     }
 
-    if (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0)
-       if (!ok_error(errno))
-           fatal("tcsetattr: %m");
-    
+    while (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0 && !ok_error(errno))
+       if (errno != EINTR)
+           fatal("tcsetattr: %m (line %d)", __LINE__);
+
     baud_rate    = baud_rate_of(speed);
     restore_term = 1;
 }
@@ -762,10 +989,10 @@ void restore_tty (int tty_fd)
  */
        if (!default_device)
            inittermios.c_lflag &= ~(ECHO | ECHONL);
-       
+
        if (tcsetattr(tty_fd, TCSAFLUSH, &inittermios) < 0) {
            if (! ok_error (errno))
-               warn("tcsetattr: %m");
+               warn("tcsetattr: %m (line %d)", __LINE__);
        }
     }
 }
@@ -777,17 +1004,23 @@ void restore_tty (int tty_fd)
 
 void output (int unit, unsigned char *p, int len)
 {
-    if (debug)
-       dbglog("sent %P", p, len);
+    int fd = ppp_fd;
+    int proto;
+
+    dump_packet("sent", p, len);
+    if (snoop_send_hook) snoop_send_hook(p, len);
 
     if (len < PPP_HDRLEN)
        return;
     if (new_style_driver) {
        p += 2;
        len -= 2;
+       proto = (p[0] << 8) + p[1];
+       if (ppp_dev_fd >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
+           fd = ppp_dev_fd;
     }
-    if (write(ppp_dev_fd, p, len) < 0) {
-       if (errno == EWOULDBLOCK || errno == ENOBUFS
+    if (write(fd, p, len) < 0) {
+       if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS
            || errno == ENXIO || errno == EIO || errno == EINTR)
            warn("write: warning: %m (%d)", errno);
        else
@@ -804,13 +1037,14 @@ void output (int unit, unsigned char *p, int len)
 
 void wait_input(struct timeval *timo)
 {
-    fd_set ready;
+    fd_set ready, exc;
     int n;
 
     ready = in_fds;
-    n = select(max_in_fd + 1, &ready, NULL, &ready, timo);
+    exc = in_fds;
+    n = select(max_in_fd + 1, &ready, NULL, &exc, timo);
     if (n < 0 && errno != EINTR)
-       fatal("select: %m(%d)", errno);
+       fatal("select: %m");
 }
 
 /*
@@ -818,6 +1052,8 @@ void wait_input(struct timeval *timo)
  */
 void add_fd(int fd)
 {
+    if (fd >= FD_SETSIZE)
+       fatal("internal error: file descriptor too large (%d)", fd);
     FD_SET(fd, &in_fds);
     if (fd > max_in_fd)
        max_in_fd = fd;
@@ -839,7 +1075,7 @@ void remove_fd(int fd)
 
 int read_packet (unsigned char *buf)
 {
-    int len;
+    int len, nr;
 
     len = PPP_MRU + PPP_HDRLEN;
     if (new_style_driver) {
@@ -847,13 +1083,25 @@ int read_packet (unsigned char *buf)
        *buf++ = PPP_UI;
        len -= 2;
     }
-    len = read(ppp_dev_fd, buf, len);
-    if (len < 0) {
-       if (errno == EWOULDBLOCK || errno == EIO)
-           return -1;
-       fatal("read: %m(%d)", errno);
+    nr = -1;
+    if (ppp_fd >= 0) {
+       nr = read(ppp_fd, buf, len);
+       if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN
+           && errno != EIO && errno != EINTR)
+           error("read: %m");
+       if (nr < 0 && errno == ENXIO)
+           return 0;
     }
-    return new_style_driver? len+2: len;
+    if (nr < 0 && new_style_driver && ppp_dev_fd >= 0) {
+       /* N.B. we read ppp_fd first since LCP packets come in there. */
+       nr = read(ppp_dev_fd, buf, len);
+       if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN
+           && errno != EIO && errno != EINTR)
+           error("read /dev/ppp: %m");
+       if (nr < 0 && errno == ENXIO)
+           return 0;
+    }
+    return (new_style_driver && nr > 0)? nr+2: nr;
 }
 
 /********************************************************************
@@ -882,56 +1130,79 @@ get_loop_output(void)
     if (n == 0)
        fatal("eof on loopback");
 
-    if (errno != EWOULDBLOCK)
+    if (errno != EWOULDBLOCK && errno != EAGAIN)
        fatal("read from loopback: %m(%d)", errno);
 
     return rv;
 }
 
-/********************************************************************
- *
- * ppp_send_config - configure the transmit characteristics of
- * the ppp interface.
+/*
+ * netif_set_mtu - set the MTU on the PPP network interface.
  */
-
-void ppp_send_config (int unit,int mtu,u_int32_t asyncmap,int pcomp,int accomp)
+void
+netif_set_mtu(int unit, int mtu)
 {
-    u_int x;
     struct ifreq ifr;
-  
-    SYSDEBUG ((LOG_DEBUG, "send_config: mtu = %d\n", mtu));
+
+    SYSDEBUG ((LOG_DEBUG, "netif_set_mtu: mtu = %d\n", mtu));
+
+    memset (&ifr, '\0', sizeof (ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+    ifr.ifr_mtu = mtu;
+
+    if (ifunit >= 0 && ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0)
+       error("ioctl(SIOCSIFMTU): %m (line %d)", __LINE__);
+}
+
 /*
- * Set the MTU and other parameters for the ppp device
+ * netif_get_mtu - get the MTU on the PPP network interface.
  */
+int
+netif_get_mtu(int unit)
+{
+    struct ifreq ifr;
+
     memset (&ifr, '\0', sizeof (ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
-    ifr.ifr_mtu = mtu;
-       
-    if (ioctl(sock_fd, SIOCSIFMTU, (caddr_t) &ifr) < 0)
-       fatal("ioctl(SIOCSIFMTU): %m(%d)", errno);
-       
-    if (!still_ppp())
-       return;
-    SYSDEBUG ((LOG_DEBUG, "send_config: asyncmap = %lx\n", asyncmap));
-    if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
-       if (!ok_error(errno))
-           fatal("ioctl(PPPIOCSASYNCMAP): %m(%d)", errno);
-       return;
+
+    if (ifunit >= 0 && ioctl(sock_fd, SIOCGIFMTU, (caddr_t) &ifr) < 0) {
+       error("ioctl(SIOCGIFMTU): %m (line %d)", __LINE__);
+       return 0;
     }
-    
-    x = get_flags(ppp_fd);
-    x = pcomp  ? x | SC_COMP_PROT : x & ~SC_COMP_PROT;
-    x = accomp ? x | SC_COMP_AC   : x & ~SC_COMP_AC;
-    x = sync_serial ? x | SC_SYNC : x & ~SC_SYNC;
-    set_flags(ppp_fd, x);
+    return ifr.ifr_mtu;
+}
+
+/********************************************************************
+ *
+ * tty_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+
+void tty_send_config(int mtu, u_int32_t asyncmap, int pcomp, int accomp)
+{
+       int x;
+
+       if (!still_ppp())
+               return;
+       link_mtu = mtu;
+       if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
+               if (errno != EIO && errno != ENOTTY)
+                       error("Couldn't set transmit async character map: %m");
+               ++error_count;
+               return;
+       }
+
+       x = (pcomp? SC_COMP_PROT: 0) | (accomp? SC_COMP_AC: 0)
+           | (sync_serial? SC_SYNC: 0);
+       modify_flags(ppp_fd, SC_COMP_PROT|SC_COMP_AC|SC_SYNC, x);
 }
 
 /********************************************************************
  *
- * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ * tty_set_xaccm - set the extended transmit ACCM for the interface.
  */
 
-void ppp_set_xaccm (int unit, ext_accm accm)
+void tty_set_xaccm (ext_accm accm)
 {
     SYSDEBUG ((LOG_DEBUG, "set_xaccm: %08lx %08lx %08lx %08lx\n",
                accm[0], accm[1], accm[2], accm[3]));
@@ -940,40 +1211,39 @@ void ppp_set_xaccm (int unit, ext_accm accm)
        return;
     if (ioctl(ppp_fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) {
        if ( ! ok_error (errno))
-           warn("ioctl(set extended ACCM): %m(%d)", errno);
+           warn("ioctl(set extended ACCM): %m (line %d)", __LINE__);
     }
 }
 
 /********************************************************************
  *
- * ppp_recv_config - configure the receive-side characteristics of
+ * tty_recv_config - configure the receive-side characteristics of
  * the ppp interface.
  */
 
-void ppp_recv_config (int unit,int mru,u_int32_t asyncmap,int pcomp,int accomp)
+void tty_recv_config(int mru, u_int32_t asyncmap, int pcomp, int accomp)
 {
-    SYSDEBUG ((LOG_DEBUG, "recv_config: mru = %d\n", mru));
 /*
  * If we were called because the link has gone down then there is nothing
  * which may be done. Just return without incident.
  */
-    if (!still_ppp())
-       return;
+       if (!still_ppp())
+               return;
 /*
  * Set the receiver parameters
  */
-    if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
-       if ( ! ok_error (errno))
-           error("ioctl(PPPIOCSMRU): %m(%d)", errno);
-    }
-    if (new_style_driver && ioctl(ppp_dev_fd, PPPIOCSMRU, (caddr_t) &mru) < 0)
-       error("Couldn't set MRU in generic PPP layer: %m");
+       if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
+               if (errno != EIO && errno != ENOTTY)
+                       error("Couldn't set channel receive MRU: %m");
+       }
+       if (new_style_driver && ppp_dev_fd >= 0
+           && ioctl(ppp_dev_fd, PPPIOCSMRU, (caddr_t) &mru) < 0)
+               error("Couldn't set MRU in generic PPP layer: %m");
 
-    SYSDEBUG ((LOG_DEBUG, "recv_config: asyncmap = %lx\n", asyncmap));
-    if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
-       if (!ok_error(errno))
-           error("ioctl(PPPIOCSRASYNCMAP): %m(%d)", errno);
-    }
+       if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
+               if (errno != EIO && errno != ENOTTY)
+                       error("Couldn't set channel receive asyncmap: %m");
+       }
 }
 
 /********************************************************************
@@ -982,7 +1252,8 @@ void ppp_recv_config (int unit,int mru,u_int32_t asyncmap,int pcomp,int accomp)
  * is acceptable for use.
  */
 
-int ccp_test (int unit, u_char *opt_ptr, int opt_len, int for_transmit)
+int
+ccp_test(int unit, u_char *opt_ptr, int opt_len, int for_transmit)
 {
     struct ppp_option_data data;
 
@@ -1004,13 +1275,39 @@ int ccp_test (int unit, u_char *opt_ptr, int opt_len, int for_transmit)
 
 void ccp_flags_set (int unit, int isopen, int isup)
 {
-    if (still_ppp()) {
-       int x = get_flags(ppp_dev_fd);
-       x = isopen? x | SC_CCP_OPEN : x &~ SC_CCP_OPEN;
-       x = isup?   x | SC_CCP_UP   : x &~ SC_CCP_UP;
-       set_flags (ppp_dev_fd, x);
-    }
+       int x;
+
+       x = (isopen? SC_CCP_OPEN: 0) | (isup? SC_CCP_UP: 0);
+       if (still_ppp() && ppp_dev_fd >= 0)
+               modify_flags(ppp_dev_fd, SC_CCP_OPEN|SC_CCP_UP, x);
+}
+
+#ifdef PPP_FILTER
+/*
+ * set_filters - set the active and pass filters in the kernel driver.
+ */
+int set_filters(struct bpf_program *pass, struct bpf_program *active)
+{
+       struct sock_fprog fp;
+
+       fp.len = pass->bf_len;
+       fp.filter = (struct sock_filter *) pass->bf_insns;
+       if (ioctl(ppp_dev_fd, PPPIOCSPASS, &fp) < 0) {
+               if (errno == ENOTTY)
+                       warn("kernel does not support PPP filtering");
+               else
+                       error("Couldn't set pass-filter in kernel: %m");
+               return 0;
+       }
+       fp.len = active->bf_len;
+       fp.filter = (struct sock_filter *) active->bf_insns;
+       if (ioctl(ppp_dev_fd, PPPIOCSACTIVE, &fp) < 0) {
+               error("Couldn't set active-filter in kernel: %m");
+               return 0;
+       }
+       return 1;
 }
+#endif /* PPP_FILTER */
 
 /********************************************************************
  *
@@ -1022,7 +1319,7 @@ get_idle_time(u, ip)
     struct ppp_idle *ip;
 {
     return ioctl(ppp_dev_fd, PPPIOCGIDLE, ip) >= 0;
-} 
+}
 
 /********************************************************************
  *
@@ -1045,6 +1342,8 @@ get_ppp_stats(u, stats)
     }
     stats->bytes_in = req.stats.p.ppp_ibytes;
     stats->bytes_out = req.stats.p.ppp_obytes;
+    stats->pkts_in = req.stats.p.ppp_ipackets;
+    stats->pkts_out = req.stats.p.ppp_opackets;
     return 1;
 }
 
@@ -1057,9 +1356,13 @@ get_ppp_stats(u, stats)
 
 int ccp_fatal_error (int unit)
 {
-    int x = get_flags(ppp_dev_fd);
+       int flags;
 
-    return x & SC_DC_FERROR;
+       if (ioctl(ppp_dev_fd, PPPIOCGFLAGS, &flags) < 0) {
+               error("Couldn't read compression error flags: %m");
+               flags = 0;
+       }
+       return flags & SC_DC_FERROR;
 }
 
 /********************************************************************
@@ -1075,39 +1378,31 @@ static char *path_to_procfs(const char *tail)
     FILE *fp;
 
     if (proc_path_len == 0) {
+       /* Default the mount location of /proc */
+       strlcpy (proc_path, "/proc", sizeof(proc_path));
+       proc_path_len = 5;
        fp = fopen(MOUNTED, "r");
-       if (fp == NULL) {
-           /* Default the mount location of /proc */
-           strlcpy (proc_path, "/proc", sizeof(proc_path));
-           proc_path_len = 5;
-
-       } else {
+       if (fp != NULL) {
            while ((mntent = getmntent(fp)) != NULL) {
                if (strcmp(mntent->mnt_type, MNTTYPE_IGNORE) == 0)
                    continue;
-               if (strcmp(mntent->mnt_type, "proc") == 0)
+               if (strcmp(mntent->mnt_type, "proc") == 0) {
+                   strlcpy(proc_path, mntent->mnt_dir, sizeof(proc_path));
+                   proc_path_len = strlen(proc_path);
                    break;
+               }
            }
            fclose (fp);
-           if (mntent == 0)
-               proc_path_len = -1;
-           else {
-               strlcpy(proc_path, mntent->mnt_dir, sizeof(proc_path));
-               proc_path_len = strlen(proc_path);
-           }
        }
     }
 
-    if (proc_path_len < 0)
-       return 0;
-
     strlcpy(proc_path + proc_path_len, tail,
            sizeof(proc_path) - proc_path_len);
     return proc_path;
 }
 
 /*
- * path_to_route - determine the path to the proc file system data
+ * /proc/net/route parsing stuff.
  */
 #define ROUTE_MAX_COLS 12
 FILE *route_fd = (FILE *) 0;
@@ -1116,26 +1411,10 @@ static int route_dev_col, route_dest_col, route_gw_col;
 static int route_flags_col, route_mask_col;
 static int route_num_cols;
 
-static char *path_to_route (void);
 static int open_route_table (void);
 static void close_route_table (void);
 static int read_route_table (struct rtentry *rt);
 
-/********************************************************************
- *
- * path_to_route - find the path to the route tables in the proc file system
- */
-
-static char *path_to_route (void)
-{
-    char *path;
-
-    path = path_to_procfs("/net/route");
-    if (path == 0)
-       error("proc file system not mounted");
-    return path;
-}
-
 /********************************************************************
  *
  * close_route_table - close the interface to the route table
@@ -1144,8 +1423,8 @@ static char *path_to_route (void)
 static void close_route_table (void)
 {
     if (route_fd != (FILE *) 0) {
-        fclose (route_fd);
-        route_fd = (FILE *) 0;
+       fclose (route_fd);
+       route_fd = (FILE *) 0;
     }
 }
 
@@ -1161,14 +1440,11 @@ static int open_route_table (void)
 
     close_route_table();
 
-    path = path_to_route();
-    if (path == NULL)
-        return 0;
-
+    path = path_to_procfs("/net/route");
     route_fd = fopen (path, "r");
     if (route_fd == NULL) {
-        error("can't open %s: %m (%d)", path, errno);
-        return 0;
+       error("can't open routing table %s: %m", path);
+       return 0;
     }
 
     route_dev_col = 0;         /* default to usual columns */
@@ -1216,7 +1492,7 @@ static int read_route_table(struct rtentry *rt)
 {
     char *cols[ROUTE_MAX_COLS], *p;
     int col;
-       
+
     memset (rt, '\0', sizeof (struct rtentry));
 
     if (fgets (route_buffer, sizeof (route_buffer), route_fd) == (char *) 0)
@@ -1250,15 +1526,15 @@ static int defaultroute_exists (struct rtentry *rt)
     int result = 0;
 
     if (!open_route_table())
-        return 0;
+       return 0;
 
     while (read_route_table(rt) != 0) {
-        if ((rt->rt_flags & RTF_UP) == 0)
+       if ((rt->rt_flags & RTF_UP) == 0)
            continue;
 
        if (kernel_version > KVERSION(2,1,0) && SIN_ADDR(rt->rt_genmask) != 0)
            continue;
-        if (SIN_ADDR(rt->rt_dst) == 0L) {
+       if (SIN_ADDR(rt->rt_dst) == 0L) {
            result = 1;
            break;
        }
@@ -1318,17 +1594,19 @@ int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
     SET_SA_FAMILY (rt.rt_dst,     AF_INET);
     SET_SA_FAMILY (rt.rt_gateway, AF_INET);
 
+    rt.rt_dev = ifname;
+
     if (kernel_version > KVERSION(2,1,0)) {
        SET_SA_FAMILY (rt.rt_genmask, AF_INET);
        SIN_ADDR(rt.rt_genmask) = 0L;
     }
 
     SIN_ADDR(rt.rt_gateway) = gateway;
-    
+
     rt.rt_flags = RTF_UP | RTF_GATEWAY;
     if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
        if ( ! ok_error ( errno ))
-           error("default route ioctl(SIOCADDRT): %m(%d)", errno);
+           error("default route ioctl(SIOCADDRT): %m");
        return 0;
     }
 
@@ -1357,12 +1635,12 @@ int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
     }
 
     SIN_ADDR(rt.rt_gateway) = gateway;
-    
+
     rt.rt_flags = RTF_UP | RTF_GATEWAY;
     if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
        if (still_ppp()) {
            if ( ! ok_error ( errno ))
-               error("default route ioctl(SIOCDELRT): %m (%d)", errno);
+               error("default route ioctl(SIOCDELRT): %m");
            return 0;
        }
     }
@@ -1382,7 +1660,7 @@ int sifproxyarp (int unit, u_int32_t his_adr)
 
     if (has_proxy_arp == 0) {
        memset (&arpreq, '\0', sizeof(arpreq));
-    
+
        SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
        SIN_ADDR(arpreq.arp_pa) = his_adr;
        arpreq.arp_flags = ATF_PERM | ATF_PUBL;
@@ -1399,7 +1677,7 @@ int sifproxyarp (int unit, u_int32_t his_adr)
 
        if (ioctl(sock_fd, SIOCSARP, (caddr_t)&arpreq) < 0) {
            if ( ! ok_error ( errno ))
-               error("ioctl(SIOCSARP): %m(%d)", errno);
+               error("ioctl(SIOCSARP): %m");
            return 0;
        }
        proxy_arp_addr = his_adr;
@@ -1440,13 +1718,13 @@ int cifproxyarp (int unit, u_int32_t his_adr)
 
        if (ioctl(sock_fd, SIOCDARP, (caddr_t)&arpreq) < 0) {
            if ( ! ok_error ( errno ))
-               warn("ioctl(SIOCDARP): %m(%d)", errno);
+               warn("ioctl(SIOCDARP): %m");
            return 0;
        }
     }
     return 1;
 }
-     
+
 /********************************************************************
  *
  * get_ether_addr - get the hardware address of an interface on the
@@ -1460,15 +1738,18 @@ static int get_ether_addr (u_int32_t ipaddr,
     struct ifreq *ifr, *ifend;
     u_int32_t ina, mask;
     char *aliasp;
-    struct ifreq ifreq;
+    struct ifreq ifreq, bestifreq;
     struct ifconf ifc;
     struct ifreq ifs[MAX_IFS];
-    
+
+    u_int32_t bestmask=0;
+    int found_interface = 0;
+
     ifc.ifc_len = sizeof(ifs);
     ifc.ifc_req = ifs;
     if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
        if ( ! ok_error ( errno ))
-           error("ioctl(SIOCGIFCONF): %m(%d)", errno);
+           error("ioctl(SIOCGIFCONF): %m (line %d)", __LINE__);
        return 0;
     }
 
@@ -1483,7 +1764,7 @@ static int get_ether_addr (u_int32_t ipaddr,
        if (ifr->ifr_addr.sa_family == AF_INET) {
            ina = SIN_ADDR(ifr->ifr_addr);
            strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
-            SYSDEBUG ((LOG_DEBUG, "proxy arp: examining interface %s",
+           SYSDEBUG ((LOG_DEBUG, "proxy arp: examining interface %s",
                        ifreq.ifr_name));
 /*
  * Check that the interface is up, and not point-to-point
@@ -1498,22 +1779,28 @@ static int get_ether_addr (u_int32_t ipaddr,
  * Get its netmask and check that it's on the right subnet.
  */
            if (ioctl(sock_fd, SIOCGIFNETMASK, &ifreq) < 0)
-               continue;
+               continue;
 
            mask = SIN_ADDR(ifreq.ifr_addr);
            SYSDEBUG ((LOG_DEBUG, "proxy arp: interface addr %s mask %lx",
-                       ip_ntoa(ina), ntohl(mask)));
+                      ip_ntoa(ina), ntohl(mask)));
 
            if (((ipaddr ^ ina) & mask) != 0)
-               continue;
-           break;
+               continue; /* no match */
+           /* matched */
+           if (mask >= bestmask) {
+               /* Compare using >= instead of > -- it is possible for
+                  an interface to have a netmask of 0.0.0.0 */
+               found_interface = 1;
+               bestifreq = ifreq;
+               bestmask = mask;
+           }
        }
     }
-    
-    if (ifr >= ifend)
-        return 0;
 
-    strlcpy(name, ifreq.ifr_name, namelen);
+    if (!found_interface) return 0;
+
+    strlcpy(name, bestifreq.ifr_name, namelen);
 
     /* trim off the :1 in eth0:1 */
     aliasp = strchr(name, ':');
@@ -1524,14 +1811,14 @@ static int get_ether_addr (u_int32_t ipaddr,
 /*
  * Now get the hardware address.
  */
-    memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
-    if (ioctl (sock_fd, SIOCGIFHWADDR, &ifreq) < 0) {
-        error("SIOCGIFHWADDR(%s): %m(%d)", ifreq.ifr_name, errno);
-        return 0;
+    memset (&bestifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+    if (ioctl (sock_fd, SIOCGIFHWADDR, &bestifreq) < 0) {
+       error("SIOCGIFHWADDR(%s): %m", bestifreq.ifr_name);
+       return 0;
     }
 
     memcpy (hwaddr,
-           &ifreq.ifr_hwaddr,
+           &bestifreq.ifr_hwaddr,
            sizeof (struct sockaddr));
 
     SYSDEBUG ((LOG_DEBUG,
@@ -1547,6 +1834,38 @@ static int get_ether_addr (u_int32_t ipaddr,
     return 1;
 }
 
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *name)
+{
+       struct ifreq ifreq;
+       int ret, sock_fd;
+
+       sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock_fd < 0)
+               return 0;
+       memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr));
+       strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
+       ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq);
+       close(sock_fd);
+       if (ret >= 0)
+               memcpy(addr, ifreq.ifr_hwaddr.sa_data, 6);
+       return ret;
+}
+
+/*
+ * get_first_ethernet - return the name of the first ethernet-style
+ * interface on this system.
+ */
+char *
+get_first_ethernet()
+{
+       return "eth0";
+}
+
 /********************************************************************
  *
  * Return user specified netmask, modified by any mask we might determine
@@ -1565,14 +1884,14 @@ u_int32_t GetMask (u_int32_t addr)
     struct ifreq ifs[MAX_IFS];
 
     addr = ntohl(addr);
-    
+
     if (IN_CLASSA(addr))       /* determine network mask for address class */
        nmask = IN_CLASSA_NET;
     else if (IN_CLASSB(addr))
            nmask = IN_CLASSB_NET;
     else
            nmask = IN_CLASSC_NET;
-    
+
     /* class D nets are disallowed by bad_ip_adrs */
     mask = netmask | htonl(nmask);
 /*
@@ -1582,10 +1901,10 @@ u_int32_t GetMask (u_int32_t addr)
     ifc.ifc_req = ifs;
     if (ioctl(sock_fd, SIOCGIFCONF, &ifc) < 0) {
        if ( ! ok_error ( errno ))
-           warn("ioctl(SIOCGIFCONF): %m(%d)", errno);
+           warn("ioctl(SIOCGIFCONF): %m (line %d)", __LINE__);
        return mask;
     }
-    
+
     ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
     for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
 /*
@@ -1602,7 +1921,7 @@ u_int32_t GetMask (u_int32_t addr)
        strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
        if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq) < 0)
            continue;
-       
+
        if (((ifreq.ifr_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0)
            continue;
 /*
@@ -1624,24 +1943,20 @@ u_int32_t GetMask (u_int32_t addr)
 static void decode_version (char *buf, int *version,
                            int *modification, int *patch)
 {
-    *version      = (int) strtoul (buf, &buf, 10);
+    char *endp;
+
+    *version      = (int) strtoul (buf, &endp, 10);
     *modification = 0;
     *patch        = 0;
-    
-    if (*buf == '.') {
-       ++buf;
-       *modification = (int) strtoul (buf, &buf, 10);
-       if (*buf == '.') {
-           ++buf;
+
+    if (endp != buf && *endp == '.') {
+       buf = endp + 1;
+       *modification = (int) strtoul (buf, &endp, 10);
+       if (endp != buf && *endp == '.') {
+           buf = endp + 1;
            *patch = (int) strtoul (buf, &buf, 10);
        }
     }
-    
-    if (*buf != '\0') {
-       *version      =
-       *modification =
-       *patch        = 0;
-    }
 }
 
 /********************************************************************
@@ -1673,10 +1988,10 @@ ppp_registered(void)
      * Try to put the device into the PPP discipline.
      */
     if (ioctl(local_fd, TIOCSETD, &ppp_disc) < 0) {
-       error("ioctl(TIOCSETD(PPP)): %m(%d)", errno);
+       error("ioctl(TIOCSETD(PPP)): %m (line %d)", __LINE__);
     } else
        ret = 1;
-    
+
     close(local_fd);
     close(mfd);
     return ret;
@@ -1694,8 +2009,9 @@ int ppp_available(void)
     struct ifreq ifr;
     int    size;
     int    my_version, my_modification, my_patch;
+    int osmaj, osmin, ospatch;
 
-    no_ppp_msg = 
+    no_ppp_msg =
        "This system lacks kernel support for PPP.  This could be because\n"
        "the PPP kernel module could not be loaded, or because PPP was not\n"
        "included in the kernel configuration.  If PPP was included as a\n"
@@ -1703,7 +2019,14 @@ int ppp_available(void)
        "ppp.o exists in /lib/modules/`uname -r`/net.\n"
        "See README.linux file in the ppp distribution for more details.\n";
 
+    /* get the kernel version now, since we are called before sys_init */
+    uname(&utsname);
+    osmaj = osmin = ospatch = 0;
+    sscanf(utsname.release, "%d.%d.%d", &osmaj, &osmin, &ospatch);
+    kernel_version = KVERSION(osmaj, osmin, ospatch);
+
     fd = open("/dev/ppp", O_RDWR);
+#if 0
     if (fd < 0 && errno == ENOENT) {
        /* try making it and see if that helps. */
        if (mknod("/dev/ppp", S_IFCHR | S_IRUSR | S_IWUSR,
@@ -1717,6 +2040,7 @@ int ppp_available(void)
            fd = open("/dev/ppp", O_RDWR);
        }
     }
+#endif /* 0 */
     if (fd >= 0) {
        new_style_driver = 1;
 
@@ -1727,14 +2051,23 @@ int ppp_available(void)
        close(fd);
        return 1;
     }
+    if (kernel_version >= KVERSION(2,3,13)) {
+       if (errno == ENOENT)
+           no_ppp_msg =
+               "pppd is unable to open the /dev/ppp device.\n"
+               "You need to create the /dev/ppp device node by\n"
+               "executing the following command as root:\n"
+               "       mknod /dev/ppp c 108 0\n";
+       return 0;
+    }
 
 /*
  * Open a socket for doing the ioctl operations.
- */    
+ */
     s = socket(AF_INET, SOCK_DGRAM, 0);
     if (s < 0)
        return 0;
-    
+
     strlcpy (ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
     ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
 /*
@@ -1752,10 +2085,10 @@ int ppp_available(void)
  * Ensure that the hardware address is for PPP and not something else
  */
     if (ok)
-        ok = ioctl (s, SIOCGIFHWADDR, (caddr_t) &ifr) >= 0;
+       ok = ioctl (s, SIOCGIFHWADDR, (caddr_t) &ifr) >= 0;
 
     if (ok && ((ifr.ifr_hwaddr.sa_family & ~0xFF) != ARPHRD_PPP))
-        ok = 0;
+       ok = 0;
 
 /*
  *  This is the PPP device. Validate the version of the driver at this
@@ -1787,7 +2120,7 @@ int ppp_available(void)
            /* The version numbers must match */
            if (driver_version != my_version)
                ok = 0;
-      
+
            /* The modification levels must be legal */
            if (driver_modification < 3) {
                if (driver_modification >= 2) {
@@ -1831,11 +2164,8 @@ void logwtmp (const char *line, const char *name, const char *host)
     utmpname(_PATH_UTMP);
     setutent();
     while ((utp = getutent()) && (utp->ut_pid != mypid))
-        /* nothing */;
+       /* nothing */;
 
-    /* Is this call really necessary? There is another one after the 'put' */
-    endutent();
-    
     if (utp)
        memcpy(&ut, utp, sizeof(ut));
     else
@@ -1844,7 +2174,7 @@ void logwtmp (const char *line, const char *name, const char *host)
 
     if (ut.ut_id[0] == 0)
        strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
-       
+
     strncpy(ut.ut_user, name, sizeof(ut.ut_user));
     strncpy(ut.ut_line, line, sizeof(ut.ut_line));
 
@@ -1861,7 +2191,7 @@ void logwtmp (const char *line, const char *name, const char *host)
     if (ipcp_protent.enabled_flag && ipcp_hisoptions[0].neg_addr)
        memcpy(&ut.ut_addr, (char *) &ipcp_hisoptions[0].hisaddr,
                 sizeof(ut.ut_addr));
-       
+
     /* CL: Makes sure that the logout works */
     if (*host == 0 && *name==0)
        ut.ut_host[0]=0;
@@ -1896,21 +2226,18 @@ void logwtmp (const char *line, const char *name, const char *host)
 
 int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid)
 {
-    u_int x = get_flags(ppp_dev_fd);
+       u_int x;
 
-    if (vjcomp) {
-        if (ioctl (ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) {
-           if (! ok_error (errno))
-               error("ioctl(PPPIOCSMAXCID): %m(%d)", errno);
-           vjcomp = 0;
+       if (vjcomp) {
+               if (ioctl(ppp_dev_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0)
+                       error("Couldn't set up TCP header compression: %m");
+               vjcomp = 0;
        }
-    }
 
-    x = vjcomp  ? x | SC_COMP_TCP     : x &~ SC_COMP_TCP;
-    x = cidcomp ? x & ~SC_NO_TCP_CCID : x | SC_NO_TCP_CCID;
-    set_flags (ppp_dev_fd, x);
+       x = (vjcomp? SC_COMP_TCP: 0) | (cidcomp? 0: SC_NO_TCP_CCID);
+       modify_flags(ppp_dev_fd, SC_COMP_TCP|SC_NO_TCP_CCID, x);
 
-    return 1;
+       return 1;
 }
 
 /********************************************************************
@@ -1926,14 +2253,14 @@ int sifup(int u)
     strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
     if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
        if (! ok_error (errno))
-           error("ioctl (SIOCGIFFLAGS): %m(%d)", errno);
+           error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__);
        return 0;
     }
 
     ifr.ifr_flags |= (IFF_UP | IFF_POINTOPOINT);
     if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
        if (! ok_error (errno))
-           error("ioctl(SIOCSIFFLAGS): %m(%d)", errno);
+           error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__);
        return 0;
     }
     if_is_up++;
@@ -1958,7 +2285,7 @@ int sifdown (int u)
     strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
     if (ioctl(sock_fd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
        if (! ok_error (errno))
-           error("ioctl (SIOCGIFFLAGS): %m(%d)", errno);
+           error("ioctl (SIOCGIFFLAGS): %m (line %d)", __LINE__);
        return 0;
     }
 
@@ -1966,7 +2293,7 @@ int sifdown (int u)
     ifr.ifr_flags |= IFF_POINTOPOINT;
     if (ioctl(sock_fd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
        if (! ok_error (errno))
-           error("ioctl(SIOCSIFFLAGS): %m(%d)", errno);
+           error("ioctl(SIOCSIFFLAGS): %m (line %d)", __LINE__);
        return 0;
     }
     return 1;
@@ -1980,15 +2307,15 @@ int sifdown (int u)
 int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
             u_int32_t net_mask)
 {
-    struct ifreq   ifr; 
+    struct ifreq   ifr;
     struct rtentry rt;
-    
+
     memset (&ifr, '\0', sizeof (ifr));
     memset (&rt,  '\0', sizeof (rt));
-    
-    SET_SA_FAMILY (ifr.ifr_addr,    AF_INET); 
-    SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET); 
-    SET_SA_FAMILY (ifr.ifr_netmask, AF_INET); 
+
+    SET_SA_FAMILY (ifr.ifr_addr,    AF_INET);
+    SET_SA_FAMILY (ifr.ifr_dstaddr, AF_INET);
+    SET_SA_FAMILY (ifr.ifr_netmask, AF_INET);
 
     strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
 /*
@@ -1998,12 +2325,12 @@ int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
     if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
        if (errno != EEXIST) {
            if (! ok_error (errno))
-               error("ioctl(SIOCSIFADDR): %m(%d)", errno);
+               error("ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
        }
-        else {
+       else {
            warn("ioctl(SIOCSIFADDR): Address already exists");
        }
-        return (0);
+       return (0);
     }
 /*
  *  Set the gateway address
@@ -2011,9 +2338,9 @@ int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
     SIN_ADDR(ifr.ifr_dstaddr) = his_adr;
     if (ioctl(sock_fd, SIOCSIFDSTADDR, (caddr_t) &ifr) < 0) {
        if (! ok_error (errno))
-           error("ioctl(SIOCSIFDSTADDR): %m(%d)", errno); 
+           error("ioctl(SIOCSIFDSTADDR): %m (line %d)", __LINE__);
        return (0);
-    } 
+    }
 /*
  *  Set the netmask.
  *  For recent kernels, force the netmask to 255.255.255.255.
@@ -2024,9 +2351,9 @@ int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
        SIN_ADDR(ifr.ifr_netmask) = net_mask;
        if (ioctl(sock_fd, SIOCSIFNETMASK, (caddr_t) &ifr) < 0) {
            if (! ok_error (errno))
-               error("ioctl(SIOCSIFNETMASK): %m(%d)", errno); 
+               error("ioctl(SIOCSIFNETMASK): %m (line %d)", __LINE__);
            return (0);
-       } 
+       }
     }
 /*
  *  Add the device route
@@ -2047,7 +2374,7 @@ int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
 
        if (ioctl(sock_fd, SIOCADDRT, &rt) < 0) {
            if (! ok_error (errno))
-               error("ioctl(SIOCADDRT) device route: %m(%d)", errno);
+               error("ioctl(SIOCADDRT) device route: %m (line %d)", __LINE__);
            return (0);
        }
     }
@@ -2104,7 +2431,7 @@ int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr)
 
        if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
            if (still_ppp() && ! ok_error (errno))
-               error("ioctl(SIOCDELRT) device route: %m(%d)", errno);
+               error("ioctl(SIOCDELRT) device route: %m (line %d)", __LINE__);
            return (0);
        }
     }
@@ -2113,10 +2440,10 @@ int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr)
     memset(&ifr, 0, sizeof(ifr));
     SET_SA_FAMILY(ifr.ifr_addr, AF_INET);
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-    
+
     if (ioctl(sock_fd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
        if (! ok_error (errno)) {
-           error("ioctl(SIOCSIFADDR): %m(%d)", errno);
+           error("ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
            return 0;
        }
     }
@@ -2128,7 +2455,7 @@ int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr)
 
 #ifdef INET6
 /********************************************************************
- * 
+ *
  * sif6addr - Config the interface with an IPv6 link-local address
  */
 int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
@@ -2137,13 +2464,18 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     struct ifreq ifr;
     struct in6_rtmsg rt6;
 
+    if (sock6_fd < 0) {
+       errno = -sock6_fd;
+       error("IPv6 socket creation failed: %m");
+       return 0;
+    }
     memset(&ifr, 0, sizeof (ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
     if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
-       error("sif6addr: ioctl(SIOCGIFINDEX): %m (%d)", errno);
+       error("sif6addr: ioctl(SIOCGIFINDEX): %m (line %d)", __LINE__);
        return 0;
     }
-    
+
     /* Local interface */
     memset(&ifr6, 0, sizeof(ifr6));
     IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
@@ -2151,10 +2483,10 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     ifr6.ifr6_prefixlen = 10;
 
     if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6) < 0) {
-       error("sif6addr: ioctl(SIOCSIFADDR): %m (%d)", errno);
+       error("sif6addr: ioctl(SIOCSIFADDR): %m (line %d)", __LINE__);
        return 0;
     }
-    
+
     /* Route to remote host */
     memset(&rt6, 0, sizeof(rt6));
     IN6_LLADDR_FROM_EUI64(rt6.rtmsg_dst, his_eui64);
@@ -2162,9 +2494,9 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     rt6.rtmsg_dst_len = 10;
     rt6.rtmsg_ifindex = ifr.ifr_ifindex;
     rt6.rtmsg_metric = 1;
-    
+
     if (ioctl(sock6_fd, SIOCADDRT, &rt6) < 0) {
-       error("sif6addr: ioctl(SIOCADDRT): %m (%d)", errno);
+       error("sif6addr: ioctl(SIOCADDRT): %m (line %d)", __LINE__);
        return 0;
     }
 
@@ -2181,13 +2513,18 @@ int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     struct ifreq ifr;
     struct in6_ifreq ifr6;
 
+    if (sock6_fd < 0) {
+       errno = -sock6_fd;
+       error("IPv6 socket creation failed: %m");
+       return 0;
+    }
     memset(&ifr, 0, sizeof(ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
     if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
-       error("cif6addr: ioctl(SIOCGIFINDEX): %m (%d)", errno);
+       error("cif6addr: ioctl(SIOCGIFINDEX): %m (line %d)", __LINE__);
        return 0;
     }
-    
+
     memset(&ifr6, 0, sizeof(ifr6));
     IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
     ifr6.ifr6_ifindex = ifr.ifr_ifindex;
@@ -2196,12 +2533,12 @@ int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     if (ioctl(sock6_fd, SIOCDIFADDR, &ifr6) < 0) {
        if (errno != EADDRNOTAVAIL) {
            if (! ok_error (errno))
-               error("cif6addr: ioctl(SIOCDIFADDR): %m (%d)", errno);
+               error("cif6addr: ioctl(SIOCDIFADDR): %m (line %d)", __LINE__);
        }
-        else {
+       else {
            warn("cif6addr: ioctl(SIOCDIFADDR): No such address");
        }
-        return (0);
+       return (0);
     }
     return 1;
 }
@@ -2237,7 +2574,7 @@ get_pty(master_fdp, slave_fdp, slave_name, uid)
            if (ioctl(mfd, TIOCSPTLCK, &ptn) < 0)
                warn("Couldn't unlock pty slave %s: %m", pty_name);
 #endif
-           if ((sfd = open(pty_name, O_RDWR)) < 0)
+           if ((sfd = open(pty_name, O_RDWR | O_NOCTTY)) < 0)
                warn("Couldn't open pty slave %s: %m", pty_name);
        }
     }
@@ -2270,8 +2607,8 @@ get_pty(master_fdp, slave_fdp, slave_name, uid)
     *slave_fdp = sfd;
     if (tcgetattr(sfd, &tios) == 0) {
        tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
-       tios.c_cflag |= CS8 | CREAD;
-       tios.c_iflag  = IGNPAR | CLOCAL;
+       tios.c_cflag |= CS8 | CREAD | CLOCAL;
+       tios.c_iflag  = IGNPAR;
        tios.c_oflag  = 0;
        tios.c_lflag  = 0;
        if (tcsetattr(sfd, TCSAFLUSH, &tios) < 0)
@@ -2295,10 +2632,9 @@ open_ppp_loopback(void)
     looped = 1;
     if (new_style_driver) {
        /* allocate ourselves a ppp unit */
-       ifunit = -1;
-       if (ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit) < 0)
-           fatal("Couldn't create PPP unit: %m");
-       set_flags(ppp_dev_fd, SC_LOOP_TRAFFIC);
+       if (make_ppp_unit() < 0)
+           die(1);
+       modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC);
        set_kdebugflag(kdebugflag);
        ppp_fd = -1;
        return ppp_dev_fd;
@@ -2313,20 +2649,20 @@ open_ppp_loopback(void)
     flags = fcntl(master_fd, F_GETFL);
     if (flags == -1 ||
        fcntl(master_fd, F_SETFL, flags | O_NONBLOCK) == -1)
-       warn("couldn't set master loopback to nonblock: %m(%d)", errno);
+       warn("couldn't set master loopback to nonblock: %m");
 
     flags = fcntl(ppp_fd, F_GETFL);
     if (flags == -1 ||
        fcntl(ppp_fd, F_SETFL, flags | O_NONBLOCK) == -1)
-       warn("couldn't set slave loopback to nonblock: %m(%d)", errno);
+       warn("couldn't set slave loopback to nonblock: %m");
 
     if (ioctl(ppp_fd, TIOCSETD, &ppp_disc) < 0)
-       fatal("ioctl(TIOCSETD): %m(%d)", errno);
+       fatal("ioctl(TIOCSETD): %m (line %d)", __LINE__);
 /*
  * Find out which interface we were given.
  */
     if (ioctl(ppp_fd, PPPIOCGUNIT, &ifunit) < 0)
-       fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
+       fatal("ioctl(PPPIOCGUNIT): %m (line %d)", __LINE__);
 /*
  * Enable debug in the driver if requested.
  */
@@ -2335,33 +2671,6 @@ open_ppp_loopback(void)
     return master_fd;
 }
 
-/********************************************************************
- *
- * restore_loop - reattach the ppp unit to the loopback.
- *
- * The kernel ppp driver automatically reattaches the ppp unit to
- * the loopback if the serial port is set to a line discipline other
- * than ppp, or if it detects a modem hangup.  The former will happen
- * in disestablish_ppp if the latter hasn't already happened, so we
- * shouldn't need to do anything.
- *
- * Just to be sure, set the real serial port to the normal discipline.
- */
-
-void
-restore_loop(void)
-{
-    looped = 1;
-    if (new_style_driver) {
-       set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_LOOP_TRAFFIC);
-       return;
-    }
-    if (ppp_fd != slave_fd) {
-       (void) ioctl(ppp_fd, TIOCSETD, &tty_disc);
-       set_ppp_fd(slave_fd);
-    }
-}
-
 /********************************************************************
  *
  * sifnpmode - Set the mode for handling packets for a given NP.
@@ -2379,8 +2688,7 @@ sifnpmode(u, proto, mode)
     npi.mode     = mode;
     if (ioctl(ppp_dev_fd, PPPIOCSNPMODE, (caddr_t) &npi) < 0) {
        if (! ok_error (errno))
-           error("ioctl(PPPIOCSNPMODE, %d, %d): %m (%d)",
-                  proto, mode, errno);
+           error("ioctl(PPPIOCSNPMODE, %d, %d): %m", proto, mode);
        return 0;
     }
     return 1;
@@ -2397,14 +2705,14 @@ int sipxfaddr (int unit, unsigned long int network, unsigned char * node )
     int    result = 1;
 
 #ifdef IPX_CHANGE
-    int    skfd; 
+    int    skfd;
     struct ifreq         ifr;
     struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
 
     skfd = socket (AF_IPX, SOCK_DGRAM, 0);
-    if (skfd < 0) { 
+    if (skfd < 0) {
        if (! ok_error (errno))
-           dbglog("socket(AF_IPX): %m (%d)", errno);
+           dbglog("socket(AF_IPX): %m (line %d)", __LINE__);
        result = 0;
     }
     else {
@@ -2424,7 +2732,7 @@ int sipxfaddr (int unit, unsigned long int network, unsigned char * node )
            result = 0;
            if (errno != EEXIST) {
                if (! ok_error (errno))
-                   dbglog("ioctl(SIOCSIFADDR, CRTITF): %m (%d)", errno);
+                   dbglog("ioctl(SIOCSIFADDR, CRTITF): %m (line %d)", __LINE__);
            }
            else {
                warn("ioctl(SIOCSIFADDR, CRTITF): Address already exists");
@@ -2448,14 +2756,14 @@ int cipxfaddr (int unit)
     int    result = 1;
 
 #ifdef IPX_CHANGE
-    int    skfd; 
+    int    skfd;
     struct ifreq         ifr;
     struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
 
     skfd = socket (AF_IPX, SOCK_DGRAM, 0);
-    if (skfd < 0) { 
+    if (skfd < 0) {
        if (! ok_error (errno))
-           dbglog("socket(AF_IPX): %m (%d)", errno);
+           dbglog("socket(AF_IPX): %m (line %d)", __LINE__);
        result = 0;
     }
     else {
@@ -2470,7 +2778,7 @@ int cipxfaddr (int unit)
  */
        if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0) {
            if (! ok_error (errno))
-               info("ioctl(SIOCSIFADDR, IPX_DLTITF): %m (%d)", errno);
+               info("ioctl(SIOCSIFADDR, IPX_DLTITF): %m (line %d)", __LINE__);
            result = 0;
        }
        close (skfd);
@@ -2510,7 +2818,7 @@ sys_check_options(void)
 
     if (ipxcp_protent.enabled_flag) {
        struct stat stat_buf;
-        if ((path = path_to_procfs("/net/ipx_interface")) == 0
+       if ((path = path_to_procfs("/net/ipx_interface")) == 0
            || lstat(path, &stat_buf) < 0) {
            error("IPX support is not present in the kernel\n");
            ipxcp_protent.enabled_flag = 0;
@@ -2523,5 +2831,57 @@ sys_check_options(void)
                     driver_patch);
        return 0;
     }
+    if (multilink && !new_style_driver) {
+       warn("Warning: multilink is not supported by the kernel driver");
+       multilink = 0;
+    }
+    return 1;
+}
+
+#ifdef INET6
+/*
+ * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI
+ *
+ * convert the 48-bit MAC address of eth0 into EUI 64. caller also assumes
+ * that the system has a properly configured Ethernet interface for this
+ * function to return non-zero.
+ */
+int
+ether_to_eui64(eui64_t *p_eui64)
+{
+    struct ifreq ifr;
+    int skfd;
+    const unsigned char *ptr;
+
+    skfd = socket(PF_INET6, SOCK_DGRAM, 0);
+    if(skfd == -1)
+    {
+        warn("could not open IPv6 socket");
+        return 0;
+    }
+
+    strcpy(ifr.ifr_name, "eth0");
+    if(ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0)
+    {
+        close(skfd);
+        warn("could not obtain hardware address for eth0");
+        return 0;
+    }
+    close(skfd);
+
+    /*
+     * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
+     */
+    ptr = ifr.ifr_hwaddr.sa_data;
+    p_eui64->e8[0] = ptr[0] | 0x02;
+    p_eui64->e8[1] = ptr[1];
+    p_eui64->e8[2] = ptr[2];
+    p_eui64->e8[3] = 0xFF;
+    p_eui64->e8[4] = 0xFE;
+    p_eui64->e8[5] = ptr[3];
+    p_eui64->e8[6] = ptr[4];
+    p_eui64->e8[7] = ptr[5];
+
     return 1;
 }
+#endif