import of dnsmasq-2.34.tar.gz
diff --git a/contrib/wrt/dhcp_release.c b/contrib/wrt/dhcp_release.c
index 2ee98c2..c66d3a0 100644
--- a/contrib/wrt/dhcp_release.c
+++ b/contrib/wrt/dhcp_release.c
@@ -44,6 +44,10 @@
 #include <stdlib.h>
 #include <net/if_arp.h>
 #include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <errno.h>
 
 #define DHCP_CHADDR_MAX          16
 #define BOOTREQUEST              1
@@ -69,6 +73,72 @@
   unsigned char options[308];
 };
 
+static struct iovec iov;
+
+static int expand_buf(struct iovec *iov, size_t size)
+{
+  void *new;
+
+  if (size <= iov->iov_len)
+    return 1;
+
+  if (!(new = malloc(size)))
+    {
+      errno = ENOMEM;
+      return 0;
+    }
+
+  if (iov->iov_base)
+    {
+      memcpy(new, iov->iov_base, iov->iov_len);
+      free(iov->iov_base);
+    }
+
+  iov->iov_base = new;
+  iov->iov_len = size;
+
+  return 1;
+}
+
+static ssize_t netlink_recv(int fd)
+{
+  struct msghdr msg;
+  ssize_t rc;
+
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+    
+  while (1)
+    {
+      msg.msg_flags = 0;
+      while ((rc = recvmsg(fd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
+      
+      /* 2.2.x doesn't suport MSG_PEEK at all, returning EOPNOTSUPP, so we just grab a 
+         big buffer and pray in that case. */
+      if (rc == -1 && errno == EOPNOTSUPP)
+        {
+          if (!expand_buf(&iov, 2000))
+            return -1;
+          break;
+        }
+      
+      if (rc == -1 || !(msg.msg_flags & MSG_TRUNC))
+        break;
+            
+      if (!expand_buf(&iov, iov.iov_len + 100))
+        return -1;
+    }
+
+  /* finally, read it for real */
+  while ((rc = recvmsg(fd, &msg, 0)) == -1 && errno == EINTR);
+  
+  return rc;
+}
+
 static int parse_hex(char *in, unsigned char *out, int maxlen, int *mac_type)
 {
   int i = 0;
@@ -103,6 +173,78 @@
     return i;
 }
 
+static 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);
+}
+
+static struct in_addr find_interface(struct in_addr client, int fd, int index)
+{
+  struct sockaddr_nl addr;
+  struct nlmsghdr *h;
+  ssize_t len;
+ 
+  struct {
+    struct nlmsghdr nlh;
+    struct rtgenmsg g; 
+  } req;
+
+  addr.nl_family = AF_NETLINK;
+  addr.nl_pad = 0;
+  addr.nl_groups = 0;
+  addr.nl_pid = 0; /* address to kernel */
+
+  req.nlh.nlmsg_len = sizeof(req);
+  req.nlh.nlmsg_type = RTM_GETADDR;
+  req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK; 
+  req.nlh.nlmsg_pid = 0;
+  req.nlh.nlmsg_seq = 1;
+  req.g.rtgen_family = AF_INET; 
+
+  if (sendto(fd, (void *)&req, sizeof(req), 0, 
+	     (struct sockaddr *)&addr, sizeof(addr)) == -1)
+    {
+      perror("sendto failed");
+      exit(1);
+    }
+  
+  while (1)
+    {
+      if ((len = netlink_recv(fd)) == -1)
+	{
+	  perror("netlink");
+	  exit(1);
+	}
+
+      for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
+	if (h->nlmsg_type == NLMSG_DONE)
+	  exit(0);
+	else if (h->nlmsg_type == RTM_NEWADDR)
+          {
+            struct ifaddrmsg *ifa = NLMSG_DATA(h);  
+            struct rtattr *rta;
+            unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
+            
+            if (ifa->ifa_index == index && ifa->ifa_family == AF_INET)
+              {
+                struct in_addr netmask, addr;
+                
+                netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
+                addr.s_addr = 0;
+                
+                for (rta = IFA_RTA(ifa); RTA_OK(rta, len1); rta = RTA_NEXT(rta, len1))
+		  if (rta->rta_type == IFA_LOCAL)
+		    addr = *((struct in_addr *)(rta+1));
+		
+                if (addr.s_addr && is_same_net(addr, client, netmask))
+		  return addr;
+	      }
+	  }
+    }
+ 
+  exit(0);
+}
+
 int main(int argc, char **argv)
 { 
   struct in_addr server, lease;
@@ -112,6 +254,11 @@
   struct sockaddr_in dest;
   struct ifreq ifr;
   int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  int nl = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+  struct iovec iov;
+ 
+  iov.iov_len = 200;
+  iov.iov_base = malloc(iov.iov_len);
 
   if (argc < 4 || argc > 5)
     { 
@@ -119,7 +266,7 @@
       exit(1);
     }
 
-  if (fd == -1)
+  if (fd == -1 || nl == -1)
     {
       perror("cannot create socket");
       exit(1);
@@ -128,15 +275,15 @@
   /* This voodoo fakes up a packet coming from the correct interface, which really matters for 
      a DHCP server */
   strcpy(ifr.ifr_name, argv[1]);
-  ifr.ifr_addr.sa_family = AF_INET;
-  if (ioctl(fd, SIOCGIFADDR, &ifr) == -1 || setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
+  if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
     {
       perror("cannot setup interface");
       exit(1);
     }
   
-  server = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+  
   lease.s_addr = inet_addr(argv[2]);
+  server = find_interface(lease, nl, if_nametoindex(argv[1]));
   
   memset(&packet, 0, sizeof(packet));
  
@@ -174,7 +321,7 @@
   dest.sin_addr = server;
 
   if (sendto(fd, &packet, sizeof(packet), 0, 
-	     (struct sockaddr *)&dest, sizeof(dest)) == 1)
+	     (struct sockaddr *)&dest, sizeof(dest)) == -1)
     {
       perror("sendto failed");
       exit(1);