mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
import of dnsmasq-2.28.tar.gz
This commit is contained in:
238
src/bpf.c
Normal file
238
src/bpf.c
Normal 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
|
||||
26
src/cache.c
26
src/cache.c
@@ -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;
|
||||
|
||||
|
||||
133
src/config.h
133
src/config.h
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
17
src/dbus.c
17
src/dbus.c
@@ -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);
|
||||
|
||||
|
||||
478
src/dhcp.c
478
src/dhcp.c
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
509
src/dnsmasq.c
509
src/dnsmasq.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
110
src/dnsmasq.h
110
src/dnsmasq.h
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#ifdef HAVE_ISC_READER
|
||||
|
||||
#define MAXTOK 50
|
||||
|
||||
struct isc_lease {
|
||||
char *name, *fqdn;
|
||||
time_t expires;
|
||||
|
||||
154
src/lease.c
154
src/lease.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
296
src/netlink.c
296
src/netlink.c
@@ -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
|
||||
|
||||
|
||||
|
||||
383
src/network.c
383
src/network.c
@@ -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);
|
||||
}
|
||||
|
||||
34
src/option.c
34
src/option.c
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
367
src/rfc2131.c
367
src/rfc2131.c
@@ -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)
|
||||
|
||||
65
src/util.c
65
src/util.c
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user