Rest of IPV6 merge which got lost somehow, plus some minor fixes.
authorPaul Mackerras <paulus@samba.org>
Tue, 24 Aug 1999 05:31:12 +0000 (05:31 +0000)
committerPaul Mackerras <paulus@samba.org>
Tue, 24 Aug 1999 05:31:12 +0000 (05:31 +0000)
pppd/Makefile.linux
pppd/ipcp.c
pppd/ipv6cp.c
pppd/ipxcp.c
pppd/main.c
pppd/pppd.8
pppd/pppd.h
pppd/sys-linux.c

index 67c012cb10c794ffbbc1dcd6fe3886d49a485ef2..bf2bcc55cd774542bb3641a466f60d98e438e82b 100644 (file)
@@ -1,6 +1,6 @@
 #
 # pppd makefile for Linux
-# $Id: Makefile.linux,v 1.32 1999/08/12 03:59:07 paulus Exp $
+# $Id: Makefile.linux,v 1.33 1999/08/24 05:31:08 paulus Exp $
 #
 
 # Default installation locations
@@ -44,6 +44,7 @@ endif
 
 HAS_SHADOW=y
 #USE_PAM=y
+#HAVE_INET6=y
 
 INCLUDE_DIRS= -I../include
 
@@ -84,6 +85,14 @@ LIBS     := -llock $(LIBS)
 CFLAGS   += -DLOCKLIB=1
 endif
 
+ifdef HAVE_INET6
+     PPPDSRCS += ipv6cp.c eui64.c
+     HEADERS  += ipv6cp.h eui64.h
+     PPPDOBJS += ipv6cp.o eui64.o
+     CFLAGS   += -DINET6=1
+endif
+
+
 INSTALL= install -o root
 
 install: pppd
index d3682a032b5243a8c7b6625b37bfe5c399f89bae..a652cbb641b51374129a4d447c99d98a13134ef2 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: ipcp.c,v 1.49 1999/08/13 06:46:12 paulus Exp $"
+#define RCSID  "$Id: ipcp.c,v 1.50 1999/08/24 05:31:09 paulus Exp $"
 
 /*
  * TODO:
@@ -1307,17 +1307,6 @@ ip_check_options()
                wo->ouraddr = local;
        }
     }
-
-    if (demand && wo->hisaddr == 0) {
-       option_error("remote IP address required for demand-dialling\n");
-       exit(1);
-    }
-#if 0
-    if (demand && wo->accept_remote) {
-       option_error("ipcp-accept-remote is incompatible with demand\n");
-       exit(1);
-    }
-#endif
 }
 
 
@@ -1331,6 +1320,17 @@ ip_demand_conf(u)
 {
     ipcp_options *wo = &ipcp_wantoptions[u];
 
+    if (wo->hisaddr == 0) {
+       /* make up an arbitrary address for the peer */
+       wo->hisaddr = htonl(0x0a707070 + ifunit);
+       wo->accept_remote = 1;
+    }
+    if (wo->ouraddr == 0) {
+       /* make up an arbitrary address for us */
+       wo->ouraddr = htonl(0x0a404040 + ifunit);
+       wo->accept_local = 1;
+       disable_defaultip = 1;  /* don't tell the peer this address */
+    }
     if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
        return 0;
     if (!sifup(u))
