First complete version of DNS-client-id EDNS0 and ARP tracking code.
diff --git a/src/arp.c b/src/arp.c
index b624dac..f41cdec 100644
--- a/src/arp.c
+++ b/src/arp.c
@@ -16,26 +16,31 @@
 
 #include "dnsmasq.h"
 
-#define ARP_FREE  0
-#define ARP_FOUND 1
-#define ARP_NEW   2
-#define ARP_EMPTY 3
+/* Time between forced re-loads from kernel. */
+#define INTERVAL 90
+
+#define ARP_MARK  0
+#define ARP_FOUND 1  /* Confirmed */
+#define ARP_NEW   2  /* Newly created */
+#define ARP_EMPTY 3  /* No MAC addr */
 
 struct arp_record {
-  short hwlen, status;
+  unsigned short hwlen, status;
   int family;
   unsigned char hwaddr[DHCP_CHADDR_MAX]; 
   struct all_addr addr;
   struct arp_record *next;
 };
 
-static struct arp_record *arps = NULL, *old = NULL;
+static struct arp_record *arps = NULL, *old = NULL, *freelist = NULL;
+static time_t last = 0;
 
 static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
 {
-  int match = 0;
   struct arp_record *arp;
 
+  (void)parmv;
+
   if (maclen > DHCP_CHADDR_MAX)
     return 1;
 
@@ -58,16 +63,18 @@
 	}
 #endif
 
-      if (arp->status != ARP_EMPTY && arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0)
-	arp->status = ARP_FOUND;
-      else
+      if (arp->status == ARP_EMPTY)
 	{
-	  /* existing address, MAC changed or arrived new. */
+	  /* existing address, was negative. */
 	  arp->status = ARP_NEW;
 	  arp->hwlen = maclen;
-	  arp->family = family;
 	  memcpy(arp->hwaddr, mac, maclen);
 	}
+      else if (arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0)
+	/* Existing entry matches - confirm. */
+	arp->status = ARP_FOUND;
+      else
+	continue;
       
       break;
     }
@@ -75,10 +82,10 @@
   if (!arp)
     {
       /* New entry */
-      if (old)
+      if (freelist)
 	{
-	  arp = old;
-	  old = old->next;
+	  arp = freelist;
+	  freelist = freelist->next;
 	}
       else if (!(arp = whine_malloc(sizeof(struct arp_record))))
 	return 1;
@@ -101,81 +108,72 @@
 }
 
 /* If in lazy mode, we cache absence of ARP entries. */
-int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy)
+int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
 {
   struct arp_record *arp, **up;
   int updated = 0;
 
  again:
   
-  for (arp = arps; arp; arp = arp->next)
-    {
-      if (addr->sa.sa_family == arp->family)
-	{
-	  if (arp->addr.addr.addr4.s_addr != addr->in.sin_addr.s_addr)
-	    continue;
-	}
+  /* If the database is less then INTERVAL old, look in there */
+  if (difftime(now, last) < INTERVAL)
+    for (arp = arps; arp; arp = arp->next)
+      {
+	if (addr->sa.sa_family == arp->family)
+	  {
+	    if (arp->addr.addr.addr4.s_addr != addr->in.sin_addr.s_addr)
+	      continue;
+	  }
 #ifdef HAVE_IPV6
-      else
-	{
-	  if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr->in6.sin6_addr))
-	    continue;
-	}
+	else
+	  {
+	    if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr.addr6, &addr->in6.sin6_addr))
+	      continue;
+	  }
 #endif
-      
-      /* Only accept poitive entries unless in lazy mode. */
-      if (arp->status != ARP_EMPTY || lazy || updated)
-	{
-	  if (mac && arp->hwlen != 0)
-	    memcpy(mac, arp->hwaddr, arp->hwlen);
-	  return arp->hwlen;
-	}
-    }
-
+	
+	/* Only accept poitive entries unless in lazy mode. */
+	if (arp->status != ARP_EMPTY || lazy || updated)
+	  {
+	    if (mac && arp->hwlen != 0)
+	      memcpy(mac, arp->hwaddr, arp->hwlen);
+	    return arp->hwlen;
+	  }
+      }
+  
   /* Not found, try the kernel */
   if (!updated)
      {
        updated = 1;
-       
+       last = now;
+
        /* Mark all non-negative entries */
        for (arp = arps, up = &arps; arp; arp = arp->next)
 	 if (arp->status != ARP_EMPTY)
-	   arp->status = ARP_FREE;
+	   arp->status = ARP_MARK;
        
        iface_enumerate(AF_UNSPEC, NULL, filter_mac);
        
-       /* Remove all unconfirmed entries to old list, announce new ones. */
+       /* Remove all unconfirmed entries to old list. */
        for (arp = arps, up = &arps; arp; arp = arp->next)
-	 if (arp->status == ARP_FREE)
+	 if (arp->status == ARP_MARK)
 	   {
 	     *up = arp->next;
 	     arp->next = old;
 	     old = arp;
 	   }
 	 else
-	   {
-	     up = &arp->next;
-	     if (arp->status == ARP_NEW)
-	       {
-		 char a[ADDRSTRLEN], m[ADDRSTRLEN];
-		 union mysockaddr pa;
-		 pa.sa.sa_family = arp->family;
-		 pa.in.sin_addr.s_addr = arp->addr.addr.addr4.s_addr;
-		 prettyprint_addr(&pa, a);
-		 print_mac(m, arp->hwaddr, arp->hwlen);
-		 my_syslog(LOG_INFO, _("new arp: %s %s"), a, m);
-	       }
-	   }
-
+	   up = &arp->next;
+	   
        goto again;
      }
 
   /* record failure, so we don't consult the kernel each time
      we're asked for this address */
-  if (old)
+  if (freelist)
     {
-      arp = old;
-      old = old->next;
+      arp = freelist;
+      freelist = freelist->next;
     }
   else
     arp = whine_malloc(sizeof(struct arp_record));
@@ -198,4 +196,36 @@
    return 0;
 }
 
+int do_arp_script_run(void)
+{
+  struct arp_record *arp;
+  
+  /* Notify any which went, then move to free list */
+  if (old)
+    {
+#ifdef HAVE_SCRIPT
+      if (option_bool(OPT_DNS_CLIENT))
+	queue_arp(ACTION_ARP_OLD, old->hwaddr, old->hwlen, old->family, &old->addr);
+#endif
+      arp = old;
+      old = arp->next;
+      arp->next = freelist;
+      freelist = arp;
+      return 1;
+    }
+
+  for (arp = arps; arp; arp = arp->next)
+    if (arp->status == ARP_NEW)
+      {
+#ifdef HAVE_SCRIPT
+	if (option_bool(OPT_DNS_CLIENT))
+	  queue_arp(ACTION_ARP, arp->hwaddr, arp->hwlen, arp->family, &arp->addr);
+#endif
+	arp->status = ARP_FOUND;
+	return 1;
+      }
+
+  return 0;
+}
+
 
diff --git a/src/config.h b/src/config.h
index f75fe9d..309be6b 100644
--- a/src/config.h
+++ b/src/config.h
@@ -337,7 +337,7 @@
 #define HAVE_DHCP
 #endif
 
-#if defined(NO_SCRIPT) || !defined(HAVE_DHCP) || defined(NO_FORK)
+#if defined(NO_SCRIPT) || defined(NO_FORK)
 #undef HAVE_SCRIPT
 #undef HAVE_LUASCRIPT
 #endif
diff --git a/src/dhcp6.c b/src/dhcp6.c
index 7b1a7c7..0e2e171 100644
--- a/src/dhcp6.c
+++ b/src/dhcp6.c
@@ -220,7 +220,7 @@
 	  inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
 	  
 	  if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
-	    relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id);
+	    relay_upstream6(parm.relay, sz, &from.sin6_addr, from.sin6_scope_id, now);
 	  return;
 	}
       
@@ -250,7 +250,7 @@
     }
 }
 
