From danny@cs.huji.ac.il  Mon Jan 12 03:10:35 2004
Return-Path: <danny@cs.huji.ac.il>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 2BD2816A4CE
	for <FreeBSD-gnats-submit@freebsd.org>; Mon, 12 Jan 2004 03:10:35 -0800 (PST)
Received: from cs.huji.ac.il (cs.huji.ac.il [132.65.16.30])
	by mx1.FreeBSD.org (Postfix) with ESMTP id B082043D45
	for <FreeBSD-gnats-submit@freebsd.org>; Mon, 12 Jan 2004 03:10:33 -0800 (PST)
	(envelope-from danny@cs.huji.ac.il)
Received: from pampa.cs.huji.ac.il ([132.65.80.32] ident=exim)
	by cs.huji.ac.il with esmtp
	id 1Afzxm-0007IG-VX; Mon, 12 Jan 2004 13:10:30 +0200
Received: from danny by pampa.cs.huji.ac.il with local (Exim 4.12)
	id 1Afzxm-000CTl-00; Mon, 12 Jan 2004 13:10:30 +0200
Message-Id: <E1Afzxm-000CTl-00@pampa.cs.huji.ac.il>
Date: Mon, 12 Jan 2004 13:10:30 +0200
From: Danny Braniss <danny@cs.huji.ac.il>
Reply-To: Danny Braniss <danny@cs.huji.ac.il>
To: FreeBSD-gnats-submit@freebsd.org
Cc: danny@cs.huji.ac.il
Subject: bootp enhancement, places the dhcp tags into the
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         61239
>Category:       bin
>Synopsis:       [patch] bootp enhancement, places the dhcp tags into the
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    dougb
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jan 12 03:20:13 PST 2004
>Closed-Date:    Thu Jul 07 07:08:12 UTC 2011
>Last-Modified:  Thu Jul 07 07:08:12 UTC 2011
>Originator:     Danny Braniss
>Release:        FreeBSD 4.9-STABLE i386
>Organization:
>Environment:
System: FreeBSD pampa 4.9-STABLE FreeBSD 4.9-STABLE #10: Sun Nov 16 14:13:37 IST 2003 danny@dev:/r+d/obj/r+d/4.9/src/sys/HUJI i386

	diskless boot via PXE
>Description:
	
This is still unfinished ...

The changes to lib/libstand/bootp.c were promped for the following reasons:

        o- need to configure many different disk/data-less clients.
        o- simplify management.
        o- allow some form of central control

Changes:

1: When the pxeboot card first send out a dhcp request, it adds a
   vendor specific id/class (PXEClient) so that the dhcp daemon
   can identify the request as comming from a pxe-aware program,
   eg: from dhcpd.conf

   class "pxeclients" {
         option PXE.mtftp-ip 0.0.0.0;
         match if substring (option vendor-class-identifier, 0, 9)
               = "PXEClient";
   }
   Note: the above 'tricks' the PXE-boot not to go and mess with
   the proxy-dhcpd/mftp.

   I modified lib/libstand/bootp.c to send "FreeBSD" instead
   of the default "PXEClient" and so:

   option FBSD.kernel code 1 = text;
   option FBSD.kernel_options code 3 = text;
   option FBSD.kernelname code 2 = text;
   option FBSD.usr-ip code 4 = ip-address;
   option FBSD.usr-path code 5 = text;

   class "freebsd" {
        # Production - uses fs-02
        option FBSD.kernel "kernel";
        match if substring (option vendor-class-identifier, 0, 8) =
                  "FreeBSD";
                option root-path "1.1.1.1:/roots/FreeBSD/i386-4.4";
                vendor-option-space FBSD;
   }

2: places all the tags received in the kernel environment eg:
   kenv |grep dhcp
   dhcp.dhcp-lease-time="0000012c"
   dhcp.dhcp-message-type="05"
   dhcp.dhcp-rebinding-time="00000106"
   dhcp.dhcp-renewal-time="00000096"
   dhcp.dhcp-server-identifier="132.65.16.17"
   dhcp.domain-name="cs.huji.ac.il"
   dhcp.domain-name-servers="132.65.16.10,132.65.16.8"
   dhcp.host-name="foo"
   dhcp.root-path="132.65.16.100:/c/2"
   dhcp.routers="132.65.16.1"
   dhcp.subnet-mask="255.255.0.0"
   dhcp.tags="053:054:051:001:003:006:012:017:058:059:015:043:"