@@ -1545,6 +1545,7 @@ ipcp_down(f)
     if (demand) {
        sifnpmode(f->unit, PPP_IP, NPMODE_QUEUE);
     } else {
+       sifnpmode(f->unit, PPP_IP, NPMODE_DROP);
        sifdown(f->unit);
        ipcp_clear_addrs(f->unit, ipcp_gotoptions[f->unit].ouraddr,
                         ipcp_hisoptions[f->unit].hisaddr);
index 94f2f67e6e945265c79d47c4f051a4ec77e62004..38cb22bbb5f60bf7585d5cd1c3dcfaebfc79578a 100644 (file)
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- * $Id: ipv6cp.c,v 1.2 1999/08/13 06:46:13 paulus Exp $ 
+ * $Id: ipv6cp.c,v 1.3 1999/08/24 05:31:09 paulus Exp $ 
  *
  *
  * Original version by Inria (www.inria.fr)
  * Modified to match RFC2472 by Tommi Komulainen <Tommi.Komulainen@iki.fi>
  */
 
-#define RCSID  "$Id: ipv6cp.c,v 1.2 1999/08/13 06:46:13 paulus Exp $"
+#define RCSID  "$Id: ipv6cp.c,v 1.3 1999/08/24 05:31:09 paulus Exp $"
 
 /*
  * TODO: 
@@ -46,6 +46,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include "pppd.h"
 #include "fsm.h"
@@ -202,8 +203,8 @@ setifaceid(arg)
     ipv6cp_options *wo = &ipv6cp_wantoptions[0];
     struct in6_addr addr;
     
-#define VALIDID(a) (   ((a).s6_addr64[0] == 0) && \
-                       ((a).s6_addr64[1] != 0) )
+#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \
+                       (((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) )
     
     if ((comma = strchr(arg, ',')) == NULL)
        comma = arg + strlen(arg);
@@ -955,13 +956,13 @@ ipv6_demand_conf(u)
     ipv6cp_options *wo = &ipv6cp_wantoptions[u];
 
 #if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
-    if (!sifup(u, PPP_IPV6))
+    if (!sifup(u))
        return 0;
 #endif    
     if (!sif6addr(u, wo->ourid, wo->hisid))
        return 0;
 #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
-    if (!sifup(u, PPP_IPV6))
+    if (!sifup(u))
        return 0;
 #endif
     if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
@@ -1063,7 +1064,7 @@ ipv6cp_up(f)
 #endif
 
        /* bring the interface up for IPv6 */
-       if (!sifup(f->unit, PPP_IPV6)) {
+       if (!sifup(f->unit)) {
            if (debug)
                warn("sif6up failed");
            ipv6cp_close(f->unit, "Interface configuration failed");
@@ -1126,11 +1127,12 @@ ipv6cp_down(f)
        sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE);
     } else {
 #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
-       sifdown(f->unit, PPP_IPV6);
+       sifnpmode(f->unit, PPP_IPV6, NPMODE_DROP);
+       sifdown(f->unit);
 #endif
        ipv6cp_clear_addrs(f->unit);
 #if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
-       sifdown(f->unit, PPP_IPV6);
+       sifdown(f->unit);
 #endif
     }
 
index 937dea1095cc55dac734b8173b0cf0e3546b78dd..8b45f89116efc5348538196a34067df84da99b3f 100644 (file)
@@ -19,7 +19,7 @@
 
 #ifdef IPX_CHANGE
 
-#define RCSID  "$Id: ipxcp.c,v 1.17 1999/08/13 06:46:14 paulus Exp $"
+#define RCSID  "$Id: ipxcp.c,v 1.18 1999/08/24 05:31:09 paulus Exp $"
 
 /*
  * TODO:
@@ -1266,6 +1266,7 @@ ipxcp_up(f)
        ipxcp_close(unit, "Interface configuration failed");
        return;
     }
+    ipxcp_is_up = 1;
 
     /* set the network number for IPX */
     if (!sipxfaddr(unit, go->network, go->our_node)) {
@@ -1275,7 +1276,6 @@ ipxcp_up(f)
        return;
     }
 
-    ipxcp_is_up = 1;
     np_up(f->unit, PPP_IPX);
 
     /*
@@ -1299,11 +1299,12 @@ ipxcp_down(f)
 {
     IPXCPDEBUG(("ipxcp: down"));
 
-    if (ipxcp_is_up) {
-       ipxcp_is_up = 0;
-       np_down(f->unit, PPP_IPX);
-    }
-    cipxfaddr (f->unit);
+    if (!ipxcp_is_up)
+       return;
+    ipxcp_is_up = 0;
+    np_down(f->unit, PPP_IPX);
+    cipxfaddr(f->unit);
+    sifnpmode(f->unit, PPP_IPX, NPMODE_DROP);
     sifdown(f->unit);
     ipxcp_script (f, _PATH_IPXDOWN);
 }
index 4da0b9af4bc73b2eaf9dea34abcea2ec18368938..e484069687ac58a6348226d65505cbf3b4dcbf64 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: main.c,v 1.83 1999/08/13 06:46:15 paulus Exp $"
+#define RCSID  "$Id: main.c,v 1.84 1999/08/24 05:31:10 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -46,6 +46,9 @@
 #include "fsm.h"
 #include "lcp.h"
 #include "ipcp.h"
+#ifdef INET6
+#include "ipv6cp.h"
+#endif
 #include "upap.h"
 #include "chap.h"
 #include "ccp.h"
@@ -160,7 +163,7 @@ static void open_ccp __P((int));
 static void bad_signal __P((int));
 static void holdoff_end __P((void *));
 static int device_script __P((char *, int, int, int));
-static void reap_kids __P((int waitfor));
+static int reap_kids __P((int waitfor));
 static void record_child __P((int, char *, void (*) (void *), void *));
 static int start_charshunt __P((int, int));
 static void charshunt_done __P((void *));
@@ -194,6 +197,9 @@ struct protent *protocols[] = {
     &cbcp_protent,
 #endif
     &ipcp_protent,
+#ifdef INET6
+    &ipv6cp_protent,
+#endif
     &ccp_protent,
 #ifdef IPX_CHANGE
     &ipxcp_protent,
@@ -251,6 +257,12 @@ main(argc, argv)
 
     ngroups = getgroups(NGROUPS_MAX, groups);
 
+    /*
+     * Initialize magic number generator now so that protocols may
+     * use magic numbers in initialization.
+     */
+    magic_init();
+
     /*
      * Initialize to the standard option set, then parse, in order,
      * the system options file, the user's options file,
@@ -373,10 +385,9 @@ main(argc, argv)
     script_setenv("DEVICE", devnam);
 
     /*
-     * Initialize system-dependent stuff and magic number package.
+     * Initialize system-dependent stuff.
      */
     sys_init();
-    magic_init();
     if (debug)
        setlogmask(LOG_UPTO(LOG_DEBUG));
 
@@ -814,6 +825,18 @@ main(argc, argv)
                 link_stats.bytes_out, link_stats.bytes_in);
        }
 
