old DLPI doesn't have dl_sap_length
[ppp.git] / pppd / ipcp.c
index 08befff160c13c809de89ff479719d7d550ce486..2566fc82e0ddc1663bbc300a15c94bae16969399 100644 (file)
  */
 
 #ifndef lint
-static char rcsid[] = "$Id: ipcp.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+static char rcsid[] = "$Id: ipcp.c,v 1.21 1995/08/17 11:57:12 paulus Exp $";
 #endif
 
 /*
  * TODO:
- * Fix IP address negotiation (wantoptions or hisoptions).
- * Don't set zero IP addresses.
  */
 
 #include <stdio.h>
+#include <string.h>
 #include <syslog.h>
-#include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
-#include <sys/time.h>
-
-#include <net/if.h>
-#include <net/if_ppp.h>
-#include <net/route.h>
 #include <netinet/in.h>
 
-#include <string.h>
-
 #include "pppd.h"
-#include "ppp.h"
 #include "fsm.h"
 #include "ipcp.h"
-
+#include "pathnames.h"
 
 /* global vars */
-ipcp_options ipcp_wantoptions[NPPP];   /* Options that we want to request */
-ipcp_options ipcp_gotoptions[NPPP];    /* Options that peer ack'd */
-ipcp_options ipcp_allowoptions[NPPP];  /* Options we allow peer to request */
-ipcp_options ipcp_hisoptions[NPPP];    /* Options that we ack'd */
+ipcp_options ipcp_wantoptions[NUM_PPP];        /* Options that we want to request */
+ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipcp_options ipcp_allowoptions[NUM_PPP];       /* Options we allow peer to request */
+ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
 
 /* local vars */
-static int cis_received[NPPP];         /* # Conf-Reqs received */
+static int cis_received[NUM_PPP];              /* # Conf-Reqs received */
 
 /*
  * Callbacks for fsm code.  (CI = Configuration Information)
  */
-static void ipcp_resetci __ARGS((fsm *));      /* Reset our CI */
-static int  ipcp_cilen __ARGS((fsm *));                /* Return length of our CI */
-static void ipcp_addci __ARGS((fsm *, u_char *, int *)); /* Add our CI */
-static int  ipcp_ackci __ARGS((fsm *, u_char *, int)); /* Peer ack'd our CI */
-static int  ipcp_nakci __ARGS((fsm *, u_char *, int)); /* Peer nak'd our CI */
-static int  ipcp_rejci __ARGS((fsm *, u_char *, int)); /* Peer rej'd our CI */
-static int  ipcp_reqci __ARGS((fsm *, u_char *, int *, int)); /* Rcv CI */
-static void ipcp_up __ARGS((fsm *));           /* We're UP */
-static void ipcp_down __ARGS((fsm *));         /* We're DOWN */
-
-
-fsm ipcp_fsm[NPPP];            /* IPCP fsm structure */
+static void ipcp_resetci __P((fsm *)); /* Reset our CI */
+static int  ipcp_cilen __P((fsm *));           /* Return length of our CI */
+static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int  ipcp_ackci __P((fsm *, u_char *, int));    /* Peer ack'd our CI */
+static int  ipcp_nakci __P((fsm *, u_char *, int));    /* Peer nak'd our CI */
+static int  ipcp_rejci __P((fsm *, u_char *, int));    /* Peer rej'd our CI */
+static int  ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipcp_up __P((fsm *));              /* We're UP */
+static void ipcp_down __P((fsm *));            /* We're DOWN */
+static void ipcp_script __P((fsm *, char *)); /* Run an up/down script */
+
+fsm ipcp_fsm[NUM_PPP];         /* IPCP fsm structure */
 
 static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
     ipcp_resetci,              /* Reset our Configuration Information */
@@ -109,7 +99,7 @@ static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
  */
 char *
 ip_ntoa(ipaddr)
