|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <ip.h> | 
|  | #include <libsec.h> | 
|  | #include "dat.h" | 
|  | #include "protos.h" | 
|  |  | 
|  | /* PPP stuff */ | 
|  | enum { | 
|  | PPP_addr=	0xff, | 
|  | PPP_ctl=	0x3, | 
|  | PPP_period=	3*1000,	/* period of retransmit process (in ms) */ | 
|  | }; | 
|  |  | 
|  | /* PPP protocols */ | 
|  | enum { | 
|  | PPP_ip=		0x21,		/* internet */ | 
|  | PPP_vjctcp=	0x2d,		/* compressing van jacobson tcp */ | 
|  | PPP_vjutcp=	0x2f,		/* uncompressing van jacobson tcp */ | 
|  | PPP_ml=		0x3d,		/* multi link */ | 
|  | PPP_comp=	0xfd,		/* compressed packets */ | 
|  | PPP_ipcp=	0x8021,		/* ip control */ | 
|  | PPP_ccp=	0x80fd,		/* compression control */ | 
|  | PPP_passwd=	0xc023,		/* passwd authentication */ | 
|  | PPP_lcp=	0xc021,		/* link control */ | 
|  | PPP_lqm=	0xc025,		/* link quality monitoring */ | 
|  | PPP_chap=	0xc223,		/* challenge/response */ | 
|  | }; | 
|  |  | 
|  | /* LCP protocol (and IPCP) */ | 
|  |  | 
|  |  | 
|  | typedef struct Lcppkt	Lcppkt; | 
|  | struct Lcppkt | 
|  | { | 
|  | uchar	code; | 
|  | uchar	id; | 
|  | uchar	len[2]; | 
|  | uchar	data[1]; | 
|  | }; | 
|  |  | 
|  | typedef struct Lcpopt	Lcpopt; | 
|  | struct Lcpopt | 
|  | { | 
|  | uchar	type; | 
|  | uchar	len; | 
|  | uchar	data[1]; | 
|  | }; | 
|  |  | 
|  | enum | 
|  | { | 
|  | /* LCP codes */ | 
|  | Lconfreq=	1, | 
|  | Lconfack=	2, | 
|  | Lconfnak=	3, | 
|  | Lconfrej=	4, | 
|  | Ltermreq=	5, | 
|  | Ltermack=	6, | 
|  | Lcoderej=	7, | 
|  | Lprotorej=	8, | 
|  | Lechoreq=	9, | 
|  | Lechoack=	10, | 
|  | Ldiscard=	11, | 
|  | Lresetreq=	14,	/* for ccp only */ | 
|  | Lresetack=	15,	/* for ccp only */ | 
|  |  | 
|  | /* Lcp configure options */ | 
|  | Omtu=		1, | 
|  | Octlmap=	2, | 
|  | Oauth=		3, | 
|  | Oquality=	4, | 
|  | Omagic=		5, | 
|  | Opc=		7, | 
|  | Oac=		8, | 
|  |  | 
|  | /* authentication protocols */ | 
|  | APmd5=		5, | 
|  | APmschap=	128, | 
|  |  | 
|  | /* Chap codes */ | 
|  | Cchallenge=	1, | 
|  | Cresponse=	2, | 
|  | Csuccess=	3, | 
|  | Cfailure=	4, | 
|  |  | 
|  | /* ipcp configure options */ | 
|  | Oipaddrs=	1, | 
|  | Oipcompress=	2, | 
|  | Oipaddr=	3, | 
|  | Oipdns=		129, | 
|  | Oipwins=	130, | 
|  | Oipdns2=	131, | 
|  | Oipwins2=	132 | 
|  | }; | 
|  |  | 
|  | char * | 
|  | lcpcode[] = { | 
|  | 0, | 
|  | "confreq", | 
|  | "confack", | 
|  | "confnak", | 
|  | "confrej", | 
|  | "termreq", | 
|  | "termack", | 
|  | "coderej", | 
|  | "protorej", | 
|  | "echoreq", | 
|  | "echoack", | 
|  | "discard", | 
|  | "id", | 
|  | "timeremain", | 
|  | "resetreq", | 
|  | "resetack" | 
|  | }; | 
|  |  | 
|  | static Mux p_mux[] = | 
|  | { | 
|  | {"ip",		PPP_ip, }, | 
|  | {"ppp_vjctcp",	PPP_vjctcp, }, | 
|  | {"ppp_vjutcp",	PPP_vjutcp, }, | 
|  | {"ppp_ml",	PPP_ml, }, | 
|  | {"ppp_comp",	PPP_comp, }, | 
|  | {"ppp_ipcp",	PPP_ipcp, }, | 
|  | {"ppp_ccp",	PPP_ccp, }, | 
|  | {"ppp_passwd",	PPP_passwd, }, | 
|  | {"ppp_lcp",	PPP_lcp, }, | 
|  | {"ppp_lqm",	PPP_lqm, }, | 
|  | {"ppp_chap",	PPP_chap, }, | 
|  | {0} | 
|  | }; | 
|  |  | 
|  | enum | 
|  | { | 
|  | OOproto | 
|  | }; | 
|  |  | 
|  | static void | 
|  | p_compile(Filter *f) | 
|  | { | 
|  | Mux *m; | 
|  |  | 
|  | for(m = p_mux; m->name != nil; m++) | 
|  | if(strcmp(f->s, m->name) == 0){ | 
|  | f->pr = m->pr; | 
|  | f->ulv = m->val; | 
|  | f->subop = OOproto; | 
|  | return; | 
|  | } | 
|  |  | 
|  | sysfatal("unknown ppp field or protocol: %s", f->s); | 
|  | } | 
|  |  | 
|  | static int | 
|  | p_filter(Filter *f, Msg *m) | 
|  | { | 
|  | int proto; | 
|  | int len; | 
|  |  | 
|  | if(f->subop != OOproto) | 
|  | return 0; | 
|  |  | 
|  | len = m->pe - m->ps; | 
|  | if(len < 3) | 
|  | return -1; | 
|  |  | 
|  | if(m->ps[0] == PPP_addr && m->ps[1] == PPP_ctl) | 
|  | m->ps += 2; | 
|  |  | 
|  | proto = *m->ps++; | 
|  | if((proto&1) == 0) | 
|  | proto = (proto<<8) | *m->ps++; | 
|  |  | 
|  | if(proto == f->ulv) | 
|  | return 1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | p_seprint(Msg *m) | 
|  | { | 
|  | int proto; | 
|  | int len; | 
|  |  | 
|  | len = m->pe - m->ps; | 
|  | if(len < 3) | 
|  | return -1; | 
|  |  | 
|  | if(m->ps[0] == PPP_addr && m->ps[1] == PPP_ctl) | 
|  | m->ps += 2; | 
|  |  | 
|  | proto = *m->ps++; | 
|  | if((proto&1) == 0) | 
|  | proto = (proto<<8) | *m->ps++; | 
|  |  | 
|  | m->p = seprint(m->p, m->e, "pr=%ud len=%d", proto, len); | 
|  | demux(p_mux, proto, proto, m, &dump); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | p_seprintchap(Msg *m) | 
|  | { | 
|  | Lcppkt *lcp; | 
|  | char *p, *e; | 
|  | int len; | 
|  |  | 
|  | if(m->pe-m->ps < 4) | 
|  | return -1; | 
|  |  | 
|  | p = m->p; | 
|  | e = m->e; | 
|  | m->pr = nil; | 
|  |  | 
|  | /* resize packet */ | 
|  | lcp = (Lcppkt*)m->ps; | 
|  | len = NetS(lcp->len); | 
|  | if(m->ps+len < m->pe) | 
|  | m->pe = m->ps+len; | 
|  | else if(m->ps+len > m->pe) | 
|  | return -1; | 
|  |  | 
|  | p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code); | 
|  | switch(lcp->code) { | 
|  | default: | 
|  | p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data); | 
|  | break; | 
|  | case 1: | 
|  | case 2: | 
|  | if(lcp->data[0] > len-4){ | 
|  | p = seprint(p, e, "%.*H", len-4, lcp->data); | 
|  | } else { | 
|  | p = seprint(p, e, " %s=", lcp->code==1?"challenge ":"response "); | 
|  | p = seprint(p, e, "%.*H", lcp->data[0], lcp->data+1); | 
|  | p = seprint(p, e, " name="); | 
|  | p = seprint(p, e, "%.*H", len-4-lcp->data[0]-1, lcp->data+lcp->data[0]+1); | 
|  | } | 
|  | break; | 
|  | case 3: | 
|  | case 4: | 
|  | if(len > 64) | 
|  | len = 64; | 
|  | p = seprint(p, e, " %s=%.*H", lcp->code==3?"success ":"failure", | 
|  | len>64?64:len, lcp->data); | 
|  | break; | 
|  | } | 
|  | m->p = seprint(p, e, " len=%d", len); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static char* | 
|  | seprintlcpopt(char *p, char *e, void *a, int len) | 
|  | { | 
|  | Lcpopt *o; | 
|  | int proto, x, period; | 
|  | uchar *cp, *ecp; | 
|  |  | 
|  | cp = a; | 
|  | ecp = cp+len; | 
|  |  | 
|  | for(; cp < ecp; cp += o->len){ | 
|  | o = (Lcpopt*)cp; | 
|  | if(cp + o->len > ecp || o->len == 0){ | 
|  | p = seprint(p, e, " bad-opt-len=%d", o->len); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | switch(o->type){ | 
|  | default: | 
|  | p = seprint(p, e, " (type=%d len=%d)", o->type, o->len); | 
|  | break; | 
|  | case Omtu: | 
|  | p = seprint(p, e, " mtu=%d", NetS(o->data)); | 
|  | break; | 
|  | case Octlmap: | 
|  | p = seprint(p, e, " ctlmap=%ux", NetL(o->data)); | 
|  | break; | 
|  | case Oauth: | 
|  | proto = NetS(o->data); | 
|  | switch(proto) { | 
|  | default: | 
|  | p = seprint(p, e, " auth=%d", proto); | 
|  | break; | 
|  | case PPP_passwd: | 
|  | p = seprint(p, e, " auth=passwd"); | 
|  | break; | 
|  | case PPP_chap: | 
|  | p = seprint(p, e, " (auth=chap data=%2.2ux)", o->data[2]); | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case Oquality: | 
|  | proto = NetS(o->data); | 
|  | switch(proto) { | 
|  | default: | 
|  | p = seprint(p, e, " qproto=%d", proto); | 
|  | break; | 
|  | case PPP_lqm: | 
|  | x = NetL(o->data+2)*10; | 
|  | period = (x+(PPP_period-1))/PPP_period; | 
|  | p = seprint(p, e, " (qproto=lqm period=%d)", period); | 
|  | break; | 
|  | } | 
|  | case Omagic: | 
|  | p = seprint(p, e, " magic=%ux", NetL(o->data)); | 
|  | break; | 
|  | case Opc: | 
|  | p = seprint(p, e, " protocol-compress"); | 
|  | break; | 
|  | case Oac: | 
|  | p = seprint(p, e, " addr-compress"); | 
|  | break; | 
|  | } | 
|  | } | 
|  | return p; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | p_seprintlcp(Msg *m) | 
|  | { | 
|  | Lcppkt *lcp; | 
|  | char *p, *e; | 
|  | int len; | 
|  |  | 
|  | if(m->pe-m->ps < 4) | 
|  | return -1; | 
|  |  | 
|  | p = m->p; | 
|  | e = m->e; | 
|  | m->pr = nil; | 
|  |  | 
|  | lcp = (Lcppkt*)m->ps; | 
|  | len = NetS(lcp->len); | 
|  | if(m->ps+len < m->pe) | 
|  | m->pe = m->ps+len; | 
|  | else if(m->ps+len > m->pe) | 
|  | return -1; | 
|  |  | 
|  | p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code); | 
|  | switch(lcp->code) { | 
|  | default: | 
|  | p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data); | 
|  | break; | 
|  | case Lconfreq: | 
|  | case Lconfack: | 
|  | case Lconfnak: | 
|  | case Lconfrej: | 
|  | p = seprint(p, e, "=%s", lcpcode[lcp->code]); | 
|  | p = seprintlcpopt(p, e, lcp->data, len-4); | 
|  | break; | 
|  | case Ltermreq: | 
|  | case Ltermack: | 
|  | case Lcoderej: | 
|  | case Lprotorej: | 
|  | case Lechoreq: | 
|  | case Lechoack: | 
|  | case Ldiscard: | 
|  | p = seprint(p, e, "=%s", lcpcode[lcp->code]); | 
|  | break; | 
|  | } | 
|  | m->p = seprint(p, e, " len=%d", len); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static char* | 
|  | seprintipcpopt(char *p, char *e, void *a, int len) | 
|  | { | 
|  | Lcpopt *o; | 
|  | uchar *cp, *ecp; | 
|  |  | 
|  | cp = a; | 
|  | ecp = cp+len; | 
|  |  | 
|  | for(; cp < ecp; cp += o->len){ | 
|  | o = (Lcpopt*)cp; | 
|  | if(cp + o->len > ecp){ | 
|  | p = seprint(p, e, " bad opt len %ux", o->type); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | switch(o->type){ | 
|  | default: | 
|  | p = seprint(p, e, " (type=%d len=%d)", o->type, o->len); | 
|  | break; | 
|  | case Oipaddrs: | 
|  | p = seprint(p, e, " ipaddrs(deprecated)"); | 
|  | break; | 
|  | case Oipcompress: | 
|  | p = seprint(p, e, " ipcompress"); | 
|  | break; | 
|  | case Oipaddr: | 
|  | p = seprint(p, e, " ipaddr=%V", o->data); | 
|  | break; | 
|  | case Oipdns: | 
|  | p = seprint(p, e, " dnsaddr=%V", o->data); | 
|  | break; | 
|  | case Oipwins: | 
|  | p = seprint(p, e, " winsaddr=%V", o->data); | 
|  | break; | 
|  | case Oipdns2: | 
|  | p = seprint(p, e, " dns2addr=%V", o->data); | 
|  | break; | 
|  | case Oipwins2: | 
|  | p = seprint(p, e, " wins2addr=%V", o->data); | 
|  | break; | 
|  | } | 
|  | } | 
|  | return p; | 
|  | } | 
|  |  | 
|  | static int | 
|  | p_seprintipcp(Msg *m) | 
|  | { | 
|  | Lcppkt *lcp; | 
|  | char *p, *e; | 
|  | int len; | 
|  |  | 
|  | if(m->pe-m->ps < 4) | 
|  | return -1; | 
|  |  | 
|  | p = m->p; | 
|  | e = m->e; | 
|  | m->pr = nil; | 
|  |  | 
|  | lcp = (Lcppkt*)m->ps; | 
|  | len = NetS(lcp->len); | 
|  | if(m->ps+len < m->pe) | 
|  | m->pe = m->ps+len; | 
|  | else if(m->ps+len > m->pe) | 
|  | return -1; | 
|  |  | 
|  | p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code); | 
|  | switch(lcp->code) { | 
|  | default: | 
|  | p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data); | 
|  | break; | 
|  | case Lconfreq: | 
|  | case Lconfack: | 
|  | case Lconfnak: | 
|  | case Lconfrej: | 
|  | p = seprint(p, e, "=%s", lcpcode[lcp->code]); | 
|  | p = seprintipcpopt(p, e, lcp->data, len-4); | 
|  | break; | 
|  | case Ltermreq: | 
|  | case Ltermack: | 
|  | p = seprint(p, e, "=%s", lcpcode[lcp->code]); | 
|  | break; | 
|  | } | 
|  | m->p = seprint(p, e, " len=%d", len); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static char* | 
|  | seprintccpopt(char *p, char *e, void *a, int len) | 
|  | { | 
|  | Lcpopt *o; | 
|  | uchar *cp, *ecp; | 
|  |  | 
|  | cp = a; | 
|  | ecp = cp+len; | 
|  |  | 
|  | for(; cp < ecp; cp += o->len){ | 
|  | o = (Lcpopt*)cp; | 
|  | if(cp + o->len > ecp){ | 
|  | p = seprint(p, e, " bad opt len %ux", o->type); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | switch(o->type){ | 
|  | default: | 
|  | p = seprint(p, e, " type=%d ", o->type); | 
|  | break; | 
|  | case 0: | 
|  | p = seprint(p, e, " OUI=(%d %.2ux%.2ux%.2ux) ", o->type, | 
|  | o->data[0], o->data[1], o->data[2]); | 
|  | break; | 
|  | case 17: | 
|  | p = seprint(p, e, " Stac-LZS"); | 
|  | break; | 
|  | case 18: | 
|  | p = seprint(p, e, " Microsoft-PPC=%ux", NetL(o->data)); | 
|  | break; | 
|  | } | 
|  | } | 
|  | return p; | 
|  | } | 
|  |  | 
|  | static int | 
|  | p_seprintccp(Msg *m) | 
|  | { | 
|  | Lcppkt *lcp; | 
|  | char *p, *e; | 
|  | int len; | 
|  |  | 
|  | if(m->pe-m->ps < 4) | 
|  | return -1; | 
|  |  | 
|  | p = m->p; | 
|  | e = m->e; | 
|  | m->pr = nil; | 
|  |  | 
|  | lcp = (Lcppkt*)m->ps; | 
|  | len = NetS(lcp->len); | 
|  | if(m->ps+len < m->pe) | 
|  | m->pe = m->ps+len; | 
|  | else if(m->ps+len > m->pe) | 
|  | return -1; | 
|  |  | 
|  | p = seprint(p, e, "id=%d code=%d", lcp->id, lcp->code); | 
|  | switch(lcp->code) { | 
|  | default: | 
|  | p = seprint(p, e, " data=%.*H", len>64?64:len, lcp->data); | 
|  | break; | 
|  | case Lconfreq: | 
|  | case Lconfack: | 
|  | case Lconfnak: | 
|  | case Lconfrej: | 
|  | p = seprint(p, e, "=%s", lcpcode[lcp->code]); | 
|  | p = seprintccpopt(p, e, lcp->data, len-4); | 
|  | break; | 
|  | case Ltermreq: | 
|  | case Ltermack: | 
|  | case Lresetreq: | 
|  | case Lresetack: | 
|  | p = seprint(p, e, "=%s", lcpcode[lcp->code]); | 
|  | break; | 
|  | } | 
|  | m->p = seprint(p, e, " len=%d", len); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | p_seprintcomp(Msg *m) | 
|  | { | 
|  | char compflag[5]; | 
|  | ushort x; | 
|  | int i; | 
|  | int len; | 
|  |  | 
|  | len = m->pe-m->ps; | 
|  | if(len < 2) | 
|  | return -1; | 
|  |  | 
|  | x = NetS(m->ps); | 
|  | m->ps += 2; | 
|  | i = 0; | 
|  | if(x & (1<<15)) | 
|  | compflag[i++] = 'r'; | 
|  | if(x & (1<<14)) | 
|  | compflag[i++] = 'f'; | 
|  | if(x & (1<<13)) | 
|  | compflag[i++] = 'c'; | 
|  | if(x & (1<<12)) | 
|  | compflag[i++] = 'e'; | 
|  | compflag[i] = 0; | 
|  | m->p = seprint(m->p, m->e, "flag=%s count=%.3ux", compflag, x&0xfff); | 
|  | m->p = seprint(m->p, m->e, " data=%.*H", len>64?64:len, m->ps); | 
|  | m->pr = nil; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | Proto ppp = | 
|  | { | 
|  | "ppp", | 
|  | p_compile, | 
|  | p_filter, | 
|  | p_seprint, | 
|  | p_mux, | 
|  | "%#.4lux", | 
|  | nil, | 
|  | defaultframer | 
|  | }; | 
|  |  | 
|  | Proto ppp_ipcp = | 
|  | { | 
|  | "ppp_ipcp", | 
|  | p_compile, | 
|  | p_filter, | 
|  | p_seprintipcp, | 
|  | nil, | 
|  | nil, | 
|  | nil, | 
|  | defaultframer | 
|  | }; | 
|  |  | 
|  | Proto ppp_lcp = | 
|  | { | 
|  | "ppp_lcp", | 
|  | p_compile, | 
|  | p_filter, | 
|  | p_seprintlcp, | 
|  | nil, | 
|  | nil, | 
|  | nil, | 
|  | defaultframer | 
|  | }; | 
|  |  | 
|  | Proto ppp_ccp = | 
|  | { | 
|  | "ppp_ccp", | 
|  | p_compile, | 
|  | p_filter, | 
|  | p_seprintccp, | 
|  | nil, | 
|  | nil, | 
|  | nil, | 
|  | defaultframer | 
|  | }; | 
|  |  | 
|  | Proto ppp_chap = | 
|  | { | 
|  | "ppp_chap", | 
|  | p_compile, | 
|  | p_filter, | 
|  | p_seprintchap, | 
|  | nil, | 
|  | nil, | 
|  | nil, | 
|  | defaultframer | 
|  | }; | 
|  |  | 
|  | Proto ppp_comp = | 
|  | { | 
|  | "ppp_comp", | 
|  | p_compile, | 
|  | p_filter, | 
|  | p_seprintcomp, | 
|  | nil, | 
|  | nil, | 
|  | nil, | 
|  | defaultframer | 
|  | }; |