+       /*
+        * Delete pid file before disestablishing ppp.  Otherwise it
+        * can happen that another pppd gets the same unit and then
+        * we delete its pid file.
+        */
+       if (!demand) {
+           if (pidfilename[0] != 0
+               && unlink(pidfilename) < 0 && errno != ENOENT) 
+               warn("unable to delete pid file %s: %m", pidfilename);
+           pidfilename[0] = 0;
+       }
+
        /*
         * If we may want to bring the link up again, transfer
         * the ppp unit back to the loopback.  Set the
@@ -905,7 +928,8 @@ main(argc, argv)
            for (chp = children; chp != NULL; chp = chp->next)
                dbglog("  script %s, pid %d", chp->prog, chp->pid);
        }
-       reap_kids(1);
+       if (reap_kids(1) < 0)
+           break;
     }
 
     die(status);
@@ -1665,7 +1689,7 @@ record_child(pid, prog, done, arg)
  * reap_kids - get status from any dead child processes,
  * and log a message for abnormal terminations.
  */
-static void
+static int
 reap_kids(waitfor)
     int waitfor;
 {
@@ -1695,8 +1719,13 @@ reap_kids(waitfor)
        if (chp)
            free(chp);
     }
-    if (pid == -1 && errno != ECHILD && errno != EINTR)
-       error("Error waiting for child process: %m");
+    if (pid == -1) {
+       if (errno == ECHILD)
+           return -1;
+       if (errno != EINTR)
+           error("Error waiting for child process: %m");
+    }
+    return 0;
 }
 
 
