extra includes for linux
[ppp.git] / pppd / lcp.c
index c475f4ce97c3ea3d3b0cd55906cb6763ffa30587..406053e52f91e6a752480f860a0ae2000f25675e 100644 (file)
  */
 
 #ifndef lint
-static char rcsid[] = "$Id: lcp.c,v 1.1 1993/11/11 03:54:25 paulus Exp $";
+static char rcsid[] = "$Id: lcp.c,v 1.7 1994/05/27 01:01:49 paulus Exp $";
 #endif
 
 /*
  * TODO:
- * Option tracing.
- * Test restart.
  */
 
 #include <stdio.h>
+#include <string.h>
 #include <syslog.h>
+#include <assert.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 <netinet/in.h>
 
-#include <string.h>
+#ifdef _linux_         /* Needs ppp ioctls */
+#include <net/if.h>
+#include <linux/ppp.h>
+#endif
 
 #include "pppd.h"
 #include "ppp.h"
@@ -55,6 +55,14 @@ lcp_options lcp_wantoptions[NPPP];   /* Options that we want to request */
 lcp_options lcp_gotoptions[NPPP];      /* Options that peer ack'd */
 lcp_options lcp_allowoptions[NPPP];    /* Options we allow peer to request */
 lcp_options lcp_hisoptions[NPPP];      /* Options that we ack'd */
+u_long xmit_accm[NPPP][8];             /* extended transmit ACCM */
+
+static u_long lcp_echos_pending = 0;    /* Number of outstanding echo msgs */
+static u_long lcp_echo_number   = 0;    /* ID number of next echo frame */
+static u_long lcp_echo_timer_running = 0;  /* TRUE if a timer is running */
+
+u_long lcp_echo_interval = 0;
+u_long lcp_echo_fails = 0;
 
 /*
  * Callbacks for fsm code.  (CI = Configuration Information)
@@ -73,6 +81,17 @@ static void lcp_finished __ARGS((fsm *));    /* We need lower layer down */
 static int  lcp_extcode __ARGS((fsm *, int, int, u_char *, int));
 static void lcp_rprotrej __ARGS((fsm *, u_char *, int));
 
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup __ARGS((int));
+static void lcp_echo_lowerdown __ARGS((int));
+static void LcpEchoTimeout __ARGS((caddr_t));
+static void lcp_received_echo_reply __ARGS((fsm *, int, u_char *, int));
+static void LcpSendEchoRequest __ARGS((fsm *));
+static void LcpLinkFailure __ARGS((fsm *));
+
 static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
     lcp_resetci,               /* Reset our Configuration Information */
     lcp_cilen,                 /* Length of our Configuration Information */
@@ -129,7 +148,7 @@ lcp_init(unit)
                                           implementations */
     wo->neg_mru = 1;
     wo->mru = DEFMRU;
-    wo->neg_asyncmap = 1;
+    wo->neg_asyncmap = 0;
     wo->asyncmap = 0;
     wo->neg_chap = 0;                  /* Set to 1 on server */
     wo->neg_upap = 0;                  /* Set to 1 on server */
@@ -151,6 +170,8 @@ lcp_init(unit)
     ao->neg_accompression = 1;
     ao->neg_lqr = 0;                   /* no LQR implementation yet */
 
+    memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
+    xmit_accm[unit][3] = 0x60000000;
 }
 
 
@@ -180,7 +201,20 @@ void
 lcp_close(unit)
     int unit;
 {
-    fsm_close(&lcp_fsm[unit]);
+    fsm *f = &lcp_fsm[unit];
+
+    if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
+       /*
+        * This action is not strictly according to the FSM in RFC1548,
+        * but it does mean that the program terminates if you do a
+        * lcp_close(0) in passive/silent mode when a connection hasn't
+        * been established.
+        */
+       f->state = CLOSED;
+       lcp_finished(f);
+
+    } else
+       fsm_close(&lcp_fsm[unit]);
 }
 
 
