version 1.1.1.1, 2013/07/29 19:37:40
|
version 1.1.1.5, 2023/09/27 11:02:07
|
Line 1
|
Line 1
|
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley | /* dnsmasq is Copyright (c) 2000-2022 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 30 static struct in_addr server_id(struct dhcp_context *c
|
Line 30 static struct in_addr server_id(struct dhcp_context *c
|
static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt); |
static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt); |
static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val); |
static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val); |
static void option_put_string(struct dhcp_packet *mess, unsigned char *end, |
static void option_put_string(struct dhcp_packet *mess, unsigned char *end, |
int opt, char *string, int null_term); | int opt, const char *string, int null_term); |
static struct in_addr option_addr(unsigned char *opt); |
static struct in_addr option_addr(unsigned char *opt); |
static unsigned int option_uint(unsigned char *opt, int i, int size); | static unsigned int option_uint(unsigned char *opt, int offset, int size); |
static void log_packet(char *type, void *addr, unsigned char *ext_mac, |
static void log_packet(char *type, void *addr, unsigned char *ext_mac, |
int mac_len, char *interface, char *string, u32 xid); | int mac_len, char *interface, char *string, char *err, u32 xid); |
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize); |
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize); |
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize); |
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize); |
static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end); |
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 void clear_packet(struct dhcp_packet *mess, unsigned char *end); |
|
static int in_list(unsigned char *list, int opt); |
static void do_options(struct dhcp_context *context, |
static void do_options(struct dhcp_context *context, |
struct dhcp_packet *mess, |
struct dhcp_packet *mess, |
unsigned char *real_end, | unsigned char *end, |
unsigned char *req_options, |
unsigned char *req_options, |
char *hostname, |
char *hostname, |
char *config_domain, | char *domain, |
struct dhcp_netid *netid, |
struct dhcp_netid *netid, |
struct in_addr subnet_addr, |
struct in_addr subnet_addr, |
unsigned char fqdn_flags, |
unsigned char fqdn_flags, |
int null_term, int pxearch, | 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, |
| const char *pxevendor); |
|
|
|
|
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); |
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); |
static int do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term); | static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term); |
static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid); | static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid, const char *pxevendor); |
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); |
|
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); |
|
|
|
|
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 loopback, |
| int *is_inform, int pxe, struct in_addr fallback, time_t recvtime) |
{ |
{ |
unsigned char *opt, *clid = NULL; |
unsigned char *opt, *clid = NULL; |
struct dhcp_lease *ltmp, *lease = NULL; |
struct dhcp_lease *ltmp, *lease = NULL; |
struct dhcp_vendor *vendor; |
struct dhcp_vendor *vendor; |
struct dhcp_mac *mac; |
struct dhcp_mac *mac; |
struct dhcp_netid_list *id_list; |
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, rapid_commit = 0, selecting = 0, pxearch = -1; |
| const char *pxevendor = NULL; |
struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base; |
struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base; |
unsigned char *end = (unsigned char *)(mess + 1); |
unsigned char *end = (unsigned char *)(mess + 1); |
unsigned char *real_end = (unsigned char *)(mess + 1); |
unsigned char *real_end = (unsigned char *)(mess + 1); |
Line 91 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 99 size_t dhcp_reply(struct dhcp_context *context, char *
|
struct dhcp_netid known_id, iface_id, cpewan_id; |
struct dhcp_netid known_id, iface_id, cpewan_id; |
struct dhcp_opt *o; |
struct dhcp_opt *o; |
unsigned char pxe_uuid[17]; |
unsigned char pxe_uuid[17]; |
unsigned char *oui = NULL, *serial = NULL, *class = NULL; | unsigned char *oui = NULL, *serial = NULL; |
| #ifdef HAVE_SCRIPT |
| unsigned char *class = NULL; |
| #endif |
|
|
subnet_addr.s_addr = override.s_addr = 0; |
subnet_addr.s_addr = override.s_addr = 0; |
|
|
Line 149 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 160 size_t dhcp_reply(struct dhcp_context *context, char *
|
for (offset = 0; offset < (len - 5); offset += elen + 5) |
for (offset = 0; offset < (len - 5); offset += elen + 5) |
{ |
{ |
elen = option_uint(opt, offset + 4 , 1); |
elen = option_uint(opt, offset + 4 , 1); |
if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA) | if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA && offset + elen + 5 <= len) |
{ |
{ |
unsigned char *x = option_ptr(opt, offset + 5); |
unsigned char *x = option_ptr(opt, offset + 5); |
unsigned char *y = option_ptr(opt, offset + elen + 5); |
unsigned char *y = option_ptr(opt, offset + elen + 5); |
oui = option_find1(x, y, 1, 1); |
oui = option_find1(x, y, 1, 1); |
serial = option_find1(x, y, 2, 1); |
serial = option_find1(x, y, 2, 1); |
class = option_find1(x, y, 3, 1); | #ifdef HAVE_SCRIPT |
| class = option_find1(x, y, 3, 1); |
| #endif |
/* If TR069-id is present set the tag "cpewan-id" to facilitate echoing |
/* If TR069-id is present set the tag "cpewan-id" to facilitate echoing |
the gateway id back. Note that the device class is optional */ |
the gateway id back. Note that the device class is optional */ |
if (oui && serial) |
if (oui && serial) |
Line 179 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 191 size_t dhcp_reply(struct dhcp_context *context, char *
|
be enough free space at the end of the packet to copy the option. */ |
be enough free space at the end of the packet to copy the option. */ |
unsigned char *sopt; |
unsigned char *sopt; |
unsigned int total = option_len(opt) + 2; |
unsigned int total = option_len(opt) + 2; |
unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0); | unsigned char *last_opt = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + sz, |
| OPTION_END, 0); |
if (last_opt && last_opt < end - total) |
if (last_opt && last_opt < end - total) |
{ |
{ |
end -= total; |
end -= total; |
Line 224 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 237 size_t dhcp_reply(struct dhcp_context *context, char *
|
subnet_addr = option_addr(opt); |
subnet_addr = option_addr(opt); |
|
|
/* If there is no client identifier option, use the hardware address */ |
/* If there is no client identifier option, use the hardware address */ |
if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1))) | if (!option_bool(OPT_IGNORE_CLID) && (opt = option_find(mess, sz, OPTION_CLIENT_ID, 1))) |
{ |
{ |
clid_len = option_len(opt); |
clid_len = option_len(opt); |
clid = option_ptr(opt, 0); |
clid = option_ptr(opt, 0); |
Line 264 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 277 size_t dhcp_reply(struct dhcp_context *context, char *
|
if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr) |
if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr) |
{ |
{ |
struct dhcp_context *context_tmp, *context_new = NULL; |
struct dhcp_context *context_tmp, *context_new = NULL; |
|
struct shared_network *share = NULL; |
struct in_addr addr; |
struct in_addr addr; |
int force = 0; | int force = 0, via_relay = 0; |
|
|
if (subnet_addr.s_addr) |
if (subnet_addr.s_addr) |
{ |
{ |
Line 276 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 290 size_t dhcp_reply(struct dhcp_context *context, char *
|
{ |
{ |
addr = mess->giaddr; |
addr = mess->giaddr; |
force = 1; |
force = 1; |
|
via_relay = 1; |
} |
} |
else |
else |
{ |
{ |
Line 292 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 307 size_t dhcp_reply(struct dhcp_context *context, char *
|
} |
} |
|
|
if (!context_new) |
if (!context_new) |
for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next) | { |
{ | for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next) |
struct in_addr netmask = context_tmp->netmask; | { |
| struct in_addr netmask = context_tmp->netmask; |
| |
| /* guess the netmask for relayed networks */ |
| if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0) |
| { |
| if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr))) |
| netmask.s_addr = htonl(0xff000000); |
| else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr))) |
| netmask.s_addr = htonl(0xffff0000); |
| else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr))) |
| netmask.s_addr = htonl(0xffffff00); |
| } |
|
|
/* guess the netmask for relayed networks */ | /* check to see is a context is OK because of a shared address on |
if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0) | the relayed subnet. */ |
{ | if (via_relay) |
if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr))) | for (share = daemon->shared_networks; share; share = share->next) |
netmask.s_addr = htonl(0xff000000); | { |
else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr))) | #ifdef HAVE_DHCP6 |
netmask.s_addr = htonl(0xffff0000); | if (share->shared_addr.s_addr == 0) |
else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr))) | continue; |
netmask.s_addr = htonl(0xffffff00); | #endif |
} | if (share->if_index != 0 || |
| share->match_addr.s_addr != mess->giaddr.s_addr) |
/* This section fills in context mainly when a client which is on a remote (relayed) | continue; |
network renews a lease without using the relay, after dnsmasq has restarted. */ | |
if (netmask.s_addr != 0 && | if (netmask.s_addr != 0 && |
is_same_net(addr, context_tmp->start, netmask) && | is_same_net(share->shared_addr, context_tmp->start, netmask) && |
is_same_net(addr, context_tmp->end, netmask)) | is_same_net(share->shared_addr, context_tmp->end, netmask)) |
{ | break; |
context_tmp->netmask = netmask; | } |
if (context_tmp->local.s_addr == 0) | |
context_tmp->local = fallback; | /* This section fills in context mainly when a client which is on a remote (relayed) |
if (context_tmp->router.s_addr == 0) | network renews a lease without using the relay, after dnsmasq has restarted. */ |
context_tmp->router = mess->giaddr; | if (share || |
| (netmask.s_addr != 0 && |
/* fill in missing broadcast addresses for relayed ranges */ | is_same_net(addr, context_tmp->start, netmask) && |
if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 ) | is_same_net(addr, context_tmp->end, netmask))) |
context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr; | { |
| context_tmp->netmask = netmask; |
context_tmp->current = context_new; | if (context_tmp->local.s_addr == 0) |
context_new = context_tmp; | context_tmp->local = fallback; |
} | if (context_tmp->router.s_addr == 0 && !share) |
} | context_tmp->router = mess->giaddr; |
| |
| /* fill in missing broadcast addresses for relayed ranges */ |
| if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 ) |
| context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr; |
| |
| context_tmp->current = context_new; |
| context_new = context_tmp; |
| } |
| |
| } |
| } |
| |
if (context_new || force) |
if (context_new || force) |
context = context_new; |
context = context_new; |
} |
} |
|
|
if (!context) |
if (!context) |
{ |
{ |
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"), | const char *via; |
subnet_addr.s_addr ? _("with subnet selector") : _("via"), | if (subnet_addr.s_addr) |
subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name)); | { |
| via = _("with subnet selector"); |
| inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN); |
| } |
| else |
| { |
| via = _("via"); |
| if (mess->giaddr.s_addr) |
| inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN); |
| else |
| safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN); |
| } |
| my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"), |
| via, daemon->addrbuff); |
return 0; |
return 0; |
} |
} |
|
|
Line 345 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 396 size_t dhcp_reply(struct dhcp_context *context, char *
|
struct dhcp_context *context_tmp; |
struct dhcp_context *context_tmp; |
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current) |
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current) |
{ |
{ |
strcpy(daemon->namebuff, inet_ntoa(context_tmp->start)); | inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME); |
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) |
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) |
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"), | { |
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask)); | inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN); |
| my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"), |
| ntohl(mess->xid), daemon->namebuff, daemon->addrbuff); |
| } |
else |
else |
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"), | { |
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end)); | inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN); |
| my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"), |
| ntohl(mess->xid), daemon->namebuff, daemon->addrbuff); |
| } |
} |
} |
} |
} |
|
|
|
/* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match. |
|
Otherwise assume the option is an array, and look for a matching element. |
|
If no data given, existence of the option is enough. This code handles |
|
rfc3925 V-I classes too. */ |
|
for (o = daemon->dhcp_match; o; o = o->next) |
|
{ |
|
unsigned int len, elen, match = 0; |
|
size_t offset, o2; |
|
|
|
if (o->flags & DHOPT_RFC3925) |
|
{ |
|
if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5))) |
|
continue; |
|
|
|
for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5) |
|
{ |
|
len = option_uint(opt, offset + 4 , 1); |
|
/* Need to take care that bad data can't run us off the end of the packet */ |
|
if ((offset + len + 5 <= (unsigned)(option_len(opt))) && |
|
(option_uint(opt, offset, 4) == (unsigned int)o->u.encap)) |
|
for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1) |
|
{ |
|
elen = option_uint(opt, o2, 1); |
|
if ((o2 + elen + 1 <= (unsigned)option_len(opt)) && |
|
(match = match_bytes(o, option_ptr(opt, o2 + 1), elen))) |
|
break; |
|
} |
|
if (match) |
|
break; |
|
} |
|
} |
|
else |
|
{ |
|
if (!(opt = option_find(mess, sz, o->opt, 1))) |
|
continue; |
|
|
|
match = match_bytes(o, option_ptr(opt, 0), option_len(opt)); |
|
} |
|
|
|
if (match) |
|
{ |
|
o->netid->next = netid; |
|
netid = o->netid; |
|
} |
|
} |
|
|
|
/* user-class options are, according to RFC3004, supposed to contain |
|
a set of counted strings. Here we check that this is so (by seeing |
|
if the counts are consistent with the overall option length) and if |
|
so zero the counts so that we don't get spurious matches between |
|
the vendor string and the counts. If the lengths don't add up, we |
|
assume that the option is a single string and non RFC3004 compliant |
|
and just do the substring match. dhclient provides these broken options. |
|
The code, later, which sends user-class data to the lease-change script |
|
relies on the transformation done here. |
|
*/ |
|
|
|
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1))) |
|
{ |
|
unsigned char *ucp = option_ptr(opt, 0); |
|
int tmp, j; |
|
for (j = 0; j < option_len(opt); j += ucp[j] + 1); |
|
if (j == option_len(opt)) |
|
for (j = 0; j < option_len(opt); j = tmp) |
|
{ |
|
tmp = j + ucp[j] + 1; |
|
ucp[j] = 0; |
|
} |
|
} |
|
|
|
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) |
|
{ |
|
int mopt; |
|
|
|
if (vendor->match_type == MATCH_VENDOR) |
|
mopt = OPTION_VENDOR_ID; |
|
else if (vendor->match_type == MATCH_USER) |
|
mopt = OPTION_USER_CLASS; |
|
else |
|
continue; |
|
|
|
if ((opt = option_find(mess, sz, mopt, 1))) |
|
{ |
|
int i; |
|
for (i = 0; i <= (option_len(opt) - vendor->len); i++) |
|
if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0) |
|
{ |
|
vendor->netid.next = netid; |
|
netid = &vendor->netid; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/* mark vendor-encapsulated options which match the client-supplied vendor class, |
|
save client-supplied vendor class */ |
|
if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1))) |
|
{ |
|
memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt)); |
|
vendor_class_len = option_len(opt); |
|
} |
|
match_vendor_opts(opt, daemon->dhcp_opts); |
|
|
|
if (option_bool(OPT_LOG_OPTS)) |
|
{ |
|
if (sanitise(opt, daemon->namebuff)) |
|
my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff); |
|
if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff)) |
|
my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff); |
|
} |
|
|
mess->op = BOOTREPLY; |
mess->op = BOOTREPLY; |
|
|
config = find_config(daemon->dhcp_conf, context, clid, clid_len, |
config = find_config(daemon->dhcp_conf, context, clid, clid_len, |
mess->chaddr, mess->hlen, mess->htype, NULL); | mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid)); |
|
|
/* set "known" tag for known hosts */ |
/* set "known" tag for known hosts */ |
if (config) |
if (config) |
Line 367 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 535 size_t dhcp_reply(struct dhcp_context *context, char *
|
known_id.next = netid; |
known_id.next = netid; |
netid = &known_id; |
netid = &known_id; |
} |
} |
|
else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len, |
|
mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid))) |
|
{ |
|
known_id.net = "known-othernet"; |
|
known_id.next = netid; |
|
netid = &known_id; |
|
} |
|
|
if (mess_type == 0 && !pxe) |
if (mess_type == 0 && !pxe) |
{ |
{ |
Line 448 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 623 size_t dhcp_reply(struct dhcp_context *context, char *
|
lease_prune(lease, now); |
lease_prune(lease, now); |
lease = NULL; |
lease = NULL; |
} |
} |
if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now)) | if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now, loopback)) |
message = _("no address available"); |
message = _("no address available"); |
} |
} |
else |
else |
Line 493 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 668 size_t dhcp_reply(struct dhcp_context *context, char *
|
lease_set_interface(lease, int_index, now); |
lease_set_interface(lease, int_index, now); |
|
|
clear_packet(mess, end); |
clear_packet(mess, end); |
match_vendor_opts(NULL, daemon->dhcp_opts); /* clear flags */ |
|
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, 0, now); | netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL); |
} |
} |
} |
} |
|
|
log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid); | daemon->metrics[METRIC_BOOTP]++; |
| log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid); |
|
|
return message ? 0 : dhcp_packet_size(mess, agent_id, real_end); |
return message ? 0 : dhcp_packet_size(mess, agent_id, real_end); |
} |
} |
|
|
if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4))) | if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3))) |
{ |
{ |
/* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */ |
/* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */ |
int len = option_len(opt); |
int len = option_len(opt); |
Line 534 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 709 size_t dhcp_reply(struct dhcp_context *context, char *
|
} |
} |
|
|
if (fqdn_flags & 0x04) |
if (fqdn_flags & 0x04) |
while (*op != 0 && ((op + (*op) + 1) - pp) < len) | while (*op != 0 && ((op + (*op)) - pp) < len) |
{ |
{ |
memcpy(pq, op+1, *op); |
memcpy(pq, op+1, *op); |
pq += *op; |
pq += *op; |
Line 572 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 747 size_t dhcp_reply(struct dhcp_context *context, char *
|
client_hostname = daemon->dhcp_buff; |
client_hostname = daemon->dhcp_buff; |
} |
} |
|
|
if (client_hostname && option_bool(OPT_LOG_OPTS)) | if (client_hostname) |
my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname); | { |
| struct dhcp_match_name *m; |
| size_t nl = strlen(client_hostname); |
| |
| if (option_bool(OPT_LOG_OPTS)) |
| my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname); |
| for (m = daemon->dhcp_name_match; m; m = m->next) |
| { |
| size_t ml = strlen(m->name); |
| char save = 0; |
| |
| if (nl < ml) |
| continue; |
| if (nl > ml) |
| { |
| save = client_hostname[ml]; |
| client_hostname[ml] = 0; |
| } |
| |
| if (hostname_isequal(client_hostname, m->name) && |
| (save == 0 || m->wildcard)) |
| { |
| m->netid->next = netid; |
| netid = m->netid; |
| } |
| |
| if (save != 0) |
| client_hostname[ml] = save; |
| } |
| } |
|
|
if (have_config(config, CONFIG_NAME)) |
if (have_config(config, CONFIG_NAME)) |
{ |
{ |
Line 591 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 795 size_t dhcp_reply(struct dhcp_context *context, char *
|
if (strlen(client_hostname) != 0) |
if (strlen(client_hostname) != 0) |
{ |
{ |
hostname = client_hostname; |
hostname = client_hostname; |
|
|
if (!config) |
if (!config) |
{ |
{ |
/* Search again now we have a hostname. |
/* Search again now we have a hostname. |
Line 598 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 803 size_t dhcp_reply(struct dhcp_context *context, char *
|
to avoid impersonation by name. */ |
to avoid impersonation by name. */ |
struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, |
struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, |
mess->chaddr, mess->hlen, |
mess->chaddr, mess->hlen, |
mess->htype, hostname); | mess->htype, hostname, run_tag_if(netid)); |
if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) |
if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) |
{ |
{ |
config = new; |
config = new; |
Line 622 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 827 size_t dhcp_reply(struct dhcp_context *context, char *
|
} |
} |
} |
} |
|
|
/* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match. |
|
Otherwise assume the option is an array, and look for a matching element. |
|
If no data given, existance of the option is enough. This code handles |
|
rfc3925 V-I classes too. */ |
|
for (o = daemon->dhcp_match; o; o = o->next) |
|
{ |
|
unsigned int len, elen, match = 0; |
|
size_t offset, o2; |
|
|
|
if (o->flags & DHOPT_RFC3925) |
|
{ |
|
if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5))) |
|
continue; |
|
|
|
for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5) |
|
{ |
|
len = option_uint(opt, offset + 4 , 1); |
|
/* Need to take care that bad data can't run us off the end of the packet */ |
|
if ((offset + len + 5 <= (option_len(opt))) && |
|
(option_uint(opt, offset, 4) == (unsigned int)o->u.encap)) |
|
for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1) |
|
{ |
|
elen = option_uint(opt, o2, 1); |
|
if ((o2 + elen + 1 <= option_len(opt)) && |
|
(match = match_bytes(o, option_ptr(opt, o2 + 1), elen))) |
|
break; |
|
} |
|
if (match) |
|
break; |
|
} |
|
} |
|
else |
|
{ |
|
if (!(opt = option_find(mess, sz, o->opt, 1))) |
|
continue; |
|
|
|
match = match_bytes(o, option_ptr(opt, 0), option_len(opt)); |
|
} |
|
|
|
if (match) |
|
{ |
|
o->netid->next = netid; |
|
netid = o->netid; |
|
} |
|
} |
|
|
|
/* user-class options are, according to RFC3004, supposed to contain |
|
a set of counted strings. Here we check that this is so (by seeing |
|
if the counts are consistent with the overall option length) and if |
|
so zero the counts so that we don't get spurious matches between |
|
the vendor string and the counts. If the lengths don't add up, we |
|
assume that the option is a single string and non RFC3004 compliant |
|
and just do the substring match. dhclient provides these broken options. |
|
The code, later, which sends user-class data to the lease-change script |
|
relies on the transformation done here. |
|
*/ |
|
|
|
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1))) |
|
{ |
|
unsigned char *ucp = option_ptr(opt, 0); |
|
int tmp, j; |
|
for (j = 0; j < option_len(opt); j += ucp[j] + 1); |
|
if (j == option_len(opt)) |
|
for (j = 0; j < option_len(opt); j = tmp) |
|
{ |
|
tmp = j + ucp[j] + 1; |
|
ucp[j] = 0; |
|
} |
|
} |
|
|
|
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) |
|
{ |
|
int mopt; |
|
|
|
if (vendor->match_type == MATCH_VENDOR) |
|
mopt = OPTION_VENDOR_ID; |
|
else if (vendor->match_type == MATCH_USER) |
|
mopt = OPTION_USER_CLASS; |
|
else |
|
continue; |
|
|
|
if ((opt = option_find(mess, sz, mopt, 1))) |
|
{ |
|
int i; |
|
for (i = 0; i <= (option_len(opt) - vendor->len); i++) |
|
if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0) |
|
{ |
|
vendor->netid.next = netid; |
|
netid = &vendor->netid; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/* mark vendor-encapsulated options which match the client-supplied vendor class, |
|
save client-supplied vendor class */ |
|
if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1))) |
|
{ |
|
memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt)); |
|
vendor_class_len = option_len(opt); |
|
} |
|
match_vendor_opts(opt, daemon->dhcp_opts); |
|
|
|
if (option_bool(OPT_LOG_OPTS)) |
|
{ |
|
if (sanitise(opt, daemon->namebuff)) |
|
my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff); |
|
if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff)) |
|
my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff); |
|
} |
|
|
|
tagif_netid = run_tag_if(netid); |
tagif_netid = run_tag_if(netid); |
| |
/* if all the netids in the ignore list are present, ignore this client */ |
/* if all the netids in the ignore list are present, ignore this client */ |
for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next) |
for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next) |
if (match_netid(id_list->list, tagif_netid, 0)) |
if (match_netid(id_list->list, tagif_netid, 0)) |
Line 763 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 857 size_t dhcp_reply(struct dhcp_context *context, char *
|
clid = NULL; |
clid = NULL; |
|
|
/* Check if client is PXE client. */ |
/* Check if client is PXE client. */ |
if (daemon->enable_pxe && | if (daemon->enable_pxe && |
(opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) && | is_pxe_client(mess, sz, &pxevendor)) |
strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0) | |
{ |
{ |
if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17))) |
if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17))) |
{ |
{ |
Line 799 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 892 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 813 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 911 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); | if (strchr(service->basename, '.')) |
| snprintf((char *)mess->file, sizeof(mess->file), |
| "%s", service->basename); |
| else |
| snprintf((char *)mess->file, sizeof(mess->file), |
| "%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, pxevendor); |
|
|
prune_vendor_opts(tagif_netid); |
prune_vendor_opts(tagif_netid); |
opt71.val = save71; |
opt71.val = save71; |
Line 827 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 931 size_t dhcp_reply(struct dhcp_context *context, char *
|
opt71.next = daemon->dhcp_opts; |
opt71.next = daemon->dhcp_opts; |
do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); |
do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); |
|
|
log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid); | log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid); |
log_tags(tagif_netid, ntohl(mess->xid)); |
log_tags(tagif_netid, ntohl(mess->xid)); |
return dhcp_packet_size(mess, agent_id, real_end); |
return dhcp_packet_size(mess, agent_id, real_end); |
} |
} |
Line 840 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 944 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 848 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 953 size_t dhcp_reply(struct dhcp_context *context, char *
|
|
|
if (tmp) |
if (tmp) |
{ |
{ |
struct dhcp_boot *boot = find_boot(tagif_netid); | struct dhcp_boot *boot; |
| int redirect4011 = 0; |
| |
| if (tmp->netid.net) |
| { |
| tmp->netid.next = netid; |
| tagif_netid = run_tag_if(&tmp->netid); |
| } |
| |
| boot = find_boot(tagif_netid); |
| |
mess->yiaddr.s_addr = 0; |
mess->yiaddr.s_addr = 0; |
if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0) |
if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0) |
{ |
{ |
Line 859 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 973 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 iPXE, 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) |
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now); |
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now); |
|
|
if (boot->file) |
if (boot->file) |
strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1); | safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file)); |
} |
} |
|
|
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, pxevendor); |
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", mess->xid); | |
| 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)); |
log_tags(tagif_netid, ntohl(mess->xid)); |
|
if (!ignore) |
|
apply_delay(mess->xid, recvtime, tagif_netid); |
return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end); |
return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end); |
} |
} |
} |
} |
Line 911 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1040 size_t dhcp_reply(struct dhcp_context *context, char *
|
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) |
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ))) |
return 0; |
return 0; |
|
|
log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid); | daemon->metrics[METRIC_DHCPDECLINE]++; |
| log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, NULL, daemon->dhcp_buff, mess->xid); |
|
|
if (lease && lease->addr.s_addr == option_addr(opt).s_addr) |
if (lease && lease->addr.s_addr == option_addr(opt).s_addr) |
lease_prune(lease, now); |
lease_prune(lease, now); |
Line 920 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1050 size_t dhcp_reply(struct dhcp_context *context, char *
|
config->addr.s_addr == option_addr(opt).s_addr) |
config->addr.s_addr == option_addr(opt).s_addr) |
{ |
{ |
prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF); |
prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF); |
|
inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN); |
my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), |
my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), |
inet_ntoa(config->addr), daemon->dhcp_buff); | daemon->addrbuff, daemon->dhcp_buff); |
config->flags |= CONFIG_DECLINED; |
config->flags |= CONFIG_DECLINED; |
config->decline_time = now; |
config->decline_time = now; |
} |
} |
Line 943 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1074 size_t dhcp_reply(struct dhcp_context *context, char *
|
else |
else |
message = _("unknown lease"); |
message = _("unknown lease"); |
|
|
log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid); | daemon->metrics[METRIC_DHCPRELEASE]++; |
| log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid); |
|
|
return 0; |
return 0; |
|
|
case DHCPDISCOVER: |
case DHCPDISCOVER: |
if (ignore || have_config(config, CONFIG_DISABLE)) |
if (ignore || have_config(config, CONFIG_DISABLE)) |
{ |
{ |
|
if (option_bool(OPT_QUIET_DHCP)) |
|
return 0; |
message = _("ignored"); |
message = _("ignored"); |
opt = NULL; |
opt = NULL; |
} |
} |
Line 964 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1098 size_t dhcp_reply(struct dhcp_context *context, char *
|
|
|
if (have_config(config, CONFIG_ADDR)) |
if (have_config(config, CONFIG_ADDR)) |
{ |
{ |
char *addrs = inet_ntoa(config->addr); | inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN); |
|
|
if ((ltmp = lease_find_by_addr(config->addr)) && |
if ((ltmp = lease_find_by_addr(config->addr)) && |
ltmp != lease && |
ltmp != lease && |
Line 974 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1108 size_t dhcp_reply(struct dhcp_context *context, char *
|
unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len, |
unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len, |
ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len); |
ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len); |
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"), |
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"), |
addrs, print_mac(daemon->namebuff, mac, len)); | daemon->addrbuff, print_mac(daemon->namebuff, mac, len)); |
} |
} |
else |
else |
{ |
{ |
Line 983 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1117 size_t dhcp_reply(struct dhcp_context *context, char *
|
if (context->router.s_addr == config->addr.s_addr) |
if (context->router.s_addr == config->addr.s_addr) |
break; |
break; |
if (tmp) |
if (tmp) |
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs); | my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), daemon->addrbuff); |
else if (have_config(config, CONFIG_DECLINED) && |
else if (have_config(config, CONFIG_DECLINED) && |
difftime(now, config->decline_time) < (float)DECLINE_BACKOFF) |
difftime(now, config->decline_time) < (float)DECLINE_BACKOFF) |
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs); | my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), daemon->addrbuff); |
else |
else |
conf = config->addr; |
conf = config->addr; |
} |
} |
Line 999 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1133 size_t dhcp_reply(struct dhcp_context *context, char *
|
!config_find_by_address(daemon->dhcp_conf, lease->addr)) |
!config_find_by_address(daemon->dhcp_conf, lease->addr)) |
mess->yiaddr = lease->addr; |
mess->yiaddr = lease->addr; |
else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) && |
else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) && |
!config_find_by_address(daemon->dhcp_conf, addr)) | !config_find_by_address(daemon->dhcp_conf, addr) && do_icmp_ping(now, addr, 0, loopback)) |
mess->yiaddr = addr; |
mess->yiaddr = addr; |
else if (emac_len == 0) |
else if (emac_len == 0) |
message = _("no unique-id"); |
message = _("no unique-id"); |
else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now)) | else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now, loopback)) |
message = _("no address available"); |
message = _("no address available"); |
} |
} |
|
|
log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid); | daemon->metrics[METRIC_DHCPDISCOVER]++; |
| log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, NULL, message, mess->xid); |
|
|
if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid))) |
if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid))) |
return 0; |
return 0; |
Line 1018 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1153 size_t dhcp_reply(struct dhcp_context *context, char *
|
tagif_netid = run_tag_if(&context->netid); |
tagif_netid = run_tag_if(&context->netid); |
} |
} |
|
|
|
apply_delay(mess->xid, recvtime, tagif_netid); |
|
|
|
if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0)) |
|
{ |
|
rapid_commit = 1; |
|
/* If a lease exists for this host and another address, squash it. */ |
|
if (lease && lease->addr.s_addr != mess->yiaddr.s_addr) |
|
{ |
|
lease_prune(lease, now); |
|
lease = NULL; |
|
} |
|
goto rapid_commit; |
|
} |
|
|
log_tags(tagif_netid, ntohl(mess->xid)); |
log_tags(tagif_netid, ntohl(mess->xid)); |
|
|
|
daemon->metrics[METRIC_DHCPOFFER]++; |
|
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid); |
|
|
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid); |
|
|
|
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4)); |
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4)); |
clear_packet(mess, end); |
clear_packet(mess, end); |
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER); |
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER); |
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, pxevendor); |
|
|
return dhcp_packet_size(mess, agent_id, real_end); |
return dhcp_packet_size(mess, agent_id, real_end); |
| |
| |
case DHCPREQUEST: |
case DHCPREQUEST: |
if (ignore || have_config(config, CONFIG_DISABLE)) |
if (ignore || have_config(config, CONFIG_DISABLE)) |
return 0; |
return 0; |
Line 1072 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1218 size_t dhcp_reply(struct dhcp_context *context, char *
|
Have to set override to make sure we echo back the correct server-id */ |
Have to set override to make sure we echo back the correct server-id */ |
struct irec *intr; |
struct irec *intr; |
|
|
enumerate_interfaces(); | enumerate_interfaces(0); |
|
|
for (intr = daemon->interfaces; intr; intr = intr->next) |
for (intr = daemon->interfaces; intr; intr = intr->next) |
if (intr->addr.sa.sa_family == AF_INET && |
if (intr->addr.sa.sa_family == AF_INET && |
Line 1135 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1281 size_t dhcp_reply(struct dhcp_context *context, char *
|
fuzz = rand16(); |
fuzz = rand16(); |
mess->yiaddr = mess->ciaddr; |
mess->yiaddr = mess->ciaddr; |
} |
} |
|
|
|
daemon->metrics[METRIC_DHCPREQUEST]++; |
|
log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid); |
|
|
log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid); | rapid_commit: |
| |
if (!message) |
if (!message) |
{ |
{ |
struct dhcp_config *addr_config; |
struct dhcp_config *addr_config; |
Line 1182 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1330 size_t dhcp_reply(struct dhcp_context *context, char *
|
a lease from one of it's MACs to give the address to another. */ |
a lease from one of it's MACs to give the address to another. */ |
if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type)) |
if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type)) |
{ |
{ |
|
inet_ntop(AF_INET, <mp->addr, daemon->addrbuff, ADDRSTRLEN); |
my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"), |
my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"), |
print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len), |
print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len), |
inet_ntoa(ltmp->addr)); | daemon->addrbuff); |
lease = ltmp; |
lease = ltmp; |
} |
} |
else |
else |
Line 1208 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1357 size_t dhcp_reply(struct dhcp_context *context, char *
|
|
|
if (message) |
if (message) |
{ |
{ |
log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid); | daemon->metrics[rapid_commit ? METRIC_NOANSWER : METRIC_DHCPNAK]++; |
| log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid); |
| |
| /* rapid commit case: lease allocate failed but don't send DHCPNAK */ |
| if (rapid_commit) |
| return 0; |
|
|
mess->yiaddr.s_addr = 0; |
mess->yiaddr.s_addr = 0; |
clear_packet(mess, end); |
clear_packet(mess, end); |
Line 1270 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1424 size_t dhcp_reply(struct dhcp_context *context, char *
|
add_extradata_opt(lease, NULL); |
add_extradata_opt(lease, NULL); |
} |
} |
|
|
|
/* DNSMASQ_REQUESTED_OPTIONS */ |
|
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1))) |
|
{ |
|
int i, len = option_len(opt); |
|
unsigned char *rop = option_ptr(opt, 0); |
|
|
|
for (i = 0; i < len; i++) |
|
lease_add_extradata(lease, (unsigned char *)daemon->namebuff, |
|
sprintf(daemon->namebuff, "%u", rop[i]), (i + 1) == len ? 0 : ','); |
|
} |
|
else |
|
lease_add_extradata(lease, NULL, 0, 0); |
|
|
|
add_extradata_opt(lease, option_find(mess, sz, OPTION_MUD_URL_V4, 1)); |
|
|
/* space-concat tag set */ |
/* space-concat tag set */ |
if (!tagif_netid) |
if (!tagif_netid) |
add_extradata_opt(lease, NULL); |
add_extradata_opt(lease, NULL); |
Line 1292 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1461 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 1347 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1516 size_t dhcp_reply(struct dhcp_context *context, char *
|
else |
else |
override = lease->override; |
override = lease->override; |
|
|
log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid); | daemon->metrics[METRIC_DHCPACK]++; |
| log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid); |
| |
clear_packet(mess, end); |
clear_packet(mess, end); |
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) | if (rapid_commit) |
{ | option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0); |
while (fuzz > (time/16)) | do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), |
fuzz = fuzz/2; | netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor); |
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), | |
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); | |
} |
} |
|
|
return dhcp_packet_size(mess, agent_id, real_end); |
return dhcp_packet_size(mess, agent_id, real_end); |
Line 1370 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1535 size_t dhcp_reply(struct dhcp_context *context, char *
|
if (ignore || have_config(config, CONFIG_DISABLE)) |
if (ignore || have_config(config, CONFIG_DISABLE)) |
message = _("ignored"); |
message = _("ignored"); |
|
|
log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid); | daemon->metrics[METRIC_DHCPINFORM]++; |
| log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid); |
|
|
if (message || mess->ciaddr.s_addr == 0) |
if (message || mess->ciaddr.s_addr == 0) |
return 0; |
return 0; |
Line 1385 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1551 size_t dhcp_reply(struct dhcp_context *context, char *
|
lease->hostname) |
lease->hostname) |
hostname = lease->hostname; |
hostname = lease->hostname; |
|
|
if (!hostname && (hostname = host_from_dns(mess->ciaddr))) | if (!hostname) |
domain = get_domain(mess->ciaddr); | hostname = host_from_dns(mess->ciaddr); |
|
|
if (context && context->netid.net) |
if (context && context->netid.net) |
{ |
{ |
Line 1396 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1562 size_t dhcp_reply(struct dhcp_context *context, char *
|
|
|
log_tags(tagif_netid, ntohl(mess->xid)); |
log_tags(tagif_netid, ntohl(mess->xid)); |
|
|
log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid); | daemon->metrics[METRIC_DHCPACK]++; |
| log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid); |
|
|
if (lease) |
if (lease) |
{ |
{ |
Line 1410 size_t dhcp_reply(struct dhcp_context *context, char *
|
Line 1577 size_t dhcp_reply(struct dhcp_context *context, char *
|
clear_packet(mess, end); |
clear_packet(mess, end); |
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)); |
| |
| /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but |
| we supply a utility which makes DHCPINFORM requests to get this information. |
| Only include lease time if OPTION_LEASE_TIME is in the parameter request list, |
| which won't be true for ordinary clients, but will be true for the |
| dhcp_lease_time utility. */ |
| if (lease && in_list(req_options, OPTION_LEASE_TIME)) |
| { |
| if (lease->expires == 0) |
| time = 0xffffffff; |
| else |
| time = (unsigned int)difftime(lease->expires, now); |
| option_put(mess, end, OPTION_LEASE_TIME, 4, time); |
| } |
| |
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, pxevendor); |
|
|
*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 1497 static int sanitise(unsigned char *opt, char *buf)
|
Line 1678 static int sanitise(unsigned char *opt, char *buf)
|
for (i = option_len(opt); i > 0; i--) |
for (i = option_len(opt); i > 0; i--) |
{ |
{ |
char c = *p++; |
char c = *p++; |
if (isprint((int)c)) | if (isprint((unsigned char)c)) |
*buf++ = c; |
*buf++ = c; |
} |
} |
*buf = 0; /* add terminator */ |
*buf = 0; /* add terminator */ |
Line 1516 static void add_extradata_opt(struct dhcp_lease *lease
|
Line 1697 static void add_extradata_opt(struct dhcp_lease *lease
|
#endif |
#endif |
|
|
static void log_packet(char *type, void *addr, unsigned char *ext_mac, |
static void log_packet(char *type, void *addr, unsigned char *ext_mac, |
int mac_len, char *interface, char *string, u32 xid) | int mac_len, char *interface, char *string, char *err, u32 xid) |
{ |
{ |
struct in_addr a; | if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP)) |
| return; |
/* addr may be misaligned */ | |
| daemon->addrbuff[0] = 0; |
if (addr) |
if (addr) |
memcpy(&a, addr, sizeof(a)); | inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN); |
|
|
print_mac(daemon->namebuff, ext_mac, mac_len); |
print_mac(daemon->namebuff, ext_mac, mac_len); |
|
|
if(option_bool(OPT_LOG_OPTS)) | if (option_bool(OPT_LOG_OPTS)) |
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s", | my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s", |
ntohl(xid), | ntohl(xid), |
type, | type, |
interface, | interface, |
addr ? inet_ntoa(a) : "", | daemon->addrbuff, |
addr ? " " : "", | addr ? " " : "", |
daemon->namebuff, | daemon->namebuff, |
string ? string : ""); | string ? string : "", |
| err ? err : ""); |
else |
else |
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s", | my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s", |
type, |
type, |
interface, |
interface, |
addr ? inet_ntoa(a) : "", | daemon->addrbuff, |
addr ? " " : "", |
addr ? " " : "", |
daemon->namebuff, |
daemon->namebuff, |
string ? string : ""); | string ? string : "", |
| err ? err : ""); |
| |
| #ifdef HAVE_UBUS |
| if (!strcmp(type, "DHCPACK")) |
| ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface); |
| else if (!strcmp(type, "DHCPRELEASE")) |
| ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface); |
| #endif |
} |
} |
|
|
static void log_options(unsigned char *start, u32 xid) |
static void log_options(unsigned char *start, u32 xid) |
Line 1561 static unsigned char *option_find1(unsigned char *p, u
|
Line 1752 static unsigned char *option_find1(unsigned char *p, u
|
{ |
{ |
while (1) |
while (1) |
{ |
{ |
if (p > end) | if (p >= end) |
return NULL; |
return NULL; |
else if (*p == OPTION_END) |
else if (*p == OPTION_END) |
return opt == OPTION_END ? p : NULL; |
return opt == OPTION_END ? p : NULL; |
Line 1693 static size_t dhcp_packet_size(struct dhcp_packet *mes
|
Line 1884 static size_t dhcp_packet_size(struct dhcp_packet *mes
|
if (option_bool(OPT_LOG_OPTS)) |
if (option_bool(OPT_LOG_OPTS)) |
{ |
{ |
if (mess->siaddr.s_addr != 0) |
if (mess->siaddr.s_addr != 0) |
my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr)); | { |
| inet_ntop(AF_INET, &mess->siaddr, daemon->addrbuff, ADDRSTRLEN); |
| my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), daemon->addrbuff); |
| } |
|
|
if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0) |
if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0) |
my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid)); |
my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid)); |
Line 1782 static void option_put(struct dhcp_packet *mess, unsig
|
Line 1976 static void option_put(struct dhcp_packet *mess, unsig
|
} |
} |
|
|
static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt, |
static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt, |
char *string, int null_term) | const char *string, int null_term) |
{ |
{ |
unsigned char *p; |
unsigned char *p; |
size_t len = strlen(string); |
size_t len = strlen(string); |
Line 1819 static int do_opt(struct dhcp_opt *opt, unsigned char
|
Line 2013 static int do_opt(struct dhcp_opt *opt, unsigned char
|
} |
} |
} |
} |
else |
else |
memcpy(p, opt->val, len); | /* empty string may be extended to "\0" by null_term */ |
| memcpy(p, opt->val ? opt->val : (unsigned char *)"", len); |
} |
} |
return len; |
return len; |
} |
} |
Line 1859 static void match_vendor_opts(unsigned char *opt, stru
|
Line 2054 static void match_vendor_opts(unsigned char *opt, stru
|
dopt->flags &= ~DHOPT_VENDOR_MATCH; |
dopt->flags &= ~DHOPT_VENDOR_MATCH; |
if (opt && (dopt->flags & DHOPT_VENDOR)) |
if (opt && (dopt->flags & DHOPT_VENDOR)) |
{ |
{ |
int i, len = 0; | const struct dhcp_pxe_vendor *pv; |
if (dopt->u.vendor_class) | struct dhcp_pxe_vendor dummy_vendor = { |
len = strlen((char *)dopt->u.vendor_class); | .data = (char *)dopt->u.vendor_class, |
for (i = 0; i <= (option_len(opt) - len); i++) | .next = NULL, |
if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0) | }; |
{ | if (dopt->flags & DHOPT_VENDOR_PXE) |
dopt->flags |= DHOPT_VENDOR_MATCH; | pv = daemon->dhcp_pxe_vendors; |
break; | else |
} | pv = &dummy_vendor; |
| for (; pv; pv = pv->next) |
| { |
| int i, len = 0, matched = 0; |
| if (pv->data) |
| len = strlen(pv->data); |
| for (i = 0; i <= (option_len(opt) - len); i++) |
| if (len == 0 || memcmp(pv->data, option_ptr(opt, i), len) == 0) |
| { |
| matched = 1; |
| break; |
| } |
| if (matched) |
| { |
| dopt->flags |= DHOPT_VENDOR_MATCH; |
| break; |
| } |
| } |
} |
} |
} |
} |
} |
} |
Line 1920 static int do_encap_opts(struct dhcp_opt *opt, int enc
|
Line 2132 static int do_encap_opts(struct dhcp_opt *opt, int enc
|
return ret; |
return ret; |
} |
} |
|
|
static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid) | static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid, const char *pxevendor) |
{ |
{ |
unsigned char *p; |
unsigned char *p; |
|
|
option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0); | if (!pxevendor) |
| pxevendor="PXEClient"; |
| option_put_string(mess, end, OPTION_VENDOR_ID, pxevendor, 0); |
if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17))) |
if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17))) |
memcpy(p, uuid, 17); |
memcpy(p, uuid, 17); |
} |
} |
Line 1946 static int prune_vendor_opts(struct dhcp_netid *netid)
|
Line 2160 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); |
|
} |
|
|
|
if (found->basename) |
|
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 2091 struct dhcp_boot *find_boot(struct dhcp_netid *netid)
|
Line 2356 struct dhcp_boot *find_boot(struct dhcp_netid *netid)
|
return boot; |
return boot; |
} |
} |
|
|
|
static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char **pxe_vendor) |
|
{ |
|
const unsigned char *opt = NULL; |
|
ssize_t conf_len = 0; |
|
const struct dhcp_pxe_vendor *conf = daemon->dhcp_pxe_vendors; |
|
opt = option_find(mess, sz, OPTION_VENDOR_ID, 0); |
|
if (!opt) |
|
return 0; |
|
for (; conf; conf = conf->next) |
|
{ |
|
conf_len = strlen(conf->data); |
|
if (option_len(opt) < conf_len) |
|
continue; |
|
if (strncmp(option_ptr(opt, 0), conf->data, conf_len) == 0) |
|
{ |
|
if (pxe_vendor) |
|
*pxe_vendor = conf->data; |
|
return 1; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
static void do_options(struct dhcp_context *context, |
static void do_options(struct dhcp_context *context, |
struct dhcp_packet *mess, |
struct dhcp_packet *mess, |
unsigned char *end, |
unsigned char *end, |
Line 2103 static void do_options(struct dhcp_context *context,
|
Line 2391 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, |
| const char *pxevendor) |
{ |
{ |
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 2153 static void do_options(struct dhcp_context *context,
|
Line 2444 static void do_options(struct dhcp_context *context,
|
/* See if we can send the boot stuff as options. |
/* See if we can send the boot stuff as options. |
To do this we need a requested option list, BOOTP |
To do this we need a requested option list, BOOTP |
and very old DHCP clients won't have this, we also |
and very old DHCP clients won't have this, we also |
provide an manual option to disable it. | provide a manual option to disable it. |
Some PXE ROMs have bugs (surprise!) and need zero-terminated |
Some PXE ROMs have bugs (surprise!) and need zero-terminated |
names, so we always send those. */ |
names, so we always send those. */ |
if ((boot = find_boot(tagif))) |
if ((boot = find_boot(tagif))) |
Line 2165 static void do_options(struct dhcp_context *context,
|
Line 2456 static void do_options(struct dhcp_context *context,
|
in_list(req_options, OPTION_SNAME)) |
in_list(req_options, OPTION_SNAME)) |
option_put_string(mess, end, OPTION_SNAME, boot->sname, 1); |
option_put_string(mess, end, OPTION_SNAME, boot->sname, 1); |
else |
else |
strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1); | safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)); |
} |
} |
|
|
if (boot->file) |
if (boot->file) |
Line 2175 static void do_options(struct dhcp_context *context,
|
Line 2466 static void do_options(struct dhcp_context *context,
|
in_list(req_options, OPTION_FILENAME)) |
in_list(req_options, OPTION_FILENAME)) |
option_put_string(mess, end, OPTION_FILENAME, boot->file, 1); |
option_put_string(mess, end, OPTION_FILENAME, boot->file, 1); |
else |
else |
strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1); | safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file)); |
} |
} |
|
|
if (boot->next_server.s_addr) |
if (boot->next_server.s_addr) |
Line 2192 static void do_options(struct dhcp_context *context,
|
Line 2483 static void do_options(struct dhcp_context *context,
|
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) && |
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) && |
(opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE)) |
(opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE)) |
{ |
{ |
strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1); | safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)); |
done_file = 1; |
done_file = 1; |
} |
} |
|
|
if ((!req_options || !in_list(req_options, OPTION_SNAME)) && |
if ((!req_options || !in_list(req_options, OPTION_SNAME)) && |
(opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE)) |
(opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE)) |
{ |
{ |
strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1); | safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)); |
done_server = 1; |
done_server = 1; |
} |
} |
|
|
Line 2227 static void do_options(struct dhcp_context *context,
|
Line 2518 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 2275 static void do_options(struct dhcp_context *context,
|
Line 2601 static void do_options(struct dhcp_context *context,
|
|
|
if (domain) |
if (domain) |
len += strlen(domain) + 1; |
len += strlen(domain) + 1; |
| else if (fqdn_flags & 0x04) |
| len--; |
| |
if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len))) |
if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len))) |
{ |
{ |
*(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */ |
*(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */ |
Line 2284 static void do_options(struct dhcp_context *context,
|
Line 2612 static void do_options(struct dhcp_context *context,
|
|
|
if (fqdn_flags & 0x04) |
if (fqdn_flags & 0x04) |
{ |
{ |
p = do_rfc1035_name(p, hostname); | p = do_rfc1035_name(p, hostname, NULL); |
if (domain) |
if (domain) |
p = do_rfc1035_name(p, domain); | { |
*p++ = 0; | p = do_rfc1035_name(p, domain, NULL); |
| *p++ = 0; |
| } |
} |
} |
else |
else |
{ |
{ |
Line 2318 static void do_options(struct dhcp_context *context,
|
Line 2648 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 2436 static void do_options(struct dhcp_context *context,
|
Line 2768 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, pxevendor); |
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)) && |
Line 2456 static void do_options(struct dhcp_context *context,
|
Line 2789 static void do_options(struct dhcp_context *context,
|
} |
} |
} |
} |
|
|
#endif | static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid) |
| { |
| struct delay_config *delay_conf; |
|
|
| /* Decide which delay_config option we're using */ |
| for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next) |
| if (match_netid(delay_conf->netid, netid, 0)) |
| break; |
|
|
| if (!delay_conf) |
| /* No match, look for one without a netid */ |
| for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next) |
| if (match_netid(delay_conf->netid, netid, 1)) |
| break; |
|
|
|
if (delay_conf) |
|
{ |
|
if (!option_bool(OPT_QUIET_DHCP)) |
|
my_syslog(MS_DHCP | LOG_INFO, _("%u reply delay: %d"), ntohl(xid), delay_conf->delay); |
|
delay_dhcp(recvtime, delay_conf->delay, -1, 0, 0); |
|
} |
|
} |
|
|
| #endif /* HAVE_DHCP */ |