home *** CD-ROM | disk | FTP | other *** search
- /*
- * ipcp.c - PPP IP Control Protocol.
- *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
- /*
- * TODO:
- * Fix IP address negotiation (wantoptions or hisoptions).
- * Don't set zero IP addresses.
- * Send NAKs for unsent CIs.
- * VJ compression.
- */
-
- #include <stdio.h>
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/time.h>
-
- #include <net/if.h>
- #include <net/route.h>
- #include <netinet/in.h>
-
- #include "ppp.h"
- #include "fsm.h"
- #include "ipcp.h"
-
-
- void ipcp_resetci(); /* Reset our Configuration Information */
- int ipcp_cilen(); /* Return length of our CI */
- void ipcp_addci(); /* Add our CIs */
- int ipcp_ackci(); /* Ack some CIs */
- void ipcp_nakci(); /* Nak some CIs */
- void ipcp_rejci(); /* Reject some CIs */
- u_char ipcp_reqci(); /* Check the requested CIs */
- void ipcp_up(); /* We're UP */
- void ipcp_down(); /* We're DOWN */
-
-
- fsm ipcp_fsm[NPPP]; /* IPCP fsm structure */
-
- fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
- ipcp_resetci, /* Reset our Configuration Information */
- ipcp_cilen, /* Length of our Configuration Information */
- ipcp_addci, /* Add our Configuration Information */
- ipcp_ackci, /* ACK our Configuration Information */
- ipcp_nakci, /* NAK our Configuration Information */
- ipcp_rejci, /* Reject our Configuration Information */
- ipcp_reqci, /* Request peer's Configuration Information */
- ipcp_up, /* Called when fsm reaches OPEN state */
- ipcp_down, /* Called when fsm leaves OPEN state */
- NULL, /* Called when fsm reaches CLOSED state */
- NULL, /* Called when Protocol-Reject received */
- NULL /* Retransmission is necessary */
- };
-
-
- 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 that we allow peer to
- request */
- ipcp_options ipcp_hisoptions[NPPP]; /* Options that we ack'd */
-
-
- /*
- * ipcp_init - Initialize IPCP.
- */
- void ipcp_init(unit)
- int unit;
- {
- fsm *f = &ipcp_fsm[unit];
- ipcp_options *wo = &ipcp_wantoptions[unit];
- ipcp_options *ao = &ipcp_allowoptions[unit];
-
- f->unit = unit;
- f->protocol = IPCP;
- f->timeouttime = DEFTIMEOUT;
- f->maxtermtransmits = DEFMAXTERMTRANSMITS;
- f->maxnakloops = DEFMAXNAKLOOPS;
- f->callbacks = &ipcp_callbacks;
-
- wo->neg_addrs = 1;
- wo->ouraddr = 0;
- wo->hisaddr = 0;
- wo->neg_vj = 0; /* XXX We don't do it yet */
-
- ao->neg_addrs = 1;
- ao->neg_vj = 0; /* XXX We don't do it yet */
-
- fsm_init(&ipcp_fsm[unit]);
- }
-
-
- /*
- * ipcp_activeopen - Actively open IPCP.
- */
- void ipcp_activeopen(unit)
- int unit;
- {
- fsm_activeopen(&ipcp_fsm[unit]);
- }
-
-
- /*
- * ipcp_passiveopen - Passively open IPCP.
- */
- void ipcp_passiveopen(unit)
- int unit;
- {
- fsm_passiveopen(&ipcp_fsm[unit]);
- }
-
-
- /*
- * ipcp_close - Close IPCP.
- */
- void ipcp_close(unit)
- int unit;
- {
- fsm_close(&ipcp_fsm[unit]);
- }
-
-
- /*
- * ipcp_lowerup - The lower layer is up.
- */
- void ipcp_lowerup(unit)
- int unit;
- {
- fsm_lowerup(&ipcp_fsm[unit]);
- }
-
-
- /*
- * ipcp_lowerdown - The lower layer is down.
- */
- void ipcp_lowerdown(unit)
- int unit;
- {
- fsm_lowerdown(&ipcp_fsm[unit]);
- }
-
-
- /*
- * ipcp_input - Input IPCP packet.
- */
- void ipcp_input(unit, p, len)
- int unit;
- PACKET *p;
- int len;
- {
- fsm_input(&ipcp_fsm[unit], p, len);
- }
-
-
- /*
- * ipcp_protrej - A Protocol-Reject was received for IPCP.
- *
- * Simply pretend that LCP went down.
- */
- void ipcp_protrej(unit)
- int unit;
- {
- fsm_lowerdown(&ipcp_fsm[unit]);
- }
-
-
- /*
- * ipcp_resetci - Reset our CI.
- */
- void ipcp_resetci(f)
- fsm *f;
- {
- ipcp_gotoptions[f->unit] = ipcp_wantoptions[f->unit];
- }
-
-
- /*
- * ipcp_cilen - Return length of our CI.
- */
- int ipcp_cilen(f)
- fsm *f;
- {
- ipcp_options *go = &ipcp_gotoptions[f->unit];
-
- #define LENCISHORT(neg) (neg ? 4 : 0)
- #define LENCIADDRS(neg) (neg ? 10 : 0)
-
- return (LENCIADDRS(go->neg_addrs) +
- LENCISHORT(go->neg_vj));
- }
-
-
- /*
- * ipcp_addci - Add our desired CIs to a packet.
- */
- void ipcp_addci(f, ucp)
- fsm *f;
- u_char *ucp;
- {
- ipcp_options *go = &ipcp_gotoptions[f->unit];
-
- #define ADDCISHORT(opt, neg, val) \
- if (neg) { \
- PUTCHAR(opt, ucp); \
- PUTCHAR(2 + sizeof (short), ucp); \
- PUTSHORT(val, ucp); \
- }
- #define ADDCIADDRS(opt, neg, val1, val2) \
- if (neg) { \
- u_long l; \
- PUTCHAR(opt, ucp); \
- PUTCHAR(2 + 2 * sizeof (long), ucp); \
- l = ntohl(val1); \
- PUTLONG(l, ucp); \
- l = ntohl(val2); \
- PUTLONG(l, ucp); \
- }
-
- ADDCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr)
- ADDCISHORT(CI_COMPRESSTYPE, go->neg_vj, IPCP_VJHDR)
- }
-
-
- /*
- * ipcp_ackci - Ack our CIs.
- *
- * Returns:
- * 0 - Ack was bad.
- * 1 - Ack was good.
- */
- int ipcp_ackci(f, p, len)
- fsm *f;
- u_char *p;
- int len;
- {
- ipcp_options *go = &ipcp_gotoptions[f->unit];
- u_short cilen, citype, cishort;
- u_long cilong;
-
- /*
- * CIs must be in exactly the same order that we sent...
- * Check packet length and CI length at each step.
- * If we find any deviations, then this packet is bad.
- */
- #define ACKCISHORT(opt, neg, val) \
- if (neg) { \
- if ((len -= 2 + sizeof (short)) < 0) \
- goto bad; \
- GETCHAR(citype, p); \
- GETCHAR(cilen, p); \
- if (cilen != 2 + sizeof (short) || \
- citype != opt) \
- goto bad; \
- GETSHORT(cishort, p); \
- if (cishort != val) \
- goto bad; \
- }
- #define ACKCIADDRS(opt, neg, val1, val2) \
- if (neg) { \
- u_long l; \
- if ((len -= 2 + 2 * sizeof (long)) < 0) \
- goto bad; \
- GETCHAR(citype, p); \
- GETCHAR(cilen, p); \
- if (cilen != 2 + 2 * sizeof (long) || \
- citype != opt) \
- goto bad; \
- GETLONG(l, p); \
- cilong = htonl(l); \
- if (val1) { \
- if (val1 != l) \
- goto bad; \
- } \
- else \
- val1 = cilong; \
- GETLONG(l, p); \
- cilong = htonl(l); \
- if (val2) { \
- if (val2 != l) \
- goto bad; \
- } \
- else \
- val2 = cilong; \
- }
-
- ACKCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr)
- ACKCISHORT(CI_COMPRESSTYPE, go->neg_vj, IPCP_VJHDR)
-
- /*
- * If there are any remaining CIs, then this packet is bad.
- */
- if (len != 0)
- goto bad;
- return (1);
- bad:
- IPCPDEBUG((stderr, "ppp: ipcp_ackci: received bad Ack!\n"));
- return (0);
- }
-
-
- /*
- * ipcp_nakci - NAK some of our CIs.
- *
- * Returns:
- * 0 - Nak was bad.
- * 1 - Nak was good.
- */
- void ipcp_nakci(f, p, len)
- fsm *f;
- u_char *p;
- int len;
- {
- ipcp_options *go = &ipcp_gotoptions[f->unit];
- u_short cishort;
- u_long ciaddr1, ciaddr2;
-
- /*
- * Any Nak'd CIs must be in exactly the same order that we sent.
- * Check packet length and CI length at each step.
- * If we find any deviations, then this packet is bad.
- */
- #define NAKCISHORT(opt, neg, code) \
- if (neg && \
- len >= 2 + sizeof (short) && \
- p[1] == 2 + sizeof (short) && \
- p[0] == opt) { \
- len -= 2 + sizeof (short); \
- INCPTR(2, p); \
- GETSHORT(cishort, p); \
- code \
- }
- #define NAKCIADDRS(opt, neg, code) \
- if (neg && \
- len >= 2 + 2 * sizeof (long) && \
- p[1] == 2 + 2 * sizeof (long) && \
- p[0] == opt) { \
- u_long l; \
- len -= 2 + 2 * sizeof (long); \
- INCPTR(2, p); \
- GETLONG(l, p); \
- ciaddr1 = htonl(l); \
- GETLONG(l, p); \
- ciaddr2 = htonl(l); \
- code \
- }
-
- NAKCIADDRS(CI_ADDRS, go->neg_addrs,
- if (!go->ouraddr) /* Didn't know our address? */
- go->ouraddr = ciaddr1;
- if (ciaddr2) /* Does he know his? */
- go->hisaddr = ciaddr2;
- )
- NAKCISHORT(CI_COMPRESSTYPE, go->neg_vj,
- go->neg_vj = 0;
- )
-
- /*
- * If there are any remaining CIs, then this packet is bad.
- */
- if (len == 0)
- return;
- bad:
- IPCPDEBUG((stderr, "ppp: ipcp_nakci: received bad Nak!\n"));
- }
-
-
- /*
- * ipcp_rejci - Reject some of our CIs.
- */
- void ipcp_rejci(f, p, len)
- fsm *f;
- u_char *p;
- int len;
- {
- ipcp_options *go = &ipcp_gotoptions[f->unit];
- u_short cishort;
- u_long cilong;
-
- /*
- * Any Rejected CIs must be in exactly the same order that we sent.
- * Check packet length and CI length at each step.
- * If we find any deviations, then this packet is bad.
- */
- #define REJCISHORT(opt, neg, val) \
- if (neg && \
- len >= 2 + sizeof (short) && \
- p[1] == 2 + sizeof (short) && \
- p[0] == opt) { \
- len -= 2 + sizeof (short); \
- INCPTR(2, p); \
- GETSHORT(cishort, p); \
- /* Check rejected value. */ \
- if (cishort != val) \
- goto bad; \
- neg = 0; \
- }
- #define REJCIADDRS(opt, neg, val1, val2) \
- if (neg && \
- len >= 2 + 2 * sizeof (long) && \
- p[1] == 2 + 2 * sizeof (long) && \
- p[0] == opt) { \
- u_long l; \
- len -= 2 + 2 * sizeof (long); \
- INCPTR(2, p); \
- GETLONG(l, p); \
- cilong = htonl(l); \
- /* Check rejected value. */ \
- if (cilong != val2) \
- goto bad; \
- GETLONG(l, p); \
- cilong = htonl(l); \
- /* Check rejected value. */ \
- if (cilong != val1) \
- goto bad; \
- neg = 0; \
- }
-
- REJCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr)
- REJCISHORT(CI_COMPRESSTYPE, go->neg_vj, IPCP_VJHDR)
-
- /*
- * If there are any remaining CIs, then this packet is bad.
- */
- if (len == 0)
- return;
- bad:
- IPCPDEBUG((stderr, "ppp: ipcp_rejci: received bad Reject!\n"));
- }
-
-
- /*
- * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
- *
- * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
- * appropriately.
- */
- u_char ipcp_reqci(f, inp, len)
- fsm *f;
- u_char *inp; /* Requested CIs */
- int *len; /* Length of requested CIs */
- {
- ipcp_options *wo = &ipcp_wantoptions[f->unit];
- ipcp_options *ho = &ipcp_hisoptions[f->unit];
- ipcp_options *ao = &ipcp_allowoptions[f->unit];
- u_char *cip; /* Pointer to Current CI */
- u_short cilen, citype; /* Parsed len, type */
- u_short cishort; /* Parsed short value */
- u_long tl, ciaddr1, ciaddr2; /* Parsed address values */
- int rc = CONFACK; /* Final packet return code */
- int orc; /* Individual option return code */
- u_char *p = inp; /* Pointer to next char to parse */
- u_char *ucp = inp; /* Pointer to current output char */
- int l = *len; /* Length left */
-
- /*
- * Reset all his options.
- */
- ho->neg_addrs = 0;
- ho->neg_vj = 0;
-
- /*
- * Process all his options.
- */
- while (l) {
- orc = CONFACK; /* Assume success */
- cip = p; /* Remember begining of CI */
- if (l < 2 || /* Not enough data for CI header or */
- p[1] < 2 || /* CI length too small or */
- p[1] > l) { /* CI length too big? */
- IPCPDEBUG((stderr, "ppp: ipcp_reqci: bad CI length!\n"));
- orc = CONFREJ; /* Reject bad CI */
- cilen = l; /* Reject till end of packet */
- l = 0; /* Don't loop again */
- goto endswitch;
- }
- GETCHAR(citype, p); /* Parse CI type */
- GETCHAR(cilen, p); /* Parse CI length */
- l -= cilen; /* Adjust remaining length */
- cilen -= 2; /* Adjust cilen to just data */
-
- switch (citype) { /* Check CI type */
- case CI_ADDRS:
- IPCPDEBUG((stderr, "ppp: ipcp_reqci: rcvd ADDRS"));
- if (!ao->neg_addrs ||
- cilen != 2 * sizeof (long)) { /* Check CI length */
- INCPTR(cilen, p); /* Skip rest of CI */
- orc = CONFREJ; /* Reject CI */
- break;
- }
-
- /*
- * If he has no address, or if we both have his address but
- * disagree about it, then NAK it with our idea.
- * In particular, if we don't know his address, but he does,
- * then accept it.
- */
- GETLONG(tl, p); /* Parse source address (his) */
- ciaddr1 = htonl(tl);
- if (!ciaddr1 ||
- (wo->neg_addrs && wo->hisaddr && ciaddr1 != wo->hisaddr)) {
- orc = CONFNAK;
- DECPTR(sizeof (long), p);
- tl = wo->neg_addrs ? ntohl(wo->hisaddr) : 0;
- PUTLONG(tl, p);
- }
-
- /*
- * If he doesn't know our address, or if we both have our address
- * but disagree about it, then NAK it with our idea.
- */
- GETLONG(tl, p); /* Parse desination address (ours) */
- ciaddr2 = htonl(tl);
- LCPDEBUG((stderr, "(%08lx:%08lx)", ciaddr1, ciaddr2));
- if (!ciaddr2 ||
- (wo->neg_addrs && wo->ouraddr && ciaddr2 != wo->ouraddr)) {
- orc = CONFNAK;
- DECPTR(sizeof (long), p);
- tl = ntohl(wo->ouraddr);
- PUTLONG(tl, p);
- }
- if (orc == CONFNAK)
- break;
-
- /* XXX ho or go? */
- ho->neg_addrs = 1;
- ho->hisaddr = ciaddr1;
- ho->ouraddr = ciaddr2;
- break;
-
- case CI_COMPRESSTYPE:
- IPCPDEBUG((stderr, "ppp: ipcp_reqci: rcvd COMPRESSTYPE"));
- if (!ao->neg_vj ||
- cilen != sizeof (short)) {
- INCPTR(cilen, p);
- orc = CONFREJ;
- break;
- }
- GETSHORT(cishort, p);
- LCPDEBUG((stderr, "(%d)", cishort));
-
- /*
- * Compresstype must be IPCP_VJHDR.
- */
- if (cishort != IPCP_VJHDR) {
- DECPTR(sizeof (short), p);
- orc = CONFNAK;
- PUTSHORT(IPCP_VJHDR, p);
- break;
- }
- ho->neg_vj = 1;
- break;
-
- default:
- INCPTR(cilen, p);
- orc = CONFREJ;
- break;
- }
- cilen += 2; /* Adjust cilen whole CI */
-
- endswitch:
- IPCPDEBUG((stderr, " (%s)\n",
- orc == CONFACK ? "ACK" : (orc == CONFNAK ? "NAK" : "REJ")));
- if (orc == CONFACK && /* Good CI */
- rc != CONFACK) /* but prior CI wasnt? */
- continue; /* Don't send this one */
-
- if (orc == CONFNAK) { /* Nak this CI? */
- if (rc == CONFREJ) /* Rejecting prior CI? */
- continue; /* Don't send this one */
- if (rc == CONFACK) { /* Ack'd all prior CIs? */
- rc = CONFNAK; /* Not anymore... */
- ucp = inp; /* Backup */
- }
- }
- if (orc == CONFREJ && /* Reject this CI */
- rc != CONFREJ) { /* but no prior ones? */
- rc = CONFREJ;
- ucp = inp; /* Backup */
- }
- if (ucp != cip) /* Need to move CI? */
- BCOPY(cip, ucp, cilen); /* Move it */
- INCPTR(cilen, ucp); /* Update output pointer */
- }
-
- /*
- * XXX If we wanted to send additional NAKs (for unsent CIs), the
- * code would go here. This must be done with care since it might
- * require a longer packet than we received.
- */
-
- *len = ucp - inp; /* Compute output length */
- IPCPDEBUG((stderr, "ppp: ipcp_reqci: returning %s.\n",
- rc == CONFACK ? "CONFACK" :
- rc == CONFNAK ? "CONFNAK" : "CONFREJ"));
- return (rc); /* Return final code */
- }
-
-
- /*
- * ipcp_up - IPCP has come UP.
- */
- void ipcp_up(f)
- fsm *f;
- {
- /* XXX gotoptions or hisoptions? */
- SIFADDR(f->unit, ipcp_gotoptions[f->unit].ouraddr,
- ipcp_gotoptions[f->unit].hisaddr);
- }
-
-
- /*
- * ipcp_down - IPCP has gone DOWN.
- *
- * Alert other protocols.
- */
- void ipcp_down(f)
- fsm *f;
- {
- CIFADDR(f->unit, ipcp_gotoptions[f->unit].ouraddr,
- ipcp_gotoptions[f->unit].hisaddr);
- }
-