]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/sys-linux.c
Check for EAGAIN as well as EWOULDBLOCK, since they are
[ppp.git] / pppd / sys-linux.c
index f1b48423ef83c537367820db55cc1ffb96b2a841..68e0584aeecb641d914f0b9f3b391f18316deb1c 100644 (file)
@@ -2,20 +2,76 @@
  * 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-2002 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. 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(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. 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>
@@ -108,7 +164,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)
 
@@ -127,7 +183,14 @@ 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 */
@@ -141,7 +204,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 */
@@ -168,8 +231,7 @@ 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 void close_route_table (void);
@@ -182,7 +244,6 @@ 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);
-static void restore_loop(void);        /* Transfer ppp unit back to loopback */
 
 extern u_char  inpacket_buf[]; /* borrowed from main.c */
 
@@ -223,36 +284,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;
 }
 
 /********************************************************************
@@ -262,18 +313,6 @@ static void set_flags (int fd, int flags)
 
 void sys_init(void)
 {
-    int flags;
-
-    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)
@@ -322,15 +361,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();
 }
 
 /********************************************************************
@@ -340,11 +382,11 @@ sys_close(void)
 
 static int set_kdebugflag (int requested_level)
 {
-    if (new_style_driver && ifunit < 0)
+    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",
@@ -359,8 +401,7 @@ static int set_kdebugflag (int requested_level)
 
 int tty_establish_ppp (int tty_fd)
 {
-    int x;
-    int fd = -1;
+    int ret_fd;
 
 /*
  * Ensure that the tty device is in exclusive mode.
@@ -374,7 +415,7 @@ int tty_establish_ppp (int tty_fd)
  */
     if (!new_style_driver && looped
        && ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) {
-       error("ioctl(transfer ppp unit): %m");
+       error("ioctl(transfer ppp unit): %m, line %d", __LINE__);
        return -1;
     }
 /*
@@ -392,11 +433,36 @@ int tty_establish_ppp (int tty_fd)
        }
     }
 
+    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) {
-       /* Open another instance of /dev/ppp and connect the channel to it */
        int flags;
 
-       if (ioctl(tty_fd, PPPIOCGCHAN, &chindex) == -1) {
+       /* 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;
        }
@@ -406,6 +472,7 @@ int tty_establish_ppp (int tty_fd)
            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;
@@ -426,7 +493,7 @@ int tty_establish_ppp (int tty_fd)
        }
 
        if (looped)
-           set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) & ~SC_LOOP_TRAFFIC);
+           modify_flags(ppp_dev_fd, SC_LOOP_TRAFFIC, 0);
 
        if (!multilink) {
            add_fd(ppp_dev_fd);
@@ -440,11 +507,11 @@ int tty_establish_ppp (int tty_fd)
        /*
         * Old-style driver: find out which interface we were given.
         */
-       set_ppp_fd (tty_fd);
-       if (ioctl(tty_fd, PPPIOCGUNIT, &x) < 0) {       
+       set_ppp_fd (fd);
+       if (ioctl(fd, PPPIOCGUNIT, &x) < 0) {
            if (ok_error (errno))
                goto err;
-           fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
+           fatal("ioctl(PPPIOCGUNIT): %m (line %d)", __LINE__);
        }
        /* Check that we got the same unit again. */
        if (looped && x != ifunit)
@@ -454,28 +521,21 @@ int tty_establish_ppp (int tty_fd)
        /*
         * Fetch the initial file flags and reset blocking mode on the file.
         */
-       initfdflags = fcntl(tty_fd, F_GETFL);
+       initfdflags = fcntl(fd, F_GETFL);
        if (initfdflags == -1 ||
-           fcntl(tty_fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+           fcntl(fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
            if ( ! ok_error (errno))
                warn("Couldn't set device to non-blocking mode: %m");
        }
     }
 
-    looped = 0;
-
     /*
      * Enable debug in the driver if requested.
      */
     if (!looped)
        set_kdebugflag (kdebugflag);
 
-#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)
-
-    set_flags(ppp_fd, ((get_flags(ppp_fd) & ~(SC_RCVB | SC_LOGB))
-                      | ((kdebugflag * SC_DEBUG) & SC_LOGB)));
+    looped = 0;
 
     SYSDEBUG ((LOG_NOTICE, "Using version %d.%d.%d of PPP driver",
            driver_version, driver_modification, driver_patch));
@@ -485,39 +545,37 @@ int tty_establish_ppp (int tty_fd)
  err_close:
     close(fd);
  err:
-    if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
-       warn("Couldn't reset tty to normal line discipline: %m");
     return -1;
 }
 
 /********************************************************************
  *
- * tty_disestablish_ppp - Restore the serial port to normal operation,
- * and reconnect the ppp unit to the loopback if in demand mode.
+ * tty_disestablish_ppp - Restore the serial port to normal operation.
  * This shouldn't call die() because it's called from die().
  */
 
 void tty_disestablish_ppp(int tty_fd)
 {
-    if (demand)
-       restore_loop();
     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. */
@@ -526,15 +584,37 @@ void tty_disestablish_ppp(int tty_fd)
                warn("Couldn't restore device fd flags: %m");
        }
     }
+flushfailed:
     initfdflags = -1;
 
+    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 (!looped && ifunit >= 0 && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
-           error("Couldn't release PPP unit: %m");
-       if (!multilink)
+       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;
+       }
+    } else {
+       /* old-style driver */
+       if (demand)
+           set_ppp_fd(slave_fd);
+       else
+           ppp_dev_fd = -1;
     }
 }
 
