]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
Set interface with longest prefix in DHCP & DHCPv6 lease
authorLung-Pin Chang <changlp@cs.nctu.edu.tw>
Wed, 2 Jul 2014 02:48:05 +0000 (10:48 +0800)
committerLung-Pin Chang <changlp@cs.nctu.edu.tw>
Sun, 6 Jul 2014 13:08:47 +0000 (21:08 +0800)
- With nested prefixes reside on different interfaces of single host

  (e.g., in 6to4, 2002::/16 on WAN and 2002:<IPv4>:<subnet>::/64 on LAN),

  current matching mechanism might return the interface with shorter prefix

  length instead of the longer one, if it appears later in the netlink message.

Signed-off-by: Lung-Pin Chang <changlp@cs.nctu.edu.tw>
src/dnsmasq.h
src/lease.c
src/util.c

index dfa97734068fd80a062727bb992c36e535494f3b..2c682a31723ebb91ac6e6f842e1c12b188fd43e9 100644 (file)
@@ -640,6 +640,8 @@ struct dhcp_lease {
   unsigned char *extradata;
   unsigned int extradata_len, extradata_size;
   int last_interface;
+  int new_interface;     /* save possible originated interface */
+  int new_prefixlen;     /* and its prefix length */
 #ifdef HAVE_DHCP6
   struct in6_addr addr6;
   int iaid;
@@ -1131,6 +1133,7 @@ int sa_len(union mysockaddr *addr);
 int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
 int hostname_isequal(const char *a, const char *b);
 time_t dnsmasq_time(void);
+int netmask_length(struct in_addr mask);
 int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
 #ifdef HAVE_IPV6
 int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
@@ -1242,6 +1245,7 @@ char *host_from_dns(struct in_addr addr);
 #ifdef HAVE_DHCP
 void lease_update_file(time_t now);
 void lease_update_dns(int force);
+void lease_update_interface(time_t now);
 void lease_init(time_t now);
 struct dhcp_lease *lease4_allocate(struct in_addr addr);
 #ifdef HAVE_DHCP6
index 5d1eefc0d1177080260432e7c4a752ffb28935c6..9479afc2623ece304ac814e64b87818bfa9166a9 100644 (file)
@@ -352,16 +352,21 @@ 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;
-  
+  int prefix;
+
   (void) label;
   (void) broadcast;
   (void) vparam;
 
   for (lease = leases; lease; lease = lease->next)
-    if (!(lease->flags & (LEASE_TA | LEASE_NA)))
-      if (is_same_net(local, lease->addr, netmask))
-       lease_set_interface(lease, if_index, *((time_t *)vparam));
-  
+    if (!(lease->flags & (LEASE_TA | LEASE_NA))) {
+      prefix = netmask_length(netmask);
+      if (is_same_net(local, lease->addr, netmask) && prefix > lease->new_prefixlen) {
+        lease->new_interface = if_index;
+        lease->new_prefixlen = prefix;
+      }
+    }
+
   return 1;
 }
 
@@ -371,17 +376,23 @@ static int find_interface_v6(struct in6_addr *local,  int prefix,
                             int preferred, int valid, void *vparam)
 {
   struct dhcp_lease *lease;
-  
+
   (void)scope;
   (void)flags;
   (void)preferred;
   (void)valid;
+  (void)vparam;
 
   for (lease = leases; lease; lease = lease->next)
     if ((lease->flags & (LEASE_TA | LEASE_NA)))
-      if (is_same_net6(local, &lease->addr6, prefix))
-       lease_set_interface(lease, if_index, *((time_t *)vparam));
-  
+      if (is_same_net6(local, &lease->addr6, prefix) && prefix > lease->new_prefixlen) {
+        /* save prefix length for comparison, as we might get shorter matching
+         * prefix in upcoming netlink GETADDR responses
+         * */
+        lease->new_interface = if_index;
+        lease->new_prefixlen = prefix;
+      }
+
   return 1;
 }
 
@@ -418,6 +429,7 @@ void lease_find_interfaces(time_t now)
 #ifdef HAVE_DHCP6
   iface_enumerate(AF_INET6, &now, find_interface_v6);
 #endif
+  lease_update_interface(now);
 }
 
 #ifdef HAVE_DHCP6
@@ -492,6 +504,16 @@ void lease_update_dns(int force)
     }
 }
 
+void lease_update_interface(time_t now)
+{
+  struct dhcp_lease *lease;
+
+  for (lease = leases; lease; lease = lease->next)
+    if (lease->new_interface > 0) {
+      lease_set_interface(lease, lease->new_interface, now);
+    }
+}
+
 void lease_prune(struct dhcp_lease *target, time_t now)
 {
   struct dhcp_lease *lease, *tmp, **up;
index a5030821d3e36dbcdb0ae480404fc99fdaaeb452..c1b0c5037000d312e92fcf10caf5cbf0d72c741b 100644 (file)
@@ -319,6 +319,18 @@ time_t dnsmasq_time(void)
 #endif
 }
 
+int netmask_length(struct in_addr mask)
+{
+  int zero_count = 0;
+
+  while (0x0 == (mask.s_addr & 0x1)) {
+    mask.s_addr >>= 1;
+    ++zero_count;
+  }
+
+  return 32 - zero_count;
+}
+
 int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
 {
   return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);