-void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep)
+void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)
 {
   /* Recieving a packet from a host does not populate the neighbour
      cache, so we send a neighbour discovery request if we can't 
@@ -280,7 +280,7 @@
     {
       struct timespec ts;
       
-      if ((maclen = find_mac(&addr, mac, 0)) != 0)
+      if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
 	break;
 	  
       sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr));
diff --git a/src/dns-protocol.h b/src/dns-protocol.h
index 6cf5158..addfa9e 100644
--- a/src/dns-protocol.h
+++ b/src/dns-protocol.h
@@ -77,6 +77,8 @@
 
 #define EDNS0_OPTION_MAC            65001 /* dyndns.org temporary assignment */
 #define EDNS0_OPTION_CLIENT_SUBNET  8     /* IANA */
+#define EDNS0_OPTION_NOMDEVICEID    65073 /* Nominum temporary assignment */
+#define EDNS0_OPTION_NOMCPEID       65074 /* Nominum temporary assignment */
 
 struct dns_header {
   u16 id;
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 45761cc..229693f 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -245,8 +245,11 @@
   /* Note that order matters here, we must call lease_init before
      creating any file descriptors which shouldn't be leaked
      to the lease-script init process. We need to call common_init
-     before lease_init to allocate buffers it uses.*/
-  if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || daemon->relay6)
+     before lease_init to allocate buffers it uses.
+     The script subsystrm relies on DHCP buffers, hence the last two
+     conditions below. */  
+  if (daemon->dhcp || daemon->doing_dhcp6 || daemon->relay4 || 
+      daemon->relay6 || option_bool(OPT_TFTP) || option_bool(OPT_ADD_MAC))
     {
       dhcp_common_init();
       if (daemon->dhcp || daemon->doing_dhcp6)
@@ -553,8 +556,9 @@
    /* if we are to run scripts, we need to fork a helper before dropping root. */
   daemon->helperfd = -1;
 #ifdef HAVE_SCRIPT 
-  if ((daemon->dhcp || daemon->dhcp6) && (daemon->lease_change_command || daemon->luascript))
-    daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
+  if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_ADD_MAC)) && 
+      (daemon->lease_change_command || daemon->luascript))
+      daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
 #endif
 
   if (!option_bool(OPT_DEBUG) && getuid() == 0)   
@@ -914,9 +918,9 @@
       
       poll_listen(piperead, POLLIN);
 
-#ifdef HAVE_DHCP
-#  ifdef HAVE_SCRIPT
-      while (helper_buf_empty() && do_script_run(now));
+#ifdef HAVE_SCRIPT
+      while (helper_buf_empty() && do_script_run(now)); 
+      while (helper_buf_empty() && do_arp_script_run());
 
 #    ifdef HAVE_TFTP
       while (helper_buf_empty() && do_tftp_script_run());
@@ -924,16 +928,17 @@
 
       if (!helper_buf_empty())
 	poll_listen(daemon->helperfd, POLLOUT);
-#  else
+#else
       /* need this for other side-effects */
       while (do_script_run(now));
+      while (do_arp_script_run(now));
 
 #    ifdef HAVE_TFTP 
       while (do_tftp_script_run());
 #    endif
 
-#  endif
 #endif
+
    
       /* must do this just before select(), when we know no
 	 more calls to my_syslog() can occur */
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 4459594..fec0f8d 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -235,7 +235,8 @@
 #define OPT_LOOP_DETECT    50
 #define OPT_EXTRALOG       51
 #define OPT_TFTP_NO_FAIL   52
-#define OPT_LAST           53
+#define OPT_DNS_CLIENT     53
+#define OPT_LAST           54
 
 /* extra flags for my_syslog, we use a couple of facilities since they are known 
    not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -633,6 +634,8 @@
 #define ACTION_OLD           3
 #define ACTION_ADD           4
 #define ACTION_TFTP          5
+#define ACTION_ARP           6
+#define ACTION_ARP_OLD       7
 
 #define LEASE_NEW            1  /* newly created */
 #define LEASE_CHANGED        2  /* modified */
@@ -948,6 +951,7 @@
   int cachesize, ftabsize;
   int port, query_port, min_port;
   unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl;
+  char *dns_client_id;
   struct hostsfile *addn_hosts;
   struct dhcp_context *dhcp, *dhcp6;
   struct ra_interface *ra_interfaces;
@@ -1135,7 +1139,7 @@
 #endif
 
 /* dnssec.c */
-size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz);
+size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, union mysockaddr *addr, int edns_pktsz);
 int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
 int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
 int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class,
@@ -1372,6 +1376,8 @@
 #ifdef HAVE_TFTP
 void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer);
 #endif
+void queue_arp(int action, unsigned char *mac, int maclen,
+	       int family, struct all_addr *addr);
 int helper_buf_empty(void);
 #endif
 
@@ -1408,7 +1414,7 @@
 void make_duid(time_t now);
 void dhcp_construct_contexts(time_t now);
 void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, 