>How-To-Repeat:
>Fix:
	ftp://ftp.cs.huji.ac.il/users/danny/freebsd/diskless-boot/bootp.c
>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->feedback 
State-Changed-By: vs 
State-Changed-When: Tue Jul 20 07:52:58 GMT 2004 
State-Changed-Why:  
Please submit the patch as a unified diff in a followup so it doesn't get lost 


Responsible-Changed-From-To: freebsd-i386->freebsd-bugs 
Responsible-Changed-By: vs 
Responsible-Changed-When: Tue Jul 20 07:52:58 GMT 2004 
Responsible-Changed-Why:  
Reclassify 

http://www.freebsd.org/cgi/query-pr.cgi?pr=61239 
State-Changed-From-To: feedback->open 
State-Changed-By: vs 
State-Changed-When: Tue Jul 20 09:12:13 GMT 2004 
State-Changed-Why:  
Patch appended. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=61239 

From: Max Campos <mcampos@bpsw.biz>
To: bug-followup@FreeBSD.org,
 danny@cs.huji.ac.il
Cc:  
Subject: Re: bin/61239: [patch] bootp enhancement, places the dhcp tags into the
Date: Sat, 6 Aug 2005 11:41:45 -0700

 I've applied this patch to my own FreeBSD 5.4 system and it works  
 great!  It would be great to see this applied.  There are several  
 things that are currently broken that this patch fixes:
 
 1) /etc/rc.d/hostname - Enables us diskless folks to get the client's  
 hostname from DHCP, rather than overriding the rc.conf (this is  
 currently the ONLY reason for me to override rc.conf for each  
 diskless client).
 
 2) /etc/rc.d/resolv - Enables a reasonable /etc/resolv.conf to be  
 written from the DHCP packet.
 
 Incidentally these features have been in the startup scripts since  
 2002 (/etc/rc.d/diskless v1.23), but the dhcp.* kenv variables have,  
 AFAIK, never been set.
 
 Finally, this was also reported as PR kern/43474, back in 2002.
 

From: Danny Braniss <danny@cs.huji.ac.il>
To: Max Campos <mcampos@bpsw.biz>
Cc: bug-followup@FreeBSD.org
Subject: Re: bin/61239: [patch] bootp enhancement, places the dhcp tags into 
 the
Date: Sun, 07 Aug 2005 09:20:30 +0300

 > I've applied this patch to my own FreeBSD 5.4 system and it works  
 > great!  It would be great to see this applied.  There are several  
 > things that are currently broken that this patch fixes:
 > 
 > 1) /etc/rc.d/hostname - Enables us diskless folks to get the client's  
 > hostname from DHCP, rather than overriding the rc.conf (this is  
 > currently the ONLY reason for me to override rc.conf for each  
 > diskless client).
 > 
 > 2) /etc/rc.d/resolv - Enables a reasonable /etc/resolv.conf to be  
 > written from the DHCP packet.
 > 
 > Incidentally these features have been in the startup scripts since  
 > 2002 (/etc/rc.d/diskless v1.23), but the dhcp.* kenv variables have,  
 > AFAIK, never been set.
 > 
 this is because the dhcp.* are set by the modified bootp.
 
 > Finally, this was also reported as PR kern/43474, back in 2002.
 > 
 
 
State-Changed-From-To: open->closed 
State-Changed-By: dougb 
State-Changed-When: Thu Jul 7 07:07:51 UTC 2011 
State-Changed-Why:  

1. Old, unsupported FreeBSD version 
2. Likely OBE 



Responsible-Changed-From-To: freebsd-bugs->dougb 
Responsible-Changed-By: dougb 
Responsible-Changed-When: Thu Jul 7 07:07:51 UTC 2011 
Responsible-Changed-Why:  

I closed it 

http://www.freebsd.org/cgi/query-pr.cgi?pr=61239 
>Unformatted:
--- 5.2/src/lib/libstand/bootp.c.orig	Fri Mar 22 01:38:48 2002
+++ 5.2/src/lib/libstand/bootp.c	Wed Nov 26 17:28:16 2003
@@ -40,7 +40,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libstand/bootp.c,v 1.5 2002/03/21 23:38:48 obrien Exp $");
+__FBSDID("$FreeBSD: src/lib/libstand/bootp.c,v 1.4 2001/09/30 22:28:00 dillon Exp $");
 
 #include <sys/types.h>
 #include <netinet/in.h>