@@ -544,12 +624,24 @@ void tty_disestablish_ppp(int tty_fd)
  */
 static int make_ppp_unit()
 {
-       int x;
+       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");
+               warn("Couldn't allocate PPP unit %d as it is already in use", req_unit);
                ifunit = -1;
                x = ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit);
        }
@@ -564,20 +656,16 @@ static int make_ppp_unit()
  */
 void cfg_bundle(int mrru, int mtru, int rssn, int tssn)
 {
-       int flags;
-
        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");
-       flags = get_flags(ppp_dev_fd);
-       flags &= ~(SC_MP_SHORTSEQ | SC_MP_XSHORTSEQ);
-       flags |= (rssn? SC_MP_SHORTSEQ: 0) | (tssn? SC_MP_XSHORTSEQ: 0)
-               | (mrru? SC_MULTILINK: 0);
 
-       set_flags(ppp_dev_fd, flags);
+       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)
@@ -611,17 +699,25 @@ void make_new_bundle(int mrru, int mtru, int rssn, int tssn)
  */
 int bundle_attach(int ifnum)
 {
+       int master_fd;
+
        if (!new_style_driver)
                return -1;
 
-       if (ioctl(ppp_dev_fd, PPPIOCATTACH, &ifnum) < 0) {
-               if (errno == ENXIO)
+       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);
-       set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_MULTILINK);
+       modify_flags(master_fd, 0, SC_MULTILINK);
+       close(master_fd);
 
        ifunit = ifnum;
        return 1;
@@ -644,20 +740,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);
@@ -665,7 +761,7 @@ void clean_check(void)
        }
     }
 }