-		    unsigned int *maclenp, unsigned int *mactypep);
+		    unsigned int *maclenp, unsigned int *mactypep, time_t now);
 #endif
   
 /* rfc3315.c */
@@ -1416,7 +1422,8 @@
 unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,  
 			   struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
 			   size_t sz, struct in6_addr *client_addr, time_t now);
-void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id);
+void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, 
+		     u32 scope_id, time_t now);
 
 unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
 #endif
@@ -1512,11 +1519,11 @@
 				   size_t *len, unsigned char **p, int *is_sign, int *is_last);
 size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, 
 			unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do);
-size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
-size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source);
-size_t add_do_bit(struct dns_header *header, size_t plen, char *limit);
+size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
+size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, 
+			union mysockaddr *source, time_t now, int *check_subnet);
 int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
 
 /* arp.c */
-int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy);
-
+int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now);
+int do_arp_script_run(void);
diff --git a/src/dnssec.c b/src/dnssec.c
index ed2d3fe..918a2dc 100644
--- a/src/dnssec.c
+++ b/src/dnssec.c
@@ -2173,7 +2173,7 @@
     }
 }
 
-size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, 
+size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, 
 			     int type, union mysockaddr *addr, int edns_pktsz)
 {
   unsigned char *p;
diff --git a/src/edns0.c b/src/edns0.c
index 9d8c0b9..12e0210 100644
--- a/src/edns0.c
+++ b/src/edns0.c
@@ -94,13 +94,6 @@
   
   return ret;
 }
-
-struct macparm {
-  unsigned char *limit;
-  struct dns_header *header;
-  size_t plen;
-  union mysockaddr *l3;
-};
  
 size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, 
 			unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do)
@@ -208,19 +201,54 @@
   return p - (unsigned char *)header;
 }
 
-size_t add_do_bit(struct dns_header *header, size_t plen, char *limit)
+size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
 {
   return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1);
 }
 
-size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)
+static unsigned char char64(unsigned char c)
+{
+  return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f];
+}
+
+static void encoder(unsigned char *in, char *out)
+{
+  out[0] = char64(in[0]>>2);
+  out[1] = char64((in[0]<<4) | (in[1]>>4));
+  out[2] = char64((in[1]<<2) | (in[2]>>6));
+  out[3] = char64(in[2]);
+}
+
+static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
+{
+  int maclen;
+  unsigned char mac[DHCP_CHADDR_MAX];
+  char encode[8]; /* handle 6 byte MACs */
+
+  if ((maclen = find_mac(l3, mac, 1, now)) == 6)
+    {
+      encoder(mac, encode);
+      encoder(mac+3, encode+4);
+      
+      plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, 8, 0); 
+    }
+
+  if (daemon->dns_client_id)
+    plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, 
+			    (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0);
+
+  return plen; 
+}
+
+
+static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
 {
   int maclen;
   unsigned char mac[DHCP_CHADDR_MAX];
 
-  if ((maclen = find_mac(l3, mac, 1)) != 0)
+  if ((maclen = find_mac(l3, mac, 1, now)) != 0)
     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0); 
-  
+    
   return plen; 
 }
 
@@ -296,7 +324,7 @@
   return len + 4;
 }
  
-size_t add_source_addr(struct dns_header *header, size_t plen, char *limit, union mysockaddr *source)
+static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source)
 {
   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
   
@@ -344,3 +372,23 @@
    
    return 1;
 }
+
+size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, 
+			union mysockaddr *source, time_t now, int *check_subnet)    
+{
+  *check_subnet = 0;
+
+  if (option_bool(OPT_ADD_MAC))
+    plen  = add_mac(header, plen, limit, source, now);
+  
+  if (option_bool(OPT_DNS_CLIENT))
+    plen = add_dns_client(header, plen, limit, source, now);
+  
+  if (option_bool(OPT_CLIENT_SUBNET))
+    {
+      plen = add_source_addr(header, plen, limit, source); 
+      *check_subnet = 1;
+    }
+	  
+  return plen;
+}
diff --git a/src/forward.c b/src/forward.c
index c0e4d9a..911f46e 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -388,36 +388,27 @@
   if (!flags && forward)
     {
       struct server *firstsentto = start;
-      int forwarded = 0;
+      int subnet, forwarded = 0;
       size_t edns0_len;
 
       /* If a query is retried, use the log_id for the retry when logging the answer. */
       forward->log_id = daemon->log_id;
       
-      if (option_bool(OPT_ADD_MAC))
+      edns0_len  = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet);
+      
+      if (edns0_len != plen)
 	{
-	  size_t new = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source);
-	  if (new != plen)
-	    {
-	      plen = new;
-	      forward->flags |= FREC_ADDED_PHEADER;
-	    }
+	  plen = edns0_len;
+	  forward->flags |= FREC_ADDED_PHEADER;
+	  
+	  if (subnet)
+	    forward->flags |= FREC_HAS_SUBNET;
 	}
