/* * ipcp.c - PPP IP Control Protocol. * * Copyright © 1996 Digital Equipment Corporation. * All rights reserved. * * 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. */ /* ** This is an example IPCP. It has been taken from the IPCP ** in the pppd-2.2 distribution and modified to work for the ** PPP on OpenVMS implementation. ** ** Some of the routines and functions that weren't needed ** have been removed or commented out. */ #include #include "fsm_if.h" /* Include prototypes of PPP's fsm routines */ #include "ipcp.h" /* Include definitions for this module */ #include "pppusr.h" /* Include definitions of PPP types */ #include "ppp_lib.h" /* Include PPP library prototype */ #ifndef TRUE #define TRUE 1 #define FALSE 1 #endif /* global vars */ ipcp_options ipcp_wantoptions[NIPCP]; /* Options that we want to request */ ipcp_options ipcp_gotoptions[NIPCP]; /* Options that peer ack'd */ ipcp_options ipcp_allowoptions[NIPCP]; /* Options we allow peer to request */ ipcp_options ipcp_hisoptions[NIPCP]; /* Options that we ack'd */ char tempstr[256]; char devname[256]; fsmRtns *fsmFns; /* pointer to calls into fsm routines */ /* local vars */ static int cis_received[NIPCP]; /* # Conf-Reqs received */ /* * Callbacks for fsm code. (CI = Configuration Information) */ void ipcp_resetci(int); /* Reset our CI */ int ipcp_cilen(int); /* Return length of our CI */ void ipcp_addci(int, unsigned char *, int *); /* Add our CI */ int ipcp_ackci(int, unsigned char *, int); /* Peer ack'd our CI */ int ipcp_nakci(int, unsigned char *, int); /* Peer nak'd our CI */ int ipcp_rejci(int, unsigned char *, int); /* Peer rej'd our CI */ int ipcp_reqci(int, unsigned char *, int *, int); /* Rcv CI */ void ipcp_up(int); /* We're UP */ void ipcp_down(int); /* We're DOWN */ void ipcp_script(int, char *); /* Run an up/down script */ void *ipcp_fsm[NIPCP]; /* IPCP fsm structure */ /* * Lengths of configuration options. */ #define CILEN_VOID 2 #define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ #define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ #define CILEN_ADDR 6 /* new-style single address option */ #define CILEN_ADDRS 10 /* old-style dual address option */ #define CODENAME(x) ((x) == CONFACK ? "ACK" : \ (x) == CONFNAK ? "NAK" : "REJ") /* * ipcp_register - Tell PPP we're doing IPCP * * This routine is used to tell PPP that this module * will handle requests to start the IP control protocol. */ char ipcp_register() { unsigned int error; clientCallbacks callbacks; /* pointers to routines that PPP calls into */ /* get pointer to calls into PPP fsm */ fsmFns = (fsmRtns *)PPPD$LIBRTNS( PPP_FSM_RTNS ); if ( !fsmFns ) { return FALSE; } /* Set up the pointers to our callback routines */ callbacks.open = ipcp_open; callbacks.close = ipcp_close; callbacks.resetci = ipcp_resetci; callbacks.cilen = ipcp_cilen; callbacks.addci = ipcp_addci; callbacks.ackci = ipcp_ackci; callbacks.nakci = ipcp_nakci; callbacks.rejci = ipcp_rejci; callbacks.reqci = ipcp_reqci; callbacks.up = ipcp_up; callbacks.down = ipcp_down; callbacks.extcode = NULL; /* register IPCP to PPP */ return ( (*fsmFns->registerNCP)(PPP_IPCP, &callbacks, &error) ) ; } /* * Make a string representation of a network IP address. */ char *ip_ntoa(unsigned long ipaddr) { static char b[64]; /* sprintf(b, "%d.%d.%d.%d", (unsigned char)(ipaddr >> 24), (unsigned char)(ipaddr >> 16), (unsigned char)(ipaddr >> 8), (unsigned char)(ipaddr)); */ return b; } /* * ipcp_init - Initialize IPCP on single connection . * * unit - Number of the connection to initialise */ void ipcp_init(int unit) { ipcp_options *wo = &ipcp_wantoptions[unit]; ipcp_options *ao = &ipcp_allowoptions[unit]; wo->neg_addr = 1; wo->old_addrs = 0; wo->ouraddr = 0; wo->hisaddr = 0; wo->neg_vj = 1; wo->old_vj = 0; wo->vj_protocol = IPCP_VJ_COMP; wo->maxslotindex = MAX_STATES - 1; /* really max index */ wo->cflag = 1; /* max slots and slot-id compression are currently hardwired in */ /* ppp_if.c to 16 and 1, this needs to be changed (among other */ /* things) gmc */ ao->neg_addr = 1; ao->neg_vj = 1; ao->maxslotindex = MAX_STATES - 1; ao->cflag = 1; } /* * ipcp_open - IPCP is allowed to come up. * * This routine will be called by PPP to start * IPCP on a new connection. Find a free slot * for this connection and initialise it. If * a free slot cannot be found stop the connection. * * fsmRef - PPP's reference for this connection * vcib - vcib of PPP port corresponding to this connection * */ void ipcp_open(void *fsmRef, void *vcib) { int unit; char match = FALSE; /* find free slot in table of IPCP connections */ for ( unit = 0; unit < NIPCP; unit++ ) { if ( ipcp_fsm[unit] == NULL ) { match = TRUE; ipcp_fsm[unit] = fsmRef; ipcp_init(unit); (*fsmFns->start)( fsmRef, unit ); break; } } if ( match == FALSE ) { /* no room at the inn - refuse this IPCP connection */ (*fsmFns->stop)(fsmRef); } } /* * ipcp_close - Take IPCP down. * * This routine will be called by PPP when the * connection is closed. */ void ipcp_close(int unit) { /* remove connection from table */ ipcp_fsm[unit] = NULL; } /* * ipcp_resetci - Reset our CI. * * This routine is called to tell us to set our * configuration options to our initial desired * values. */ void ipcp_resetci(int unit) { ipcp_options *wo = &ipcp_wantoptions[IPCP_UNIT(unit)]; wo->req_addr = wo->neg_addr && ipcp_allowoptions[IPCP_UNIT(unit)].neg_addr; if (wo->ouraddr == 0) wo->accept_local = 1; if (wo->hisaddr == 0) wo->accept_remote = 1; ipcp_gotoptions[IPCP_UNIT(unit)] = *wo; cis_received[IPCP_UNIT(unit)] = 0; } /* * ipcp_cilen - Return length of our CI. */ int ipcp_cilen(int unit) { ipcp_options *go = &ipcp_gotoptions[IPCP_UNIT(unit)]; #define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) #define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) return (LENCIADDR(go->neg_addr, go->old_addrs) + LENCIVJ(go->neg_vj, go->old_vj)); } /* * ipcp_addci - Add our desired CIs to a packet. */ void ipcp_addci(int unit, unsigned char *ucp, int *lenp) { ipcp_options *wo = &ipcp_wantoptions[IPCP_UNIT(unit)]; ipcp_options *go = &ipcp_gotoptions[IPCP_UNIT(unit)]; ipcp_options *ho = &ipcp_hisoptions[IPCP_UNIT(unit)]; int len = *lenp; #define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ if (neg) { \ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ if (len >= vjlen) { \ PUTCHAR(opt, ucp); \ PUTCHAR(vjlen, ucp); \ PUTSHORT(val, ucp); \ if (!old) { \ PUTCHAR(maxslotindex, ucp); \ PUTCHAR(cflag, ucp); \ } \ len -= vjlen; \ } else \ neg = 0; \ } #define ADDCIADDR(opt, neg, old, val1, val2) \ if (neg) { \ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ if (len >= addrlen) { \ unsigned long l; \ PUTCHAR(opt, ucp); \ PUTCHAR(addrlen, ucp); \ l = val1; \ PUTLONG(l, ucp); \ if (old) { \ l = val2; \ PUTLONG(l, ucp); \ } \ len -= addrlen; \ } else \ neg = 0; \ } /* * First see if we want to change our options to the old * forms because we have received old forms from the peer. */ if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { /* use the old style of address negotiation */ go->neg_addr = 1; go->old_addrs = 1; } if (wo->neg_vj && !go->neg_vj && !go->old_vj) { /* try an older style of VJ negotiation */ if (cis_received[IPCP_UNIT(unit)] == 0) { /* keep trying the new style until we see some CI from the peer */ go->neg_vj = 1; } else { /* use the old style only if the peer did */ if (ho->neg_vj && ho->old_vj) { go->neg_vj = 1; go->old_vj = 1; go->vj_protocol = ho->vj_protocol; } } } ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, go->old_addrs, go->ouraddr, go->hisaddr); ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, go->maxslotindex, go->cflag); *lenp -= len; } /* * ipcp_ackci - Ack our CIs. * * * unit Number of connection this is for * p Pointer to configuration options * len Length of configuration options * * Returns: * 0 - Ack was bad. * 1 - Ack was good. */ int ipcp_ackci(int unit, unsigned char *p, int len) { ipcp_options *go = &ipcp_gotoptions[IPCP_UNIT(unit)]; unsigned short cilen, citype, cishort; unsigned long cilong; unsigned char cimaxslotindex, cicflag; /* * 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 ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ if (neg) { \ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ if ((len -= vjlen) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != vjlen || \ citype != opt) \ goto bad; \ GETSHORT(cishort, p); \ if (cishort != val) \ goto bad; \ if (!old) { \ GETCHAR(cimaxslotindex, p); \ if (cimaxslotindex != maxslotindex) \ goto bad; \ GETCHAR(cicflag, p); \ if (cicflag != cflag) \ goto bad; \ } \ } #define ACKCIADDR(opt, neg, old, val1, val2) \ if (neg) { \ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ unsigned long l; \ if ((len -= addrlen) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != addrlen || \ citype != opt) \ goto bad; \ GETLONG(l, p); \ cilong = l; \ if (val1 != cilong) \ goto bad; \ if (old) { \ GETLONG(l, p); \ cilong = l; \ if (val2 != cilong) \ goto bad; \ } \ } ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, go->old_addrs, go->ouraddr, go->hisaddr); ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, go->maxslotindex, go->cflag); /* * If there are any remaining CIs, then this packet is bad. */ if (len != 0) goto bad; return (1); bad: IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!")); return (0); } /* * ipcp_nakci - Peer has sent a NAK for some of our CIs. * This should not modify any state if the Nak is bad * or if IPCP is in the OPENED state. * * Returns: * 0 - Nak was bad. * 1 - Nak was good. */ int ipcp_nakci(int unit, unsigned char *p, int len) { ipcp_options *go = &ipcp_gotoptions[IPCP_UNIT(unit)]; unsigned char cimaxslotindex, cicflag; unsigned char citype, cilen, *next; unsigned short cishort; unsigned long ciaddr1, ciaddr2, l; ipcp_options no; /* options we've seen Naks for */ ipcp_options try; /* options to request next time */ BZERO(&no, sizeof(no)); try = *go; /* * 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 NAKCIADDR(opt, neg, old, code) \ if (go->neg && \ len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ p[1] == cilen && \ p[0] == opt) { \ len -= cilen; \ INCPTR(2, p); \ GETLONG(l, p); \ ciaddr1 = l; \ if (old) { \ GETLONG(l, p); \ ciaddr2 = l; \ no.old_addrs = 1; \ } else \ ciaddr2 = 0; \ no.neg = 1; \ code \ } #define NAKCIVJ(opt, neg, code) \ if (go->neg && \ ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ len >= cilen && \ p[0] == opt) { \ len -= cilen; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ no.neg = 1; \ code \ } /* * 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->accept_local && ciaddr1) { /* Do we know our address? */ try.ouraddr = ciaddr1; sprintf(tempstr,"local IP address %s",ip_ntoa(ciaddr1)); IPCPDEBUG((LOG_INFO, tempstr)); } if (go->accept_remote && ciaddr2) { /* Does he know his? */ try.hisaddr = ciaddr2; sprintf(tempstr,"remote IP address %s",ip_ntoa(ciaddr2)); IPCPDEBUG((LOG_INFO,tempstr)); } ); /* * Accept the peer's value of maxslotindex provided that it * is less than what we asked for. Turn off slot-ID compression * if the peer wants. Send old-style compress-type option if * the peer wants. */ 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) try.maxslotindex = cimaxslotindex; if (!cicflag) try.cflag = 0; } else { try.neg_vj = 0; } } else { if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { try.old_vj = 1; try.vj_protocol = cishort; } else { try.neg_vj = 0; } } ); /* * There may be remaining CIs, if the peer is requesting negotiation * on an option that we didn't include in our request packet. * If they want to negotiate about IP addresses, we comply. * If they want us to ask for compression, we refuse. */ while (len > CILEN_VOID) { GETCHAR(citype, p); GETCHAR(cilen, p); if( (len -= cilen) < 0 ) goto bad; next = p + cilen - 2; switch (citype) { case CI_COMPRESSTYPE: if (go->neg_vj || no.neg_vj || (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) goto bad; no.neg_vj = 1; break; case CI_ADDRS: if (go->neg_addr && go->old_addrs || no.old_addrs || cilen != CILEN_ADDRS) goto bad; try.neg_addr = 1; try.old_addrs = 1; GETLONG(l, p); ciaddr1 = l; if (ciaddr1 && go->accept_local) try.ouraddr = ciaddr1; GETLONG(l, p); ciaddr2 = l; 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 = l; if (ciaddr1 && go->accept_local) try.ouraddr = ciaddr1; no.neg_addr = 1; break; default: goto bad; } p = next; } /* If there is still anything left, this packet is bad. */ if (len != 0) goto bad; /* * OK, the Nak is good. Now we can update state. */ *go = try; return 1; bad: IPCPDEBUG((LOG_INFO,"ipcp_nakci: received bad Nak!")); return 0; } /* * ipcp_rejci - Reject some of our CIs. */ int ipcp_rejci(int unit, unsigned char *p, int len) { ipcp_options *go = &ipcp_gotoptions[IPCP_UNIT(unit)]; unsigned char cimaxslotindex, ciflag, cilen; unsigned short cishort; unsigned long cilong; ipcp_options try; /* options to request next time */ try = *go; /* * 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 REJCIADDR(opt, neg, old, val1, val2) \ if (go->neg && \ len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ p[1] == cilen && \ p[0] == opt) { \ unsigned long l; \ len -= cilen; \ INCPTR(2, p); \ GETLONG(l, p); \ cilong = l; \ /* Check rejected value. */ \ if (cilong != val1) \ goto bad; \ if (old) { \ GETLONG(l, p); \ cilong = l; \ /* Check rejected value. */ \ if (cilong != val2) \ goto bad; \ } \ try.neg = 0; \ } #define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ if (go->neg && \ p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ len >= p[1] && \ p[0] == opt) { \ len -= p[1]; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ /* Check rejected value. */ \ if (cishort != val) \ goto bad; \ if (!old) { \ GETCHAR(cimaxslotindex, p); \ if (cimaxslotindex != maxslot) \ goto bad; \ GETCHAR(ciflag, p); \ if (ciflag != cflag) \ goto bad; \ } \ try.neg = 0; \ } REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, go->ouraddr, go->hisaddr); REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, go->maxslotindex, go->cflag); /* * If there are any remaining CIs, then this packet is bad. */ if (len != 0) goto bad; /* * Now we can update state. */ *go = try; return 1; bad: IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!")); return 0; } /* * ipcp_reqci - Check the peer's requested CIs and send appropriate response. * * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified * appropriately. If reject_if_disagree is non-zero, doesn't return * CONFNAK; returns CONFREJ if it can't return CONFACK. */ int ipcp_reqci(int unit, unsigned char *inp, /* Requested CIs */ int *len, /* Length of requested CIs */ int reject_if_disagree) { ipcp_options *wo = &ipcp_wantoptions[IPCP_UNIT(unit)]; ipcp_options *ho = &ipcp_hisoptions[IPCP_UNIT(unit)]; ipcp_options *ao = &ipcp_allowoptions[IPCP_UNIT(unit)]; ipcp_options *go = &ipcp_gotoptions[IPCP_UNIT(unit)]; unsigned char *cip, *next; /* Pointer to current and next CIs */ unsigned short cilen, citype; /* Parsed len, type */ unsigned short cishort; /* Parsed short value */ unsigned long tl, ciaddr1, ciaddr2;/* Parsed address values */ int rc = CONFACK; /* Final packet return code */ int orc; /* Individual option return code */ unsigned char *p; /* Pointer to next char to parse */ unsigned char *ucp = inp; /* Pointer to current output char */ int l = *len; /* Length left */ unsigned char maxslotindex, cflag; /* * Reset all his options. */ BZERO(ho, sizeof(*ho)); /* * Process all his options. */ next = inp; while (l) { orc = CONFACK; /* Assume success */ cip = p = next; /* 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((LOG_INFO, "ipcp_reqci: bad CI length!")); 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 */ next += cilen; /* Step to next CI */ switch (citype) { /* Check CI type */ case CI_ADDRS: IPCPDEBUG((LOG_INFO, "ipcp: received ADDRS ")); if (!ao->neg_addr || cilen != CILEN_ADDRS) { /* Check CI length */ 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 = tl; sprintf(tempstr,"(%s:", ip_ntoa(ciaddr1)); IPCPDEBUG((LOG_INFO, tempstr)); if (ciaddr1 != wo->hisaddr && (ciaddr1 == 0 || !wo->accept_remote)) { orc = CONFNAK; if (!reject_if_disagree) { DECPTR(sizeof (long), p); tl = wo->hisaddr; 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 = tl; sprintf(tempstr,"%s)", ip_ntoa(ciaddr2)); IPCPDEBUG((LOG_INFO, tempstr)); if (ciaddr2 != wo->ouraddr) { if (ciaddr2 == 0 || !wo->accept_local) { orc = CONFNAK; if (!reject_if_disagree) { DECPTR(sizeof (long), p); tl = wo->ouraddr; PUTLONG(tl, p); } } else { go->ouraddr = ciaddr2; /* accept peer's idea */ } } ho->neg_addr = 1; ho->old_addrs = 1; ho->hisaddr = ciaddr1; ho->ouraddr = ciaddr2; break; case CI_ADDR: IPCPDEBUG((LOG_INFO, "ipcp: received ADDR ")); if (!ao->neg_addr || cilen != CILEN_ADDR) { /* Check CI length */ 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 = tl; sprintf(tempstr,"(%s)", ip_ntoa(ciaddr1)); IPCPDEBUG((LOG_INFO, tempstr)); if (ciaddr1 != wo->hisaddr && (ciaddr1 == 0 || !wo->accept_remote)) { orc = CONFNAK; if (!reject_if_disagree) { DECPTR(sizeof (long), p); tl = wo->hisaddr; PUTLONG(tl, p); } } ho->neg_addr = 1; ho->hisaddr = ciaddr1; break; case CI_COMPRESSTYPE: IPCPDEBUG((LOG_INFO, "ipcp: received COMPRESSTYPE ")); if (!ao->neg_vj || (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { orc = CONFREJ; break; } GETSHORT(cishort, p); sprintf(tempstr,"(%d)", cishort); IPCPDEBUG((LOG_INFO, tempstr)); if (!(cishort == IPCP_VJ_COMP || (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { orc = CONFREJ; break; } ho->neg_vj = 1; ho->vj_protocol = cishort; if (cilen == CILEN_VJ) { GETCHAR(maxslotindex, p); if (maxslotindex > ao->maxslotindex) { orc = CONFNAK; if (!reject_if_disagree){ DECPTR(1, p); PUTCHAR(ao->maxslotindex, p); } } GETCHAR(cflag, p); if (cflag && !ao->cflag) { orc = CONFNAK; if (!reject_if_disagree){ DECPTR(1, p); PUTCHAR(wo->cflag, p); } } ho->maxslotindex = maxslotindex; ho->cflag = wo->cflag; } else { ho->old_vj = 1; ho->maxslotindex = MAX_STATES - 1; ho->cflag = 1; } break; default: orc = CONFREJ; break; } endswitch: sprintf(tempstr," (%s)", CODENAME(orc)); IPCPDEBUG((LOG_INFO, tempstr)); if (orc == CONFACK && /* Good CI */ rc != CONFACK) /* but prior CI wasnt? */ continue; /* Don't send this one */ if (orc == CONFNAK) { /* Nak this CI? */ if (reject_if_disagree) /* Getting fed up with sending NAKs? */ orc = CONFREJ; /* Get tough if so */ else { 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 */ } /* Need to move CI? */ if (ucp != cip) BCOPY(cip, ucp, cilen); /* Move it */ /* Update output pointer */ INCPTR(cilen, ucp); } /* * If we aren't rejecting this packet, and we want to negotiate * their address, and they didn't send their address, then we * send a NAK with a CI_ADDR option appended. We assume the * input buffer is long enough that we can append the extra * option safely. */ if (rc != CONFREJ && !ho->neg_addr && wo->req_addr && !reject_if_disagree) { if (rc == CONFACK) { rc = CONFNAK; ucp = inp; /* reset pointer */ wo->req_addr = 0; /* don't ask again */ } PUTCHAR(CI_ADDR, ucp); PUTCHAR(CILEN_ADDR, ucp); tl = wo->hisaddr; PUTLONG(tl, ucp); } *len = ucp - inp; /* Compute output length */ sprintf(tempstr,"ipcp: returning Configure-%s", CODENAME(rc)); IPCPDEBUG((LOG_INFO, tempstr)); return (rc); /* Return final code */ } /* * ipcp_up - IPCP has come UP. * * Configure the IP network interface appropriately and bring it up. * * This routine is called when the connection has * been OPENED. This means negotiation of IPCP options * has completed. This routine can then set the the IP * address of the connection etc. */ void ipcp_up(int unit) { /* Set IP address of connection, etc here */ } /* * ipcp_down - IPCP has gone DOWN. * * Take the IP network interface down, clear its addresses * and delete routes through it. * * This routine is called when the the connection has * left the OPENED state . This means IP can no longer be * run on the connection. The actual protocol will be * notified separately. */ void ipcp_down(int unit) { /* Do any actions that relate to stopping the connection here */ } /* * ipcp_script - Execute a script with arguments * interface-name tty-name speed local-IP remote-IP. */ void ipcp_script(int unit, char *script) { char strspeed[32], strlocal[32], strremote[32]; char *argv[8]; strcpy(strlocal, ip_ntoa(ipcp_gotoptions[IPCP_UNIT(unit)].ouraddr)); strcpy(strremote, ip_ntoa(ipcp_hisoptions[IPCP_UNIT(unit)].hisaddr)); argv[0] = script; /* argv[1] = ifname;*/ argv[2] = devname; argv[3] = strspeed; argv[4] = strlocal; argv[5] = strremote; argv[6] = 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(unsigned char *p, int plen, void (*printer)(), void *arg) { int code, id, len, olen; unsigned char *pstart, *optend; unsigned short cishort; unsigned 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(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(cilong)); GETLONG(cilong, p); printer(arg, " %s", ip_ntoa(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(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; }