index be3fcab908cdb46ff5a6866434b567b17b5994d9..67b5d3299378461a390d66daaf1f3038b2bd3e81 100644 (file)
@@ -1,5 +1,5 @@
 .\" manual page [] for pppd 2.3
-.\" $Id: pppd.8,v 1.45 1999/08/13 13:42:35 johnsonm Exp $
+.\" $Id: pppd.8,v 1.46 1999/08/24 05:31:10 paulus Exp $
 .\" SH section heading
 .\" SS subsection heading
 .\" LP paragraph
@@ -148,13 +148,14 @@ Set the MRU [Maximum Receive Unit] value to \fIn\fR. Pppd
 will ask the peer to send packets of no more than \fIn\fR bytes.  The
 minimum MRU value is 128.  The default MRU value is 1500.  A value of
 296 is recommended for slow links (40 bytes for TCP/IP header + 256
-bytes of data).
+bytes of data).  (Note that for IPv6 MRU must be at least 1280)
 .TP
 .B mtu \fIn
 Set the MTU [Maximum Transmit Unit] value to \fIn\fR.  Unless the
 peer requests a smaller value via MRU negotiation, pppd will
 request that the kernel networking code send data packets of no more
-than \fIn\fR bytes through the PPP network interface. 
+than \fIn\fR bytes through the PPP network interface.  (Note that for 
+IPv6 MTU must be at least 1280)
 .TP
 .B passive
 Enables the "passive" option in the LCP.  With this option, pppd will
@@ -177,6 +178,14 @@ will not accept a different value from the peer in the IPCP
 negotiation, unless the \fIipcp-accept-local\fR and/or
 \fIipcp-accept-remote\fR options are given, respectively.
 .TP
+.B ipv6 \fI<local_interface_identifier>\fR,\fI<remote_interface_identifier>
+Set the local and/or remote 64-bit interface identifier. Either one may be
+omitted. The identifier must be specified in standard ascii notation of
+IPv6 addresses (e.g. ::dead:beef). If the
+\fIipv6cp-use-ipaddr\fR
+option is given, the local identifier is the local IPv4 address (see above).
+Otherwise the identifier is randomized.
+.TP
 .B active-filter \fIfilter-expression
 Specifies a packet filter to be applied to data packets to determine
 which packets are to be regarded as link activity, and therefore reset
@@ -320,6 +329,22 @@ Provides an extra parameter to the ip-up and ip-down scripts.  If this
 option is given, the \fIstring\fR supplied is given as the 6th
 parameter to those scripts.
 .TP
+.B ipv6cp-max-configure \fIn
+Set the maximum number of IPv6CP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B ipv6cp-max-failure \fIn
+Set the maximum number of IPv6CP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B ipv6cp-max-terminate \fIn
+Set the maximum number of IPv6CP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B ipv6cp-restart \fIn
+Set the IPv6CP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
 .B ipx
 Enable the IPXCP and IPX protocols.  This option is presently only
 supported under Linux, and only if your kernel has been configured to
@@ -557,6 +582,11 @@ Disable IPCP negotiation and IP communication.  This option should
 only be required if the peer is buggy and gets confused by requests
 from pppd for IPCP negotiation.
 .TP
+.B noipv6
+Disable IPv6CP negotiation and IPv6 communication. This option should
+only be required if the peer is buggy and gets confused by requests
+from pppd for IPv6CP negotiation.
+.TP
 .B noipdefault
 Disables the default behaviour when no local IP address is specified,
 which is to determine (if possible) the local IP address from the
@@ -1257,6 +1287,18 @@ used for undoing the effects of the /etc/ppp/ip-up script.  It is
 invoked in the same manner and with the same parameters as the ip-up
 script.
 .TP
+.B /etc/ppp/ipv6-up
+Like /etc/ppp/ip-up, except that it is executed when the link is available 
+for sending and receiving IPv6 packets. It is executed with the parameters
+.IP
+\fIinterface-name tty-device speed local-link-local-address
+remote-link-local-address ipparam\fR
+.TP
+.B /etc/ppp/ipv6-down
+Similar to /etc/ppp/ip-down, but it is executed when IPv6 packets can no
+longer be transmitted on the link. It is executed with the same parameters 
+as the ipv6-up script.
+.TP
 .B /etc/ppp/ipx-up
 A program or script which is executed when the link is available for
 sending and receiving IPX packets (that is, IPXCP has come up).  It is
