Commit dc8a1b1b authored by Lung-Pin Chang's avatar Lung-Pin Chang

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: default avatarLung-Pin Chang <changlp@cs.nctu.edu.tw>
parent cdb755c5
...@@ -640,6 +640,8 @@ struct dhcp_lease { ...@@ -640,6 +640,8 @@ struct dhcp_lease {
unsigned char *extradata; unsigned char *extradata;
unsigned int extradata_len, extradata_size; unsigned int extradata_len, extradata_size;
int last_interface; int last_interface;
int new_interface; /* save possible originated interface */
int new_prefixlen; /* and its prefix length */
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
struct in6_addr addr6; struct in6_addr addr6;
int iaid; int iaid;
...@@ -1131,6 +1133,7 @@ int sa_len(union mysockaddr *addr); ...@@ -1131,6 +1133,7 @@ int sa_len(union mysockaddr *addr);
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2); int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
int hostname_isequal(const char *a, const char *b); int hostname_isequal(const char *a, const char *b);
time_t dnsmasq_time(void); 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); int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen); 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); ...@@ -1242,6 +1245,7 @@ char *host_from_dns(struct in_addr addr);
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
void lease_update_file(time_t now); void lease_update_file(time_t now);
void lease_update_dns(int force); void lease_update_dns(int force);
void lease_update_interface(time_t now);
void lease_init(time_t now); void lease_init(time_t now);
struct dhcp_lease *lease4_allocate(struct in_addr addr); struct dhcp_lease *lease4_allocate(struct in_addr addr);
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
......
...@@ -352,16 +352,21 @@ static int find_interface_v4(struct in_addr local, int if_index, char *label, ...@@ -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 in_addr netmask, struct in_addr broadcast, void *vparam)
{ {
struct dhcp_lease *lease; struct dhcp_lease *lease;
int prefix;
(void) label; (void) label;
(void) broadcast; (void) broadcast;
(void) vparam; (void) vparam;
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
if (!(lease->flags & (LEASE_TA | LEASE_NA))) if (!(lease->flags & (LEASE_TA | LEASE_NA))) {
if (is_same_net(local, lease->addr, netmask)) prefix = netmask_length(netmask);
lease_set_interface(lease, if_index, *((time_t *)vparam)); if (is_same_net(local, lease->addr, netmask) && prefix > lease->new_prefixlen) {
lease->new_interface = if_index;
lease->new_prefixlen = prefix;
}
}
return 1; return 1;
} }
...@@ -371,17 +376,23 @@ static int find_interface_v6(struct in6_addr *local, int prefix, ...@@ -371,17 +376,23 @@ static int find_interface_v6(struct in6_addr *local, int prefix,
int preferred, int valid, void *vparam) int preferred, int valid, void *vparam)
{ {
struct dhcp_lease *lease; struct dhcp_lease *lease;
(void)scope; (void)scope;
(void)flags; (void)flags;
(void)preferred; (void)preferred;
(void)valid; (void)valid;
(void)vparam;
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
if ((lease->flags & (LEASE_TA | LEASE_NA))) if ((lease->flags & (LEASE_TA | LEASE_NA)))
if (is_same_net6(local, &lease->addr6, prefix)) if (is_same_net6(local, &lease->addr6, prefix) && prefix > lease->new_prefixlen) {
lease_set_interface(lease, if_index, *((time_t *)vparam)); /* 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; return 1;
} }
...@@ -418,6 +429,7 @@ void lease_find_interfaces(time_t now) ...@@ -418,6 +429,7 @@ void lease_find_interfaces(time_t now)
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
iface_enumerate(AF_INET6, &now, find_interface_v6); iface_enumerate(AF_INET6, &now, find_interface_v6);
#endif #endif
lease_update_interface(now);
} }
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
...@@ -492,6 +504,16 @@ void lease_update_dns(int force) ...@@ -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) void lease_prune(struct dhcp_lease *target, time_t now)
{ {
struct dhcp_lease *lease, *tmp, **up; struct dhcp_lease *lease, *tmp, **up;
......
...@@ -319,6 +319,18 @@ time_t dnsmasq_time(void) ...@@ -319,6 +319,18 @@ time_t dnsmasq_time(void)
#endif #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) 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); return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment