[Dnsmasq-discuss] [PATCH] Allow PXE style proxy mode for arbitrary Vendor Classes

Stefan Brüns stefan.bruens at rwth-aachen.de
Fri Aug 28 13:54:36 BST 2015


Currently dnsmasq provides PXE style DHCP Proxy server support only
for clients with a Vendor Class Identifier matching "^PXEClient.*".
PXE is only defined for a few architectures, but the Proxy mechanism
is independent of the arch, so it could be used for any boot client.

For it to actually work it also needs support from bootloaders, e.g.
u-boot, barebox, ...

Signed-off-by: Stefan Brüns <stefan.bruens at rwth-aachen.de>
---
 src/dnsmasq.h |  2 +-
 src/option.c  |  8 ++++++++
 src/rfc2131.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 71 insertions(+), 3 deletions(-)

diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index cf1a782..1eb0313 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -973,7 +973,7 @@ extern struct daemon {
   int override;
   int enable_pxe;
   int doing_ra, doing_dhcp6;
-  struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names; 
+  struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names, *dhcp_force_proxy;
   struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
   struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
   int dhcp_max, tftp_max;
diff --git a/src/option.c b/src/option.c
index ecc2619..0870d06 100644
--- a/src/option.c
+++ b/src/option.c
@@ -154,6 +154,7 @@ struct myoption {
 #define LOPT_HOST_INOTIFY  342
 #define LOPT_DNSSEC_STAMP  343
 #define LOPT_TFTP_NO_FAIL  344
+#define LOPT_FORCE_PROXY   345
 
 #ifdef HAVE_GETOPT_LONG
 static const struct option opts[] =  
@@ -313,6 +314,7 @@ static const struct myoption opts[] =
     { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
     { "quiet-ra", 0, 0, LOPT_QUIET_RA },
     { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
+    { "dhcp-force-proxy", 1, 0, LOPT_FORCE_PROXY },
     { NULL, 0, 0, 0 }
   };
 
@@ -3405,6 +3407,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
     case LOPT_BROADCAST: /* --dhcp-broadcast */
     case '3':            /* --bootp-dynamic */
     case LOPT_GEN_NAMES: /* --dhcp-generate-names */
+    case LOPT_FORCE_PROXY: /* --dhcp-force-proxy */
       {
 	struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
 	struct dhcp_netid *list = NULL;
@@ -3428,6 +3431,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
 	    new->next = daemon->dhcp_gen_names;
 	    daemon->dhcp_gen_names = new;
 	  }
+	else if (option == LOPT_FORCE_PROXY)
+	  {
+	    new->next = daemon->dhcp_force_proxy;
+	    daemon->dhcp_force_proxy = new;
+	  }
 	else
 	  {
 	    new->next = daemon->dhcp_ignore_names;
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 9f69ed5..99621da 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -73,7 +73,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
   struct dhcp_vendor *vendor;
   struct dhcp_mac *mac;
   struct dhcp_netid_list *id_list;
-  int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
+  int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1, force_proxy = 0;
   struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
   unsigned char *end = (unsigned char *)(mess + 1); 
   unsigned char *real_end = (unsigned char *)(mess + 1); 
@@ -486,6 +486,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
       netid = &known_id;
     }
   
+  my_syslog(MS_DHCP | LOG_INFO, _("Msg type: %d PXE: %d"), mess_type, pxe);
   if (mess_type == 0 && !pxe)
     {
       /* BOOTP request */
@@ -746,6 +747,11 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
     if (match_netid(id_list->list, tagif_netid, 0))
       ignore = 1;
 
+  /* if all the netids in the proxy force list are present, allow proxy mode */
+  for (id_list = daemon->dhcp_force_proxy; id_list; id_list = id_list->next)
+    if (match_netid(id_list->list, tagif_netid, 0))
+      force_proxy = 1;
+
   /* If configured, we can override the server-id to be the address of the relay, 
      so that all traffic goes via the relay and can pick up agent-id info. This can be
      configured for all relays, or by address. */
@@ -767,7 +773,61 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
   /* Can have setting to ignore the client ID for a particular MAC address or hostname */
   if (have_config(config, CONFIG_NOCLID))
     clid = NULL;
-          
+
+  if (force_proxy)
+    {
+      if (mess_type == DHCPDISCOVER)
+	{
+	  struct dhcp_context *tmp;
+
+	  log_packet("DHCPDISCOVER", NULL, emac, emac_len, iface_name, NULL, message, mess->xid);
+
+	  for (tmp = context; tmp; tmp = tmp->current)
+	    if ((tmp->flags & CONTEXT_PROXY) &&
+		match_netid(tmp->filter, tagif_netid, 1))
+	      break;
+
+	  if (tmp)
+	    {
+	      if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
+		{
+		  memcpy(pxe_uuid, option_ptr(opt, 0), 17);
+		  uuid = pxe_uuid;
+		  my_syslog(MS_DHCP | LOG_INFO, _("PXE UUID found"));
+		}
+
+	      if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
+		{
+		  req_options = (unsigned char *)daemon->dhcp_buff2;
+		  memcpy(req_options, option_ptr(opt, 0), option_len(opt));
+		  req_options[option_len(opt)] = OPTION_END;
+		}
+
+	      if (tmp->netid.net)
+		{
+		  tmp->netid.next = netid;
+		  tagif_netid = run_tag_if(&tmp->netid);
+		}
+
+	      mess->yiaddr.s_addr = 0;
+	      mess->ciaddr.s_addr = 0;
+	      mess->flags |= htons(0x8000); /* broadcast */
+
+	      clear_packet(mess, end);
+
+	      log_tags(tagif_netid, ntohl(mess->xid));
+	      log_packet("DHCPOFFER", NULL, emac, emac_len, iface_name, ignore ? "force-proxy-ignored" : "force-proxy", NULL, mess->xid);
+
+	      option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
+	      option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
+	      do_options(tmp, mess, end, req_options, offer_hostname, NULL,
+			 netid, subnet_addr, fqdn_flags, borken_opt, -1, uuid, vendor_class_len, now, 0xffffffff, 0);
+
+	      return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
+	    }
+	}
+    }
+
   /* Check if client is PXE client. */
   if (daemon->enable_pxe && 
       (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) && 
-- 
2.1.4




More information about the Dnsmasq-discuss mailing list