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.