@@ -56,7 +56,6 @@
 #include "netif.h"
 #include "bootp.h"
 
-
 struct in_addr servip;
 
 static n_long	nmask, smask;
@@ -79,13 +78,13 @@
 #ifdef SUPPORT_DHCP
 static char expected_dhcpmsgtype = -1, dhcp_ok;
 struct in_addr dhcp_serverip;
+static void setenv_(u_char *cp, u_char *ep, void *);
 #endif
 
+
 /* Fetch required bootp infomation */
 void
-bootp(sock, flag)
-	int sock;
-	int flag;
+bootp(int sock, int flag)
 {
 	struct iodesc *d;
 	struct bootp *bp;
@@ -97,6 +96,7 @@
 		u_char header[HEADER_SIZE];
 		struct bootp rbootp;
 	} rbuf;
+     int nv;
 
 #ifdef BOOTP_DEBUG
  	if (debug)
@@ -124,37 +124,37 @@
 	MACPY(d->myea, bp->bp_chaddr);
 	strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file));
 	bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048));
+     nv = sizeof(vm_rfc1048);
+
 #ifdef SUPPORT_DHCP
-	bp->bp_vend[4] = TAG_DHCP_MSGTYPE;
-	bp->bp_vend[5] = 1;
-	bp->bp_vend[6] = DHCPDISCOVER;
+     bp->bp_vend[nv++] = TAG_DHCP_MSGTYPE;
+     bp->bp_vend[nv++] = 1;
+     bp->bp_vend[nv++] = DHCPDISCOVER;
 
 	/*
 	 * If we are booting from PXE, we want to send the string
-	 * 'PXEClient' to the DHCP server so you have the option of
+      * CLASSID to the DHCP server so you have the option of
 	 * only responding to PXE aware dhcp requests.
 	 */
+#define CLASSID	"FreeBSD"
 	if (flag & BOOTP_PXE) {
-		bp->bp_vend[7] = TAG_CLASSID;
-		bp->bp_vend[8] = 9;
-		bcopy("PXEClient", &bp->bp_vend[9], 9);
-		bp->bp_vend[18] = TAG_END;
-	} else
-		bp->bp_vend[7] = TAG_END;
-#else
-	bp->bp_vend[4] = TAG_END;
+	  int len;
+	  bp->bp_vend[nv++] = TAG_CLASSID;
+	  len = strlen(CLASSID);
+	  bp->bp_vend[nv++] = len;;
+	  bcopy(CLASSID, &bp->bp_vend[nv], len);
+	  nv += len;
+     }
+     expected_dhcpmsgtype = DHCPOFFER;
+     dhcp_ok = 0;
 #endif
+     bp->bp_vend[nv++] = TAG_END;
 
 	d->myip.s_addr = INADDR_ANY;
 	d->myport = htons(IPPORT_BOOTPC);
 	d->destip.s_addr = INADDR_BROADCAST;
 	d->destport = htons(IPPORT_BOOTPS);
 
-#ifdef SUPPORT_DHCP
-	expected_dhcpmsgtype = DHCPOFFER;
-	dhcp_ok = 0;
-#endif
-
 	if(sendrecv(d,
 		    bootpsend, bp, sizeof(*bp),
 		    bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp))
@@ -166,24 +166,32 @@
 #ifdef SUPPORT_DHCP
 	if(dhcp_ok) {
 		u_int32_t leasetime;
-		bp->bp_vend[6] = DHCPREQUEST;
-		bp->bp_vend[7] = TAG_REQ_ADDR;
-		bp->bp_vend[8] = 4;
+	  char		*p;
+
+	  nv = 6;
+	  bp->bp_vend[nv++] = DHCPREQUEST;
+	  bp->bp_vend[nv++] = TAG_REQ_ADDR;
+	  bp->bp_vend[nv++] = 4;
 		bcopy(&rbuf.rbootp.bp_yiaddr, &bp->bp_vend[9], 4);
-		bp->bp_vend[13] = TAG_SERVERID;
-		bp->bp_vend[14] = 4;
+	  nv += 4;
+	  bp->bp_vend[nv++] = TAG_SERVERID;
+	  bp->bp_vend[nv++] = 4;
 		bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4);
