import of dnsmasq-2.41.tar.gz
diff --git a/src/dhcp.c b/src/dhcp.c
index 14252c4..dcd76d4 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -1,13 +1,17 @@
-/* dnsmasq is Copyright (c) 2000-2006 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2007 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
- the Free Software Foundation; version 2 dated June, 1991.
-
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
@@ -26,12 +30,17 @@
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in saddr;
int oneopt = 1;
- struct dhcp_config *configs, *cp;
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+ int mtu = IP_PMTUDISC_DONT;
+#endif
if (fd == -1)
die (_("cannot create DHCP socket : %s"), NULL, EC_BADNET);
- if (!fix_fd(fd) ||
+ if (!fix_fd(fd) ||
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+ setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
+#endif
#if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
#elif defined(IP_RECVIF)
@@ -73,7 +82,7 @@
daemon->dhcpfd = fd;
-#ifndef HAVE_LINUX_NETWORK
+#if defined(HAVE_BSD_NETWORK)
/* When we're not using capabilities, we need to do this here before
we drop root. Also, set buffer size small, to avoid wasting
kernel buffers */
@@ -88,21 +97,8 @@
init_bpf();
#endif
- /* If the same IP appears in more than one host config, then DISCOVER
- for one of the hosts will get the address, but REQUEST will be NAKed,
- since the address is reserved by the other one -> protocol loop.
- Also check that FQDNs match the domain we are using. */
- for (configs = daemon->dhcp_conf; configs; configs = configs->next)
- {
- char *domain;
- for (cp = configs->next; cp; cp = cp->next)
- if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
- die(_("duplicate IP address %s in dhcp-config directive."), inet_ntoa(cp->addr), EC_BADCONF);
-
- if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
- die(_("illegal domain %s in dhcp-config directive."), domain, EC_BADCONF);
- }
-
+ check_dhcp_hosts(1);
+
daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
}
@@ -124,9 +120,11 @@
union {
struct cmsghdr align; /* this ensures alignment */
-#ifdef HAVE_LINUX_NETWORK
+#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
-#else
+#elif defined(HAVE_SOLARIS_NETWORK)
+ char control[CMSG_SPACE(sizeof(unsigned int))];
+#elif defined(IP_RECVIF)
char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
@@ -147,7 +145,7 @@
expand_buf(&daemon->dhcp_packet, daemon->dhcp_packet.iov_len + 100));
/* expand_buf may have moved buffer */
- mess = daemon->dhcp_packet.iov_base;
+ mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
msg.msg_controllen = sizeof(control_u);
msg.msg_control = control_u.control;
msg.msg_flags = 0;
@@ -177,8 +175,12 @@
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
- iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
-
+#ifdef HAVE_SOLARIS_NETWORK
+ iface_index = *((unsigned int *)CMSG_DATA(cmptr));
+#else
+ iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
+#endif
+
if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
return;
@@ -198,7 +200,7 @@
iface_index = if_nametoindex(name->name);
}
#endif
-
+
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
{
@@ -237,7 +239,7 @@
if (!iface_enumerate(&parm, complete_context, NULL))
return;
lease_prune(NULL, now); /* lose any expired leases */
- iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, (size_t)sz,
+ iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
now, unicast_dest, &is_inform);
lease_update_file(now);
lease_update_dns();
@@ -253,7 +255,7 @@
iov.iov_base = daemon->dhcp_packet.iov_base;
/* packet buffer may have moved */
- mess = daemon->dhcp_packet.iov_base;
+ mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
#ifdef HAVE_SOCKADDR_SA_LEN
dest.sin_len = sizeof(struct sockaddr_in);
@@ -278,7 +280,7 @@
dest.sin_addr = mess->ciaddr;
}
}
-#ifdef HAVE_LINUX_NETWORK
+#if defined(HAVE_LINUX_NETWORK)
else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
{
@@ -287,14 +289,14 @@
msg.msg_control = control_u.control;
msg.msg_controllen = sizeof(control_u);
cmptr = CMSG_FIRSTHDR(&msg);
- dest.sin_addr.s_addr = INADDR_BROADCAST;
- dest.sin_port = htons(DHCP_CLIENT_PORT);
pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
pkt->ipi_ifindex = iface_index;
pkt->ipi_spec_dst.s_addr = 0;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP;
- cmptr->cmsg_type = IP_PKTINFO;
+ cmptr->cmsg_type = IP_PKTINFO;
+ dest.sin_addr.s_addr = INADDR_BROADCAST;
+ dest.sin_port = htons(DHCP_CLIENT_PORT);
}
else
{
@@ -310,7 +312,31 @@
req.arp_flags = ATF_COM;
ioctl(daemon->dhcpfd, SIOCSARP, &req);
}
-#else
+#elif defined(HAVE_SOLARIS_NETWORK)
+ else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
+ {
+ /* broadcast to 255.255.255.255 (or mac address invalid) */
+ dest.sin_addr.s_addr = INADDR_BROADCAST;
+ dest.sin_port = htons(DHCP_CLIENT_PORT);
+ /* note that we don't specify the interface here: that's done by the
+ IP_XMIT_IF sockopt lower down. */
+ }
+ else
+ {
+ /* unicast to unconfigured client. Inject mac address direct into ARP cache.
+ Note that this only works for ethernet on solaris, because we use SIOCSARP
+ and not SIOCSXARP, which would be perfect, except that it returns ENXIO
+ mysteriously. Bah. Fall back to broadcast for other net types. */
+ struct arpreq req;
+ dest.sin_addr = mess->yiaddr;
+ dest.sin_port = htons(DHCP_CLIENT_PORT);
+ *((struct sockaddr_in *)&req.arp_pa) = dest;
+ req.arp_ha.sa_family = AF_UNSPEC;
+ memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
+ req.arp_flags = ATF_COM;
+ ioctl(daemon->dhcpfd, SIOCSARP, &req);
+ }
+#elif defined(HAVE_BSD_NETWORK)
else
{
send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
@@ -318,6 +344,10 @@
}
#endif
+#ifdef HAVE_SOLARIS_NETWORK
+ setsockopt(daemon->dhcpfd, IPPROTO_IP, IP_XMIT_IF, &iface_index, sizeof(iface_index));
+#endif
+
while(sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send());
}
@@ -392,7 +422,9 @@
return 1;
}
-struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
+struct dhcp_context *address_available(struct dhcp_context *context,
+ struct in_addr taddr,
+ struct dhcp_netid *netids)
{
/* Check is an address is OK for this network, check all
possible ranges. Make sure that the address isn't in use
@@ -412,14 +444,17 @@
if (!(tmp->flags & CONTEXT_STATIC) &&
addr >= start &&
- addr <= end)
+ addr <= end &&
+ match_netid(tmp->filter, netids, 1))
return tmp;
}
return NULL;
}
-struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr)
+struct dhcp_context *narrow_context(struct dhcp_context *context,
+ struct in_addr taddr,
+ struct dhcp_netid *netids)
{
/* We start of with a set of possible contexts, all on the current physical interface.
These are chained on ->current.
@@ -431,19 +466,24 @@
struct dhcp_context *tmp;
- if ((tmp = address_available(context, taddr)))
- return tmp;
+ if (!(tmp = address_available(context, taddr, netids)))
+ {
+ for (tmp = context; tmp; tmp = tmp->current)
+ if (is_same_net(taddr, tmp->start, tmp->netmask) &&
+ (tmp->flags & CONTEXT_STATIC))
+ break;
+
+ if (!tmp)
+ for (tmp = context; tmp; tmp = tmp->current)
+ if (is_same_net(taddr, tmp->start, tmp->netmask))
+ break;
+ }
- for (tmp = context; tmp; tmp = tmp->current)
- if (is_same_net(taddr, tmp->start, tmp->netmask) &&
- (tmp->flags & CONTEXT_STATIC))
- return tmp;
-
- for (tmp = context; tmp; tmp = tmp->current)
- if (is_same_net(taddr, tmp->start, tmp->netmask))
- return tmp;
-
- return NULL;
+ /* Only one context allowed now */
+ if (tmp)
+ tmp->current = NULL;
+
+ return tmp;
}
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
@@ -458,12 +498,12 @@
}
/* Is every member of check matched by a member of pool?
- If negonly, match unless there's a negative tag which matches. */
-int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly)
+ If tagnotneeded, untagged is OK */
+int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
{
struct dhcp_netid *tmp1;
- if (!check && !negonly)
+ if (!check && !tagnotneeded)
return 0;
for (; check; check = check->next)
@@ -473,7 +513,7 @@
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0)
break;
- if (!tmp1 || negonly)
+ if (!tmp1)
return 0;
}
else
@@ -690,14 +730,14 @@
{
lineno++;
- while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
+ while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
buff[strlen(buff)-1] = 0;
if ((*buff == '#') || (*buff == '+'))
continue;
- for (ip = buff; *ip && !isspace(*ip); ip++);
- for(; *ip && isspace(*ip); ip++)
+ for (ip = buff; *ip && !isspace((int)*ip); ip++);
+ for(; *ip && isspace((int)*ip); ip++)
*ip = 0;
if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
{
@@ -785,59 +825,44 @@
my_syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
}
-void dhcp_read_hosts(void)
+void check_dhcp_hosts(int fatal)
{
- struct dhcp_config *configs, *cp, **up;
- int count;
-
- /* remove existing... */
- for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
- {
- cp = configs->next;
-
- if (configs->flags & CONFIG_BANK)
- {
- if (configs->flags & CONFIG_CLID)
- free(configs->clid);
- if (configs->flags & CONFIG_NETID)
- free(configs->netid.net);
- if (configs->flags & CONFIG_NAME)
- free(configs->hostname);
-
- *up = configs->next;
- free(configs);
- }
- else
- up = &configs->next;
- }
+ /* If the same IP appears in more than one host config, then DISCOVER
+ for one of the hosts will get the address, but REQUEST will be NAKed,
+ since the address is reserved by the other one -> protocol loop.
+ Also check that FQDNs match the domain we are using. */
- one_file(daemon->dhcp_hosts_file, 1, 1);
-
- for (count = 0, configs = daemon->dhcp_conf; configs; configs = configs->next)
+ struct dhcp_config *configs, *cp;
+
+ for (configs = daemon->dhcp_conf; configs; configs = configs->next)
{
- if (configs->flags & CONFIG_BANK)
- {
- char *domain;
- count++;
-
- for (cp = configs->next; cp; cp = cp->next)
- if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
- {
- my_syslog(LOG_ERR, _("duplicate IP address %s in %s."), inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
- configs->flags &= ~CONFIG_ADDR;
- }
-
- if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
- {
- my_syslog(LOG_ERR, _("illegal domain %s in %s."), domain, daemon->dhcp_hosts_file);
- free(configs->hostname);
- configs->flags &= ~CONFIG_NAME;
- }
- }
+ char *domain;
+
+ if ((configs->flags & DHOPT_BANK) || fatal)
+ {
+ for (cp = configs->next; cp; cp = cp->next)
+ if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
+ {
+ if (fatal)
+ die(_("duplicate IP address %s in dhcp-config directive."),
+ inet_ntoa(cp->addr), EC_BADCONF);
+ else
+ my_syslog(LOG_ERR, _("duplicate IP address %s in %s."),
+ inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
+ configs->flags &= ~CONFIG_ADDR;
+ }
+
+ if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
+ {
+ if (fatal)
+ die(_("illegal domain %s in dhcp-config directive."), domain, EC_BADCONF);
+ else
+ my_syslog(LOG_ERR, _("illegal domain %s in %s."), domain, daemon->dhcp_hosts_file);
+ free(configs->hostname);
+ configs->flags &= ~CONFIG_NAME;
+ }
+ }
}
-
- my_syslog(LOG_INFO, _("read %s - %d hosts"), daemon->dhcp_hosts_file, count);
-
}
void dhcp_update_configs(struct dhcp_config *configs)
@@ -856,21 +881,34 @@
if (config->flags & CONFIG_ADDR_HOSTS)
config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
- for (config = configs; config; config = config->next)
- if (!(config->flags & CONFIG_ADDR) &&
- (config->flags & CONFIG_NAME) &&
- (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
- (crec->flags & F_HOSTS))
- {
- if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
- my_syslog(LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
- inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
- else
- {
- config->addr = crec->addr.addr.addr.addr4;
- config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
- }
- }
+
+ if (daemon->port != 0)
+ for (config = configs; config; config = config->next)
+ if (!(config->flags & CONFIG_ADDR) &&
+ (config->flags & CONFIG_NAME) &&
+ (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
+ (crec->flags & F_HOSTS))
+ {
+ if (cache_find_by_name(crec, config->hostname, 0, F_IPV4))
+ {
+ /* use primary (first) address */
+ while (crec && !(crec->flags & F_REVERSE))
+ crec = cache_find_by_name(crec, config->hostname, 0, F_IPV4);
+ if (!crec)
+ continue; /* should be never */
+ my_syslog(LOG_WARNING, _("%s has more then one address in hostsfile, using %s for DHCP"),
+ config->hostname, inet_ntoa(crec->addr.addr.addr.addr4));
+ }
+
+ if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
+ my_syslog(LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
+ inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
+ else
+ {
+ config->addr = crec->addr.addr.addr.addr4;
+ config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
+ }
+ }
}
/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
@@ -878,9 +916,13 @@
it gets stripped. */
char *host_from_dns(struct in_addr addr)
{
- struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
+ struct crec *lookup;
char *hostname = NULL;
+
+ if (daemon->port == 0)
+ return NULL; /* DNS disabled. */
+ lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
if (lookup && (lookup->flags & F_HOSTS))
{
hostname = daemon->dhcp_buff;