mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-20 02:38:32 +00:00
Add --dhcp-option-pxe config.
This acts almost exactly like --dhcp-option except that the defined option is only sent when replying to PXE clients. More importantly, these options are sent in reply PXE clients when dnsmasq in acting in PXE proxy mode. In PXE proxy mode, the set of options sent is defined by the PXE standard and the normal set of options is not sent. This config allows arbitrary options in PXE-proxy replies. A typical use-case is to send option 175 to iPXE. Thanks to Jason Berry for finding the requirement for this.
This commit is contained in:
@@ -40,6 +40,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
|
||||
static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
|
||||
static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
|
||||
static int in_list(unsigned char *list, int opt);
|
||||
static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len);
|
||||
static void do_options(struct dhcp_context *context,
|
||||
struct dhcp_packet *mess,
|
||||
unsigned char *end,
|
||||
@@ -67,6 +68,8 @@ struct dhcp_boot *find_boot(struct dhcp_netid *netid);
|
||||
static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe);
|
||||
static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid);
|
||||
static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char **pxe_vendor);
|
||||
static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term);
|
||||
static void handle_encap(struct dhcp_packet *mess, unsigned char *end, unsigned char *req_options, int null_term, struct dhcp_netid *tagif, int pxemode);
|
||||
|
||||
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
size_t sz, time_t now, int unicast_dest, int loopback,
|
||||
@@ -955,13 +958,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
{
|
||||
struct dhcp_boot *boot;
|
||||
int redirect4011 = 0;
|
||||
struct dhcp_opt *option;
|
||||
|
||||
if (tmp->netid.net)
|
||||
{
|
||||
tmp->netid.next = netid;
|
||||
tagif_netid = run_tag_if(&tmp->netid);
|
||||
}
|
||||
|
||||
/* OK only dhcp-option-pxe options. */
|
||||
tagif_netid = option_filter(netid, tmp->netid.net ? &tmp->netid : NULL, daemon->dhcp_opts, 2);
|
||||
boot = find_boot(tagif_netid);
|
||||
|
||||
mess->yiaddr.s_addr = 0;
|
||||
@@ -1004,7 +1004,24 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
prune_vendor_opts(tagif_netid);
|
||||
if ((pxe && !workaround) || !redirect4011)
|
||||
do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
|
||||
|
||||
|
||||
/* dhcp-option-pxe ONLY */
|
||||
for (option = daemon->dhcp_opts; option; option = option->next)
|
||||
{
|
||||
int len;
|
||||
unsigned char *p;
|
||||
|
||||
if (!(option->flags & DHOPT_TAGOK))
|
||||
continue;
|
||||
|
||||
len = do_opt(option, NULL, tmp, borken_opt);
|
||||
|
||||
if ((p = free_space(mess, end, option->opt, len)))
|
||||
do_opt(option, p, tmp, borken_opt);
|
||||
}
|
||||
|
||||
handle_encap(mess, end, req_options, borken_opt, tagif_netid, 2);
|
||||
|
||||
daemon->metrics[METRIC_PXE]++;
|
||||
log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
|
||||
log_tags(tagif_netid, ntohl(mess->xid));
|
||||
@@ -2409,7 +2426,7 @@ static void do_options(struct dhcp_context *context,
|
||||
/* filter options based on tags, those we want get DHOPT_TAGOK bit set */
|
||||
if (context)
|
||||
context->netid.next = NULL;
|
||||
tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
|
||||
tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts, pxe_arch != -1);
|
||||
|
||||
/* logging */
|
||||
if (option_bool(OPT_LOG_OPTS) && req_options)
|
||||
@@ -2699,19 +2716,55 @@ static void do_options(struct dhcp_context *context,
|
||||
}
|
||||
}
|
||||
|
||||
/* Now send options to be encapsulated in arbitrary options,
|
||||
/* encapsulated options. */
|
||||
handle_encap(mess, end, req_options, null_term, tagif, pxe_arch != 1);
|
||||
|
||||
force_encap = prune_vendor_opts(tagif);
|
||||
|
||||
if (context && pxe_arch != -1)
|
||||
{
|
||||
pxe_misc(mess, end, uuid, pxevendor);
|
||||
if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
|
||||
config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
|
||||
}
|
||||
|
||||
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
|
||||
do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
|
||||
pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
|
||||
(p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
|
||||
/* If we send vendor encapsulated options, and haven't already sent option 60,
|
||||
echo back the value we got from the client. */
|
||||
memcpy(p, daemon->dhcp_buff3, vendor_class_len);
|
||||
|
||||
/* restore BOOTP anti-overload hack */
|
||||
if (!req_options || option_bool(OPT_NO_OVERRIDE))
|
||||
{
|
||||
mess->file[0] = f0;
|
||||
mess->sname[0] = s0;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_encap(struct dhcp_packet *mess, unsigned char *end, unsigned char *req_options,
|
||||
int null_term, struct dhcp_netid *tagif, int pxemode)
|
||||
{
|
||||
/* Send options to be encapsulated in arbitrary options,
|
||||
eg dhcp-option=encap:172,17,.......
|
||||
Also handle vendor-identifying vendor-encapsulated options,
|
||||
dhcp-option = vi-encap:13,17,.......
|
||||
The may be more that one "outer" to do, so group
|
||||
all the options which match each outer in turn. */
|
||||
|
||||
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
|
||||
unsigned char *p;
|
||||
int i, len;
|
||||
|
||||
for (opt = config_opts; opt; opt = opt->next)
|
||||
opt->flags &= ~DHOPT_ENCAP_DONE;
|
||||
|
||||
for (opt = config_opts; opt; opt = opt->next)
|
||||
{
|
||||
int flags;
|
||||
|
||||
|
||||
if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
|
||||
{
|
||||
int found = 0;
|
||||
@@ -2731,6 +2784,7 @@ static void do_options(struct dhcp_context *context,
|
||||
|
||||
o->flags |= DHOPT_ENCAP_DONE;
|
||||
if (match_netid(o->netid, tagif, 1) &&
|
||||
pxe_ok(o, pxemode) &&
|
||||
((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
|
||||
{
|
||||
o->flags |= DHOPT_ENCAP_MATCH;
|
||||
@@ -2763,30 +2817,6 @@ static void do_options(struct dhcp_context *context,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
force_encap = prune_vendor_opts(tagif);
|
||||
|
||||
if (context && pxe_arch != -1)
|
||||
{
|
||||
pxe_misc(mess, end, uuid, pxevendor);
|
||||
if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
|
||||
config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
|
||||
}
|
||||
|
||||
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
|
||||
do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
|
||||
pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
|
||||
(p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
|
||||
/* If we send vendor encapsulated options, and haven't already sent option 60,
|
||||
echo back the value we got from the client. */
|
||||
memcpy(p, daemon->dhcp_buff3, vendor_class_len);
|
||||
|
||||
/* restore BOOTP anti-overload hack */
|
||||
if (!req_options || option_bool(OPT_NO_OVERRIDE))
|
||||
{
|
||||
mess->file[0] = f0;
|
||||
mess->sname[0] = s0;
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
|
||||
|
||||
Reference in New Issue
Block a user