ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /os/ip/ppp.c/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include <libcrypt.h>
#include <kernel.h>
#include "ip.h"
#include "ppp.h"
int nocompress;
Ipaddr pppdns[2];
/*
* Calculate FCS - rfc 1331
*/
ushort fcstab[256] =
{
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
static char *snames[] =
{
"Sclosed",
"Sclosing",
"Sreqsent",
"Sackrcvd",
"Sacksent",
"Sopened",
};
static void init(PPP*);
static void setphase(PPP*, int);
static void pinit(PPP*, Pstate*);
static void ppptimer(void*);
static void ptimer(PPP*, Pstate*);
static int getframe(PPP*, Block**);
static Block* putframe(PPP*, int, Block*);
static uchar* escapebyte(PPP*, ulong, uchar*, ushort*);
static void config(PPP*, Pstate*, int);
static int getopts(PPP*, Pstate*, Block*);
static void rejopts(PPP*, Pstate*, Block*, int);
static void newstate(PPP*, Pstate*, int);
static void rcv(PPP*, Pstate*, Block*);
static void getchap(PPP*, Block*);
static void getpap(PPP*, Block*);
static void sendpap(PPP*);
static void getlqm(PPP*, Block*);
static void putlqm(PPP*);
static void hangup(PPP*);
static void remove(PPP*);
static int validv4(Ipaddr);
static void invalidate(Ipaddr);
static void ipconnect(PPP *);
static void setdefroute(PPP *, Ipaddr);
static void printopts(PPP *, Pstate*, Block*, int);
static void sendtermreq(PPP*, Pstate*);
static void
errlog(PPP *ppp, char *err)
{
int n;
char msg[64];
n = snprint(msg, sizeof(msg), "%s\n", err);
qproduce(ppp->ifc->conv->eq, msg, n);
}
static void
init(PPP* ppp)
{
if(ppp->inbuf == nil){
ppp->inbuf = allocb(4096);
ppp->outbuf = allocb(4096);
ppp->lcp = malloc(sizeof(Pstate));
ppp->ipcp = malloc(sizeof(Pstate));
if(ppp->lcp == nil || ppp->ipcp == nil)
error("ppp init: malloc");
ppp->lcp->proto = Plcp;
ppp->lcp->state = Sclosed;
ppp->ipcp->proto = Pipcp;
ppp->ipcp->state = Sclosed;
kproc("ppptimer", ppptimer, ppp, KPDUPPG|KPDUPFDG);
}
pinit(ppp, ppp->lcp);
setphase(ppp, Plink);
}
static void
setphase(PPP *ppp, int phase)
{
int oldphase;
oldphase = ppp->phase;
ppp->phase = phase;
switch(phase){
default:
panic("ppp: unknown phase %d", phase);
case Pdead:
/* restart or exit? */
pinit(ppp, ppp->lcp);
setphase(ppp, Plink);
break;
case Plink:
/* link down */
switch(oldphase) {
case Pnet:
newstate(ppp, ppp->ipcp, Sclosed);
}
break;
case Pauth:
if(ppp->usepap)
sendpap(ppp);
else if(!ppp->usechap)
setphase(ppp, Pnet);
break;
case Pnet:
pinit(ppp, ppp->ipcp);
break;
case Pterm:
/* what? */
break;
}
}
static void
pinit(PPP *ppp, Pstate *p)
{
p->timeout = 0;
switch(p->proto){
case Plcp:
ppp->magic = TK2MS(MACHP(0)->ticks);
ppp->xctlmap = 0xffffffff;
ppp->period = 0;
p->optmask = 0xffffffff;
ppp->rctlmap = 0;
ppp->ipcp->state = Sclosed;
ppp->ipcp->optmask = 0xffffffff;
/* quality goo */
ppp->timeout = 0;
memset(&ppp->in, 0, sizeof(ppp->in));
memset(&ppp->out, 0, sizeof(ppp->out));
memset(&ppp->pin, 0, sizeof(ppp->pin));
memset(&ppp->pout, 0, sizeof(ppp->pout));
memset(&ppp->sin, 0, sizeof(ppp->sin));
break;
case Pipcp:
if(ppp->localfrozen == 0)
invalidate(ppp->local);
if(ppp->remotefrozen == 0)
invalidate(ppp->remote);
p->optmask = 0xffffffff;
ppp->ctcp = compress_init(ppp->ctcp);
ppp->usedns = 3;
invalidate(ppp->dns1);
invalidate(ppp->dns2);
break;
}
p->confid = p->rcvdconfid = -1;
config(ppp, p, 1);
newstate(ppp, p, Sreqsent);
}
/*
* change protocol to a new state.
*/
static void
newstate(PPP *ppp, Pstate *p, int state)
{
netlog(ppp->f, Logppp, "%ux %ux %s->%s ctlmap %lux/%lux flags %ux mtu %d mru %d\n", ppp, p->proto,
snames[p->state], snames[state], ppp->rctlmap, ppp->xctlmap, p->flags,
ppp->mtu, ppp->mru);
if(p->proto == Plcp) {
if(state == Sopened)
setphase(ppp, Pauth);
else if(state == Sclosed)
setphase(ppp, Pdead);
else if(p->state == Sopened)
setphase(ppp, Plink);
}
if(p->proto == Pipcp && state == Sopened && validv4(ppp->local) && validv4(ppp->remote)){
netlog(ppp->f, Logppp, "pppnewstate: local %I remote %I\n", ppp->local, ppp->remote);
ipmove(pppdns[0], ppp->dns1);
ipmove(pppdns[1], ppp->dns2);
ipconnect(ppp);
/* if this is the only network, set up a default route */
// if(ppp->ifc->link==nil) /* how??? */
setdefroute(ppp, ppp->remote);
errlog(ppp, Enoerror);
}
p->state = state;
}
static void
remove(PPP *ppp)
{
free(ppp->ipcp);
ppp->ipcp = 0;
free(ppp->ctcp);
ppp->ctcp = 0;
free(ppp->lcp);
ppp->lcp = 0;
if (ppp->inbuf) {
freeb(ppp->inbuf);
ppp->inbuf = nil;
}
if (ppp->outbuf) {
freeb(ppp->outbuf);
ppp->outbuf = nil;
}
free(ppp);
}
void
pppclose(PPP *ppp)
{
hangup(ppp);
remove(ppp);
}
static void
dumpblock(Block *b)
{
char x[256];
int i;
for(i = 0; i < (sizeof(x)-1)/3 && b->rp+i < b->wp; i++)
sprint(&x[3*i], "%2.2ux ", b->rp[i]);
print("%s\n", x);
}
/* returns (protocol, information) */
static int
getframe(PPP *ppp, Block **info)
{
uchar *p, *from, *to;
int n, len, proto;
ulong c;
ushort fcs;
Block *buf, *b;
buf = ppp->inbuf;
for(;;){
/* read till we hit a frame byte or run out of room */
for(p = buf->rp; buf->wp < buf->lim;){
for(; p < buf->wp; p++)
if(*p == HDLC_frame)
goto break2;
len = buf->lim - buf->wp;
n = 0;
if(ppp->dchan != nil)
n = kchanio(ppp->dchan, buf->wp, len, OREAD);
netlog(ppp->f, Logppp, "ppp kchanio %d bytes\n", n);
if(n <= 0){
buf->wp = buf->rp;
// if(n < 0)
// print("ppp kchanio(%s) returned %d: %r",
// ppp->dchan->path->elem, n);
*info = nil;
return 0;
}
buf->wp += n;
}
break2:
/* copy into block, undoing escapes, and caculating fcs */
fcs = PPP_initfcs;
b = allocb(p - buf->rp);
to = b->wp;
for(from = buf->rp; from != p;){
c = *from++;
if(c == HDLC_esc){
if(from == p)
break;
c = *from++ ^ 0x20;
} else if((c < 0x20) && (ppp->rctlmap & (1 << c)))
continue;
*to++ = c;
fcs = (fcs >> 8) ^ fcstab[(fcs ^ c) & 0xff];
}
/* copy down what's left in buffer */
p++;
memmove(buf->rp, p, buf->wp - p);
n = p - buf->rp;
buf->wp -= n;
b->wp = to - 2;
/* return to caller if checksum matches */
if(fcs == PPP_goodfcs){
if(b->rp[0] == PPP_addr && b->rp[1] == PPP_ctl)
b->rp += 2;
proto = *b->rp++;
if((proto & 0x1) == 0)
proto = (proto<<8) | *b->rp++;
if(b->rp < b->wp){
ppp->in.bytes += n;
ppp->in.packets++;
*info = b;
return proto;
}
} else if(BLEN(b) > 0){
ppp->ifc->inerr++;
ppp->in.discards++;
netlog(ppp->f, Logppp, "len %d/%d cksum %ux (%ux %ux %ux %ux)\n",
BLEN(b), BLEN(buf), fcs, b->rp[0],
b->rp[1], b->rp[2], b->rp[3]);
}
freeblist(b);
}
*info = nil;
return 0;
}
/* send a PPP frame */
static Block *
putframe(PPP *ppp, int proto, Block *b)
{
Block *buf;
uchar *to, *from;
ushort fcs;
ulong ctlmap;
int c;
Block *bp;
if(ppp->dchan == nil){
netlog(ppp->f, Logppp, "putframe: dchan down\n");
errlog(ppp, Ehungup);
return b;
}
netlog(ppp->f, Logppp, "putframe %ux %d %d (%d bytes)\n", proto, b->rp[0], b->rp[1], BLEN(b));
ppp->out.packets++;
if(proto == Plcp)
ctlmap = 0xffffffff;
else
ctlmap = ppp->xctlmap;
/* make sure we have head room */
if(b->rp - b->base < 4){
b = padblock(b, 4);
b->rp += 4;
}
/* add in the protocol and address, we'd better have left room */
from = b->rp;
*--from = proto;
if(!(ppp->lcp->flags&Fpc) || proto > 0x100 || proto == Plcp)
*--from = proto>>8;
if(!(ppp->lcp->flags&Fac) || proto == Plcp){
*--from = PPP_ctl;
*--from = PPP_addr;
}
qlock(&ppp->outlock);
buf = ppp->outbuf;
/* escape and checksum the body */
fcs = PPP_initfcs;
to = buf->rp;
*to++ = HDLC_frame;
for(bp = b; bp; bp = bp->next){
if(bp != b)
from = bp->rp;
for(; from < bp->wp; from++){
c = *from;
if(c == HDLC_frame || c == HDLC_esc
|| (c < 0x20 && ((1<<c) & ctlmap))){
*to++ = HDLC_esc;
*to++ = c ^ 0x20;
} else
*to++ = c;
fcs = (fcs >> 8) ^ fcstab[(fcs ^ c) & 0xff];
}
}
/* add on and escape the checksum */
fcs = ~fcs;
c = fcs;
if(c == HDLC_frame || c == HDLC_esc
|| (c < 0x20 && ((1<<c) & ctlmap))){
*to++ = HDLC_esc;
*to++ = c ^ 0x20;
} else
*to++ = c;
c = fcs>>8;
if(c == HDLC_frame || c == HDLC_esc
|| (c < 0x20 && ((1<<c) & ctlmap))){
*to++ = HDLC_esc;
*to++ = c ^ 0x20;
} else
*to++ = c;
/* add frame marker and send */
*to++ = HDLC_frame;
buf->wp = to;
if(ppp->dchan == nil){
netlog(ppp->f, Logppp, "putframe: dchan down\n");
errlog(ppp, Ehungup);
}else{
kchanio(ppp->dchan, buf->rp, BLEN(buf), OWRITE);
ppp->out.bytes += BLEN(buf);
}
qunlock(&ppp->outlock);
return b;
}
#define IPB2LCP(b) ((Lcpmsg*)((b)->wp-4))
static Block*
alloclcp(int code, int id, int len)
{
Block *b;
Lcpmsg *m;
/*
* leave room for header
*/
b = allocb(len);
m = (Lcpmsg*)b->wp;
m->code = code;
m->id = id;
b->wp += 4;
return b;
}
static void
putao(Block *b, int type, int aproto, int alg)
{
*b->wp++ = type;
*b->wp++ = 5;
hnputs(b->wp, aproto);
b->wp += 2;
*b->wp++ = alg;
}
static void
putlo(Block *b, int type, ulong val)
{
*b->wp++ = type;
*b->wp++ = 6;
hnputl(b->wp, val);
b->wp += 4;
}
static void
putv4o(Block *b, int type, Ipaddr val)
{
*b->wp++ = type;
*b->wp++ = 6;
if(v6tov4(b->wp, val) < 0){
/*panic("putv4o")*/;
}
b->wp += 4;
}
static void
putso(Block *b, int type, ulong val)
{
*b->wp++ = type;
*b->wp++ = 4;
hnputs(b->wp, val);
b->wp += 2;
}
static void
puto(Block *b, int type)
{
*b->wp++ = type;
*b->wp++ = 2;
}
/*
* send configuration request
*/
static void
config(PPP *ppp, Pstate *p, int newid)
{
Block *b;
Lcpmsg *m;
int id;
if(newid){
id = ++(p->id);
p->confid = id;
p->timeout = Timeout;
} else
id = p->confid;
b = alloclcp(Lconfreq, id, 256);
m = IPB2LCP(b);
USED(m);
switch(p->proto){
case Plcp:
if(p->optmask & Fmagic)
putlo(b, Omagic, ppp->magic);
if(p->optmask & Fmtu)
putso(b, Omtu, ppp->mru);
if(p->optmask & Fac)
puto(b, Oac);
if(p->optmask & Fpc)
puto(b, Opc);
if(p->optmask & Fctlmap)
putlo(b, Octlmap, 0); /* we don't want anything escaped */
break;
case Pipcp:
if((p->optmask & Fipaddr) /*&& validv4(ppp->local)*/)
putv4o(b, Oipaddr, ppp->local);
if(!nocompress && (p->optmask & Fipcompress)){
*b->wp++ = Oipcompress;
*b->wp++ = 6;
hnputs(b->wp, Pvjctcp);
b->wp += 2;
*b->wp++ = MAX_STATES-1;
*b->wp++ = 1;
}
if(ppp->usedns & 1)
putlo(b, Oipdns, 0);
if(ppp->usedns & 2)
putlo(b, Oipdns2, 0);
break;
}
hnputs(m->len, BLEN(b));
b = putframe(ppp, p->proto, b);
freeblist(b);
}
/*
* parse configuration request, sends an ack or reject packet
*
* returns: -1 if request was syntacticly incorrect
* 0 if packet was accepted
* 1 if packet was rejected
*/
static int
getopts(PPP *ppp, Pstate *p, Block *b)
{
Lcpmsg *m, *repm;
Lcpopt *o;
uchar *cp;
ulong rejecting, nacking, flags, proto;
ulong mtu, ctlmap, period;
ulong x;
Block *repb;
Ipaddr ipaddr;
rejecting = 0;
nacking = 0;
flags = 0;
/* defaults */
invalidate(ipaddr);
mtu = ppp->mtu;
ctlmap = 0xffffffff;
period = 0;
m = (Lcpmsg*)b->rp;
repb = alloclcp(Lconfack, m->id, BLEN(b));
repm = IPB2LCP(repb);
/* copy options into ack packet */
memmove(repm->data, m->data, b->wp - m->data);
repb->wp += b->wp - m->data;
/* look for options we don't recognize or like */
for(cp = m->data; cp < b->wp; cp += o->len){
o = (Lcpopt*)cp;
if(cp + o->len > b->wp || o->len == 0){
freeblist(repb);
netlog(ppp->f, Logppp, "ppp %s: bad option length %ux\n", ppp->ifc->dev,
o->type);
return -1;
}
switch(p->proto){
case Plcp:
switch(o->type){
case Oac:
flags |= Fac;
continue;
case Opc:
flags |= Fpc;
continue;
case Omtu:
mtu = nhgets(o->data);
if(mtu < ppp->ifc->m->mintu){
netlog(ppp->f, Logppp, "bogus mtu %d\n", mtu);
mtu = ppp->ifc->m->mintu;
}
continue;
case Omagic:
if(ppp->magic == nhgetl(o->data))
netlog(ppp->f, Logppp, "ppp: possible loop\n");
continue;
case Octlmap:
ctlmap = nhgetl(o->data);
continue;
case Oquality:
proto = nhgets(o->data);
if(proto != Plqm)
break;
x = nhgetl(o->data+2)*10;
period = (x+Period-1)/Period;
continue;
case Oauth:
proto = nhgets(o->data);
if(proto == Ppap && ppp->chapname[0] && ppp->secret[0]){
ppp->usepap = 1;
netlog(ppp->f, Logppp, "PPP %s: select PAP\n", ppp->ifc->dev);
continue;
}
if(proto != Pchap || o->data[2] != APmd5){
if(!nacking){
nacking = 1;
repb->wp = repm->data;
repm->code = Lconfnak;
}
putao(repb, Oauth, Pchap, APmd5);
}
else
ppp->usechap = 1;
ppp->usepap = 0;
continue;
}
break;
case Pipcp:
switch(o->type){
case Oipaddr:
v4tov6(ipaddr, o->data);
if(!validv4(ppp->remote))
continue;
if(!validv4(ipaddr) && !rejecting){
/* other side requesting an address */
if(!nacking){
nacking = 1;
repb->wp = repm->data;
repm->code = Lconfnak;
}
putv4o(repb, Oipaddr, ppp->remote);
}
continue;
case Oipcompress:
proto = nhgets(o->data);
if(nocompress || proto != Pvjctcp || compress_negotiate(ppp->ctcp, o->data+2) < 0)
break;
flags |= Fipcompress;
continue;
}
break;
}
/* come here if option is not recognized */
if(!rejecting){
rejecting = 1;
repb->wp = repm->data;
repm->code = Lconfrej;
}
netlog(ppp->f, Logppp, "ppp %s: bad %ux option %d\n", ppp->ifc->dev, p->proto, o->type);
memmove(repb->wp, o, o->len);
repb->wp += o->len;
}
/* permanent changes only after we know that we liked the packet */
if(!rejecting && !nacking){
switch(p->proto){
case Plcp:
netlog(ppp->f, Logppp, "Plcp: mtu: %d %d x:%lux/r:%lux %lux\n", mtu, ppp->mtu, ppp->xctlmap, ppp->rctlmap, ctlmap);
ppp->period = period;
ppp->xctlmap = ctlmap;
if(mtu > Maxmtu)
mtu = Maxmtu;
if(mtu < Minmtu)
mtu = Minmtu;
ppp->mtu = mtu;
break;
case Pipcp:
if(validv4(ipaddr) && ppp->remotefrozen == 0)
ipmove(ppp->remote, ipaddr);
break;
}
p->flags = flags;
}
hnputs(repm->len, BLEN(repb));
repb = putframe(ppp, p->proto, repb);
freeblist(repb);
return rejecting || nacking;
}
/*
* parse configuration rejection, just stop sending anything that they
* don't like (except for ipcp address nak).
*/
static void
rejopts(PPP *ppp, Pstate *p, Block *b, int code)
{
Lcpmsg *m;
Lcpopt *o;
/* just give up trying what the other side doesn't like */
m = (Lcpmsg*)b->rp;
for(b->rp = m->data; b->rp < b->wp; b->rp += o->len){
o = (Lcpopt*)b->rp;
if(b->rp + o->len > b->wp || o->len == 0){
netlog(ppp->f, Logppp, "ppp %s: bad roption length %ux\n", ppp->ifc->dev,
o->type);
return;
}
if(code == Lconfrej){
if(o->type < 8*sizeof(p->optmask))
p->optmask &= ~(1<<o->type);
if(o->type == Oipdns)
ppp->usedns &= ~1;
else if(o->type == Oipdns2)
ppp->usedns &= ~2;
netlog(ppp->f, Logppp, "ppp %s: %ux rejecting %d\n", ppp->ifc->dev, p->proto,
o->type);
continue;
}
switch(p->proto){
case Plcp:
switch(o->type){
case Octlmap:
ppp->rctlmap = nhgetl(o->data);
break;
default:
if(o->type < 8*sizeof(p->optmask))
p->optmask &= ~(1<<o->type);
break;
};
case Pipcp:
switch(o->type){
case Oipaddr:
if(!validv4(ppp->local))
v4tov6(ppp->local, o->data);
// if(o->type < 8*sizeof(p->optmask))
// p->optmask &= ~(1<<o->type);
break;
case Oipdns:
if(!validv4(ppp->dns1))
v4tov6(ppp->dns1, o->data);
ppp->usedns &= ~1;
break;
case Oipdns2:
if(!validv4(ppp->dns2))
v4tov6(ppp->dns2, o->data);
ppp->usedns &= ~2;
break;
default:
if(o->type < 8*sizeof(p->optmask))
p->optmask &= ~(1<<o->type);
break;
}
break;
}
}
}
/*
* put a messages through the lcp or ipcp state machine. They are
* very similar.
*/
static void
rcv(PPP *ppp, Pstate *p, Block *b)
{
ulong len;
int err;
Lcpmsg *m;
if(BLEN(b) < 4){
netlog(ppp->f, Logppp, "ppp %s: short lcp message\n", ppp->ifc->dev);
freeblist(b);
return;
}
m = (Lcpmsg*)b->rp;
len = nhgets(m->len);
if(BLEN(b) < len){
netlog(ppp->f, Logppp, "ppp %s: short lcp message\n", ppp->ifc->dev);
freeblist(b);
return;
}
netlog(ppp->f, Logppp, "ppp: %ux rcv %d len %d id %d/%d/%d\n",
p->proto, m->code, len, m->id, p->confid, p->id);
if(p->proto != Plcp && ppp->lcp->state != Sopened){
netlog(ppp->f, Logppp, "ppp: non-lcp with lcp not open\n");
freeb(b);
return;
}
qlock(ppp);
switch(m->code){
case Lconfreq:
/* flush the output queue */
if(p->state == Sopened && p->proto == Plcp)
kchanio(ppp->cchan, "f", 1, OWRITE);
printopts(ppp, p, b, 0);
err = getopts(ppp, p, b);
if(err < 0)
break;
if(m->id == p->rcvdconfid)
break; /* don't change state for duplicates */
p->rcvdconfid = m->id;
switch(p->state){
case Sackrcvd:
if(err)
break;
newstate(ppp, p, Sopened);
break;
case Sclosed:
case Sopened:
config(ppp, p, 1);
if(err == 0)
newstate(ppp, p, Sacksent);
else
newstate(ppp, p, Sreqsent);
break;
break;
case Sreqsent:
case Sacksent:
if(err == 0)
newstate(ppp, p, Sacksent);
else
newstate(ppp, p, Sreqsent);
break;
}
break;
case Lconfack:
if(p->confid != m->id){
/* ignore if it isn't the message we're sending */
netlog(ppp->f, Logppp, "ppp: dropping confack\n");
break;
}
p->confid = -1; /* ignore duplicates */
p->id++; /* avoid sending duplicates */
switch(p->state){
case Sopened:
case Sackrcvd:
config(ppp, p, 1);
newstate(ppp, p, Sreqsent);
break;
case Sreqsent:
newstate(ppp, p, Sackrcvd);
break;
case Sacksent:
newstate(ppp, p, Sopened);
break;
}
break;
case Lconfrej:
case Lconfnak:
if(p->confid != m->id) {
/* ignore if it isn't the message we're sending */
netlog(ppp->f, Logppp, "ppp: dropping confrej or confnak\n");
break;
}
p->confid = -1; /* ignore duplicates */
p->id++; /* avoid sending duplicates */
switch(p->state){
case Sopened:
case Sackrcvd:
config(ppp, p, 1);
newstate(ppp, p, Sreqsent);
break;
case Sreqsent:
case Sacksent:
printopts(ppp, p, b, 0);
rejopts(ppp, p, b, m->code);
config(ppp, p, 1);
break;
}
break;
case Ltermreq:
m->code = Ltermack;
b = putframe(ppp, p->proto, b);
switch(p->state){
case Sackrcvd:
case Sacksent:
newstate(ppp, p, Sreqsent);
break;
case Sopened:
newstate(ppp, p, Sclosing);
break;
}
break;
case Ltermack:
if(p->termid != m->id) /* ignore if it isn't the message we're sending */
break;
if(p->proto == Plcp)
ppp->ipcp->state = Sclosed;
switch(p->state){
case Sclosing:
newstate(ppp, p, Sclosed);
break;
case Sackrcvd:
newstate(ppp, p, Sreqsent);
break;
case Sopened:
config(ppp, p, 0);
newstate(ppp, p, Sreqsent);
break;
}
break;
case Lcoderej:
netlog(ppp->f, Logppp, "ppp %s: code reject %d\n", ppp->ifc->dev, m->data[0]);
break;
case Lprotorej:
netlog(ppp->f, Logppp, "ppp %s: proto reject %lux\n", ppp->ifc->dev, nhgets(m->data));
break;
case Lechoreq:
m->code = Lechoack;
b = putframe(ppp, p->proto, b);
break;
case Lechoack:
case Ldiscard:
/* nothing to do */
break;
}
qunlock(ppp);
freeblist(b);
}
/*
* timer for protocol state machine
*/
static void
ptimer(PPP *ppp, Pstate *p)
{
if(p->state == Sopened || p->state == Sclosed)
return;
p->timeout--;
switch(p->state){
case Sclosing:
sendtermreq(ppp, p);
break;
case Sreqsent:
case Sacksent:
if(p->timeout <= 0){
if(p->proto && ppp->cchan != nil)
kchanio(ppp->cchan, "f", 1, OWRITE); /* flush output queue */
newstate(ppp, p, Sclosed);
} else {
config(ppp, p, 0);
}
break;
case Sackrcvd:
if(p->timeout <= 0){
if(p->proto && ppp->cchan != nil)
kchanio(ppp->cchan, "f", 1, OWRITE); /* flush output queue */
newstate(ppp, p, Sclosed);
}
else {
config(ppp, p, 0);
newstate(ppp, p, Sreqsent);
}
break;
}
}
/*
* timer for ppp
*/
static void
ppptimer(void *arg)
{
PPP *ppp;
ppp = arg;
ppp->timep = up;
if(waserror()){
netlog(ppp->f, Logppp, "ppptimer: %I: %s\n", ppp->local, up->env->errstr);
ppp->timep = 0;
pexit("hangup", 1);
}
for(;;){
tsleep(&up->sleep, return0, nil, Period);
if(ppp->pppup){
qlock(ppp);
ptimer(ppp, ppp->lcp);
if(ppp->lcp->state == Sopened)
ptimer(ppp, ppp->ipcp);
if(ppp->period && --(ppp->timeout) <= 0){
ppp->timeout = ppp->period;
putlqm(ppp);
}
qunlock(ppp);
}
}
}
static void
setdefroute(PPP *ppp, Ipaddr gate)
{
int fd, n;
char path[128], msg[128];
snprint(path, sizeof path, "#I%d/iproute", ppp->f->dev);
fd = kopen(path, ORDWR);
if(fd < 0)
return;
n = snprint(msg, sizeof(msg), "add 0 0 %I", gate);
kwrite(fd, msg, n);
kclose(fd);
}
static void
ipconnect(PPP *ppp)
{
int fd, n;
char path[128], msg[128];
snprint(path, sizeof path, "#I%d/ipifc/%d/ctl", ppp->f->dev, ppp->ifc->conv->x);
fd = kopen(path, ORDWR);
if(fd < 0)
return;
n = snprint(msg, sizeof(msg), "connect %I 255.255.255.255 %I", ppp->local, ppp->remote);
if (kwrite(fd, msg, n) != n)
print("ppp ipconnect: %s: %r\n", msg);
kclose(fd);
}
PPP*
pppopen(PPP *ppp, char *dev,
Ipaddr ipaddr, Ipaddr remip,
int mtu, int framing,
char *chapname, char *secret)
{
int fd, cfd;
char ctl[Maxpath];
invalidate(ppp->remote);
invalidate(ppp->local);
invalidate(ppp->dns1);
invalidate(ppp->dns2);
ppp->mtu = Defmtu;
ppp->mru = mtu;
ppp->framing = framing;
if(remip != nil && validv4(remip)){
ipmove(ppp->remote, remip);
ppp->remotefrozen = 1;
}
if(ipaddr != nil && validv4(ipaddr)){
ipmove(ppp->local, ipaddr);
ppp->localfrozen = 1;
}
/* authentication goo */
ppp->secret[0] = 0;
if(secret != nil)
strncpy(ppp->secret, secret, sizeof(ppp->secret));
ppp->chapname[0] = 0;
if(chapname != nil)
strncpy(ppp->chapname, chapname, sizeof(ppp->chapname));
if(strchr(dev, '!'))
fd = kdial(dev, nil, nil, nil);
else
fd = kopen(dev, ORDWR);
if(fd < 0){
netlog(ppp->f, Logppp, "ppp: can't open %s\n", dev);
return nil;
}
ppp->dchan = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
kclose(fd);
/* set up serial line */
/* XXX this stuff belongs in application, not driver */
sprint(ctl, "%sctl", dev);
cfd = kopen(ctl, ORDWR);
if(cfd >= 0){
ppp->cchan = fdtochan(up->env->fgrp, cfd, ORDWR, 0, 1);
kclose(cfd);
kchanio(ppp->cchan, "m1", 2, OWRITE); /* cts/rts flow control/fifo's) on */
kchanio(ppp->cchan, "q64000", 6, OWRITE);/* increas q size to 64k */
kchanio(ppp->cchan, "n1", 2, OWRITE); /* nonblocking writes on */
kchanio(ppp->cchan, "r1", 2, OWRITE); /* rts on */
kchanio(ppp->cchan, "d1", 2, OWRITE); /* dtr on */
}
ppp->pppup = 1;
init(ppp);
return ppp;
}
static void
hangup(PPP *ppp)
{
qlock(ppp);
if(waserror()){
qunlock(ppp);
nexterror();
}
netlog(ppp->f, Logppp, "PPP Hangup\n");
errlog(ppp, Ehungup);
if(ppp->pppup && ppp->cchan != nil){
kchanio(ppp->cchan, "f", 1, OWRITE); /* flush */
kchanio(ppp->cchan, "h", 1, OWRITE); /* hangup */
}
cclose(ppp->dchan);
cclose(ppp->cchan);
ppp->dchan = nil;
ppp->cchan = nil;
ppp->pppup = 0;
qunlock(ppp);
poperror();
}
/* return next input IP packet */
Block*
pppread(PPP *ppp)
{
Block *b;
int proto;
Lcpmsg *m;
for(;;){
proto = getframe(ppp, &b);
if(b == nil)
return nil;
netlog(ppp->f, Logppp, "ppp: read proto %d len %d\n", proto, blocklen(b));
switch(proto){
case Plcp:
rcv(ppp, ppp->lcp, b);
break;
case Pipcp:
rcv(ppp, ppp->ipcp, b);
break;
case Pip:
if(ppp->ipcp->state == Sopened)
return b;
freeblist(b);
break;
case Plqm:
getlqm(ppp, b);
break;
case Pchap:
getchap(ppp, b);
break;
case Ppap:
getpap(ppp, b);
break;
case Pvjctcp:
case Pvjutcp:
if(ppp->ipcp->state == Sopened){
b = tcpuncompress(ppp->ctcp, b, proto, ppp->f);
if(b != nil)
return b;
}
freeblist(b);
break;
default:
netlog(ppp->f, Logppp, "unknown proto %ux\n", proto);
if(ppp->lcp->state == Sopened){
/* reject the protocol */
b->rp -= 6;
m = (Lcpmsg*)b->rp;
m->code = Lprotorej;
m->id = ++ppp->lcp->id;
hnputs(m->data, proto);
hnputs(m->len, BLEN(b));
b = putframe(ppp, Plcp, b);
}
freeblist(b);
break;
}
}
return nil; /* compiler confused */
}
/* transmit an IP packet */
int
pppwrite(PPP *ppp, Block *b)
{
ushort proto;
int r;
qlock(ppp);
/* can't send ip packets till we're established */
if(ppp->ipcp->state != Sopened)
goto ret;
/* link hung up */
if(ppp->dchan == nil)
goto ret;
b = concatblock(b); /* or else compression will barf */
proto = Pip;
if(ppp->ipcp->flags & Fipcompress)
proto = compress(ppp->ctcp, b, ppp->f);
b = putframe(ppp, proto, b);
ret:
qunlock(ppp);
r = blocklen(b);
netlog(ppp->f, Logppp, "ppp wrt len %d\n", r);
freeblist(b);
return r;
}
/*
* link quality management
*/
static void
getlqm(PPP *ppp, Block *b)
{
Qualpkt *p;
p = (Qualpkt*)b->rp;
if(BLEN(b) == sizeof(Qualpkt)){
ppp->in.reports++;
ppp->pout.reports = nhgetl(p->peeroutreports);
ppp->pout.packets = nhgetl(p->peeroutpackets);
ppp->pout.bytes = nhgetl(p->peeroutbytes);
ppp->pin.reports = nhgetl(p->peerinreports);
ppp->pin.packets = nhgetl(p->peerinpackets);
ppp->pin.discards = nhgetl(p->peerindiscards);
ppp->pin.errors = nhgetl(p->peerinerrors);
ppp->pin.bytes = nhgetl(p->peerinbytes);
/* save our numbers at time of reception */
memmove(&ppp->sin, &ppp->in, sizeof(Qualstats));
}
freeblist(b);
if(ppp->period == 0)
putlqm(ppp);
}
static void
putlqm(PPP *ppp)
{
Qualpkt *p;
Block *b;
b = allocb(sizeof(Qualpkt));
b->wp += sizeof(Qualpkt);
p = (Qualpkt*)b->rp;
hnputl(p->magic, 0);
/* heresay (what he last told us) */
hnputl(p->lastoutreports, ppp->pout.reports);
hnputl(p->lastoutpackets, ppp->pout.packets);
hnputl(p->lastoutbytes, ppp->pout.bytes);
/* our numbers at time of last reception */
hnputl(p->peerinreports, ppp->sin.reports);
hnputl(p->peerinpackets, ppp->sin.packets);
hnputl(p->peerindiscards, ppp->sin.discards);
hnputl(p->peerinerrors, ppp->sin.errors);
hnputl(p->peerinbytes, ppp->sin.bytes);
/* our numbers now */
hnputl(p->peeroutreports, ppp->out.reports+1);
hnputl(p->peeroutpackets, ppp->out.packets+1);
hnputl(p->peeroutbytes, ppp->out.bytes+53/*hack*/);
b = putframe(ppp, Plqm, b);
freeblist(b);
ppp->out.reports++;
}
/*
* challenge response dialog
*/
static void
getchap(PPP *ppp, Block *b)
{
Lcpmsg *m;
int len, vlen, n;
char md5buf[512];
m = (Lcpmsg*)b->rp;
len = nhgets(m->len);
if(BLEN(b) < len){
netlog(ppp->f, Logppp, "ppp %s: short chap message\n", ppp->ifc->dev);
freeblist(b);
return;
}
switch(m->code){
case Cchallenge:
vlen = m->data[0];
if(vlen > len - 5){
netlog(ppp->f, Logppp, "PPP %s: bad challenge len\n", ppp->ifc->dev);
freeblist(b);
break;
}
netlog(ppp->f, Logppp, "PPP %s: CHAP Challenge\n", ppp->ifc->dev);
netlog(ppp->f, Logppp, "(secret %s chapname %s id %d)\n", ppp->secret, ppp->chapname, m->id);
/* create string to hash */
md5buf[0] = m->id;
strcpy(md5buf+1, ppp->secret);
n = strlen(ppp->secret) + 1;
memmove(md5buf+n, m->data+1, vlen);
n += vlen;
freeblist(b);
/* send reply */
len = 4 + 1 + 16 + strlen(ppp->chapname);
b = alloclcp(2, md5buf[0], len);
m = IPB2LCP(b);
m->data[0] = 16;
md5((uchar*)md5buf, n, m->data+1, 0);
memmove((char*)m->data+17, ppp->chapname, strlen(ppp->chapname));
hnputs(m->len, len);
b->wp += len-4;
b = putframe(ppp, Pchap, b);
break;
case Cresponse:
netlog(ppp->f, Logppp, "PPP %s: chap response?\n", ppp->ifc->dev);
break;
case Csuccess:
netlog(ppp->f, Logppp, "PPP %s: chap succeeded\n", ppp->ifc->dev);
setphase(ppp, Pnet);
break;
case Cfailure:
netlog(ppp->f, Logppp, "PPP %s: chap failed: %.*s\n", ppp->ifc->dev, len-4, m->data);
errlog(ppp, Eperm);
break;
default:
netlog(ppp->f, Logppp, "PPP %s: chap code %d?\n", ppp->ifc->dev, m->code);
break;
}
freeblist(b);
}
/*
* password authentication protocol dialog
* -- obsolete but all we know how to use with NT just now
*/
static void
sendpap(PPP *ppp)
{
Lcpmsg *m;
int clen, slen, len;
Block *b;
uchar *p;
clen = strlen(ppp->chapname);
slen = strlen(ppp->secret);
len = 4 + 1 + clen + 1 + slen;
ppp->papid = ++ppp->lcp->id;
b = alloclcp(Cpapreq, ppp->papid, len);
m = IPB2LCP(b);
p = m->data;
p[0] = clen;
memmove(p+1, ppp->chapname, clen);
p += clen + 1;
p[0] = slen;
memmove(p+1, ppp->secret, slen);
hnputs(m->len, len);
b->wp += len-4;
b = putframe(ppp, Ppap, b);
netlog(ppp->f, Logppp, "PPP %s: sent pap auth req (%d)\n", ppp->ifc->dev, len);
freeblist(b);
}
static void
getpap(PPP *ppp, Block *b)
{
Lcpmsg *m;
int len;
m = (Lcpmsg*)b->rp;
len = nhgets(m->len);
if(BLEN(b) < len){
netlog(ppp->f, Logppp, "ppp %s: short pap message\n", ppp->ifc->dev);
freeblist(b);
return;
}
switch(m->code){
case Cpapreq:
netlog(ppp->f, Logppp, "PPP %s: pap request?\n", ppp->ifc->dev);
break;
case Cpapack:
netlog(ppp->f, Logppp, "PPP %s: PAP succeeded\n", ppp->ifc->dev);
setphase(ppp, Pnet);
break;
case Cpapnak:
if(m->data[0])
netlog(ppp->f, Logppp, "PPP %s: PAP failed: %.*s\n", ppp->ifc->dev, len-5, m->data+1);
else
netlog(ppp->f, Logppp, "PPP %s: PAP failed\n", ppp->ifc->dev);
errlog(ppp, Eperm);
break;
default:
netlog(ppp->f, Logppp, "PPP %s: pap code %d?\n", ppp->ifc->dev, m->code);
break;
}
freeblist(b);
}
static void
printopts(PPP *ppp, Pstate *p, Block *b, int send)
{
Lcpmsg *m;
Lcpopt *o;
int proto, x, period;
uchar *cp;
char *code, *dir;
m = (Lcpmsg*)b->rp;
switch(m->code) {
default: code = "<unknown>"; break;
case Lconfreq: code = "confrequest"; break;
case Lconfack: code = "confack"; break;
case Lconfnak: code = "confnak"; break;
case Lconfrej: code = "confreject"; break;
}
if(send)
dir = "send";
else
dir = "recv";
netlog(ppp->f, Logppp, "ppp: %s %s: id=%d\n", dir, code, m->id);
for(cp = m->data; cp < b->wp; cp += o->len){
o = (Lcpopt*)cp;
if(cp + o->len > b->wp || o->len == 0){
netlog(ppp->f, Logppp, "\tbad option length %ux\n", o->type);
return;
}
switch(p->proto){
case Plcp:
switch(o->type){
default:
netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len);
break;
case Omtu:
netlog(ppp->f, Logppp, "\tmtu = %d\n", nhgets(o->data));
break;
case Octlmap:
netlog(ppp->f, Logppp, "\tctlmap = %ux\n", nhgetl(o->data));
break;
case Oauth:
netlog(ppp->f, Logppp, "\tauth = ", nhgetl(o->data));
proto = nhgets(o->data);
switch(proto) {
default:
netlog(ppp->f, Logppp, "unknown auth proto %d\n", proto);
break;
case Ppap:
netlog(ppp->f, Logppp, "password\n");
break;
case Pchap:
netlog(ppp->f, Logppp, "chap %ux\n", o->data[2]);
break;
}
break;
case Oquality:
proto = nhgets(o->data);
switch(proto) {
default:
netlog(ppp->f, Logppp, "\tunknown quality proto %d\n", proto);
break;
case Plqm:
x = nhgetl(o->data+2)*10;
period = (x+Period-1)/Period;
netlog(ppp->f, Logppp, "\tlqm period = %d\n", period);
break;
}
case Omagic:
netlog(ppp->f, Logppp, "\tmagic = %ux\n", nhgetl(o->data));
break;
case Opc:
netlog(ppp->f, Logppp, "\tprotocol compress\n");
break;
case Oac:
netlog(ppp->f, Logppp, "\taddr compress\n");
break;
}
break;
case Pccp:
switch(o->type){
default:
netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len);
break;
case Ocoui:
netlog(ppp->f, Logppp, "\tOUI\n");
break;
case Ocstac:
netlog(ppp->f, Logppp, "\tstac LZS\n");
break;
case Ocmppc:
netlog(ppp->f, Logppp, "\tMicrosoft PPC len=%d %ux\n", o->len, nhgetl(o->data));
break;
}
break;
case Pecp:
switch(o->type){
default:
netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len);
break;
case Oeoui:
netlog(ppp->f, Logppp, "\tOUI\n");
break;
case Oedese:
netlog(ppp->f, Logppp, "\tDES\n");
break;
}
break;
case Pipcp:
switch(o->type){
default:
netlog(ppp->f, Logppp, "\tunknown %d len=%d\n", o->type, o->len);
break;
case Oipaddrs:
netlog(ppp->f, Logppp, "\tip addrs - deprecated\n");
break;
case Oipcompress:
netlog(ppp->f, Logppp, "\tip compress\n");
break;
case Oipaddr:
netlog(ppp->f, Logppp, "\tip addr %V\n", o->data);
break;
case Oipdns:
netlog(ppp->f, Logppp, "\tdns addr %V\n", o->data);
break;
case Oipwins:
netlog(ppp->f, Logppp, "\twins addr %V\n", o->data);
break;
case Oipdns2:
netlog(ppp->f, Logppp, "\tdns2 addr %V\n", o->data);
break;
case Oipwins2:
netlog(ppp->f, Logppp, "\twins2 addr %V\n", o->data);
break;
}
break;
}
}
}
static void
sendtermreq(PPP *ppp, Pstate *p)
{
Block *b;
Lcpmsg *m;
p->termid = ++(p->id);
b = alloclcp(Ltermreq, p->termid, 4);
m = IPB2LCP(b);
hnputs(m->len, 4);
putframe(ppp, p->proto, b);
freeb(b);
newstate(ppp, p, Sclosing);
}
static void
sendechoreq(PPP *ppp, Pstate *p)
{
Block *b;
Lcpmsg *m;
p->termid = ++(p->id);
b = alloclcp(Lechoreq, p->id, 4);
m = IPB2LCP(b);
hnputs(m->len, 4);
putframe(ppp, p->proto, b);
freeb(b);
}
/*
* return non-zero if this is a valid v4 address
*/
static int
validv4(Ipaddr addr)
{
return memcmp(addr, v4prefix, IPv4off) == 0;
}
static void
invalidate(Ipaddr addr)
{
ipmove(addr, IPnoaddr);
}