From 3f2873d42c4d7e7dba32b6e64a3687d43928bc8e Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 14 May 2013 11:28:47 +0100
Subject: [PATCH] Handle IPv4 interface-address labels in Linux.
---
CHANGELOG | 9 +++++++++
src/bpf.c | 2 +-
src/dhcp.c | 14 +++++++++-----
src/dnsmasq.h | 1 +
src/forward.c | 3 ++-
src/lease.c | 3 ++-
src/netlink.c | 7 +++++--
src/network.c | 39 +++++++++++++++++++++++++++++++--------
src/tftp.c | 3 ++-
9 files changed, 62 insertions(+), 19 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index f6ce80e..7aa0024 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -21,6 +21,15 @@ version 2.67
Fix --dhcp-match, --dhcp-vendorclass and --dhcp-userclass
to work with BOOTP and well as DHCP. Thanks to Peter
Korsgaard for spotting the problem.
+
+ Handle IPv4 interface-address labels in Linux. These are
+ often used to emulate the old IP-alias addresses. Before,
+ using --interface=eth0 would service all the addresses of
+ eth0, including ones configured as aliases, which appear
+ in ifconfig as eth0:0. Now, only addresses with the label
+ eth0 are active. This is not backwards compatible: if you
+ want to continue to bind the aliases too, you need to add
+ eg. --interface=eth0:0 to the config.
version 2.66
diff --git a/src/bpf.c b/src/bpf.c
index 02a3abb..e75b0c6 100644
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -123,7 +123,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
else
broadcast.s_addr = 0;
- if (!((*callback)(addr, iface_index, netmask, broadcast, parm)))
+ if (!((*callback)(addr, iface_index, NULL, netmask, broadcast, parm)))
goto err;
}
#ifdef HAVE_IPV6
diff --git a/src/dhcp.c b/src/dhcp.c
index dd25632..333a327 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -28,9 +28,9 @@ struct match_param {
struct in_addr netmask, broadcast, addr;
};
-static int complete_context(struct in_addr local, int if_index,
+static int complete_context(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
-static int check_listen_addrs(struct in_addr local, int if_index,
+static int check_listen_addrs(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int make_fd(int port)
@@ -287,7 +287,7 @@ void dhcp_packet(time_t now, int pxe_fd)
iface_addr = match.addr;
/* make sure secondary address gets priority in case
there is more than one address on the interface in the same subnet */
- complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm);
+ complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
}
if (!iface_enumerate(AF_INET, &parm, complete_context))
@@ -411,12 +411,14 @@ void dhcp_packet(time_t now, int pxe_fd)
}
/* check against secondary interface addresses */
-static int check_listen_addrs(struct in_addr local, int if_index,
+static int check_listen_addrs(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
struct match_param *param = vparam;
struct iname *tmp;
+ (void) label;
+
if (if_index == param->ind)
{
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
@@ -444,11 +446,13 @@ static int check_listen_addrs(struct in_addr local, int if_index,
Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
-static int complete_context(struct in_addr local, int if_index,
+static int complete_context(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
struct dhcp_context *context;
struct iface_param *param = vparam;
+
+ (void)label;
for (context = daemon->dhcp; context; context = context->next)
{
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index e177cea..8866dd8 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1030,6 +1030,7 @@ void create_bound_listeners(int die);
int is_dad_listeners(void);
int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns);
int loopback_exception(int fd, int family, struct all_addr *addr, char *name);
+int label_exception(int index, int family, struct all_addr *addr);
int fix_fd(int fd);
int tcp_interface(int fd, int af);
struct in_addr get_ifaddr(char *intr);
diff --git a/src/forward.c b/src/forward.c
index 78495ca..28fe9eb 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -789,7 +789,8 @@ void receive_query(struct listener *listen, time_t now)
{
if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
- if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name))
+ if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name) &&
+ !label_exception(if_index, listen->family, &dst_addr))
return;
}
diff --git a/src/lease.c b/src/lease.c
index a4560ba..b85cf57 100644
--- a/src/lease.c
+++ b/src/lease.c
@@ -345,11 +345,12 @@ void lease_update_file(time_t now)
}
-static int find_interface_v4(struct in_addr local, int if_index,
+static int find_interface_v4(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
struct dhcp_lease *lease;
+ (void) label;
(void) broadcast;
(void) vparam;
diff --git a/src/netlink.c b/src/netlink.c
index 0881b71..78d0926 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -215,7 +215,8 @@ int iface_enumerate(int family, void *parm, int (*callback)())
if (ifa->ifa_family == AF_INET)
{
struct in_addr netmask, addr, broadcast;
-
+ char *label = NULL;
+
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
addr.s_addr = 0;
broadcast.s_addr = 0;
@@ -226,12 +227,14 @@ int iface_enumerate(int family, void *parm, int (*callback)())
addr = *((struct in_addr *)(rta+1));
else if (rta->rta_type == IFA_BROADCAST)
broadcast = *((struct in_addr *)(rta+1));
+ else if (rta->rta_type == IFA_LABEL)
+ label = RTA_DATA(rta);
rta = RTA_NEXT(rta, len1);
}
if (addr.s_addr && callback_ok)
- if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
+ if (!((*callback)(addr, ifa->ifa_index, label, netmask, broadcast, parm)))
callback_ok = 0;
}
#ifdef HAVE_IPV6
diff --git a/src/network.c b/src/network.c
index 792914b..473e85f 100644
--- a/src/network.c
+++ b/src/network.c
@@ -204,7 +204,27 @@ int loopback_exception(int fd, int family, struct all_addr *addr, char *name)
return 0;
}
-static int iface_allowed(struct irec **irecp, int if_index,
+/* If we're configured with something like --interface=eth0:0 then we'll listen correctly
+ on the relevant address, but the name of the arrival interface, derived from the
+ index won't match the config. Check that we found an interface address for the arrival
+ interface: daemon->interfaces must be up-to-date. */
+int label_exception(int index, int family, struct all_addr *addr)
+{
+ struct irec *iface;
+
+ /* labels only supported on IPv4 addresses. */
+ if (family != AF_INET)
+ return 0;
+
+ for (iface = daemon->interfaces; iface; iface = iface->next)
+ if (iface->index == index && iface->addr.sa.sa_family == AF_INET &&
+ iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
+ return 1;
+
+ return 0;
+}
+
+static int iface_allowed(struct irec **irecp, int if_index, char *label,
union mysockaddr *addr, struct in_addr netmask, int dad)
{
struct irec *iface;
@@ -242,8 +262,8 @@ static int iface_allowed(struct irec **irecp, int if_index,
loopback = ifr.ifr_flags & IFF_LOOPBACK;
if (loopback)
- dhcp_ok = 0;
-
+ dhcp_ok = 0;
+
if (ioctl(fd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
@@ -272,13 +292,16 @@ static int iface_allowed(struct irec **irecp, int if_index,
}
}
+ if (!label)
+ label = ifr.ifr_name;
+
if (addr->sa.sa_family == AF_INET &&
- !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, &auth_dns))
+ !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, label, &auth_dns))
return 1;
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
- !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, &auth_dns))
+ !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, label, &auth_dns))
return 1;
#endif
@@ -348,11 +371,11 @@ static int iface_allowed_v6(struct in6_addr *local, int prefix,
addr.in6.sin6_port = htons(daemon->port);
addr.in6.sin6_scope_id = if_index;
- return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, !!(flags & IFACE_TENTATIVE));
+ return iface_allowed((struct irec **)vparam, if_index, NULL, &addr, netmask, !!(flags & IFACE_TENTATIVE));
}
#endif
-static int iface_allowed_v4(struct in_addr local, int if_index,
+static int iface_allowed_v4(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
union mysockaddr addr;
@@ -366,7 +389,7 @@ static int iface_allowed_v4(struct in_addr local, int if_index,
addr.in.sin_addr = local;
addr.in.sin_port = htons(daemon->port);
- return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, 0);
+ return iface_allowed((struct irec **)vparam, if_index, label, &addr, netmask, 0);
}
int enumerate_interfaces(void)
diff --git a/src/tftp.c b/src/tftp.c
index 960b1ee..d7d050f 100644
--- a/src/tftp.c
+++ b/src/tftp.c
@@ -202,7 +202,8 @@ void tftp_request(struct listener *listen, time_t now)
{
if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
- if (!loopback_exception(listen->tftpfd, listen->family, &addra, name))
+ if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) &&
+ !label_exception(if_index, listen->family, &addra) )
return;
}
--
1.8.1.4