home *** CD-ROM | disk | FTP | other *** search
/ Super Net 1 / SUPERNET_1.iso / PC / OTROS / SUN / PPP / SUNOS_OL / DDP_PPP.TAR / ipcp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-01-03  |  15.0 KB  |  637 lines

  1. /*
  2.  * ipcp.c - PPP IP Control Protocol.
  3.  *
  4.  * Copyright (c) 1989 Carnegie Mellon University.
  5.  * All rights reserved.
  6.  *
  7.  * Redistribution and use in source and binary forms are permitted
  8.  * provided that the above copyright notice and this paragraph are
  9.  * duplicated in all such forms and that any documentation,
  10.  * advertising materials, and other materials related to such
  11.  * distribution and use acknowledge that the software was developed
  12.  * by Carnegie Mellon University.  The name of the
  13.  * University may not be used to endorse or promote products derived
  14.  * from this software without specific prior written permission.
  15.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  16.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  17.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18.  */
  19.  
  20. /*
  21.  * TODO:
  22.  * Fix IP address negotiation (wantoptions or hisoptions).
  23.  * Don't set zero IP addresses.
  24.  * Send NAKs for unsent CIs.
  25.  * VJ compression.
  26.  */
  27.  
  28. #include <stdio.h>
  29. #include <sys/ioctl.h>
  30. #include <sys/types.h>
  31. #include <sys/socket.h>
  32. #include <sys/time.h>
  33.  
  34. #include <net/if.h>
  35. #include <net/route.h>
  36. #include <netinet/in.h>
  37.  
  38. #include "ppp.h"
  39. #include "fsm.h"
  40. #include "ipcp.h"
  41.  
  42.  
  43. void ipcp_resetci();        /* Reset our Configuration Information */
  44. int ipcp_cilen();        /* Return length of our CI */
  45. void ipcp_addci();        /* Add our CIs */
  46. int ipcp_ackci();        /* Ack some CIs */
  47. void ipcp_nakci();        /* Nak some CIs */
  48. void ipcp_rejci();        /* Reject some CIs */
  49. u_char ipcp_reqci();        /* Check the requested CIs */
  50. void ipcp_up();            /* We're UP */
  51. void ipcp_down();        /* We're DOWN */
  52.  
  53.  
  54. fsm ipcp_fsm[NPPP];        /* IPCP fsm structure */
  55.  
  56. fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
  57.     ipcp_resetci,        /* Reset our Configuration Information */
  58.     ipcp_cilen,            /* Length of our Configuration Information */
  59.     ipcp_addci,            /* Add our Configuration Information */
  60.     ipcp_ackci,            /* ACK our Configuration Information */
  61.     ipcp_nakci,            /* NAK our Configuration Information */
  62.     ipcp_rejci,            /* Reject our Configuration Information */
  63.     ipcp_reqci,            /* Request peer's Configuration Information */
  64.     ipcp_up,            /* Called when fsm reaches OPEN state */
  65.     ipcp_down,            /* Called when fsm leaves OPEN state */
  66.     NULL,            /* Called when fsm reaches CLOSED state */
  67.     NULL,            /* Called when Protocol-Reject received */
  68.     NULL            /* Retransmission is necessary */
  69. };
  70.  
  71.  
  72. ipcp_options ipcp_wantoptions[NPPP]; /* Options that we want to request */
  73. ipcp_options ipcp_gotoptions[NPPP]; /* Options that peer ack'd */
  74. ipcp_options ipcp_allowoptions[NPPP]; /* Options that we allow peer to
  75.                      request */
  76. ipcp_options ipcp_hisoptions[NPPP]; /* Options that we ack'd */
  77.  
  78.  
  79. /*
  80.  * ipcp_init - Initialize IPCP.
  81.  */
  82. void ipcp_init(unit)
  83.     int unit;
  84. {
  85.     fsm *f = &ipcp_fsm[unit];
  86.     ipcp_options *wo = &ipcp_wantoptions[unit];
  87.     ipcp_options *ao = &ipcp_allowoptions[unit];
  88.  
  89.     f->unit = unit;
  90.     f->protocol = IPCP;
  91.     f->timeouttime = DEFTIMEOUT;
  92.     f->maxtermtransmits = DEFMAXTERMTRANSMITS;
  93.     f->maxnakloops = DEFMAXNAKLOOPS;
  94.     f->callbacks = &ipcp_callbacks;
  95.  
  96.     wo->neg_addrs = 1;
  97.     wo->ouraddr = 0;
  98.     wo->hisaddr = 0;
  99.     wo->neg_vj = 0;        /* XXX We don't do it yet */
  100.  
  101.     ao->neg_addrs = 1;
  102.     ao->neg_vj = 0;        /* XXX We don't do it yet */
  103.  
  104.     fsm_init(&ipcp_fsm[unit]);
  105. }
  106.  
  107.  
  108. /*
  109.  * ipcp_activeopen - Actively open IPCP.
  110.  */
  111. void ipcp_activeopen(unit)
  112.     int unit;
  113. {
  114.     fsm_activeopen(&ipcp_fsm[unit]);
  115. }
  116.  
  117.  
  118. /*
  119.  * ipcp_passiveopen - Passively open IPCP.
  120.  */
  121. void ipcp_passiveopen(unit)
  122.     int unit;
  123. {
  124.     fsm_passiveopen(&ipcp_fsm[unit]);
  125. }
  126.  
  127.  
  128. /*
  129.  * ipcp_close - Close IPCP.
  130.  */
  131. void ipcp_close(unit)
  132.     int unit;
  133. {
  134.     fsm_close(&ipcp_fsm[unit]);
  135. }
  136.  
  137.  
  138. /*
  139.  * ipcp_lowerup - The lower layer is up.
  140.  */
  141. void ipcp_lowerup(unit)
  142.     int unit;
  143. {
  144.     fsm_lowerup(&ipcp_fsm[unit]);
  145. }
  146.  
  147.  
  148. /*
  149.  * ipcp_lowerdown - The lower layer is down.
  150.  */
  151. void ipcp_lowerdown(unit)
  152.     int unit;
  153. {
  154.     fsm_lowerdown(&ipcp_fsm[unit]);
  155. }
  156.  
  157.  
  158. /*
  159.  * ipcp_input - Input IPCP packet.
  160.  */
  161. void ipcp_input(unit, p, len)
  162.     int unit;
  163.     PACKET *p;
  164.     int len;
  165. {
  166.     fsm_input(&ipcp_fsm[unit], p, len);
  167. }
  168.  
  169.  
  170. /*
  171.  * ipcp_protrej - A Protocol-Reject was received for IPCP.
  172.  *
  173.  * Simply pretend that LCP went down.
  174.  */
  175. void ipcp_protrej(unit)
  176.     int unit;
  177. {
  178.     fsm_lowerdown(&ipcp_fsm[unit]);
  179. }
  180.  
  181.  
  182. /*
  183.  * ipcp_resetci - Reset our CI.
  184.  */
  185. void ipcp_resetci(f)
  186.     fsm *f;
  187. {
  188.     ipcp_gotoptions[f->unit] = ipcp_wantoptions[f->unit];
  189. }
  190.  
  191.  
  192. /*
  193.  * ipcp_cilen - Return length of our CI.
  194.  */
  195. int ipcp_cilen(f)
  196.     fsm *f;
  197. {
  198.     ipcp_options *go = &ipcp_gotoptions[f->unit];
  199.  
  200. #define LENCISHORT(neg)  (neg ? 4 : 0)
  201. #define LENCIADDRS(neg)  (neg ? 10 : 0)
  202.  
  203.     return (LENCIADDRS(go->neg_addrs) +
  204.         LENCISHORT(go->neg_vj));
  205. }
  206.  
  207.  
  208. /*
  209.  * ipcp_addci - Add our desired CIs to a packet.
  210.  */
  211. void ipcp_addci(f, ucp)
  212.     fsm *f;
  213.     u_char *ucp;
  214. {
  215.     ipcp_options *go = &ipcp_gotoptions[f->unit];
  216.  
  217. #define ADDCISHORT(opt, neg, val) \
  218.     if (neg) { \
  219.     PUTCHAR(opt, ucp); \
  220.     PUTCHAR(2 + sizeof (short), ucp); \
  221.     PUTSHORT(val, ucp); \
  222.     }
  223. #define ADDCIADDRS(opt, neg, val1, val2) \
  224.     if (neg) { \
  225.     u_long l; \
  226.     PUTCHAR(opt, ucp); \
  227.     PUTCHAR(2 + 2 * sizeof (long), ucp); \
  228.     l = ntohl(val1); \
  229.     PUTLONG(l, ucp); \
  230.     l = ntohl(val2); \
  231.     PUTLONG(l, ucp); \
  232.     }
  233.  
  234.     ADDCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr)
  235.     ADDCISHORT(CI_COMPRESSTYPE, go->neg_vj, IPCP_VJHDR)
  236. }
  237.  
  238.  
  239. /*
  240.  * ipcp_ackci - Ack our CIs.
  241.  *
  242.  * Returns:
  243.  *    0 - Ack was bad.
  244.  *    1 - Ack was good.
  245.  */
  246. int ipcp_ackci(f, p, len)
  247.     fsm *f;
  248.     u_char *p;
  249.     int len;
  250. {
  251.     ipcp_options *go = &ipcp_gotoptions[f->unit];
  252.     u_short cilen, citype, cishort;
  253.     u_long cilong;
  254.  
  255.     /*
  256.      * CIs must be in exactly the same order that we sent...
  257.      * Check packet length and CI length at each step.
  258.      * If we find any deviations, then this packet is bad.
  259.      */
  260. #define ACKCISHORT(opt, neg, val) \
  261.     if (neg) { \
  262.     if ((len -= 2 + sizeof (short)) < 0) \
  263.         goto bad; \
  264.     GETCHAR(citype, p); \
  265.     GETCHAR(cilen, p); \
  266.     if (cilen != 2 + sizeof (short) || \
  267.         citype != opt) \
  268.         goto bad; \
  269.     GETSHORT(cishort, p); \
  270.     if (cishort != val) \
  271.         goto bad; \
  272.     }
  273. #define ACKCIADDRS(opt, neg, val1, val2) \
  274.     if (neg) { \
  275.     u_long l; \
  276.     if ((len -= 2 + 2 * sizeof (long)) < 0) \
  277.         goto bad; \
  278.     GETCHAR(citype, p); \
  279.     GETCHAR(cilen, p); \
  280.     if (cilen != 2 + 2 * sizeof (long) || \
  281.         citype != opt) \
  282.         goto bad; \
  283.     GETLONG(l, p); \
  284.     cilong = htonl(l); \
  285.     if (val1) { \
  286.         if (val1 != l) \
  287.         goto bad; \
  288.     } \
  289.     else \
  290.         val1 = cilong; \
  291.     GETLONG(l, p); \
  292.     cilong = htonl(l); \
  293.     if (val2) { \
  294.         if (val2 != l) \
  295.         goto bad; \
  296.     } \
  297.     else \
  298.         val2 = cilong; \
  299.     }
  300.  
  301.     ACKCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr)
  302.     ACKCISHORT(CI_COMPRESSTYPE, go->neg_vj, IPCP_VJHDR)
  303.  
  304.     /*
  305.      * If there are any remaining CIs, then this packet is bad.
  306.      */
  307.     if (len != 0)
  308.     goto bad;
  309.     return (1);
  310. bad:
  311.     IPCPDEBUG((stderr, "ppp: ipcp_ackci: received bad Ack!\n"));
  312.     return (0);
  313. }
  314.  
  315.  
  316. /*
  317.  * ipcp_nakci - NAK some of our CIs.
  318.  *
  319.  * Returns:
  320.  *    0 - Nak was bad.
  321.  *    1 - Nak was good.
  322.  */
  323. void ipcp_nakci(f, p, len)
  324.     fsm *f;
  325.     u_char *p;
  326.     int len;
  327. {
  328.     ipcp_options *go = &ipcp_gotoptions[f->unit];
  329.     u_short cishort;
  330.     u_long ciaddr1, ciaddr2;
  331.  
  332.     /*
  333.      * Any Nak'd CIs must be in exactly the same order that we sent.
  334.      * Check packet length and CI length at each step.
  335.      * If we find any deviations, then this packet is bad.
  336.      */
  337. #define NAKCISHORT(opt, neg, code) \
  338.     if (neg && \
  339.     len >= 2 + sizeof (short) && \
  340.     p[1] == 2 + sizeof (short) && \
  341.     p[0] == opt) { \
  342.     len -= 2 + sizeof (short); \
  343.     INCPTR(2, p); \
  344.     GETSHORT(cishort, p); \
  345.     code \
  346.     }
  347. #define NAKCIADDRS(opt, neg, code) \
  348.     if (neg && \
  349.     len >= 2 + 2 * sizeof (long) && \
  350.     p[1] == 2 + 2 * sizeof (long) && \
  351.     p[0] == opt) { \
  352.     u_long l; \
  353.     len -= 2 + 2 * sizeof (long); \
  354.     INCPTR(2, p); \
  355.     GETLONG(l, p); \
  356.     ciaddr1 = htonl(l); \
  357.     GETLONG(l, p); \
  358.     ciaddr2 = htonl(l); \
  359.     code \
  360.     }
  361.  
  362.     NAKCIADDRS(CI_ADDRS, go->neg_addrs,
  363.            if (!go->ouraddr)    /* Didn't know our address? */
  364.            go->ouraddr = ciaddr1;
  365.            if (ciaddr2)        /* Does he know his? */
  366.                go->hisaddr = ciaddr2;
  367.            )
  368.     NAKCISHORT(CI_COMPRESSTYPE, go->neg_vj,
  369.                   go->neg_vj = 0;
  370.            )
  371.  
  372.     /*
  373.      * If there are any remaining CIs, then this packet is bad.
  374.      */
  375.     if (len == 0)
  376.     return;
  377. bad:
  378.     IPCPDEBUG((stderr, "ppp: ipcp_nakci: received bad Nak!\n"));
  379. }
  380.  
  381.  
  382. /*
  383.  * ipcp_rejci - Reject some of our CIs.
  384.  */
  385. void ipcp_rejci(f, p, len)
  386.     fsm *f;
  387.     u_char *p;
  388.     int len;
  389. {
  390.     ipcp_options *go = &ipcp_gotoptions[f->unit];
  391.     u_short cishort;
  392.     u_long cilong;
  393.  
  394.     /*
  395.      * Any Rejected CIs must be in exactly the same order that we sent.
  396.      * Check packet length and CI length at each step.
  397.      * If we find any deviations, then this packet is bad.
  398.      */
  399. #define REJCISHORT(opt, neg, val) \
  400.     if (neg && \
  401.     len >= 2 + sizeof (short) && \
  402.     p[1] == 2 + sizeof (short) && \
  403.     p[0] == opt) { \
  404.     len -= 2 + sizeof (short); \
  405.     INCPTR(2, p); \
  406.     GETSHORT(cishort, p); \
  407.     /* Check rejected value. */ \
  408.     if (cishort != val) \
  409.         goto bad; \
  410.     neg = 0; \
  411.     }
  412. #define REJCIADDRS(opt, neg, val1, val2) \
  413.     if (neg && \
  414.     len >= 2 + 2 * sizeof (long) && \
  415.     p[1] == 2 + 2 * sizeof (long) && \
  416.     p[0] == opt) { \
  417.     u_long l; \
  418.     len -= 2 + 2 * sizeof (long); \
  419.     INCPTR(2, p); \
  420.     GETLONG(l, p); \
  421.     cilong = htonl(l); \
  422.     /* Check rejected value. */ \
  423.     if (cilong != val2) \
  424.         goto bad; \
  425.     GETLONG(l, p); \
  426.     cilong = htonl(l); \
  427.     /* Check rejected value. */ \
  428.     if (cilong != val1) \
  429.         goto bad; \
  430.     neg = 0; \
  431.     }
  432.  
  433.     REJCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr)
  434.     REJCISHORT(CI_COMPRESSTYPE, go->neg_vj, IPCP_VJHDR)
  435.  
  436.     /*
  437.      * If there are any remaining CIs, then this packet is bad.
  438.      */
  439.     if (len == 0)
  440.     return;
  441. bad:
  442.     IPCPDEBUG((stderr, "ppp: ipcp_rejci: received bad Reject!\n"));
  443. }
  444.  
  445.  
  446. /*
  447.  * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
  448.  *
  449.  * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
  450.  * appropriately.
  451.  */
  452. u_char ipcp_reqci(f, inp, len)
  453.     fsm *f;
  454.     u_char *inp;        /* Requested CIs */
  455.     int *len;            /* Length of requested CIs */
  456. {
  457.     ipcp_options *wo = &ipcp_wantoptions[f->unit];
  458.     ipcp_options *ho = &ipcp_hisoptions[f->unit];
  459.     ipcp_options *ao = &ipcp_allowoptions[f->unit];
  460.     u_char *cip;        /* Pointer to Current CI */
  461.     u_short cilen, citype;    /* Parsed len, type */
  462.     u_short cishort;        /* Parsed short value */
  463.     u_long tl, ciaddr1, ciaddr2;    /* Parsed address values */
  464.     int rc = CONFACK;        /* Final packet return code */
  465.     int orc;            /* Individual option return code */
  466.     u_char *p = inp;        /* Pointer to next char to parse */
  467.     u_char *ucp = inp;        /* Pointer to current output char */
  468.     int l = *len;        /* Length left */
  469.  
  470.     /*
  471.      * Reset all his options.
  472.      */
  473.     ho->neg_addrs = 0;
  474.     ho->neg_vj = 0;
  475.  
  476.     /*
  477.      * Process all his options.
  478.      */
  479.     while (l) {
  480.     orc = CONFACK;            /* Assume success */
  481.     cip = p;            /* Remember begining of CI */
  482.     if (l < 2 ||            /* Not enough data for CI header or */
  483.         p[1] < 2 ||            /*  CI length too small or */
  484.         p[1] > l) {            /*  CI length too big? */
  485.         IPCPDEBUG((stderr, "ppp: ipcp_reqci: bad CI length!\n"));
  486.         orc = CONFREJ;        /* Reject bad CI */
  487.         cilen = l;            /* Reject till end of packet */
  488.         l = 0;            /* Don't loop again */
  489.         goto endswitch;
  490.     }
  491.     GETCHAR(citype, p);        /* Parse CI type */
  492.     GETCHAR(cilen, p);        /* Parse CI length */
  493.     l -= cilen;            /* Adjust remaining length */
  494.     cilen -= 2;            /* Adjust cilen to just data */
  495.  
  496.     switch (citype) {        /* Check CI type */
  497.       case CI_ADDRS:
  498.         IPCPDEBUG((stderr, "ppp: ipcp_reqci: rcvd ADDRS"));
  499.         if (!ao->neg_addrs ||
  500.         cilen != 2 * sizeof (long)) { /* Check CI length */
  501.         INCPTR(cilen, p);     /* Skip rest of CI */
  502.         orc = CONFREJ;        /* Reject CI */
  503.         break;
  504.         }
  505.  
  506.         /*
  507.          * If he has no address, or if we both have his address but
  508.          * disagree about it, then NAK it with our idea.
  509.          * In particular, if we don't know his address, but he does,
  510.          * then accept it.
  511.          */
  512.         GETLONG(tl, p);        /* Parse source address (his) */
  513.         ciaddr1 = htonl(tl);
  514.         if (!ciaddr1 ||
  515.         (wo->neg_addrs && wo->hisaddr && ciaddr1 != wo->hisaddr)) {
  516.         orc = CONFNAK;
  517.         DECPTR(sizeof (long), p);
  518.         tl = wo->neg_addrs ? ntohl(wo->hisaddr) : 0;
  519.         PUTLONG(tl, p);
  520.         }
  521.  
  522.         /*
  523.          * If he doesn't know our address, or if we both have our address
  524.          * but disagree about it, then NAK it with our idea.
  525.          */
  526.         GETLONG(tl, p);        /* Parse desination address (ours) */
  527.         ciaddr2 = htonl(tl);
  528.         LCPDEBUG((stderr, "(%08lx:%08lx)", ciaddr1, ciaddr2));
  529.         if (!ciaddr2 ||
  530.         (wo->neg_addrs && wo->ouraddr && ciaddr2 != wo->ouraddr)) {
  531.         orc = CONFNAK;
  532.         DECPTR(sizeof (long), p);
  533.         tl = ntohl(wo->ouraddr);
  534.         PUTLONG(tl, p);
  535.         }
  536.         if (orc == CONFNAK)
  537.         break;
  538.  
  539.         /* XXX ho or go? */
  540.         ho->neg_addrs = 1;
  541.         ho->hisaddr = ciaddr1;
  542.         ho->ouraddr = ciaddr2;
  543.         break;
  544.  
  545.       case CI_COMPRESSTYPE:
  546.         IPCPDEBUG((stderr, "ppp: ipcp_reqci: rcvd COMPRESSTYPE"));
  547.         if (!ao->neg_vj ||
  548.         cilen != sizeof (short)) {
  549.         INCPTR(cilen, p);
  550.         orc = CONFREJ;
  551.         break;
  552.         }
  553.         GETSHORT(cishort, p);
  554.         LCPDEBUG((stderr, "(%d)", cishort));
  555.  
  556.         /*
  557.          * Compresstype must be IPCP_VJHDR.
  558.          */
  559.         if (cishort != IPCP_VJHDR) {
  560.         DECPTR(sizeof (short), p);
  561.         orc = CONFNAK;
  562.         PUTSHORT(IPCP_VJHDR, p);
  563.         break;
  564.         }
  565.         ho->neg_vj = 1;
  566.         break;
  567.  
  568.       default:
  569.         INCPTR(cilen, p);
  570.         orc = CONFREJ;
  571.         break;
  572.     }
  573.     cilen += 2;            /* Adjust cilen whole CI */
  574.  
  575. endswitch:
  576.     IPCPDEBUG((stderr, " (%s)\n",
  577.            orc == CONFACK ? "ACK" : (orc == CONFNAK ? "NAK" : "REJ")));
  578.     if (orc == CONFACK &&        /* Good CI */
  579.         rc != CONFACK)        /*  but prior CI wasnt? */
  580.         continue;            /* Don't send this one */
  581.  
  582.     if (orc == CONFNAK) {        /* Nak this CI? */
  583.         if (rc == CONFREJ)        /* Rejecting prior CI? */
  584.         continue;        /* Don't send this one */
  585.         if (rc == CONFACK) {    /* Ack'd all prior CIs? */
  586.         rc = CONFNAK;        /* Not anymore... */
  587.         ucp = inp;        /* Backup */
  588.         }
  589.     }
  590.     if (orc == CONFREJ &&        /* Reject this CI */
  591.         rc != CONFREJ) {        /*  but no prior ones? */
  592.         rc = CONFREJ;
  593.         ucp = inp;            /* Backup */
  594.     }
  595.     if (ucp != cip)            /* Need to move CI? */
  596.         BCOPY(cip, ucp, cilen);    /* Move it */
  597.     INCPTR(cilen, ucp);        /* Update output pointer */
  598.     }
  599.  
  600.     /*
  601.      * XXX If we wanted to send additional NAKs (for unsent CIs), the
  602.      * code would go here.  This must be done with care since it might
  603.      * require a longer packet than we received.
  604.      */
  605.  
  606.     *len = ucp - inp;            /* Compute output length */
  607.     IPCPDEBUG((stderr, "ppp: ipcp_reqci: returning %s.\n",
  608.           rc == CONFACK ? "CONFACK" :
  609.           rc == CONFNAK ? "CONFNAK" : "CONFREJ"));
  610.     return (rc);            /* Return final code */
  611. }
  612.  
  613.  
  614. /*
  615.  * ipcp_up - IPCP has come UP.
  616.  */
  617. void ipcp_up(f)
  618.     fsm *f;
  619. {
  620.     /* XXX gotoptions or hisoptions? */
  621.     SIFADDR(f->unit, ipcp_gotoptions[f->unit].ouraddr,
  622.        ipcp_gotoptions[f->unit].hisaddr);
  623. }
  624.  
  625.  
  626. /*
  627.  * ipcp_down - IPCP has gone DOWN.
  628.  *
  629.  * Alert other protocols.
  630.  */
  631. void ipcp_down(f)
  632.     fsm *f;
  633. {
  634.     CIFADDR(f->unit, ipcp_gotoptions[f->unit].ouraddr,
  635.        ipcp_gotoptions[f->unit].hisaddr);
  636. }
  637.