-
-      if (option_bool(OPT_CLIENT_SUBNET))
-	{
-	  size_t new = add_source_addr(header, plen, ((char *) header) + PACKETSZ, &forward->source); 
-	  if (new != plen)
-	    {
-	      plen = new;
-	      forward->flags |= FREC_HAS_SUBNET | FREC_ADDED_PHEADER;
-	    }
-	}
-
+      
 #ifdef HAVE_DNSSEC
       if (option_bool(OPT_DNSSEC_VALID))
 	{
-	  size_t new = add_do_bit(header, plen, ((char *) header) + PACKETSZ);
+	  size_t new = add_do_bit(header, plen, ((unsigned char *) header) + PACKETSZ);
 	 
 	  if (new != plen)
 	    forward->flags |= FREC_ADDED_PHEADER;
@@ -607,15 +598,30 @@
 	    }
 	  else
 	    {
+	      unsigned short udpsz;
+
 	      /* If upstream is advertising a larger UDP packet size
 		 than we allow, trim it so that we don't get overlarge
 		 requests for the client. We can't do this for signed packets. */
-	      unsigned short udpsz;
-	      unsigned char *psave = sizep;
-	      
 	      GETSHORT(udpsz, sizep);
 	      if (udpsz > daemon->edns_pktsz)
-		PUTSHORT(daemon->edns_pktsz, psave);
+		{
+		  sizep -= 2;
+		  PUTSHORT(daemon->edns_pktsz, sizep);
+		}
+
+#ifdef HAVE_DNSSEC
+	      /* If the client didn't set the do bit, but we did, reset it. */
+	      if (option_bool(OPT_DNSSEC_VALID) && !do_bit)
+		{
+		  unsigned short flags;
+		  sizep += 2; /* skip RCODE */
+		  GETSHORT(flags, sizep);
+		  flags &= ~0x8000;
+		  sizep -= 2;
+		  PUTSHORT(flags, sizep);
+		}
+#endif
 	    }
 	}
     }
@@ -674,14 +680,11 @@
     }
   
 #ifdef HAVE_DNSSEC
-  if (bogusanswer && !(header->hb4 & HB4_CD)) 
+  if (bogusanswer && !(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG))
     {
-      if (!option_bool(OPT_DNSSEC_DEBUG))
-	{
-	  /* Bogus reply, turn into SERVFAIL */
-	  SET_RCODE(header, SERVFAIL);
-	  munged = 1;
-	}
+      /* Bogus reply, turn into SERVFAIL */
+      SET_RCODE(header, SERVFAIL);
+      munged = 1;
     }
 
   if (option_bool(OPT_DNSSEC_VALID))
@@ -802,7 +805,7 @@
 	      if (forward->flags |= FREC_AD_QUESTION)
 		header->hb4 |= HB4_AD;
 	      if (forward->flags & FREC_DO_QUESTION)
-		add_do_bit(header, nn,  (char *)pheader + plen);
+		add_do_bit(header, nn,  (unsigned char *)pheader + plen);
 	      forward_query(-1, NULL, NULL, 0, header, nn, now, forward, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION);
 	      return;
 	    }
@@ -927,13 +930,13 @@
 		      if (status == STAT_NEED_KEY)
 			{
 			  new->flags |= FREC_DNSKEY_QUERY; 
-			  nn = dnssec_generate_query(header, ((char *) header) + server->edns_pktsz,
+			  nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
 						     daemon->keyname, forward->class, T_DNSKEY, &server->addr, server->edns_pktsz);
 			}
 		      else 
 			{
 			  new->flags |= FREC_DS_QUERY;
-			  nn = dnssec_generate_query(header,((char *) header) + server->edns_pktsz,
+			  nn = dnssec_generate_query(header,((unsigned char *) header) + server->edns_pktsz,
 						     daemon->keyname, forward->class, T_DS, &server->addr, server->edns_pktsz);
 			}
 		      if ((hash = hash_questions(header, nn, daemon->namebuff)))
@@ -1434,7 +1437,7 @@
 	  break;
 	}
 	 
