| #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 | 
 | }; |