version 1.1.1.2, 2014/06/15 16:31:38
|
version 1.1.1.3, 2016/11/02 09:57:01
|
Line 1
|
Line 1
|
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley | /* dnsmasq is Copyright (c) 2000-2016 Simon Kelley |
|
|
This program is free software; you can redistribute it and/or modify |
This program is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
Line 52 static void do_options(struct dhcp_context *context,
|
Line 52 static void do_options(struct dhcp_context *context,
|
int null_term, int pxearch, |
int null_term, int pxearch, |
unsigned char *uuid, |
unsigned char *uuid, |
int vendor_class_len, |
int vendor_class_len, |
time_t now); | time_t now, |
| unsigned int lease_time, |
| unsigned short fuzz); |
|
|
|
|
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); |
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); |
Line 61 static void pxe_misc(struct dhcp_packet *mess, unsigne
|
Line 63 static void pxe_misc(struct dhcp_packet *mess, unsigne
|
static int prune_vendor_opts(struct dhcp_netid *netid); |
static int prune_vendor_opts(struct dhcp_netid *netid); |
static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now); |
static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now); |
struct dhcp_boot *find_boot(struct dhcp_netid *netid); |
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); |
|
|
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, |
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, |
size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback) |
size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback) |
Line 610 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 612 size_t dhcp_reply(struct dhcp_context *context, char *
|
|
|
clear_packet(mess, end); |
clear_packet(mess, end); |
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), |
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), |
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now); | netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0); |
} |
} |
} |
} |
|
|
Line 803 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 805 size_t dhcp_reply(struct dhcp_context *context, char *
|
if (service->type == type) |
if (service->type == type) |
break; |
break; |
|
|
if (!service || !service->basename) | for (; context; context = context->current) |
return 0; | if (match_netid(context->filter, tagif_netid, 1) && |
| is_same_net(mess->ciaddr, context->start, context->netmask)) |
| break; |
|
|
|
if (!service || !service->basename || !context) |
|
return 0; |
|
|
clear_packet(mess, end); |
clear_packet(mess, end); |
|
|
mess->yiaddr = mess->ciaddr; |
mess->yiaddr = mess->ciaddr; |
Line 817 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 824 size_t dhcp_reply(struct dhcp_context *context, char *
|
else |
else |
mess->siaddr = context->local; |
mess->siaddr = context->local; |
|
|
snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer); | snprintf((char *)mess->file, sizeof(mess->file), |
| strchr(service->basename, '.') ? "%s" :"%s.%d", |
| service->basename, layer); |
| |
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); |
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); |
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr)); |
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr)); |
pxe_misc(mess, end, uuid); |
pxe_misc(mess, end, uuid); |
Line 844 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 854 size_t dhcp_reply(struct dhcp_context *context, char *
|
if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST))) |
if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST))) |
{ |
{ |
struct dhcp_context *tmp; |
struct dhcp_context *tmp; |
|
int workaround = 0; |
|
|
for (tmp = context; tmp; tmp = tmp->current) |
for (tmp = context; tmp; tmp = tmp->current) |
if ((tmp->flags & CONTEXT_PROXY) && |
if ((tmp->flags & CONTEXT_PROXY) && |
Line 853 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 864 size_t dhcp_reply(struct dhcp_context *context, char *
|
if (tmp) |
if (tmp) |
{ |
{ |
struct dhcp_boot *boot; |
struct dhcp_boot *boot; |
| int redirect4011 = 0; |
| |
if (tmp->netid.net) |
if (tmp->netid.net) |
{ |
{ |
tmp->netid.next = netid; |
tmp->netid.next = netid; |
Line 871 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 883 size_t dhcp_reply(struct dhcp_context *context, char *
|
|
|
clear_packet(mess, end); |
clear_packet(mess, end); |
|
|
/* Provide the bootfile here, for gPXE, and in case we have no menu items | /* Redirect EFI clients to port 4011 */ |
and set discovery_control = 8 */ | if (pxearch >= 6) |
if (boot) | |
{ |
{ |
|
redirect4011 = 1; |
|
mess->siaddr = tmp->local; |
|
} |
|
|
|
/* Returns true if only one matching service is available. On port 4011, |
|
it also inserts the boot file and server name. */ |
|
workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe); |
|
|
|
if (!workaround && boot) |
|
{ |
|
/* Provide the bootfile here, for gPXE, and in case we have no menu items |
|
and set discovery_control = 8 */ |
if (boot->next_server.s_addr) |
if (boot->next_server.s_addr) |
mess->siaddr = boot->next_server; |
mess->siaddr = boot->next_server; |
else if (boot->tftp_sname) |
else if (boot->tftp_sname) |
Line 886 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 909 size_t dhcp_reply(struct dhcp_context *context, char *
|
|
|
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, |
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, |
mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK); |
mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK); |
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr)); | option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr)); |
pxe_misc(mess, end, uuid); |
pxe_misc(mess, end, uuid); |
prune_vendor_opts(tagif_netid); |
prune_vendor_opts(tagif_netid); |
do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); | 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); |
| |
log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid); |
log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid); |
log_tags(tagif_netid, ntohl(mess->xid)); |
log_tags(tagif_netid, ntohl(mess->xid)); |
return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end); |
return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end); |
Line 1042 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1066 size_t dhcp_reply(struct dhcp_context *context, char *
|
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr)); |
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr)); |
option_put(mess, end, OPTION_LEASE_TIME, 4, time); |
option_put(mess, end, OPTION_LEASE_TIME, 4, time); |
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */ |
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */ |
if (time != 0xffffffff) |
|
{ |
|
option_put(mess, end, OPTION_T1, 4, (time/2)); |
|
option_put(mess, end, OPTION_T2, 4, (time*7)/8); |
|
} |
|
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), |
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), |
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); | netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz); |
|
|
return dhcp_packet_size(mess, agent_id, real_end); |
return dhcp_packet_size(mess, agent_id, real_end); |
|
|
Line 1306 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1325 size_t dhcp_reply(struct dhcp_context *context, char *
|
/* If the user-class option started as counted strings, the first byte will be zero. */ |
/* If the user-class option started as counted strings, the first byte will be zero. */ |
if (len != 0 && ucp[0] == 0) |
if (len != 0 && ucp[0] == 0) |
ucp++, len--; |
ucp++, len--; |
lease_add_extradata(lease, ucp, len, 0); | lease_add_extradata(lease, ucp, len, -1); |
} |
} |
} |
} |
#endif |
#endif |
Line 1367 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1386 size_t dhcp_reply(struct dhcp_context *context, char *
|
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); |
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); |
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr)); |
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr)); |
option_put(mess, end, OPTION_LEASE_TIME, 4, time); |
option_put(mess, end, OPTION_LEASE_TIME, 4, time); |
if (time != 0xffffffff) |
|
{ |
|
while (fuzz > (time/16)) |
|
fuzz = fuzz/2; |
|
option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz); |
|
option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz); |
|
} |
|
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), |
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), |
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); | netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz); |
} |
} |
|
|
return dhcp_packet_size(mess, agent_id, real_end); |
return dhcp_packet_size(mess, agent_id, real_end); |
Line 1440 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1452 size_t dhcp_reply(struct dhcp_context *context, char *
|
} |
} |
|
|
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr), |
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr), |
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); | netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0); |
|
|
*is_inform = 1; /* handle reply differently */ |
*is_inform = 1; /* handle reply differently */ |
return dhcp_packet_size(mess, agent_id, real_end); |
return dhcp_packet_size(mess, agent_id, real_end); |
Line 1980 static int prune_vendor_opts(struct dhcp_netid *netid)
|
Line 1992 static int prune_vendor_opts(struct dhcp_netid *netid)
|
return force; |
return force; |
} |
} |
|
|
|
|
|
/* Many UEFI PXE implementations have badly broken menu code. |
|
If there's exactly one relevant menu item, we abandon the menu system, |
|
and jamb the data direct into the DHCP file, siaddr and sname fields. |
|
Note that in this case, we have to assume that layer zero would be requested |
|
by the client PXE stack. */ |
|
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) |
|
{ |
|
struct pxe_service *service, *found; |
|
|
|
/* Only workaround UEFI archs. */ |
|
if (pxe_arch < 6) |
|
return 0; |
|
|
|
for (found = NULL, service = daemon->pxe_services; service; service = service->next) |
|
if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1)) |
|
{ |
|
if (found) |
|
return 0; /* More than one relevant menu item */ |
|
|
|
found = service; |
|
} |
|
|
|
if (!found) |
|
return 0; /* No relevant menu items. */ |
|
|
|
if (!pxe) |
|
return 1; |
|
|
|
if (found->sname) |
|
{ |
|
mess->siaddr = a_record_from_hosts(found->sname, now); |
|
snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname); |
|
} |
|
else |
|
{ |
|
if (found->server.s_addr != 0) |
|
mess->siaddr = found->server; |
|
else |
|
mess->siaddr = local; |
|
|
|
inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN); |
|
} |
|
|
|
snprintf((char *)mess->file, sizeof(mess->file), |
|
strchr(found->basename, '.') ? "%s" : "%s.0", found->basename); |
|
|
|
return 1; |
|
} |
|
|
static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now) |
static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now) |
{ |
{ |
#define NUM_OPTS 4 |
#define NUM_OPTS 4 |
Line 2137 static void do_options(struct dhcp_context *context,
|
Line 2199 static void do_options(struct dhcp_context *context,
|
int null_term, int pxe_arch, |
int null_term, int pxe_arch, |
unsigned char *uuid, |
unsigned char *uuid, |
int vendor_class_len, |
int vendor_class_len, |
time_t now) | time_t now, |
| unsigned int lease_time, |
| unsigned short fuzz) |
{ |
{ |
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts; |
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts; |
struct dhcp_boot *boot; |
struct dhcp_boot *boot; |
Line 2261 static void do_options(struct dhcp_context *context,
|
Line 2325 static void do_options(struct dhcp_context *context,
|
/* rfc3011 says this doesn't need to be in the requested options list. */ |
/* rfc3011 says this doesn't need to be in the requested options list. */ |
if (subnet_addr.s_addr) |
if (subnet_addr.s_addr) |
option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr)); |
option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr)); |
| |
| if (lease_time != 0xffffffff) |
| { |
| unsigned int t1val = lease_time/2; |
| unsigned int t2val = (lease_time*7)/8; |
| unsigned int hval; |
| |
| /* If set by user, sanity check, so not longer than lease. */ |
| if ((opt = option_find2(OPTION_T1))) |
| { |
| hval = ntohl(*((unsigned int *)opt->val)); |
| if (hval < lease_time && hval > 2) |
| t1val = hval; |
| } |
| |
| if ((opt = option_find2(OPTION_T2))) |
| { |
| hval = ntohl(*((unsigned int *)opt->val)); |
| if (hval < lease_time && hval > 2) |
| t2val = hval; |
| } |
| |
| /* ensure T1 is still < T2 */ |
| if (t2val <= t1val) |
| t1val = t2val - 1; |
| |
| while (fuzz > (t1val/8)) |
| fuzz = fuzz/2; |
| |
| t1val -= fuzz; |
| t2val -= fuzz; |
| |
| option_put(mess, end, OPTION_T1, 4, t1val); |
| option_put(mess, end, OPTION_T2, 4, t2val); |
| } |
| |
/* replies to DHCPINFORM may not have a valid context */ |
/* replies to DHCPINFORM may not have a valid context */ |
if (context) |
if (context) |
{ |
{ |
Line 2356 static void do_options(struct dhcp_context *context,
|
Line 2455 static void do_options(struct dhcp_context *context,
|
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno)) |
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno)) |
continue; |
continue; |
|
|
/* prohibit some used-internally options */ | /* prohibit some used-internally options. T1 and T2 already handled. */ |
if (optno == OPTION_CLIENT_FQDN || |
if (optno == OPTION_CLIENT_FQDN || |
optno == OPTION_MAXMESSAGE || |
optno == OPTION_MAXMESSAGE || |
optno == OPTION_OVERLOAD || |
optno == OPTION_OVERLOAD || |
optno == OPTION_PAD || |
optno == OPTION_PAD || |
optno == OPTION_END) | optno == OPTION_END || |
| optno == OPTION_T1 || |
| optno == OPTION_T2) |
continue; |
continue; |
|
|
if (optno == OPTION_SNAME && done_server) |
if (optno == OPTION_SNAME && done_server) |
Line 2475 static void do_options(struct dhcp_context *context,
|
Line 2576 static void do_options(struct dhcp_context *context,
|
if (context && pxe_arch != -1) |
if (context && pxe_arch != -1) |
{ |
{ |
pxe_misc(mess, end, uuid); |
pxe_misc(mess, end, uuid); |
config_opts = pxe_opts(pxe_arch, tagif, context->local, now); | 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)) && |
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) && |