ftp.nice.ch/pub/next/unix/communication/Alby.2.PPP.0.3.N.bs.tar.gz#/source/ipcp.c

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.