import of dnsmasq-2.28.tar.gz

This commit is contained in:
Simon Kelley
2006-04-17 14:24:29 +01:00
parent cdeda28f82
commit 5e9e0efb01
31 changed files with 3191 additions and 2712 deletions

238
src/bpf.c Normal file
View File

@@ -0,0 +1,238 @@
/* dnsmasq is Copyright (c) 2000-2006 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.
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.
*/
#include "dnsmasq.h"
#ifndef HAVE_LINUX_NETWORK
#include <net/bpf.h>
static struct iovec ifconf = {
.iov_base = NULL,
.iov_len = 0
};
static struct iovec ifreq = {
.iov_base = NULL,
.iov_len = 0
};
struct header {
struct ether_header ether;
struct ip ip;
struct udphdr {
u16 uh_sport; /* source port */
u16 uh_dport; /* destination port */
u16 uh_ulen; /* udp length */
u16 uh_sum; /* udp checksum */
} udp;
};
void init_bpf(struct daemon *daemon)
{
int i = 0;
while (1)
{
/* useful size which happens to be sufficient */
if (expand_buf(&ifreq, sizeof(struct ifreq)))
{
sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
return;
}
if (errno != EBUSY)
die(_("cannot create DHCP BPF socket: %s"), NULL);
}
}
void send_via_bpf(struct daemon *daemon, struct dhcp_packet *mess, size_t len,
struct in_addr iface_addr, struct ifreq *ifr)
{
/* Hairy stuff, packet either has to go to the
net broadcast or the destination can't reply to ARP yet,
but we do know the physical address.
Build the packet by steam, and send directly, bypassing
the kernel IP stack */
struct header header;
u32 i, sum;
struct iovec iov[2];
/* Only know how to do ethernet on *BSD */
if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
{
syslog(LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
mess->htype, ifr->ifr_name);
return;
}
ifr->ifr_addr.sa_family = AF_LINK;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
return;
memcpy(header.ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
header.ether.ether_type = htons(ETHERTYPE_IP);
if (ntohs(mess->flags) & 0x8000)
{
memset(header.ether.ether_dhost, 255, ETHER_ADDR_LEN);
header.ip.ip_dst.s_addr = INADDR_BROADCAST;
}
else
{
memcpy(header.ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
header.ip.ip_dst.s_addr = mess->yiaddr.s_addr;
}
header.ip.ip_p = IPPROTO_UDP;
header.ip.ip_src.s_addr = iface_addr.s_addr;
header.ip.ip_len = htons(sizeof(struct ip) +
sizeof(struct udphdr) +
len) ;
header.ip.ip_hl = sizeof(struct ip) / 4;
header.ip.ip_v = IPVERSION;
header.ip.ip_tos = 0;
header.ip.ip_id = htons(0);
header.ip.ip_off = htons(0x4000); /* don't fragment */
header.ip.ip_ttl = IPDEFTTL;
header.ip.ip_sum = 0;
for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
sum += ((u16 *)&header.ip)[i];
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
header.ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
header.udp.uh_sport = htons(DHCP_SERVER_PORT);
header.udp.uh_dport = htons(DHCP_CLIENT_PORT);
if (len & 1)
((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
header.udp.uh_sum = 0;
header.udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
sum += htons(IPPROTO_UDP);
for (i = 0; i < 4; i++)
sum += ((u16 *)&header.ip.ip_src)[i];
for (i = 0; i < sizeof(struct udphdr)/2; i++)
sum += ((u16 *)&header.udp)[i];
for (i = 0; i < (len + 1) / 2; i++)
sum += ((u16 *)mess)[i];
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
header.udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
iov[0].iov_base = &header;
iov[0].iov_len = sizeof(struct header);
iov[1].iov_base = mess;
iov[1].iov_len = len;
while (writev(daemon->dhcp_raw_fd, iov, 2) == -1 && retry_send());
}
int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
{
char *ptr;
struct ifreq *ifr, ifaux;
struct ifconf ifc;
int fd, errsav, ret = 0;
int lastlen = 0;
size_t len;
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
return 0;
for (len = 0; ; len += 10*sizeof(struct ifreq))
{
if (!expand_buf(&ifconf, len))
goto err;
ifc.ifc_len = len;
ifc.ifc_buf = ifconf.iov_base;
if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
{
if (errno != EINVAL || lastlen != 0)
goto err;
}
else
{
if (ifc.ifc_len == lastlen)
break; /* got a big enough buffer now */
lastlen = ifc.ifc_len;
}
}
for (ptr = ifc.ifc_buf; ptr < ifc.ifc_buf + ifc.ifc_len; ptr += len )
{
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
unaligned accesses. */
#ifdef HAVE_SOCKADDR_SA_LEN
len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
#else
len = sizeof(struct ifreq);
#endif
if (!expand_buf(&ifreq, len))
goto err;
ifr = ifreq.iov_base;
memcpy(ifr, ptr, len);
strncpy(ifaux.ifr_name, ifr->ifr_name, IF_NAMESIZE);
if (ifr->ifr_addr.sa_family == AF_INET && ipv4_callback)
{
struct in_addr addr, netmask, broadcast;
if (ioctl(fd, SIOCGIFINDEX, &ifaux) == -1)
continue;
broadcast.s_addr = 0;
addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
continue;
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (!((*ipv4_callback)(daemon, addr, (int)ifaux.ifr_index, netmask, broadcast, parm)))
goto err;
}
#ifdef HAVE_IPV6
else if (ifr->ifr_addr.sa_family == AF_INET6 && ipv6_callback)
{
struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
/* voodoo to clear interface field in address */
if (!(daemon->options & OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
{
addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0;
}
if (ioctl(fd, SIOCGIFINDEX, &ifaux) == -1)
continue;
if (!((*ipv6_callback)(daemon, addr,
(int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
(int)ifaux.ifr_index,
parm)))
goto err;
}
#endif
}
ret = 1;
err:
errsav = errno;
close(fd);
errno = errsav;
return ret;
}
#endif

View File

@@ -18,6 +18,7 @@ static int cache_inserted, cache_live_freed, insert_error;
static union bigname *big_free;
static int bignames_left, log_queries, cache_size, hash_size;
static int uid;
static char *addrbuff;
static void cache_free(struct crec *crecp);
static void cache_unlink(struct crec *crecp);
@@ -29,7 +30,11 @@ void cache_init(int size, int logq)
struct crec *crecp;
int i;
log_queries = logq;
if ((log_queries = logq))
addrbuff = safe_malloc(ADDRSTRLEN);
else
addrbuff = NULL;
cache_head = cache_tail = NULL;
dhcp_inuse = dhcp_spare = NULL;
new_chain = NULL;
@@ -762,17 +767,17 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
void dump_cache(struct daemon *daemon)
void dump_cache(struct daemon *daemon, time_t now)
{
syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
daemon->cachesize, cache_live_freed, cache_inserted);
syslog(LOG_INFO, _("time %lu, cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
(unsigned long)now, daemon->cachesize, cache_live_freed, cache_inserted);
if (daemon->options & (OPT_DEBUG | OPT_LOG))
if ((daemon->options & (OPT_DEBUG | OPT_LOG)) &&
(addrbuff || (addrbuff = malloc(ADDRSTRLEN))))
{
struct crec *cache ;
char addrbuff[ADDRSTRLEN];
int i;
syslog(LOG_DEBUG, "Host Address Flags Expires\n");
syslog(LOG_DEBUG, "Host Address Flags Expires");
for (i=0; i<hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next)
@@ -797,7 +802,7 @@ void dump_cache(struct daemon *daemon)
#endif
syslog(LOG_DEBUG,
#ifdef HAVE_BROKEN_RTC
"%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %ld\n",
"%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %lu",
#else
"%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %s",
#endif
@@ -813,7 +818,7 @@ void dump_cache(struct daemon *daemon)
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ",
#ifdef HAVE_BROKEN_RTC
cache->flags & F_IMMORTAL ? 0: (unsigned long)cache->ttd
cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now)
#else
cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd))
#endif
@@ -844,8 +849,7 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
char *source;
char *verb = "is";
char types[20];
char addrbuff[ADDRSTRLEN];
if (!log_queries)
return;

View File

@@ -10,9 +10,7 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#define VERSION "2.27"
#define VERSION "2.28"
#define FTABSIZ 150 /* max number of outstanding requests */
#define MAX_PROCS 20 /* max no children for TCP requests */
@@ -22,8 +20,10 @@
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define LOGRATE 120 /* log table overflows every LOGRATE seconds */
#define CACHESIZ 150 /* default cache size */
#define MAXTOK 50 /* token in DHCP leases */
#define MAXLEASES 150 /* maximum number of DHCP leases */
#define PING_WAIT 3 /* wait for ping address-in-use test */
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
#define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
#define SMALLDNAME 40 /* most domain names are smaller than this */
#define HOSTSFILE "/etc/hosts"
#define ETHERSFILE "/etc/ethers"
@@ -46,8 +46,6 @@
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
#define CHGRP "dip"
#define IP6INTERFACES "/proc/net/if_inet6"
#define UPTIME "/proc/uptime"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
@@ -89,26 +87,25 @@
new system, you may want to edit these.
May replace this with Autoconf one day.
HAVE_LINUX_IPV6_PROC
define this to do IPv6 interface discovery using
proc/net/if_inet6 ala LINUX.
HAVE_LINUX_NETWORK
define this to do networking the Linux way. When it's defined, the code will
use IP_PKTINFO, Linux capabilities and the RTnetlink system. If it's not defined,
a few facilities will be lost, namely support for multiple addresses on an interface,
DNS query retransmission, and (on some systems) wildcard interface binding.
HAVE_BROKEN_RTC
define this on embeded systems which don't have an RTC
which keeps time over reboots. Causes dnsmasq to use uptime()
for timing, and keep relative time values in its leases file.
Also enables "Flash disk mode". Normally, dnsmasq tries very hard to
keep the on-disk leases file up-to-date: rewriting it after every change.
When HAVE_BROKEN_RTC is in effect, a different regime is used:
The leases file is written when dnsmasq terminates, when it receives
SIGALRM, when a brand new lease is allocated, or every n seconds,
where n is one third of the smallest time configured for leases
in a --dhcp-range or --dhcp-host option.
define this on embedded systems which don't have an RTC
which keeps time over reboots. Causes dnsmasq to use uptime
for timing, and keep lease lengths rather than expiry times
in its leases file. This also make dnsmasq "flash disk friendly".
Normally, dnsmasq tries very hard to keep the on-disk leases file
up-to-date: rewriting it after every renewal. When HAVE_BROKEN_RTC
is in effect, the lease file is only written when a new lease is
created, or an old one destroyed. (Because those are the only times
it changes.) This vastly reduces the number of file writes, and makes
it viable to keep the lease file on a flash filesystem.
NOTE: when enabling or disabling this, be sure to delete any old
leases file, otherwise dnsmasq may get very confused.
This configuration currently only works on Linux, but could be made to
work on other systems by teaching dnsmasq_time() in utils.c how to
read the system uptime.
HAVE_ISC_READER
define this to include the old ISC dhcpcd integration. Note that you cannot
@@ -137,18 +134,6 @@ HAVE_DEV_URANDOM
HAVE_SOCKADDR_SA_LEN
define this if struct sockaddr has sa_len field (*BSD)
HAVE_PSELECT
If your C library implements pselect, define this.
HAVE_BPF
If your OS implements Berkeley Packet filter, define this.
HAVE_RTNETLINK
If your OS has the Linux Routing netlink socket API and suitable
C library headers, define this. Note that the code will fall
back to the Berkley API at runtime if netlink support is not
configured into the kernel.
HAVE_DBUS
Define this if you want to link against libdbus, and have dnsmasq
define some methods to allow (re)configuration of the upstream DNS
@@ -156,12 +141,11 @@ HAVE_DBUS
NOTES:
For Linux you should define
HAVE_LINUX_IPV6_PROC
HAVE_LINUX_NETWORK
HAVE_GETOPT_LONG
HAVE_RANDOM
HAVE_DEV_RANDOM
HAVE_DEV_URANDOM
HAVE_RTNETLINK
you should NOT define
HAVE_ARC4RANDOM
HAVE_SOCKADDR_SA_LEN
@@ -169,10 +153,8 @@ NOTES:
For *BSD systems you should define
HAVE_SOCKADDR_SA_LEN
HAVE_RANDOM
HAVE_BPF
you should NOT define
HAVE_LINUX_IPV6_PROC
HAVE_RTNETLINK
HAVE_LINUX_NETWORK
and you MAY define
HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
HAVE_DEV_URANDOM - OpenBSD and FreeBSD and NetBSD
@@ -185,7 +167,7 @@ NOTES:
/* platform independent options. */
#undef HAVE_BROKEN_RTC
#define HAVE_ISC_READER
#undef HAVE_ISC_READER
#undef HAVE_DBUS
#if defined(HAVE_BROKEN_RTC) && defined(HAVE_ISC_READER)
@@ -196,76 +178,47 @@ NOTES:
/* Must preceed __linux__ since uClinux defines __linux__ too. */
#if defined(__uClinux__)
#define HAVE_LINUX_IPV6_PROC
#define HAVE_LINUX_NETWORK
#define HAVE_GETOPT_LONG
#define HAVE_RTNETLINK
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
/* Never use fork() on uClinux. Note that this is subtly different from the
--keep-in-foreground option, since it also suppresses forking new
processes for TCP connections. It's intended for use on MMU-less kernels. */
#define NO_FORK
#elif defined(__UCLIBC__)
#define HAVE_LINUX_IPV6_PROC
#define HAVE_LINUX_NETWORK
#if defined(__UCLIBC_HAS_GNU_GETOPT__) || \
((__UCLIBC_MAJOR__==0) && (__UCLIBC_MINOR__==9) && (__UCLIBC_SUBLEVEL__<21))
# define HAVE_GETOPT_LONG
# else
#else
# undef HAVE_GETOPT_LONG
# endif
#define HAVE_RTNETLINK
#endif
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#if !defined(__ARCH_HAS_MMU__)
#if !defined(__UCLIBC_HAS_MMU__)
# define NO_FORK
#endif
#if !defined(__UCLIBC_HAS_IPV6__)
# define NO_IPV6
#endif
/* libc5 - must precede __linux__ too */
/* Note to build a libc5 binary on a modern Debian system:
install the packages altgcc libc5 and libc5-altdev
then run "make CC=i486-linuxlibc1-gcc" */
/* Note that compling dnsmasq 2.x under libc5 and kernel 2.0.x
is probably doomed - no packet socket for starters. */
#elif defined(__linux__) && \
defined(_LINUX_C_LIB_VERSION_MAJOR) && \
(_LINUX_C_LIB_VERSION_MAJOR == 5 )
#undef HAVE_IPV6
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_RTNETLINK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
/* Fix various misfeatures of libc5 headers */
typedef unsigned long in_addr_t;
typedef size_t socklen_t;
/* This is for glibc 2.x */
#elif defined(__linux__)
#define HAVE_LINUX_IPV6_PROC
#define HAVE_RTNETLINK
#define HAVE_LINUX_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#define HAVE_PSELECT
/* glibc < 2.2 has broken Sockaddr_in6 so we have to use our own. */
/* glibc < 2.2 doesn't define in_addr_t */
#if defined(__GLIBC__) && (__GLIBC__ == 2) && \
@@ -275,8 +228,7 @@ typedef unsigned long in_addr_t;
#endif
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_RTNETLINK
#undef HAVE_LINUX_NETWORK
/* Later verions of FreeBSD have getopt_long() */
#if defined(optional_argument) && defined(required_argument)
# define HAVE_GETOPT_LONG
@@ -287,51 +239,29 @@ typedef unsigned long in_addr_t;
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#define HAVE_BPF
#elif defined(__APPLE__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_RTNETLINK
#undef HAVE_LINUX_NETWORK
#undef HAVE_GETOPT_LONG
#define HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#define HAVE_BPF
/* Define before sys/socket.h is included so we get socklen_t */
#define _BSD_SOCKLEN_T_
/* This is not defined in Mac OS X arpa/nameserv.h */
#define IN6ADDRSZ 16
#elif defined(__NetBSD__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_RTNETLINK
#undef HAVE_LINUX_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#define HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#define HAVE_BPF
/* env "LIBS=-lsocket -lnsl" make */
#elif defined(__sun) || defined(__sun__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_RTNETLINK
#undef HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#undef HAVE_DEV_URANDOM
#undef HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#define HAVE_BPF
#endif
/* Decide if we're going to support IPv6 */
/* We assume that systems which don't have IPv6
headers don't have ntop and pton either */
@@ -353,4 +283,3 @@ typedef unsigned long in_addr_t;
#endif

View File

@@ -113,13 +113,14 @@ static void dbus_read_servers(struct daemon *daemon, DBusMessage *message)
#else
if (i == sizeof(struct in6_addr)-1)
{
memcpy(&addr.in6.sin6_addr, p, sizeof(struct in6_addr));
memcpy(&addr.in6.sin6_addr, p, sizeof(addr.in6));
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(struct sockaddr_in6);
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(addr.in6);
#endif
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = htonl(0);
source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
source_addr.in6.sin6_scope_id = addr.in6.sin6_scope_id = 0;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
skip = 0;
@@ -211,14 +212,14 @@ static void dbus_read_servers(struct daemon *daemon, DBusMessage *message)
free(serv);
}
else
up = &serv->next;
up = &serv->next;
}
}
DBusHandlerResult message_handler (DBusConnection *connection,
DBusMessage *message,
void *user_data)
DBusHandlerResult message_handler(DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
char *method = (char *)dbus_message_get_member(message);
struct daemon *daemon = (struct daemon *)user_data;
@@ -239,7 +240,7 @@ DBusHandlerResult message_handler (DBusConnection *connection,
check_servers(daemon);
}
else if (strcmp(method, "ClearCache") == 0)
clear_cache_and_reload(daemon, dnsmasq_time(daemon->uptime_fd));
clear_cache_and_reload(daemon);
else
return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);

View File

@@ -10,15 +10,22 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#include "dnsmasq.h"
struct iface_param {
struct in_addr relay, primary;
struct dhcp_context *current;
int ind;
};
static int complete_context(struct daemon *daemon, struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
void dhcp_init(struct daemon *daemon)
{
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in saddr;
int flags, oneopt = 1, zeroopt = 0;
int flags, oneopt = 1;
struct dhcp_config *configs, *cp;
if (fd == -1)
@@ -26,7 +33,7 @@ void dhcp_init(struct daemon *daemon)
if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
#if defined(IP_PKTINFO)
#if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
#elif defined(IP_RECVIF)
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
@@ -53,41 +60,20 @@ void dhcp_init(struct daemon *daemon)
daemon->dhcpfd = fd;
if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1 ||
(flags = fcntl(fd, F_GETFL, 0)) == -1 ||
fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 ||
setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
die(_("cannot create ICMP raw socket: %s."), NULL);
daemon->dhcp_icmp_fd = fd;
#ifdef HAVE_BPF
{
int i = 0;
while (1)
{
char filename[50];
sprintf(filename, "/dev/bpf%d", i++);
if ((fd = open(filename, O_RDWR, 0)) != -1)
break;
if (errno != EBUSY)
die(_("cannot create DHCP BPF socket: %s"), NULL);
}
}
#else
/* since we don't ever use the packet socket for reception,
and it receives copies of _all_ IP packets, then that data
will build up in kernel buffers, wasting memory. Set the
socket receive buffer size to one to avoid that. (zero is
rejected as non-sensical by some BSD kernels) */
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1 ||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1)
die(_("cannot create DHCP packet socket: %s. "
"Is CONFIG_PACKET enabled in your kernel?"), NULL);
#endif
#ifndef HAVE_LINUX_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 */
daemon->dhcp_raw_fd = fd;
if (daemon->options & OPT_NO_PING)
daemon->dhcp_icmp_fd = -1;
else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
die(_("cannot create ICMP raw socket: %s."), NULL);
/* Make BPF raw send socket */
init_bpf(daemon);
#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,
@@ -97,58 +83,68 @@ void dhcp_init(struct daemon *daemon)
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));
daemon->dhcp_packet = safe_malloc(sizeof(struct udp_dhcp_packet));
/* These two each hold a DHCP option max size 255
daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
/* These two each hold a DHCP option max size 255
and get a terminating zero added */
daemon->dhcp_buff = safe_malloc(256);
daemon->dhcp_buff2 = safe_malloc(256);
daemon->ping_results = NULL;
}
void dhcp_packet(struct daemon *daemon, time_t now)
{
struct udp_dhcp_packet *rawpacket = daemon->dhcp_packet;
struct dhcp_packet *mess = &rawpacket->data;
struct dhcp_packet *mess;
struct dhcp_context *context;
struct iname *tmp;
struct ifreq ifr;
struct msghdr msg;
struct iovec iov[2];
struct sockaddr_in dest;
struct cmsghdr *cmptr;
ssize_t sz;
size_t newlen;
struct iovec iov;
ssize_t sz;
int iface_index = 0, unicast_dest = 0;
struct in_addr iface_addr;
#ifdef HAVE_BPF
unsigned char iface_hwaddr[ETHER_ADDR_LEN];
#endif
struct iface_param parm;
union {
struct cmsghdr align; /* this ensures alignment */
#ifdef IP_PKTINFO
#ifdef HAVE_LINUX_NETWORK
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#else
char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
iov[0].iov_base = (char *)mess;
iov[0].iov_len = sizeof(struct dhcp_packet);
msg.msg_control = control_u.control;
msg.msg_controllen = sizeof(control_u);
msg.msg_flags = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iov = &daemon->dhcp_packet;
msg.msg_iovlen = 1;
sz = recvmsg(daemon->dhcpfd, &msg, 0);
do
{
msg.msg_flags = 0;
while ((sz = recvmsg(daemon->dhcpfd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
}
while (sz != -1 && (msg.msg_flags & MSG_TRUNC) &&
expand_buf(&daemon->dhcp_packet, daemon->dhcp_packet.iov_len + 100));
if (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
/* expand_buf may have moved buffer */
mess = daemon->dhcp_packet.iov_base;
msg.msg_controllen = sizeof(control_u);
msg.msg_flags = 0;
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) && errno == EINTR);
if ((msg.msg_flags & MSG_TRUNC) ||
sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
return;
#if defined (IP_PKTINFO)
#if defined (HAVE_LINUX_NETWORK)
if (msg.msg_controllen < sizeof(struct cmsghdr))
return;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
@@ -178,225 +174,122 @@ void dhcp_packet(struct daemon *daemon, time_t now)
struct iname *name;
for (name = daemon->if_names; name->isloop; name = name->next);
strcpy(ifr.ifr_name, name->name);
iface_index = if_nametoindex(name->name);
}
#endif
#ifdef HAVE_BPF
ifr.ifr_addr.sa_family = AF_LINK;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0)
return;
memcpy(iface_hwaddr, LLADDR((struct sockaddr_dl *)&ifr.ifr_addr), ETHER_ADDR_LEN);
#endif
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0 )
return;
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
/* enforce available interface configuration */
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
if (daemon->if_names || daemon->if_addrs)
{
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
break;
if (!tmp)
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == iface_addr.s_addr)
break;
if (!tmp)
return;
}
if (!iface_check(daemon, AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name))
return;
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
context->current = context;
#ifdef HAVE_RTNETLINK
if (!netlink_process(daemon, iface_index, mess->giaddr, iface_addr, &context))
#endif
{
struct in_addr iface_netmask, iface_broadcast;
#ifdef HAVE_RTNETLINK
static int warned = 0;
if (!warned)
{
syslog(LOG_WARNING, _("Cannot use RTnetlink socket, falling back to ioctl API"));
warned = 1;
}
#endif
if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) < 0)
return;
iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) < 0)
return;
iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
context = complete_context(daemon, iface_addr, NULL, iface_netmask,
iface_broadcast, mess->giaddr, iface_addr);
}
parm.relay = mess->giaddr;
parm.primary = iface_addr;
parm.current = NULL;
parm.ind = iface_index;
if (!iface_enumerate(daemon, &parm, complete_context, NULL))
return;
lease_prune(NULL, now); /* lose any expired leases */
newlen = dhcp_reply(daemon, context, ifr.ifr_name, (size_t)sz, now, unicast_dest);
lease_update_file(daemon, 0, now);
iov.iov_len = dhcp_reply(daemon, parm.current, ifr.ifr_name, (size_t)sz, now, unicast_dest);
lease_update_file(daemon);
lease_update_dns(daemon);
if (newlen == 0)
if (iov.iov_len == 0)
return;
if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
{
/* To send to BOOTP relay or configured client, use the IP packet */
struct sockaddr_in dest;
dest.sin_family = AF_INET;
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_iov = &iov;
iov.iov_base = daemon->dhcp_packet.iov_base;
/* packet buffer may have moved */
mess = daemon->dhcp_packet.iov_base;
#ifdef HAVE_SOCKADDR_SA_LEN
dest.sin_len = sizeof(struct sockaddr_in);
dest.sin_len = sizeof(struct sockaddr_in);
#endif
if (mess->giaddr.s_addr)
{
dest.sin_port = htons(DHCP_SERVER_PORT);
dest.sin_addr = mess->giaddr;
}
else
{
dest.sin_port = htons(DHCP_CLIENT_PORT);
dest.sin_addr = mess->ciaddr;
}
while(sendto(daemon->dhcpfd, mess, newlen, 0,
(struct sockaddr *)&dest, sizeof(dest)) == -1 &&
retry_send());
if (mess->giaddr.s_addr)
{
/* Send to BOOTP relay */
if (!dest.sin_port)
dest.sin_port = htons(DHCP_SERVER_PORT);
dest.sin_addr = mess->giaddr;
}
else if (mess->ciaddr.s_addr)
{
dest.sin_addr = mess->ciaddr;
if (!dest.sin_port)
dest.sin_port = htons(DHCP_CLIENT_PORT);
}
#ifdef HAVE_LINUX_NETWORK
else if (ntohs(mess->flags) & 0x8000)
{
/* broadcast to 255.255.255.255 */
struct in_pktinfo *pkt;
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;
}
else
{
/* Hairy stuff, packet either has to go to the
net broadcast or the destination can't reply to ARP yet,
but we do know the physical address.
Build the packet by steam, and send directly, bypassing
the kernel IP stack */
u32 i, sum;
unsigned char hwdest[DHCP_CHADDR_MAX];
if (ntohs(mess->flags) & 0x8000)
{
memset(hwdest, 255, mess->hlen);
rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
}
else
{
memcpy(hwdest, mess->chaddr, mess->hlen);
rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
}
rawpacket->ip.ip_p = IPPROTO_UDP;
rawpacket->ip.ip_src.s_addr = iface_addr.s_addr;
rawpacket->ip.ip_len = htons(sizeof(struct ip) +
sizeof(struct udphdr) +
newlen) ;
rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
rawpacket->ip.ip_v = IPVERSION;
rawpacket->ip.ip_tos = 0;
rawpacket->ip.ip_id = htons(0);
rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
rawpacket->ip.ip_ttl = IPDEFTTL;
rawpacket->ip.ip_sum = 0;
for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
sum += ((u16 *)&rawpacket->ip)[i];
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
if (newlen & 1)
((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
rawpacket->udp.uh_sum = 0;
rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
sum += htons(IPPROTO_UDP);
for (i = 0; i < 4; i++)
sum += ((u16 *)&rawpacket->ip.ip_src)[i];
for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
sum += ((u16 *)&rawpacket->udp)[i];
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
{
#ifdef HAVE_BPF
struct ether_header header;
/* Only know how to do ethernet on *BSD */
if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
syslog(LOG_WARNING, _("DHCP request for unsupported hardware type (%d) recieved on %s"),
mess->htype, ifr.ifr_name);
else
{
header.ether_type = htons(ETHERTYPE_IP);
memcpy(header.ether_shost, iface_hwaddr, ETHER_ADDR_LEN);
memcpy(header.ether_dhost, hwdest, ETHER_ADDR_LEN);
ioctl(daemon->dhcp_raw_fd, BIOCSETIF, &ifr);
iov[0].iov_base = (char *)&header;
iov[0].iov_len = sizeof(struct ether_header);
iov[1].iov_base = (char *)rawpacket;
iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
while (writev(daemon->dhcp_raw_fd, iov, 2) == -1 && retry_send());
}
#else
/* Most definitions of this only include 8 bytes of address,
so we roll our own, since later kernels allow more. */
struct {
unsigned short int sll_family;
unsigned short int sll_protocol;
int sll_ifindex;
unsigned short int sll_hatype;
unsigned char sll_pkttype;
unsigned char sll_halen;
unsigned char sll_addr[DHCP_CHADDR_MAX];
} dest;
memset(&dest, 0, sizeof(dest));
dest.sll_family = AF_PACKET;
dest.sll_halen = mess->hlen;
dest.sll_ifindex = iface_index;
dest.sll_protocol = htons(ETHERTYPE_IP);
memcpy(dest.sll_addr, hwdest, mess->hlen);
while (sendto(daemon->dhcp_raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len),
0, (struct sockaddr *)&dest, sizeof(dest)) == -1 &&
retry_send());
#endif
}
/* unicast to unconfigured client */
dest.sin_addr = mess->yiaddr;
dest.sin_port = htons(DHCP_CLIENT_PORT);
if (mess->hlen != 0 && mess->htype != 0)
arp_inject(daemon->netlinkfd, mess->yiaddr, iface_index,
mess->chaddr, mess->hlen);
}
#else
else
{
send_via_bpf(daemon, mess, iov.iov_len, iface_addr, &ifr);
return;
}
#endif
while(sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send());
}
/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
of the interface on which a DHCP packet arrives (and any relay address) and does the
following things:
1) Fills in any netmask and broadcast addresses which have not been explicitly configured.
2) Fills in local (this host) and router (this host or relay) addresses.
3) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
of each interface (and any relay address) and does the following things:
1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
3) Fills in local (this host) and router (this host or relay) addresses.
4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr local, struct dhcp_context *current,
struct in_addr netmask, struct in_addr broadcast, struct in_addr relay,
struct in_addr primary)
static int complete_context(struct daemon *daemon, struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
struct dhcp_context *context;
struct iface_param *param = vparam;
if (if_index != param->ind)
return 1; /* no for us. */
for (context = daemon->dhcp; context; context = context->next)
{
@@ -426,8 +319,8 @@ struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr loca
{
context->router = local;
context->local = local;
context->current = current;
current = context;
context->current = param->current;
param->current = context;
}
if (!(context->flags & CONTEXT_BRDCAST))
@@ -438,10 +331,10 @@ struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr loca
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
}
else if (relay.s_addr && is_same_net(relay, context->start, context->netmask))
else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
{
context->router = relay;
context->local = primary;
context->router = param->relay;
context->local = param->primary;
/* fill in missing broadcast addresses for relayed ranges */
if (!(context->flags & CONTEXT_BRDCAST))
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
@@ -450,25 +343,31 @@ struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr loca
}
}
return current;
return 1;
}
struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
{
/* Check is an address is OK for this network, check all
possible ranges. */
possible ranges. Make sure that the address isn't in use
by the server itself. */
unsigned int start, end, addr = ntohl(taddr.s_addr);
for (; context; context = context->current)
{
start = ntohl(context->start.s_addr);
end = ntohl(context->end.s_addr);
struct dhcp_context *tmp;
if (!(context->flags & CONTEXT_STATIC) &&
for (tmp = context; tmp; tmp = tmp->current)
if (taddr.s_addr == context->local.s_addr)
return NULL;
for (tmp = context; tmp; tmp = tmp->current)
{
start = ntohl(tmp->start.s_addr);
end = ntohl(tmp->end.s_addr);
if (!(tmp->flags & CONTEXT_STATIC) &&
addr >= start &&
addr <= end)
return context;
return tmp;
}
return NULL;
@@ -547,11 +446,15 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
a particular hwaddr/clientid/hostname in our configuration.
Try to return from contexts which match netids first. */
struct in_addr start, addr ;
struct dhcp_context *c;
struct in_addr start, addr;
struct dhcp_context *c, *d;
int i, pass;
unsigned int j;
/* hash hwaddr */
for (j = 0, i = 0; i < hw_len; i++)
j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
if (c->flags & CONTEXT_STATIC)
@@ -561,35 +464,41 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
else
{
/* pick a seed based on hwaddr then iterate until we find a free address. */
for (j = c->addr_epoch, i = 0; i < hw_len; i++)
j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
start.s_addr = addr.s_addr =
htonl(ntohl(c->start.s_addr) +
(j % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
do {
if (!lease_find_by_addr(addr) &&
/* eliminate addresses in use by the server. */
for (d = context; d; d = d->current)
if (addr.s_addr == d->local.s_addr)
break;
if (!d &&
!lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr))
{
struct ping_result *r, *victim = NULL;
int count;
int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
((float)PING_WAIT)));
*addrp = addr;
if (daemon->options & OPT_NO_PING)
return 1;
/* check if we failed to ping addr sometime in the last
30s. If so, assume the same situation still exists.
PING_CACHE_TIME seconds. If so, assume the same situation still exists.
This avoids problems when a stupid client bangs
on us repeatedly. As a final check, is we did more
than six ping checks in the last 30s, we are in
high-load mode, so don't do any more. */
on us repeatedly. As a final check, if we did more
than 60% of the possible ping checks in the last
PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
for (count = 0, r = daemon->ping_results; r; r = r->next)
if (difftime(now, r->time) > 30.0)
if (difftime(now, r->time) > (float)PING_CACHE_TIME)
victim = r; /* old record */
else if (++count == 6 || r->addr.s_addr == addr.s_addr)
{
*addrp = addr;
return 1;
}
else if (++count == max || r->addr.s_addr == addr.s_addr)
return 1;
if (icmp_ping(daemon, addr))
/* address in use: perturb address selection so that we are
less likely to try this address again. */
@@ -613,7 +522,6 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
victim->addr = addr;
victim->time = now;
}
*addrp = addr;
return 1;
}
}
@@ -677,7 +585,6 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
is_addr_in_context(context, config))
return config;
if (hostname && context)
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_NAME) &&
@@ -690,17 +597,10 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
config->wildcard_mask != 0 &&
config->hwaddr_len == hw_len &&
(config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
is_addr_in_context(context, config))
{
int i;
unsigned int mask = config->wildcard_mask;
for (i = hw_len - 1; i >= 0; i--, mask = mask >> 1)
if (mask & 1)
config->hwaddr[i] = hwaddr[i];
if (memcmp(config->hwaddr, hwaddr, hw_len) == 0)
return config;
}
is_addr_in_context(context, config) &&
memcmp_masked(config->hwaddr, hwaddr, hw_len, config->wildcard_mask))
return config;
return NULL;
}
@@ -863,8 +763,8 @@ char *host_from_dns(struct daemon *daemon, struct in_addr addr)
if (lookup && (lookup->flags & F_HOSTS))
{
hostname = daemon->dhcp_buff;
hostname[256] = 0;
strncpy(hostname, cache_get_name(lookup), 256);
hostname[255] = 0;
hostname = strip_hostname(daemon, hostname);
}

View File

@@ -10,8 +10,6 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#include "dnsmasq.h"
static char *compile_opts =
@@ -26,9 +24,6 @@ static char *compile_opts =
#ifdef HAVE_BROKEN_RTC
"no-RTC "
#endif
#ifdef HAVE_RTNETLINK
"RTNetlink "
#endif
#ifndef HAVE_ISC_READER
"no-"
#endif
@@ -42,7 +37,8 @@ static char *compile_opts =
#endif
"I18N ";
static volatile int sigterm, sighup, sigusr1, sigalarm, num_kids, in_child;
static pid_t pid;
static int pipewrite;
static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd);
static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now);
@@ -51,12 +47,12 @@ static void sig_handler(int sig);
int main (int argc, char **argv)
{
struct daemon *daemon;
int first_loop = 1;
int bind_fallback = 0;
time_t now, last = 0;
struct sigaction sigact;
sigset_t sigmask;
struct iname *if_tmp;
int flags, piperead, pipefd[2];
unsigned char sig;
#ifndef NO_GETTEXT
setlocale(LC_ALL, "");
@@ -64,16 +60,7 @@ int main (int argc, char **argv)
textdomain("dnsmasq");
#endif
sighup = 1; /* init cache the first time through */
sigusr1 = 0; /* but don't dump */
sigterm = 0; /* or die */
#ifdef HAVE_BROKEN_RTC
sigalarm = 1; /* need regular lease dumps */
#else
sigalarm = 0; /* or not */
#endif
num_kids = 0;
in_child = 0;
pid = 0;
sigact.sa_handler = sig_handler;
sigact.sa_flags = 0;
@@ -88,15 +75,6 @@ int main (int argc, char **argv)
sigact.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sigact, NULL);
/* now block all the signals, they stay that way except
during the call to pselect */
sigaddset(&sigact.sa_mask, SIGUSR1);
sigaddset(&sigact.sa_mask, SIGTERM);
sigaddset(&sigact.sa_mask, SIGHUP);
sigaddset(&sigact.sa_mask, SIGALRM);
sigaddset(&sigact.sa_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &sigact.sa_mask, &sigmask);
daemon = read_opts(argc, argv, compile_opts);
if (daemon->edns_pktsz < PACKETSZ)
@@ -115,7 +93,12 @@ int main (int argc, char **argv)
die(_("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h"), NULL);
#endif
if (!enumerate_interfaces(daemon, &daemon->interfaces, NULL, NULL))
#ifdef HAVE_LINUX_NETWORK
netlink_init(daemon);
#endif
daemon->interfaces = NULL;
if (!enumerate_interfaces(daemon))
die(_("failed to find list of interfaces: %s"), NULL);
if (!(daemon->options & OPT_NOWILD) &&
@@ -127,7 +110,7 @@ int main (int argc, char **argv)
if (daemon->options & OPT_NOWILD)
{
daemon->listeners = create_bound_listeners(daemon->interfaces, daemon->port);
daemon->listeners = create_bound_listeners(daemon);
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
@@ -144,16 +127,11 @@ int main (int argc, char **argv)
forward_init(1);
cache_init(daemon->cachesize, daemon->options & OPT_LOG);
#ifdef HAVE_BROKEN_RTC
if ((daemon->uptime_fd = open(UPTIME, O_RDONLY)) == -1)
die(_("cannot open %s:%s"), UPTIME);
#endif
now = dnsmasq_time(daemon->uptime_fd);
now = dnsmasq_time();
if (daemon->dhcp)
{
#if !defined(IP_PKTINFO) && !defined(IP_RECVIF)
#if !defined(HAVE_LINUX_NETWORK) && !defined(IP_RECVIF)
int c;
struct iname *tmp;
for (c = 0, tmp = daemon->if_names; tmp; tmp = tmp->next)
@@ -196,7 +174,8 @@ int main (int argc, char **argv)
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = htons(daemon->query_port);
addr.in6.sin6_flowinfo = htonl(0);
addr.in6.sin6_flowinfo = 0;
addr.in6.sin6_scope_id = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif
@@ -204,15 +183,49 @@ int main (int argc, char **argv)
#endif
}
setbuf(stdout, NULL);
if (!(daemon->options & OPT_DEBUG))
/* Use a pipe to carry signals back to the event loop in a race-free manner */
if (pipe(pipefd) == -1 ||
(flags = fcntl(pipefd[0], F_GETFL)) == -1 ||
fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK) == -1 ||
(flags = fcntl(pipefd[1], F_GETFL)) == -1 ||
fcntl(pipefd[1], F_SETFL, flags | O_NONBLOCK) == -1)
die(_("cannot create pipe: %s"), NULL);
piperead = pipefd[0];
pipewrite = pipefd[1];
/* prime the pipe to load stuff first time. */
sig = SIGHUP;
write(pipewrite, &sig, 1);
if (!(daemon->options & OPT_DEBUG))
{
FILE *pidfile;
struct passwd *ent_pw;
struct passwd *ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
fd_set test_set;
int maxfd, i;
int maxfd, i;
#ifdef HAVE_LINUX_NETWORK
cap_user_header_t hdr = NULL;
cap_user_data_t data = NULL;
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp */
if (ent_pw)
{
hdr = safe_malloc(sizeof(*hdr));
data = safe_malloc(sizeof(*data));
hdr->version = _LINUX_CAPABILITY_VERSION;
hdr->pid = 0; /* this process */
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
(1 << CAP_SETGID) | (1 << CAP_SETUID);
/* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
die(_("Cannot set capabilities: %s"), NULL);
}
#endif
FD_ZERO(&test_set);
maxfd = set_dns_listeners(daemon, &test_set, -1);
#ifdef HAVE_DBUS
@@ -249,16 +262,21 @@ int main (int argc, char **argv)
for (i=0; i<64; i++)
{
#ifdef HAVE_BROKEN_RTC
if (i == daemon->uptime_fd)
if (i == piperead || i == pipewrite)
continue;
#ifdef HAVE_LINUX_NETWORK
if (i == daemon->netlinkfd)
continue;
#endif
if (daemon->dhcp &&
(i == fileno(daemon->lease_stream) ||
i == daemon->dhcpfd ||
#ifndef HAVE_LINUX_NETWORK
i == daemon->dhcp_raw_fd ||
i == daemon->dhcp_icmp_fd))
i == daemon->dhcp_icmp_fd ||
#endif
i == daemon->dhcpfd))
continue;
if (i <= maxfd && FD_ISSET(i, &test_set))
@@ -268,10 +286,11 @@ int main (int argc, char **argv)
}
/* Change uid and gid for security */
if (daemon->username && (ent_pw = getpwnam(daemon->username)))
if (ent_pw)
{
gid_t dummy;
struct group *gp;
/* remove all supplimentary groups */
setgroups(0, &dummy);
/* change group for /etc/ppp/resolv.conf
@@ -281,8 +300,21 @@ int main (int argc, char **argv)
setgid(gp->gr_gid);
/* finally drop root */
setuid(ent_pw->pw_uid);
#ifdef HAVE_LINUX_NETWORK
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
data->inheritable = 0;
/* lose the setuid and setgid capbilities */
capset(hdr, data);
#endif
}
}
#ifdef HAVE_LINUX_NETWORK
else
prctl(PR_SET_DUMPABLE, 1);
#endif
openlog("dnsmasq",
DNSMASQ_LOG_OPT(daemon->options & OPT_DEBUG),
@@ -312,12 +344,7 @@ int main (int argc, char **argv)
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name);
#ifdef HAVE_RTNETLINK
/* Must do this after daemonizing so that the pid is right */
netlink_init(daemon);
#endif
if (daemon->dhcp)
{
struct dhcp_context *dhcp_tmp;
@@ -332,16 +359,6 @@ int main (int argc, char **argv)
_("DHCP, IP range %s -- %s, lease time %s"),
daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
}
#ifdef HAVE_BROKEN_RTC
daemon->min_leasetime = daemon->min_leasetime/3;
if (daemon->min_leasetime > (60 * 60 * 24))
daemon->min_leasetime = 60 * 60 * 24;
if (daemon->min_leasetime < 60)
daemon->min_leasetime = 60;
prettyprint_time(daemon->dhcp_buff2, daemon->min_leasetime);
syslog(LOG_INFO, _("DHCP, %s will be written every %s"), daemon->lease_file, daemon->dhcp_buff2);
#endif
}
if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
@@ -349,111 +366,58 @@ int main (int argc, char **argv)
check_servers(daemon);
while (sigterm == 0)
pid = getpid();
while (1)
{
int maxfd;
struct timeval t, *tp = NULL;
fd_set rset, wset, eset;
if (sighup)
{
clear_cache_and_reload(daemon, now);
if (daemon->resolv_files && (daemon->options & OPT_NO_POLL))
{
reload_servers(daemon->resolv_files->name, daemon);
check_servers(daemon);
}
sighup = 0;
}
if (sigusr1)
{
dump_cache(daemon);
sigusr1 = 0;
}
if (sigalarm)
{
if (daemon->dhcp)
{
lease_update_file(daemon, 1, now);
#ifdef HAVE_BROKEN_RTC
alarm(daemon->min_leasetime);
#endif
}
sigalarm = 0;
}
t.tv_sec = 0; /* no warning */
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
if (!first_loop)
maxfd = set_dns_listeners(daemon, &rset, -1);
#ifdef HAVE_DBUS
/* Whilst polling for the dbus, wake every quarter second */
if ((daemon->options & OPT_DBUS) && !daemon->dbus)
{
int maxfd = set_dns_listeners(daemon, &rset, -1);
#ifdef HAVE_DBUS
maxfd = set_dbus_listeners(daemon, maxfd, &rset, &wset, &eset);
#endif
if (daemon->dhcp)
{
FD_SET(daemon->dhcpfd, &rset);
if (daemon->dhcpfd > maxfd)
maxfd = daemon->dhcpfd;
}
#ifdef HAVE_RTNETLINK
if (daemon->netlinkfd != -1)
{
FD_SET(daemon->netlinkfd, &rset);
if (daemon->netlinkfd > maxfd)
maxfd = daemon->netlinkfd;
}
#endif
/* Whilst polling for the dbus, wake every quarter second */
#ifdef HAVE_PSELECT
{
struct timespec *tp = NULL;
#ifdef HAVE_DBUS
struct timespec t;
if ((daemon->options & OPT_DBUS) && !daemon->dbus)
{
tp = &t;
tp->tv_sec = 0;
tp->tv_nsec = 250000000;
}
#endif
if (pselect(maxfd+1, &rset, &wset, &eset, tp, &sigmask) < 0)
{
/* otherwise undefined after error */
FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
}
}
#else
{
sigset_t save_mask;
struct timeval *tp = NULL;
#ifdef HAVE_DBUS
struct timeval t;
if ((daemon->options & OPT_DBUS) && !daemon->dbus)
{
tp = &t;
tp->tv_sec = 0;
tp->tv_usec = 250000;
}
#endif
sigprocmask(SIG_SETMASK, &sigmask, &save_mask);
if (select(maxfd+1, &rset, &wset, &eset, tp) < 0)
{
/* otherwise undefined after error */
FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
}
sigprocmask(SIG_SETMASK, &save_mask, NULL);
}
#endif
tp = &t;
tp->tv_sec = 0;
tp->tv_usec = 250000;
}
maxfd = set_dbus_listeners(daemon, maxfd, &rset, &wset, &eset);
#endif
if (daemon->dhcp)
{
FD_SET(daemon->dhcpfd, &rset);
if (daemon->dhcpfd > maxfd)
maxfd = daemon->dhcpfd;
}
#ifdef HAVE_LINUX_NETWORK
FD_SET(daemon->netlinkfd, &rset);
if (daemon->netlinkfd > maxfd)
maxfd = daemon->netlinkfd;
#endif
first_loop = 0;
now = dnsmasq_time(daemon->uptime_fd);
FD_SET(piperead, &rset);
if (piperead > maxfd)
maxfd = piperead;
if (select(maxfd+1, &rset, &wset, &eset, tp) < 0)
{
/* otherwise undefined after error */
FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
}
now = dnsmasq_time();
/* Check for changes to resolv files once per second max. */
/* Don't go silent for long periods if the clock goes backwards. */
@@ -506,8 +470,46 @@ int main (int argc, char **argv)
}
}
#ifdef HAVE_RTNETLINK
if (daemon->netlinkfd != -1 && FD_ISSET(daemon->netlinkfd, &rset))
if (FD_ISSET(piperead, &rset))
{
if (read(piperead, &sig, 1) == 1)
switch (sig)
{
case SIGHUP:
clear_cache_and_reload(daemon);
if (daemon->resolv_files && (daemon->options & OPT_NO_POLL))
{
reload_servers(daemon->resolv_files->name, daemon);
check_servers(daemon);
}
break;
case SIGUSR1:
dump_cache(daemon, now);
break;
case SIGALRM:
if (daemon->dhcp)
lease_update_file(daemon);
break;
case SIGTERM:
syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
if (daemon->dhcp)
fclose(daemon->lease_stream);
exit(0);
case SIGCHLD:
/* See Stevens 5.10 */
while (waitpid(-1, NULL, WNOHANG) > 0)
daemon->num_kids--;
break;
}
}
#ifdef HAVE_LINUX_NETWORK
if (FD_ISSET(daemon->netlinkfd, &rset))
netlink_multicast(daemon);
#endif
@@ -529,47 +531,34 @@ int main (int argc, char **argv)
if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
dhcp_packet(daemon, now);
}
syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
if (daemon->dhcp)
{
#ifdef HAVE_BROKEN_RTC
lease_update_file(daemon, 1, now);
#endif
fclose(daemon->lease_stream);
}
return 0;
}
static void sig_handler(int sig)
{
if (sig == SIGTERM)
sigterm = 1;
else if (sig == SIGHUP)
sighup = 1;
else if (sig == SIGUSR1)
sigusr1 = 1;
else if (sig == SIGALRM)
if (pid == 0)
{
/* ignore anything other than TERM during startup */
if (sig == SIGTERM)
exit(0);
}
else if (pid == getpid())
{
/* master process */
unsigned char sigchr = sig;
int errsave = errno;
write(pipewrite, &sigchr, 1);
errno = errsave;
}
else
{
/* alarm is used to kill children after a fixed time. */
if (in_child)
if (sig == SIGALRM)
exit(0);
else
sigalarm = 1;
}
else if (sig == SIGCHLD)
{
/* See Stevens 5.10 */
while (waitpid(-1, NULL, WNOHANG) > 0)
num_kids--;
}
}
void clear_cache_and_reload(struct daemon *daemon, time_t now)
void clear_cache_and_reload(struct daemon *daemon)
{
cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
if (daemon->dhcp)
@@ -578,7 +567,7 @@ void clear_cache_and_reload(struct daemon *daemon, time_t now)
dhcp_read_ethers(daemon);
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs(daemon);
lease_update_file(daemon, 0, now);
lease_update_file(daemon);
lease_update_dns(daemon);
}
}
@@ -625,30 +614,39 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
if (FD_ISSET(listener->tcpfd, set))
{
int confd;
struct in_addr netmask, dst_addr_4;
while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
if (confd != -1)
{
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
/* Check for allowed interfaces when binding the wildcard address:
we do this by looking for an interface with the same address as
the local address of the TCP connection, then looking to see if that's
an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */
if ((num_kids >= MAX_PROCS) ||
(!(daemon->options & OPT_NOWILD) &&
(getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1 ||
!enumerate_interfaces(daemon, NULL, &tcp_addr, &netmask))))
struct irec *iface = NULL;
if (daemon->options & OPT_NOWILD)
iface = listener->iface;
else
{
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
/* Check for allowed interfaces when binding the wildcard address:
we do this by looking for an interface with the same address as
the local address of the TCP connection, then looking to see if that's
an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */
/* interface may be new since startup */
if (enumerate_interfaces(daemon) &&
getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, &tcp_addr))
break;
}
if ((daemon->num_kids >= MAX_PROCS) || !iface)
close(confd);
#ifndef NO_FORK
else if (!(daemon->options & OPT_DEBUG) && fork())
{
num_kids++;
daemon->num_kids++;
close(confd);
}
#endif
@@ -657,18 +655,14 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
unsigned char *buff;
struct server *s;
int flags;
struct in_addr dst_addr_4;
dst_addr_4.s_addr = 0;
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
terminate the process. */
if (!(daemon->options & OPT_DEBUG))
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
alarm(CHILD_LIFETIME);
in_child = 1;
}
alarm(CHILD_LIFETIME);
/* start with no upstream connections. */
for (s = daemon->servers; s; s = s->next)
@@ -681,20 +675,9 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
if (listener->family == AF_INET)
{
if (daemon->options & OPT_NOWILD)
{
netmask = listener->iface->netmask;
dst_addr_4 = listener->iface->addr.in.sin_addr;
}
else
/* netmask already set by enumerate_interfaces */
dst_addr_4 = tcp_addr.in.sin_addr;
}
else
dst_addr_4.s_addr = 0;
dst_addr_4 = iface->addr.in.sin_addr;
buff = tcp_request(daemon, confd, now, dst_addr_4, netmask);
buff = tcp_request(daemon, confd, now, dst_addr_4, iface->netmask);
if (!(daemon->options & OPT_DEBUG))
exit(0);
@@ -711,21 +694,35 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
}
}
int make_icmp_sock(void)
{
int fd, flags;
int zeroopt = 0;
if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1)
{
if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
{
close(fd);
fd = -1;
}
}
return fd;
}
int icmp_ping(struct daemon *daemon, struct in_addr addr)
{
/* Try and get an ICMP echo from a machine.
Note that we can't create the raw socket each time
we do this, since that needs root. Therefore the socket has to hang
around all the time. Since most of the time we won't read the
socket, it will accumulate buffers full of ICMP messages,
wasting memory. To avoid that we set the receive buffer
length to zero except when we're actively pinging. */
/* Try and get an ICMP echo from a machine. */
/* Note that whilst in the three second wait, we check for
(and service) events on the DNS sockets, (so doing that
better not use any resources our caller has in use...)
but we remain deaf to signals or further DHCP packets. */
int fd;
struct sockaddr_in saddr;
struct {
struct ip ip;
@@ -733,9 +730,18 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
} packet;
unsigned short id = rand16();
unsigned int i, j;
int opt = 2000, gotreply = 0;
int gotreply = 0;
time_t start, now;
#ifdef HAVE_LINUX_NETWORK
if ((fd = make_icmp_sock()) == -1)
return 0;
#else
int opt = 2000;
fd = daemon->dhcp_icmp_fd;
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
#endif
saddr.sin_family = AF_INET;
saddr.sin_port = 0;
saddr.sin_addr = addr;
@@ -752,13 +758,12 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
j = (j & 0xffff) + (j >> 16);
packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
while (sendto(daemon->dhcp_icmp_fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
while (sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0,
(struct sockaddr *)&saddr, sizeof(saddr)) == -1 &&
retry_send());
for (now = start = dnsmasq_time(daemon->uptime_fd); difftime(now, start) < 3.0;)
for (now = start = dnsmasq_time();
difftime(now, start) < (float)PING_WAIT;)
{
struct timeval tv;
fd_set rset;
@@ -770,17 +775,17 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
tv.tv_sec = 0;
FD_ZERO(&rset);
FD_SET(daemon->dhcp_icmp_fd, &rset);
maxfd = set_dns_listeners(daemon, &rset, daemon->dhcp_icmp_fd);
FD_SET(fd, &rset);
maxfd = set_dns_listeners(daemon, &rset, fd);
if (select(maxfd+1, &rset, NULL, NULL, &tv) < 0)
FD_ZERO(&rset);
now = dnsmasq_time(daemon->uptime_fd);
now = dnsmasq_time();
check_dns_listeners(daemon, &rset, now);
if (FD_ISSET(daemon->dhcp_icmp_fd, &rset) &&
recvfrom(daemon->dhcp_icmp_fd, &packet, sizeof(packet), 0,
if (FD_ISSET(fd, &rset) &&
recvfrom(fd, &packet, sizeof(packet), 0,
(struct sockaddr *)&faddr, &len) == sizeof(packet) &&
saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
packet.icmp.icmp_type == ICMP_ECHOREPLY &&
@@ -792,9 +797,13 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
}
}
#ifdef HAVE_LINUX_NETWORK
close(fd);
#else
opt = 1;
setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
#endif
return gotreply;
}

View File

@@ -10,17 +10,8 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#define COPYRIGHT "Copyright (C) 2000-2006 Simon Kelley"
#ifdef __linux__
/* for pselect.... */
# define _XOPEN_SOURCE 600
/* but then DNS headers don't compile without.... */
#define _BSD_SOURCE
#endif
/* get these before config.h for IPv6 stuff... */
#include <sys/types.h>
#include <netinet/in.h>
@@ -53,9 +44,6 @@
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/wait.h>
#if defined(__sun) || defined(__sun__)
# include <sys/sockio.h>
#endif
#include <sys/time.h>
#include <limits.h>
#include <net/if.h>
@@ -80,13 +68,18 @@
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#ifdef HAVE_BPF
# include <net/bpf.h>
# include <net/if_dl.h>
#else
# include <netpacket/packet.h>
#endif
#include <sys/uio.h>
#ifndef HAVE_LINUX_NETWORK
# include <net/if_dl.h>
#endif
#ifdef HAVE_LINUX_NETWORK
#include <linux/capability.h>
/* There doesn't seem to be a universally-available
userpace header for this. */
extern int capset(cap_user_header_t header, cap_user_data_t data);
#include <sys/prctl.h>
#endif
/* Min buffer size: we check after adding each record, so there must be
memory for the largest packet, and the largest record so the
@@ -116,6 +109,7 @@
#define OPT_LOCALISE 262144
#define OPT_DBUS 524288
#define OPT_BOOTP_DYNAMIC 1048576
#define OPT_NO_PING 2097152
struct all_addr {
union {
@@ -296,6 +290,9 @@ struct dhcp_lease {
char *hostname, *fqdn; /* name from client-hostname option or config */
int auth_name; /* hostname came from config, not from client */
time_t expires; /* lease expiry */
#ifdef HAVE_BROKEN_RTC
unsigned int length;
#endif
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct in_addr addr;
@@ -385,22 +382,13 @@ typedef unsigned short u16;
typedef unsigned int u32;
struct udp_dhcp_packet {
struct ip ip;
struct udphdr {
u16 uh_sport; /* source port */
u16 uh_dport; /* destination port */
u16 uh_ulen; /* udp length */
u16 uh_sum; /* udp checksum */
} udp;
struct dhcp_packet {
u8 op, htype, hlen, hops;
u32 xid;
u16 secs, flags;
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
u8 options[312];
} data;
struct dhcp_packet {
u8 op, htype, hlen, hops;
u32 xid;
u16 secs, flags;
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
u8 options[312];
};
struct ping_result {
@@ -452,14 +440,16 @@ struct daemon {
struct server *last_server;
struct server *srv_save; /* Used for resend on DoD */
size_t packet_len; /* " " */
int uptime_fd;
int num_kids;
/* DHCP state */
int dhcpfd, dhcp_raw_fd, dhcp_icmp_fd;
#ifdef HAVE_RTNETLINK
int dhcpfd;
#ifdef HAVE_LINUX_NETWORK
int netlinkfd;
#else
int dhcp_raw_fd, dhcp_icmp_fd;
#endif
struct udp_dhcp_packet *dhcp_packet;
struct iovec dhcp_packet;
char *dhcp_buff, *dhcp_buff2;
struct ping_result *ping_results;
FILE *lease_stream;
@@ -489,7 +479,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts);
void cache_add_dhcp_entry(struct daemon *daemon, char *host_name, struct in_addr *host_address, time_t ttd);
void cache_unhash_dhcp(void);
void dump_cache(struct daemon *daemon);
void dump_cache(struct daemon *daemon, time_t now);
char *cache_get_name(struct crec *crecp);
/* rfc1035.c */
@@ -522,14 +512,16 @@ void *safe_malloc(size_t size);
int sa_len(union mysockaddr *addr);
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
int hostname_isequal(char *a, char *b);
time_t dnsmasq_time(int fd);
time_t dnsmasq_time(void);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
int retry_send(void);
void prettyprint_time(char *buf, unsigned int t);
int prettyprint_addr(union mysockaddr *addr, char *buf);
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type);
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask);
int expand_buf(struct iovec *iov, size_t size);
/* option.c */
struct daemon *read_opts (int argc, char **argv, char *compile_opts);
@@ -544,10 +536,11 @@ unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds);
void reload_servers(char *fname, struct daemon *daemon);
void check_servers(struct daemon *daemon);
int enumerate_interfaces(struct daemon *daemon, struct irec **chainp,
union mysockaddr *test_addrp, struct in_addr *netmaskp);
int enumerate_interfaces(struct daemon *daemon);
struct listener *create_wildcard_listeners(int port);
struct listener *create_bound_listeners(struct irec *interfaces, int port);
struct listener *create_bound_listeners(struct daemon *daemon);
int iface_check(struct daemon *daemon, int family,
struct all_addr *addr, char *name);
/* dhcp.c */
void dhcp_init(struct daemon *daemon);
@@ -569,13 +562,9 @@ void dhcp_read_ethers(struct daemon *daemon);
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
char *strip_hostname(struct daemon *daemon, char *hostname);
char *host_from_dns(struct daemon *daemon, struct in_addr addr);
struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr local,
struct dhcp_context *current, struct in_addr netmask,
struct in_addr broadcast, struct in_addr relay,
struct in_addr primary);
/* lease.c */
void lease_update_file(struct daemon *daemon, int force, time_t now);
void lease_update_file(struct daemon *daemon);
void lease_update_dns(struct daemon *daemon);
void lease_init(struct daemon *daemon, time_t now);
struct dhcp_lease *lease_allocate(unsigned char *hwaddr, unsigned char *clid,
@@ -584,7 +573,7 @@ int lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len);
void lease_set_hostname(struct dhcp_lease *lease, char *name,
char *suffix, int auth);
void lease_set_expires(struct dhcp_lease *lease, time_t exp);
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
@@ -595,8 +584,9 @@ void lease_update_from_configs(struct daemon *daemon);
size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name, size_t sz, time_t now, int unicast_dest);
/* dnsmasq.c */
int make_icmp_sock(void);
int icmp_ping(struct daemon *daemon, struct in_addr addr);
void clear_cache_and_reload(struct daemon *daemon, time_t now);
void clear_cache_and_reload(struct daemon *daemon);
/* isc.c */
#ifdef HAVE_ISC_READER
@@ -604,12 +594,22 @@ void load_dhcp(struct daemon *daemon, time_t now);
#endif
/* netlink.c */
#ifdef HAVE_RTNETLINK
#ifdef HAVE_LINUX_NETWORK
void netlink_init(struct daemon *daemon);
int netlink_process(struct daemon *daemon, int index,
struct in_addr relay, struct in_addr primary,
struct dhcp_context **retp);
int iface_enumerate(struct daemon *daemon, void *parm,
int (*ipv4_callback)(), int (*ipv6_callback)());
void netlink_multicast(struct daemon *daemon);
void arp_inject(int fd, struct in_addr addr,
int iface, unsigned char *mac, unsigned int mac_len);
#endif
/* bpf.c */
#ifndef HAVE_LINUX_NETWORK
void init_bpf(struct daemon *daemon);
void send_via_bpf(struct daemon *daemon, struct dhcp_packet *mess, size_t len,
struct in_addr iface_addr, struct ifreq *ifr);
int iface_enumerate(struct daemon *daemon, void *parm,
int (*ipv4_callback)(), int (*ipv6_callback)());
#endif
/* dbus.c */

