]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/ipv6cp.c
pppd.8: Document netmask option
[ppp.git] / pppd / ipv6cp.c
index caa2b265d7e7b20f3b3bb1a093d88d26fb1cdcda..a36b1d942be4d530215078aebba7699156ebc75a 100644 (file)
  * 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.
- *
- * $Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $ 
  */
 
-#define RCSID  "$Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $"
-
 /*
  * TODO: 
  *
  *   since SVR4 && (SNI || __USLC__) didn't work properly)
  */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
 #include <unistd.h>
 #include <netdb.h>
 #include <sys/param.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#include "pppd.h"
+#include "pppd-private.h"
+#include "options.h"
 #include "fsm.h"
+#include "eui64.h"
 #include "ipcp.h"
 #include "ipv6cp.h"
 #include "magic.h"
 #include "pathnames.h"
 
-static const char rcsid[] = RCSID;
 
 /* global vars */
 ipv6cp_options ipv6cp_wantoptions[NUM_PPP];     /* Options that we want to request */
@@ -178,13 +180,12 @@ ipv6cp_options ipv6cp_hisoptions[NUM_PPP];        /* Options that we ack'd */
 int no_ifaceid_neg = 0;
 
 /* local vars */
+static int default_route_set[NUM_PPP];         /* Have set up a default route */
 static int ipv6cp_is_up;
+static bool ipv6cp_noremote;
 
-/* Hook for a plugin to know when IPv6 protocol has come up */
-void (*ipv6_up_hook) __P((void)) = NULL;
-
-/* Hook for a plugin to know when IPv6 protocol has come down */
-void (*ipv6_down_hook) __P((void)) = NULL;
+ipv6_up_hook_fn *ipv6_up_hook = NULL;
+ipv6_down_hook_fn *ipv6_down_hook = NULL;
 
 /* Notifiers for when IPCPv6 goes up and down */
 struct notifier *ipv6_up_notifier = NULL;
@@ -193,16 +194,16 @@ struct notifier *ipv6_down_notifier = NULL;
 /*
  * Callbacks for fsm code.  (CI = Configuration Information)
  */
-static void ipv6cp_resetci __P((fsm *));       /* Reset our CI */
-static int  ipv6cp_cilen __P((fsm *));         /* Return length of our CI */
-static void ipv6cp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
-static int  ipv6cp_ackci __P((fsm *, u_char *, int));  /* Peer ack'd our CI */
-static int  ipv6cp_nakci __P((fsm *, u_char *, int, int));/* Peer nak'd our CI */
-static int  ipv6cp_rejci __P((fsm *, u_char *, int));  /* Peer rej'd our CI */
-static int  ipv6cp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
-static void ipv6cp_up __P((fsm *));            /* We're UP */
-static void ipv6cp_down __P((fsm *));          /* We're DOWN */
-static void ipv6cp_finished __P((fsm *));      /* Don't need lower layer */
+static void ipv6cp_resetci (fsm *);    /* Reset our CI */
+static int  ipv6cp_cilen (fsm *);              /* Return length of our CI */
+static void ipv6cp_addci (fsm *, u_char *, int *); /* Add our CI */
+static int  ipv6cp_ackci (fsm *, u_char *, int);       /* Peer ack'd our CI */
+static int  ipv6cp_nakci (fsm *, u_char *, int, int);/* Peer nak'd our CI */
+static int  ipv6cp_rejci (fsm *, u_char *, int);       /* Peer rej'd our CI */
+static int  ipv6cp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */
+static void ipv6cp_up (fsm *);         /* We're UP */
+static void ipv6cp_down (fsm *);               /* We're DOWN */
+static void ipv6cp_finished (fsm *);   /* Don't need lower layer */
 
 fsm ipv6cp_fsm[NUM_PPP];               /* IPV6CP fsm structure */
 
@@ -227,11 +228,11 @@ static fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */
 /*
  * Command-line options.
  */
-static int setifaceid __P((char **arg));
-static void printifaceid __P((option_t *,
-                             void (*)(void *, char *, ...), void *));
+static int setifaceid (char **arg);
+static void printifaceid (struct option *,
+                         void (*)(void *, char *, ...), void *);
 
-static option_t ipv6cp_option_list[] = {
+static struct option ipv6cp_option_list[] = {
     { "ipv6", o_special, (void *)setifaceid,
       "Set interface identifiers for IPV6",
       OPT_A2PRINTER, (void *)printifaceid },
@@ -243,16 +244,33 @@ static option_t ipv6cp_option_list[] = {
     { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
       "Disable IPv6 and IPv6CP", OPT_PRIOSUB | OPT_ALIAS },
 
-    { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local,
+    { "ipv6cp-accept-local", o_bool, &ipv6cp_wantoptions[0].accept_local,
       "Accept peer's interface identifier for us", 1 },
+    { "ipv6cp-accept-remote", o_bool, &ipv6cp_wantoptions[0].accept_remote,
+      "Accept peer's interface identifier for itself", 1 },
+
+    { "defaultroute6", o_bool, &ipv6cp_wantoptions[0].default_route,
+      "Add default IPv6 route", OPT_ENABLE|1, &ipv6cp_allowoptions[0].default_route },
+    { "nodefaultroute6", o_bool, &ipv6cp_allowoptions[0].default_route,
+      "disable defaultroute6 option", OPT_A2CLR,
+      &ipv6cp_wantoptions[0].default_route },
+    { "-defaultroute6", o_bool, &ipv6cp_allowoptions[0].default_route,
+      "disable defaultroute6 option", OPT_ALIAS | OPT_A2CLR,
+      &ipv6cp_wantoptions[0].default_route },
 
     { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
-      "Use (default) IPv4 address as interface identifier", 1 },
-
-#if defined(SOL2) || defined(__linux__)
+      "Use (default) IPv4 addresses for both local and remote interface identifiers", 1 },
     { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
-      "Use uniquely-available persistent value for link local address", 1 },
-#endif /* defined(SOL2) */
+      "Use uniquely-available persistent value for local interface identifier", 1 },
+    { "ipv6cp-use-remotenumber", o_bool, &ipv6cp_wantoptions[0].use_remotenumber,
+      "Use remotenumber value for remote interface identifier", 1 },
+
+#ifdef __linux__
+    { "ipv6cp-noremote", o_bool, &ipv6cp_noremote,
+      "Allow peer to have no interface identifier", 1 },
+#endif
+    { "ipv6cp-nosend", o_bool, &ipv6cp_wantoptions[0].neg_ifaceid,
+      "Don't send local interface identifier to peer", OPT_A2CLR },
 
     { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
       "Set timeout for IPv6CP", OPT_PRIO },
@@ -270,18 +288,18 @@ static option_t ipv6cp_option_list[] = {
 /*
  * Protocol entry points from main code.
  */
-static void ipv6cp_init __P((int));
-static void ipv6cp_open __P((int));
-static void ipv6cp_close __P((int, char *));
-static void ipv6cp_lowerup __P((int));
-static void ipv6cp_lowerdown __P((int));
-static void ipv6cp_input __P((int, u_char *, int));
-static void ipv6cp_protrej __P((int));
-static int  ipv6cp_printpkt __P((u_char *, int,
-                              void (*) __P((void *, char *, ...)), void *));
-static void ipv6_check_options __P((void));
-static int  ipv6_demand_conf __P((int));
-static int  ipv6_active_pkt __P((u_char *, int));
+static void ipv6cp_init (int);
+static void ipv6cp_open (int);
+static void ipv6cp_close (int, char *);
+static void ipv6cp_lowerup (int);
+static void ipv6cp_lowerdown (int);
+static void ipv6cp_input (int, u_char *, int);
+static void ipv6cp_protrej (int);
+static int  ipv6cp_printpkt (u_char *, int,
+                            void (*) (void *, char *, ...), void *);
+static void ipv6_check_options (void);
+static int  ipv6_demand_conf (int);
+static int  ipv6_active_pkt (u_char *, int);
 
 struct protent ipv6cp_protent = {
     PPP_IPV6CP,
@@ -294,7 +312,7 @@ struct protent ipv6cp_protent = {
     ipv6cp_close,
     ipv6cp_printpkt,
     NULL,
-    0,
+    1,
     "IPV6CP",
     "IPV6",
     ipv6cp_option_list,
@@ -303,9 +321,9 @@ struct protent ipv6cp_protent = {
     ipv6_active_pkt
 };
 
-static void ipv6cp_clear_addrs __P((int, eui64_t, eui64_t));
-static void ipv6cp_script __P((char *));
-static void ipv6cp_script_done __P((void *));
+static void ipv6cp_clear_addrs (int, eui64_t, eui64_t);
+static void ipv6cp_script (char *);
+static void ipv6cp_script_done (void *);
 
 /*
  * Lengths of configuration options.
@@ -331,8 +349,7 @@ static pid_t ipv6cp_script_pid;
  * setifaceid - set the interface identifiers manually
  */
 static int
-setifaceid(argv)
-    char **argv;
+setifaceid(char **argv)
 {
     char *comma, *arg, c;
     ipv6cp_options *wo = &ipv6cp_wantoptions[0];
@@ -354,7 +371,7 @@ setifaceid(argv)
        *comma = '\0';
 
        if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) {
-           option_error("Illegal interface identifier (local): %s", arg);
+           ppp_option_error("Illegal interface identifier (local): %s", arg);
            return 0;
        }
 
@@ -371,7 +388,7 @@ setifaceid(argv)
      */
     if (*comma != 0 && *++comma != '\0') {
        if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) {
-           option_error("Illegal interface identifier (remote): %s", comma);
+           ppp_option_error("Illegal interface identifier (remote): %s", comma);
            return 0;
        }
        if (option_priority >= prio_remote) {
@@ -389,10 +406,7 @@ setifaceid(argv)
 char *llv6_ntoa(eui64_t ifaceid);
 
 static void
-printifaceid(opt, printer, arg)
-    option_t *opt;
-    void (*printer) __P((void *, char *, ...));
-    void *arg;
+printifaceid(struct option *opt, void (*printer) (void *, char *, ...), void *arg)
 {
        ipv6cp_options *wo = &ipv6cp_wantoptions[0];
 
@@ -407,8 +421,7 @@ printifaceid(opt, printer, arg)
  * Make a string representation of a network address.
  */
 char *
-llv6_ntoa(ifaceid)
-    eui64_t ifaceid;
+llv6_ntoa(eui64_t ifaceid)
 {
     static char b[64];
 
@@ -421,8 +434,7 @@ llv6_ntoa(ifaceid)
  * ipv6cp_init - Initialize IPV6CP.
  */
 static void
-ipv6cp_init(unit)
-    int unit;
+ipv6cp_init(int unit)
 {
     fsm *f = &ipv6cp_fsm[unit];
     ipv6cp_options *wo = &ipv6cp_wantoptions[unit];
@@ -436,7 +448,8 @@ ipv6cp_init(unit)
     memset(wo, 0, sizeof(*wo));
     memset(ao, 0, sizeof(*ao));
 
-    wo->accept_local = 1;
+    wo->accept_local = 0;
+    wo->accept_remote = 0;
     wo->neg_ifaceid = 1;
     ao->neg_ifaceid = 1;
 
@@ -446,6 +459,10 @@ ipv6cp_init(unit)
     wo->vj_protocol = IPV6CP_COMP;
 #endif
 
+    /*
+     * XXX This controls whether the user may use the defaultroute option.
+     */
+    ao->default_route = 1;
 }
 
 
@@ -453,8 +470,7 @@ ipv6cp_init(unit)
  * ipv6cp_open - IPV6CP is allowed to come up.
  */
 static void
-ipv6cp_open(unit)
-    int unit;
+ipv6cp_open(int unit)
 {
     fsm_open(&ipv6cp_fsm[unit]);
 }
@@ -464,9 +480,7 @@ ipv6cp_open(unit)
  * ipv6cp_close - Take IPV6CP down.
  */
 static void
-ipv6cp_close(unit, reason)
-    int unit;
-    char *reason;
+ipv6cp_close(int unit, char *reason)
 {
     fsm_close(&ipv6cp_fsm[unit], reason);
 }
@@ -476,8 +490,7 @@ ipv6cp_close(unit, reason)
  * ipv6cp_lowerup - The lower layer is up.
  */
 static void
-ipv6cp_lowerup(unit)
-    int unit;
+ipv6cp_lowerup(int unit)
 {
     fsm_lowerup(&ipv6cp_fsm[unit]);
 }
@@ -487,8 +500,7 @@ ipv6cp_lowerup(unit)
  * ipv6cp_lowerdown - The lower layer is down.
  */
 static void
-ipv6cp_lowerdown(unit)
-    int unit;
+ipv6cp_lowerdown(int unit)
 {
     fsm_lowerdown(&ipv6cp_fsm[unit]);
 }
@@ -498,10 +510,7 @@ ipv6cp_lowerdown(unit)
  * ipv6cp_input - Input IPV6CP packet.
  */
 static void
-ipv6cp_input(unit, p, len)
-    int unit;
-    u_char *p;
-    int len;
+ipv6cp_input(int unit, u_char *p, int len)
 {
     fsm_input(&ipv6cp_fsm[unit], p, len);
 }
@@ -513,8 +522,7 @@ ipv6cp_input(unit, p, len)
  * Pretend the lower layer went down, so we shut up.
  */
 static void
-ipv6cp_protrej(unit)
-    int unit;
+ipv6cp_protrej(int unit)
 {
     fsm_lowerdown(&ipv6cp_fsm[unit]);
 }
@@ -524,8 +532,7 @@ ipv6cp_protrej(unit)
  * ipv6cp_resetci - Reset our CI.
  */
 static void
-ipv6cp_resetci(f)
-    fsm *f;
+ipv6cp_resetci(fsm *f)
 {
     ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
@@ -533,8 +540,12 @@ ipv6cp_resetci(f)
     wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid;
     
     if (!wo->opt_local) {
-       eui64_magic_nz(wo->ourid);
+       wo->accept_local = 1;
+       if (!demand)
+           eui64_magic_nz(wo->ourid);
     }
+    if (!wo->opt_remote)
+       wo->accept_remote = 1;
     
     *go = *wo;
     eui64_zero(go->hisid);     /* last proposed interface identifier */
@@ -545,8 +556,7 @@ ipv6cp_resetci(f)
  * ipv6cp_cilen - Return length of our CI.
  */
 static int
-ipv6cp_cilen(f)
-    fsm *f;
+ipv6cp_cilen(fsm *f)
 {
     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
 
@@ -562,10 +572,7 @@ ipv6cp_cilen(f)
  * ipv6cp_addci - Add our desired CIs to a packet.
  */
 static void
-ipv6cp_addci(f, ucp, lenp)
-    fsm *f;
-    u_char *ucp;
-    int *lenp;
+ipv6cp_addci(fsm *f, u_char *ucp, int *lenp)
 {
     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
     int len = *lenp;
@@ -610,10 +617,7 @@ ipv6cp_addci(f, ucp, lenp)
  *     1 - Ack was good.
  */
 static int
-ipv6cp_ackci(f, p, len)
-    fsm *f;
-    u_char *p;
-    int len;
+ipv6cp_ackci(fsm *f, u_char *p, int len)
 {
     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
     u_short cilen, citype, cishort;
@@ -681,12 +685,9 @@ bad:
  *     1 - Nak was good.
  */
 static int
-ipv6cp_nakci(f, p, len, treat_as_reject)
-    fsm *f;
-    u_char *p;
-    int len;
-    int treat_as_reject;
+ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject)
 {
+    ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
     u_char citype, cilen, *next;
     u_short cishort;
@@ -733,7 +734,9 @@ ipv6cp_nakci(f, p, len, treat_as_reject)
     NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
                 if (treat_as_reject) {
                     try.neg_ifaceid = 0;
-                } else if (go->accept_local) {
+                } else if (go->accept_local && !eui64_iszero(ifaceid) && !eui64_equals(ifaceid, go->hisid)) {
+                    try.ourid = ifaceid;
+                } else if (eui64_iszero(ifaceid) && !go->opt_local && wo->neg_ifaceid) {
                     while (eui64_iszero(ifaceid) || 
                            eui64_equals(ifaceid, go->hisid)) /* bad luck */
                         eui64_magic(ifaceid);
@@ -785,11 +788,15 @@ ipv6cp_nakci(f, p, len, treat_as_reject)
                goto bad;
            try.neg_ifaceid = 1;
            eui64_get(ifaceid, p);
-           if (go->accept_local) {
+           if (go->accept_local && !eui64_iszero(ifaceid) && !eui64_equals(ifaceid, go->hisid)) {
+               try.ourid = ifaceid;
+           } else if (eui64_iszero(ifaceid) && !go->opt_local && wo->neg_ifaceid) {
                while (eui64_iszero(ifaceid) || 
                       eui64_equals(ifaceid, go->hisid)) /* bad luck */
                    eui64_magic(ifaceid);
                try.ourid = ifaceid;
+           } else {
+               try.neg_ifaceid = 0;
            }
            no.neg_ifaceid = 1;
            break;
@@ -819,10 +826,7 @@ bad:
  * ipv6cp_rejci - Reject some of our CIs.
  */
 static int
-ipv6cp_rejci(f, p, len)
-    fsm *f;
-    u_char *p;
-    int len;
+ipv6cp_rejci(fsm *f, u_char *p, int len)
 {
     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
     u_char cilen;
@@ -894,11 +898,7 @@ bad:
  * CONFNAK; returns CONFREJ if it can't return CONFACK.
  */
 static int
-ipv6cp_reqci(f, inp, len, reject_if_disagree)
-    fsm *f;
-    u_char *inp;               /* Requested CIs */
-    int *len;                  /* Length of requested CIs */
-    int reject_if_disagree;
+ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree)
 {
     ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
     ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
@@ -962,9 +962,8 @@ ipv6cp_reqci(f, inp, len, reject_if_disagree)
                orc = CONFREJ;          /* Reject CI */
                break;
            }
-           if (!eui64_iszero(wo->hisid) && 
-               !eui64_equals(ifaceid, wo->hisid) && 
-               eui64_iszero(go->hisid)) {
+           if (!eui64_iszero(wo->hisid) && !wo->accept_remote &&
+               !eui64_equals(ifaceid, wo->hisid)) {
                    
                orc = CONFNAK;
                ifaceid = wo->hisid;
@@ -976,9 +975,17 @@ ipv6cp_reqci(f, inp, len, reject_if_disagree)
                orc = CONFNAK;
                if (eui64_iszero(go->hisid))    /* first time, try option */
                    ifaceid = wo->hisid;
-               while (eui64_iszero(ifaceid) || 
-                      eui64_equals(ifaceid, go->ourid)) /* bad luck */
-                   eui64_magic(ifaceid);
+               if (eui64_equals(ifaceid, go->ourid)) /* bad luck */
+                   eui64_zero(ifaceid);
+               if (eui64_iszero(ifaceid)) {
+                   if (wo->opt_remote)
+                       ifaceid = wo->hisid;
+                   else {
+                       while (eui64_iszero(ifaceid) ||
+                              eui64_equals(ifaceid, go->ourid)) /* bad luck */
+                           eui64_magic(ifaceid);
+                   }
+               }
                go->hisid = ifaceid;
                DECPTR(sizeof(ifaceid), p);
                eui64_put(ifaceid, p);
@@ -1076,24 +1083,64 @@ endswitch:
 }
 
 
+/*
+ * eui48_to_eui64 - Convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
+ */
+static void
+eui48_to_eui64(eui64_t *p_eui64, const u_char addr[6])
+{
+    p_eui64->e8[0] = addr[0] | 0x02;
+    p_eui64->e8[1] = addr[1];
+    p_eui64->e8[2] = addr[2];
+    p_eui64->e8[3] = 0xFF;
+    p_eui64->e8[4] = 0xFE;
+    p_eui64->e8[5] = addr[3];
+    p_eui64->e8[6] = addr[4];
+    p_eui64->e8[7] = addr[5];
+}
+
+
+/*
+ * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI
+ *
+ * walks the list of valid ethernet interfaces, starting with devnam
+ * (for PPPoE it is ethernet interface), and convert the first
+ * found 48-bit MAC address into EUI 64. caller also assumes that
+ * the system has a properly configured Ethernet interface for this
+ * function to return non-zero.
+ */
+static int
+ether_to_eui64(eui64_t *p_eui64)
+{
+    u_char addr[6];
+
+    if (get_if_hwaddr(addr, devnam) < 0 && get_first_ether_hwaddr(addr) < 0) {
+        error("ipv6cp: no persistent id can be found");
+        return 0;
+    }
+
+    eui48_to_eui64(p_eui64, addr);
+    return 1;
+}
+
+
 /*
  * ipv6_check_options - check that any IP-related options are OK,
  * and assign appropriate defaults.
  */
 static void
-ipv6_check_options()
+ipv6_check_options(void)
 {
     ipv6cp_options *wo = &ipv6cp_wantoptions[0];
 
     if (!ipv6cp_protent.enabled_flag)
        return;
 
-#if defined(SOL2) || defined(__linux__)
     /*
      * Persistent link-local id is only used when user has not explicitly
      * configure/hard-code the id
      */
-    if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
+    if ((wo->use_persistent) && (!wo->opt_local)) {
 
        /* 
         * On systems where there are no Ethernet interfaces used, there
@@ -1108,7 +1155,46 @@ ipv6_check_options()
            wo->opt_local = 1;
        }
     }
-#endif
+
+    if (!wo->opt_remote && wo->use_remotenumber && *remote_number) {
+       /* remote number can be either MAC address, IPv4 address, IPv6 address or telephone number */
+       struct in_addr addr;
+       struct in6_addr addr6;
+       unsigned long long tel;
+       unsigned char mac[6];
+       const char *str;
+       char *endptr;
+       if (sscanf(remote_number, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+                  &mac[0], &mac[1], &mac[2],
+                  &mac[3], &mac[4], &mac[5]) == 6) {
+           eui48_to_eui64(&wo->hisid, mac);
+       } else if (inet_pton(AF_INET, remote_number, &addr) == 1) {
+           eui64_setlo32(wo->hisid, ntohl(addr.s_addr));
+       } else if (inet_pton(AF_INET6, remote_number, &addr6) == 1) {
+           /* use low 64 bits of IPv6 address for interface identifier */
+           wo->hisid.e8[0] = addr6.s6_addr[8];
+           wo->hisid.e8[1] = addr6.s6_addr[9];
+           wo->hisid.e8[2] = addr6.s6_addr[10];
+           wo->hisid.e8[3] = addr6.s6_addr[11];
+           wo->hisid.e8[4] = addr6.s6_addr[12];
+           wo->hisid.e8[5] = addr6.s6_addr[13];
+           wo->hisid.e8[6] = addr6.s6_addr[14];
+           wo->hisid.e8[7] = addr6.s6_addr[15];
+       } else {
+           str = remote_number;
+           /* telephone number may start with leading '+' sign, so skip it */
+           if (str[0] == '+')
+               str++;
+           errno = 0;
+           tel = strtoull(str, &endptr, 10);
+           if (!errno && *str && !*endptr && tel) {
+               wo->hisid.e32[0] = htonl(tel >> 32);
+               wo->hisid.e32[1] = htonl(tel & 0xFFFFFFFF);
+           }
+       }
+       if (!eui64_iszero(wo->hisid))
+           wo->opt_remote = 1;
+    }
 
     if (!wo->opt_local) {      /* init interface identifier */
        if (wo->use_ip && eui64_iszero(wo->ourid)) {
@@ -1128,11 +1214,6 @@ ipv6_check_options()
                wo->opt_remote = 1;
        }
     }
-
-    if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
-       option_error("local/remote LL address required for demand-dialling\n");
-       exit(EXIT_OPTION_ERROR);
-    }
 }
 
 
@@ -1141,20 +1222,21 @@ ipv6_check_options()
  * IPV6CP were up, for use with dial-on-demand.
  */
 static int
-ipv6_demand_conf(u)
-    int u;
+ipv6_demand_conf(int u)
 {
     ipv6cp_options *wo = &ipv6cp_wantoptions[u];
 
-#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
-#if defined(SOL2)
+    if (eui64_iszero(wo->hisid) && !ipv6cp_noremote) {
+       /* make up an arbitrary identifier for the peer */
+       eui64_magic_nz(wo->hisid);
+    }
+    if (eui64_iszero(wo->ourid)) {
+       /* make up an arbitrary identifier for us */
+       eui64_magic_nz(wo->ourid);
+    }
+
     if (!sif6up(u))
        return 0;
-#else
-    if (!sifup(u))
-       return 0;
-#endif /* defined(SOL2) */
-#endif    
     if (!sif6addr(u, wo->ourid, wo->hisid))
        return 0;
 #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
@@ -1163,10 +1245,13 @@ ipv6_demand_conf(u)
 #endif
     if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
        return 0;
+    if (wo->default_route)
+       if (sif6defaultroute(u, wo->ourid, wo->hisid))
+           default_route_set[u] = 1;
 
-    notice("ipv6_demand_conf");
     notice("local  LL address %s", llv6_ntoa(wo->ourid));
-    notice("remote LL address %s", llv6_ntoa(wo->hisid));
+    if (!eui64_iszero(wo->hisid))
+       notice("remote LL address %s", llv6_ntoa(wo->hisid));
 
     return 1;
 }
@@ -1178,8 +1263,7 @@ ipv6_demand_conf(u)
  * Configure the IPv6 network interface appropriately and bring it up.
  */
 static void
-ipv6cp_up(f)
-    fsm *f;
+ipv6cp_up(fsm *f)
 {
     ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
     ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
@@ -1190,11 +1274,17 @@ ipv6cp_up(f)
     /*
      * We must have a non-zero LL address for both ends of the link.
      */
+
+    if (!eui64_iszero(wo->hisid) && !wo->accept_remote && (!ho->neg_ifaceid || !eui64_equals(ho->hisid, wo->hisid))) {
+       error("Peer refused to agree to his interface identifier");
+       ipv6cp_close(f->unit, "Refused his interface identifier");
+       return;
+    }
     if (!ho->neg_ifaceid)
        ho->hisid = wo->hisid;
 
     if(!no_ifaceid_neg) {
-       if (eui64_iszero(ho->hisid)) {
+       if (eui64_iszero(ho->hisid) && !ipv6cp_noremote) {
            error("Could not determine remote LL address");
            ipv6cp_close(f->unit, "Could not determine remote LL address");
            return;
@@ -1210,8 +1300,9 @@ ipv6cp_up(f)
            return;
        }
     }
-    script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
-    script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
+    ppp_script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
+    if (!eui64_iszero(ho->hisid))
+        ppp_script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
 
 #ifdef IPV6CP_COMP
     /* set tcp compression */
@@ -1232,7 +1323,7 @@ ipv6cp_up(f)
            if (! eui64_equals(ho->hisid, wo->hisid))
                warn("Remote LL address changed to %s", 
                     llv6_ntoa(ho->hisid));
-           ipv6cp_clear_addrs(f->unit, go->ourid, ho->hisid);
+           ipv6cp_clear_addrs(f->unit, wo->ourid, wo->hisid);
 
            /* Set the interface to the new addresses */
            if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
@@ -1242,52 +1333,39 @@ ipv6cp_up(f)
                return;
            }
 
+           /* assign a default route through the interface if required */
+           if (ipv6cp_wantoptions[f->unit].default_route)
+               if (sif6defaultroute(f->unit, go->ourid, ho->hisid))
+                   default_route_set[f->unit] = 1;
        }
        demand_rexmit(PPP_IPV6);
        sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
 
     } else {
-       /*
-        * Set LL addresses
-        */
-#if !defined(__linux__) && !defined(SOL2) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
-       if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
-           if (debug)
-               warn("sif6addr failed");
-           ipv6cp_close(f->unit, "Interface configuration failed");
-           return;
-       }
-#endif
-
        /* bring the interface up for IPv6 */
-#if defined(SOL2)
        if (!sif6up(f->unit)) {
            if (debug)
-               warn("sifup failed (IPV6)");
-           ipv6cp_close(f->unit, "Interface configuration failed");
-           return;
-       }
-#else
-       if (!sifup(f->unit)) {
-           if (debug)
-               warn("sifup failed (IPV6)");
+               warn("sif6up failed (IPV6)");
            ipv6cp_close(f->unit, "Interface configuration failed");
            return;
        }
-#endif /* defined(SOL2) */
 
-#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
        if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
            if (debug)
                warn("sif6addr failed");
            ipv6cp_close(f->unit, "Interface configuration failed");
            return;
        }
-#endif
        sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
 
+       /* assign a default route through the interface if required */
+       if (ipv6cp_wantoptions[f->unit].default_route)
+           if (sif6defaultroute(f->unit, go->ourid, ho->hisid))
+               default_route_set[f->unit] = 1;
+
        notice("local  LL address %s", llv6_ntoa(go->ourid));
-       notice("remote LL address %s", llv6_ntoa(ho->hisid));
+       if (!eui64_iszero(ho->hisid))
+           notice("remote LL address %s", llv6_ntoa(ho->hisid));
     }
 
     np_up(f->unit, PPP_IPV6);
@@ -1303,7 +1381,7 @@ ipv6cp_up(f)
      */
     if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
        ipv6cp_script_state = s_up;
-       ipv6cp_script(_PATH_IPV6UP);
+       ipv6cp_script(path_ipv6up);
     }
 }
 
@@ -1315,11 +1393,10 @@ ipv6cp_up(f)
  * and delete routes through it.
  */
 static void
-ipv6cp_down(f)
-    fsm *f;
+ipv6cp_down(fsm *f)
 {
     IPV6CPDEBUG(("ipv6cp: down"));
-    update_link_stats(f->unit);
+    ppp_get_link_stats(NULL);
     notify(ipv6_down_notifier, 0);
     if (ipv6_down_hook)
        ipv6_down_hook();
@@ -1340,16 +1417,14 @@ ipv6cp_down(f)
     } else {
        sifnpmode(f->unit, PPP_IPV6, NPMODE_DROP);
 #if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
-#if defined(SOL2)
        sif6down(f->unit);
-#else
-       sifdown(f->unit);
-#endif /* defined(SOL2) */
 #endif
        ipv6cp_clear_addrs(f->unit, 
                           ipv6cp_gotoptions[f->unit].ourid,
                           ipv6cp_hisoptions[f->unit].hisid);
-#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
+#if defined(__linux__)
+       sif6down(f->unit);
+#elif defined(SVR4) && (defined(SNI) || defined(__USLC))
        sifdown(f->unit);
 #endif
     }
@@ -1357,7 +1432,7 @@ ipv6cp_down(f)
     /* Execute the ipv6-down script */
     if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
        ipv6cp_script_state = s_down;
-       ipv6cp_script(_PATH_IPV6DOWN);
+       ipv6cp_script(path_ipv6down);
     }
 }
 
@@ -1367,10 +1442,7 @@ ipv6cp_down(f)
  * proxy neighbour discovery entries, etc.
  */
 static void
-ipv6cp_clear_addrs(unit, ourid, hisid)
-    int unit;
-    eui64_t ourid;
-    eui64_t hisid;
+ipv6cp_clear_addrs(int unit, eui64_t ourid, eui64_t hisid)
 {
     cif6addr(unit, ourid, hisid);
 }
@@ -1380,8 +1452,7 @@ ipv6cp_clear_addrs(unit, ourid, hisid)
  * ipv6cp_finished - possibly shut down the lower layers.
  */
 static void
-ipv6cp_finished(f)
-    fsm *f;
+ipv6cp_finished(fsm *f)
 {
     np_finished(f->unit, PPP_IPV6);
 }
@@ -1392,21 +1463,20 @@ ipv6cp_finished(f)
  * has finished.
  */
 static void
-ipv6cp_script_done(arg)
-    void *arg;
+ipv6cp_script_done(void *arg)
 {
     ipv6cp_script_pid = 0;
     switch (ipv6cp_script_state) {
     case s_up:
        if (ipv6cp_fsm[0].state != OPENED) {
            ipv6cp_script_state = s_down;
-           ipv6cp_script(_PATH_IPV6DOWN);
+           ipv6cp_script(path_ipv6down);
        }
        break;
     case s_down:
        if (ipv6cp_fsm[0].state == OPENED) {
            ipv6cp_script_state = s_up;
-           ipv6cp_script(_PATH_IPV6UP);
+           ipv6cp_script(path_ipv6up);
        }
        break;
     }
@@ -1418,10 +1488,9 @@ ipv6cp_script_done(arg)
  * interface-name tty-name speed local-LL remote-LL.
  */
 static void
-ipv6cp_script(script)
-    char *script;
+ipv6cp_script(char *script)
 {
-    char strspeed[32], strlocal[32], strremote[32];
+    char strspeed[32], strlocal[64], strremote[64];
     char *argv[8];
 
     sprintf(strspeed, "%d", baud_rate);
@@ -1450,11 +1519,8 @@ static char *ipv6cp_codenames[] = {
 };
 
 static int
-ipv6cp_printpkt(p, plen, printer, arg)
-    u_char *p;
-    int plen;
-    void (*printer) __P((void *, char *, ...));
-    void *arg;
+ipv6cp_printpkt(u_char *p, int plen,
+               void (*printer) (void *, char *, ...), void *arg)
 {
     int code, id, len, olen;
     u_char *pstart, *optend;
@@ -1557,9 +1623,7 @@ ipv6cp_printpkt(p, plen, printer, arg)
 #define get_tcpflags(x)        (((unsigned char *)(x))[13])
 
 static int
-ipv6_active_pkt(pkt, len)
-    u_char *pkt;
-    int len;
+ipv6_active_pkt(u_char *pkt, int len)
 {
     u_char *tcp;