0f62c4d065
requestip, vendorclass, clientid are client-side variables, they do not make sense as udhcpd opts dhcptype is the packet type (not interesting, it's always 5) userclass needs parser support in order to work function old new delta dhcp_options 68 66 -2 read_opt 865 859 -6 dhcp_option_strings 253 203 -50 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/3 up/down: 0/-58) Total: -58 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
286 lines
7.0 KiB
C
286 lines
7.0 KiB
C
/* vi: set sw=4 ts=4: */
|
|
/* script.c
|
|
*
|
|
* Functions to call the DHCP client notification scripts
|
|
*
|
|
* Russ Dill <Russ.Dill@asu.edu> July 2001
|
|
*
|
|
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "dhcpc.h"
|
|
#include "options.h"
|
|
|
|
|
|
/* get a rough idea of how long an option will be (rounding up...) */
|
|
static const uint8_t len_of_option_as_string[] = {
|
|
[OPTION_IP] = sizeof("255.255.255.255 "),
|
|
[OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2,
|
|
[OPTION_STATIC_ROUTES]= sizeof("255.255.255.255/32 255.255.255.255 "),
|
|
[OPTION_STRING] = 1,
|
|
#if ENABLE_FEATURE_UDHCP_RFC3397
|
|
[OPTION_STR1035] = 1,
|
|
#endif
|
|
[OPTION_BOOLEAN] = sizeof("yes "),
|
|
[OPTION_U8] = sizeof("255 "),
|
|
[OPTION_U16] = sizeof("65535 "),
|
|
[OPTION_S16] = sizeof("-32768 "),
|
|
[OPTION_U32] = sizeof("4294967295 "),
|
|
[OPTION_S32] = sizeof("-2147483684 "),
|
|
};
|
|
|
|
|
|
/* note: ip is a pointer to an IP in network order, possibly misaliged */
|
|
static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
|
|
{
|
|
return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
|
|
}
|
|
|
|
|
|
/* really simple implementation, just count the bits */
|
|
static int mton(uint32_t mask)
|
|
{
|
|
int i = 0;
|
|
mask = ntohl(mask); /* 111110000-like bit pattern */
|
|
while (mask) {
|
|
i++;
|
|
mask <<= 1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
/* Create "opt_name=opt_value" string */
|
|
static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name)
|
|
{
|
|
unsigned upper_length;
|
|
int len, type, optlen;
|
|
uint16_t val_u16;
|
|
int16_t val_s16;
|
|
uint32_t val_u32;
|
|
int32_t val_s32;
|
|
char *dest, *ret;
|
|
|
|
/* option points to OPT_DATA, need to go back and get OPT_LEN */
|
|
len = option[OPT_LEN - OPT_DATA];
|
|
type = type_p->flags & TYPE_MASK;
|
|
optlen = dhcp_option_lengths[type];
|
|
upper_length = len_of_option_as_string[type] * (len / optlen);
|
|
|
|
dest = ret = xmalloc(upper_length + strlen(opt_name) + 2);
|
|
dest += sprintf(ret, "%s=", opt_name);
|
|
|
|
while (len >= optlen) {
|
|
switch (type) {
|
|
case OPTION_IP_PAIR:
|
|
dest += sprint_nip(dest, "", option);
|
|
*dest++ = '/';
|
|
option += 4;
|
|
optlen = 4;
|
|
case OPTION_IP:
|
|
dest += sprint_nip(dest, "", option);
|
|
// TODO: it can be a list only if (type_p->flags & OPTION_LIST).
|
|
// Should we bail out/warn if we see multi-ip option which is
|
|
// not allowed to be such? For example, DHCP_BROADCAST...
|
|
break;
|
|
case OPTION_BOOLEAN:
|
|
dest += sprintf(dest, *option ? "yes" : "no");
|
|
break;
|
|
case OPTION_U8:
|
|
dest += sprintf(dest, "%u", *option);
|
|
break;
|
|
case OPTION_U16:
|
|
move_from_unaligned16(val_u16, option);
|
|
dest += sprintf(dest, "%u", ntohs(val_u16));
|
|
break;
|
|
case OPTION_S16:
|
|
move_from_unaligned16(val_s16, option);
|
|
dest += sprintf(dest, "%d", ntohs(val_s16));
|
|
break;
|
|
case OPTION_U32:
|
|
move_from_unaligned32(val_u32, option);
|
|
dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
|
|
break;
|
|
case OPTION_S32:
|
|
move_from_unaligned32(val_s32, option);
|
|
dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
|
|
break;
|
|
case OPTION_STRING:
|
|
memcpy(dest, option, len);
|
|
dest[len] = '\0';
|
|
return ret; /* Short circuit this case */
|
|
case OPTION_STATIC_ROUTES: {
|
|
/* Option binary format:
|
|
* mask [one byte, 0..32]
|
|
* ip [big endian, 0..4 bytes depending on mask]
|
|
* router [big endian, 4 bytes]
|
|
* may be repeated
|
|
*
|
|
* We convert it to a string "IP/MASK ROUTER IP2/MASK2 ROUTER2"
|
|
*/
|
|
const char *pfx = "";
|
|
|
|
while (len >= 1 + 4) { /* mask + 0-byte ip + router */
|
|
uint32_t nip;
|
|
uint8_t *p;
|
|
unsigned mask;
|
|
int bytes;
|
|
|
|
mask = *option++;
|
|
if (mask > 32)
|
|
break;
|
|
len--;
|
|
|
|
nip = 0;
|
|
p = (void*) &nip;
|
|
bytes = (mask + 7) / 8; /* 0 -> 0, 1..8 -> 1, 9..16 -> 2 etc */
|
|
while (--bytes >= 0) {
|
|
*p++ = *option++;
|
|
len--;
|
|
}
|
|
if (len < 4)
|
|
break;
|
|
|
|
/* print ip/mask */
|
|
dest += sprint_nip(dest, pfx, (void*) &nip);
|
|
pfx = " ";
|
|
dest += sprintf(dest, "/%u ", mask);
|
|
/* print router */
|
|
dest += sprint_nip(dest, "", option);
|
|
option += 4;
|
|
len -= 4;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#if ENABLE_FEATURE_UDHCP_RFC3397
|
|
case OPTION_STR1035:
|
|
/* unpack option into dest; use ret for prefix (i.e., "optname=") */
|
|
dest = dname_dec(option, len, ret);
|
|
if (dest) {
|
|
free(ret);
|
|
return dest;
|
|
}
|
|
/* error. return "optname=" string */
|
|
return ret;
|
|
#endif
|
|
}
|
|
option += optlen;
|
|
len -= optlen;
|
|
if (len <= 0)
|
|
break;
|
|
*dest++ = ' ';
|
|
*dest = '\0';
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* put all the parameters into the environment */
|
|
static char **fill_envp(struct dhcp_packet *packet)
|
|
{
|
|
int num_options = 0;
|
|
int i;
|
|
char **envp, **curr;
|
|
const char *opt_name;
|
|
uint8_t *temp;
|
|
uint8_t over = 0;
|
|
|
|
if (packet) {
|
|
for (i = 0; dhcp_options[i].code; i++) {
|
|
if (get_option(packet, dhcp_options[i].code)) {
|
|
num_options++;
|
|
if (dhcp_options[i].code == DHCP_SUBNET)
|
|
num_options++; /* for mton */
|
|
}
|
|
}
|
|
if (packet->siaddr_nip)
|
|
num_options++;
|
|
temp = get_option(packet, DHCP_OPTION_OVERLOAD);
|
|
if (temp)
|
|
over = *temp;
|
|
if (!(over & FILE_FIELD) && packet->file[0])
|
|
num_options++;
|
|
if (!(over & SNAME_FIELD) && packet->sname[0])
|
|
num_options++;
|
|
}
|
|
|
|
curr = envp = xzalloc(sizeof(char *) * (num_options + 3));
|
|
*curr = xasprintf("interface=%s", client_config.interface);
|
|
putenv(*curr++);
|
|
|
|
if (packet == NULL)
|
|
return envp;
|
|
|
|
*curr = xmalloc(sizeof("ip=255.255.255.255"));
|
|
sprint_nip(*curr, "ip=", (uint8_t *) &packet->yiaddr);
|
|
putenv(*curr++);
|
|
|
|
opt_name = dhcp_option_strings;
|
|
i = 0;
|
|
while (*opt_name) {
|
|
temp = get_option(packet, dhcp_options[i].code);
|
|
if (!temp)
|
|
goto next;
|
|
*curr = xmalloc_optname_optval(temp, &dhcp_options[i], opt_name);
|
|
putenv(*curr++);
|
|
|
|
/* Fill in a subnet bits option for things like /24 */
|
|
if (dhcp_options[i].code == DHCP_SUBNET) {
|
|
uint32_t subnet;
|
|
move_from_unaligned32(subnet, temp);
|
|
*curr = xasprintf("mask=%d", mton(subnet));
|
|
putenv(*curr++);
|
|
}
|
|
next:
|
|
opt_name += strlen(opt_name) + 1;
|
|
i++;
|
|
}
|
|
if (packet->siaddr_nip) {
|
|
*curr = xmalloc(sizeof("siaddr=255.255.255.255"));
|
|
sprint_nip(*curr, "siaddr=", (uint8_t *) &packet->siaddr_nip);
|
|
putenv(*curr++);
|
|
}
|
|
if (!(over & FILE_FIELD) && packet->file[0]) {
|
|
/* watch out for invalid packets */
|
|
packet->file[sizeof(packet->file) - 1] = '\0';
|
|
*curr = xasprintf("boot_file=%s", packet->file);
|
|
putenv(*curr++);
|
|
}
|
|
if (!(over & SNAME_FIELD) && packet->sname[0]) {
|
|
/* watch out for invalid packets */
|
|
packet->sname[sizeof(packet->sname) - 1] = '\0';
|
|
*curr = xasprintf("sname=%s", packet->sname);
|
|
putenv(*curr++);
|
|
}
|
|
return envp;
|
|
}
|
|
|
|
|
|
/* Call a script with a par file and env vars */
|
|
void FAST_FUNC udhcp_run_script(struct dhcp_packet *packet, const char *name)
|
|
{
|
|
char **envp, **curr;
|
|
char *argv[3];
|
|
|
|
if (client_config.script == NULL)
|
|
return;
|
|
|
|
envp = fill_envp(packet);
|
|
|
|
/* call script */
|
|
log1("Executing %s %s", client_config.script, name);
|
|
argv[0] = (char*) client_config.script;
|
|
argv[1] = (char*) name;
|
|
argv[2] = NULL;
|
|
spawn_and_wait(argv);
|
|
|
|
for (curr = envp; *curr; curr++) {
|
|
log2(" %s", *curr);
|
|
bb_unsetenv(*curr);
|
|
free(*curr);
|
|
}
|
|
free(envp);
|
|
}
|