-       
+
 
 /*
  * List of valid speeds.
@@ -782,7 +878,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)
@@ -807,13 +903,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;
 
@@ -822,7 +918,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);
 
@@ -844,7 +940,7 @@ void set_up_tty(int tty_fd, int local)
     default:
        break;
     }
-    
+
     speed = translate_speed(inspeed);
     if (speed) {
        cfsetospeed (&tios, speed);
@@ -862,8 +958,8 @@ void set_up_tty(int tty_fd, int local)
 
     if (tcsetattr(tty_fd, TCSAFLUSH, &tios) < 0)
        if (!ok_error(errno))
-           fatal("tcsetattr: %m");
-    
+           fatal("tcsetattr: %m (line %d)", __LINE__);
+
     baud_rate    = baud_rate_of(speed);
     restore_term = 1;
 }
@@ -898,10 +994,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__);
        }
     }
 }
@@ -916,8 +1012,8 @@ void output (int unit, unsigned char *p, int len)
     int fd = ppp_fd;
     int proto;
 
-    if (debug)
-       dbglog("sent %P", p, len);
+    dump_packet("sent", p, len);
+    if (snoop_send_hook) snoop_send_hook(p, len);
 
     if (len < PPP_HDRLEN)
        return;
@@ -925,11 +1021,11 @@ void output (int unit, unsigned char *p, int len)
        p += 2;
        len -= 2;
        proto = (p[0] << 8) + p[1];
-       if (ifunit >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
+       if (ppp_dev_fd >= 0 && !(proto >= 0xc000 || proto == PPP_CCPFRAG))
            fd = ppp_dev_fd;
     }
     if (write(fd, p, len) < 0) {
-       if (errno == EWOULDBLOCK || errno == ENOBUFS
+       if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS
            || errno == ENXIO || errno == EIO || errno == EINTR)
            warn("write: warning: %m (%d)", errno);
        else
@@ -953,7 +1049,7 @@ void wait_input(struct timeval *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");
 }
 
 /*
@@ -961,6 +1057,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;
@@ -993,15 +1091,17 @@ int read_packet (unsigned char *buf)
     nr = -1;
     if (ppp_fd >= 0) {
        nr = read(ppp_fd, buf, len);
-       if (nr < 0 && errno != EWOULDBLOCK && errno != EIO && errno != EINTR)
+       if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN
+           && errno != EIO && errno != EINTR)
            error("read: %m");
        if (nr < 0 && errno == ENXIO)
            return 0;
     }
-    if (nr < 0 && new_style_driver && ifunit >= 0) {
+    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 != EIO && errno != EINTR)
+       if (nr < 0 && errno != EWOULDBLOCK && errno != EAGAIN
+           && errno != EIO && errno != EINTR)
            error("read /dev/ppp: %m");
        if (nr < 0 && errno == ENXIO)
            return 0;
@@ -1035,7 +1135,7 @@ 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;
@@ -1054,9 +1154,27 @@ netif_set_mtu(int unit, int 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)
-       fatal("ioctl(SIOCSIFMTU): %m");
+       error("ioctl(SIOCSIFMTU): %m (line %d)", __LINE__);
+}
+
+/*
+ * 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));
+
+    if (ifunit >= 0 && ioctl(sock_fd, SIOCGIFMTU, (caddr_t) &ifr) < 0) {
+       error("ioctl(SIOCGIFMTU): %m (line %d)", __LINE__);
+       return 0;
+    }
+    return ifr.ifr_mtu;
 }
 
 /********************************************************************
@@ -1065,28 +1183,23 @@ netif_set_mtu(int unit, int mtu)
  * the ppp interface.
  */
 
-void tty_send_config (int mtu,u_int32_t asyncmap,int pcomp,int accomp)
+void tty_send_config(int mtu, u_int32_t asyncmap, int pcomp, int accomp)
 {
-    u_int x;
+       int x;
 
-/*
- * Set the asyncmap and other parameters for the ppp device
- */
-    if (!still_ppp())
-       return;
-    link_mtu = mtu;
-    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;
-    }
-    
-    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);
+       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);
 }
 
 /********************************************************************
@@ -1103,7 +1216,7 @@ void tty_set_xaccm (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__);
     }
 }
 
@@ -1113,31 +1226,29 @@ void tty_set_xaccm (ext_accm accm)
  * the ppp interface.
  */
 