@@ -192,9 +226,11 @@ lcp_lowerup(unit)
     int unit;
 {
     sifdown(unit);
+    ppp_set_xaccm(unit, xmit_accm[unit]);
     ppp_send_config(unit, MTU, 0xffffffff, 0, 0);
-    ppp_recv_config(unit, MTU, 0, 0, 0);
+    ppp_recv_config(unit, MTU, 0x00000000, 0, 0);
     peer_mru[unit] = MTU;
+    lcp_allowoptions[unit].asyncmap = xmit_accm[unit][0];
 
     fsm_lowerup(&lcp_fsm[unit]);
 }
@@ -234,19 +270,28 @@ lcp_extcode(f, code, id, inp, len)
     u_char *inp;
     int len;
 {
+    u_char *magp;
+
     switch( code ){
     case PROTREJ:
        lcp_rprotrej(f, inp, len);
        break;
     
     case ECHOREQ:
-       if( f->state != OPENED )
+       if (f->state != OPENED)
            break;
        LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d", id));
+       magp = inp;
+       PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
+       if (len < CILEN_LONG)
+           len = CILEN_LONG;
        fsm_sdata(f, ECHOREP, id, inp, len);
        break;
     
     case ECHOREP:
+       lcp_received_echo_reply(f, id, inp, len);
+       break;
+
     case DISCREQ:
        break;
 
@@ -681,12 +726,12 @@ lcp_nakci(f, p, len)
      * to stop asking for LQR.  We haven't got any other protocol.
      * If they Nak the reporting period, take their value XXX ?
      */
-    NAKCILONG(CI_QUALITY, neg_lqr,
-             if (cishort != LQR)
-                 try.neg_lqr = 0;
-             else
-                 try.lqr_period = cilong;
-             );
+    NAKCILQR(CI_QUALITY, neg_lqr,
+            if (cishort != LQR)
+                try.neg_lqr = 0;
+            else
+                try.lqr_period = cilong;
+            );
     /*
      * Check for a looped-back line.
      */
@@ -876,7 +921,7 @@ lcp_rejci(f, p, len)
        GETSHORT(cishort, p); \
        GETLONG(cilong, p); \
        /* Check rejected value. */ \
-       if (cishort != LQR || cichar != val) \
+       if (cishort != LQR || cilong != val) \
            goto bad; \
        try.neg = 0; \
        LCPDEBUG((LOG_INFO,"lcp_rejci rejected LQR opt %d", opt)); \
@@ -1213,13 +1258,18 @@ lcp_up(f)
     lcp_options *go = &lcp_gotoptions[f->unit];
     lcp_options *ao = &lcp_allowoptions[f->unit];
 
+    if (!go->neg_magicnumber)
+       go->magicnumber = 0;
+    if (!ho->neg_magicnumber)
+       ho->magicnumber = 0;
+
     /*
      * Set our MTU to the smaller of the MTU we wanted and
      * the MRU our peer wanted.  If we negotiated an MRU,
      * set our MRU to the larger of value we wanted and
      * the value we got in the negotiation.
      */
-    ppp_send_config(f->unit, (ho->neg_mru? MIN(ao->mru, ho->mru): MTU),
+    ppp_send_config(f->unit, MIN(ao->mru, (ho->neg_mru? ho->mru: MTU)),
                    (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
                    ho->neg_pcompression, ho->neg_accompression);
     ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): MTU),
@@ -1232,6 +1282,7 @@ lcp_up(f)
     ChapLowerUp(f->unit);      /* Enable CHAP */
     upap_lowerup(f->unit);     /* Enable UPAP */
     ipcp_lowerup(f->unit);     /* Enable IPCP */
+    lcp_echo_lowerup(f->unit);  /* Enable echo messages */
 
     link_established(f->unit);
 }
@@ -1246,15 +1297,17 @@ static void
 lcp_down(f)
     fsm *f;
 {
+    lcp_echo_lowerdown(f->unit);
     ipcp_lowerdown(f->unit);
     ChapLowerDown(f->unit);
     upap_lowerdown(f->unit);
 
     sifdown(f->unit);
     ppp_send_config(f->unit, MTU, 0xffffffff, 0, 0);
-    ppp_recv_config(f->unit, MTU, 0, 0, 0);
+    ppp_recv_config(f->unit, MTU, 0x00000000, 0, 0);
     peer_mru[f->unit] = MTU;
-    syslog(LOG_NOTICE, "Connection terminated.");
+
+    link_down(f->unit);
 }
 
 
@@ -1279,3 +1332,309 @@ lcp_finished(f)
     link_terminated(f->unit);
 }
 