@@ -1354,6 +1396,11 @@ July 1994.
 Simpson, W.A.
 .I PPP in HDLC-like Framing.
 July 1994.
+.TP
+.B RFC2472
+Haskin, D.
+.I IP Version 6 over PPP
+December 1998.
 .SH NOTES
 The following signals have the specified effect when sent to pppd.
 .TP
index 2e7c5cabb5133072e6416f6a7a43d23a080909c7..8e4472be3db7f2f61aa3c4d7c3cbd20c919afc17 100644 (file)
@@ -16,7 +16,7 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- * $Id: pppd.h,v 1.44 1999/08/13 01:57:37 paulus Exp $
+ * $Id: pppd.h,v 1.45 1999/08/24 05:31:11 paulus Exp $
  */
 
 /*
 #define volatile
 #endif
 
+#if INET6
+#include "eui64.h"
+#endif
+
 /*
  * Limits.
  */
@@ -385,14 +389,22 @@ int  get_ppp_stats __P((int, struct pppd_stats *));
                                /* Return link statistics */
 int  sifvjcomp __P((int, int, int, int));
                                /* Configure VJ TCP header compression */
-int  sifup __P((int));         /* Configure i/f up (for IP) */
+int  sifup __P((int));         /* Configure i/f up for one protocol */
 int  sifnpmode __P((int u, int proto, enum NPmode mode));
                                /* Set mode for handling packets for proto */
-int  sifdown __P((int));       /* Configure i/f down (for IP) */
+int  sifdown __P((int));       /* Configure i/f down for one protocol */
 int  sifaddr __P((int, u_int32_t, u_int32_t, u_int32_t));
-                               /* Configure IP addresses for i/f */
+                               /* Configure IPv4 addresses for i/f */
 int  cifaddr __P((int, u_int32_t, u_int32_t));
                                /* Reset i/f IP addresses */
+#if INET6
+int  sif6up __P((int));                /* Configure i/f up (for IPv6) */
+int  sif6down __P((int));      /* Configure i/f down (for IPv6) */
+int  sif6addr __P((int, eui64_t, eui64_t));
+                               /* Configure IPv6 addresses for i/f */
+int  cif6addr __P((int, eui64_t, eui64_t));
+                               /* Remove an IPv6 address from i/f */
+#endif
 int  sifdefaultroute __P((int, u_int32_t, u_int32_t));
                                /* Create default route through i/f */
 int  cifdefaultroute __P((int, u_int32_t, u_int32_t));
@@ -545,6 +557,7 @@ extern struct option_info ptycommand_info;
 #define DEBUGFSM       1
 #define DEBUGLCP       1
 #define DEBUGIPCP      1
+#define DEBUGIPV6CP    1
 #define DEBUGUPAP      1
 #define DEBUGCHAP      1
 #endif
@@ -552,7 +565,7 @@ extern struct option_info ptycommand_info;
 #ifndef LOG_PPP                        /* we use LOG_LOCAL2 for syslog by default */
 #if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUGSYS) \
   || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \
-  || defined(DEBUGCHAP) || defined(DEBUG)
+  || defined(DEBUGCHAP) || defined(DEBUG) || defined(DEBUGIPV6CP)
 #define LOG_PPP LOG_LOCAL2
 #else
 #define LOG_PPP LOG_DAEMON
@@ -589,6 +602,12 @@ extern struct option_info ptycommand_info;
 #define IPCPDEBUG(x)
 #endif
 
+#ifdef DEBUGIPV6CP
+#define IPV6CPDEBUG(x)  if (debug) dbglog x
+#else
+#define IPV6CPDEBUG(x)
+#endif
+
 #ifdef DEBUGUPAP
 #define UPAPDEBUG(x)   if (debug) dbglog x
 #else
