138451Smsmith/* $NetBSD: bootp.c,v 1.14 1998/02/16 11:10:54 drochner Exp $ */ 238451Smsmith 338451Smsmith/* 438451Smsmith * Copyright (c) 1992 Regents of the University of California. 538451Smsmith * All rights reserved. 638451Smsmith * 738451Smsmith * This software was developed by the Computer Systems Engineering group 838451Smsmith * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 938451Smsmith * contributed to Berkeley. 1038451Smsmith * 1138451Smsmith * Redistribution and use in source and binary forms, with or without 1238451Smsmith * modification, are permitted provided that the following conditions 1338451Smsmith * are met: 1438451Smsmith * 1. Redistributions of source code must retain the above copyright 1538451Smsmith * notice, this list of conditions and the following disclaimer. 1638451Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1738451Smsmith * notice, this list of conditions and the following disclaimer in the 1838451Smsmith * documentation and/or other materials provided with the distribution. 1938451Smsmith * 4. Neither the name of the University nor the names of its contributors 2038451Smsmith * may be used to endorse or promote products derived from this software 2138451Smsmith * without specific prior written permission. 2238451Smsmith * 2338451Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2438451Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2538451Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2638451Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2738451Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2838451Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2938451Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3038451Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3138451Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3238451Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3338451Smsmith * SUCH DAMAGE. 3438451Smsmith * 3538451Smsmith * @(#) Header: bootp.c,v 1.4 93/09/11 03:13:51 leres Exp (LBL) 3638451Smsmith */ 3738451Smsmith 3884221Sdillon#include <sys/cdefs.h> 3984221Sdillon__FBSDID("$FreeBSD: stable/11/stand/libsa/bootp.c 344408 2019-02-21 02:43:48Z kevans $"); 4084221Sdillon 41329100Skevans#include <stddef.h> 4238451Smsmith#include <sys/types.h> 43329099Skevans#include <sys/limits.h> 44297150Sian#include <sys/endian.h> 4538451Smsmith#include <netinet/in.h> 4638451Smsmith#include <netinet/in_systm.h> 4738451Smsmith 4838451Smsmith#include <string.h> 4938451Smsmith 5038451Smsmith#define BOOTP_DEBUGxx 5138451Smsmith#define SUPPORT_DHCP 5238451Smsmith 53185643Sluigi#define DHCP_ENV_NOVENDOR 1 /* do not parse vendor options */ 54185643Sluigi#define DHCP_ENV_PXE 10 /* assume pxe vendor options */ 55193189Sed#define DHCP_ENV_FREEBSD 11 /* assume freebsd vendor options */ 56185643Sluigi/* set DHCP_ENV to one of the values above to export dhcp options to kenv */ 57185643Sluigi#define DHCP_ENV DHCP_ENV_NO_VENDOR 58185643Sluigi 5938451Smsmith#include "stand.h" 6038451Smsmith#include "net.h" 6138451Smsmith#include "netif.h" 6238451Smsmith#include "bootp.h" 6338451Smsmith 6438451Smsmith 6538451Smsmithstruct in_addr servip; 6638451Smsmith 6738451Smsmithstatic time_t bot; 6838451Smsmith 6938451Smsmithstatic char vm_rfc1048[4] = VM_RFC1048; 7038451Smsmith#ifdef BOOTP_VEND_CMU 7138451Smsmithstatic char vm_cmu[4] = VM_CMU; 7238451Smsmith#endif 7338451Smsmith 7438451Smsmith/* Local forwards */ 7538451Smsmithstatic ssize_t bootpsend(struct iodesc *, void *, size_t); 76330898Skevansstatic ssize_t bootprecv(struct iodesc *, void **, void **, time_t, void *); 7738451Smsmithstatic int vend_rfc1048(u_char *, u_int); 7838451Smsmith#ifdef BOOTP_VEND_CMU 7938451Smsmithstatic void vend_cmu(u_char *); 8038451Smsmith#endif 8138451Smsmith 82185643Sluigi#ifdef DHCP_ENV /* export the dhcp response to kenv */ 83185643Sluigistruct dhcp_opt; 84185643Sluigistatic void setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts); 85185643Sluigi#else 86185643Sluigi#define setenv_(a, b, c) 87185643Sluigi#endif 88185643Sluigi 8938451Smsmith#ifdef SUPPORT_DHCP 9038451Smsmithstatic char expected_dhcpmsgtype = -1, dhcp_ok; 9138451Smsmithstruct in_addr dhcp_serverip; 9238451Smsmith#endif 93329100Skevansstruct bootp *bootp_response; 94329100Skevanssize_t bootp_response_size; 9538451Smsmith 96329100Skevansstatic void 97329100Skevansbootp_fill_request(unsigned char *bp_vend) 98329100Skevans{ 99329100Skevans /* 100329100Skevans * We are booting from PXE, we want to send the string 101329100Skevans * 'PXEClient' to the DHCP server so you have the option of 102329100Skevans * only responding to PXE aware dhcp requests. 103329100Skevans */ 104329100Skevans bp_vend[0] = TAG_CLASSID; 105329100Skevans bp_vend[1] = 9; 106329100Skevans bcopy("PXEClient", &bp_vend[2], 9); 107329100Skevans bp_vend[11] = TAG_USER_CLASS; 108329100Skevans /* len of each user class + number of user class */ 109329100Skevans bp_vend[12] = 8; 110329100Skevans /* len of the first user class */ 111329100Skevans bp_vend[13] = 7; 112329100Skevans bcopy("FreeBSD", &bp_vend[14], 7); 113329100Skevans bp_vend[21] = TAG_PARAM_REQ; 114329100Skevans bp_vend[22] = 7; 115329100Skevans bp_vend[23] = TAG_ROOTPATH; 116329100Skevans bp_vend[24] = TAG_HOSTNAME; 117329100Skevans bp_vend[25] = TAG_SWAPSERVER; 118329100Skevans bp_vend[26] = TAG_GATEWAY; 119329100Skevans bp_vend[27] = TAG_SUBNET_MASK; 120329100Skevans bp_vend[28] = TAG_INTF_MTU; 121329100Skevans bp_vend[29] = TAG_SERVERID; 122329100Skevans bp_vend[30] = TAG_END; 123329100Skevans} 124329100Skevans 12538451Smsmith/* Fetch required bootp infomation */ 12638451Smsmithvoid 127329100Skevansbootp(int sock) 12838451Smsmith{ 129329100Skevans void *pkt; 13038451Smsmith struct iodesc *d; 13192913Sobrien struct bootp *bp; 13238451Smsmith struct { 13338451Smsmith u_char header[HEADER_SIZE]; 13438451Smsmith struct bootp wbootp; 13538451Smsmith } wbuf; 136329100Skevans struct bootp *rbootp; 13738451Smsmith 13838451Smsmith#ifdef BOOTP_DEBUG 13938451Smsmith if (debug) 14038451Smsmith printf("bootp: socket=%d\n", sock); 14138451Smsmith#endif 14238451Smsmith if (!bot) 14338451Smsmith bot = getsecs(); 14438451Smsmith 14538451Smsmith if (!(d = socktodesc(sock))) { 14638451Smsmith printf("bootp: bad socket. %d\n", sock); 14738451Smsmith return; 14838451Smsmith } 14938451Smsmith#ifdef BOOTP_DEBUG 15038451Smsmith if (debug) 15138451Smsmith printf("bootp: d=%lx\n", (long)d); 15238451Smsmith#endif 15338451Smsmith 15438451Smsmith bp = &wbuf.wbootp; 15538451Smsmith bzero(bp, sizeof(*bp)); 15638451Smsmith 15738451Smsmith bp->bp_op = BOOTREQUEST; 15838451Smsmith bp->bp_htype = 1; /* 10Mb Ethernet (48 bits) */ 15938451Smsmith bp->bp_hlen = 6; 16038451Smsmith bp->bp_xid = htonl(d->xid); 16138451Smsmith MACPY(d->myea, bp->bp_chaddr); 16238451Smsmith strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file)); 16338451Smsmith bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)); 16438451Smsmith#ifdef SUPPORT_DHCP 16538451Smsmith bp->bp_vend[4] = TAG_DHCP_MSGTYPE; 16638451Smsmith bp->bp_vend[5] = 1; 16738451Smsmith bp->bp_vend[6] = DHCPDISCOVER; 168329100Skevans bootp_fill_request(&bp->bp_vend[7]); 16964527Sps 17038451Smsmith#else 17138451Smsmith bp->bp_vend[4] = TAG_END; 17238451Smsmith#endif 17338451Smsmith 17438451Smsmith d->myip.s_addr = INADDR_ANY; 17538451Smsmith d->myport = htons(IPPORT_BOOTPC); 17638451Smsmith d->destip.s_addr = INADDR_BROADCAST; 17738451Smsmith d->destport = htons(IPPORT_BOOTPS); 17838451Smsmith 17938451Smsmith#ifdef SUPPORT_DHCP 18038451Smsmith expected_dhcpmsgtype = DHCPOFFER; 18138451Smsmith dhcp_ok = 0; 18238451Smsmith#endif 18338451Smsmith 18438451Smsmith if(sendrecv(d, 18538451Smsmith bootpsend, bp, sizeof(*bp), 186330898Skevans bootprecv, &pkt, (void **)&rbootp, NULL) == -1) { 18738451Smsmith printf("bootp: no reply\n"); 18838451Smsmith return; 18938451Smsmith } 19038451Smsmith 19138451Smsmith#ifdef SUPPORT_DHCP 19238451Smsmith if(dhcp_ok) { 193332154Skevans uint32_t leasetime; 19438451Smsmith bp->bp_vend[6] = DHCPREQUEST; 19538451Smsmith bp->bp_vend[7] = TAG_REQ_ADDR; 19638451Smsmith bp->bp_vend[8] = 4; 197329100Skevans bcopy(&rbootp->bp_yiaddr, &bp->bp_vend[9], 4); 19838451Smsmith bp->bp_vend[13] = TAG_SERVERID; 19938451Smsmith bp->bp_vend[14] = 4; 20038451Smsmith bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4); 20138451Smsmith bp->bp_vend[19] = TAG_LEASETIME; 20238451Smsmith bp->bp_vend[20] = 4; 20338451Smsmith leasetime = htonl(300); 20438451Smsmith bcopy(&leasetime, &bp->bp_vend[21], 4); 205329100Skevans bootp_fill_request(&bp->bp_vend[25]); 20638451Smsmith 20738451Smsmith expected_dhcpmsgtype = DHCPACK; 20838451Smsmith 209329100Skevans free(pkt); 21038451Smsmith if(sendrecv(d, 21138451Smsmith bootpsend, bp, sizeof(*bp), 212330898Skevans bootprecv, &pkt, (void **)&rbootp, NULL) == -1) { 21338451Smsmith printf("DHCPREQUEST failed\n"); 21438451Smsmith return; 21538451Smsmith } 21638451Smsmith } 21738451Smsmith#endif 21838451Smsmith 219329100Skevans myip = d->myip = rbootp->bp_yiaddr; 220329100Skevans servip = rbootp->bp_siaddr; 221329100Skevans if (rootip.s_addr == INADDR_ANY) 222329100Skevans rootip = servip; 223329100Skevans bcopy(rbootp->bp_file, bootfile, sizeof(bootfile)); 22438451Smsmith bootfile[sizeof(bootfile) - 1] = '\0'; 22538451Smsmith 226312930Sbapt if (!netmask) { 227312930Sbapt if (IN_CLASSA(ntohl(myip.s_addr))) 228312930Sbapt netmask = htonl(IN_CLASSA_NET); 229312930Sbapt else if (IN_CLASSB(ntohl(myip.s_addr))) 230312930Sbapt netmask = htonl(IN_CLASSB_NET); 231312930Sbapt else 232312930Sbapt netmask = htonl(IN_CLASSC_NET); 23338451Smsmith#ifdef BOOTP_DEBUG 23438451Smsmith if (debug) 235312930Sbapt printf("'native netmask' is %s\n", intoa(netmask)); 23638451Smsmith#endif 23738451Smsmith } 23838451Smsmith 23938451Smsmith#ifdef BOOTP_DEBUG 24038451Smsmith if (debug) 24138451Smsmith printf("mask: %s\n", intoa(netmask)); 24238451Smsmith#endif 24338451Smsmith 24438451Smsmith /* We need a gateway if root is on a different net */ 24538451Smsmith if (!SAMENET(myip, rootip, netmask)) { 24638451Smsmith#ifdef BOOTP_DEBUG 24738451Smsmith if (debug) 24838451Smsmith printf("need gateway for root ip\n"); 24938451Smsmith#endif 25038451Smsmith } 25138451Smsmith 25238451Smsmith /* Toss gateway if on a different net */ 25338451Smsmith if (!SAMENET(myip, gateip, netmask)) { 25438451Smsmith#ifdef BOOTP_DEBUG 25538451Smsmith if (debug) 25638451Smsmith printf("gateway ip (%s) bad\n", inet_ntoa(gateip)); 25738451Smsmith#endif 25838451Smsmith gateip.s_addr = 0; 25938451Smsmith } 26038451Smsmith 26138451Smsmith /* Bump xid so next request will be unique. */ 26238451Smsmith ++d->xid; 263329100Skevans free(pkt); 26438451Smsmith} 26538451Smsmith 26638451Smsmith/* Transmit a bootp request */ 26738451Smsmithstatic ssize_t 268329100Skevansbootpsend(struct iodesc *d, void *pkt, size_t len) 26938451Smsmith{ 27092913Sobrien struct bootp *bp; 27138451Smsmith 27238451Smsmith#ifdef BOOTP_DEBUG 27338451Smsmith if (debug) 27438451Smsmith printf("bootpsend: d=%lx called.\n", (long)d); 27538451Smsmith#endif 27638451Smsmith 27738451Smsmith bp = pkt; 27838451Smsmith bp->bp_secs = htons((u_short)(getsecs() - bot)); 27938451Smsmith 28038451Smsmith#ifdef BOOTP_DEBUG 28138451Smsmith if (debug) 28238451Smsmith printf("bootpsend: calling sendudp\n"); 28338451Smsmith#endif 28438451Smsmith 28538451Smsmith return (sendudp(d, pkt, len)); 28638451Smsmith} 28738451Smsmith 28838451Smsmithstatic ssize_t 289330898Skevansbootprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft, 290330898Skevans void *extra) 29138451Smsmith{ 29292913Sobrien ssize_t n; 29392913Sobrien struct bootp *bp; 294329100Skevans void *ptr; 29538451Smsmith 296329100Skevans#ifdef BOOTP_DEBUG 29738451Smsmith if (debug) 29838451Smsmith printf("bootp_recvoffer: called\n"); 29938451Smsmith#endif 30038451Smsmith 301329100Skevans ptr = NULL; 302329100Skevans n = readudp(d, &ptr, (void **)&bp, tleft); 30338451Smsmith if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE) 30438451Smsmith goto bad; 30538451Smsmith 30638451Smsmith#ifdef BOOTP_DEBUG 30738451Smsmith if (debug) 308329100Skevans printf("bootprecv: checked. bp = %p, n = %zd\n", bp, n); 30938451Smsmith#endif 31038451Smsmith if (bp->bp_xid != htonl(d->xid)) { 31138451Smsmith#ifdef BOOTP_DEBUG 31238451Smsmith if (debug) { 31338451Smsmith printf("bootprecv: expected xid 0x%lx, got 0x%x\n", 31438451Smsmith d->xid, ntohl(bp->bp_xid)); 31538451Smsmith } 31638451Smsmith#endif 31738451Smsmith goto bad; 31838451Smsmith } 31938451Smsmith 32038451Smsmith#ifdef BOOTP_DEBUG 32138451Smsmith if (debug) 32238451Smsmith printf("bootprecv: got one!\n"); 32338451Smsmith#endif 32438451Smsmith 32538451Smsmith /* Suck out vendor info */ 32638451Smsmith if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) { 327329100Skevans int vsize = n - offsetof(struct bootp, bp_vend); 328329100Skevans if (vend_rfc1048(bp->bp_vend, vsize) != 0) 32938451Smsmith goto bad; 330329100Skevans 331329100Skevans /* Save copy of bootp reply or DHCP ACK message */ 332329100Skevans if (bp->bp_op == BOOTREPLY && 333329100Skevans ((dhcp_ok == 1 && expected_dhcpmsgtype == DHCPACK) || 334329100Skevans dhcp_ok == 0)) { 335329100Skevans free(bootp_response); 336329100Skevans bootp_response = malloc(n); 337329100Skevans if (bootp_response != NULL) { 338329100Skevans bootp_response_size = n; 339329100Skevans bcopy(bp, bootp_response, bootp_response_size); 340329100Skevans } 341329100Skevans } 34238451Smsmith } 34338451Smsmith#ifdef BOOTP_VEND_CMU 34438451Smsmith else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0) 34538451Smsmith vend_cmu(bp->bp_vend); 34638451Smsmith#endif 34738451Smsmith else 34838451Smsmith printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend); 34938451Smsmith 350329100Skevans *pkt = ptr; 351329100Skevans *payload = bp; 352329100Skevans return (n); 35338451Smsmithbad: 354329100Skevans free(ptr); 35538451Smsmith errno = 0; 35638451Smsmith return (-1); 35738451Smsmith} 35838451Smsmith 35938451Smsmithstatic int 360329100Skevansvend_rfc1048(u_char *cp, u_int len) 36138451Smsmith{ 36292913Sobrien u_char *ep; 36392913Sobrien int size; 36492913Sobrien u_char tag; 365292583Sian const char *val; 36638451Smsmith 36738451Smsmith#ifdef BOOTP_DEBUG 36838451Smsmith if (debug) 36938451Smsmith printf("vend_rfc1048 bootp info. len=%d\n", len); 37038451Smsmith#endif 37138451Smsmith ep = cp + len; 37238451Smsmith 37338451Smsmith /* Step over magic cookie */ 37438451Smsmith cp += sizeof(int); 37538451Smsmith 376185643Sluigi setenv_(cp, ep, NULL); 377185643Sluigi 37838451Smsmith while (cp < ep) { 37938451Smsmith tag = *cp++; 38038451Smsmith size = *cp++; 38138451Smsmith if (tag == TAG_END) 38238451Smsmith break; 38338451Smsmith 38438451Smsmith if (tag == TAG_SUBNET_MASK) { 385312930Sbapt bcopy(cp, &netmask, sizeof(netmask)); 38638451Smsmith } 38738451Smsmith if (tag == TAG_GATEWAY) { 38838451Smsmith bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr)); 38938451Smsmith } 39038451Smsmith if (tag == TAG_SWAPSERVER) { 39138451Smsmith /* let it override bp_siaddr */ 392292583Sian bcopy(cp, &rootip.s_addr, sizeof(rootip.s_addr)); 39338451Smsmith } 39438451Smsmith if (tag == TAG_ROOTPATH) { 395292583Sian if ((val = getenv("dhcp.root-path")) == NULL) 396292583Sian val = (const char *)cp; 397292583Sian strlcpy(rootpath, val, sizeof(rootpath)); 39838451Smsmith } 39938451Smsmith if (tag == TAG_HOSTNAME) { 400292583Sian if ((val = getenv("dhcp.host-name")) == NULL) 401292583Sian val = (const char *)cp; 402292583Sian strlcpy(hostname, val, sizeof(hostname)); 40338451Smsmith } 404297150Sian if (tag == TAG_INTF_MTU) { 405329099Skevans intf_mtu = 0; 406297150Sian if ((val = getenv("dhcp.interface-mtu")) != NULL) { 407329099Skevans unsigned long tmp; 408329099Skevans char *end; 409329099Skevans 410329099Skevans errno = 0; 411329099Skevans /* 412329099Skevans * Do not allow MTU to exceed max IPv4 packet 413329099Skevans * size, max value of 16-bit word. 414329099Skevans */ 415329099Skevans tmp = strtoul(val, &end, 0); 416329099Skevans if (errno != 0 || 417329099Skevans *val == '\0' || *end != '\0' || 418329099Skevans tmp > USHRT_MAX) { 419329099Skevans printf("%s: bad value: \"%s\", " 420329099Skevans "ignoring\n", 421329099Skevans "dhcp.interface-mtu", val); 422329099Skevans } else { 423329099Skevans intf_mtu = (u_int)tmp; 424329099Skevans } 425329099Skevans } 426329099Skevans if (intf_mtu <= 0) 427297150Sian intf_mtu = be16dec(cp); 428297150Sian } 42938451Smsmith#ifdef SUPPORT_DHCP 43038451Smsmith if (tag == TAG_DHCP_MSGTYPE) { 43138451Smsmith if(*cp != expected_dhcpmsgtype) 43238451Smsmith return(-1); 43338451Smsmith dhcp_ok = 1; 43438451Smsmith } 43538451Smsmith if (tag == TAG_SERVERID) { 43638451Smsmith bcopy(cp, &dhcp_serverip.s_addr, 43738451Smsmith sizeof(dhcp_serverip.s_addr)); 43838451Smsmith } 43938451Smsmith#endif 44038451Smsmith cp += size; 44138451Smsmith } 44238451Smsmith return(0); 44338451Smsmith} 44438451Smsmith 44538451Smsmith#ifdef BOOTP_VEND_CMU 44638451Smsmithstatic void 447329100Skevansvend_cmu(u_char *cp) 44838451Smsmith{ 44992913Sobrien struct cmu_vend *vp; 45038451Smsmith 45138451Smsmith#ifdef BOOTP_DEBUG 45238451Smsmith if (debug) 45338451Smsmith printf("vend_cmu bootp info.\n"); 45438451Smsmith#endif 45538451Smsmith vp = (struct cmu_vend *)cp; 45638451Smsmith 45738451Smsmith if (vp->v_smask.s_addr != 0) { 458312930Sbapt netmask = vp->v_smask.s_addr; 45938451Smsmith } 46038451Smsmith if (vp->v_dgate.s_addr != 0) { 46138451Smsmith gateip = vp->v_dgate; 46238451Smsmith } 46338451Smsmith} 46438451Smsmith#endif 465185643Sluigi 466185643Sluigi#ifdef DHCP_ENV 467185643Sluigi/* 468185643Sluigi * Parse DHCP options and store them into kenv variables. 469185643Sluigi * Original code from Danny Braniss, modifications by Luigi Rizzo. 470185643Sluigi * 471185643Sluigi * The parser is driven by tables which specify the type and name of 472185643Sluigi * each dhcp option and how it appears in kenv. 473185643Sluigi * The first entry in the list contains the prefix used to set the kenv 474185643Sluigi * name (including the . if needed), the last entry must have a 0 tag. 475185643Sluigi * Entries do not need to be sorted though it helps for readability. 476185643Sluigi * 477185643Sluigi * Certain vendor-specific tables can be enabled according to DHCP_ENV. 478185643Sluigi * Set it to 0 if you don't want any. 479185643Sluigi */ 480185643Sluigienum opt_fmt { __NONE = 0, 481185643Sluigi __8 = 1, __16 = 2, __32 = 4, /* Unsigned fields, value=size */ 482185643Sluigi __IP, /* IPv4 address */ 483185643Sluigi __TXT, /* C string */ 484185643Sluigi __BYTES, /* byte sequence, printed %02x */ 485185643Sluigi __INDIR, /* name=value */ 486185643Sluigi __ILIST, /* name=value;name=value ... */ 487185643Sluigi __VE, /* vendor specific, recurse */ 488185643Sluigi}; 489185643Sluigi 490185643Sluigistruct dhcp_opt { 491185643Sluigi uint8_t tag; 492185643Sluigi uint8_t fmt; 493185643Sluigi const char *desc; 494185643Sluigi}; 495185643Sluigi 496185643Sluigistatic struct dhcp_opt vndr_opt[] = { /* Vendor Specific Options */ 497185643Sluigi#if DHCP_ENV == DHCP_ENV_FREEBSD /* FreeBSD table in the original code */ 498185643Sluigi {0, 0, "FreeBSD"}, /* prefix */ 499185643Sluigi {1, __TXT, "kernel"}, 500185643Sluigi {2, __TXT, "kernelname"}, 501185643Sluigi {3, __TXT, "kernel_options"}, 502185643Sluigi {4, __IP, "usr-ip"}, 503185643Sluigi {5, __TXT, "conf-path"}, 504185643Sluigi {6, __TXT, "rc.conf0"}, 505185643Sluigi {7, __TXT, "rc.conf1"}, 506185643Sluigi {8, __TXT, "rc.conf2"}, 507185643Sluigi {9, __TXT, "rc.conf3"}, 508185643Sluigi {10, __TXT, "rc.conf4"}, 509185643Sluigi {11, __TXT, "rc.conf5"}, 510185643Sluigi {12, __TXT, "rc.conf6"}, 511185643Sluigi {13, __TXT, "rc.conf7"}, 512185643Sluigi {14, __TXT, "rc.conf8"}, 513185643Sluigi {15, __TXT, "rc.conf9"}, 514185643Sluigi 515185643Sluigi {20, __TXT, "boot.nfsroot.options"}, 516185643Sluigi 517185643Sluigi {245, __INDIR, ""}, 518185643Sluigi {246, __INDIR, ""}, 519185643Sluigi {247, __INDIR, ""}, 520185643Sluigi {248, __INDIR, ""}, 521185643Sluigi {249, __INDIR, ""}, 522185643Sluigi {250, __INDIR, ""}, 523185643Sluigi {251, __INDIR, ""}, 524185643Sluigi {252, __INDIR, ""}, 525185643Sluigi {253, __INDIR, ""}, 526185643Sluigi {254, __INDIR, ""}, 527185643Sluigi 528185643Sluigi#elif DHCP_ENV == DHCP_ENV_PXE /* some pxe options, RFC4578 */ 529185643Sluigi {0, 0, "pxe"}, /* prefix */ 530185643Sluigi {93, __16, "system-architecture"}, 531185643Sluigi {94, __BYTES, "network-interface"}, 532185643Sluigi {97, __BYTES, "machine-identifier"}, 533185643Sluigi#else /* default (empty) table */ 534186799Sluigi {0, 0, "dhcp.vendor."}, /* prefix */ 535185643Sluigi#endif 536185643Sluigi {0, __TXT, "%soption-%d"} 537185643Sluigi}; 538185643Sluigi 539185643Sluigistatic struct dhcp_opt dhcp_opt[] = { 540185643Sluigi /* DHCP Option names, formats and codes, from RFC2132. */ 541185643Sluigi {0, 0, "dhcp."}, // prefix 542185643Sluigi {1, __IP, "subnet-mask"}, 543185643Sluigi {2, __32, "time-offset"}, /* this is signed */ 544185643Sluigi {3, __IP, "routers"}, 545185643Sluigi {4, __IP, "time-servers"}, 546185643Sluigi {5, __IP, "ien116-name-servers"}, 547185643Sluigi {6, __IP, "domain-name-servers"}, 548185643Sluigi {7, __IP, "log-servers"}, 549185643Sluigi {8, __IP, "cookie-servers"}, 550185643Sluigi {9, __IP, "lpr-servers"}, 551185643Sluigi {10, __IP, "impress-servers"}, 552185643Sluigi {11, __IP, "resource-location-servers"}, 553185643Sluigi {12, __TXT, "host-name"}, 554185643Sluigi {13, __16, "boot-size"}, 555185643Sluigi {14, __TXT, "merit-dump"}, 556185643Sluigi {15, __TXT, "domain-name"}, 557185643Sluigi {16, __IP, "swap-server"}, 558185643Sluigi {17, __TXT, "root-path"}, 559185643Sluigi {18, __TXT, "extensions-path"}, 560185643Sluigi {19, __8, "ip-forwarding"}, 561185643Sluigi {20, __8, "non-local-source-routing"}, 562185643Sluigi {21, __IP, "policy-filter"}, 563185643Sluigi {22, __16, "max-dgram-reassembly"}, 564185643Sluigi {23, __8, "default-ip-ttl"}, 565185643Sluigi {24, __32, "path-mtu-aging-timeout"}, 566185643Sluigi {25, __16, "path-mtu-plateau-table"}, 567185643Sluigi {26, __16, "interface-mtu"}, 568185643Sluigi {27, __8, "all-subnets-local"}, 569185643Sluigi {28, __IP, "broadcast-address"}, 570185643Sluigi {29, __8, "perform-mask-discovery"}, 571185643Sluigi {30, __8, "mask-supplier"}, 572185643Sluigi {31, __8, "perform-router-discovery"}, 573185643Sluigi {32, __IP, "router-solicitation-address"}, 574185643Sluigi {33, __IP, "static-routes"}, 575185643Sluigi {34, __8, "trailer-encapsulation"}, 576185643Sluigi {35, __32, "arp-cache-timeout"}, 577185643Sluigi {36, __8, "ieee802-3-encapsulation"}, 578185643Sluigi {37, __8, "default-tcp-ttl"}, 579185643Sluigi {38, __32, "tcp-keepalive-interval"}, 580185643Sluigi {39, __8, "tcp-keepalive-garbage"}, 581185643Sluigi {40, __TXT, "nis-domain"}, 582185643Sluigi {41, __IP, "nis-servers"}, 583185643Sluigi {42, __IP, "ntp-servers"}, 584185643Sluigi {43, __VE, "vendor-encapsulated-options"}, 585185643Sluigi {44, __IP, "netbios-name-servers"}, 586185643Sluigi {45, __IP, "netbios-dd-server"}, 587185643Sluigi {46, __8, "netbios-node-type"}, 588185643Sluigi {47, __TXT, "netbios-scope"}, 589185643Sluigi {48, __IP, "x-font-servers"}, 590185643Sluigi {49, __IP, "x-display-managers"}, 591185643Sluigi {50, __IP, "dhcp-requested-address"}, 592185643Sluigi {51, __32, "dhcp-lease-time"}, 593185643Sluigi {52, __8, "dhcp-option-overload"}, 594185643Sluigi {53, __8, "dhcp-message-type"}, 595185643Sluigi {54, __IP, "dhcp-server-identifier"}, 596185643Sluigi {55, __8, "dhcp-parameter-request-list"}, 597185643Sluigi {56, __TXT, "dhcp-message"}, 598185643Sluigi {57, __16, "dhcp-max-message-size"}, 599185643Sluigi {58, __32, "dhcp-renewal-time"}, 600185643Sluigi {59, __32, "dhcp-rebinding-time"}, 601185643Sluigi {60, __TXT, "vendor-class-identifier"}, 602185643Sluigi {61, __TXT, "dhcp-client-identifier"}, 603185643Sluigi {64, __TXT, "nisplus-domain"}, 604185643Sluigi {65, __IP, "nisplus-servers"}, 605185643Sluigi {66, __TXT, "tftp-server-name"}, 606185643Sluigi {67, __TXT, "bootfile-name"}, 607185643Sluigi {68, __IP, "mobile-ip-home-agent"}, 608185643Sluigi {69, __IP, "smtp-server"}, 609185643Sluigi {70, __IP, "pop-server"}, 610185643Sluigi {71, __IP, "nntp-server"}, 611185643Sluigi {72, __IP, "www-server"}, 612185643Sluigi {73, __IP, "finger-server"}, 613185643Sluigi {74, __IP, "irc-server"}, 614185643Sluigi {75, __IP, "streettalk-server"}, 615185643Sluigi {76, __IP, "streettalk-directory-assistance-server"}, 616185643Sluigi {77, __TXT, "user-class"}, 617185643Sluigi {85, __IP, "nds-servers"}, 618185643Sluigi {86, __TXT, "nds-tree-name"}, 619185643Sluigi {87, __TXT, "nds-context"}, 620185643Sluigi {210, __TXT, "authenticate"}, 621185643Sluigi 622185643Sluigi /* use the following entries for arbitrary variables */ 623185643Sluigi {246, __ILIST, ""}, 624185643Sluigi {247, __ILIST, ""}, 625185643Sluigi {248, __ILIST, ""}, 626185643Sluigi {249, __ILIST, ""}, 627185643Sluigi {250, __INDIR, ""}, 628185643Sluigi {251, __INDIR, ""}, 629185643Sluigi {252, __INDIR, ""}, 630185643Sluigi {253, __INDIR, ""}, 631185643Sluigi {254, __INDIR, ""}, 632185643Sluigi {0, __TXT, "%soption-%d"} 633185643Sluigi}; 634185643Sluigi 635185643Sluigi/* 636185643Sluigi * parse a dhcp response, set environment variables translating options 637185643Sluigi * names and values according to the tables above. Also set dhcp.tags 638185643Sluigi * to the list of selected tags. 639185643Sluigi */ 640185643Sluigistatic void 641185643Sluigisetenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts) 642185643Sluigi{ 643185643Sluigi u_char *ncp; 644185643Sluigi u_char tag; 645185643Sluigi char tags[512], *tp; /* the list of tags */ 646185643Sluigi 647185643Sluigi#define FLD_SEP ',' /* separator in list of elements */ 648185643Sluigi ncp = cp; 649185643Sluigi tp = tags; 650185643Sluigi if (opts == NULL) 651185643Sluigi opts = dhcp_opt; 652185643Sluigi 653185643Sluigi while (ncp < ep) { 654185643Sluigi unsigned int size; /* option size */ 655185643Sluigi char *vp, *endv, buf[256]; /* the value buffer */ 656185643Sluigi struct dhcp_opt *op; 657185643Sluigi 658185643Sluigi tag = *ncp++; /* extract tag and size */ 659185643Sluigi size = *ncp++; 660185643Sluigi cp = ncp; /* current payload */ 661185643Sluigi ncp += size; /* point to the next option */ 662185643Sluigi 663185643Sluigi if (tag == TAG_END) 664185643Sluigi break; 665185643Sluigi if (tag == 0) 666185643Sluigi continue; 667185643Sluigi 668185643Sluigi for (op = opts+1; op->tag && op->tag != tag; op++) 669185643Sluigi ; 670185643Sluigi /* if not found we end up on the default entry */ 671185643Sluigi 672185643Sluigi /* 673185643Sluigi * Copy data into the buffer. libstand does not have snprintf so we 674185643Sluigi * need to be careful with sprintf(). With strings, the source is 675185643Sluigi * always <256 char so shorter than the buffer so we are safe; with 676185643Sluigi * other arguments, the longest string is inet_ntoa which is 16 bytes 677185643Sluigi * so we make sure to have always enough room in the string before 678185643Sluigi * trying an sprint. 679185643Sluigi */ 680185643Sluigi vp = buf; 681185643Sluigi *vp = '\0'; 682185643Sluigi endv = buf + sizeof(buf) - 1 - 16; /* last valid write position */ 683185643Sluigi 684185643Sluigi switch(op->fmt) { 685185643Sluigi case __NONE: 686185643Sluigi break; /* should not happen */ 687185643Sluigi 688185643Sluigi case __VE: /* recurse, vendor specific */ 689185643Sluigi setenv_(cp, cp+size, vndr_opt); 690185643Sluigi break; 691185643Sluigi 692185643Sluigi case __IP: /* ip address */ 693185643Sluigi for (; size > 0 && vp < endv; size -= 4, cp += 4) { 694185643Sluigi struct in_addr in_ip; /* ip addresses */ 695185643Sluigi if (vp != buf) 696185643Sluigi *vp++ = FLD_SEP; 697185643Sluigi bcopy(cp, &in_ip.s_addr, sizeof(in_ip.s_addr)); 698185643Sluigi sprintf(vp, "%s", inet_ntoa(in_ip)); 699185643Sluigi vp += strlen(vp); 700185643Sluigi } 701185643Sluigi break; 702185643Sluigi 703185643Sluigi case __BYTES: /* opaque byte string */ 704185643Sluigi for (; size > 0 && vp < endv; size -= 1, cp += 1) { 705185643Sluigi sprintf(vp, "%02x", *cp); 706185643Sluigi vp += strlen(vp); 707185643Sluigi } 708185643Sluigi break; 709185643Sluigi 710185643Sluigi case __TXT: 711185643Sluigi bcopy(cp, buf, size); /* cannot overflow */ 712185643Sluigi buf[size] = 0; 713185643Sluigi break; 714185643Sluigi 715185643Sluigi case __32: 716185643Sluigi case __16: 717185643Sluigi case __8: /* op->fmt is also the length of each field */ 718185643Sluigi for (; size > 0 && vp < endv; size -= op->fmt, cp += op->fmt) { 719185643Sluigi uint32_t v; 720185643Sluigi if (op->fmt == __32) 721185643Sluigi v = (cp[0]<<24) + (cp[1]<<16) + (cp[2]<<8) + cp[3]; 722185643Sluigi else if (op->fmt == __16) 723185643Sluigi v = (cp[0]<<8) + cp[1]; 724185643Sluigi else 725185643Sluigi v = cp[0]; 726185643Sluigi if (vp != buf) 727185643Sluigi *vp++ = FLD_SEP; 728185643Sluigi sprintf(vp, "%u", v); 729185643Sluigi vp += strlen(vp); 730185643Sluigi } 731185643Sluigi break; 732185643Sluigi 733185643Sluigi case __INDIR: /* name=value */ 734185643Sluigi case __ILIST: /* name=value;name=value... */ 735185643Sluigi bcopy(cp, buf, size); /* cannot overflow */ 736185643Sluigi buf[size] = '\0'; 737185643Sluigi for (endv = buf; endv; endv = vp) { 738344408Skevans char *s = NULL; /* semicolon ? */ 739185643Sluigi 740185643Sluigi /* skip leading whitespace */ 741229403Sed while (*endv && strchr(" \t\n\r", *endv)) 742185643Sluigi endv++; 743229403Sed vp = strchr(endv, '='); /* find name=value separator */ 744185643Sluigi if (!vp) 745185643Sluigi break; 746185643Sluigi *vp++ = 0; 747229403Sed if (op->fmt == __ILIST && (s = strchr(vp, ';'))) 748185643Sluigi *s++ = '\0'; 749185643Sluigi setenv(endv, vp, 1); 750185643Sluigi vp = s; /* prepare for next round */ 751185643Sluigi } 752185643Sluigi buf[0] = '\0'; /* option already done */ 753185643Sluigi } 754185643Sluigi 755185643Sluigi if (tp - tags < sizeof(tags) - 5) { /* add tag to the list */ 756185643Sluigi if (tp != tags) 757185643Sluigi *tp++ = FLD_SEP; 758185643Sluigi sprintf(tp, "%d", tag); 759185643Sluigi tp += strlen(tp); 760185643Sluigi } 761185643Sluigi if (buf[0]) { 762185643Sluigi char env[128]; /* the string name */ 763185643Sluigi 764185643Sluigi if (op->tag == 0) 765185643Sluigi sprintf(env, op->desc, opts[0].desc, tag); 766185643Sluigi else 767185643Sluigi sprintf(env, "%s%s", opts[0].desc, op->desc); 768292583Sian /* 769292583Sian * Do not replace existing values in the environment, so that 770292583Sian * locally-obtained values can override server-provided values. 771292583Sian */ 772292583Sian setenv(env, buf, 0); 773185643Sluigi } 774185643Sluigi } 775185643Sluigi if (tp != tags) { 776185643Sluigi char env[128]; /* the string name */ 777185643Sluigi sprintf(env, "%stags", opts[0].desc); 778185643Sluigi setenv(env, tags, 1); 779185643Sluigi } 780185643Sluigi} 781185643Sluigi#endif /* additional dhcp */ 782