components/dnsmasq/patches/05_dhcp_bind_fix.patch
author Vardhnee Ramanujam Ravi <vardhnee.ramanujam.ravi@oracle.com>
Mon, 26 Sep 2016 16:23:01 -0700
changeset 6979 93dcf1920c71
permissions -rw-r--r--
24623299 hundreds of dnsmasq processes causes DHCP to never succeed

Patch to address a scalability issue when using dnsmasq in Openstack,
that causes DHCP to be extremely slow.

This patch was developed in-house. Since it is Solaris-specific, it is not
suitable for upstream.

--- dnsmasq-2.75/src/dhcp-common.c	2015-07-30 12:59:07.000000000 -0700
+++ new/src/dhcp-common.c	2016-09-22 15:12:02.104708123 -0700
@@ -493,6 +493,58 @@
       errno != EPERM)
     die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
 }
+#elif defined(HAVE_SOLARIS_NETWORK)
+int which_ifindex(void)
+{
+  /* If we are doing DHCP on exactly one interface, and using Solaris, we want
+   * to limit packet transmission/reception to that interface using IP_BOUND_IF
+   * for IPv4 and IPV6_BOUND_IF for IPv6. This is for the use case of OpenStack,
+   * which runs a new dnsmasq instance for each network it creates. Without this
+   * socket option, each of the dnsmasq process would unnecessarily process 
+   * packets that arrive on other interfaces as well, thus slowing down the 
+   * entire DHCP process.
+   */
+
+   struct irec *iface, *found;
+   struct iname *if_tmp;
+
+   if (!daemon->if_names)
+     return -1;
+
+   for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
+     if (if_tmp->name && (!if_tmp->used || strchr(if_tmp->name, '*')))
+       return -1;
+ 
+   for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
+     if (iface->dhcp_ok)
+       {
+	 if (!found)
+	   found = iface;
+	 else if (strcmp(found->name, iface->name) != 0)
+	   return -1; /* more than one. */
+       }
+
+   if (found)
+     return found->index;
+
+   return -1;
+}
+
+void bindtoif(int ifindex, int fd, int is_dhcp6)
+{
+  if (is_dhcp6)
+    {
+      if (setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, (char *)&ifindex, sizeof(ifindex)) == -1 && 
+          errno != EPERM)
+        die(_("failed to set IPv6_BOUND_IF on DHCP socket: %s"), NULL, EC_BADNET);
+    }
+  else
+    {
+      if (setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex)) == -1 &&
+          errno != EPERM)
+        die(_("failed to set IP_BOUND_IF on DHCP socket: %s"), NULL, EC_BADNET);
+    }
+}
 #endif
 
 static const struct opttab_t {
--- dnsmasq-2.75/src/dnsmasq.h	2016-09-22 15:20:19.502011580 -0700
+++ new/src/dnsmasq.h	2016-09-22 13:17:03.865473011 -0700
@@ -1464,6 +1464,9 @@
 #ifdef HAVE_LINUX_NETWORK
 char *whichdevice(void);
 void bindtodevice(char *device, int fd);
+#elif defined(HAVE_SOLARIS_NETWORK)
+int which_ifindex(void);
+void bindtoif(int ifindex, int fd, int is_dhcp6);
 #endif
 #  ifdef HAVE_DHCP6
 void display_opts6(void);
--- dnsmasq-2.75/src/dnsmasq.c	2015-07-30 12:59:07.000000000 -0700
+++ new/src/dnsmasq.c	2016-09-22 15:20:03.040122945 -0700
@@ -54,6 +54,10 @@
   char *bound_device = NULL;
   int did_bind = 0;
 #endif 
+#if defined(HAVE_SOLARIS_NETWORK)
+  int bound_ifindex = -1;
+  int did_bind = 0;
+#endif
 #if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
   struct dhcp_context *context;
   struct dhcp_relay *relay;
@@ -306,6 +310,23 @@
 	      did_bind = 1;
 	    }
 	}
+#elif defined(HAVE_SOLARIS_NETWORK) && defined(HAVE_DHCP)
+      bound_ifindex = which_ifindex();
+
+      if (daemon->dhcp)
+	{
+          if (!daemon->relay4 && bound_ifindex >= 0)
+            {
+	      bindtoif(bound_ifindex, daemon->dhcpfd, 0);
+	      did_bind = 1;
+	    }
+
+	  if (daemon->enable_pxe && bound_ifindex >= 0)
+	    {
+              bindtoif(bound_ifindex, daemon->pxefd, 0);
+	      did_bind = 1;
+	    }
+	}
 #endif
 
 #if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6)
@@ -314,6 +335,12 @@
 	  bindtodevice(bound_device, daemon->dhcp6fd);
 	  did_bind = 1;
 	}
+#elif defined(HAVE_SOLARIS_NETWORK) && defined(HAVE_DHCP6)
+      if (daemon->doing_dhcp6 && !daemon->relay6 && bound_ifindex >= 0)
+	{
+          bindtoif(bound_ifindex, daemon->dhcp6fd, 1);
+	  did_bind = 1;
+	}
 #endif
     }
   else 
@@ -788,6 +815,9 @@
 #  ifdef HAVE_LINUX_NETWORK
   if (did_bind)
     my_syslog(MS_DHCP | LOG_INFO, _("DHCP, sockets bound exclusively to interface %s"), bound_device);
+#  elif defined(HAVE_SOLARIS_NETWORK)
+  if (did_bind)
+    my_syslog(MS_DHCP | LOG_INFO, _("DHCP, sockets bound exclusively to interface index %d"), bound_ifindex);
 #  endif
 
   /* after dhcp_contruct_contexts */