aboutsummaryrefslogtreecommitdiffstats
path: root/dhcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'dhcp.c')
-rw-r--r--dhcp.c179
1 files changed, 108 insertions, 71 deletions
diff --git a/dhcp.c b/dhcp.c
index 2469429..1854cf2 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -52,6 +52,8 @@
#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. */
@@ -65,8 +67,13 @@ struct dhcp_opt {
static const struct dhcp_opt const dhcp_opts[] = {
{ 1, IPV4 | REQUEST, "subnet_mask" },
- { 2, UINT32, "time_offset" },
+ /* RFC 3442 states that the CSR has to come before all other routes.
+ * For completeness, we also specify static routes, then routers. */
+ { 121, RFC3442 | REQUEST, "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" },
@@ -96,7 +103,6 @@ static const struct dhcp_opt const dhcp_opts[] = {
{ 30, UINT8, "mask_supplier" },
{ 31, UINT8, "router_discovery" },
{ 32, IPV4, "router_solicitation_address" },
- { 33, IPV4 | ARRAY | REQUEST, "static_routes" },
{ 34, UINT8, "trailer_encapsulation" },
{ 35, UINT32, "arp_cache_timeout" },
{ 36, UINT16, "ieee802_3_encapsulation" },
@@ -151,8 +157,6 @@ static const struct dhcp_opt const dhcp_opts[] = {
{ 114, STRING, "default_url" },
{ 118, IPV4, "subnet_selection" },
{ 119, STRING | RFC3397, "domain_search" },
- { 121, RFC3442 | REQUEST, "classless_static_routes" },
- { 249, RFC3442, "ms_classless_static_routes" },
{ 0, 0, NULL }
};
@@ -323,25 +327,27 @@ exit:
}
int
-get_option_addr(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option)
+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, p, sizeof(*a));
+ 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)
{
- uint32_t a;
+ const uint8_t *p = get_option_raw(dhcp, option);
+ uint32_t d;
- if (get_option_addr(&a, dhcp, option) == -1)
+ if (!p)
return -1;
-
- *i = ntohl(a);
+ memcpy(&d, p, sizeof(d));
+ *i = ntohl(d);
return 0;
}
@@ -715,34 +721,39 @@ get_option_routes(const struct dhcp_message *dhcp)
}
static size_t
-encode_rfc1035(const char *src, uint8_t *dst, size_t len)
+encode_rfc1035(const char *src, uint8_t *dst)
{
- const char *c = src;
uint8_t *p = dst;
uint8_t *lp = p++;
- if (len == 0)
+ if (*src == '\0')
return 0;
- while (c < src + len) {
- if (*c == '\0')
+ for (; *src; src++) {
+ if (*src == '\0')
break;
- if (*c == '.') {
+ if (*src == '.') {
/* Skip the trailing . */
- if (c == src + len - 1)
+ if (src[1] == '\0')
break;
*lp = p - lp - 1;
if (*lp == '\0')
return p - dst;
lp = p++;
} else
- *p++ = (uint8_t) *c;
- c++;
+ *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; \
+}
ssize_t
make_message(struct dhcp_message **message,
const struct interface *iface, const struct dhcp_lease *lease,
@@ -755,6 +766,8 @@ make_message(struct dhcp_message **message,
uint32_t ul;
uint16_t sz;
const struct dhcp_opt *opt;
+ size_t len;
+ const char *hp;
dhcp = xzalloc(sizeof (*dhcp));
m = (uint8_t *)dhcp;
@@ -786,15 +799,18 @@ make_message(struct dhcp_message **message,
case ARPHRD_IEEE1394:
case ARPHRD_INFINIBAND:
dhcp->hwlen = 0;
- if (dhcp->ciaddr == 0)
+ if (dhcp->ciaddr == 0 &&
+ type != DHCP_DECLINE && type != DHCP_RELEASE)
dhcp->flags = htons(BROADCAST_FLAG);
break;
}
- if (up < 0 || up > (time_t)UINT16_MAX)
- dhcp->secs = htons((uint16_t)UINT16_MAX);
- else
- dhcp->secs = htons(up);
+ 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 = xid;
dhcp->cookie = htonl(MAGIC_COOKIE);
@@ -802,7 +818,41 @@ make_message(struct dhcp_message **message,
*p++ = 1;
*p++ = type;
- if (type == DHCP_REQUEST) {
+ if (iface->clientid) {
+ *p++ = DHO_CLIENTID;
+ memcpy(p, iface->clientid, iface->clientid[0] + 1);
+ p += iface->clientid[0] + 1;
+ }
+
+ if (lease->addr.s_addr && !IN_LINKLOCAL(htonl(lease->addr.s_addr))) {
+ if (type == DHCP_DECLINE ||
+ type == DHCP_DISCOVER ||
+ (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_DECLINE) {
+ *p++ = DHO_MESSAGE;
+ len = strlen(DAD);
+ *p++ = len;
+ memcpy(p, DAD, len);
+ p += len;
+ }
+
+ if (type == DHCP_RELEASE) {
+ if (lease->server.s_addr)
+ PUTADDR(DHO_SERVERID, lease->server);
+ }
+
+ if (type == DHCP_DISCOVER ||
+ type == DHCP_INFORM ||
+ type == DHCP_REQUEST)
+ {
*p++ = DHO_MAXMESSAGESIZE;
*p++ = 2;
sz = get_mtu(iface->name);
@@ -813,15 +863,7 @@ make_message(struct dhcp_message **message,
sz = htons(sz);
memcpy(p, &sz, 2);
p += 2;
- }
- if (iface->clientid) {
- *p++ = DHO_CLIENTID;
- memcpy(p, iface->clientid, iface->clientid[0] + 1);
- p += iface->clientid[0] + 1;
- }
-
- if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
if (options->userclass[0]) {
*p++ = DHO_USERCLASS;
memcpy(p, options->userclass, options->userclass[0] + 1);
@@ -834,43 +876,31 @@ make_message(struct dhcp_message **message,
options->vendorclassid[0] + 1);
p += options->vendorclassid[0] + 1;
}
- }
- if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
-#define PUTADDR(_type, _val) \
- { \
- *p++ = _type; \
- *p++ = 4; \
- memcpy(p, &_val.s_addr, 4); \
- p += 4; \
- }
- if (lease->addr.s_addr &&
- lease->addr.s_addr != iface->addr.s_addr &&
- !IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
- {
- PUTADDR(DHO_IPADDRESS, lease->addr);
- if (lease->server.s_addr)
- PUTADDR(DHO_SERVERID, lease->server);
- }
-#undef PUTADDR
-
- if (options->leasetime != 0) {
- *p++ = DHO_LEASETIME;
- *p++ = 4;
- ul = htonl(options->leasetime);
- memcpy(p, &ul, 4);
- p += 4;
+ if (type != DHCP_INFORM) {
+ if (options->leasetime != 0) {
+ *p++ = DHO_LEASETIME;
+ *p++ = 4;
+ ul = htonl(options->leasetime);
+ memcpy(p, &ul, 4);
+ p += 4;
+ }
}
- }
- if (type == DHCP_DISCOVER ||
- type == DHCP_INFORM ||
- type == DHCP_REQUEST)
- {
+ /* 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 (options->hostname[0]) {
*p++ = DHO_HOSTNAME;
- memcpy(p, options->hostname, options->hostname[0] + 1);
- p += options->hostname[0] + 1;
+ hp = strchr(options->hostname, '.');
+ if (hp)
+ len = hp - options->hostname;
+ else
+ len = strlen(options->hostname);
+ *p++ = len;
+ memcpy(p, options->hostname, len);
+ p += len;
}
if (options->fqdn != FQDN_DISABLE) {
/* IETF DHC-FQDN option (81), RFC4702 */
@@ -890,8 +920,7 @@ make_message(struct dhcp_message **message,
*p++ = (options->fqdn & 0x09) | 0x04;
*p++ = 0; /* from server for PTR RR */
*p++ = 0; /* from server for A RR if S=1 */
- ul = encode_rfc1035(options->hostname + 1, p,
- options->hostname[0]);
+ ul = encode_rfc1035(options->hostname, p);
*lp += ul;
p += ul;
}
@@ -999,13 +1028,21 @@ static ssize_t
print_string(char *s, ssize_t len, int dl, const uint8_t *data)
{
uint8_t c;
- const uint8_t *e;
+ const uint8_t *e, *p;
ssize_t bytes = 0;
ssize_t r;
e = data + dl;
while (data < e) {
c = *data++;
+ if (c == '\0') {
+ /* If rest is all NULL, skip it. */
+ for (p = data; p < e; p++)
+ if (*p != '\0')
+ break;
+ if (p == e)
+ break;
+ }
if (!isascii(c) || !isprint(c)) {
if (s) {
if (len < 5) {
@@ -1203,16 +1240,16 @@ configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
* message but are not necessarily in the options */
addr.s_addr = dhcp->yiaddr;
setvar(&ep, prefix, "ip_address", inet_ntoa(addr));
- if (get_option_addr(&net.s_addr, dhcp, DHO_SUBNETMASK) == -1) {
+ if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1) {
net.s_addr = get_netmask(addr.s_addr);
setvar(&ep, prefix, "subnet_mask", inet_ntoa(net));
}
i = inet_ntocidr(net);
snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net));
setvar(&ep, prefix, "subnet_cidr", cidr);
- if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1) {
+ if (get_option_addr(&brd, dhcp, DHO_BROADCAST) == -1) {
brd.s_addr = addr.s_addr | ~net.s_addr;
- setvar(&ep, prefix, "broadcast_address", inet_ntoa(net));
+ setvar(&ep, prefix, "broadcast_address", inet_ntoa(brd));
}
addr.s_addr = dhcp->yiaddr & net.s_addr;
setvar(&ep, prefix, "network_number", inet_ntoa(addr));