diff options
author | Simon Kelley <simon@thekelleys.org.uk> | 2013-12-09 16:50:19 +0000 |
---|---|---|
committer | Simon Kelley <simon@thekelleys.org.uk> | 2013-12-09 16:50:19 +0000 |
commit | 1ee9be4c3f60e16b6815699ba95d67d29beaf015 (patch) | |
tree | 7fb4d0b9d0ea99735122465c09f0cb684bd165f6 | |
parent | 56ad6c9be1b48791edfb140f87c3738dd723d116 (diff) | |
download | dnsmasq-1ee9be4c3f60e16b6815699ba95d67d29beaf015.tar.gz |
Implement dynamic interface discovery on *BSDv2.69test1
-rw-r--r-- | CHANGELOG | 7 | ||||
-rw-r--r-- | src/bpf.c | 106 | ||||
-rw-r--r-- | src/dhcp6.c | 1 | ||||
-rw-r--r-- | src/dnsmasq.c | 24 | ||||
-rw-r--r-- | src/dnsmasq.h | 8 | ||||
-rw-r--r-- | src/netlink.c | 29 | ||||
-rw-r--r-- | src/network.c | 28 |
7 files changed, 160 insertions, 43 deletions
@@ -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 @@ -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 |