8bcbd2f7a1
variables in loader(8) and other libstand applications. Sometimes a dhcp server provides incorrect information along with the IP address. It would be useful to have a way to override this with locally-supplied information, such as command line parameters passed from a prior-stage bootloader. This change allows pre-existing env vars to take precedence over values delivered by the dhcp or bootp server. The bootp/dhcp code in libstand automatically creates environment variables from the data provided by the server (dhcp.root-path, dhcp.domain-name, etc). It also transcribes the values to some global variables such as 'rootpath' and 'hostname'. This change does two things: When adding dhcp.* vars to the environment, don't replace existing vars/values. When setting the global vars rootpath and hostname, use the dhcp.root-path and dhcp.host-name env var values if they exist. This allows the platform-specific part of loader(8) to obtain override values in some platform-specific way and store them in the environment before opening the network device. The set of values that can be overriden is currently limited to just string options. The values that are delivered as binary data are things that probably shouldn't be overridden (IP, netmask, gateway, etc). The original patch this evolved from was submitted by martymac@ PR: 202098 Differential Revision: https://reviews.freebsd.org/D4559
750 lines
19 KiB
C
750 lines
19 KiB
C
/* $NetBSD: bootp.c,v 1.14 1998/02/16 11:10:54 drochner Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1992 Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* This software was developed by the Computer Systems Engineering group
|
|
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
|
|
* contributed to Berkeley.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* @(#) Header: bootp.c,v 1.4 93/09/11 03:13:51 leres Exp (LBL)
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/types.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <string.h>
|
|
|
|
#define BOOTP_DEBUGxx
|
|
#define SUPPORT_DHCP
|
|
|
|
#define DHCP_ENV_NOVENDOR 1 /* do not parse vendor options */
|
|
#define DHCP_ENV_PXE 10 /* assume pxe vendor options */
|
|
#define DHCP_ENV_FREEBSD 11 /* assume freebsd vendor options */
|
|
/* set DHCP_ENV to one of the values above to export dhcp options to kenv */
|
|
#define DHCP_ENV DHCP_ENV_NO_VENDOR
|
|
|
|
#include "stand.h"
|
|
#include "net.h"
|
|
#include "netif.h"
|
|
#include "bootp.h"
|
|
|
|
|
|
struct in_addr servip;
|
|
|
|
static n_long nmask, smask;
|
|
|
|
static time_t bot;
|
|
|
|
static char vm_rfc1048[4] = VM_RFC1048;
|
|
#ifdef BOOTP_VEND_CMU
|
|
static char vm_cmu[4] = VM_CMU;
|
|
#endif
|
|
|
|
/* Local forwards */
|
|
static ssize_t bootpsend(struct iodesc *, void *, size_t);
|
|
static ssize_t bootprecv(struct iodesc *, void *, size_t, time_t);
|
|
static int vend_rfc1048(u_char *, u_int);
|
|
#ifdef BOOTP_VEND_CMU
|
|
static void vend_cmu(u_char *);
|
|
#endif
|
|
|
|
#ifdef DHCP_ENV /* export the dhcp response to kenv */
|
|
struct dhcp_opt;
|
|
static void setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts);
|
|
#else
|
|
#define setenv_(a, b, c)
|
|
#endif
|
|
|
|
#ifdef SUPPORT_DHCP
|
|
static char expected_dhcpmsgtype = -1, dhcp_ok;
|
|
struct in_addr dhcp_serverip;
|
|
#endif
|
|
|
|
/* Fetch required bootp infomation */
|
|
void
|
|
bootp(sock, flag)
|
|
int sock;
|
|
int flag;
|
|
{
|
|
struct iodesc *d;
|
|
struct bootp *bp;
|
|
struct {
|
|
u_char header[HEADER_SIZE];
|
|
struct bootp wbootp;
|
|
} wbuf;
|
|
struct {
|
|
u_char header[HEADER_SIZE];
|
|
struct bootp rbootp;
|
|
} rbuf;
|
|
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("bootp: socket=%d\n", sock);
|
|
#endif
|
|
if (!bot)
|
|
bot = getsecs();
|
|
|
|
if (!(d = socktodesc(sock))) {
|
|
printf("bootp: bad socket. %d\n", sock);
|
|
return;
|
|
}
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("bootp: d=%lx\n", (long)d);
|
|
#endif
|
|
|
|
bp = &wbuf.wbootp;
|
|
bzero(bp, sizeof(*bp));
|
|
|
|
bp->bp_op = BOOTREQUEST;
|
|
bp->bp_htype = 1; /* 10Mb Ethernet (48 bits) */
|
|
bp->bp_hlen = 6;
|
|
bp->bp_xid = htonl(d->xid);
|
|
MACPY(d->myea, bp->bp_chaddr);
|
|
strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file));
|
|
bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048));
|
|
#ifdef SUPPORT_DHCP
|
|
bp->bp_vend[4] = TAG_DHCP_MSGTYPE;
|
|
bp->bp_vend[5] = 1;
|
|
bp->bp_vend[6] = DHCPDISCOVER;
|
|
|
|
/*
|
|
* If we are booting from PXE, we want to send the string
|
|
* 'PXEClient' to the DHCP server so you have the option of
|
|
* only responding to PXE aware dhcp requests.
|
|
*/
|
|
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;
|
|
#endif
|
|
|
|
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))
|
|
== -1) {
|
|
printf("bootp: no reply\n");
|
|
return;
|
|
}
|
|
|
|
#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;
|
|
bcopy(&rbuf.rbootp.bp_yiaddr, &bp->bp_vend[9], 4);
|
|
bp->bp_vend[13] = TAG_SERVERID;
|
|
bp->bp_vend[14] = 4;
|
|
bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4);
|
|
bp->bp_vend[19] = TAG_LEASETIME;
|
|
bp->bp_vend[20] = 4;
|
|
leasetime = htonl(300);
|
|
bcopy(&leasetime, &bp->bp_vend[21], 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;
|
|
|
|
expected_dhcpmsgtype = DHCPACK;
|
|
|
|
if(sendrecv(d,
|
|
bootpsend, bp, sizeof(*bp),
|
|
bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp))
|
|
== -1) {
|
|
printf("DHCPREQUEST failed\n");
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
myip = d->myip = rbuf.rbootp.bp_yiaddr;
|
|
servip = rbuf.rbootp.bp_siaddr;
|
|
if(rootip.s_addr == INADDR_ANY) rootip = servip;
|
|
bcopy(rbuf.rbootp.bp_file, bootfile, sizeof(bootfile));
|
|
bootfile[sizeof(bootfile) - 1] = '\0';
|
|
|
|
if (IN_CLASSA(ntohl(myip.s_addr)))
|
|
nmask = htonl(IN_CLASSA_NET);
|
|
else if (IN_CLASSB(ntohl(myip.s_addr)))
|
|
nmask = htonl(IN_CLASSB_NET);
|
|
else
|
|
nmask = htonl(IN_CLASSC_NET);
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("'native netmask' is %s\n", intoa(nmask));
|
|
#endif
|
|
|
|
/* Check subnet mask against net mask; toss if bogus */
|
|
if ((nmask & smask) != nmask) {
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("subnet mask (%s) bad\n", intoa(smask));
|
|
#endif
|
|
smask = 0;
|
|
}
|
|
|
|
/* Get subnet (or natural net) mask */
|
|
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
|
|
}
|
|
|
|
/* Toss gateway if on a different net */
|
|
if (!SAMENET(myip, gateip, netmask)) {
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("gateway ip (%s) bad\n", inet_ntoa(gateip));
|
|
#endif
|
|
gateip.s_addr = 0;
|
|
}
|
|
|
|
/* Bump xid so next request will be unique. */
|
|
++d->xid;
|
|
}
|
|
|
|
/* Transmit a bootp request */
|
|
static ssize_t
|
|
bootpsend(d, pkt, len)
|
|
struct iodesc *d;
|
|
void *pkt;
|
|
size_t len;
|
|
{
|
|
struct bootp *bp;
|
|
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("bootpsend: d=%lx called.\n", (long)d);
|
|
#endif
|
|
|
|
bp = pkt;
|
|
bp->bp_secs = htons((u_short)(getsecs() - bot));
|
|
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("bootpsend: calling sendudp\n");
|
|
#endif
|
|
|
|
return (sendudp(d, pkt, len));
|
|
}
|
|
|
|
static ssize_t
|
|
bootprecv(d, pkt, len, tleft)
|
|
struct iodesc *d;
|
|
void *pkt;
|
|
size_t len;
|
|
time_t tleft;
|
|
{
|
|
ssize_t n;
|
|
struct bootp *bp;
|
|
|
|
#ifdef BOOTP_DEBUGx
|
|
if (debug)
|
|
printf("bootp_recvoffer: called\n");
|
|
#endif
|
|
|
|
n = readudp(d, pkt, len, tleft);
|
|
if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE)
|
|
goto bad;
|
|
|
|
bp = (struct bootp *)pkt;
|
|
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("bootprecv: checked. bp = 0x%lx, n = %d\n",
|
|
(long)bp, (int)n);
|
|
#endif
|
|
if (bp->bp_xid != htonl(d->xid)) {
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug) {
|
|
printf("bootprecv: expected xid 0x%lx, got 0x%x\n",
|
|
d->xid, ntohl(bp->bp_xid));
|
|
}
|
|
#endif
|
|
goto bad;
|
|
}
|
|
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("bootprecv: got one!\n");
|
|
#endif
|
|
|
|
/* Suck out vendor info */
|
|
if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) {
|
|
if(vend_rfc1048(bp->bp_vend, sizeof(bp->bp_vend)) != 0)
|
|
goto bad;
|
|
}
|
|
#ifdef BOOTP_VEND_CMU
|
|
else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0)
|
|
vend_cmu(bp->bp_vend);
|
|
#endif
|
|
else
|
|
printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend);
|
|
|
|
return(n);
|
|
bad:
|
|
errno = 0;
|
|
return (-1);
|
|
}
|
|
|
|
static int
|
|
vend_rfc1048(cp, len)
|
|
u_char *cp;
|
|
u_int len;
|
|
{
|
|
u_char *ep;
|
|
int size;
|
|
u_char tag;
|
|
const char *val;
|
|
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("vend_rfc1048 bootp info. len=%d\n", len);
|
|
#endif
|
|
ep = cp + len;
|
|
|
|
/* Step over magic cookie */
|
|
cp += sizeof(int);
|
|
|
|
setenv_(cp, ep, NULL);
|
|
|
|
while (cp < ep) {
|
|
tag = *cp++;
|
|
size = *cp++;
|
|
if (tag == TAG_END)
|
|
break;
|
|
|
|
if (tag == TAG_SUBNET_MASK) {
|
|
bcopy(cp, &smask, sizeof(smask));
|
|
}
|
|
if (tag == TAG_GATEWAY) {
|
|
bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr));
|
|
}
|
|
if (tag == TAG_SWAPSERVER) {
|
|
/* let it override bp_siaddr */
|
|
bcopy(cp, &rootip.s_addr, sizeof(rootip.s_addr));
|
|
}
|
|
if (tag == TAG_ROOTPATH) {
|
|
if ((val = getenv("dhcp.root-path")) == NULL)
|
|
val = (const char *)cp;
|
|
strlcpy(rootpath, val, sizeof(rootpath));
|
|
}
|
|
if (tag == TAG_HOSTNAME) {
|
|
if ((val = getenv("dhcp.host-name")) == NULL)
|
|
val = (const char *)cp;
|
|
strlcpy(hostname, val, sizeof(hostname));
|
|
}
|
|
#ifdef SUPPORT_DHCP
|
|
if (tag == 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));
|
|
}
|
|
#endif
|
|
cp += size;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
#ifdef BOOTP_VEND_CMU
|
|
static void
|
|
vend_cmu(cp)
|
|
u_char *cp;
|
|
{
|
|
struct cmu_vend *vp;
|
|
|
|
#ifdef BOOTP_DEBUG
|
|
if (debug)
|
|
printf("vend_cmu bootp info.\n");
|
|
#endif
|
|
vp = (struct cmu_vend *)cp;
|
|
|
|
if (vp->v_smask.s_addr != 0) {
|
|
smask = vp->v_smask.s_addr;
|
|
}
|
|
if (vp->v_dgate.s_addr != 0) {
|
|
gateip = vp->v_dgate;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef DHCP_ENV
|
|
/*
|
|
* Parse DHCP options and store them into kenv variables.
|
|
* Original code from Danny Braniss, modifications by Luigi Rizzo.
|
|
*
|
|
* The parser is driven by tables which specify the type and name of
|
|
* each dhcp option and how it appears in kenv.
|
|
* The first entry in the list contains the prefix used to set the kenv
|
|
* name (including the . if needed), the last entry must have a 0 tag.
|
|
* Entries do not need to be sorted though it helps for readability.
|
|
*
|
|
* Certain vendor-specific tables can be enabled according to DHCP_ENV.
|
|
* Set it to 0 if you don't want any.
|
|
*/
|
|
enum opt_fmt { __NONE = 0,
|
|
__8 = 1, __16 = 2, __32 = 4, /* Unsigned fields, value=size */
|
|
__IP, /* IPv4 address */
|
|
__TXT, /* C string */
|
|
__BYTES, /* byte sequence, printed %02x */
|
|
__INDIR, /* name=value */
|
|
__ILIST, /* name=value;name=value ... */
|
|
__VE, /* vendor specific, recurse */
|
|
};
|
|
|
|
struct dhcp_opt {
|
|
uint8_t tag;
|
|
uint8_t fmt;
|
|
const char *desc;
|
|
};
|
|
|
|
static struct dhcp_opt vndr_opt[] = { /* Vendor Specific Options */
|
|
#if DHCP_ENV == DHCP_ENV_FREEBSD /* FreeBSD table in the original code */
|
|
{0, 0, "FreeBSD"}, /* prefix */
|
|
{1, __TXT, "kernel"},
|
|
{2, __TXT, "kernelname"},
|
|
{3, __TXT, "kernel_options"},
|
|
{4, __IP, "usr-ip"},
|
|
{5, __TXT, "conf-path"},
|
|
{6, __TXT, "rc.conf0"},
|
|
{7, __TXT, "rc.conf1"},
|
|
{8, __TXT, "rc.conf2"},
|
|
{9, __TXT, "rc.conf3"},
|
|
{10, __TXT, "rc.conf4"},
|
|
{11, __TXT, "rc.conf5"},
|
|
{12, __TXT, "rc.conf6"},
|
|
{13, __TXT, "rc.conf7"},
|
|
{14, __TXT, "rc.conf8"},
|
|
{15, __TXT, "rc.conf9"},
|
|
|
|
{20, __TXT, "boot.nfsroot.options"},
|
|
|
|
{245, __INDIR, ""},
|
|
{246, __INDIR, ""},
|
|
{247, __INDIR, ""},
|
|
{248, __INDIR, ""},
|
|
{249, __INDIR, ""},
|
|
{250, __INDIR, ""},
|
|
{251, __INDIR, ""},
|
|
{252, __INDIR, ""},
|
|
{253, __INDIR, ""},
|
|
{254, __INDIR, ""},
|
|
|
|
#elif DHCP_ENV == DHCP_ENV_PXE /* some pxe options, RFC4578 */
|
|
{0, 0, "pxe"}, /* prefix */
|
|
{93, __16, "system-architecture"},
|
|
{94, __BYTES, "network-interface"},
|
|
{97, __BYTES, "machine-identifier"},
|
|
#else /* default (empty) table */
|
|
{0, 0, "dhcp.vendor."}, /* prefix */
|
|
#endif
|
|
{0, __TXT, "%soption-%d"}
|
|
};
|
|
|
|
static struct dhcp_opt dhcp_opt[] = {
|
|
/* DHCP Option names, formats and codes, from RFC2132. */
|
|
{0, 0, "dhcp."}, // prefix
|
|
{1, __IP, "subnet-mask"},
|
|
{2, __32, "time-offset"}, /* this is signed */
|
|
{3, __IP, "routers"},
|
|
{4, __IP, "time-servers"},
|
|
{5, __IP, "ien116-name-servers"},
|
|
{6, __IP, "domain-name-servers"},
|
|
{7, __IP, "log-servers"},
|
|
{8, __IP, "cookie-servers"},
|
|
{9, __IP, "lpr-servers"},
|
|
{10, __IP, "impress-servers"},
|
|
{11, __IP, "resource-location-servers"},
|
|
{12, __TXT, "host-name"},
|
|
{13, __16, "boot-size"},
|
|
{14, __TXT, "merit-dump"},
|
|
{15, __TXT, "domain-name"},
|
|
{16, __IP, "swap-server"},
|
|
{17, __TXT, "root-path"},
|
|
{18, __TXT, "extensions-path"},
|
|
{19, __8, "ip-forwarding"},
|
|
{20, __8, "non-local-source-routing"},
|
|
{21, __IP, "policy-filter"},
|
|
{22, __16, "max-dgram-reassembly"},
|
|
{23, __8, "default-ip-ttl"},
|
|
{24, __32, "path-mtu-aging-timeout"},
|
|
{25, __16, "path-mtu-plateau-table"},
|
|
{26, __16, "interface-mtu"},
|
|
{27, __8, "all-subnets-local"},
|
|
{28, __IP, "broadcast-address"},
|
|
{29, __8, "perform-mask-discovery"},
|
|
{30, __8, "mask-supplier"},
|
|
{31, __8, "perform-router-discovery"},
|
|
{32, __IP, "router-solicitation-address"},
|
|
{33, __IP, "static-routes"},
|
|
{34, __8, "trailer-encapsulation"},
|
|
{35, __32, "arp-cache-timeout"},
|
|
{36, __8, "ieee802-3-encapsulation"},
|
|
{37, __8, "default-tcp-ttl"},
|
|
{38, __32, "tcp-keepalive-interval"},
|
|
{39, __8, "tcp-keepalive-garbage"},
|
|
{40, __TXT, "nis-domain"},
|
|
{41, __IP, "nis-servers"},
|
|
{42, __IP, "ntp-servers"},
|
|
{43, __VE, "vendor-encapsulated-options"},
|
|
{44, __IP, "netbios-name-servers"},
|
|
{45, __IP, "netbios-dd-server"},
|
|
{46, __8, "netbios-node-type"},
|
|
{47, __TXT, "netbios-scope"},
|
|
{48, __IP, "x-font-servers"},
|
|
{49, __IP, "x-display-managers"},
|
|
{50, __IP, "dhcp-requested-address"},
|
|
{51, __32, "dhcp-lease-time"},
|
|
{52, __8, "dhcp-option-overload"},
|
|
{53, __8, "dhcp-message-type"},
|
|
{54, __IP, "dhcp-server-identifier"},
|
|
{55, __8, "dhcp-parameter-request-list"},
|
|
{56, __TXT, "dhcp-message"},
|
|
{57, __16, "dhcp-max-message-size"},
|
|
{58, __32, "dhcp-renewal-time"},
|
|
{59, __32, "dhcp-rebinding-time"},
|
|
{60, __TXT, "vendor-class-identifier"},
|
|
{61, __TXT, "dhcp-client-identifier"},
|
|
{64, __TXT, "nisplus-domain"},
|
|
{65, __IP, "nisplus-servers"},
|
|
{66, __TXT, "tftp-server-name"},
|
|
{67, __TXT, "bootfile-name"},
|
|
{68, __IP, "mobile-ip-home-agent"},
|
|
{69, __IP, "smtp-server"},
|
|
{70, __IP, "pop-server"},
|
|
{71, __IP, "nntp-server"},
|
|
{72, __IP, "www-server"},
|
|
{73, __IP, "finger-server"},
|
|
{74, __IP, "irc-server"},
|
|
{75, __IP, "streettalk-server"},
|
|
{76, __IP, "streettalk-directory-assistance-server"},
|
|
{77, __TXT, "user-class"},
|
|
{85, __IP, "nds-servers"},
|
|
{86, __TXT, "nds-tree-name"},
|
|
{87, __TXT, "nds-context"},
|
|
{210, __TXT, "authenticate"},
|
|
|
|
/* use the following entries for arbitrary variables */
|
|
{246, __ILIST, ""},
|
|
{247, __ILIST, ""},
|
|
{248, __ILIST, ""},
|
|
{249, __ILIST, ""},
|
|
{250, __INDIR, ""},
|
|
{251, __INDIR, ""},
|
|
{252, __INDIR, ""},
|
|
{253, __INDIR, ""},
|
|
{254, __INDIR, ""},
|
|
{0, __TXT, "%soption-%d"}
|
|
};
|
|
|
|
/*
|
|
* parse a dhcp response, set environment variables translating options
|
|
* names and values according to the tables above. Also set dhcp.tags
|
|
* to the list of selected tags.
|
|
*/
|
|
static void
|
|
setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts)
|
|
{
|
|
u_char *ncp;
|
|
u_char tag;
|
|
char tags[512], *tp; /* the list of tags */
|
|
|
|
#define FLD_SEP ',' /* separator in list of elements */
|
|
ncp = cp;
|
|
tp = tags;
|
|
if (opts == NULL)
|
|
opts = dhcp_opt;
|
|
|
|
while (ncp < ep) {
|
|
unsigned int size; /* option size */
|
|
char *vp, *endv, buf[256]; /* the value buffer */
|
|
struct dhcp_opt *op;
|
|
|
|
tag = *ncp++; /* extract tag and size */
|
|
size = *ncp++;
|
|
cp = ncp; /* current payload */
|
|
ncp += size; /* point to the next option */
|
|
|
|
if (tag == TAG_END)
|
|
break;
|
|
if (tag == 0)
|
|
continue;
|
|
|
|
for (op = opts+1; op->tag && op->tag != tag; op++)
|
|
;
|
|
/* if not found we end up on the default entry */
|
|
|
|
/*
|
|
* Copy data into the buffer. libstand does not have snprintf so we
|
|
* need to be careful with sprintf(). With strings, the source is
|
|
* always <256 char so shorter than the buffer so we are safe; with
|
|
* other arguments, the longest string is inet_ntoa which is 16 bytes
|
|
* so we make sure to have always enough room in the string before
|
|
* trying an sprint.
|
|
*/
|
|
vp = buf;
|
|
*vp = '\0';
|
|
endv = buf + sizeof(buf) - 1 - 16; /* last valid write position */
|
|
|
|
switch(op->fmt) {
|
|
case __NONE:
|
|
break; /* should not happen */
|
|
|
|
case __VE: /* recurse, vendor specific */
|
|
setenv_(cp, cp+size, vndr_opt);
|
|
break;
|
|
|
|
case __IP: /* ip address */
|
|
for (; size > 0 && vp < endv; size -= 4, cp += 4) {
|
|
struct in_addr in_ip; /* ip addresses */
|
|
if (vp != buf)
|
|
*vp++ = FLD_SEP;
|
|
bcopy(cp, &in_ip.s_addr, sizeof(in_ip.s_addr));
|
|
sprintf(vp, "%s", inet_ntoa(in_ip));
|
|
vp += strlen(vp);
|
|
}
|
|
break;
|
|
|
|
case __BYTES: /* opaque byte string */
|
|
for (; size > 0 && vp < endv; size -= 1, cp += 1) {
|
|
sprintf(vp, "%02x", *cp);
|
|
vp += strlen(vp);
|
|
}
|
|
break;
|
|
|
|
case __TXT:
|
|
bcopy(cp, buf, size); /* cannot overflow */
|
|
buf[size] = 0;
|
|
break;
|
|
|
|
case __32:
|
|
case __16:
|
|
case __8: /* op->fmt is also the length of each field */
|
|
for (; size > 0 && vp < endv; size -= op->fmt, cp += op->fmt) {
|
|
uint32_t v;
|
|
if (op->fmt == __32)
|
|
v = (cp[0]<<24) + (cp[1]<<16) + (cp[2]<<8) + cp[3];
|
|
else if (op->fmt == __16)
|
|
v = (cp[0]<<8) + cp[1];
|
|
else
|
|
v = cp[0];
|
|
if (vp != buf)
|
|
*vp++ = FLD_SEP;
|
|
sprintf(vp, "%u", v);
|
|
vp += strlen(vp);
|
|
}
|
|
break;
|
|
|
|
case __INDIR: /* name=value */
|
|
case __ILIST: /* name=value;name=value... */
|
|
bcopy(cp, buf, size); /* cannot overflow */
|
|
buf[size] = '\0';
|
|
for (endv = buf; endv; endv = vp) {
|
|
u_char *s = NULL; /* semicolon ? */
|
|
|
|
/* skip leading whitespace */
|
|
while (*endv && strchr(" \t\n\r", *endv))
|
|
endv++;
|
|
vp = strchr(endv, '='); /* find name=value separator */
|
|
if (!vp)
|
|
break;
|
|
*vp++ = 0;
|
|
if (op->fmt == __ILIST && (s = strchr(vp, ';')))
|
|
*s++ = '\0';
|
|
setenv(endv, vp, 1);
|
|
vp = s; /* prepare for next round */
|
|
}
|
|
buf[0] = '\0'; /* option already done */
|
|
}
|
|
|
|
if (tp - tags < sizeof(tags) - 5) { /* add tag to the list */
|
|
if (tp != tags)
|
|
*tp++ = FLD_SEP;
|
|
sprintf(tp, "%d", tag);
|
|
tp += strlen(tp);
|
|
}
|
|
if (buf[0]) {
|
|
char env[128]; /* the string name */
|
|
|
|
if (op->tag == 0)
|
|
sprintf(env, op->desc, opts[0].desc, tag);
|
|
else
|
|
sprintf(env, "%s%s", opts[0].desc, op->desc);
|
|
/*
|
|
* Do not replace existing values in the environment, so that
|
|
* locally-obtained values can override server-provided values.
|
|
*/
|
|
setenv(env, buf, 0);
|
|
}
|
|
}
|
|
if (tp != tags) {
|
|
char env[128]; /* the string name */
|
|
sprintf(env, "%stags", opts[0].desc);
|
|
setenv(env, tags, 1);
|
|
}
|
|
}
|
|
#endif /* additional dhcp */
|