+
+/*
+ * lcp_printpkt - print the contents of an LCP packet.
+ */
+char *lcp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej", "ProtRej",
+    "EchoReq", "EchoRep", "DiscReq"
+};
+
+int
+lcp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __ARGS((void *, char *, ...));
+    void *arg;
+{
+    int code, id, len, olen;
+    u_char *pstart, *optend;
+    u_short cishort;
+    u_long 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(lcp_codenames) / sizeof(char *))
+       printer(arg, " %s", lcp_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_MRU:
+               if (olen == CILEN_SHORT) {
+                   p += 2;
+                   GETSHORT(cishort, p);
+                   printer(arg, "mru %d", cishort);
+               }
+               break;
+           case CI_ASYNCMAP:
+               if (olen == CILEN_LONG) {
+                   p += 2;
+                   GETLONG(cilong, p);
+                   printer(arg, "asyncmap 0x%x", cilong);
+               }
+               break;
+           case CI_AUTHTYPE:
+               if (olen >= CILEN_SHORT) {
+                   p += 2;
+                   printer(arg, "auth ");
+                   GETSHORT(cishort, p);
+                   switch (cishort) {
+                   case UPAP:
+                       printer(arg, "upap");
+                       break;
+                   case CHAP:
+                       printer(arg, "chap");
+                       break;
+                   default:
+                       printer(arg, "0x%x", cishort);
+                   }
+               }
+               break;
+           case CI_QUALITY:
+               if (olen >= CILEN_SHORT) {
+                   p += 2;
+                   printer(arg, "quality ");
+                   GETSHORT(cishort, p);
+                   switch (cishort) {
+                   case LQR:
+                       printer(arg, "lqr");
+                       break;
+                   default:
+                       printer(arg, "0x%x", cishort);
+                   }
+               }
+               break;
+           case CI_MAGICNUMBER:
+               if (olen == CILEN_LONG) {
+                   p += 2;
+                   GETLONG(cilong, p);
+                   printer(arg, "magic 0x%x", cilong);
+               }
+               break;
+           case CI_PCOMPRESSION:
+               if (olen == CILEN_VOID) {
+                   p += 2;
+                   printer(arg, "pcomp");
+               }
+               break;
+           case CI_ACCOMPRESSION:
+               if (olen == CILEN_VOID) {
+                   p += 2;
+                   printer(arg, "accomp");
+               }
+               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;
+}
+
+/*
+ * Time to shut down the link because there is nothing out there.
+ */
+
+static
+void LcpLinkFailure (f)
+    fsm *f;
+{
+    if (f->state == OPENED) {
+        syslog (LOG_NOTICE, "Excessive lack of response to LCP echo frames.");
+        lcp_lowerdown(f->unit);                /* Reset connection */
+    }
+}
+
+/*
+ * Timer expired for the LCP echo requests from this process.
+ */
+
+static void
+LcpEchoCheck (f)
+    fsm *f;
+{
+    u_long             delta;
+#ifdef __linux__
+    struct ppp_ddinfo  ddinfo;
+    u_long             latest;
+/*
+ * Read the time since the last packet was received.
+ */
+    if (ioctl (fd, PPPIOCGTIME, &ddinfo) < 0) {
+        syslog (LOG_ERR, "ioctl(PPPIOCGTIME): %m");
+        die (1);
+    }
+/*
+ * Choose the most recient frame received. It may be an IP or NON-IP frame.
+ */
+    latest = ddinfo.nip_rjiffies < ddinfo.ip_rjiffies ? ddinfo.nip_rjiffies
+                                                      : ddinfo.ip_rjiffies;
+/*
+ * Compute the time since the last packet was received. If the timer
+ *  has expired then send the echo request and reset the timer to maximum.
+ */
+    delta = (lcp_echo_interval * HZ) - latest;
+    if (delta < HZ || latest < 0L) {
+        LcpSendEchoRequest (f);
+        delta = lcp_echo_interval * HZ;
+    }
+    delta /= HZ;
+
+#else /* Other implementations do not have ability to find delta */
+    LcpSendEchoRequest (f);
+    delta = lcp_echo_interval;
+#endif
+
+/*
+ * Start the timer for the next interval.
+ */
+    assert (lcp_echo_timer_running==0);
+    TIMEOUT (LcpEchoTimeout, (caddr_t) f, delta);
+    lcp_echo_timer_running = 1;
+}
+
+/*
+ * LcpEchoTimeout - Timer expired on the LCP echo
+ */
+
+static void
+LcpEchoTimeout (arg)
+    caddr_t arg;
+{
+    if (lcp_echo_timer_running != 0) {
+        lcp_echo_timer_running = 0;
+        LcpEchoCheck ((fsm *) arg);
+    }
+}
+
+/*
+ * LcpEchoReply - LCP has received a reply to the echo
+ */
+
+static void
+lcp_received_echo_reply (f, id, inp, len)
+    fsm *f;
+    int id; u_char *inp; int len;
+{
+    u_long magic;
+
+    /* Check the magic number - don't count replies from ourselves. */
+    if (len < CILEN_LONG)
+       return;
+    GETLONG(magic, inp);
+    if (lcp_gotoptions[f->unit].neg_magicnumber
+       && magic == lcp_gotoptions[f->unit].magicnumber)
+       return;
+
+    /* Reset the number of outstanding echo frames */
+    lcp_echos_pending = 0;
+}
+
+/*
+ * LcpSendEchoRequest - Send an echo request frame to the peer
+ */
+
+static void
+LcpSendEchoRequest (f)
+    fsm *f;
+{
+    u_long lcp_magic;
+    u_char pkt[4], *pktp;
+
+/*
+ * Detect the failure of the peer at this point.
+ */
+    if (lcp_echo_fails != 0) {
+        if (lcp_echos_pending++ >= lcp_echo_fails) {
+            LcpLinkFailure(f);
+           lcp_echos_pending = 0;
+       }
+    }
+/*
+ * Make and send the echo request frame.
+ */
+    if (f->state == OPENED) {
+        lcp_magic = lcp_gotoptions[f->unit].neg_magicnumber
+                   ? lcp_gotoptions[f->unit].magicnumber
+                   : 0L;
+       pktp = pkt;
+       PUTLONG(lcp_magic, pktp);
+      
+        fsm_sdata(f, ECHOREQ,
+                 lcp_echo_number++ & 0xFF, pkt, pktp - pkt);
+    }
+}
+
+/*
+ * lcp_echo_lowerup - Start the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerup (unit)
+    int unit;
+{
+    fsm *f = &lcp_fsm[unit];
+
+    /* Clear the parameters for generating echo frames */
+    lcp_echos_pending      = 0;
+    lcp_echo_number        = 0;
+    lcp_echo_timer_running = 0;
+  
+    /* If a timeout interval is specified then start the timer */
+    if (lcp_echo_interval != 0)
+        LcpEchoCheck (f);
+}
+
+/*
+ * lcp_echo_lowerdown - Stop the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerdown (unit)
+    int unit;
+{
+    fsm *f = &lcp_fsm[unit];
+
+    if (lcp_echo_timer_running != 0) {
+        UNTIMEOUT (LcpEchoTimeout, (caddr_t) f);
+        lcp_echo_timer_running = 0;
+    }
+}