summaryrefslogtreecommitdiff
path: root/os/ip/ppp.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/ip/ppp.c')
-rw-r--r--os/ip/ppp.c1656
1 files changed, 1656 insertions, 0 deletions
diff --git a/os/ip/ppp.c b/os/ip/ppp.c
new file mode 100644
index 00000000..73885adc
--- /dev/null
+++ b/os/ip/ppp.c
@@ -0,0 +1,1656 @@
+#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);
+}