-		bp->bp_vend[19] = TAG_LEASETIME;
-		bp->bp_vend[20] = 4;
+	  nv += 4;
+	  bp->bp_vend[nv++] = TAG_LEASETIME;
+	  bp->bp_vend[nv++] = 4;
 		leasetime = htonl(300);
 		bcopy(&leasetime, &bp->bp_vend[21], 4);
+	  nv += 4;
 		if (flag & BOOTP_PXE) {
-			bp->bp_vend[25] = TAG_CLASSID;
-			bp->bp_vend[26] = 9;
-			bcopy("PXEClient", &bp->bp_vend[27], 9);
-			bp->bp_vend[36] = TAG_END;
-		} else
-			bp->bp_vend[25] = TAG_END;
+	       int len;
+	       bp->bp_vend[nv++] = TAG_CLASSID;
+	       len = strlen(CLASSID);
+	       bp->bp_vend[nv++] = len;
+	       bcopy(CLASSID, &bp->bp_vend[nv], len);
+	       nv += len;
+	  }
+	  bp->bp_vend[nv++] = TAG_END;
 
 		expected_dhcpmsgtype = DHCPACK;
 
@@ -199,7 +207,8 @@
 
 	myip = d->myip = rbuf.rbootp.bp_yiaddr;
 	servip = rbuf.rbootp.bp_siaddr;
-	if(rootip.s_addr == INADDR_ANY) rootip = servip;
+     if(rootip.s_addr == INADDR_ANY)
+	  rootip = servip;
 	bcopy(rbuf.rbootp.bp_file, bootfile, sizeof(bootfile));
 	bootfile[sizeof(bootfile) - 1] = '\0';
 
@@ -227,18 +236,16 @@
 	netmask = nmask;
 	if (smask)
 		netmask = smask;
+
 #ifdef BOOTP_DEBUG
 	if (debug)
 		printf("mask: %s\n", intoa(netmask));
-#endif
-
 	/* We need a gateway if root is on a different net */
 	if (!SAMENET(myip, rootip, netmask)) {
-#ifdef BOOTP_DEBUG
 		if (debug)
 			printf("need gateway for root ip\n");
-#endif
 	}
+#endif
 
 	/* Toss gateway if on a different net */
 	if (!SAMENET(myip, gateip, netmask)) {
@@ -255,10 +262,7 @@
 
 /* Transmit a bootp request */
 static ssize_t
-bootpsend(d, pkt, len)
-	struct iodesc *d;
-	void *pkt;
-	size_t len;
+bootpsend(struct iodesc *d, void *pkt, size_t len)
 {
 	struct bootp *bp;
 
@@ -279,11 +283,7 @@
 }
 
 static ssize_t
-bootprecv(d, pkt, len, tleft)
-struct iodesc *d;
-void *pkt;
-size_t len;
-time_t tleft;
+bootprecv(struct iodesc *d, void *pkt, size_t len, time_t tleft)
 {
 	ssize_t n;
 	struct bootp *bp;
@@ -332,15 +332,13 @@
 		printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend);
 
 	return(n);
-bad:
+ bad:
 	errno = 0;
 	return (-1);
 }
 
 static int