index c25758ed4a193294d918db76b0c07a8bfb1b1541..fedfccc02f9eb3a6c4ffb8a44f97b0021710ed31 100644 (file)
@@ -26,6 +26,7 @@
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <sys/utsname.h>
+#include <sys/sysmacros.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/locks.h>
 #endif
 
-#ifndef RTF_DEFAULT  /* Normally in <linux/route.h> from <net/route.h> */
-#define RTF_DEFAULT  0
+#ifdef INET6
+#ifndef _LINUX_IN6_H
+/*
+ *    This is in linux/include/net/ipv6.h.
+ */
+
+struct in6_ifreq {
+    struct in6_addr ifr6_addr;
+    __u32 ifr6_prefixlen;
+    unsigned int ifr6_ifindex;
+};
 #endif
 
+#define IN6_LLADDR_FROM_EUI64(sin6, eui64) do {                        \
+       memset(&sin6.s6_addr, 0, sizeof(struct in6_addr));      \
+       sin6.s6_addr16[0] = htons(0xfe80);                      \
+       eui64_copy(eui64, sin6.s6_addr32[2]);                   \
+       } while (0)
+
+#endif /* INET6 */
+
 /* We can get an EIO error on an ioctl if the modem has hung up */
 #define ok_error(num) ((num)==EIO)
 
@@ -102,6 +120,9 @@ static int ppp_fd = -1;             /* fd which is set to PPP discipline */
 static int sock_fd = -1;       /* socket for doing interface ioctls */
 static int slave_fd = -1;
 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) */
 
 static fd_set in_fds;          /* set of fds that wait_input waits for */
@@ -249,10 +270,14 @@ void sys_init(void)
 
     /* Get an internet socket for doing socket ioctls. */
     sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
-    if (sock_fd < 0) {
-       if ( ! ok_error ( errno ))
-           fatal("Couldn't create IP socket: %m(%d)", errno);
-    }
+    if (sock_fd < 0)
+       fatal("Couldn't create IP socket: %m(%d)", errno);
+
+#ifdef INET6
+    sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (sock6_fd < 0)
+       fatal("Couldn't create IPv6 socket: %m(%d)", errno);
+#endif
 
     FD_ZERO(&in_fds);
     max_in_fd = 0;
@@ -275,8 +300,10 @@ void sys_cleanup(void)
 /*
  * Take down the device
  */
-    if (if_is_up)
+    if (if_is_up) {
+       if_is_up = 0;
        sifdown(0);
+    }
 /*
  * Delete any routes through the device.
  */
@@ -1012,54 +1039,65 @@ int ccp_fatal_error (int unit)
     return x & SC_DC_FERROR;
 }
 
-/*
- * path_to_route - determine the path to the proc file system data
- */
-#define ROUTE_MAX_COLS 12
-FILE *route_fd = (FILE *) 0;
-static char route_buffer [512];
-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_procfs - find the path to the proc file system mount point
  */
+static char proc_path[MAXPATHLEN];
+static int proc_path_len;
 
