diff options
Diffstat (limited to 'src/dhcp-common.c')
-rw-r--r-- | src/dhcp-common.c | 142 |
1 files changed, 102 insertions, 40 deletions
diff --git a/src/dhcp-common.c b/src/dhcp-common.c index d9719d1..eae9886 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2020 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,7 +38,7 @@ void dhcp_common_init(void) ssize_t recv_dhcp_packet(int fd, struct msghdr *msg) { - ssize_t sz; + ssize_t sz, new_sz; while (1) { @@ -65,9 +65,18 @@ ssize_t recv_dhcp_packet(int fd, struct msghdr *msg) } } - while ((sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR); + while ((new_sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR); + + /* Some kernels seem to ignore MSG_PEEK, and dequeue the packet anyway. + If that happens we get EAGAIN here because the socket is non-blocking. + Use the result of the original testing recvmsg as long as the buffer + was big enough. There's a small race here that may lose the odd packet, + but it's UDP anyway. */ + + if (new_sz == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) + new_sz = sz; - return (msg->msg_flags & MSG_TRUNC) ? -1 : sz; + return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz; } struct dhcp_netid *run_tag_if(struct dhcp_netid *tags) @@ -271,35 +280,45 @@ static int is_config_in_context(struct dhcp_context *context, struct dhcp_config { if (!context) /* called via find_config() from lease_update_from_configs() */ return 1; - - if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6))) - return 1; #ifdef HAVE_DHCP6 - if ((context->flags & CONTEXT_V6) && (config->flags & CONFIG_WILDCARD)) - return 1; -#endif + if (context->flags & CONTEXT_V6) + { + struct addrlist *addr_list; - for (; context; context = context->current) -#ifdef HAVE_DHCP6 - if (context->flags & CONTEXT_V6) - { - if ((config->flags & CONFIG_ADDR6) && is_same_net6(&config->addr6, &context->start6, context->prefix)) - return 1; - } - else + if (!(config->flags & CONFIG_ADDR6)) + return 1; + + for (; context; context = context->current) + for (addr_list = config->addr6; addr_list; addr_list = addr_list->next) + { + if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64) + return 1; + + if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix)) + return 1; + } + } + else #endif - if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask)) + { + if (!(config->flags & CONFIG_ADDR)) return 1; + + for (; context; context = context->current) + if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask)) + return 1; + } return 0; } -struct dhcp_config *find_config(struct dhcp_config *configs, - struct dhcp_context *context, - unsigned char *clid, int clid_len, - unsigned char *hwaddr, int hw_len, - int hw_type, char *hostname) +static struct dhcp_config *find_config_match(struct dhcp_config *configs, + struct dhcp_context *context, + unsigned char *clid, int clid_len, + unsigned char *hwaddr, int hw_len, + int hw_type, char *hostname, + struct dhcp_netid *tags, int tag_not_needed) { int count, new; struct dhcp_config *config, *candidate; @@ -311,7 +330,9 @@ struct dhcp_config *find_config(struct dhcp_config *configs, { if (config->clid_len == clid_len && memcmp(config->clid, clid, clid_len) == 0 && - is_config_in_context(context, config)) + is_config_in_context(context, config) && + match_netid(config->filter, tags, tag_not_needed)) + return config; /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and @@ -319,7 +340,8 @@ struct dhcp_config *find_config(struct dhcp_config *configs, see lease_update_from_configs() */ if ((!context || !(context->flags & CONTEXT_V6)) && *clid == 0 && config->clid_len == clid_len-1 && memcmp(config->clid, clid+1, clid_len-1) == 0 && - is_config_in_context(context, config)) + is_config_in_context(context, config) && + match_netid(config->filter, tags, tag_not_needed)) return config; } @@ -327,14 +349,16 @@ struct dhcp_config *find_config(struct dhcp_config *configs, if (hwaddr) for (config = configs; config; config = config->next) if (config_has_mac(config, hwaddr, hw_len, hw_type) && - is_config_in_context(context, config)) + is_config_in_context(context, config) && + match_netid(config->filter, tags, tag_not_needed)) return config; if (hostname && context) for (config = configs; config; config = config->next) if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, hostname) && - is_config_in_context(context, config)) + is_config_in_context(context, config) && + match_netid(config->filter, tags, tag_not_needed)) return config; @@ -343,7 +367,8 @@ struct dhcp_config *find_config(struct dhcp_config *configs, /* use match with fewest wildcard octets */ for (candidate = NULL, count = 0, config = configs; config; config = config->next) - if (is_config_in_context(context, config)) + if (is_config_in_context(context, config) && + match_netid(config->filter, tags, tag_not_needed)) for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next) if (conf_addr->wildcard_mask != 0 && conf_addr->hwaddr_len == hw_len && @@ -357,6 +382,21 @@ struct dhcp_config *find_config(struct dhcp_config *configs, return candidate; } +/* Find tagged configs first. */ +struct dhcp_config *find_config(struct dhcp_config *configs, + struct dhcp_context *context, + unsigned char *clid, int clid_len, + unsigned char *hwaddr, int hw_len, + int hw_type, char *hostname, struct dhcp_netid *tags) +{ + struct dhcp_config *ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 0); + + if (!ret) + ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 1); + + return ret; +} + void dhcp_update_configs(struct dhcp_config *configs) { /* Some people like to keep all static IP addresses in /etc/hosts. @@ -371,8 +411,14 @@ void dhcp_update_configs(struct dhcp_config *configs) int prot = AF_INET; for (config = configs; config; config = config->next) + { if (config->flags & CONFIG_ADDR_HOSTS) - config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR6 | CONFIG_ADDR_HOSTS); + config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS); +#ifdef HAVE_DHCP6 + if (config->flags & CONFIG_ADDR6_HOSTS) + config->flags &= ~(CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS); +#endif + } #ifdef HAVE_DHCP6 again: @@ -403,30 +449,41 @@ void dhcp_update_configs(struct dhcp_config *configs) crec = cache_find_by_name(crec, config->hostname, 0, cacheflags); if (!crec) continue; /* should be never */ - inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN); + inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"), config->hostname, daemon->addrbuff); } if (prot == AF_INET && - (!(conf_tmp = config_find_by_address(configs, crec->addr.addr.addr.addr4)) || conf_tmp == config)) + (!(conf_tmp = config_find_by_address(configs, crec->addr.addr4)) || conf_tmp == config)) { - config->addr = crec->addr.addr.addr.addr4; + config->addr = crec->addr.addr4; config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS; continue; } #ifdef HAVE_DHCP6 if (prot == AF_INET6 && - (!(conf_tmp = config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0)) || conf_tmp == config)) + (!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr6)) || conf_tmp == config)) { - memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ); - config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS; + /* host must have exactly one address if comming from /etc/hosts. */ + if (!config->addr6 && (config->addr6 = whine_malloc(sizeof(struct addrlist)))) + { + config->addr6->next = NULL; + config->addr6->flags = 0; + } + + if (config->addr6 && !config->addr6->next && !(config->addr6->flags & (ADDRLIST_WILDCARD|ADDRLIST_PREFIX))) + { + memcpy(&config->addr6->addr.addr6, &crec->addr.addr6, IN6ADDRSZ); + config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS; + } + continue; } #endif - inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN); + inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), daemon->addrbuff, config->hostname); @@ -485,8 +542,11 @@ char *whichdevice(void) void bindtodevice(char *device, int fd) { + size_t len = strlen(device)+1; + if (len > IFNAMSIZ) + len = IFNAMSIZ; /* only allowed by root. */ - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, IFNAMSIZ) == -1 && + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 && errno != EPERM) die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET); } @@ -556,6 +616,7 @@ static const struct opttab_t { { "nntp-server", 71, OT_ADDR_LIST }, { "irc-server", 74, OT_ADDR_LIST }, { "user-class", 77, 0 }, + { "rapid-commit", 80, 0 }, { "FQDN", 81, OT_INTERNAL }, { "agent-id", 82, OT_INTERNAL }, { "client-arch", 93, 2 | OT_DEC }, @@ -566,6 +627,7 @@ static const struct opttab_t { { "sip-server", 120, 0 }, { "classless-static-route", 121, 0 }, { "vendor-id-encap", 125, 0 }, + { "tftp-server-address", 150, OT_ADDR_LIST }, { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */ { NULL, 0, 0 } }; @@ -596,7 +658,7 @@ static const struct opttab_t opttab6[] = { { "sntp-server", 31, OT_ADDR_LIST }, { "information-refresh-time", 32, OT_TIME }, { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME }, - { "ntp-server", 56, 0 }, + { "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ }, { "bootfile-url", 59, OT_NAME }, { "bootfile-param", 60, OT_CSTRING }, { NULL, 0, 0 } @@ -689,7 +751,7 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, if (ot[o].size & OT_ADDR_LIST) { - struct all_addr addr; + union all_addr addr; int addr_len = INADDRSZ; #ifdef HAVE_DHCP6 |