This is ipcp.c in view mode; [Download] [Up]
/* * 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> #ifdef STREAMS #include <sys/stream.h> #endif #include "ppp.h" #include "fsm.h" #include "ipcp.h" short ip_vj_comp = IP_VJ_COMP; /* VJ compression protocol for negotiation. */ /* Change back to 0x0037 for rfc1171 */ /* compatibility */ 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 = 1; /* XXX We don't do it yet */ ao->neg_addrs = 1; ao->neg_vj = 1; /* 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, ip_vj_comp) } /* * 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 != cilong) \ goto bad; \ } \ else \ val1 = cilong; \ GETLONG(l, p); \ cilong = htonl(l); \ if (val2) { \ if (val2 != cilong) \ goto bad; \ } \ else \ val2 = cilong; \ } ACKCIADDRS(CI_ADDRS, go->neg_addrs, go->ouraddr, go->hisaddr) ACKCISHORT(CI_COMPRESSTYPE, go->neg_vj, ip_vj_comp) /* * 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, ip_vj_comp) /* * 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 ip_vj_comp. */ if (cishort != ip_vj_comp) { DECPTR(sizeof (short), p); orc = CONFNAK; PUTSHORT(ip_vj_comp, 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); SIFVJCOMP(f->unit, ipcp_hisoptions[f->unit].neg_vj); } /* * 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); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.