-static int path_to_procfs (const char *tail)
+static char *path_to_procfs(const char *tail)
 {
     struct mntent *mntent;
     FILE *fp;
 
-    fp = fopen(MOUNTED, "r");
-    if (fp == NULL) {
-       /* Default the mount location of /proc */
-       strlcpy (route_buffer, "/proc", sizeof (route_buffer));
-       strlcat (route_buffer, tail, sizeof(route_buffer));
-       return 1;
-    }
+    if (proc_path_len == 0) {
+       fp = fopen(MOUNTED, "r");
+       if (fp == NULL) {
+           /* Default the mount location of /proc */
+           strlcpy (proc_path, "/proc", sizeof(proc_path));
+           proc_path_len = 5;
 
-    while ((mntent = getmntent(fp)) != NULL) {
-       if (strcmp(mntent->mnt_type, MNTTYPE_IGNORE) == 0)
-           continue;
-       if (strcmp(mntent->mnt_type, "proc") == 0)
-           break;
+       } else {
+           while ((mntent = getmntent(fp)) != NULL) {
+               if (strcmp(mntent->mnt_type, MNTTYPE_IGNORE) == 0)
+                   continue;
+               if (strcmp(mntent->mnt_type, "proc") == 0)
+                   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);
+           }
+       }
     }
-    fclose (fp);
-    if (mntent == 0)
+
+    if (proc_path_len < 0)
        return 0;
 
-    strlcpy(route_buffer, mntent->mnt_dir, sizeof (route_buffer));
-    strlcat (route_buffer, tail, sizeof(route_buffer));
-    return 1;
+    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
+ */
+#define ROUTE_MAX_COLS 12
+FILE *route_fd = (FILE *) 0;
+static char route_buffer[512];
+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
@@ -1067,11 +1105,12 @@ static int path_to_procfs (const char *tail)
 
 static char *path_to_route (void)
 {
-    if (!path_to_procfs("/net/route")) {
+    char *path;
+
+    path = path_to_procfs("/net/route");
+    if (path == 0)
        error("proc file system not mounted");
-       return 0;
-    }
-    return (route_buffer);
+    return path;
 }
 
 /********************************************************************
@@ -1268,7 +1307,7 @@ int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
 
     ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = gateway;
     
-    rt.rt_flags = RTF_UP | RTF_GATEWAY | RTF_DEFAULT;
+    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);
@@ -1301,7 +1340,7 @@ int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
 
     ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = gateway;
     
-    rt.rt_flags = RTF_UP | RTF_GATEWAY | RTF_DEFAULT;
+    rt.rt_flags = RTF_UP | RTF_GATEWAY;
     if (ioctl(sock_fd, SIOCDELRT, &rt) < 0 && errno != ESRCH) {
        if (still_ppp()) {
            if ( ! ok_error ( errno ))
@@ -1321,6 +1360,7 @@ int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
 int sifproxyarp (int unit, u_int32_t his_adr)
 {
     struct arpreq arpreq;
+    char *forw_path;
 
     if (has_proxy_arp == 0) {
        memset (&arpreq, '\0', sizeof(arpreq));
@@ -1346,6 +1386,15 @@ int sifproxyarp (int unit, u_int32_t his_adr)
        }
        proxy_arp_addr = his_adr;
        has_proxy_arp = 1;
+
+       forw_path = path_to_procfs("/sys/net/ipv4/ip_forward");
+       if (forw_path != 0) {
+           int fd = open(forw_path, O_WRONLY);
+           if (fd >= 0) {
+               write(fd, "1", 1);
+               close(fd);
+           }
+       }
     }
 
     return 1;
@@ -1848,7 +1897,7 @@ int sifvjcomp (int u, int vjcomp, int cidcomp, int maxcid)
  * sifup - Config the interface up and enable IP packets to pass.
  */
 
-int sifup (int u)
+int sifup(int u)
 {
     struct ifreq ifr;
 
@@ -1866,20 +1915,23 @@ int sifup (int u)
            error("ioctl(SIOCSIFFLAGS): %m(%d)", errno);
        return 0;
     }
-    if_is_up = 1;
+    if_is_up++;
+
     return 1;
 }
 
 /********************************************************************
  *
- * sifdown - Config the interface down and disable IP.
+ * sifdown - Disable the indicated protocol and config the interface
+ *          down if there are no remaining protocols.
  */
 
 int sifdown (int u)
 {
     struct ifreq ifr;
 
-    if_is_up = 0;
+    if (if_is_up && --if_is_up > 0)
+       return 1;
 
     memset (&ifr, '\0', sizeof (ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
@@ -1989,12 +2041,13 @@ int sifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr,
 
 int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr)
 {
-    struct rtentry rt;
+    struct ifreq ifr;
 
     if (kernel_version < KVERSION(2,1,16)) {
 /*
  *  Delete the route through the device
  */
+       struct rtentry rt;
        memset (&rt, '\0', sizeof (rt));
 
        SET_SA_FAMILY (rt.rt_dst,     AF_INET);
@@ -2016,8 +2069,102 @@ int cifaddr (int unit, u_int32_t our_adr, u_int32_t his_adr)
            return (0);
        }
     }
+
+    /* This way it is possible to have an IPX-only or IPv6-only interface */
+    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);
+           return 0;
+       }
+    }
+
+    return 1;
+}
+
+#ifdef INET6
+/********************************************************************
+ * 
+ * sif6addr - Config the interface with an IPv6 link-local address
+ */
+int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
+{
+    struct in6_ifreq ifr6;
+    struct ifreq ifr;
+    struct in6_rtmsg rt6;
+
+    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);
+       return 0;
+    }
+    
+    /* Local interface */
+    memset(&ifr6, 0, sizeof(ifr6));
+    IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
+    ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+    ifr6.ifr6_prefixlen = 10;
+
+    if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6) < 0) {
+       error("sif6addr: ioctl(SIOCSIFADDR): %m (%d)", errno);
+       return 0;
+    }
+    
+    /* Route to remote host */
+    memset(&rt6, 0, sizeof(rt6));
+    IN6_LLADDR_FROM_EUI64(rt6.rtmsg_dst, his_eui64);
+    rt6.rtmsg_flags = RTF_UP | RTF_HOST;
+    rt6.rtmsg_dst_len = 128;
+    rt6.rtmsg_ifindex = ifr.ifr_ifindex;
+    rt6.rtmsg_metric = 1;
+    
+    if (ioctl(sock6_fd, SIOCADDRT, &rt6) < 0) {
+       error("sif6addr: ioctl(SIOCADDRT): %m (%d)", errno);
+       return 0;
+    }
+
+    return 1;
+}
+
+
+/********************************************************************
+ *
+ * cif6addr - Remove IPv6 address from interface
+ */
+int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
+{
+    struct ifreq ifr;
+    struct in6_ifreq ifr6;
+
+    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);
+       return 0;
+    }
+    
+    memset(&ifr6, 0, sizeof(ifr6));
+    IN6_LLADDR_FROM_EUI64(ifr6.ifr6_addr, our_eui64);
+    ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+    ifr6.ifr6_prefixlen = 10;
+
+    if (ioctl(sock6_fd, SIOCDIFADDR, &ifr6) < 0) {
+       if (errno != EADDRNOTAVAIL) {
+           if (! ok_error (errno))
+               error("cif6addr: ioctl(SIOCDIFADDR): %m (%d)", errno);
+       }
+        else {
+           warn("cif6addr: ioctl(SIOCDIFADDR): No such address");
+       }
+        return (0);
+    }
     return 1;
 }
