]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
First complete version of DNS-client-id EDNS0 and ARP tracking code.
authorSimon Kelley <simon@thekelleys.org.uk>
Mon, 28 Dec 2015 23:17:15 +0000 (23:17 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Mon, 28 Dec 2015 23:17:15 +0000 (23:17 +0000)
12 files changed:
src/arp.c
src/config.h
src/dhcp6.c
src/dns-protocol.h
src/dnsmasq.c
src/dnsmasq.h
src/dnssec.c
src/edns0.c
src/forward.c
src/helper.c
src/option.c
src/rfc3315.c

index b624dac3ba6e811f8837770dc7d3f22205323105..f41cdece8b7c3598bafb4d683c43dbdaf543b794 100644 (file)
--- a/src/arp.c
+++ b/src/arp.c
 
 #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 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
        }
 #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 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
   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 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
 }
 
 /* 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 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy)
    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;
+}
+
 
index f75fe9db7081ce384b78f66bd71bd0412b8e62fc..309be6b89c51a1cb0d63f6c9b7abd3cc6f44b25b 100644 (file)
@@ -337,7 +337,7 @@ HAVE_SOCKADDR_SA_LEN
 #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
index 7b1a7c7a46e0833a6ce55535bf8d0b70beef3213..0e2e171858a2befdbda15a030e72a6069835d079 100644 (file)
@@ -220,7 +220,7 @@ void dhcp6_packet(time_t now)
          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 dhcp6_packet(time_t now)
     }
 }
 
-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 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsi
     {
       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));
index 6cf515881c17b061a59d3179bb259320b2f04b10..addfa9ece91cc63e10082440b537049b85e5bcb0 100644 (file)
@@ -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;
index 45761ccd89bec1bf5039b337219ff4f2e9001822..229693fb5228d0a24e6fadb704383ff4269f6d53 100644 (file)
@@ -245,8 +245,11 @@ int main (int argc, char **argv)
   /* 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 @@ int main (int argc, char **argv)
    /* 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 @@ int main (int argc, char **argv)
       
       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 @@ int main (int argc, char **argv)
 
       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 */
index 445959443affb065c731bea2884bffa65fdb2e6e..fec0f8d7dc08402fd2ac3d629e0a8d443e77d6e3 100644 (file)
@@ -235,7 +235,8 @@ struct event_desc {
 #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 @@ struct frec {
 #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 @@ extern struct daemon {
   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 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
 #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 @@ void queue_script(int action, struct dhcp_lease *lease,
 #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 @@ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct
 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 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
 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 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
                                   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);
index ed2d3fed1bf818cd1d0a8812a06b07c3bde15314..918a2dce245ae81b9ead685dbd5c7e204e4f42db 100644 (file)
@@ -2173,7 +2173,7 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
     }
 }
 
-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;
index 9d8c0b994119f52563b642782a053b4d4eef78b3..12e0210ab50c45d7ac0b24c51ced5098d6e47d37 100644 (file)
@@ -94,13 +94,6 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
   
   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 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
   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; 
+}
+
 
-  if ((maclen = find_mac(l3, mac, 1)) != 0)
+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, now)) != 0)
     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0); 
-  
+    
   return plen; 
 }
 
@@ -296,7 +324,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
   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 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
    
    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;
+}
index c0e4d9aa31813703838115aaff03e396dc44c3da..911f46ef38b7603c7bcf3f996818cfb0abe7ffbe 100644 (file)
@@ -388,36 +388,27 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
   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))
-       {
-         size_t new = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source);
-         if (new != plen)
-           {
-             plen = new;
-             forward->flags |= FREC_ADDED_PHEADER;
-           }
-       }
-
-      if (option_bool(OPT_CLIENT_SUBNET))
+      edns0_len  = add_edns0_config(header, plen, ((unsigned char *)header) + PACKETSZ, &forward->source, now, &subnet);
+      
+      if (edns0_len != plen)
        {
-         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;
-           }
+         plen = edns0_len;
+         forward->flags |= FREC_ADDED_PHEADER;
+         
+         if (subnet)
+           forward->flags |= FREC_HAS_SUBNET;
        }
-
+      
 #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 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
            }
          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 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
     }
   
 #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 @@ void reply_query(int fd, int family, time_t now)
              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 @@ void reply_query(int fd, int family, time_t now)
                      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 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
          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 @@ unsigned char *tcp_request(int confd, time_t now,
       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 @@ unsigned char *tcp_request(int confd, time_t now,
              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_size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &check_subnet);
+
+             if (size != new_size)
                {
-                 size_t new = add_source_addr(header, size, ((char *) header) + 65536, &peer_addr);
-                 if (size != new)
-                   {
-                     size = new;
-                     check_subnet = 1;
-                   }
+                 added_pheader = 1;
+                 size = new_size;
                }
-
+             
              if (gotname)
                flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
              
@@ -1715,20 +1710,20 @@ unsigned char *tcp_request(int confd, time_t now,
                            }
                          
 #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
                        }
index 1fee72dead8c5863f2c892cd5c3b7c713d1654db..517cfd9226877ae8915737404c3778d0b3240826 100644 (file)
@@ -219,7 +219,18 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
          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 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
                  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 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
          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 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
          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 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
       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 @@ 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)
+{
+  /* 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;
index 71beb9801a85b0ea548d8367a4b993e648d45102..f359bc5b4db28ef3288b0115bbe3e49b2f1fa8cb 100644 (file)
@@ -154,6 +154,7 @@ struct myoption {
 #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 @@ static const struct myoption opts[] =
     { "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 @@ static struct {
   { 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 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
        }
       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;
index 3ed8623fd4165884314a6a705027df7c1328a96c..31bb41b572828a5b6e1fb9d7e4e7a4399a4f6c40 100644 (file)
@@ -130,7 +130,7 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz,
       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 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
   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 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer
   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;