Allow more than one --tfp-root flag. The per-interface
stuff is pointless without that.
+ Add --bind-dynamic. A hybrid mode between the default and
+ --bind-interfaces which copes with dynamically created
+ interfaces.
+
version 2.62
Update German translation. Thanks to Conrad Kostecki.
same machine. Setting this option also enables multiple instances of
dnsmasq which provide DHCP service to run in the same machine.
.TP
+.B --bind-dynamic
+Enable a network mode which is a hybrid between
+.B --bind-interfaces
+and the default. Dnsmasq binds the address of indivdual interfaces,
+allowing multiple dnsmasq instances, but if new interfaces or
+addresses appear, it automatically listens on those (subject to any
+access-control configuration). This makes dynamically created
+interfaces work in the same way as the default. Implementing this
+option requires non-standard networking APIs and it is only availble
+under Linux.
+.TP
.B \-y, --localise-queries
Return answers to DNS queries from /etc/hosts which depend on the interface over which the query was
received. If a name in /etc/hosts has more than one address associated with
/* When bind-interfaces is set, there might be more than one dnmsasq
instance binding port 67. That's OK if they serve different networks.
Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */
- if (option_bool(OPT_NOWILD))
+ if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
{
#ifdef SO_REUSEPORT
int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
set_option_bool(OPT_NOWILD);
}
# endif
+
+ if (option_bool(OPT_CLEVERBIND))
+ die(_("--bind-dynamic not available on this platform"), NULL, EC_BADCONF);
#endif
#ifndef HAVE_TFTP
#ifdef HAVE_LINUX_NETWORK
/* After lease_init */
netlink_init();
+
+ if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
+ die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
#endif
#ifdef HAVE_DHCP6
if (!enumerate_interfaces())
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
- if (option_bool(OPT_NOWILD))
+ if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
{
create_bound_listeners(1);
-
- for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
- if (if_tmp->name && !if_tmp->used)
- die(_("unknown interface %s"), if_tmp->name, EC_BADNET);
+
+ if (!option_bool(OPT_CLEVERBIND))
+ for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
+ if (if_tmp->name && !if_tmp->used)
+ die(_("unknown interface %s"), if_tmp->name, EC_BADNET);
#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP)
/* after enumerate_interfaces() */
#if defined(HAVE_LINUX_NETWORK)
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp. If we have yet to bind
- ports because of DAD, we need CAP_NET_BIND_SERVICE too. */
- if (is_dad_listeners())
+ ports because of DAD, or we're doing it dynamically,
+ we need CAP_NET_BIND_SERVICE too. */
+ if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
(1 << CAP_SETUID) | (1 << CAP_NET_BIND_SERVICE);
}
#ifdef HAVE_LINUX_NETWORK
- if (is_dad_listeners())
+ if (is_dad_listeners() || option_bool(OPT_CLEVERBIND))
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE);
else
getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
continue;
- if (option_bool(OPT_NOWILD))
+ if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
iface = listener->iface; /* May be NULL */
else
{
break;
}
- if (!iface && !option_bool(OPT_NOWILD))
+ if (!iface && !(option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)))
{
shutdown(confd, SHUT_RDWR);
close(confd);
#define OPT_FQDN_UPDATE 36
#define OPT_RA 37
#define OPT_TFTP_LC 38
-#define OPT_LAST 39
+#define OPT_CLEVERBIND 39
+#define OPT_LAST 40
/* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
if (udpfd != -1)
{
plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
- send_from(udpfd, option_bool(OPT_NOWILD), (char *)header, plen, udpaddr, dst_addr, dst_iface);
+ send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
}
return 0;
{
header->id = htons(forward->orig_id);
header->hb4 |= HB4_RA; /* recursion if available */
- send_from(forward->fd, option_bool(OPT_NOWILD), daemon->packet, nn,
+ send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
free_frec(forward); /* cancel */
dst_addr_4, netmask, now);
if (m >= 1)
{
- send_from(listen->fd, option_bool(OPT_NOWILD), (char *)header,
- m, &source_addr, &dst_addr, if_index);
+ send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
+ (char *)header, m, &source_addr, &dst_addr, if_index);
daemon->local_answer++;
}
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
static struct iovec iov;
static u32 netlink_pid;
-static void nl_err(struct nlmsghdr *h);
+static int nl_async(struct nlmsghdr *h);
static void nl_routechange(struct nlmsghdr *h);
void netlink_init(void)
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = 0; /* autobind */
-#ifdef HAVE_IPV6
- addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
-#else
addr.nl_groups = RTMGRP_IPV4_ROUTE;
+ if (option_bool(OPT_CLEVERBIND))
+ addr.nl_groups |= RTMGRP_IPV4_IFADDR;
+#ifdef HAVE_IPV6
+ addr.nl_groups |= RTMGRP_IPV6_ROUTE;
+ if (daemon->ra_contexts || option_bool(OPT_CLEVERBIND))
+ addr.nl_groups |= RTMGRP_IPV6_IFADDR;
#endif
/* May not be able to have permission to set multicast groups don't die in that case */
struct nlmsghdr *h;
ssize_t len;
static unsigned int seq = 0;
- int callback_ok = 1;
+ int callback_ok = 1, newaddr = 0;
struct {
struct nlmsghdr nlh;
}
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
- if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid)
- nl_routechange(h); /* May be multicast arriving async */
- else if (h->nlmsg_type == NLMSG_ERROR)
- nl_err(h);
+ if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
+ {
+ /* May be multicast arriving async */
+ if (nl_async(h))
+ newaddr = 1;
+ }
else if (h->nlmsg_type == NLMSG_DONE)
- return callback_ok;
+ {
+ /* handle async new interface address arrivals, these have to be done
+ after we complete as we're not re-entrant */
+ if (newaddr)
+ {
+ enumerate_interfaces();
+ create_bound_listeners(0);
+ }
+
+ return callback_ok;
+ }
else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
{
struct ifaddrmsg *ifa = NLMSG_DATA(h);
{
ssize_t len;
struct nlmsghdr *h;
- int flags;
+ int flags, newaddr = 0;
/* don't risk blocking reading netlink messages here. */
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
return;
if ((len = netlink_recv()) != -1)
+ for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
+ if (nl_async(h))
+ newaddr = 1;
+
+ /* restore non-blocking status */
+ fcntl(daemon->netlinkfd, F_SETFL, flags);
+
+ if (newaddr)
{
- 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(h);
+ enumerate_interfaces();
+ create_bound_listeners(0);
}
-
- /* restore non-blocking status */
- fcntl(daemon->netlinkfd, F_SETFL, flags);
}
-static void nl_err(struct nlmsghdr *h)
+static int nl_async(struct nlmsghdr *h)
{
- struct nlmsgerr *err = NLMSG_DATA(h);
-
- if (err->error != 0)
- my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
-}
+ if (h->nlmsg_type == NLMSG_ERROR)
+ {
+ struct nlmsgerr *err = NLMSG_DATA(h);
+ my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
+ return 0;
+ }
+ else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
+ {
+ nl_routechange(h);
+ return 0;
+ }
+ else if (h->nlmsg_type == RTM_NEWADDR)
+ {
+#ifdef HAVE_DHCP6
+ /* force RAs to sync new network and pick up new interfaces. */
+ if (daemon->ra_contexts)
+ {
+ schedule_subnet_map();
+ ra_start_unsolicted(dnsmasq_time(), NULL);
+ /* cause lease_update_file to run after we return, in case we were called from
+ iface_enumerate and can't re-enter it now */
+ send_alarm(0, 0);
+ }
+ return !!option_bool(OPT_CLEVERBIND); /* clever bind mode - rescan */
+ }
+#endif
+ return 0;
+}
+
/* 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. Note that we only accept these messages from the kernel (pid == 0) */
+ failing. */
static void nl_routechange(struct nlmsghdr *h)
{
- if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
+ struct rtmsg *rtm = NLMSG_DATA(h);
+ int fd;
+
+ if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK)
+ return;
+
+ /* Force re-reading resolv file right now, for luck. */
+ daemon->last_resolv = 0;
+
+ if (daemon->srv_save)
{
- struct rtmsg *rtm = NLMSG_DATA(h);
- int fd;
-
- if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK)
+ if (daemon->srv_save->sfd)
+ fd = daemon->srv_save->sfd->fd;
+ else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
+ fd = daemon->rfd_save->fd;
+ else
return;
-
- /* Force re-reading resolv file right now, for luck. */
- daemon->last_resolv = 0;
-#ifdef HAVE_DHCP6
- /* force RAs to sync new network and pick up new interfaces. */
- if (daemon->ra_contexts)
- {
- schedule_subnet_map();
- ra_start_unsolicted(dnsmasq_time(), NULL);
- /* cause lease_update_file to run after we return, in case we were called from
- iface_enumerate and can't re-enter it now */
- send_alarm(0, 0);
- }
-#endif
-
- if (daemon->srv_save)
- {
- if (daemon->srv_save->sfd)
- fd = daemon->srv_save->sfd->fd;
- else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
- fd = daemon->rfd_save->fd;
- else
- return;
-
- while(sendto(fd, daemon->packet, daemon->packet_len, 0,
- &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
- }
+ while(sendto(fd, daemon->packet, daemon->packet_len, 0,
+ &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
}
}
+
#endif
#define LOPT_HOST_REC 308
#define LOPT_TFTP_LC 309
#define LOPT_RR 310
+#define LOPT_CLVERBIND 311
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
{ "enable-ra", 0, 0, LOPT_RA },
{ "dhcp-duid", 1, 0, LOPT_DUID },
{ "host-record", 1, 0, LOPT_HOST_REC },
+ { "bind-dynamic", 0, 0, LOPT_CLVERBIND },
{ NULL, 0, 0, 0 }
};
{ LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
{ LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
{ LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
+ { LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL},
{ 0, 0, NULL, NULL, NULL }
};