+#endif /* INET6 */
 
 /*
  * get_pty - get a pty master/slave pair and chown the slave side
@@ -2163,7 +2310,7 @@ void
 restore_loop(void)
 {
     if (new_style_driver) {
-       set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) & SC_LOOP_TRAFFIC);
+       set_flags(ppp_dev_fd, get_flags(ppp_dev_fd) | SC_LOOP_TRAFFIC);
        return;
     }
     if (ppp_fd != slave_fd) {
@@ -2318,10 +2465,13 @@ sys_check_options(void)
 /*
  * Disable the IPX protocol if the support is not present in the kernel.
  */
+    char *path;
+    int fd;
+
     if (ipxcp_protent.enabled_flag) {
        struct stat stat_buf;
-        if (!path_to_procfs("/net/ipx_interface")
-           || lstat (route_buffer, &stat_buf) < 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;
        }
@@ -2333,5 +2483,13 @@ sys_check_options(void)
                     driver_patch);
        return 0;
     }
+    if (demand) {
+       /* set ip_dynaddr if possible */
+       path = path_to_procfs("/sys/net/ipv4/ip_dynaddr");
+       if (path != 0 && (fd = open(path, O_WRONLY)) >= 0) {
+           write(fd, "1", 1);
+           close(fd);
+       }
+    }
     return 1;
 }