Some libstand/bootp.c extension (written by Danny Braniss, slightly
revised/modified by me) to store dhcp options into kenv variables, so the information is available to the boot loader and can be used to customize the boot process. The change is totally unintrusive, essentially made of a single function to be called while parsing a dhcp response, and a couple of tables to classify options. The values extracted from dhcp options are stored in the kenv environment in one of these forms: + options whose name and type is known are saved as dhcp.name = value (string, or number/ip addresses lists) + unknown options are assumed to be strings and saved as dhcp.option-NNN = "value" + options listed as '__INDIR' and sent on the wire as e.g. option unknown-252 "some.name=the actual value" are saved as some.name = "the actual value" + options listed as '__ILIST' and sent on the wire as e.g. option unknown-249 "a.b=foo bar; c.d= 123; e.f=done" are saved as multiple values a.b="foo bar" c.d="123" e.f="done" As you can see there is quite a bit of flexibility on what can be passed to the loader or the kernel. For the time being the vendor-specific table is mostly disabled, because there is no standard set of options for FreeBSD, and I don't know all the pxe-specific vendor options. Also, applications using libstand may live in memory-constrained environments, so it makes sense to keep these tables as small as possible, especially considering that one can generate arbitrary name=value pairs using site-specific options of type __INDIR or __ILIST (there are 4 __ILIST and 5 __INDIR in the table, numbered 246..249 and 250..254). Actually, considering that probably 75% of the standard dhcp options are totally useless, it might make sense to remove them as well. Submitted by: Danny Braniss MFC after: 4 weeks
This commit is contained in:
parent
75444a8590
commit
7e8cc2bd03
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=185643
@ -47,6 +47,12 @@ __FBSDID("$FreeBSD$");
|
||||
#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"
|
||||
@ -72,6 +78,13 @@ static int vend_rfc1048(u_char *, u_int);
|
||||
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;
|
||||
@ -351,6 +364,8 @@ vend_rfc1048(cp, len)
|
||||
/* Step over magic cookie */
|
||||
cp += sizeof(int);
|
||||
|
||||
setenv_(cp, ep, NULL);
|
||||
|
||||
while (cp < ep) {
|
||||
tag = *cp++;
|
||||
size = *cp++;
|
||||
@ -412,3 +427,316 @@ vend_cmu(cp)
|
||||
}
|
||||
}
|
||||
#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, ""}, /* 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 && index(" \t\n\r", *endv))
|
||||
endv++;
|
||||
vp = index(endv, '='); /* find name=value separator */
|
||||
if (!vp)
|
||||
break;
|
||||
*vp++ = 0;
|
||||
if (op->fmt == __ILIST && (s = index(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);
|
||||
setenv(env, buf, 1);
|
||||
}
|
||||
}
|
||||
if (tp != tags) {
|
||||
char env[128]; /* the string name */
|
||||
sprintf(env, "%stags", opts[0].desc);
|
||||
setenv(env, tags, 1);
|
||||
}
|
||||
}
|
||||
#endif /* additional dhcp */
|
||||
|
Loading…
Reference in New Issue
Block a user