-      m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class, 
+      m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 
 				new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr, server->edns_pktsz);
       
       *length = htons(m);
@@ -1548,8 +1551,6 @@
       daemon->log_display_id = ++daemon->log_id;
       daemon->log_source_addr = &peer_addr;
       
-      check_subnet = 0;
-
       /* save state of "cd" flag in query */
       if ((checking_disabled = header->hb4 & HB4_CD))
 	no_cache_dnssec = 1;
@@ -1627,20 +1628,14 @@
 	      struct all_addr *addrp = NULL;
 	      int type = 0;
 	      char *domain = NULL;
-	      
-	      if (option_bool(OPT_ADD_MAC))
-		size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
-	      	
-	      if (option_bool(OPT_CLIENT_SUBNET))
-		{
-		  size_t new = add_source_addr(header, size, ((char *) header) + 65536, &peer_addr);
-		  if (size != new)
-		    {
-		      size = new;
-		      check_subnet = 1;
-		    }
-		}
+	      size_t new_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet);
 
+	      if (size != new_size)
+		{
+		  added_pheader = 1;
+		  size = new_size;
+		}
+	      
 	      if (gotname)
 		flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
 	      
@@ -1715,20 +1710,20 @@
 			    }
 			  
 #ifdef HAVE_DNSSEC
-			  added_pheader = 0;			  
 			  if (option_bool(OPT_DNSSEC_VALID))
 			    {
-			      size_t new_size = add_do_bit(header, size, ((char *) header) + 65536);
+			      new_size = add_do_bit(header, size, ((unsigned char *) header) + 65536);
+			      
+			      if (size != new_size)
+				{
+				  added_pheader = 1;
+				  size = new_size;
+				}
 			      
 			      /* For debugging, set Checking Disabled, otherwise, have the upstream check too,
 				 this allows it to select auth servers when one is returning bad data. */
 			      if (option_bool(OPT_DNSSEC_DEBUG))
 				header->hb4 |= HB4_CD;
-			      
-			      if (size != new_size)
-				added_pheader = 1;
-			      
-			      size = new_size;
 			    }
 #endif
 			}
diff --git a/src/helper.c b/src/helper.c
index 1fee72d..517cfd9 100644
--- a/src/helper.c
+++ b/src/helper.c
@@ -219,7 +219,18 @@
 	  action_str = "tftp";
 	  is6 = (data.flags != AF_INET);
 	}
-      else
+      else if (data.action == ACTION_ARP)
+	{
+	  action_str = "arp";
+	  is6 = (data.flags != AF_INET);
+	}
+       else if (data.action == ACTION_ARP_OLD)
+	{
+	  action_str = "arp-old";
+	  is6 = (data.flags != AF_INET);
+	  data.action = ACTION_ARP;
+	}
+       else 
 	continue;
 
       	
@@ -321,6 +332,22 @@
 		  lua_call(lua, 2, 0);	/* pass 2 values, expect 0 */
 		}
 	    }
+	  else if (data.action == ACTION_ARP)
+	    {
+	      lua_getglobal(lua, "arp"); 
+	      if (lua_type(lua, -1) != LUA_TFUNCTION)
+		lua_pop(lua, 1); /* arp function optional */
+	      else
+		{
+		  lua_pushstring(lua, action_str); /* arg1 - action */
+		  lua_newtable(lua);               /* arg2 - data table */
+		  lua_pushstring(lua, daemon->addrbuff);
+		  lua_setfield(lua, -2, "client_address");
+		  lua_pushstring(lua, daemon->dhcp_buff);
+		  lua_setfield(lua, -2, "mac_address");
+		  lua_call(lua, 2, 0);	/* pass 2 values, expect 0 */
+		}
+	    }
 	  else
 	    {
 	      lua_getglobal(lua, "lease");     /* function to call */
@@ -478,7 +505,7 @@
 	  continue;
 	}
       