-u_long ipaddr;
+u_int32_t ipaddr;
 {
     static char b[64];
 
@@ -136,7 +126,7 @@ ipcp_init(unit)
     ipcp_options *ao = &ipcp_allowoptions[unit];
 
     f->unit = unit;
-    f->protocol = IPCP;
+    f->protocol = PPP_IPCP;
     f->callbacks = &ipcp_callbacks;
     fsm_init(&ipcp_fsm[unit]);
 
@@ -159,6 +149,13 @@ ipcp_init(unit)
     ao->neg_vj = 1;
     ao->maxslotindex = MAX_STATES - 1;
     ao->cflag = 1;
+
+    /*
+     * XXX These control whether the user may use the proxyarp
+     * and defaultroute options.
+     */
+    ao->proxy_arp = 1;
+    ao->default_route = 1;
 }
 
 
@@ -242,6 +239,10 @@ ipcp_resetci(f)
     ipcp_options *wo = &ipcp_wantoptions[f->unit];
 
     wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+    if (wo->ouraddr == 0)
+       wo->accept_local = 1;
+    if (wo->hisaddr == 0)
+       wo->accept_remote = 1;
     ipcp_gotoptions[f->unit] = *wo;
     cis_received[f->unit] = 0;
 }
@@ -298,7 +299,7 @@ ipcp_addci(f, ucp, lenp)
     if (neg) { \
        int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
        if (len >= addrlen) { \
-           u_long l; \
+           u_int32_t l; \
            PUTCHAR(opt, ucp); \
            PUTCHAR(addrlen, ucp); \
            l = ntohl(val1); \
@@ -361,7 +362,7 @@ ipcp_ackci(f, p, len)
 {
     ipcp_options *go = &ipcp_gotoptions[f->unit];
     u_short cilen, citype, cishort;
-    u_long cilong;
+    u_int32_t cilong;
     u_char cimaxslotindex, cicflag;
 
     /*
@@ -396,7 +397,7 @@ ipcp_ackci(f, p, len)
 #define ACKCIADDR(opt, neg, old, val1, val2) \
     if (neg) { \
        int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
-       u_long l; \
+       u_int32_t l; \
        if ((len -= addrlen) < 0) \
            goto bad; \
        GETCHAR(citype, p); \
@@ -453,7 +454,7 @@ ipcp_nakci(f, p, len)
     u_char cimaxslotindex, cicflag;
     u_char citype, cilen, *next;
     u_short cishort;
-    u_long ciaddr1, ciaddr2, l;
+    u_int32_t ciaddr1, ciaddr2, l;
     ipcp_options no;           /* options we've seen Naks for */
     ipcp_options try;          /* options to request next time */
 
@@ -492,26 +493,22 @@ ipcp_nakci(f, p, len)
        len -= cilen; \
        INCPTR(2, p); \
        GETSHORT(cishort, p); \
-       if (cilen == CILEN_VJ) { \
-           GETCHAR(cimaxslotindex, p); \
-            GETCHAR(cicflag, p); \
-        } \
        no.neg = 1; \
         code \
     }
 
     /*
-     * Accept the peer's idea of our address if we don't know it.
-     * Accept the peer's idea of his address if he knows it.
+     * Accept the peer's idea of {our,his} address, if different
+     * from our idea, only if the accept_{local,remote} flag is set.
      */
-    NAKCIADDR(CI_ADDR, neg_addr, go->old_addrs,
-             if (!go->ouraddr && ciaddr1) {    /* Do we know our address? */
-                 go->ouraddr = ciaddr1;
+    NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
+             if (go->accept_local && ciaddr1) { /* Do we know our address? */
+                 try.ouraddr = ciaddr1;
                  IPCPDEBUG((LOG_INFO, "local IP address %s",
                             ip_ntoa(ciaddr1)));
              }
-             if (ciaddr2) {                    /* Does he know his? */
-                 go->hisaddr = ciaddr2;
+             if (go->accept_remote && ciaddr2) { /* Does he know his? */
+                 try.hisaddr = ciaddr2;
                  IPCPDEBUG((LOG_INFO, "remote IP address %s",
                             ip_ntoa(ciaddr2)));
              }
@@ -525,6 +522,8 @@ ipcp_nakci(f, p, len)
      */
     NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
            if (cilen == CILEN_VJ) {
+               GETCHAR(cimaxslotindex, p);
+               GETCHAR(cicflag, p);
                if (cishort == IPCP_VJ_COMP) {
                    try.old_vj = 0;
                    if (cimaxslotindex < go->maxslotindex)
@@ -572,27 +571,26 @@ ipcp_nakci(f, p, len)
            try.old_addrs = 1;
            GETLONG(l, p);
            ciaddr1 = htonl(l);
-           if (ciaddr1)
+           if (ciaddr1 && go->accept_local)
                try.ouraddr = ciaddr1;
            GETLONG(l, p);
            ciaddr2 = htonl(l);
-           if (ciaddr2)
+           if (ciaddr2 && go->accept_remote)
                try.hisaddr = ciaddr2;
            no.old_addrs = 1;
            break;
        case CI_ADDR:
            if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR)
                goto bad;
-           try.neg_addr = 1;
            try.old_addrs = 0;
            GETLONG(l, p);
            ciaddr1 = htonl(l);
-           if (ciaddr1)
+           if (ciaddr1 && go->accept_local)
                try.ouraddr = ciaddr1;
+           if (try.ouraddr != 0)
+               try.neg_addr = 1;
            no.neg_addr = 1;
            break;
-       default:
-           goto bad;
        }
        p = next;
     }
@@ -627,7 +625,7 @@ ipcp_rejci(f, p, len)
     ipcp_options *go = &ipcp_gotoptions[f->unit];
     u_char cimaxslotindex, ciflag, cilen;
     u_short cishort;
-    u_long cilong;
+    u_int32_t cilong;
     ipcp_options try;          /* options to request next time */
 
     try = *go;
@@ -641,7 +639,7 @@ ipcp_rejci(f, p, len)
        len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
        p[1] == cilen && \
        p[0] == opt) { \
-       u_long l; \
+       u_int32_t l; \
        len -= cilen; \
        INCPTR(2, p); \
        GETLONG(l, p); \
@@ -726,7 +724,7 @@ ipcp_reqci(f, inp, len, reject_if_disagree)
     u_char *cip, *next;                /* Pointer to current and next CIs */
     u_short cilen, citype;     /* Parsed len, type */
     u_short cishort;           /* Parsed short value */
-    u_long tl, ciaddr1, ciaddr2;/* Parsed address values */
+    u_int32_t tl, ciaddr1, ciaddr2;/* Parsed address values */
     int rc = CONFACK;          /* Final packet return code */
     int orc;                   /* Individual option return code */
     u_char *p;                 /* Pointer to next char to parse */
@@ -778,13 +776,21 @@ ipcp_reqci(f, inp, len, reject_if_disagree)
            GETLONG(tl, p);             /* Parse source address (his) */
            ciaddr1 = htonl(tl);
            IPCPDEBUG((LOG_INFO, "(%s:", ip_ntoa(ciaddr1)));
-           if (wo->hisaddr && ciaddr1 != wo->hisaddr) {
+           if (ciaddr1 != wo->hisaddr
+               && (ciaddr1 == 0 || !wo->accept_remote)) {
                orc = CONFNAK;
                if (!reject_if_disagree) {
-                   DECPTR(sizeof (long), p);
+                   DECPTR(sizeof(u_int32_t), p);
                    tl = ntohl(wo->hisaddr);
                    PUTLONG(tl, p);
                }
+           } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+               /*
+                * If neither we nor he knows his address, reject the option.
+                */
+               orc = CONFREJ;
+               wo->req_addr = 0;       /* don't NAK with 0.0.0.0 later */
+               break;
            }
 
            /*
@@ -794,16 +800,18 @@ ipcp_reqci(f, inp, len, reject_if_disagree)
            GETLONG(tl, p);             /* Parse desination address (ours) */
            ciaddr2 = htonl(tl);
            IPCPDEBUG((LOG_INFO, "%s)", ip_ntoa(ciaddr2)));
-           if (wo->ouraddr && ciaddr2 != wo->ouraddr) {
-               orc = CONFNAK;
-               if (!reject_if_disagree) {
-                   DECPTR(sizeof (long), p);
-                   tl = ntohl(wo->ouraddr);
-                   PUTLONG(tl, p);
+           if (ciaddr2 != wo->ouraddr) {
+               if (ciaddr2 == 0 || !wo->accept_local) {
+                   orc = CONFNAK;
+                   if (!reject_if_disagree) {
+                       DECPTR(sizeof(u_int32_t), p);
+                       tl = ntohl(wo->ouraddr);
+                       PUTLONG(tl, p);
+                   }
+               } else {
+                   go->ouraddr = ciaddr2;      /* accept peer's idea */
                }
            }
-           if (orc == CONFNAK)
-               break;
 
            ho->neg_addr = 1;
            ho->old_addrs = 1;
@@ -829,18 +837,23 @@ ipcp_reqci(f, inp, len, reject_if_disagree)
            GETLONG(tl, p);     /* Parse source address (his) */
            ciaddr1 = htonl(tl);
            IPCPDEBUG((LOG_INFO, "(%s)", ip_ntoa(ciaddr1)));
-           if (wo->hisaddr && ciaddr1 != wo->hisaddr) {
+           if (ciaddr1 != wo->hisaddr
+               && (ciaddr1 == 0 || !wo->accept_remote)) {
                orc = CONFNAK;
                if (!reject_if_disagree) {
-                   DECPTR(sizeof (long), p);
+                   DECPTR(sizeof(u_int32_t), p);
                    tl = ntohl(wo->hisaddr);
                    PUTLONG(tl, p);
                }
+           } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+               /*
+                * Don't ACK an address of 0.0.0.0 - reject it instead.
+                */
+               orc = CONFREJ;
+               wo->req_addr = 0;       /* don't NAK with 0.0.0.0 later */
+               break;
            }
        
-           if (orc == CONFNAK)
-               break;
-
            ho->neg_addr = 1;
            ho->hisaddr = ciaddr1;
            break;
@@ -880,10 +893,12 @@ ipcp_reqci(f, inp, len, reject_if_disagree)
                        PUTCHAR(wo->cflag, p);
                    }
                }
-               if (orc == CONFNAK)
-                   break;
                ho->maxslotindex = maxslotindex;
-               ho->cflag = wo->cflag;
+               ho->cflag = cflag;
+           } else {
+               ho->old_vj = 1;
+               ho->maxslotindex = MAX_STATES - 1;
+               ho->cflag = 1;
            }
            break;
 
@@ -961,7 +976,7 @@ static void
 ipcp_up(f)
     fsm *f;
 {
-    u_long mask;
+    u_int32_t mask;
     ipcp_options *ho = &ipcp_hisoptions[f->unit];
     ipcp_options *go = &ipcp_gotoptions[f->unit];
 
@@ -972,7 +987,7 @@ ipcp_up(f)
     /*
      * We must have a non-zero IP address for both ends of the link.
      */
-    if (ho->hisaddr == 0)
+    if (!ho->neg_addr)
        ho->hisaddr = ipcp_wantoptions[f->unit].hisaddr;
 
     if (ho->hisaddr == 0) {
@@ -987,7 +1002,7 @@ ipcp_up(f)
     }
 
     /*
-     * Check that the peer is allowed to use the IP address he wants.
+     * Check that the peer is allowed to use the IP address it wants.
      */
     if (!auth_ip_addr(f->unit, ho->hisaddr)) {
        syslog(LOG_ERR, "Peer is not authorized to use remote address %s",
@@ -1010,7 +1025,7 @@ ipcp_up(f)
     }
 
     /* set tcp compression */
-    sifvjcomp(f->unit, ho->neg_vj, ho->cflag);
+    sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex);
 
     /* bring the interface up for IP */
     if (!sifup(f->unit)) {
@@ -1028,6 +1043,13 @@ ipcp_up(f)
     if (ipcp_wantoptions[f->unit].proxy_arp)
        if (sifproxyarp(f->unit, ho->hisaddr))
            go->proxy_arp = 1;
+
+    /*
+     * Execute the ip-up script, like this:
+     * /etc/ppp/ip-up interface tty speed local-IP remote-IP
+     */
+    ipcp_script(f, _PATH_IPUP);
+
 }
 
 
@@ -1041,7 +1063,7 @@ static void
 ipcp_down(f)
     fsm *f;
 {
-    u_long ouraddr, hisaddr;
+    u_int32_t ouraddr, hisaddr;
 
     IPCPDEBUG((LOG_INFO, "ipcp: down"));
 
@@ -1053,4 +1075,139 @@ ipcp_down(f)
        cifdefaultroute(f->unit, hisaddr);
     sifdown(f->unit);
     cifaddr(f->unit, ouraddr, hisaddr);
+
+    /* Execute the ip-down script */
+    ipcp_script(f, _PATH_IPDOWN);
+}
+
+
+/*
+ * ipcp_script - Execute a script with arguments
+ * interface-name tty-name speed local-IP remote-IP.
+ */
+static void
+ipcp_script(f, script)
+    fsm *f;
+    char *script;
+{
+    char strspeed[32], strlocal[32], strremote[32];
+    char *argv[8];
+
+    sprintf(strspeed, "%d", baud_rate);
+    strcpy(strlocal, ip_ntoa(ipcp_gotoptions[f->unit].ouraddr));
+    strcpy(strremote, ip_ntoa(ipcp_hisoptions[f->unit].hisaddr));
+
+    argv[0] = script;
+    argv[1] = ifname;
+    argv[2] = devnam;
+    argv[3] = strspeed;
+    argv[4] = strlocal;
+    argv[5] = strremote;
+    argv[6] = ipparam;
+    argv[7] = NULL;
+    run_program(script, argv, 0);
+}
+
+/*
+ * ipcp_printpkt - print the contents of an IPCP packet.
+ */
+char *ipcp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej"
+};
+
+int
+ipcp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer)();
+    void *arg;
+{
+    int code, id, len, olen;
+    u_char *pstart, *optend;
+    u_short cishort;
+    u_int32_t cilong;
+
+    if (plen < HEADERLEN)
+       return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < HEADERLEN || len > plen)
+       return 0;
+
+    if (code >= 1 && code <= sizeof(ipcp_codenames) / sizeof(char *))
+       printer(arg, " %s", ipcp_codenames[code-1]);
+    else
+       printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+       /* print option list */
+       while (len >= 2) {
+           GETCHAR(code, p);
+           GETCHAR(olen, p);
+           p -= 2;
+           if (olen < 2 || olen > len) {
+               break;
+           }
+           printer(arg, " <");
+           len -= olen;
+           optend = p + olen;
+           switch (code) {
+           case CI_ADDRS:
+               if (olen == CILEN_ADDRS) {
+                   p += 2;
+                   GETLONG(cilong, p);
+                   printer(arg, "addrs %s", ip_ntoa(htonl(cilong)));
+                   GETLONG(cilong, p);
+                   printer(arg, " %s", ip_ntoa(htonl(cilong)));
+               }
+               break;
+           case CI_COMPRESSTYPE:
+               if (olen >= CILEN_COMPRESS) {
+                   p += 2;
+                   GETSHORT(cishort, p);
+                   printer(arg, "compress ");
+                   switch (cishort) {
+                   case IPCP_VJ_COMP:
+                       printer(arg, "VJ");
+                       break;
+                   case IPCP_VJ_COMP_OLD:
+                       printer(arg, "old-VJ");
+                       break;
+                   default:
+                       printer(arg, "0x%x", cishort);
+                   }
+               }
+               break;
+           case CI_ADDR:
+               if (olen == CILEN_ADDR) {
+                   p += 2;
+                   GETLONG(cilong, p);
+                   printer(arg, "addr %s", ip_ntoa(htonl(cilong)));
+               }
+               break;
+           }
+           while (p < optend) {
+               GETCHAR(code, p);
+               printer(arg, " %.2x", code);
+           }
+           printer(arg, ">");
+       }
+       break;
+    }
+
+    /* print the rest of the bytes in the packet */
+    for (; len > 0; --len) {
+       GETCHAR(code, p);
+       printer(arg, " %.2x", code);
+    }
+
+    return p - pstart;
 }