Blob Blame History Raw
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