Set interface with longest prefix in DHCP & DHCPv6 lease
- 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>
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index dfa9773..2c682a3 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -640,6 +640,8 @@
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 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 @@
#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
diff --git a/src/lease.c b/src/lease.c
index 5d1eefc..9479afc 100644
--- a/src/lease.c
+++ b/src/lease.c
@@ -352,16 +352,21 @@
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 @@
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 @@
#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_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;
diff --git a/src/util.c b/src/util.c
index a503082..c1b0c50 100644
--- a/src/util.c
+++ b/src/util.c
@@ -319,6 +319,18 @@
#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);