e963960681
for talking to the server and for setting the interface address and route. However, otherwise it is quite different, since we need to be working under the assumptions that there is no stable storage on a rump instance, and that there are n networking stacks on a given host.
774 lines
19 KiB
C
774 lines
19 KiB
C
/*
|
|
* dhcpcd - DHCP client daemon
|
|
* Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
|
|
* All rights reserved
|
|
|
|
* 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "common.h"
|
|
#include "dhcp.h"
|
|
|
|
#define REQUEST (1 << 0)
|
|
#define UINT8 (1 << 1)
|
|
#define UINT16 (1 << 2)
|
|
#define SINT16 (1 << 3)
|
|
#define UINT32 (1 << 4)
|
|
#define SINT32 (1 << 5)
|
|
#define IPV4 (1 << 6)
|
|
#define STRING (1 << 7)
|
|
#define PAIR (1 << 8)
|
|
#define ARRAY (1 << 9)
|
|
#define RFC3361 (1 << 10)
|
|
#define RFC3397 (1 << 11)
|
|
#define RFC3442 (1 << 12)
|
|
|
|
#define IPV4R IPV4 | REQUEST
|
|
|
|
#define DAD "Duplicate address detected"
|
|
|
|
/* Our aggregate option buffer.
|
|
* We ONLY use this when options are split, which for most purposes is
|
|
* practically never. See RFC3396 for details. */
|
|
static uint8_t *opt_buffer;
|
|
|
|
struct dhcp_opt {
|
|
uint8_t option;
|
|
int type;
|
|
const char *var;
|
|
};
|
|
|
|
static const struct dhcp_opt const dhcp_opts[] = {
|
|
{ 1, IPV4 | REQUEST, "subnet_mask" },
|
|
/* RFC 3442 states that the CSR has to come before all other
|
|
* routes. For completeness, we also specify static routes,
|
|
* then routers. */
|
|
{ 121, RFC3442, "classless_static_routes" },
|
|
{ 249, RFC3442, "ms_classless_static_routes" },
|
|
{ 33, IPV4 | ARRAY | REQUEST, "static_routes" },
|
|
{ 3, IPV4 | ARRAY | REQUEST, "routers" },
|
|
{ 2, UINT32, "time_offset" },
|
|
{ 4, IPV4 | ARRAY, "time_servers" },
|
|
{ 5, IPV4 | ARRAY, "ien116_name_servers" },
|
|
{ 6, IPV4 | ARRAY, "domain_name_servers" },
|
|
{ 7, IPV4 | ARRAY, "log_servers" },
|
|
{ 8, IPV4 | ARRAY, "cookie_servers" },
|
|
{ 9, IPV4 | ARRAY, "lpr_servers" },
|
|
{ 10, IPV4 | ARRAY, "impress_servers" },
|
|
{ 11, IPV4 | ARRAY, "resource_location_servers" },
|
|
{ 12, STRING, "host_name" },
|
|
{ 13, UINT16, "boot_size" },
|
|
{ 14, STRING, "merit_dump" },
|
|
{ 15, STRING, "domain_name" },
|
|
{ 16, IPV4, "swap_server" },
|
|
{ 17, STRING, "root_path" },
|
|
{ 18, STRING, "extensions_path" },
|
|
{ 19, UINT8, "ip_forwarding" },
|
|
{ 20, UINT8, "non_local_source_routing" },
|
|
{ 21, IPV4 | ARRAY, "policy_filter" },
|
|
{ 22, SINT16, "max_dgram_reassembly" },
|
|
{ 23, UINT16, "default_ip_ttl" },
|
|
{ 24, UINT32, "path_mtu_aging_timeout" },
|
|
{ 25, UINT16 | ARRAY, "path_mtu_plateau_table" },
|
|
{ 26, UINT16, "interface_mtu" },
|
|
{ 27, UINT8, "all_subnets_local" },
|
|
{ 28, IPV4 | REQUEST, "broadcast_address" },
|
|
{ 29, UINT8, "perform_mask_discovery" },
|
|
{ 30, UINT8, "mask_supplier" },
|
|
{ 31, UINT8, "router_discovery" },
|
|
{ 32, IPV4, "router_solicitation_address" },
|
|
{ 34, UINT8, "trailer_encapsulation" },
|
|
{ 35, UINT32, "arp_cache_timeout" },
|
|
{ 36, UINT16, "ieee802_3_encapsulation" },
|
|
{ 37, UINT8, "default_tcp_ttl" },
|
|
{ 38, UINT32, "tcp_keepalive_interval" },
|
|
{ 39, UINT8, "tcp_keepalive_garbage" },
|
|
{ 40, STRING, "nis_domain" },
|
|
{ 41, IPV4 | ARRAY, "nis_servers" },
|
|
{ 42, IPV4 | ARRAY, "ntp_servers" },
|
|
{ 43, STRING, "vendor_encapsulated_options" },
|
|
{ 44, IPV4 | ARRAY, "netbios_name_servers" },
|
|
{ 45, IPV4, "netbios_dd_server" },
|
|
{ 46, UINT8, "netbios_node_type" },
|
|
{ 47, STRING, "netbios_scope" },
|
|
{ 48, IPV4 | ARRAY, "font_servers" },
|
|
{ 49, IPV4 | ARRAY, "x_display_manager" },
|
|
{ 50, IPV4, "dhcp_requested_address" },
|
|
{ 51, UINT32 | REQUEST, "dhcp_lease_time" },
|
|
{ 52, UINT8, "dhcp_option_overload" },
|
|
{ 53, UINT8, "dhcp_message_type" },
|
|
{ 54, IPV4, "dhcp_server_identifier" },
|
|
{ 55, UINT8 | ARRAY, "dhcp_parameter_request_list" },
|
|
{ 56, STRING, "dhcp_message" },
|
|
{ 57, UINT16, "dhcp_max_message_size" },
|
|
{ 58, UINT32 | REQUEST, "dhcp_renewal_time" },
|
|
{ 59, UINT32 | REQUEST, "dhcp_rebinding_time" },
|
|
{ 64, STRING, "nisplus_domain" },
|
|
{ 65, IPV4 | ARRAY, "nisplus_servers" },
|
|
{ 66, STRING, "tftp_server_name" },
|
|
{ 67, STRING, "bootfile_name" },
|
|
{ 68, IPV4 | ARRAY, "mobile_ip_home_agent" },
|
|
{ 69, IPV4 | ARRAY, "smtp_server" },
|
|
{ 70, IPV4 | ARRAY, "pop_server" },
|
|
{ 71, IPV4 | ARRAY, "nntp_server" },
|
|
{ 72, IPV4 | ARRAY, "www_server" },
|
|
{ 73, IPV4 | ARRAY, "finger_server" },
|
|
{ 74, IPV4 | ARRAY, "irc_server" },
|
|
{ 75, IPV4 | ARRAY, "streettalk_server" },
|
|
{ 76, IPV4 | ARRAY, "streettalk_directory_assistance_server" },
|
|
{ 77, STRING, "user_class" },
|
|
{ 81, STRING | RFC3397, "fqdn_name" },
|
|
{ 85, IPV4 | ARRAY, "nds_servers" },
|
|
{ 86, STRING, "nds_tree_name" },
|
|
{ 87, STRING, "nds_context" },
|
|
{ 88, STRING | RFC3397, "bcms_controller_names" },
|
|
{ 89, IPV4 | ARRAY, "bcms_controller_address" },
|
|
{ 91, UINT32, "client_last_transaction_time" },
|
|
{ 92, IPV4 | ARRAY, "associated_ip" },
|
|
{ 98, STRING, "uap_servers" },
|
|
{ 112, IPV4 | ARRAY, "netinfo_server_address" },
|
|
{ 113, STRING, "netinfo_server_tag" },
|
|
{ 114, STRING, "default_url" },
|
|
{ 118, IPV4, "subnet_selection" },
|
|
{ 119, STRING | RFC3397, "domain_search" },
|
|
{ 0, 0, NULL }
|
|
};
|
|
|
|
static int
|
|
valid_length(uint8_t option, int dl, int *type)
|
|
{
|
|
const struct dhcp_opt *opt;
|
|
ssize_t sz;
|
|
|
|
if (dl == 0)
|
|
return -1;
|
|
|
|
for (opt = dhcp_opts; opt->option; opt++) {
|
|
if (opt->option != option)
|
|
continue;
|
|
|
|
if (type)
|
|
*type = opt->type;
|
|
|
|
if (opt->type == 0 ||
|
|
opt->type & STRING ||
|
|
opt->type & RFC3442)
|
|
return 0;
|
|
|
|
sz = 0;
|
|
if (opt->type & UINT32 || opt->type & IPV4)
|
|
sz = sizeof(uint32_t);
|
|
if (opt->type & UINT16)
|
|
sz = sizeof(uint16_t);
|
|
if (opt->type & UINT8)
|
|
sz = sizeof(uint8_t);
|
|
if (opt->type & IPV4 || opt->type & ARRAY)
|
|
return dl % sz;
|
|
return (dl == sz ? 0 : -1);
|
|
}
|
|
|
|
/* unknown option, so let it pass */
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DEBUG_MEMORY
|
|
static void
|
|
free_option_buffer(void)
|
|
{
|
|
free(opt_buffer);
|
|
}
|
|
#endif
|
|
|
|
#define get_option_raw(dhcp, opt) get_option(dhcp, opt, NULL, NULL)
|
|
static const uint8_t *
|
|
get_option(const struct dhcp_message *dhcp, uint8_t opt, int *len, int *type)
|
|
{
|
|
const uint8_t *p = dhcp->options;
|
|
const uint8_t *e = p + sizeof(dhcp->options);
|
|
uint8_t l, ol = 0;
|
|
uint8_t o = 0;
|
|
uint8_t overl = 0;
|
|
uint8_t *bp = NULL;
|
|
const uint8_t *op = NULL;
|
|
int bl = 0;
|
|
|
|
while (p < e) {
|
|
o = *p++;
|
|
if (o == opt) {
|
|
if (op) {
|
|
if (!opt_buffer) {
|
|
opt_buffer = xmalloc(sizeof(*dhcp));
|
|
#ifdef DEBUG_MEMORY
|
|
atexit(free_option_buffer);
|
|
#endif
|
|
}
|
|
if (!bp)
|
|
bp = opt_buffer;
|
|
memcpy(bp, op, ol);
|
|
bp += ol;
|
|
}
|
|
ol = *p;
|
|
op = p + 1;
|
|
bl += ol;
|
|
}
|
|
switch (o) {
|
|
case DHO_PAD:
|
|
continue;
|
|
case DHO_END:
|
|
if (overl & 1) {
|
|
/* bit 1 set means parse boot file */
|
|
overl &= ~1;
|
|
p = dhcp->bootfile;
|
|
e = p + sizeof(dhcp->bootfile);
|
|
} else if (overl & 2) {
|
|
/* bit 2 set means parse server name */
|
|
overl &= ~2;
|
|
p = dhcp->servername;
|
|
e = p + sizeof(dhcp->servername);
|
|
} else
|
|
goto exit;
|
|
break;
|
|
case DHO_OPTIONSOVERLOADED:
|
|
/* Ensure we only get this option once */
|
|
if (!overl)
|
|
overl = p[1];
|
|
break;
|
|
}
|
|
l = *p++;
|
|
p += l;
|
|
}
|
|
|
|
exit:
|
|
if (valid_length(opt, bl, type) == -1) {
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
if (len)
|
|
*len = bl;
|
|
if (bp) {
|
|
memcpy(bp, op, ol);
|
|
return (const uint8_t *)opt_buffer;
|
|
}
|
|
if (op)
|
|
return op;
|
|
errno = ENOENT;
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
get_option_addr(struct in_addr *a, const struct dhcp_message *dhcp,
|
|
uint8_t option)
|
|
{
|
|
const uint8_t *p = get_option_raw(dhcp, option);
|
|
|
|
if (!p)
|
|
return -1;
|
|
memcpy(&a->s_addr, p, sizeof(a->s_addr));
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option)
|
|
{
|
|
const uint8_t *p = get_option_raw(dhcp, option);
|
|
uint32_t d;
|
|
|
|
if (!p)
|
|
return -1;
|
|
memcpy(&d, p, sizeof(d));
|
|
*i = ntohl(d);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
get_option_uint16(uint16_t *i, const struct dhcp_message *dhcp, uint8_t option)
|
|
{
|
|
const uint8_t *p = get_option_raw(dhcp, option);
|
|
uint16_t d;
|
|
|
|
if (!p)
|
|
return -1;
|
|
memcpy(&d, p, sizeof(d));
|
|
*i = ntohs(d);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option)
|
|
{
|
|
const uint8_t *p = get_option_raw(dhcp, option);
|
|
|
|
if (!p)
|
|
return -1;
|
|
if (i)
|
|
*i = *(p);
|
|
return 0;
|
|
}
|
|
|
|
static struct rt *
|
|
decode_rfc3442_rt(int dl, const uint8_t *data)
|
|
{
|
|
const uint8_t *p = data;
|
|
const uint8_t *e;
|
|
uint8_t cidr;
|
|
size_t ocets;
|
|
struct rt *routes = NULL;
|
|
struct rt *rt = NULL;
|
|
|
|
/* Minimum is 5 -first is CIDR and a router length of 4 */
|
|
if (dl < 5)
|
|
return NULL;
|
|
|
|
e = p + dl;
|
|
while (p < e) {
|
|
cidr = *p++;
|
|
if (cidr > 32) {
|
|
free_routes(routes);
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
if (rt) {
|
|
rt->next = xzalloc(sizeof(*rt));
|
|
rt = rt->next;
|
|
} else {
|
|
routes = rt = xzalloc(sizeof(*routes));
|
|
}
|
|
rt->next = NULL;
|
|
|
|
ocets = (cidr + 7) / 8;
|
|
/* If we have ocets then we have a destination and netmask */
|
|
if (ocets > 0) {
|
|
memcpy(&rt->dest.s_addr, p, ocets);
|
|
p += ocets;
|
|
rt->net.s_addr = htonl(~0U << (32 - cidr));
|
|
}
|
|
|
|
/* Finally, snag the router */
|
|
memcpy(&rt->gate.s_addr, p, 4);
|
|
p += 4;
|
|
}
|
|
return routes;
|
|
}
|
|
|
|
|
|
/* This calculates the netmask that we should use for static routes.
|
|
* This IS different from the calculation used to calculate the netmask
|
|
* for an interface address. */
|
|
static uint32_t
|
|
route_netmask(uint32_t ip_in)
|
|
{
|
|
/* used to be unsigned long - check if error */
|
|
uint32_t p = ntohl(ip_in);
|
|
uint32_t t;
|
|
|
|
if (IN_CLASSA(p))
|
|
t = ~IN_CLASSA_NET;
|
|
else {
|
|
if (IN_CLASSB(p))
|
|
t = ~IN_CLASSB_NET;
|
|
else {
|
|
if (IN_CLASSC(p))
|
|
t = ~IN_CLASSC_NET;
|
|
else
|
|
t = 0;
|
|
}
|
|
}
|
|
|
|
while (t & p)
|
|
t >>= 1;
|
|
|
|
return (htonl(~t));
|
|
}
|
|
|
|
/* We need to obey routing options.
|
|
* If we have a CSR then we only use that.
|
|
* Otherwise we add static routes and then routers. */
|
|
struct rt *
|
|
get_option_routes(const struct dhcp_message *dhcp,
|
|
const char *ifname, int *opts)
|
|
{
|
|
const uint8_t *p;
|
|
const uint8_t *e;
|
|
struct rt *routes = NULL;
|
|
struct rt *route = NULL;
|
|
int len;
|
|
|
|
/* If we have CSR's then we MUST use these only */
|
|
p = get_option(dhcp, DHO_CSR, &len, NULL);
|
|
/* Check for crappy MS option */
|
|
if (!p)
|
|
p = get_option(dhcp, DHO_MSCSR, &len, NULL);
|
|
if (p) {
|
|
routes = decode_rfc3442_rt(len, p);
|
|
if (routes && !(*opts & DHCPCD_CSR_WARNED)) {
|
|
*opts |= DHCPCD_CSR_WARNED;
|
|
return routes;
|
|
}
|
|
}
|
|
|
|
/* OK, get our static routes first. */
|
|
p = get_option(dhcp, DHO_STATICROUTE, &len, NULL);
|
|
if (p) {
|
|
e = p + len;
|
|
while (p < e) {
|
|
if (route) {
|
|
route->next = xmalloc(sizeof(*route));
|
|
route = route->next;
|
|
} else
|
|
routes = route = xmalloc(sizeof(*routes));
|
|
route->next = NULL;
|
|
memcpy(&route->dest.s_addr, p, 4);
|
|
p += 4;
|
|
memcpy(&route->gate.s_addr, p, 4);
|
|
p += 4;
|
|
route->net.s_addr = route_netmask(route->dest.s_addr);
|
|
}
|
|
}
|
|
|
|
/* Now grab our routers */
|
|
p = get_option(dhcp, DHO_ROUTER, &len, NULL);
|
|
if (p) {
|
|
e = p + len;
|
|
while (p < e) {
|
|
if (route) {
|
|
route->next = xzalloc(sizeof(*route));
|
|
route = route->next;
|
|
} else
|
|
routes = route = xzalloc(sizeof(*route));
|
|
memcpy(&route->gate.s_addr, p, 4);
|
|
p += 4;
|
|
}
|
|
}
|
|
|
|
return routes;
|
|
}
|
|
|
|
static size_t
|
|
encode_rfc1035(const char *src, uint8_t *dst)
|
|
{
|
|
uint8_t *p = dst;
|
|
uint8_t *lp = p++;
|
|
|
|
if (*src == '\0')
|
|
return 0;
|
|
for (; *src; src++) {
|
|
if (*src == '\0')
|
|
break;
|
|
if (*src == '.') {
|
|
/* Skip the trailing . */
|
|
if (src[1] == '\0')
|
|
break;
|
|
*lp = p - lp - 1;
|
|
if (*lp == '\0')
|
|
return p - dst;
|
|
lp = p++;
|
|
} else
|
|
*p++ = (uint8_t)*src;
|
|
}
|
|
*lp = p - lp - 1;
|
|
*p++ = '\0';
|
|
return p - dst;
|
|
}
|
|
|
|
#define PUTADDR(_type, _val) \
|
|
{ \
|
|
*p++ = _type; \
|
|
*p++ = 4; \
|
|
memcpy(p, &_val.s_addr, 4); \
|
|
p += 4; \
|
|
}
|
|
|
|
int
|
|
dhcp_message_add_addr(struct dhcp_message *dhcp,
|
|
uint8_t type, struct in_addr addr)
|
|
{
|
|
uint8_t *p;
|
|
size_t len;
|
|
|
|
p = dhcp->options;
|
|
while (*p != DHO_END) {
|
|
p++;
|
|
p += *p + 1;
|
|
}
|
|
|
|
len = p - (uint8_t *)dhcp;
|
|
if (len + 6 > sizeof(*dhcp)) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
PUTADDR(type, addr);
|
|
*p = DHO_END;
|
|
return 0;
|
|
}
|
|
|
|
ssize_t
|
|
make_message(struct dhcp_message **message,
|
|
const struct interface *iface,
|
|
uint8_t type)
|
|
{
|
|
struct dhcp_message *dhcp;
|
|
uint8_t *m, *lp, *p;
|
|
uint8_t *n_params = NULL;
|
|
time_t up = uptime() - iface->start_uptime;
|
|
uint32_t ul;
|
|
uint16_t sz;
|
|
size_t len;
|
|
const char *hp;
|
|
const struct dhcp_opt *opt;
|
|
const struct if_options *ifo = iface->state->options;
|
|
const struct dhcp_lease *lease = &iface->state->lease;
|
|
|
|
dhcp = xzalloc(sizeof (*dhcp));
|
|
m = (uint8_t *)dhcp;
|
|
p = dhcp->options;
|
|
|
|
if ((type == DHCP_INFORM || type == DHCP_RELEASE ||
|
|
(type == DHCP_REQUEST &&
|
|
iface->net.s_addr == lease->net.s_addr &&
|
|
(iface->state->new == NULL ||
|
|
iface->state->new->cookie == htonl(MAGIC_COOKIE)))))
|
|
{
|
|
dhcp->ciaddr = iface->addr.s_addr;
|
|
/* In-case we haven't actually configured the address yet */
|
|
if (type == DHCP_INFORM && iface->addr.s_addr == 0)
|
|
dhcp->ciaddr = lease->addr.s_addr;
|
|
}
|
|
|
|
dhcp->op = DHCP_BOOTREQUEST;
|
|
dhcp->hwtype = iface->family;
|
|
switch (iface->family) {
|
|
case ARPHRD_ETHER:
|
|
case ARPHRD_IEEE802:
|
|
dhcp->hwlen = iface->hwlen;
|
|
memcpy(&dhcp->chaddr, &iface->hwaddr, iface->hwlen);
|
|
break;
|
|
}
|
|
|
|
if (ifo->options & DHCPCD_BROADCAST &&
|
|
dhcp->ciaddr == 0 &&
|
|
type != DHCP_DECLINE &&
|
|
type != DHCP_RELEASE)
|
|
dhcp->flags = htons(BROADCAST_FLAG);
|
|
|
|
if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
|
|
if (up < 0 || up > (time_t)UINT16_MAX)
|
|
dhcp->secs = htons((uint16_t)UINT16_MAX);
|
|
else
|
|
dhcp->secs = htons(up);
|
|
}
|
|
dhcp->xid = iface->state->xid;
|
|
dhcp->cookie = htonl(MAGIC_COOKIE);
|
|
|
|
*p++ = DHO_MESSAGETYPE;
|
|
*p++ = 1;
|
|
*p++ = type;
|
|
|
|
if (iface->clientid) {
|
|
*p++ = DHO_CLIENTID;
|
|
memcpy(p, iface->clientid, iface->clientid[0] + 1);
|
|
p += iface->clientid[0] + 1;
|
|
}
|
|
|
|
if (lease->addr.s_addr && lease->cookie == htonl(MAGIC_COOKIE)) {
|
|
if (type == DHCP_DECLINE ||
|
|
(type == DHCP_REQUEST &&
|
|
lease->addr.s_addr != iface->addr.s_addr))
|
|
{
|
|
PUTADDR(DHO_IPADDRESS, lease->addr);
|
|
if (lease->server.s_addr)
|
|
PUTADDR(DHO_SERVERID, lease->server);
|
|
}
|
|
|
|
if (type == DHCP_RELEASE) {
|
|
if (lease->server.s_addr)
|
|
PUTADDR(DHO_SERVERID, lease->server);
|
|
}
|
|
}
|
|
|
|
if (type == DHCP_DECLINE) {
|
|
*p++ = DHO_MESSAGE;
|
|
len = strlen(DAD);
|
|
*p++ = len;
|
|
memcpy(p, DAD, len);
|
|
p += len;
|
|
}
|
|
|
|
if (type == DHCP_DISCOVER && ifo->options & DHCPCD_REQUEST)
|
|
PUTADDR(DHO_IPADDRESS, ifo->req_addr);
|
|
|
|
if (type == DHCP_DISCOVER ||
|
|
type == DHCP_INFORM ||
|
|
type == DHCP_REQUEST)
|
|
{
|
|
*p++ = DHO_MAXMESSAGESIZE;
|
|
*p++ = 2;
|
|
sz = get_mtu(iface->name);
|
|
if (sz < MTU_MIN) {
|
|
if (set_mtu(iface->name, MTU_MIN) == 0)
|
|
sz = MTU_MIN;
|
|
} else if (sz > MTU_MAX) {
|
|
/* Even though our MTU could be greater than
|
|
* MTU_MAX (1500) dhcpcd does not presently
|
|
* handle DHCP packets any bigger. */
|
|
sz = MTU_MAX;
|
|
}
|
|
sz = htons(sz);
|
|
memcpy(p, &sz, 2);
|
|
p += 2;
|
|
|
|
if (ifo->userclass[0]) {
|
|
*p++ = DHO_USERCLASS;
|
|
memcpy(p, ifo->userclass, ifo->userclass[0] + 1);
|
|
p += ifo->userclass[0] + 1;
|
|
}
|
|
|
|
if (ifo->vendorclassid[0]) {
|
|
*p++ = DHO_VENDORCLASSID;
|
|
memcpy(p, ifo->vendorclassid,
|
|
ifo->vendorclassid[0] + 1);
|
|
p += ifo->vendorclassid[0] + 1;
|
|
}
|
|
|
|
|
|
if (type != DHCP_INFORM) {
|
|
if (ifo->leasetime != 0) {
|
|
*p++ = DHO_LEASETIME;
|
|
*p++ = 4;
|
|
ul = htonl(ifo->leasetime);
|
|
memcpy(p, &ul, 4);
|
|
p += 4;
|
|
}
|
|
}
|
|
|
|
/* Regardless of RFC2132, we should always send a hostname
|
|
* upto the first dot (the short hostname) as otherwise
|
|
* confuses some DHCP servers when updating DNS.
|
|
* The FQDN option should be used if a FQDN is required. */
|
|
if (ifo->options & DHCPCD_HOSTNAME && ifo->hostname[0]) {
|
|
*p++ = DHO_HOSTNAME;
|
|
hp = strchr(ifo->hostname, '.');
|
|
if (hp)
|
|
len = hp - ifo->hostname;
|
|
else
|
|
len = strlen(ifo->hostname);
|
|
*p++ = len;
|
|
memcpy(p, ifo->hostname, len);
|
|
p += len;
|
|
}
|
|
if (ifo->fqdn != FQDN_DISABLE && ifo->hostname[0]) {
|
|
/* IETF DHC-FQDN option (81), RFC4702 */
|
|
*p++ = DHO_FQDN;
|
|
lp = p;
|
|
*p++ = 3;
|
|
/*
|
|
* Flags: 0000NEOS
|
|
* S: 1 => Client requests Server to update
|
|
* a RR in DNS as well as PTR
|
|
* O: 1 => Server indicates to client that
|
|
* DNS has been updated
|
|
* E: 1 => Name data is DNS format
|
|
* N: 1 => Client requests Server to not
|
|
* update DNS
|
|
*/
|
|
*p++ = (ifo->fqdn & 0x09) | 0x04;
|
|
*p++ = 0; /* from server for PTR RR */
|
|
*p++ = 0; /* from server for A RR if S=1 */
|
|
ul = encode_rfc1035(ifo->hostname, p);
|
|
*lp += ul;
|
|
p += ul;
|
|
}
|
|
|
|
/* vendor is already encoded correctly, so just add it */
|
|
if (ifo->vendor[0]) {
|
|
*p++ = DHO_VENDOR;
|
|
memcpy(p, ifo->vendor, ifo->vendor[0] + 1);
|
|
p += ifo->vendor[0] + 1;
|
|
}
|
|
|
|
*p++ = DHO_PARAMETERREQUESTLIST;
|
|
n_params = p;
|
|
*p++ = 0;
|
|
for (opt = dhcp_opts; opt->option; opt++) {
|
|
if (!(opt->type & REQUEST ||
|
|
has_option_mask(ifo->requestmask, opt->option)))
|
|
continue;
|
|
if (type == DHCP_INFORM &&
|
|
(opt->option == DHO_RENEWALTIME ||
|
|
opt->option == DHO_REBINDTIME))
|
|
continue;
|
|
*p++ = opt->option;
|
|
}
|
|
*n_params = p - n_params - 1;
|
|
}
|
|
*p++ = DHO_END;
|
|
|
|
#ifdef BOOTP_MESSAGE_LENTH_MIN
|
|
/* Some crappy DHCP servers think they have to obey the BOOTP minimum
|
|
* message length.
|
|
* They are wrong, but we should still cater for them. */
|
|
while (p - m < BOOTP_MESSAGE_LENTH_MIN)
|
|
*p++ = DHO_PAD;
|
|
#endif
|
|
|
|
*message = dhcp;
|
|
return p - m;
|
|
}
|
|
|
|
void
|
|
get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
|
|
{
|
|
struct timeval now;
|
|
|
|
lease->cookie = dhcp->cookie;
|
|
/* BOOTP does not set yiaddr for replies when ciaddr is set. */
|
|
if (dhcp->yiaddr)
|
|
lease->addr.s_addr = dhcp->yiaddr;
|
|
else
|
|
lease->addr.s_addr = dhcp->ciaddr;
|
|
if (get_option_addr(&lease->net, dhcp, DHO_SUBNETMASK) == -1)
|
|
lease->net.s_addr = get_netmask(lease->addr.s_addr);
|
|
if (get_option_addr(&lease->brd, dhcp, DHO_BROADCAST) == -1)
|
|
lease->brd.s_addr = lease->addr.s_addr | ~lease->net.s_addr;
|
|
if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) == 0) {
|
|
/* Ensure that we can use the lease */
|
|
get_monotonic(&now);
|
|
if (now.tv_sec + (time_t)lease->leasetime < now.tv_sec)
|
|
lease->leasetime = ~0U; /* Infinite lease */
|
|
} else
|
|
lease->leasetime = ~0U; /* Default to infinite lease */
|
|
if (get_option_uint32(&lease->renewaltime, dhcp, DHO_RENEWALTIME) != 0)
|
|
lease->renewaltime = 0;
|
|
if (get_option_uint32(&lease->rebindtime, dhcp, DHO_REBINDTIME) != 0)
|
|
lease->rebindtime = 0;
|
|
if (get_option_addr(&lease->server, dhcp, DHO_SERVERID) != 0)
|
|
lease->server.s_addr = INADDR_ANY;
|
|
}
|