-void tty_recv_config (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 && ifunit >= 0
-       && 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");
+       }
 }
 
 /********************************************************************
@@ -1146,7 +1257,8 @@ void tty_recv_config (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;
 
@@ -1168,12 +1280,11 @@ 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
@@ -1213,7 +1324,7 @@ get_idle_time(u, ip)
     struct ppp_idle *ip;
 {
     return ioctl(ppp_dev_fd, PPPIOCGIDLE, ip) >= 0;
-} 
+}
 
 /********************************************************************
  *
@@ -1236,6 +1347,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;
 }
 
@@ -1248,9 +1361,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;
 }
 
 /********************************************************************
@@ -1311,8 +1428,8 @@ static int read_route_table (struct rtentry *rt);
 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;
     }
 }
 
@@ -1331,8 +1448,8 @@ static int open_route_table (void)
     path = path_to_procfs("/net/route");
     route_fd = fopen (path, "r");
     if (route_fd == NULL) {
-        error("can't open routing table %s: %m", path);
-        return 0;
+       error("can't open routing table %s: %m", path);
+       return 0;
     }
 
     route_dev_col = 0;         /* default to usual columns */
@@ -1380,7 +1497,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)
@@ -1414,15 +1531,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;
        }
@@ -1482,17 +1599,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;
     }
 
@@ -1521,12 +1640,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;
        }
     }
@@ -1546,7 +1665,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;
@@ -1563,7 +1682,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;
@@ -1604,13 +1723,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
@@ -1624,15 +1743,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;
     }
 
@@ -1647,7 +1769,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
@@ -1662,22 +1784,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, ':');
@@ -1688,14 +1816,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,
@@ -1761,14 +1889,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);
 /*
@@ -1778,10 +1906,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++) {
 /*
@@ -1798,7 +1926,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;
 /*
@@ -1825,7 +1953,7 @@ static void decode_version (char *buf, int *version,
     *version      = (int) strtoul (buf, &endp, 10);
     *modification = 0;
     *patch        = 0;
-    
+
     if (endp != buf && *endp == '.') {
        buf = endp + 1;
        *modification = (int) strtoul (buf, &endp, 10);
@@ -1865,10 +1993,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;
@@ -1888,7 +2016,7 @@ int ppp_available(void)
     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"
@@ -1940,11 +2068,11 @@ int ppp_available(void)
 
 /*
  * 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;
 /*
@@ -1962,10 +2090,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
@@ -1997,7 +2125,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) {
@@ -2041,11 +2169,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
@@ -2054,7 +2179,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));
 
@@ -2071,7 +2196,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;
@@ -2106,21 +2231,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;
 }
 
 /********************************************************************
@@ -2136,14 +2258,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++;
@@ -2168,7 +2290,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;
     }
 
@@ -2176,7 +2298,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;
@@ -2190,15 +2312,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));
 /*
@@ -2208,12 +2330,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
@@ -2221,9 +2343,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.
@@ -2234,9 +2356,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
@@ -2257,7 +2379,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);
        }
     }
@@ -2314,7 +2436,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);
        }
     }
@@ -2323,10 +2445,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;
        }
     }
@@ -2338,7 +2460,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)
@@ -2355,10 +2477,10 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     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);
@@ -2366,10 +2488,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);
@@ -2377,9 +2499,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;
     }
 
@@ -2404,10 +2526,10 @@ int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     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;
@@ -2416,12 +2538,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;
 }
@@ -2517,7 +2639,7 @@ open_ppp_loopback(void)
        /* allocate ourselves a ppp unit */
        if (make_ppp_unit() < 0)
            die(1);
-       set_flags(ppp_dev_fd, SC_LOOP_TRAFFIC);
+       modify_flags(ppp_dev_fd, 0, SC_LOOP_TRAFFIC);
        set_kdebugflag(kdebugflag);
        ppp_fd = -1;
        return ppp_dev_fd;
@@ -2532,20 +2654,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.
  */
@@ -2554,33 +2676,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.
- */
-
-static 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.
@@ -2598,8 +2693,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;
@@ -2616,14 +2710,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 {
@@ -2643,7 +2737,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");
@@ -2667,14 +2761,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 {
@@ -2689,7 +2783,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);
@@ -2729,7 +2823,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;
@@ -2748,3 +2842,51 @@ sys_check_options(void)
     }
     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