/* * dhcpcd - DHCP client daemon * Copyright (c) 2006-2010 Roy Marples * 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 #include #include #include #include #include #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; }