Stefan Brüns
2015-08-28 12:54:36 UTC
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 <***@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)) &&
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 <***@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
2.1.4