text: #include <u.h>
  #include <libc.h>
  #include <ip.h>
  #include "dat.h"
  #include "protos.h"
  
  enum
  {
  	Maxoptlen=	312-4,
  
  	/* dhcp types */
  	Discover=	1,
  	Offer=		2,
  	Request=	3,
  	Decline=	4,
  	Ack=		5,
  	Nak=		6,
  	Release=	7,
  	Inform=		8,
  
  	/* bootp option types */
  	OBend=			255,
  	OBpad=			0,
  	OBmask=			1,
  	OBtimeoff=		2,
  	OBrouter=		3,
  	OBtimeserver=		4,
  	OBnameserver=		5,
  	OBdnserver=		6,
  	OBlogserver=		7,
  	OBcookieserver=		8,
  	OBlprserver=		9,
  	OBimpressserver=	10,
  	OBrlserver=		11,
  	OBhostname=		12,	/* 0xc0 */
  	OBbflen=		13,
  	OBdumpfile=		14,
  	OBdomainname=		15,
  	OBswapserver=		16,	/* 0x10 */
  	OBrootpath=		17,
  	OBextpath=		18,
  	OBipforward=		19,
  	OBnonlocal=		20,
  	OBpolicyfilter=		21,
  	OBmaxdatagram=		22,
  	OBttl=			23,
  	OBpathtimeout=		24,
  	OBpathplateau=		25,
  	OBmtu=			26,
  	OBsubnetslocal=		27,
  	OBbaddr=		28,
  	OBdiscovermask=		29,
  	OBsupplymask=		30,
  	OBdiscoverrouter=	31,
  	OBrsserver=		32,	/* 0x20 */
  	OBstaticroutes=		33,
  	OBtrailerencap=		34,
  	OBarptimeout=		35,
  	OBetherencap=		36,
  	OBtcpttl=		37,
  	OBtcpka=		38,
  	OBtcpkag=		39,
  	OBnisdomain=		40,
  	OBniserver=		41,	
  	OBntpserver=		42,
  	OBvendorinfo=		43,	/* 0x2b */
  	OBnetbiosns=		44,
  	OBnetbiosdds=		45,
  	OBnetbiostype=		46,
  	OBnetbiosscope=		47,
  	OBxfontserver=		48,	/* 0x30 */
  	OBxdispmanager=		49,
  	OBnisplusdomain=	64,	/* 0x40 */
  	OBnisplusserver=	65,
  	OBhomeagent=		68,
  	OBsmtpserver=		69,
  	OBpop3server=		70,
  	OBnntpserver=		71,
  	OBwwwserver=		72,
  	OBfingerserver=		73,
  	OBircserver=		74,
  	OBstserver=		75,
  	OBstdaserver=		76,
  
  	/* dhcp options */
  	ODipaddr=		50,	/* 0x32 */
  	ODlease=		51,
  	ODoverload=		52,
  	ODtype=			53,	/* 0x35 */
  	ODserverid=		54,	/* 0x36 */
  	ODparams=		55,	/* 0x37 */
  	ODmessage=		56,
  	ODmaxmsg=		57,
  	ODrenewaltime=		58,
  	ODrebindingtime=	59,
  	ODvendorclass=		60,
  	ODclientid=		61,	/* 0x3d */
  	ODtftpserver=		66,
  	ODbootfile=		67,
  
  	/* plan9 vendor info options */
  	OP9fs=			128,	/* plan9 file servers */
  	OP9auth=		129,	/* plan9 auth servers */
  };
  
  static void
  p_compile(Filter *f)
  {
  	sysfatal("unknown dhcp field: %s", f->s);
  }
  
  /*
   *  convert a byte array to hex
   */
  static char
  hex(int x)
  {
  	if(x < 10)
  		return x + '0';
  	return x - 10 + 'a';
  }
  static char*
  phex(char *p, char *e, char *tag, uchar *o, int n)
  {
  	p = seprint(p, e, "%s=", tag);
  
  	for(; p+2 < e && n > 0; n--){
  		*p++ = hex(*o>>4);
  		*p++ = hex(*o & 0xf);
  		o++;
  	}
  	return p;
  }
  
  static char*
  pstring(char *p, char *e, char *tag, uchar *o, int n)
  {
  	char msg[256];
  
  	if(n > sizeof msg - 1)
  		n = sizeof msg - 1;
  	memmove(msg, o, n);
  	msg[n] = 0;
  	return seprint(p, e, "%s=%s", tag, msg);
  }
  
  static char*
  pint(char *p, char *e, char *tag, uchar *o, int n)
  {
  	int x;
  
  	x = *(char*)o++;
  	for(; n > 1; n--)
  		x = (x<<8)|*o++;
  	return seprint(p, e, "%s=%d", tag, x);
  }
  
  static char*
  puint(char *p, char *e, char *tag, uchar *o, int n)
  {
  	uint x;
  
  	x = *o++;
  	for(; n > 1; n--)
  		x = (x<<8)|*o++;
  	return seprint(p, e, "%s=%ud", tag, x);
  }
  
  static char*
  pserver(char *p, char *e, char *tag, uchar *o, int n)
  {
  	p = seprint(p, e, "%s=(", tag);
  	while(n >= 4){
  		p = seprint(p, e, " %V", o);
  		n -= 4;
  		o += 4;
  	}
  	p = seprint(p, e, ")");
  	return p;
  }
  
  static char *dhcptype[256] =
  {
  [Discover]	"Discover",
  [Offer]		"Offer",
  [Request]	"Request",
  [Decline]	"Decline",
  [Ack]		"Ack",
  [Nak]		"Nak",
  [Release]	"Release",
  [Inform]	"Inform"
  };
  
  
  static char*
  ptype(char *p, char *e, uchar val)
  {
  	char *x;
  
  	x = dhcptype[val];
  	if(x != nil)
  		return seprint(p, e, "t=%s", x);
  	else
  		return seprint(p, e, "t=%d", val);
  }
  
  static int
  p_seprint(Msg *m)
  {
  	int i, n, code;
  	uchar *o, *ps;
  	char *p, *e;
  	char msg[64];
  
  	/* no next proto */
  	m->pr = nil;
  
  	p = m->p;
  	e = m->e;
  	ps = m->ps;
  
  	while(ps < m->pe){
  		code = *ps++;
  		if(code == 255)
  			break;
  		if(code == 0)
  			continue;
  
  		/* ignore anything that's too long */
  		n = *ps++;
  		o = ps;
  		ps += n;
  		if(ps > m->pe)
  			break;
  		
  		switch(code){
  		case ODipaddr:	/* requested ip address */
  			p = pserver(p, e, "ipaddr", o, n);
  			break;
  		case ODlease:	/* requested lease time */
  			p = pint(p, e, "lease", o, n);
  			break;
  		case ODtype:
  			p = ptype(p, e, *o);
  			break;
  		case ODserverid:
  			p = pserver(p, e, "serverid", o, n);
  			break;
  		case ODmessage:
  			p = pstring(p, e, "message", o, n);
  			break;
  		case ODmaxmsg:
  			p = puint(p, e, "maxmsg", o, n);
  			break;
  		case ODclientid:
  			p = phex(p, e, "clientid", o, n);
  			break;
  		case ODparams:
  			p = seprint(p, e, " requested=(");
  			for(i = 0; i < n; i++){
  				if(i != 0)
  					p = seprint(p, e, " ");
  				p = seprint(p, e, "%ud", o[i]);
  			}
  			p = seprint(p, e, ")");
  			break;
  		case ODvendorclass:
  			p = pstring(p, e, "vendorclass", o, n);
  			break;
  		case OBmask:
  			p = pserver(p, e, "mask", o, n);
  			break;
  		case OBtimeoff:
  			p = pint(p, e, "timeoff", o, n);
  			break;
  		case OBrouter:
  			p = pserver(p, e, "router", o, n);
  			break;
  		case OBtimeserver:
  			p = pserver(p, e, "timesrv", o, n);
  			break;
  		case OBnameserver:
  			p = pserver(p, e, "namesrv", o, n);
  			break;
  		case OBdnserver:
  			p = pserver(p, e, "dnssrv", o, n);
  			break;
  		case OBlogserver:
  			p = pserver(p, e, "logsrv", o, n);
  			break;
  		case OBcookieserver:
  			p = pserver(p, e, "cookiesrv", o, n);
  			break;
  		case OBlprserver:
  			p = pserver(p, e, "lprsrv", o, n);
  			break;
  		case OBimpressserver:
  			p = pserver(p, e, "impresssrv", o, n);
  			break;
  		case OBrlserver:
  			p = pserver(p, e, "rlsrv", o, n);
  			break;
  		case OBhostname:
  			p = pstring(p, e, "hostname", o, n);
  			break;
  		case OBbflen:
  			break;
  		case OBdumpfile:
  			p = pstring(p, e, "dumpfile", o, n);
  			break;
  		case OBdomainname:
  			p = pstring(p, e, "domname", o, n);
  			break;
  		case OBswapserver:
  			p = pserver(p, e, "swapsrv", o, n);
  			break;
  		case OBrootpath:
  			p = pstring(p, e, "rootpath", o, n);
  			break;
  		case OBextpath:
  			p = pstring(p, e, "extpath", o, n);
  			break;
  		case OBipforward:
  			p = phex(p, e, "ipforward", o, n);
  			break;
  		case OBnonlocal:
  			p = phex(p, e, "nonlocal", o, n);
  			break;
  		case OBpolicyfilter:
  			p = phex(p, e, "policyfilter", o, n);
  			break;
  		case OBmaxdatagram:
  			p = phex(p, e, "maxdatagram", o, n);
  			break;
  		case OBttl:
  			p = puint(p, e, "ttl", o, n);
  			break;
  		case OBpathtimeout:
  			p = puint(p, e, "pathtimeout", o, n);
  			break;
  		case OBpathplateau:
  			p = phex(p, e, "pathplateau", o, n);
  			break;
  		case OBmtu:
  			p = puint(p, e, "mtu", o, n);
  			break;
  		case OBsubnetslocal:
  			p = pserver(p, e, "subnet", o, n);
  			break;
  		case OBbaddr:
  			p = pserver(p, e, "baddr", o, n);
  			break;
  		case OBdiscovermask:
  			p = pserver(p, e, "discovermsak", o, n);
  			break;
  		case OBsupplymask:
  			p = pserver(p, e, "rousupplymaskter", o, n);
  			break;
  		case OBdiscoverrouter:
  			p = pserver(p, e, "discoverrouter", o, n);
  			break;
  		case OBrsserver:
  			p = pserver(p, e, "rsrouter", o, n);
  			break;
  		case OBstaticroutes:
  			p = phex(p, e, "staticroutes", o, n);
  			break;
  		case OBtrailerencap:
  			p = phex(p, e, "trailerencap", o, n);
  			break;
  		case OBarptimeout:
  			p = puint(p, e, "arptimeout", o, n);
  			break;
  		case OBetherencap:
  			p = phex(p, e, "etherencap", o, n);
  			break;
  		case OBtcpttl:
  			p = puint(p, e, "tcpttl", o, n);
  			break;
  		case OBtcpka:
  			p = puint(p, e, "tcpka", o, n);
  			break;
  		case OBtcpkag:
  			p = phex(p, e, "tcpkag", o, n);
  			break;
  		case OBnisdomain:
  			p = pstring(p, e, "nisdomain", o, n);
  			break;
  		case OBniserver:
  			p = pserver(p, e, "nisrv", o, n);
  			break;
  		case OBntpserver:
  			p = pserver(p, e, "ntpsrv", o, n);
  			break;
  		case OBvendorinfo:
  			p = phex(p, e, "vendorinfo", o, n);
  			break;
  		case OBnetbiosns:
  			p = pserver(p, e, "biosns", o, n);
  			break;
  		case OBnetbiosdds:
  			p = phex(p, e, "biosdds", o, n);
  			break;
  		case OBnetbiostype:
  			p = phex(p, e, "biostype", o, n);
  			break;
  		case OBnetbiosscope:
  			p = phex(p, e, "biosscope", o, n);
  			break;
  		case OBxfontserver:
  			p = pserver(p, e, "fontsrv", o, n);
  			break;
  		case OBxdispmanager:
  			p = pserver(p, e, "xdispmgr", o, n);
  			break;
  		case OBnisplusdomain:
  			p = pstring(p, e, "nisplusdomain", o, n);
  			break;
  		case OBnisplusserver:
  			p = pserver(p, e, "nisplussrv", o, n);
  			break;
  		case OBhomeagent:
  			p = pserver(p, e, "homeagent", o, n);
  			break;
  		case OBsmtpserver:
  			p = pserver(p, e, "smtpsrv", o, n);
  			break;
  		case OBpop3server:
  			p = pserver(p, e, "pop3srv", o, n);
  			break;
  		case OBnntpserver:
  			p = pserver(p, e, "ntpsrv", o, n);
  			break;
  		case OBwwwserver:
  			p = pserver(p, e, "wwwsrv", o, n);
  			break;
  		case OBfingerserver:
  			p = pserver(p, e, "fingersrv", o, n);
  			break;
  		case OBircserver:
  			p = pserver(p, e, "ircsrv", o, n);
  			break;
  		case OBstserver:
  			p = pserver(p, e, "stsrv", o, n);
  			break;
  		case OBstdaserver:
  			p = pserver(p, e, "stdasrv", o, n);
  			break;
  		case OBend:
  			goto out;
  		default:
  			snprint(msg, sizeof msg, " T%ud", code);
  			p = phex(p, e, msg, o, n);
  			break;
  		}
  		if(*ps != OBend)
  			p = seprint(p, e, " ");
  	}
  out:
  	m->p = p;
  	m->ps = ps;
  	return 0;
  }
  
  Proto dhcp =
  {
  	"dhcp",
  	p_compile,
  	nil,
  	p_seprint,
  	nil,
  	nil,
  	nil,
  	defaultframer
  };
  
  