summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2013-12-09 16:50:19 +0000
committerSimon Kelley <simon@thekelleys.org.uk>2013-12-09 16:50:19 +0000
commit1ee9be4c3f60e16b6815699ba95d67d29beaf015 (patch)
tree7fb4d0b9d0ea99735122465c09f0cb684bd165f6
parent56ad6c9be1b48791edfb140f87c3738dd723d116 (diff)
downloaddnsmasq-1ee9be4c3f60e16b6815699ba95d67d29beaf015.tar.gz
Implement dynamic interface discovery on *BSDv2.69test1
-rw-r--r--CHANGELOG7
-rw-r--r--src/bpf.c106
-rw-r--r--src/dhcp6.c1
-rw-r--r--src/dnsmasq.c24
-rw-r--r--src/dnsmasq.h8
-rw-r--r--src/netlink.c29
-rw-r--r--src/network.c28
7 files changed, 160 insertions, 43 deletions
diff --git a/CHANGELOG b/CHANGELOG
index bc402bb..39dbe17 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,10 @@
+version 2.69
+ Implement dynamic interface discovery on *BSD. This allows
+ the contructor: syntax to be used in dhcp-range for DHCPv6
+ on the BSD platform. Thanks to Matthias Andree for
+ valuable research on how to implement this.
+
+
version 2.68
Use random addresses for DHCPv6 temporary address
allocations, instead of algorithmically determined stable
diff --git a/src/bpf.c b/src/bpf.c
index 98a073b..a3c5ad4 100644
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -19,9 +19,9 @@
#if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
#include <ifaddrs.h>
-#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
#include <sys/param.h>
#include <sys/sysctl.h>
+#include <net/if.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <netinet/if_ether.h>
@@ -29,7 +29,9 @@
# include <net/if_var.h>
#endif
#include <netinet/in_var.h>
-#include <netinet6/in6_var.h>
+#ifdef HAVE_IPV6
+# include <netinet6/in6_var.h>
+#endif
#ifndef SA_SIZE
#define SA_SIZE(sa) \
@@ -38,6 +40,13 @@
1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
#endif
+#ifdef HAVE_BSD_NETWORK
+static int del_family = 0;
+static struct all_addr del_addr;
+#endif
+
+#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
+
int arp_enumerate(void *parm, int (*callback)())
{
int mib[6];
@@ -88,7 +97,7 @@ int arp_enumerate(void *parm, int (*callback)())
return 1;
}
-#endif
+#endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */
int iface_enumerate(int family, void *parm, int (*callback)())
@@ -129,6 +138,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
{
struct in_addr addr, netmask, broadcast;
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
+#ifdef HAVE_BSD_NETWORK
+ if (del_family == AF_INET && del_addr.addr.addr4.s_addr == addr.s_addr)
+ continue;
+#endif
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
if (addrs->ifa_broadaddr)
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
@@ -146,6 +159,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
int i, j, prefix = 0;
u32 valid = 0xffffffff, preferred = 0xffffffff;
int flags = 0;
+#ifdef HAVE_BSD_NETWORK
+ if (del_family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&del_addr.addr.addr6, addr))
+ continue;
+#endif
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
struct in6_ifreq ifr6;
@@ -226,7 +243,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
return ret;
}
-#endif
+#endif /* defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK) */
#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
@@ -345,6 +362,87 @@ void send_via_bpf(struct dhcp_packet *mess, size_t len,
while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
}
+#endif /* defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP) */
+
+
+#ifdef HAVE_BSD_NETWORK
+
+void route_init(void)
+{
+ /* AF_UNSPEC: all addr families */
+ daemon->routefd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
+
+ if (daemon->routefd == -1 || !fix_fd(daemon->routefd))
+ die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
+}
+
+void route_sock(time_t now)
+{
+ struct if_msghdr *msg;
+ int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
+
+ if (rc < 4)
+ return;
+
+ msg = (struct if_msghdr *)daemon->packet;
+
+ if (rc < msg->ifm_msglen)
+ return;
+
+ if (msg->ifm_version != RTM_VERSION)
+ {
+ static int warned = 0;
+ if (!warned)
+ {
+ my_syslog(LOG_WARNING, _("Unknown protocol version from route socket"));
+ warned = 1;
+ }
+ }
+ else if (msg->ifm_type == RTM_NEWADDR)
+ {
+ del_family = 0;
+ newaddress(now);
+ }
+ else if (msg->ifm_type == RTM_DELADDR)
+ {
+ /* There's a race in the kernel, such that if we run iface_enumerate() immediately
+ we get a DELADDR event, the deleted address still appears. Here we store the deleted address
+ in a static variable, and omit it from the set returned by iface_enumerate() */
+ int mask = ((struct ifa_msghdr *)msg)->ifam_addrs;
+ int maskvec[] = { RTA_DST, RTA_GATEWAY, RTA_NETMASK, RTA_GENMASK,
+ RTA_IFP, RTA_IFA, RTA_AUTHOR, RTA_BRD };
+ int of;
+ unsigned int i;
+
+ for (i = 0, of = sizeof(struct ifa_msghdr); of < rc && i < sizeof(maskvec)/sizeof(maskvec[0]); i++)
+ if (mask & maskvec[i])
+ {
+ struct sockaddr *sa = (struct sockaddr *)((char *)msg + of);
+ size_t diff = (sa->sa_len != 0) ? sa->sa_len : sizeof(long);
+
+ if (maskvec[i] == RTA_IFA)
+ {
+ del_family = sa->sa_family;
+ if (del_family == AF_INET)
+ del_addr.addr.addr4 = ((struct sockaddr_in *)sa)->sin_addr;
+#ifdef HAVE_IPV6
+ else if (del_family == AF_INET6)
+ del_addr.addr.addr6 = ((struct sockaddr_in6 *)sa)->sin6_addr;
#endif
+ else
+ del_family = 0;
+ }
+
+ of += diff;
+ /* round up as needed */
+ if (diff & (sizeof(long) - 1))
+ of += sizeof(long) - (diff & (sizeof(long) - 1));
+ }
+
+ newaddress(now);
+ }
+}
+
+#endif /* HAVE_BSD_NETWORK */
diff --git a/src/dhcp6.c b/src/dhcp6.c
index 8c762fd..9d4f450 100644
--- a/src/dhcp6.c
+++ b/src/dhcp6.c
@@ -720,7 +720,6 @@ void dhcp_construct_contexts(time_t now)
if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
{
-
if ((context->flags & (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)) ||
option_bool(OPT_RA))
{
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 4c8cf8a..0b31d68 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -182,7 +182,7 @@ int main (int argc, char **argv)
daemon->doing_dhcp6 = 1;
if (context->flags & CONTEXT_RA)
daemon->doing_ra = 1;
-#ifndef HAVE_LINUX_NETWORK
+#if !defined(HAVE_LINUX_NETWORK) && !defined(HAVE_BSD_NETWORK)
if (context->flags & CONTEXT_TEMPLATE)
die (_("dhcp-range constructor not available on this platform"), NULL, EC_BADCONF);
#endif
@@ -220,13 +220,15 @@ int main (int argc, char **argv)
ipset_init();
#endif
-#ifdef HAVE_LINUX_NETWORK
+#if defined(HAVE_LINUX_NETWORK)
netlink_init();
-
- if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
- die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
+#elif defined(HAVE_BSD_NETWORK)
+ route_init();
#endif
+ if (option_bool(OPT_NOWILD) && option_bool(OPT_CLEVERBIND))
+ die(_("cannot set --bind-interfaces and --bind-dynamic"), NULL, EC_BADCONF);
+
if (!enumerate_interfaces(1) || !enumerate_interfaces(0))
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
@@ -808,11 +810,14 @@ int main (int argc, char **argv)
}
#endif
-#ifdef HAVE_LINUX_NETWORK
+#if defined(HAVE_LINUX_NETWORK)
FD_SET(daemon->netlinkfd, &rset);
bump_maxfd(daemon->netlinkfd, &maxfd);
+#elif defined(HAVE_BSD_NETWORK)
+ FD_SET(daemon->routefd, &rset);
+ bump_maxfd(daemon->routefd, &maxfd);
#endif
-
+
FD_SET(piperead, &rset);
bump_maxfd(piperead, &maxfd);
@@ -867,9 +872,12 @@ int main (int argc, char **argv)
warn_bound_listeners();
}
-#ifdef HAVE_LINUX_NETWORK
+#if defined(HAVE_LINUX_NETWORK)
if (FD_ISSET(daemon->netlinkfd, &rset))
netlink_multicast(now);
+#elif defined(HAVE_BSD_NETWORK)
+ if (FD_ISSET(daemon->routefd, &rset))
+ route_sock(now);
#endif
/* Check for changes to resolv files once per second max. */
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 0b9c7dd..8bd3ddf 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -900,7 +900,7 @@ extern struct daemon {
#if defined(HAVE_LINUX_NETWORK)
int netlinkfd;
#elif defined(HAVE_BSD_NETWORK)
- int dhcp_raw_fd, dhcp_icmp_fd;
+ int dhcp_raw_fd, dhcp_icmp_fd, routefd;
#endif
struct iovec dhcp_packet;
char *dhcp_buff, *dhcp_buff2, *dhcp_buff3;
@@ -1090,6 +1090,10 @@ int set_ipv6pktinfo(int fd);
#ifdef HAVE_DHCP6
void join_multicast(int dienow);
#endif
+#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_BSD_NETWORK)
+void newaddress(time_t now);
+#endif
+
/* dhcp.c */
#ifdef HAVE_DHCP
@@ -1177,6 +1181,8 @@ void netlink_multicast(time_t now);
void init_bpf(void);
void send_via_bpf(struct dhcp_packet *mess, size_t len,
struct in_addr iface_addr, struct ifreq *ifr);
+void route_init(void);
+void route_sock(time_t now);
#endif
/* bpf.c or netlink.c */
diff --git a/src/netlink.c b/src/netlink.c
index d093988..3be94ee 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -39,7 +39,6 @@ static struct iovec iov;
static u32 netlink_pid;
static int nl_async(struct nlmsghdr *h);
-static void nl_newaddress(time_t now);
void netlink_init(void)
{
@@ -203,7 +202,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
/* handle async new interface address arrivals, these have to be done
after we complete as we're not re-entrant */
if (newaddr)
- nl_newaddress(dnsmasq_time());
+ newaddress(dnsmasq_time());
return callback_ok;
}
@@ -351,7 +350,7 @@ void netlink_multicast(time_t now)
fcntl(daemon->netlinkfd, F_SETFL, flags);
if (newaddr)
- nl_newaddress(now);
+ newaddress(now);
}
static int nl_async(struct nlmsghdr *h)
@@ -399,30 +398,6 @@ static int nl_async(struct nlmsghdr *h)
return 0;
}
-
-static void nl_newaddress(time_t now)
-{
- (void)now;
-
- if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
- enumerate_interfaces(0);
-
- if (option_bool(OPT_CLEVERBIND))
- create_bound_listeners(0);
-
-#ifdef HAVE_DHCP6
- if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
- join_multicast(0);
-
- if (daemon->doing_dhcp6 || daemon->doing_ra)
- dhcp_construct_contexts(now);
-
- if (daemon->doing_dhcp6)
- lease_find_interfaces(now);
-#endif
-}
-
-
#endif
diff --git a/src/network.c b/src/network.c
index 3a6cad2..fd49b5c 100644
--- a/src/network.c
+++ b/src/network.c
@@ -489,7 +489,7 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix,
addr.in6.sin6_scope_id = if_index;
else
addr.in6.sin6_scope_id = 0;
-
+
return iface_allowed((struct iface_param *)vparam, if_index, NULL, &addr, netmask, prefix, !!(flags & IFACE_TENTATIVE));
}
#endif
@@ -681,7 +681,7 @@ static int make_sock(union mysockaddr *addr, int type, int dienow)
close (fd);
errno = errsav;
-
+
if (dienow)
{
/* failure to bind addresses given by --listen-address at this point
@@ -1470,7 +1470,31 @@ int reload_servers(char *fname)
return gotone;
}
+#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_BSD_NETWORK)
+/* Called when addresses are added or deleted from an interface */
+void newaddress(time_t now)
+{
+ (void)now;
+
+ if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
+ enumerate_interfaces(0);
+
+ if (option_bool(OPT_CLEVERBIND))
+ create_bound_listeners(0);
+
+#ifdef HAVE_DHCP6
+ if (daemon->doing_dhcp6 || daemon->relay6 || daemon->doing_ra)
+ join_multicast(0);
+
+ if (daemon->doing_dhcp6 || daemon->doing_ra)
+ dhcp_construct_contexts(now);
+
+ if (daemon->doing_dhcp6)
+ lease_find_interfaces(now);
+#endif
+}
+#endif