View File

@@ -10,8 +10,6 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#include "dnsmasq.h"
static struct frec *frec_list;
@@ -44,7 +42,7 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
struct iovec iov[1];
union {
struct cmsghdr align; /* this ensures alignment */
#if defined(IP_PKTINFO)
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(IP_SENDSRCADDR)
char control[CMSG_SPACE(sizeof(struct in_addr))];
@@ -74,7 +72,7 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
if (to->sa.sa_family == AF_INET)
{
#if defined(IP_PKTINFO)
#if defined(HAVE_LINUX_NETWORK)
struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
pkt->ipi_ifindex = 0;
pkt->ipi_spec_dst = source->addr.addr4;
@@ -352,7 +350,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
}
static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
unsigned int query_crc, struct server *server, size_t n)
unsigned int query_crc, struct server *server, size_t n)
{
unsigned char *pheader, *sizep;
int munged = 0;
@@ -448,7 +446,7 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
#ifdef HAVE_IPV6
if (serveraddr.sa.sa_family == AF_INET6)
serveraddr.in6.sin6_flowinfo = htonl(0);
serveraddr.in6.sin6_flowinfo = 0;
#endif
header = (HEADER *)daemon->packet;
@@ -522,7 +520,6 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
HEADER *header = (HEADER *)daemon->packet;
union mysockaddr source_addr;
unsigned short type;
struct iname *tmp;
struct all_addr dst_addr;
struct in_addr netmask, dst_addr_4;
size_t m;
@@ -536,7 +533,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
#ifdef HAVE_IPV6
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
#if defined(IP_PKTINFO)
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(IP_RECVDSTADDR)
char control[CMSG_SPACE(sizeof(struct in_addr)) +
@@ -572,13 +569,15 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
return;
if (n < (int)sizeof(HEADER) || header->qr)
if (n < (int)sizeof(HEADER) ||
(msg.msg_flags & MSG_TRUNC) ||
header->qr)
return;
source_addr.sa.sa_family = listen->family;
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
source_addr.in6.sin6_flowinfo = htonl(0);
source_addr.in6.sin6_flowinfo = 0;
#endif
if (!(daemon->options & OPT_NOWILD))
@@ -588,7 +587,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
if (msg.msg_controllen < sizeof(struct cmsghdr))
return;
#if defined(IP_PKTINFO)
#if defined(HAVE_LINUX_NETWORK)
if (listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
@@ -643,33 +642,8 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
if (daemon->if_names || daemon->if_addrs)
{
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
break;
if (!tmp)
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == listen->family)
{
if (tmp->addr.sa.sa_family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == dst_addr.addr.addr4.s_addr)
break;
#ifdef HAVE_IPV6
else if (tmp->addr.sa.sa_family == AF_INET6 &&
memcmp(&tmp->addr.in6.sin6_addr,
&dst_addr.addr.addr6,
sizeof(struct in6_addr)) == 0)
break;
#endif
}
if (!tmp)
return;
}
if (!iface_check(daemon, listen->family, &dst_addr, ifr.ifr_name))
return;
}
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
@@ -709,7 +683,7 @@ static int read_write(int fd, unsigned char *packet, int size, int rw)
return 0;
else if (n == -1)
{
if (retry_send())
if (errno == EINTR)
goto retry;
else
return 0;
@@ -920,6 +894,7 @@ static struct frec *get_new_frec(time_t now)
{
f->next = frec_list;
f->time = now;
f->new_id = 0;
frec_list = f;
}
return f; /* OK if malloc fails and this is NULL */

View File

@@ -17,6 +17,8 @@
#ifdef HAVE_ISC_READER
#define MAXTOK 50
struct isc_lease {
char *name, *fqdn;
time_t expires;

View File

@@ -10,26 +10,22 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#include "dnsmasq.h"
static struct dhcp_lease *leases;
static int dns_dirty;
static enum { no, yes, force } file_dirty;
static int leases_left;
static int dns_dirty, file_dirty, leases_left;
void lease_init(struct daemon *daemon, time_t now)
{
unsigned int a0, a1, a2, a3;
unsigned long ei;
time_t expires;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct in_addr addr;
struct dhcp_lease *lease;
int clid_len = 0, hw_len, hw_type;
int has_old = 0;
dns_dirty = 1;
file_dirty = 0;
leases = NULL;
leases_left = daemon->dhcp_max;
@@ -46,26 +42,21 @@ void lease_init(struct daemon *daemon, time_t now)
&ei, daemon->dhcp_buff2, &a0, &a1, &a2, &a3,
daemon->dhcp_buff, daemon->packet) == 8)
{
#ifdef HAVE_BROKEN_RTC
if (ei)
expires = (time_t)ei + now;
else
expires = (time_t)0;
#else
#ifndef HAVE_BROKEN_RTC
/* strictly time_t is opaque, but this hack should work on all sane systems,
even when sizeof(time_t) == 8 */
expires = (time_t)ei;
time_t expires = (time_t)ei;
if (ei != 0 && difftime(now, expires) > 0)
{
has_old = 1;
file_dirty = 1;
continue; /* expired */
}
#endif
hw_len = parse_hex(daemon->dhcp_buff2, hwaddr, DHCP_CHADDR_MAX, NULL, &hw_type);
/* For backwards compatibility, no explict MAC address type means ether. */
if (hw_type == 0)
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
addr.s_addr = htonl((a0<<24) + (a1<<16) + (a2<<8) + a3);
@@ -79,14 +70,19 @@ void lease_init(struct daemon *daemon, time_t now)
if (!(lease = lease_allocate(hwaddr, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len, addr)))
die (_("too many stored leases"), NULL);
#ifdef HAVE_BROKEN_RTC
if (ei != 0)
lease->expires = (time_t)ei + now;
else
lease->expires = (time_t)0;
lease->length = ei;
#else
lease->expires = expires;
#endif
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix, 0);
lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix, 0);
}
dns_dirty = 1;
file_dirty = has_old ? yes: no;
}
void lease_update_from_configs(struct daemon *daemon)
@@ -107,47 +103,43 @@ void lease_update_from_configs(struct daemon *daemon)
lease_set_hostname(lease, name, daemon->domain_suffix, 1); /* updates auth flag only */
}
void lease_update_file(struct daemon *daemon, int always, time_t now)
void lease_update_file(struct daemon *daemon)
{
struct dhcp_lease *lease;
int i = always; /* avoid warning */
unsigned long expires;
char *mess;
#ifdef HAVE_BROKEN_RTC
if (always || file_dirty == force)
int i;
if (file_dirty != 0)
{
lease_prune(NULL, now);
#else
if (file_dirty != no)
{
#endif
errno = 0;
rewind(daemon->lease_stream);
if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0)
goto write_err;
{
write_err:
syslog(LOG_ERR, _("failed to write %s: %m (retry in %ds)"), daemon->lease_file, LEASE_RETRY);
alarm(LEASE_RETRY);
return;
}
for (lease = leases; lease; lease = lease->next)
{
#ifdef HAVE_BROKEN_RTC
if (lease->expires)
expires = (unsigned long) difftime(lease->expires, now);
else
expires = 0;
#else
expires = now; /* eliminate warning */
expires = (unsigned long)lease->expires;
#endif
if (fprintf(daemon->lease_stream, "%lu ", expires) < 0)
if (fprintf(daemon->lease_stream, "%u ", lease->length) < 0)
goto write_err;
if (lease->hwaddr_type != ARPHRD_ETHER &&
#else
if (fprintf(daemon->lease_stream, "%lu ", (unsigned long)lease->expires) < 0)
goto write_err;
#endif
if ((lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0) &&
fprintf(daemon->lease_stream, "%.2x-", lease->hwaddr_type) < 0)
goto write_err;
for (i = 0; i < lease->hwaddr_len - 1; i++)
if (fprintf(daemon->lease_stream, "%.2x:", lease->hwaddr[i]) < 0)
goto write_err;
if (fprintf(daemon->lease_stream, "%.2x", lease->hwaddr[i]) < 0)
goto write_err;
for (i = 0; i < lease->hwaddr_len; i++)
{
if (fprintf(daemon->lease_stream, "%.2x", lease->hwaddr[i]) < 0)
goto write_err;
if (i != lease->hwaddr_len - 1 &&
fprintf(daemon->lease_stream, ":") < 0)
goto write_err;
}
if (fprintf(daemon->lease_stream, " %s %s ", inet_ntoa(lease->addr),
lease->hostname && strlen(lease->hostname) != 0 ? lease->hostname : "*") < 0)
goto write_err;
@@ -170,19 +162,8 @@ void lease_update_file(struct daemon *daemon, int always, time_t now)
goto write_err;
if (fsync(fileno(daemon->lease_stream)) < 0)
goto write_err;
file_dirty = no;
file_dirty = 0;
}
return;
write_err:
mess = _("failed to write");
#ifdef HAVE_BROKEN_RTC
syslog(LOG_ERR, "%s %s: %m", mess, daemon->lease_file);
#else
syslog(LOG_ERR, "%s %s: %m (retry in %ds)", mess, daemon->lease_file, LEASE_RETRY);
alarm(LEASE_RETRY);
#endif
}
void lease_update_dns(struct daemon *daemon)
@@ -212,7 +193,7 @@ void lease_prune(struct dhcp_lease *target, time_t now)
tmp = lease->next;
if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target)
{
file_dirty = yes;
file_dirty = 1;
*up = lease->next; /* unlink */
if (lease->hostname)
@@ -246,6 +227,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h
for (lease = leases; lease; lease = lease->next)
if ((!lease->clid || !clid) &&
hw_len != 0 &&
lease->hwaddr_len == hw_len &&
lease->hwaddr_type == hw_type &&
memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
@@ -277,7 +259,12 @@ struct dhcp_lease *lease_allocate(unsigned char *hwaddr, unsigned char *clid,
lease->hostname = lease->fqdn = NULL;
lease->addr = addr;
memset(lease->hwaddr, 0, DHCP_CHADDR_MAX);
lease->hwaddr_len = 0;
lease->hwaddr_type = 0;
lease->expires = 1;
#ifdef HAVE_BROKEN_RTC
lease->length = 0xffffffff; /* illegal value */
#endif
if (!lease_set_hwaddr(lease, hwaddr, clid, hw_len, hw_type, clid_len))
{
@@ -288,30 +275,53 @@ struct dhcp_lease *lease_allocate(unsigned char *hwaddr, unsigned char *clid,
lease->next = leases;
leases = lease;
file_dirty = force;
file_dirty = 1;
leases_left--;
return lease;
}
void lease_set_expires(struct dhcp_lease *lease, time_t exp)
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
{
time_t exp = now + (time_t)len;
if (len == 0xffffffff)
{
exp = 0;
len = 0;
}
if (exp != lease->expires)
{
file_dirty = yes;
dns_dirty = 1;
lease->expires = exp;
#ifndef HAVE_BROKEN_RTC
file_dirty = 1;
#endif
}
lease->expires = exp;
}
#ifdef HAVE_BROKEN_RTC
if (len != lease->length)
{
lease->length = len;
file_dirty = 1;
}
#endif
}
int lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len)
{
/* must have some sort of unique-id */
if (hw_len == 0 && (clid_len == 0 || !clid))
return 0;
if (hw_len != lease->hwaddr_len ||
hw_type != lease->hwaddr_type ||
hw_len == 0 ||
memcmp(lease->hwaddr, hwaddr, hw_len) != 0)
{
file_dirty = force;
file_dirty = 1;
memcpy(lease->hwaddr, hwaddr, hw_len);
lease->hwaddr_len = hw_len;
lease->hwaddr_type = hw_type;
@@ -327,15 +337,15 @@ int lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
if (lease->clid_len != clid_len)
{
file_dirty = force;
file_dirty = 1;
if (lease->clid)
free(lease->clid);
if (!(lease->clid = malloc(clid_len)))
return 0;
}
else if (memcmp(lease->clid, clid, clid_len) != 0)
file_dirty = force;
file_dirty = 1;
lease->clid_len = clid_len;
memcpy(lease->clid, clid, clid_len);
}
@@ -399,7 +409,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
lease->fqdn = new_fqdn;
lease->auth_name = auth;
file_dirty = force;
file_dirty = 1;
dns_dirty = 1;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2006 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2006 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
@@ -12,36 +12,36 @@
#include "dnsmasq.h"
#ifdef HAVE_RTNETLINK
#ifdef HAVE_LINUX_NETWORK
#include <linux/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
static struct iovec iov[1];
static struct iovec iov;
static void nl_err(struct nlmsghdr *h);
static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h);
void netlink_init(struct daemon *daemon)
{
struct sockaddr_nl addr;
daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (daemon->netlinkfd < 0)
return; /* no kernel support */
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = getpid();
addr.nl_pid = 0; /* autobind */
#ifdef HAVE_IPV6
addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
#else
addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
addr.nl_groups = RTMGRP_IPV4_ROUTE;
#endif
if (bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
die(_("cannot bind netlink socket: %s"), NULL);
iov[0].iov_len = 200;
iov[0].iov_base = safe_malloc(iov[0].iov_len);
if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1 ||
bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
die(_("cannot create RTnetlink socket: %s"), NULL);
iov.iov_len = 200;
iov.iov_base = safe_malloc(iov.iov_len);
}
static ssize_t netlink_recv(struct daemon *daemon)
@@ -51,162 +51,218 @@ static ssize_t netlink_recv(struct daemon *daemon)
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
retry:
while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK)) == -1 && retry_send());
if (rc == -1)
return -1;
if (msg.msg_flags & MSG_TRUNC)
while (1)
{
size_t newsz = iov[0].iov_len + 100;
void *new = realloc(iov[0].iov_base, newsz);
if (!new)
msg.msg_flags = 0;
while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
/* 2.2.x doesn't suport MSG_PEEK at all, returning EOPNOTSUPP, so we just grab a
big buffer and pray in that case. */
if (rc == -1 && errno == EOPNOTSUPP)
{
if (!expand_buf(&iov, 2000))
return -1;
break;
}
if (rc == -1 || !(msg.msg_flags & MSG_TRUNC))
break;
if (!expand_buf(&iov, iov.iov_len + 100))
return -1;
iov[0].iov_len = newsz;
iov[0].iov_base = new;
goto retry;
}
while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && retry_send());
/* finally, read it for real */
while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR);
return rc;
}
int netlink_process(struct daemon *daemon, int index, struct in_addr relay,
struct in_addr primary, struct dhcp_context **retp)
int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
{
struct sockaddr_nl addr;
struct nlmsghdr *h;
int found_primary = 0;
ssize_t len;
struct dhcp_context *ret = NULL;
static unsigned int seq = 0;
int family = AF_INET;
struct {
struct nlmsghdr nlh;
struct rtgenmsg g;
} req;
if (daemon->netlinkfd == -1)
return 0;
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_groups = 0;
addr.nl_pid = 0; /* address to kernel */
again:
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = RTM_GETADDR;
req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = ++seq;
req.g.rtgen_family = AF_INET;
req.g.rtgen_family = family;
/* Don't block in recvfrom if send fails */
while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,
(struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());
if (len == -1)
{
/* if RTnetlink not configured in the kernel, don't keep trying. */
if (errno == ECONNREFUSED)
{
close(daemon->netlinkfd);
daemon->netlinkfd = -1;
}
return 0;
}
get_next:
if ((len = netlink_recv(daemon)) == -1)
return 0;
for (h = (struct nlmsghdr *)iov[0].iov_base; NLMSG_OK(h, (unsigned int)len); h = NLMSG_NEXT(h, len))
if (len == -1)
return 0;
while (1)
{
if (h->nlmsg_seq != seq)
goto get_next;
if (h->nlmsg_type == NLMSG_DONE)
break;
if (h->nlmsg_type == NLMSG_ERROR)
if ((len = netlink_recv(daemon)) == -1)
return 0;
if (h->nlmsg_type == RTM_NEWADDR)
{
struct ifaddrmsg *ifa = NLMSG_DATA(h);
if (ifa->ifa_index == index && ifa->ifa_family == AF_INET)
{
struct rtattr *rta = IFA_RTA(ifa);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
struct in_addr netmask, addr, broadcast;
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
addr.s_addr = 0;
broadcast.s_addr = 0;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFA_LOCAL)
addr = *((struct in_addr *)(rta+1));
else if (rta->rta_type == IFA_BROADCAST)
broadcast = *((struct in_addr *)(rta+1));
rta = RTA_NEXT(rta, len1);
}
if (addr.s_addr)
{
ret = complete_context(daemon, addr, ret, netmask, broadcast, relay, primary);
if (addr.s_addr == primary.s_addr)
found_primary = 1;
}
}
}
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h);
else if (h->nlmsg_seq != seq)
nl_routechange(daemon, h); /* May be multicast arriving async */
else if (h->nlmsg_type == NLMSG_DONE)
{
#ifdef HAVE_IPV6
if (family == AF_INET && ipv6_callback)
{
family = AF_INET6;
goto again;
}
#endif
return 1;
}
else if (h->nlmsg_type == RTM_NEWADDR)
{
struct ifaddrmsg *ifa = NLMSG_DATA(h);
struct rtattr *rta = IFA_RTA(ifa);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
if (ifa->ifa_family == AF_INET)
{
struct in_addr netmask, addr, broadcast;
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
addr.s_addr = 0;
broadcast.s_addr = 0;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFA_LOCAL)
addr = *((struct in_addr *)(rta+1));
else if (rta->rta_type == IFA_BROADCAST)
broadcast = *((struct in_addr *)(rta+1));
rta = RTA_NEXT(rta, len1);
}
if (addr.s_addr && ipv4_callback)
if (!((*ipv4_callback)(daemon, addr, ifa->ifa_index, netmask, broadcast, parm)))
return 0;
}
#ifdef HAVE_IPV6
else if (ifa->ifa_family == AF_INET6)
{
struct in6_addr *addrp = NULL;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFA_ADDRESS)
addrp = ((struct in6_addr *)(rta+1));
rta = RTA_NEXT(rta, len1);
}
if (addrp && ipv6_callback)
if (!((*ipv6_callback)(daemon, addrp, ifa->ifa_index, ifa->ifa_index, parm)))
return 0;
}
#endif
}
}
*retp = ret;
return found_primary;
}
/* We arrange to receive netlink multicast messages whenever the network config changes.
If this happens and we still have a DNS packet in the buffer, we re-send it.
This helps on DoD links, where frequently the packet which triggers dialling is
a DNS query, which then gets lost. By re-sending, we can avoid the lookup
failing. */
void netlink_multicast(struct daemon *daemon)
{
ssize_t len;
struct nlmsghdr *h;
if ((len = netlink_recv(daemon)) == -1)
return;
if (!daemon->srv_save)
return;
for (h = (struct nlmsghdr *)iov[0].iov_base; NLMSG_OK(h, (unsigned int)len); h = NLMSG_NEXT(h, len))
if ((len = netlink_recv(daemon)) != -1)
{
if (h->nlmsg_type == NLMSG_DONE || h->nlmsg_type == NLMSG_ERROR)
break;
if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_NEWROUTE)
{
while(sendto(daemon->srv_save->sfd->fd, daemon->packet, daemon->packet_len, 0,
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
break;
}
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h);
else
nl_routechange(daemon, h);
}
}
static void nl_err(struct nlmsghdr *h)
{
struct nlmsgerr *err = NLMSG_DATA(h);
if (err->error != 0)
syslog(LOG_ERR, _("RTnetlink returns error: %s"), strerror(-(err->error)));
}
/* We arrange to receive netlink multicast messages whenever the network route is added.
If this happens and we still have a DNS packet in the buffer, we re-send it.
This helps on DoD links, where frequently the packet which triggers dialling is
a DNS query, which then gets lost. By re-sending, we can avoid the lookup
failing. */
static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h)
{
if (h->nlmsg_type == RTM_NEWROUTE && daemon->srv_save)
{
struct rtmsg *rtm = NLMSG_DATA(h);
if (rtm->rtm_type == RTN_UNICAST &&
rtm->rtm_scope == RT_SCOPE_LINK)
while(sendto(daemon->srv_save->sfd->fd, daemon->packet, daemon->packet_len, 0,
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
}
}
void arp_inject(int fd, struct in_addr ip_addr, int iface,
unsigned char *mac, unsigned int mac_len)
{
struct sockaddr_nl addr;
struct {
struct nlmsghdr nlh;
struct ndmsg m;
struct rtattr addr_attr;
struct in_addr addr;
struct rtattr ll_attr;
char mac[DHCP_CHADDR_MAX];
} req;
memset(&req, 0, sizeof(req));
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = RTM_NEWNEIGH;
req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE;
req.m.ndm_family = AF_INET;
req.m.ndm_ifindex = iface;
req.m.ndm_state = NUD_REACHABLE;
req.addr_attr.rta_type = NDA_DST;
req.addr_attr.rta_len = RTA_LENGTH(sizeof(struct in_addr));
req.addr = ip_addr;
req.ll_attr.rta_type = NDA_LLADDR;
req.ll_attr.rta_len = RTA_LENGTH(mac_len);
memcpy(req.mac, mac, mac_len);
while(sendto(fd, (void *)&req, sizeof(req), 0, (struct sockaddr *)&addr, sizeof(addr)) == -1 &&
retry_send());
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
/* dnsmasq is Copyright (c) 2000 - 2006 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
@@ -10,262 +10,183 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#include "dnsmasq.h"
static int iface_allowed(struct daemon *daemon, struct irec *iface,
char *name, int is_loopback, union mysockaddr *addr)
int iface_check(struct daemon *daemon, int family, struct all_addr *addr, char *name)
{
struct iname *tmp;
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
return 0;
if (daemon->if_names || daemon->if_addrs)
{
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
{
tmp->used = 1;
return 1;
}
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == family)
{
if (family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
{
tmp->used = 1;
return 1;
}
#ifdef HAVE_IPV6
else if (family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr,
&addr->addr.addr6))
{
tmp->used = 1;
return 1;
}
#endif
}
return 0;
}
return 1;
}
static int iface_allowed(struct daemon *daemon, struct irec **irecp, int if_index,
union mysockaddr *addr, struct in_addr netmask)
{
struct irec *iface;
int fd;
struct ifreq ifr;
/* check whether the interface IP has been added already
we call this routine multiple times. */
for (iface = *irecp; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
return 1;
#ifdef HAVE_LINUX_NETWORK
ifr.ifr_ifindex = if_index;
#endif
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 ||
#ifdef HAVE_LINUX_NETWORK
ioctl(fd, SIOCGIFNAME, &ifr) == -1 ||
#else
!if_indextoname(if_index, ifr.ifr_name) ||
#endif
ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)
{
if (fd != -1)
{
int errsave = errno;
close(fd);
errno = errsave;
}
return 0;
}
close(fd);
/* If we are restricting the set of interfaces to use, make
sure that loopback interfaces are in that set. */
if (daemon->if_names && is_loopback)
if (daemon->if_names && (ifr.ifr_flags & IFF_LOOPBACK))
{
struct iname *lo;
for (lo = daemon->if_names; lo; lo = lo->next)
if (lo->name && strcmp(lo->name, name) == 0)
if (lo->name && strcmp(lo->name, ifr.ifr_name) == 0)
{
lo->isloop = 1;
break;
}
if (!lo)
if (!lo && (lo = malloc(sizeof(struct iname))))
{
lo = safe_malloc(sizeof(struct iname));
lo->name = safe_malloc(strlen(name)+1);
strcpy(lo->name, name);
lo->name = safe_malloc(strlen(ifr.ifr_name)+1);
strcpy(lo->name, ifr.ifr_name);
lo->isloop = lo->used = 1;
lo->next = daemon->if_names;
daemon->if_names = lo;
}
}
/* check blacklist */
if (daemon->if_except)
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && strcmp(tmp->name, name) == 0)
return 0;
/* we may need to check the whitelist */
if (daemon->if_names || daemon->if_addrs)
{
int found = 0;
if (addr->sa.sa_family == AF_INET &&
!iface_check(daemon, AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name))
return 1;
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
found = tmp->used = 1;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, addr))
found = tmp->used = 1;
if (!found)
return 0;
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
!iface_check(daemon, AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name))
return 1;
#endif
/* add to list */
if ((iface = malloc(sizeof(struct irec))))
{
iface->addr = *addr;
iface->netmask = netmask;
iface->next = *irecp;
*irecp = iface;
return 1;
}
/* check whether the interface IP has been added already
it is possible to have multiple interfaces with the same address */
for (; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
break;
if (iface)
return 0;
return 1;
errno = ENOMEM;
return 0;
}
/* This does two different jobs: if chainp is non-NULL, it puts
a list of all the interfaces allowed by config into *chainp.
If chainp is NULL, it returns 1 if addr is an address of an interface
allowed by config and if that address is IPv4, it fills in the
netmask of the interface.
If chainp is non-NULL, a zero return indicates a fatal error.
If chainp is NULL, errors result in a match failure and zero return.
*/
int enumerate_interfaces(struct daemon *daemon, struct irec **chainp,
union mysockaddr *test_addrp, struct in_addr *netmaskp)
#ifdef HAVE_IPV6
static int iface_allowed_v6(struct daemon *daemon, struct in6_addr *local,
int scope, int if_index, void *vparam)
{
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
FILE *f;
#endif
union mysockaddr addr;
struct irec *iface = NULL;
char *buf, *ptr;
struct ifreq *ifr = NULL;
struct ifconf ifc;
int lastlen = 0;
int len = 20 * sizeof(struct ifreq);
int fd = socket(PF_INET, SOCK_DGRAM, 0);
struct in_addr netmask;
int ret = 0;
struct in_addr netmask; /* dummy */
netmask.s_addr = 0; /* eliminate warning */
if (fd == -1)
return 0;
#ifdef HAVE_IPV6
if (test_addrp && test_addrp->sa.sa_family == AF_INET6)
test_addrp->in6.sin6_flowinfo = htonl(0);
#endif
while (1)
{
buf = safe_malloc(len);
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
{
if (errno != EINVAL || lastlen != 0)
goto exit;
}
else
{
if (ifc.ifc_len == lastlen)
break; /* got a big enough buffer now */
lastlen = ifc.ifc_len;
}
len += 10*sizeof(struct ifreq);
free(buf);
}
netmask.s_addr = 0;
for (ptr = buf; ptr < buf + ifc.ifc_len; )
{
#ifdef HAVE_SOCKADDR_SA_LEN
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
unaligned accesses. */
int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
if (!(ifr = realloc(ifr, ifr_len)))
goto exit;
memcpy(ifr, ptr, ifr_len);
ptr += ifr_len;
#else
ifr = (struct ifreq *)ptr;
ptr += sizeof(struct ifreq);
addr.in6.sin6_len = sizeof(addr.in6);
#endif
/* copy address since getting flags overwrites */
if (ifr->ifr_addr.sa_family == AF_INET)
{
addr.in = *((struct sockaddr_in *) &ifr->ifr_addr);
addr.in.sin_port = htons(daemon->port);
if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
goto exit;
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
}
#ifdef HAVE_IPV6
else if (ifr->ifr_addr.sa_family == AF_INET6)
{
#ifdef HAVE_BROKEN_SOCKADDR_IN6
addr.in6 = *((struct my_sockaddr_in6 *) &ifr->ifr_addr);
#else
addr.in6 = *((struct sockaddr_in6 *) &ifr->ifr_addr);
#endif
addr.in6.sin6_port = htons(daemon->port);
addr.in6.sin6_flowinfo = htonl(0);
}
#endif
else
continue; /* unknown address family */
if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0)
goto exit;
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = *local;
addr.in6.sin6_port = htons(daemon->port);
addr.in6.sin6_scope_id = scope;
addr.in6.sin6_flowinfo = 0;
if (iface_allowed(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr))
{
if (chainp)
{
struct irec *new = safe_malloc(sizeof(struct irec));
new->addr = addr;
new->netmask = netmask;
new->next = iface;
iface = new;
}
else if (sockaddr_isequal(&addr, test_addrp))
{
*netmaskp = netmask;
ret = 1;
goto exit;
}
}
}
return iface_allowed(daemon, (struct irec **)vparam, if_index, &addr, netmask);
}
#endif
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
/* This code snarfed from net-tools 1.60 and certainly linux specific, though
it shouldn't break on other Unices, and their SIOGIFCONF might work. */
if ((f = fopen(IP6INTERFACES, "r")))
{
unsigned int plen, scope, flags, if_idx;
char devname[21], addrstring[33];
while (fscanf(f, "%32s %x %x %x %x %20s\n",
addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF)
{
int i;
struct ifreq sifr;
unsigned char *addr6p = (unsigned char *) &addr.in6.sin6_addr;
memset(&addr, 0, sizeof(addr));
addr.sa.sa_family = AF_INET6;
for (i=0; i<16; i++)
{
unsigned int byte;
sscanf(addrstring+i+i, "%02x", &byte);
addr6p[i] = byte;
}
addr.in6.sin6_port = htons(daemon->port);
addr.in6.sin6_flowinfo = htonl(0);
addr.in6.sin6_scope_id = htonl(scope);
strncpy(sifr.ifr_name, devname, IF_NAMESIZE);
if (ioctl(fd, SIOCGIFFLAGS, &sifr) < 0)
goto exit;
if (iface_allowed(daemon, iface, sifr.ifr_name, sifr.ifr_flags & IFF_LOOPBACK, &addr))
{
if (chainp)
{
struct irec *new = safe_malloc(sizeof(struct irec));
new->addr = addr;
new->next = iface;
iface = new;
}
else if (sockaddr_isequal(&addr, test_addrp))
{
ret = 1;
goto exit;
}
}
}
fclose(f);
}
#endif /* LINUX */
static int iface_allowed_v4(struct daemon *daemon, struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
union mysockaddr addr;
if (chainp)
{
*chainp = iface;
ret = 1;
}
exit:
if (buf)
free(buf);
#ifdef HAVE_SOCKADDR_SA_LEN
if (ifr)
free(ifr);
addr.in.sin_len = sizeof(addr.in);
#endif
close(fd);
addr.in.sin_family = AF_INET;
addr.in.sin_addr = broadcast; /* warning */
addr.in.sin_addr = local;
addr.in.sin_port = htons(daemon->port);
return ret;
return iface_allowed(daemon, (struct irec **)vparam, if_index, &addr, netmask);
}
int enumerate_interfaces(struct daemon *daemon)
{
#ifdef HAVE_IPV6
return iface_enumerate(daemon, &daemon->interfaces, iface_allowed_v4, iface_allowed_v6);
#else
return iface_enumerate(daemon, &daemon->interfaces, iface_allowed_v4, NULL);
#endif
}
#if defined(HAVE_IPV6) && (defined(IP_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR)))
#if defined(HAVE_IPV6) && \
(defined(HAVE_LINUX_NETWORK) || \
(defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR)))
static int create_ipv6_listener(struct listener **link, int port)
{
union mysockaddr addr;
@@ -276,9 +197,10 @@ static int create_ipv6_listener(struct listener **link, int port)
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = htons(port);
addr.in6.sin6_flowinfo = htonl(0);
addr.in6.sin6_flowinfo = 0;
addr.in6.sin6_scope_id = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
addr.in6.sin6_len = sizeof(addr.in6);
#endif
/* No error of the kernel doesn't support IPv6 */
@@ -332,7 +254,7 @@ static int create_ipv6_listener(struct listener **link, int port)
struct listener *create_wildcard_listeners(int port)
{
#if !(defined(IP_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR)))
#if !(defined(HAVE_LINUX_NETWORK) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR)))
port = 0; /* eliminate warning */
return NULL;
#else
@@ -369,7 +291,7 @@ struct listener *create_wildcard_listeners(int port)
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
(flags = fcntl(fd, F_GETFL, 0)) == -1 ||
fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
#if defined(IP_PKTINFO)
#if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
@@ -393,14 +315,14 @@ struct listener *create_wildcard_listeners(int port)
#endif
}
struct listener *create_bound_listeners(struct irec *interfaces, int port)
struct listener *create_bound_listeners(struct daemon *daemon)
{
struct listener *listeners = NULL;
struct irec *iface;
int flags = port, opt = 1;
int flags, opt = 1;
for (iface = interfaces ;iface; iface = iface->next)
for (iface = daemon->interfaces; iface; iface = iface->next)
{
struct listener *new = safe_malloc(sizeof(struct listener));
new->family = iface->addr.sa.sa_family;
@@ -439,9 +361,9 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port)
else
#endif
{
char addrbuff[ADDRSTRLEN];
prettyprint_addr(&iface->addr, addrbuff);
die(_("failed to bind listening socket for %s: %s"), addrbuff);
prettyprint_addr(&iface->addr, daemon->namebuff);
die(_("failed to bind listening socket for %s: %s"),
daemon->namebuff);
}
}
else
@@ -611,7 +533,7 @@ void reload_servers(char *fname, struct daemon *daemon)
#endif
{
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr.in.sin_len = addr.in.sin_len = sizeof(struct sockaddr_in);
source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in);
#endif
source_addr.in.sin_family = addr.in.sin_family = AF_INET;
addr.in.sin_port = htons(NAMESERVER_PORT);
@@ -622,11 +544,12 @@ void reload_servers(char *fname, struct daemon *daemon)
else if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
{
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(struct sockaddr_in6);
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
#endif
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = htonl(0);
source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
source_addr.in6.sin6_scope_id = addr.in6.sin6_scope_id = 0;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
}

View File

@@ -10,8 +10,6 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#include "dnsmasq.h"
struct myoption {
@@ -21,7 +19,7 @@ struct myoption {
int val;
};
#define OPTSTRING "31yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:"
#define OPTSTRING "531yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:"
static const struct myoption opts[] = {
{"version", 0, 0, 'v'},
@@ -83,6 +81,7 @@ static const struct myoption opts[] = {
{"enable-dbus", 0, 0, '1'},
{"bootp-dynamic", 0, 0, '3'},
{"dhcp-mac", 1, 0, '4'},
{"no-ping", 0, 0, '5'},
{0, 0, 0, 0}
};
@@ -112,6 +111,7 @@ static const struct optflags optmap[] = {
{ 'y', OPT_LOCALISE },
{ '1', OPT_DBUS },
{ '3', OPT_BOOTP_DYNAMIC },
{ '5', OPT_NO_PING },
{ 'v', 0},
{ 'w', 0},
{ 0, 0 }
@@ -179,6 +179,7 @@ static const struct {
{ "-2, --no-dhcp-interface=interface", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
{ "-3, --bootp-dynamic", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
{ "-4, --dhcp-mac=<id>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
{ "-5, --no-ping", gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
{ NULL, NULL, NULL }
};
@@ -300,7 +301,6 @@ struct daemon *read_opts (int argc, char **argv, char *compile_opts)
daemon->namebuff = buff;
/* Set defaults - everything else is zero or NULL */
daemon->min_leasetime = UINT_MAX;
daemon->cachesize = CACHESIZ;
daemon->port = NAMESERVER_PORT;
daemon->default_resolv.is_default = 1;
@@ -704,16 +704,17 @@ struct daemon *read_opts (int argc, char **argv, char *compile_opts)
{
new->addr.sa.sa_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
new->addr.in.sin_len = sizeof(struct sockaddr_in);
new->addr.in.sin_len = sizeof(new->addr.in);
#endif
}
#ifdef HAVE_IPV6
else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
{
new->addr.sa.sa_family = AF_INET6;
new->addr.in6.sin6_flowinfo = htonl(0);
new->addr.in6.sin6_flowinfo = 0;
new->addr.in6.sin6_scope_id = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
new->addr.in6.sin6_len = sizeof(struct sockaddr_in6);
new->addr.in6.sin6_len = sizeof(new->addr.in6);
#endif
}
#endif
@@ -841,9 +842,10 @@ struct daemon *read_opts (int argc, char **argv, char *compile_opts)
newlist->addr.in6.sin6_port = htons(serv_port);
newlist->source_addr.in6.sin6_port = htons(source_port);
newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET6;
newlist->addr.in6.sin6_flowinfo = newlist->source_addr.in6.sin6_flowinfo = htonl(0);
newlist->addr.in6.sin6_flowinfo = newlist->source_addr.in6.sin6_flowinfo = 0;
newlist->addr.in6.sin6_scope_id = newlist->source_addr.in6.sin6_scope_id = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
newlist->addr.in6.sin6_len = newlist->source_addr.in6.sin6_len = sizeof(struct sockaddr_in6);
newlist->addr.in6.sin6_len = newlist->source_addr.in6.sin6_len = sizeof(newlist->addr.in6);
#endif
if (source)
{
@@ -1084,9 +1086,6 @@ struct daemon *read_opts (int argc, char **argv, char *compile_opts)
}
}
}
if (new->lease_time < daemon->min_leasetime)
daemon->min_leasetime = new->lease_time;
break;
}
@@ -1226,11 +1225,8 @@ struct daemon *read_opts (int argc, char **argv, char *compile_opts)
free(new);
}
else
{
if ((new->flags & CONFIG_TIME) && new->lease_time < daemon->min_leasetime)
daemon->min_leasetime = new->lease_time;
daemon->dhcp_conf = new;
}
daemon->dhcp_conf = new;
break;
}
@@ -1808,9 +1804,9 @@ struct daemon *read_opts (int argc, char **argv, char *compile_opts)
else
die(_("bad command line options: %s."),
#ifdef HAVE_GETOPT_LONG
problem ? problem : "try --help"
problem ? problem : _("try --help")
#else
problem ? problem : "try -w"
problem ? problem : _("try -w")
#endif
);
}

View File

@@ -10,8 +10,6 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#include "dnsmasq.h"
#define BOOTREQUEST 1
@@ -44,9 +42,12 @@
#define OPTION_CLIENT_ID 61
#define OPTION_USER_CLASS 77
#define OPTION_CLIENT_FQDN 81
#define OPTION_AGENT_ID 82
#define OPTION_SUBNET_SELECT 118
#define OPTION_END 255
#define SUBOPT_SUBNET_SELECT 5 /* RFC 3527 */
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
@@ -57,19 +58,25 @@
#define DHCPINFORM 8
#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
#define option_ptr(opt) ((void *)&(((unsigned char *)(opt))[2]))
static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config,
struct dhcp_lease *lease, unsigned char *opt, time_t now);
static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val);
static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start);
static unsigned char *option_end(unsigned char *p, unsigned char *end,
unsigned char *agent_id, struct dhcp_packet *start);
static unsigned char *option_put_string(unsigned char *p, unsigned char *end,
int opt, char *string, int null_term);
static void bootp_option_put(struct dhcp_packet *mess,
struct dhcp_boot *boot_opts, struct dhcp_netid *netids);
static int option_len(unsigned char *opt);
static void *option_ptr(unsigned char *opt);
static struct in_addr option_addr(unsigned char *opt);
static unsigned int option_uint(unsigned char *opt, int size);
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, int hw_len, char *interface, char *string);
static char *print_mac(struct daemon *daemon, unsigned char *mac, int len);
static void log_packet(struct daemon *daemon, char *type, struct in_addr *addr,
struct dhcp_packet *mess, char *interface, char *string);
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
static unsigned char *do_req_options(struct dhcp_context *context,
unsigned char *p, unsigned char *end,
unsigned char *req_options,
@@ -80,6 +87,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
unsigned char fqdn_flags,
int null_term);
size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name,
size_t sz, time_t now, int unicast_dest)
{
@@ -89,9 +97,8 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
struct dhcp_mac *mac;
struct dhcp_netid_list *id_list;
int clid_len = 0, ignore = 0;
struct dhcp_packet *mess = &daemon->dhcp_packet->data;
unsigned char *p = mess->options + sizeof(u32); /* skip cookie */
unsigned char *end = (unsigned char *)(daemon->dhcp_packet + 1);
struct dhcp_packet *mess = daemon->dhcp_packet.iov_base;
unsigned char *p, *end = (unsigned char *)(mess + 1);
char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL;
int hostname_auth = 0, borken_opt = 0;
unsigned char *req_options = NULL;
@@ -104,13 +111,14 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
unsigned int mess_type = 0;
unsigned char fqdn_flags = 0;
subnet_addr.s_addr = 0;
unsigned char *agent_id = NULL;
if (mess->op != BOOTREQUEST)
if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
return 0;
if (mess->hlen > DHCP_CHADDR_MAX)
if (mess->htype == 0 && mess->hlen != 0)
return 0;
/* check for DHCP rather than BOOTP */
if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
{
@@ -120,13 +128,52 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE))
return 0;
/* two things to note here: expand_buf may move the packet,
so reassign mess from daemon->packet. Also, the size
sent includes the IP and UDP headers, hence the magic "-28" */
if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
{
size_t size = (size_t)option_uint(opt, 2) - 28;
if (size > DHCP_PACKET_MAX)
size = DHCP_PACKET_MAX;
else if (size < sizeof(struct dhcp_packet))
size = sizeof(struct dhcp_packet);
if (expand_buf(&daemon->dhcp_packet, size))
{
mess = daemon->dhcp_packet.iov_base;
end = ((unsigned char *)mess) + size;
}
}
/* Some buggy clients set ciaddr when they shouldn't, so clear that here since
it can affect the context-determination code. */
if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
mess->ciaddr.s_addr = 0;
/* Check for RFC3011 subnet selector */
if ((opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
{
/* Any agent-id needs to be copied back out, verbatim, as the last option
in the packet. Here, we shift it to the very end of the buffer, if it doesn't
get overwritten, then it will be shuffled back at the end of processing.
Note that the incoming options must not be overwritten here, so there has to
be enough free space at the end of the packet to copy the option. */
unsigned int total = option_len(opt) + 2;
unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
if (last_opt && last_opt < end - total)
{
agent_id = end - total;
memcpy(agent_id, opt, total);
}
/* look for RFC3527 Link selection sub-option */
if ((opt = option_find1(option_ptr(opt), option_ptr(opt) + option_len(opt), SUBOPT_SUBNET_SELECT, INADDRSZ)))
subnet_addr = option_addr(opt);
}
/* Check for RFC3011 subnet selector - only if RFC3527 one not present */
if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
subnet_addr = option_addr(opt);
/* If there is no client identifier option, use the hardware address */
@@ -137,8 +184,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
}
/* do we have a lease in store? */
if (mess->htype != 0 && mess->hlen != 0)
lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
/* If this request is missing a clid, but we've seen one before,
use it again for option matching etc. */
@@ -149,30 +195,13 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
}
}
/* htype == 0 is only allowed in DHCPINFORM,
this seems to be a microsoftism. */
if (mess->htype == 0)
{
if (mess_type != DHCPINFORM || mess->hlen != 0)
return 0;
}
else if (mess->hlen == 0)
return 0;
for (mac = daemon->dhcp_macs; mac; mac = mac->next)
if (mac->hwaddr_len == mess->hlen &&
(mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0))
(mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
{
int i;
unsigned int mask = mac->mask;
for (i = mac->hwaddr_len - 1; i >= 0; i--, mask = mask >> 1)
if (mask & 1)
mac->hwaddr[i] = mess->chaddr[i];
if (memcmp(mac->hwaddr, mess->chaddr, mac->hwaddr_len) == 0)
{
mac->netid.next = netid;
netid = &mac->netid;
}
mac->netid.next = netid;
netid = &mac->netid;
}
/* Determine network for this packet. Our caller will have already linked all the
@@ -225,7 +254,8 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
fallback = context->local;
mess->op = BOOTREPLY;
p = mess->options + sizeof(u32); /* skip cookie */
config = find_config(daemon->dhcp_conf, context, clid, clid_len,
mess->chaddr, mess->hlen, mess->htype, NULL);
@@ -235,6 +265,10 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
struct dhcp_netid id;
char save = mess->file[128];
struct in_addr *logaddr = NULL;
/* must have a MAC addr for bootp */
if (mess->htype == 0 || mess->hlen == 0)
return 0;
if (have_config(config, CONFIG_DISABLE))
message = _("disabled");
@@ -271,6 +305,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
mess->yiaddr = config->addr;
if ((lease = lease_find_by_addr(config->addr)) &&
(lease->hwaddr_len != mess->hlen ||
lease->hwaddr_type != mess->htype ||
memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
message = _("address in use");
}
@@ -313,18 +348,18 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
if (hostname)
lease_set_hostname(lease, hostname, daemon->domain_suffix, 1);
lease_set_expires(lease, 0); /* infinite lease */
lease_set_expires(lease, 0xffffffff, now); /* infinite lease */
p = do_req_options(context, p, end, NULL, daemon,
hostname, netid, subnet_addr, fqdn_flags, 0);
/* must do this after do_req_options since it overwrites filename field. */
mess->siaddr = context->local;
bootp_option_put(mess, daemon->boot_config, netid);
p = option_end(p, end, mess);
p = option_end(p, end, NULL, mess);
}
}
log_packet(NULL, logaddr, mess->chaddr, mess->hlen, iface_name, message);
log_packet(daemon, NULL, logaddr, mess, iface_name, message);
mess->file[128] = save;
if (message)
@@ -494,7 +529,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
return 0;
log_packet("DECLINE", option_ptr(opt), mess->chaddr, mess->hlen, iface_name, message);
log_packet(daemon, "DECLINE", option_ptr(opt), mess, iface_name, message);
if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
lease_prune(lease, now);
@@ -522,30 +557,55 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
else
message = _("unknown lease");
log_packet("RELEASE", &mess->ciaddr, mess->chaddr, mess->hlen, iface_name, message);
log_packet(daemon, "RELEASE", &mess->ciaddr, mess, iface_name, message);
return 0;
case DHCPDISCOVER:
{
struct in_addr addr;
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
addr = option_addr(opt);
if (ignore || have_config(config, CONFIG_DISABLE))
if (ignore || have_config(config, CONFIG_DISABLE))
{
message = _("ignored");
else if (have_config(config, CONFIG_ADDR) &&
(!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
mess->yiaddr = config->addr;
else if (lease && address_available(context, lease->addr))
mess->yiaddr = lease->addr;
else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr))
mess->yiaddr = addr;
else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
message = _("no address available");
log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, mess->hlen, iface_name, message);
}
opt = NULL;
}
else
{
struct in_addr addr, conf;
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
addr = option_addr(opt);
conf.s_addr = 0;
if (have_config(config, CONFIG_ADDR))
{
if ((ltmp = lease_find_by_addr(config->addr)) && ltmp != lease)
syslog(LOG_WARNING, _("not using configured address %s because it is leased to %s"),
inet_ntoa(config->addr), print_mac(daemon, ltmp->hwaddr, ltmp->hwaddr_len));
else
{
struct dhcp_context *tmp;
for (tmp = context; tmp; tmp = tmp->current)
if (context->local.s_addr == config->addr.s_addr)
break;
if (tmp)
syslog(LOG_WARNING, _("not using configured address %s because it is in use by the server"),
inet_ntoa(config->addr));
else
conf = config->addr;
}
}
if (conf.s_addr)
mess->yiaddr = conf;
else if (lease && address_available(context, lease->addr))
mess->yiaddr = lease->addr;
else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr))
mess->yiaddr = addr;
else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
message = _("no address available");
}
log_packet(daemon, "DISCOVER", opt ? (struct in_addr *)option_ptr(opt) : NULL, mess, iface_name, message);
if (message || !(context = narrow_context(context, mess->yiaddr)))
return 0;
@@ -556,16 +616,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
netid = &context->netid;
}
time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME, 4)))
{
unsigned int req_time = option_uint(opt, 4);
if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
time = req_time;
}
else if (lease && lease->expires != 0)
time = (unsigned int)difftime(lease->expires, now);
time = calc_time(context, config, lease, option_find(mess, sz, OPTION_LEASE_TIME, 4), now);
mess->siaddr = context->local;
bootp_option_put(mess, daemon->boot_config, netid);
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
@@ -579,9 +630,9 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
}
p = do_req_options(context, p, end, req_options, daemon,
offer_hostname, netid, subnet_addr, fqdn_flags, borken_opt);
p = option_end(p, end, mess);
p = option_end(p, end, agent_id, mess);
log_packet("OFFER" , &mess->yiaddr, mess->chaddr, mess->hlen, iface_name, NULL);
log_packet(daemon, "OFFER" , &mess->yiaddr, mess, iface_name, NULL);
return p - (unsigned char *)mess;
case DHCPREQUEST:
@@ -638,11 +689,17 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
mess->yiaddr = mess->ciaddr;
}
log_packet("REQUEST", &mess->yiaddr, mess->chaddr, mess->hlen, iface_name, NULL);
log_packet(daemon, "REQUEST", &mess->yiaddr, mess, iface_name, NULL);
if (!message)
{
struct dhcp_config *addr_config;
struct dhcp_context *tmp = NULL;
if (have_config(config, CONFIG_ADDR))
for (tmp = context; tmp; tmp = tmp->current)
if (context->local.s_addr == config->addr.s_addr)
break;
if (!(context = narrow_context(context, mess->yiaddr)))
{
@@ -660,8 +717,8 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
/* Check if a new static address has been configured. Be very sure that
when the client does DISCOVER, it will get the static address, otherwise
an endless protocol loop will ensue. */
else if (have_config(config, CONFIG_ADDR) &&
else if (!tmp &&
have_config(config, CONFIG_ADDR) &&
config->addr.s_addr != mess->yiaddr.s_addr &&
(!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
message = _("static lease available");
@@ -679,7 +736,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
if (message)
{
log_packet("NAK", &mess->yiaddr, mess->chaddr, mess->hlen, iface_name, message);
log_packet(daemon, "NAK", &mess->yiaddr, mess, iface_name, message);
mess->siaddr.s_addr = mess->yiaddr.s_addr = 0;
bootp_option_put(mess, NULL, NULL);
@@ -704,7 +761,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
hostname_auth = 1;
}
log_packet("ACK", &mess->yiaddr, mess->chaddr, mess->hlen, iface_name, hostname);
log_packet(daemon, "ACK", &mess->yiaddr, mess, iface_name, hostname);
if (context->netid.net)
{
@@ -712,18 +769,11 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
netid = &context->netid;
}
time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME, 4)))
{
unsigned int req_time = option_uint(opt, 4);
if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
time = req_time;
}
time = calc_time(context, config, NULL, option_find(mess, sz, OPTION_LEASE_TIME, 4), now);
lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
if (hostname)
lease_set_hostname(lease, hostname, daemon->domain_suffix, hostname_auth);
lease_set_expires(lease, time == 0xffffffff ? 0 : now + (time_t)time);
lease_set_expires(lease, time, now);
mess->siaddr = context->local;
bootp_option_put(mess, daemon->boot_config, netid);
@@ -735,20 +785,20 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
while (fuzz > (time/16))
fuzz = fuzz/2;
p = option_put(p, end, OPTION_T1, 4, (time/2) - fuzz);
p = option_put(p, end, OPTION_T2, 4, ((time * 7)/8) - fuzz);
p = option_put(p, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
}
p = do_req_options(context, p, end, req_options, daemon,
hostname, netid, subnet_addr, fqdn_flags, borken_opt);
}
p = option_end(p, end, mess);
p = option_end(p, end, agent_id, mess);
return p - (unsigned char *)mess;
case DHCPINFORM:
if (ignore || have_config(config, CONFIG_DISABLE))
message = _("ignored");
log_packet("INFORM", &mess->ciaddr, mess->chaddr, mess->hlen, iface_name, message);
log_packet(daemon, "INFORM", &mess->ciaddr, mess, iface_name, message);
if (message || mess->ciaddr.s_addr == 0 ||
!(context = narrow_context(context, mess->ciaddr)))
@@ -768,46 +818,71 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
hostname = host_from_dns(daemon, mess->yiaddr);
p = do_req_options(context, p, end, req_options, daemon,
hostname, netid, subnet_addr, fqdn_flags, borken_opt);
p = option_end(p, end, mess);
p = option_end(p, end, agent_id, mess);
log_packet("ACK", &mess->ciaddr, mess->chaddr, mess->hlen, iface_name, hostname);
log_packet(daemon, "ACK", &mess->ciaddr, mess, iface_name, hostname);
return p - (unsigned char *)mess;
}
return 0;
}
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, int hw_len, char *interface, char *string)
static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config,
struct dhcp_lease *lease, unsigned char *opt, time_t now)
{
char buff[(DHCP_CHADDR_MAX * 3) + 1];
char *p = buff;
int i;
*buff = 0;
for (i = 0; i < hw_len - 1; i++)
p += sprintf(p, "%.2x:", hwaddr[i]);
p += sprintf(p, "%.2x ", hwaddr[i]);
unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
if (opt)
{
unsigned int req_time = option_uint(opt, 4);
if (req_time < 120 )
req_time = 120; /* sanity */
if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
time = req_time;
}
else if (lease && lease->expires != 0 && difftime(lease->expires, now) > 0.0)
{
unsigned int lease_time = (unsigned int)difftime(lease->expires, now);
/* put a floor on lease-remaining time. */
if (lease_time < 360 )
lease_time = 360;
if (time > lease_time)
time = lease_time;
}
return time;
}
static char *print_mac(struct daemon *daemon, unsigned char *mac, int len)
{
char *p = daemon->namebuff;
int i;
if (len == 0)
sprintf(p, "<null> ");
else
for (i = 0; i < len; i++)
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? " " : ":");
return daemon->namebuff;
}
static void log_packet(struct daemon *daemon, char *type, struct in_addr *addr,
struct dhcp_packet *mess, char *interface, char *string)
{
syslog(LOG_INFO, "%s%s(%s) %s%s%s%s",
type ? "DHCP" : "BOOTP",
type ? type : "",
interface,
addr ? inet_ntoa(*addr) : "",
addr ? " " : "",
buff,
print_mac(daemon, mess->chaddr, mess->hlen),
string ? string : "");
}
static int option_len(unsigned char *opt)
{
return opt[1];
}
static void *option_ptr(unsigned char *opt)
{
return &opt[2];
}
static struct in_addr option_addr(unsigned char *opt)
{
/* this worries about unaligned data in the option. */
@@ -891,8 +966,16 @@ static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt,
return p;
}
static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start)
static unsigned char *option_end(unsigned char *p, unsigned char *end,
unsigned char *agent_id, struct dhcp_packet *start)
{
/* shuffle agent-id back down if we have room for it */
if (agent_id && p < agent_id)
{
memmove(p, agent_id, end - agent_id);
p += end - agent_id;
}
*(p++) = OPTION_END;
while ((p < end) && (p - ((unsigned char *)start) < MIN_PACKETSZ))
*p++ = 0;
@@ -919,61 +1002,54 @@ static unsigned char *option_put_string(unsigned char *p, unsigned char *end, in
return p;
}
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int *overload)
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
{
if (!p)
return NULL;
while (*p != OPTION_END)
{
if (p >= end)
return 0; /* malformed packet */
return NULL; /* malformed packet */
else if (*p == OPTION_PAD)
p++;
else if (*p == OPTION_OVERLOAD)
{
if (p >= end - 3)
return 0; /* malformed packet */
if (overload)
*overload = *(p+2);
p += 3;
}
else
{
int opt_len;;
int opt_len;
if (p >= end - 2)
return 0; /* malformed packet */
return NULL; /* malformed packet */
opt_len = option_len(p);
if (p >= end - (2 + opt_len))
return 0; /* malformed packet */
if (*p == opt)
return NULL; /* malformed packet */
if (*p == opt && opt_len >= minsize)
return p;
p += opt_len + 2;
}
}
return NULL;
return opt == OPTION_END ? p : NULL;
}
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
{
int overload = 0;
unsigned char *ret;
unsigned char *ret, *opt;
/* skip over DHCP cookie; */
ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, &overload);
if (!ret && (overload & 1))
ret = option_find1(&mess->file[0], &mess->file[128], opt_type, &overload);
if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
return ret;
if (!ret && (overload & 2))
ret = option_find1(&mess->sname[0], &mess->file[64], opt_type, &overload);
/* look for overload option. */
if (!(opt = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
return NULL;
/* Check the option field is big enough */
if (ret && (option_len(ret) < minsize))
ret = NULL;
return ret;
/* Can we look in filename area ? */
if ((option_uint(opt, 1) & 1) &&
(ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
return ret;
/* finally try sname area */
if ((option_uint(opt, 1) & 2) &&
(ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
return ret;
return NULL;
}
static int in_list(unsigned char *list, int opt)
@@ -1054,9 +1130,6 @@ static unsigned char *do_req_options(struct dhcp_context *context,
{
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
char *vendor_class = NULL;
if (in_list(req_options, OPTION_MAXMESSAGE))
p = option_put(p, end, OPTION_MAXMESSAGE, 2, end - (unsigned char *)daemon->dhcp_packet);
/* rfc3011 says this doesn't need to be in the requested options list. */
if (subnet_addr.s_addr)

View File

@@ -15,6 +15,10 @@
#include "dnsmasq.h"
#ifdef HAVE_BROKEN_RTC
#include <sys/times.h>
#endif
/* Prefer arc4random(3) over random(3) over rand(3) */
/* Also prefer /dev/urandom over /dev/random, to preserve the entropy pool */
#ifdef HAVE_ARC4RANDOM
@@ -186,13 +190,12 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
{
if (s1->sa.sa_family == AF_INET &&
s1->in.sin_port == s2->in.sin_port &&
memcmp(&s1->in.sin_addr, &s2->in.sin_addr, sizeof(struct in_addr)) == 0)
s1->in.sin_addr.s_addr == s2->in.sin_addr.s_addr)
return 1;
#ifdef HAVE_IPV6
if (s1->sa.sa_family == AF_INET6 &&
s1->in6.sin6_port == s2->in6.sin6_port &&
s1->in6.sin6_flowinfo == s2->in6.sin6_flowinfo &&
memcmp(&s1->in6.sin6_addr, &s2->in6.sin6_addr, sizeof(struct in6_addr)) == 0)
IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))
return 1;
#endif
}
@@ -234,21 +237,17 @@ int hostname_isequal(char *a, char *b)
return 1;
}
time_t dnsmasq_time(int fd)
time_t dnsmasq_time(void)
{
#ifdef HAVE_BROKEN_RTC
/* we use uptime as a time-base, rather than epoch time
because epoch time can break when a machine contacts
a nameserver and updates it. */
char buf[30];
lseek(fd, 0, SEEK_SET);
read(fd, buf, 30);
/* ensure the time is terminated even if /proc/uptime sends something unexpected */
buf[29] = 0;
read(fd, buf, 30);
return (time_t)atol(buf);
struct tms dummy;
static long tps = 0;
if (tps == 0)
tps = sysconf(_SC_CLK_TCK);
return (time_t)(times(&dummy)/tps);
#else
fd = 0; /* stop warning */
return time(NULL);
#endif
}
@@ -361,3 +360,39 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
return i;
}
int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
{
int i;
for (i = len - 1; i >= 0; i--, mask = mask >> 1)
if (!(mask & 1) && a[i] != b[i])
return 0;
return 1;
}
/* _note_ may copy buffer */
int expand_buf(struct iovec *iov, size_t size)
{
void *new;
if (size <= iov->iov_len)
return 1;
if (!(new = malloc(size)))
{
errno = ENOMEM;
return 0;
}
if (iov->iov_base)
{
memcpy(new, iov->iov_base, iov->iov_len);
free(iov->iov_base);
}
iov->iov_base = new;
iov->iov_len = size;
return 1;
}