-      if (data.action != ACTION_TFTP)
+      if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
 	{
 #ifdef HAVE_DHCP6
 	  my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
@@ -550,10 +577,9 @@
 	  my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
 	  if (data.action == ACTION_OLD_HOSTNAME)
 	    hostname = NULL;
-	}
-
-      my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
-      
+	  
+	  my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
+    }
       /* we need to have the event_fd around if exec fails */
       if ((i = fcntl(event_fd, F_GETFD)) != -1)
 	fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
@@ -563,8 +589,8 @@
       if (err == 0)
 	{
 	  execl(daemon->lease_change_command, 
-		p ? p+1 : daemon->lease_change_command,
-		action_str, is6 ? daemon->packet : daemon->dhcp_buff, 
+		p ? p+1 : daemon->lease_change_command, action_str, 
+		(is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff, 
 		daemon->addrbuff, hostname, (char*)NULL);
 	  err = errno;
 	}
@@ -760,6 +786,30 @@
 }
 #endif
 
+void queue_arp(int action, unsigned char *mac, int maclen, int family, struct all_addr *addr)
+{
+  /* no script */
+  if (daemon->helperfd == -1)
+    return;
+  
+  buff_alloc(sizeof(struct script_data));
+  memset(buf, 0, sizeof(struct script_data));
+
+  buf->action = action;
+  buf->hwaddr_len = maclen;
+  buf->hwaddr_type =  ARPHRD_ETHER; 
+  if ((buf->flags = family) == AF_INET)
+    buf->addr = addr->addr.addr4;
+#ifdef HAVE_IPV6
+  else
+    buf->addr6 = addr->addr.addr6;
+#endif
+  
+  memcpy(buf->hwaddr, mac, maclen);
+  
+  bytes_in_buf = sizeof(struct script_data);
+}
+
 int helper_buf_empty(void)
 {
   return bytes_in_buf == 0;
diff --git a/src/option.c b/src/option.c
index 71beb98..f359bc5 100644
--- a/src/option.c
+++ b/src/option.c
@@ -154,6 +154,7 @@
 #define LOPT_HOST_INOTIFY  342
 #define LOPT_DNSSEC_STAMP  343
 #define LOPT_TFTP_NO_FAIL  344
+#define LOPT_DNS_CLIENT_ID 355
 
 #ifdef HAVE_GETOPT_LONG
 static const struct option opts[] =  
@@ -281,6 +282,7 @@
     { "rebind-localhost-ok", 0, 0,  LOPT_LOC_REBND },
     { "add-mac", 0, 0, LOPT_ADD_MAC },
     { "add-subnet", 2, 0, LOPT_ADD_SBNET },
+    { "add-dns-client", 2, 0 , LOPT_DNS_CLIENT_ID },
     { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
     { "dhcp-sequential-ip", 0, 0,  LOPT_INCR_ADDR },
     { "conntrack", 0, 0, LOPT_CONNTRACK },
@@ -446,6 +448,7 @@
   { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
   { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
   { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
+  { LOPT_DNS_CLIENT_ID, ARG_ONE, "<proxyname>", gettext_noop("Add client identification to forwarded DNS queries."), NULL },
   { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
   { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
   { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
@@ -2150,6 +2153,12 @@
 	}
       break;
       
+    case LOPT_DNS_CLIENT_ID: /* --add-dns-client */
+       set_option_bool(OPT_DNS_CLIENT);
+       if (arg)
+	daemon->dns_client_id = opt_string_alloc(arg);
+      break;
+
     case 'u':  /* --user */
       daemon->username = opt_string_alloc(arg);
       break;
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 3ed8623..31bb41b 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -130,7 +130,7 @@
       MAC address from the local ND cache. */
       
       if (!state->link_address)
-	get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type);
+	get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now);
       else
 	{
 	  struct dhcp_context *c;
@@ -2054,7 +2054,8 @@
   return ret;
 } 
 
-void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id)
+void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, 
+		     struct in6_addr *peer_address, u32 scope_id, time_t now)
 {
   /* ->local is same value for all relays on ->current chain */
   
@@ -2068,7 +2069,7 @@
   unsigned char mac[DHCP_CHADDR_MAX];
 
   inet_pton(AF_INET6, ALL_SERVERS, &multicast);
-  get_client_mac(peer_address, scope_id, mac, &maclen, &mactype);
+  get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
 
   /* source address == relay address */
   from.addr.addr6 = relay->local.addr.addr6;