-vend_rfc1048(cp, len)
-	u_char *cp;
-	u_int len;
+vend_rfc1048(u_char *cp, u_int len)
 {
 	u_char *ep;
 	int size;
@@ -353,43 +351,53 @@
 	ep = cp + len;
 
 	/* Step over magic cookie */
-	cp += sizeof(int);
+     cp += sizeof(vm_rfc1048);
+
+
+     setenv_(cp, ep, NULL);
 
 	while (cp < ep) {
 		tag = *cp++;
 		size = *cp++;
-		if (tag == TAG_END)
+	  switch(tag) {
+	  case 0:
+	       continue;
+	  case TAG_END:
 			break;
-
-		if (tag == TAG_SUBNET_MASK) {
+	  case TAG_SUBNET_MASK:
 			bcopy(cp, &smask, sizeof(smask));
-		}
-		if (tag == TAG_GATEWAY) {
+	       break;
+	  case TAG_GATEWAY:
 			bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr));
-		}
-		if (tag == TAG_SWAPSERVER) {
+	       break;
+	  case TAG_SWAPSERVER:
 			/* let it override bp_siaddr */
 			bcopy(cp, &rootip.s_addr, sizeof(swapip.s_addr));
-		}
-		if (tag == TAG_ROOTPATH) {
+	       break;
+	  case TAG_ROOTPATH:
 			strncpy(rootpath, (char *)cp, sizeof(rootpath));
 			rootpath[size] = '\0';
-		}
-		if (tag == TAG_HOSTNAME) {
+	       break;
+	  case TAG_HOSTNAME:
 			strncpy(hostname, (char *)cp, sizeof(hostname));
 			hostname[size] = '\0';
-		}
+	       break;
 #ifdef SUPPORT_DHCP
-		if (tag == TAG_DHCP_MSGTYPE) {
+	  case TAG_DHCP_MSGTYPE:
 			if(*cp != expected_dhcpmsgtype)
 			    return(-1);
 			dhcp_ok = 1;
-		}
-		if (tag == TAG_SERVERID) {
-			bcopy(cp, &dhcp_serverip.s_addr,
-			      sizeof(dhcp_serverip.s_addr));
-		}
+	       break;
+	  case TAG_SERVERID:
+	       bcopy(cp, &dhcp_serverip.s_addr, sizeof(dhcp_serverip.s_addr));
+	       break;
 #endif
+#ifdef BOOTP_DEBUG
+	  default:
+	       if (debug)
+		    printf("unknown tag:%d size=%d\n", tag, size);
+#endif
+	  }
 		cp += size;
 	}
 	return(0);
@@ -397,8 +405,7 @@
 
 #ifdef BOOTP_VEND_CMU
 static void
-vend_cmu(cp)
-	u_char *cp;
+vend_cmu(u_char *cp)
 {
 	struct cmu_vend *vp;
 
@@ -416,3 +423,252 @@
 	}
 }
 #endif
+#define __IP	1
+#define	__TXT	2
+#define __8	3
+#define __16	4
+#define __32	5
+#define __VE	6
+#define _DHCP(num, fmt, dsc)	{num, fmt, dsc}
+
+struct dhcp_opt {
+     int	tag;
+     int	fmt;
+     char	*desc;
+} vndr_opt[] = {
+     /*
+       | Vendor Specific Options - FreeBSD's
+     */
+     _DHCP(0,	0,	""),	// prefix
+     _DHCP(1,	__TXT,	"kernel"),
+     _DHCP(2,	__TXT,	"kernelname"),
+     _DHCP(3,	__TXT,	"kernel_options"),
+     _DHCP(4,	__IP,	"usr-ip"),
+     _DHCP(5,	__TXT,	"conf-path"),
+     _DHCP(6,	__TXT,	"rc.conf0"),
+     _DHCP(7,	__TXT,	"rc.conf1"),
+     _DHCP(8,	__TXT,	"rc.conf2"),
+     _DHCP(9,	__TXT,	"rc.conf3"),
+     _DHCP(10,	__TXT,	"rc.conf4"),
+     _DHCP(11,	__TXT,	"rc.conf5"),
+     _DHCP(12,	__TXT,	"rc.conf6"),
+     _DHCP(13,	__TXT,	"rc.conf7"),
+     _DHCP(14,	__TXT,	"rc.conf8"),
+     _DHCP(15,	__TXT,	"rc.conf9"),
+     {0}
+}, dhcp_opt[] = {
+     /*
+       | DHCP Option names, formats and codes, from RFC1533.
+     */
+     _DHCP(0,	0,	"dhcp."),	// prefix
+     _DHCP(1,	__IP,	"subnet-mask"),
+     _DHCP(2,	__32,	"time-offset"),
+     _DHCP(3,	__IP,	"routers"),
+     _DHCP(4,	__IP,	"time-servers"),
+     _DHCP(5,	__IP,	"ien116-name-servers"),
+     _DHCP(6,	__IP,	"domain-name-servers"),
+     _DHCP(7,	__IP,	"log-servers"),
+     _DHCP(8,	__IP,	"cookie-servers"),
+     _DHCP(9,	__IP,	"lpr-servers"),
+     _DHCP(10,	__IP,	"impress-servers"),
+     _DHCP(11,	__IP,	"resource-location-servers"),
+     _DHCP(12,	__TXT,	"host-name"),
+     _DHCP(13,	__16,	"boot-size"),
+     _DHCP(14,	__TXT,	"merit-dump"),
+     _DHCP(15,	__TXT,	"domain-name"),
+     _DHCP(16,	__IP,	"swap-server"),
+     _DHCP(17,	__TXT,	"root-path"),
+     _DHCP(18,	__TXT,	"extensions-path"),
+     _DHCP(19,	__8,	"ip-forwarding"),
+     _DHCP(20,	__8,	"non-local-source-routing"),
+     _DHCP(21,	__IP,	"policy-filter"),
+     _DHCP(22,	__16,	"max-dgram-reassembly"),
+     _DHCP(23,	__8,	"default-ip-ttl"),
+     _DHCP(24,	__32,	"path-mtu-aging-timeout"),
+     _DHCP(25,	__16,	"path-mtu-plateau-table"),
+     _DHCP(26,	__16,	"interface-mtu"),
+     _DHCP(27,	__8,	"all-subnets-local"),
+     _DHCP(28,	__IP,	"broadcast-address"),
+     _DHCP(29,	__8,	"perform-mask-discovery"),
+     _DHCP(30,	__8,	"mask-supplier"),
+     _DHCP(31,	__8,	"router-discovery"),
+     _DHCP(32,	__IP,	"router-solicitation-address"),
+     _DHCP(33,	__IP,	"static-routes"),
+     _DHCP(34,	__8,	"trailer-encapsulation"),
+     _DHCP(35,	__32,	"arp-cache-timeout"),
+     _DHCP(36,	__8,	"ieee802-3-encapsulation"),
+     _DHCP(37,	__8,	"default-tcp-ttl"),
+     _DHCP(38,	__32,	"tcp-keepalive-interval"),
+     _DHCP(39,	__8,	"tcp-keepalive-garbage"),
+     _DHCP(40,	__TXT,	"nis-domain"),
+     _DHCP(41,	__IP,	"nis-servers"),
+     _DHCP(42,	__IP,	"ntp-servers"),
+     _DHCP(43,	__VE,	"vendor-encapsulated-options"),
+     _DHCP(44,	__IP,	"netbios-name-servers"),
+     _DHCP(45,	__IP,	"netbios-dd-server"),
+     _DHCP(46,	__8,	"netbios-node-type"),
+     _DHCP(47,	__TXT,	"netbios-scope"),
+     _DHCP(48,	__IP,	"font-servers"),
+     _DHCP(49,	__IP,	"x-display-manager"),
+     _DHCP(50,	__IP,	"dhcp-requested-address"),
+     _DHCP(51,	__32,	"dhcp-lease-time"),
+     _DHCP(52,	__8,	"dhcp-option-overload"),
+     _DHCP(53,	__8,	"dhcp-message-type"),
+     _DHCP(54,	__IP,	"dhcp-server-identifier"),
+     _DHCP(55,	__8,	"dhcp-parameter-request-list"),
+     _DHCP(56,	__TXT,	"dhcp-message"),
+     _DHCP(57,	__16,	"dhcp-max-message-size"),
+     _DHCP(58,	__32,	"dhcp-renewal-time"),
+     _DHCP(59,	__32,	"dhcp-rebinding-time"),
+     _DHCP(60,	__TXT,	"vendor-class-identifier"),
+     _DHCP(61,	__TXT,	"dhcp-client-identifier"),
+     _DHCP(64,	__TXT,	"nisplus-domain"),
+     _DHCP(65,	__IP,	"nisplus-servers"),
+     _DHCP(66,	__TXT,	"tftp-server-name"),
+     _DHCP(67,	__TXT,	"bootfile-name"),
+     _DHCP(68,	__IP,	"mobile-ip-home-agent"),
+     _DHCP(69,	__IP,	"smtp-server"),
+     _DHCP(70,	__IP,	"pop-server"),
+     _DHCP(71,	__IP,	"nntp-server"),
+     _DHCP(72,	__IP,	"www-server"),
+     _DHCP(73,	__IP,	"finger-server"),
+     _DHCP(74,	__IP,	"irc-server"),
+     _DHCP(75,	__IP,	"streettalk-server"),
+     _DHCP(76,	__IP,	"streettalk-directory-assistance-server"),
+     _DHCP(77,	__TXT,	"user-class"),
+     _DHCP(85,	__IP,	"nds-servers"),
+     _DHCP(86,	__TXT,	"nds-tree-name"),
+     _DHCP(87,	__TXT,	"nds-context"),
+     _DHCP(210,	__TXT,	"authenticate"),
+     {0}
+};
+
+struct dhcp_opt optdef = _DHCP(0, __TXT, "option-%d");
+
+#undef _DHCP
+
+static void
+setenv_(u_char *cp,  u_char *ep, void *ops)
+{
+     struct dhcp_opt *opts = (struct dhcp_opt *)ops;
+     u_char	*ncp;
+     int	size;
+     u_char	tag;
+     char	env[128], val[512], *vp, tags[512], *tp;
+     struct	in_addr in_ip;
+
+     ncp = cp;
+     tp = tags;
+     if(opts == NULL)
+	  opts = dhcp_opt;
+
+     while(ncp < ep) {
+	  struct dhcp_opt *op;
+
+	  tag = *ncp++;
+	  size = *ncp++;
+	  cp = ncp;
+	  ncp += size;
+
+	  if(tag == TAG_END)
+	       break;
+	  if(tag == 0)
+	       continue;
+
+	  for(op = opts+1; op->tag; op++)
+	       if(op->tag >= tag)
+		    break;
+	  if(op->tag != tag)
+	       op = &optdef;
+	  vp = val;
+	  switch(op->fmt) {
+	  case __VE: /* vendor specific */
+	       setenv_(cp, cp+size, vndr_opt);
+	       *vp = 0;
+	       break;
+
+	  case __IP:	/* ip address */
+	       if((size/4 * 5) > sizeof(val))
+		    size = (sizeof(val)/5) * 4;
+	       while(size > 0) {
+	       
+		    if(vp != val)
+			 *vp++ = ',';
+		    bcopy(cp, &in_ip.s_addr, sizeof(in_ip.s_addr));
+		    sprintf(vp, "%s", inet_ntoa(in_ip));
+		    vp += strlen(vp);
+		    size -= 4;
+		    cp += 4;
+	       }
+	       break;
+
+	  case __TXT:
+	       if(size >= sizeof(val))
+		    size = sizeof(val) - 1;
+	       bcopy(cp, val, size);
+	       val[size] = 0;
+	       break;
+
+	  case __32:
+	       if(((size / 32) * 33) > sizeof(val))
+		    size = sizeof(val) / 33 * 32;
+	       while(size > 0) {
+		    if(vp != val)
+			 *vp++ = ',';
+		    sprintf(vp, "%02x%02x%02x%02x", cp[0], cp[1], cp[2], cp[3]);
+		    cp += 4;
+		    size -= 4;
+		    vp += 8;
+	       }
+	       break;
+
+	  case __16:
+	       if(((size / 16) * 17) > sizeof(val))
+		    size = sizeof(val) / 17 * 32;     
+	       while(size > 0) {
+		    if(vp != val)
+			 *vp++ = ',';
+		    sprintf(vp, "%02x%02x", cp[0], cp[1]);
+		    cp += 2;
+		    size -= 2;
+		    vp += 4;
+	       }
+	       break;
+
+	  case __8:
+	       if(((size / 8) * 9) > sizeof(val))
+		    size = sizeof(val) / 9 * 8;
+	       while(size > 0) {
+		    if(vp != val)
+			 *vp++ = ',';
+		    sprintf(vp, "%02x", cp[0]);
+		    cp += 1;
+		    size -= 1;
+		    vp += 2;
+	       }
+	       break;
+	  }
+	  if((sizeof(tags) - strlen(tags)) > 3) {
+	       sprintf(tp, "%03d:", tag);
+	       tp += strlen(tp);
+	  }
+	  if(op == &optdef)
+	       sprintf(env, "%soption-%d", opts[0].desc, tag);
+	  else
+	       sprintf(env, "%s%s", opts[0].desc, op->desc);
+#ifdef BOOTP_DEBUG
+	  if(debug)
+	       printf("%s %s\n", env, val);
+#endif
+	  if(*val)
+	       setenv(env, val, 1);
+     }
+     if(tp != tags) {
+#ifdef BOOTP_DEBUG
+	  if(debug)
+	       printf("%s %s\n", "tags", opts[0].desc, tags);
+#endif
+	  sprintf(env, "%stags", opts[0].desc);
+	  setenv(env, tags, 1);
+     }
+}
