summaryrefslogtreecommitdiff
path: root/src/rfc2131.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rfc2131.c')
-rw-r--r--src/rfc2131.c930
1 files changed, 480 insertions, 450 deletions
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 3dbfd79..9f69ed5 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
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
@@ -18,16 +18,13 @@
#ifdef HAVE_DHCP
-#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
#ifdef HAVE_SCRIPT
-static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim);
static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt);
#endif
-static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
static int sanitise(unsigned char *opt, char *buf);
static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
@@ -35,39 +32,41 @@ static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, in
static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
int opt, char *string, int null_term);
static struct in_addr option_addr(unsigned char *opt);
-static struct in_addr option_addr_arr(unsigned char *opt, int offset);
static unsigned int option_uint(unsigned char *opt, int i, int size);
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_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
-static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
- 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 int in_list(unsigned char *list, int opt);
static void do_options(struct dhcp_context *context,
struct dhcp_packet *mess,
unsigned char *real_end,
unsigned char *req_options,
char *hostname,
- char *domain, char *config_domain,
+ char *config_domain,
struct dhcp_netid *netid,
- struct in_addr subnet_addr,
+ struct in_addr subnet_addr,
unsigned char fqdn_flags,
int null_term, int pxearch,
unsigned char *uuid,
- int vendor_class_len);
+ int vendor_class_len,
+ time_t now,
+ unsigned int lease_time,
+ unsigned short fuzz);
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 void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
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);
+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);
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)
+ size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)
{
unsigned char *opt, *clid = NULL;
struct dhcp_lease *ltmp, *lease = NULL;
@@ -85,7 +84,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
unsigned int time;
struct dhcp_config *config;
struct dhcp_netid *netid, *tagif_netid;
- struct in_addr subnet_addr, fallback, override;
+ struct in_addr subnet_addr, override;
unsigned short fuzz = 0;
unsigned int mess_type = 0;
unsigned char fqdn_flags = 0;
@@ -95,7 +94,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
struct dhcp_netid known_id, iface_id, cpewan_id;
struct dhcp_opt *o;
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;
@@ -159,8 +161,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
unsigned char *y = option_ptr(opt, offset + elen + 5);
oui = option_find1(x, y, 1, 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
the gateway id back. Note that the device class is optional */
if (oui && serial)
@@ -297,17 +300,43 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!context_new)
for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
- if (context_tmp->netmask.s_addr &&
- is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
- is_same_net(addr, context_tmp->end, context_tmp->netmask))
- {
- context_tmp->current = context_new;
- context_new = context_tmp;
- }
+ {
+ 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);
+ }
+
+ /* This section fills in context mainly when a client which is on a remote (relayed)
+ network renews a lease without using the relay, after dnsmasq has restarted. */
+ if (netmask.s_addr != 0 &&
+ is_same_net(addr, context_tmp->start, netmask) &&
+ is_same_net(addr, context_tmp->end, netmask))
+ {
+ context_tmp->netmask = netmask;
+ if (context_tmp->local.s_addr == 0)
+ context_tmp->local = fallback;
+ if (context_tmp->router.s_addr == 0)
+ 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)
- context = context_new;
-
+ context = context_new;
}
if (!context)
@@ -318,9 +347,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
return 0;
}
- /* keep _a_ local address available. */
- fallback = context->local;
-
if (option_bool(OPT_LOG_OPTS))
{
struct dhcp_context *context_tmp;
@@ -335,6 +361,117 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
}
}
+
+ /* 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);
+ }
mess->op = BOOTREPLY;
@@ -441,9 +578,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else if (context->netid.net)
{
context->netid.next = netid;
- netid = &context->netid;
- tagif_netid = run_tag_if(netid);
+ tagif_netid = run_tag_if(&context->netid);
}
+
+ log_tags(tagif_netid, ntohl(mess->xid));
if (!message && !nailed)
{
@@ -456,34 +594,34 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!message &&
!lease &&
- (!(lease = lease_allocate(mess->yiaddr))))
+ (!(lease = lease4_allocate(mess->yiaddr))))
message = _("no leases left");
if (!message)
{
logaddr = &mess->yiaddr;
- lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
+ lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
if (hostname)
- lease_set_hostname(lease, hostname, 1);
+ lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
/* infinite lease unless nailed in dhcp-host line. */
lease_set_expires(lease,
have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
now);
- lease_set_interface(lease, int_index);
+ lease_set_interface(lease, int_index, now);
clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
- domain, tagif_netid, subnet_addr, 0, 0, 0, NULL, 0);
+ netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0);
}
}
- log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
+ log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
- return message ? 0 : dhcp_packet_size(mess, tagif_netid, 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 */
int len = option_len(opt);
@@ -495,15 +633,25 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
op += 3;
pp = op;
- /* Always force update, since the client has no way to do it itself. */
- if (!(fqdn_flags & 0x01))
- fqdn_flags |= 0x02;
-
- fqdn_flags &= ~0x08;
- fqdn_flags |= 0x01;
+ /* NB, the following always sets at least one bit */
+ if (option_bool(OPT_FQDN_UPDATE))
+ {
+ if (fqdn_flags & 0x01)
+ {
+ fqdn_flags |= 0x02; /* set O */
+ fqdn_flags &= ~0x01; /* clear S */
+ }
+ fqdn_flags |= 0x08; /* set N */
+ }
+ else
+ {
+ if (!(fqdn_flags & 0x01))
+ fqdn_flags |= 0x03; /* set S and O */
+ fqdn_flags &= ~0x08; /* clear N */
+ }
if (fqdn_flags & 0x04)
- while (*op != 0 && ((op + (*op) + 1) - pp) < len)
+ while (*op != 0 && ((op + (*op)) - pp) < len)
{
memcpy(pq, op+1, *op);
pq += *op;
@@ -591,119 +739,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
}
- /* 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);
-
+
/* 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)
if (match_netid(id_list->list, tagif_netid, 0))
@@ -768,14 +805,21 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (service->type == type)
break;
- if (!service || !service->basename)
- return 0;
+ for (; context; context = context->current)
+ 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);
mess->yiaddr = mess->ciaddr;
mess->ciaddr.s_addr = 0;
- if (service->server.s_addr != 0)
+ if (service->sname)
+ mess->siaddr = a_record_from_hosts(service->sname, now);
+ else if (service->server.s_addr != 0)
mess->siaddr = service->server;
else
mess->siaddr = context->local;
@@ -794,8 +838,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
opt71.next = daemon->dhcp_opts;
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);
- return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+ log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
+ log_tags(tagif_netid, ntohl(mess->xid));
+ return dhcp_packet_size(mess, agent_id, real_end);
}
if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
@@ -814,8 +859,16 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (tmp)
{
- struct dhcp_boot *boot = find_boot(tagif_netid);
-
+ struct dhcp_boot *boot;
+
+ 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;
if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
{
@@ -829,8 +882,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
and set discovery_control = 8 */
if (boot)
{
- if (boot->next_server.s_addr)
+ if (boot->next_server.s_addr)
mess->siaddr = boot->next_server;
+ else if (boot->tftp_sname)
+ mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
if (boot->file)
strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
@@ -838,13 +893,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
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);
prune_vendor_opts(tagif_netid);
- do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
+ 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);
- return ignore ? 0 : dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+ log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
+ log_tags(tagif_netid, ntohl(mess->xid));
+ return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
}
}
}
@@ -874,7 +930,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
return 0;
- log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
+ 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)
lease_prune(lease, now);
@@ -906,13 +962,15 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else
message = _("unknown lease");
- log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
+ log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
return 0;
case DHCPDISCOVER:
if (ignore || have_config(config, CONFIG_DISABLE))
{
+ if (option_bool(OPT_QUIET_DHCP))
+ return 0;
message = _("ignored");
opt = NULL;
}
@@ -970,35 +1028,31 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
message = _("no address available");
}
- log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);
+ 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)))
return 0;
- log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
-
if (context->netid.net)
{
context->netid.next = netid;
- netid = &context->netid;
- tagif_netid = run_tag_if(netid);
+ tagif_netid = run_tag_if(&context->netid);
}
-
+
+ log_tags(tagif_netid, ntohl(mess->xid));
+
+ log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
+
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
clear_packet(mess, end);
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_LEASE_TIME, 4, time);
/* 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),
- domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+ netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
- return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+ return dhcp_packet_size(mess, agent_id, real_end);
case DHCPREQUEST:
if (ignore || have_config(config, CONFIG_DISABLE))
@@ -1029,12 +1083,30 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (!context)
{
- /* In auth mode, a REQUEST sent to the wrong server
- should be faulted, so that the client establishes
- communication with us, otherwise, silently ignore. */
- if (!option_bool(OPT_AUTHORITATIVE))
- return 0;
- message = _("wrong server-ID");
+ /* Handle very strange configs where clients have more than one route to the server.
+ If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
+ Have to set override to make sure we echo back the correct server-id */
+ struct irec *intr;
+
+ enumerate_interfaces(0);
+
+ for (intr = daemon->interfaces; intr; intr = intr->next)
+ if (intr->addr.sa.sa_family == AF_INET &&
+ intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
+ intr->tftp_ok)
+ break;
+
+ if (intr)
+ override = intr->addr.in.sin_addr;
+ else
+ {
+ /* In auth mode, a REQUEST sent to the wrong server
+ should be faulted, so that the client establishes
+ communication with us, otherwise, silently ignore. */
+ if (!option_bool(OPT_AUTHORITATIVE))
+ return 0;
+ message = _("wrong server-ID");
+ }
}
}
@@ -1080,7 +1152,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
mess->yiaddr = mess->ciaddr;
}
- log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
+ log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
if (!message)
{
@@ -1142,7 +1214,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else if (!lease)
{
- if ((lease = lease_allocate(mess->yiaddr)))
+ if ((lease = lease4_allocate(mess->yiaddr)))
do_classes = 1;
else
message = _("no leases left");
@@ -1152,7 +1224,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (message)
{
- log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
+ log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
mess->yiaddr.s_addr = 0;
clear_packet(mess, end);
@@ -1173,47 +1245,74 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (context->netid.net)
{
context->netid.next = netid;
- netid = &context->netid;
- tagif_netid = run_tag_if(netid);
+ tagif_netid = run_tag_if( &context->netid);
}
+
+ log_tags(tagif_netid, ntohl(mess->xid));
-#ifdef HAVE_SCRIPT
- if (do_classes && daemon->lease_change_command)
+ if (do_classes)
{
- struct dhcp_netid *n;
-
- if (mess->giaddr.s_addr)
- lease->giaddr = mess->giaddr;
-
- lease->changed = 1;
- free(lease->extradata);
- lease->extradata = NULL;
- lease->extradata_size = lease->extradata_len = 0;
-
- add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
- add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
- add_extradata_opt(lease, oui);
- add_extradata_opt(lease, serial);
- add_extradata_opt(lease, class);
-
- /* space-concat tag set */
- if (!tagif_netid)
- add_extradata_opt(lease, NULL);
- else
- for (n = tagif_netid; n; n = n->next)
- add_extradata_data(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
-
- if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
+ /* pick up INIT-REBOOT events. */
+ lease->flags |= LEASE_CHANGED;
+
+#ifdef HAVE_SCRIPT
+ if (daemon->lease_change_command)
{
- int len = option_len(opt);
- unsigned char *ucp = option_ptr(opt, 0);
- /* If the user-class option started as counted strings, the first byte will be zero. */
- if (len != 0 && ucp[0] == 0)
- ucp++, len--;
- add_extradata_data(lease, ucp, len, 0);
+ struct dhcp_netid *n;
+
+ if (mess->giaddr.s_addr)
+ lease->giaddr = mess->giaddr;
+
+ free(lease->extradata);
+ lease->extradata = NULL;
+ lease->extradata_size = lease->extradata_len = 0;
+
+ add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
+ add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
+ add_extradata_opt(lease, oui);
+ add_extradata_opt(lease, serial);
+ add_extradata_opt(lease, class);
+
+ if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
+ {
+ add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
+ add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
+ add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
+ }
+ else
+ {
+ add_extradata_opt(lease, NULL);
+ add_extradata_opt(lease, NULL);
+ add_extradata_opt(lease, NULL);
+ }
+
+ /* space-concat tag set */
+ if (!tagif_netid)
+ add_extradata_opt(lease, NULL);
+ else
+ for (n = tagif_netid; n; n = n->next)
+ {
+ struct dhcp_netid *n1;
+ /* kill dupes */
+ for (n1 = n->next; n1; n1 = n1->next)
+ if (strcmp(n->net, n1->net) == 0)
+ break;
+ if (!n1)
+ lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
+ }
+
+ if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
+ {
+ int len = option_len(opt);
+ unsigned char *ucp = option_ptr(opt, 0);
+ /* If the user-class option started as counted strings, the first byte will be zero. */
+ if (len != 0 && ucp[0] == 0)
+ ucp++, len--;
+ lease_add_extradata(lease, ucp, len, 0);
+ }
}
- }
#endif
+ }
if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
{
@@ -1223,7 +1322,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
- lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
+ lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
/* if all the netids in the ignore_name list are present, ignore client-supplied name */
if (!hostname_auth)
@@ -1254,41 +1353,33 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
if (hostname)
- lease_set_hostname(lease, hostname, hostname_auth);
+ lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
lease_set_expires(lease, time, now);
- lease_set_interface(lease, int_index);
+ lease_set_interface(lease, int_index, now);
if (override.s_addr != 0)
lease->override = override;
else
override = lease->override;
- log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);
- emit_dbus_signal(ACTION_CONNECT, lease, hostname);
+ log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
clear_packet(mess, end);
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_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),
- domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+ netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
}
- return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+ return dhcp_packet_size(mess, agent_id, real_end);
case DHCPINFORM:
if (ignore || have_config(config, CONFIG_DISABLE))
message = _("ignored");
- log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
+ log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
if (message || mess->ciaddr.s_addr == 0)
return 0;
@@ -1303,20 +1394,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
lease->hostname)
hostname = lease->hostname;
- if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
- domain = get_domain(mess->ciaddr);
-
- log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
+ if (!hostname)
+ hostname = host_from_dns(mess->ciaddr);
if (context && context->netid.net)
{
context->netid.next = netid;
- netid = &context->netid;
- tagif_netid = run_tag_if(netid);
+ tagif_netid = run_tag_if(&context->netid);
}
+
+ log_tags(tagif_netid, ntohl(mess->xid));
+
+ log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
if (lease)
{
+ lease_set_interface(lease, int_index, now);
if (override.s_addr != 0)
lease->override = override;
else
@@ -1326,58 +1419,31 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
clear_packet(mess, end);
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));
-
- if (lease)
+
+ /* 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);
- lease_set_interface(lease, int_index);
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
- domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+ netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
*is_inform = 1; /* handle reply differently */
- return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+ return dhcp_packet_size(mess, agent_id, real_end);
}
return 0;
}
-static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
-{
- int i;
-
- if (o->len > len)
- return 0;
-
- if (o->len == 0)
- return 1;
-
- if (o->flags & DHOPT_HEX)
- {
- if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
- return 1;
- }
- else
- for (i = 0; i <= (len - o->len); )
- {
- if (memcmp(o->val, p + i, o->len) == 0)
- return 1;
-
- if (o->flags & DHOPT_STRING)
- i++;
- else
- i += o->len;
- }
-
- return 0;
-}
-
-
/* find a good value to use as MAC address for logging and address-allocation hashing.
This is normally just the chaddr field from the DHCP packet,
but eg Firewire will have hlen == 0 and use the client-id instead.
@@ -1433,7 +1499,7 @@ static struct in_addr server_id(struct dhcp_context *context, struct in_addr ove
{
if (override.s_addr != 0)
return override;
- else if (context)
+ else if (context && context->local.s_addr != 0)
return context->local;
else
return fallback;
@@ -1463,59 +1529,23 @@ static int sanitise(unsigned char *opt, char *buf)
}
#ifdef HAVE_SCRIPT
-static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim)
-{
- if ((lease->extradata_size - lease->extradata_len) < (len + 1))
- {
- size_t newsz = lease->extradata_len + len + 100;
- unsigned char *new = whine_malloc(newsz);
-
- if (!new)
- return;
-
- if (lease->extradata)
- {
- memcpy(new, lease->extradata, lease->extradata_len);
- free(lease->extradata);
- }
-
- lease->extradata = new;
- lease->extradata_size = newsz;
- }
-
- if (len != 0)
- memcpy(lease->extradata + lease->extradata_len, data, len);
- lease->extradata[lease->extradata_len + len] = delim;
- lease->extradata_len += len + 1;
-}
-
static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
{
if (!opt)
- add_extradata_data(lease, NULL, 0, 0);
+ lease_add_extradata(lease, NULL, 0, 0);
else
- {
- size_t i, len = option_len(opt);
- unsigned char *ucp = option_ptr(opt, 0);
-
- /* check for embeded NULLs */
- for (i = 0; i < len; i++)
- if (ucp[i] == 0)
- {
- len = i;
- break;
- }
-
- add_extradata_data(lease, ucp, len, 0);
- }
+ lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
}
#endif
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 */
if (addr)
memcpy(&a, addr, sizeof(a));
@@ -1523,52 +1553,34 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
print_mac(daemon->namebuff, ext_mac, mac_len);
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),
type,
interface,
addr ? inet_ntoa(a) : "",
addr ? " " : "",
daemon->namebuff,
- string ? string : "");
+ string ? string : "",
+ err ? err : "");
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,
interface,
addr ? inet_ntoa(a) : "",
addr ? " " : "",
daemon->namebuff,
- string ? string : "");
+ string ? string : "",
+ err ? err : "");
}
static void log_options(unsigned char *start, u32 xid)
{
while (*start != OPTION_END)
{
- int is_ip, is_name, i;
- char *text = option_string(start[0], &is_ip, &is_name);
- unsigned char trunc = option_len(start);
+ char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAME);
- if (is_ip)
- for (daemon->namebuff[0]= 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ)
- {
- if (i != 0)
- strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff));
- strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)), 256 - strlen(daemon->namebuff));
- }
- else if (!is_name || !sanitise(start, daemon->namebuff))
- {
- if (trunc > 13)
- trunc = 13;
- print_mac(daemon->namebuff, option_ptr(start, 0), trunc);
- }
-
- my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s",
- ntohl(xid), option_len(start), start[0],
- text ? ":" : "", text ? text : "",
- trunc == 0 ? "" : " ",
- trunc == 0 ? "" : daemon->namebuff,
- trunc == option_len(start) ? "" : "...");
+ my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
+ ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
start += start[1] + 2;
}
}
@@ -1623,22 +1635,17 @@ static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt
return NULL;
}
-static struct in_addr option_addr_arr(unsigned char *opt, int offset)
+static struct in_addr option_addr(unsigned char *opt)
{
- /* this worries about unaligned data in the option. */
+ /* this worries about unaligned data in the option. */
/* struct in_addr is network byte order */
struct in_addr ret;
- memcpy(&ret, option_ptr(opt, offset), INADDRSZ);
+ memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
return ret;
}
-static struct in_addr option_addr(unsigned char *opt)
-{
- return option_addr_arr(opt, 0);
-}
-
static unsigned int option_uint(unsigned char *opt, int offset, int size)
{
/* this worries about unaligned data and byte order */
@@ -1673,15 +1680,12 @@ static unsigned char *find_overload(struct dhcp_packet *mess)
return NULL;
}
-static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
- 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)
{
unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
unsigned char *overload;
size_t ret;
- struct dhcp_netid_list *id_list;
- struct dhcp_netid *n;
-
+
/* move agent_id back down to the end of the packet */
if (agent_id)
{
@@ -1690,27 +1694,6 @@ static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *neti
memset(p, 0, real_end - p); /* in case of overlap */
}
- /* We do logging too */
- if (netid && option_bool(OPT_LOG_OPTS))
- {
- char *s = daemon->namebuff;
- for (*s = 0; netid; netid = netid->next)
- {
- /* kill dupes. */
- for (n = netid->next; n; n = n->next)
- if (strcmp(netid->net, n->net) == 0)
- break;
-
- if (!n)
- {
- strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
- if (netid->next)
- strncat (s, ", ", (MAXDNAME-1) - strlen(s));
- }
- }
- my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
- }
-
/* add END options to the regions. */
overload = find_overload(mess);
@@ -1735,12 +1718,6 @@ static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *neti
*p++ = OPTION_END;
- for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
- if ((!id_list->list) || match_netid(id_list->list, netid, 0))
- break;
- if (id_list)
- mess->flags |= htons(0x8000); /* force broadcast */
-
if (option_bool(OPT_LOG_OPTS))
{
if (mess->siaddr.s_addr != 0)
@@ -1803,7 +1780,7 @@ static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, i
if (overload[2] & 2)
{
p = dhcp_skip_opts(mess->sname);
- if (p + len + 3 >= mess->sname + sizeof(mess->file))
+ if (p + len + 3 >= mess->sname + sizeof(mess->sname))
p = NULL;
}
}
@@ -1870,7 +1847,8 @@ static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *c
}
}
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;
}
@@ -1890,20 +1868,14 @@ static int in_list(unsigned char *list, int opt)
return 0;
}
-static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
+static struct dhcp_opt *option_find2(int opt)
{
- struct dhcp_opt *tmp;
- for (tmp = opts; tmp; tmp = tmp->next)
- if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
- if (match_netid(tmp->netid, netid, 0))
- return tmp;
-
- /* No match, look for one without a netid */
- for (tmp = opts; tmp; tmp = tmp->next)
- if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
- if (match_netid(tmp->netid, netid, 1))
- return tmp;
-
+ struct dhcp_opt *opts;
+
+ for (opts = daemon->dhcp_opts; opts; opts = opts->next)
+ if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
+ return opts;
+
return NULL;
}
@@ -2003,7 +1975,7 @@ static int prune_vendor_opts(struct dhcp_netid *netid)
return force;
}
-static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local)
+static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)
{
#define NUM_OPTS 4
@@ -2060,8 +2032,9 @@ static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct
return daemon->dhcp_opts;
}
- boot_server = service->basename ? local : service->server;
-
+ boot_server = service->basename ? local :
+ (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
+
if (boot_server.s_addr != 0)
{
if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
@@ -2152,13 +2125,16 @@ static void do_options(struct dhcp_context *context,
unsigned char *end,
unsigned char *req_options,
char *hostname,
- char *domain, char *config_domain,
+ char *domain,
struct dhcp_netid *netid,
struct in_addr subnet_addr,
unsigned char fqdn_flags,
int null_term, int pxe_arch,
unsigned char *uuid,
- int vendor_class_len)
+ int vendor_class_len,
+ time_t now,
+ unsigned int lease_time,
+ unsigned short fuzz)
{
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
struct dhcp_boot *boot;
@@ -2167,22 +2143,26 @@ static void do_options(struct dhcp_context *context,
unsigned char f0 = 0, s0 = 0;
int done_file = 0, done_server = 0;
int done_vendor_class = 0;
+ struct dhcp_netid *tagif;
+ struct dhcp_netid_list *id_list;
- if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
- my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
-
+ /* 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);
+
/* logging */
if (option_bool(OPT_LOG_OPTS) && req_options)
{
char *q = daemon->namebuff;
for (i = 0; req_options[i] != OPTION_END; i++)
{
- char *s = option_string(req_options[i], NULL, NULL);
+ char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
"%d%s%s%s",
req_options[i],
- s ? ":" : "",
- s ? s : "",
+ strlen(s) != 0 ? ":" : "",
+ s,
req_options[i+1] == OPTION_END ? "" : ", ");
if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
{
@@ -2192,6 +2172,12 @@ static void do_options(struct dhcp_context *context,
}
}
+ for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
+ if ((!id_list->list) || match_netid(id_list->list, netid, 0))
+ break;
+ if (id_list)
+ mess->flags |= htons(0x8000); /* force broadcast */
+
if (context)
mess->siaddr = context->local;
@@ -2201,7 +2187,7 @@ static void do_options(struct dhcp_context *context,
provide an manual option to disable it.
Some PXE ROMs have bugs (surprise!) and need zero-terminated
names, so we always send those. */
- if ((boot = find_boot(netid)))
+ if ((boot = find_boot(tagif)))
{
if (boot->sname)
{
@@ -2223,8 +2209,10 @@ static void do_options(struct dhcp_context *context,
strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
}
- if (boot->next_server.s_addr)
+ if (boot->next_server.s_addr)
mess->siaddr = boot->next_server;
+ else if (boot->tftp_sname)
+ mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
}
else
/* Use the values of the relevant options if no dhcp-boot given and
@@ -2233,20 +2221,20 @@ static void do_options(struct dhcp_context *context,
dhcp-optsfile. */
{
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
- (opt = option_find2(netid, config_opts, 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);
done_file = 1;
}
if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
- (opt = option_find2(netid, config_opts, 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);
done_server = 1;
}
- if ((opt = option_find2(netid, config_opts, OPTION_END)))
+ if ((opt = option_find2(OPTION_END)))
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
}
@@ -2270,40 +2258,76 @@ static void do_options(struct dhcp_context *context,
/* rfc3011 says this doesn't need to be in the requested options list. */
if (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 */
if (context)
{
- if (!option_find2(netid, config_opts, OPTION_NETMASK))
+ if (!option_find2(OPTION_NETMASK))
option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
/* May not have a "guessed" broadcast address if we got no packets via a relay
from this net yet (ie just unicast renewals after a restart */
if (context->broadcast.s_addr &&
- !option_find2(netid, config_opts, OPTION_BROADCAST))
+ !option_find2(OPTION_BROADCAST))
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
/* Same comments as broadcast apply, and also may not be able to get a sensible
default when using subnet select. User must configure by steam in that case. */
if (context->router.s_addr &&
in_list(req_options, OPTION_ROUTER) &&
- !option_find2(netid, config_opts, OPTION_ROUTER))
+ !option_find2(OPTION_ROUTER))
option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
- if (in_list(req_options, OPTION_DNSSERVER) &&
- !option_find2(netid, config_opts, OPTION_DNSSERVER))
+ if (daemon->port == NAMESERVER_PORT &&
+ in_list(req_options, OPTION_DNSSERVER) &&
+ !option_find2(OPTION_DNSSERVER))
option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
}
if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
- !option_find2(netid, config_opts, OPTION_DOMAINNAME))
+ !option_find2(OPTION_DOMAINNAME))
option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
/* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
if (hostname)
{
if (in_list(req_options, OPTION_HOSTNAME) &&
- !option_find2(netid, config_opts, OPTION_HOSTNAME))
+ !option_find2(OPTION_HOSTNAME))
option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
if (fqdn_flags != 0)
@@ -2317,10 +2341,12 @@ static void do_options(struct dhcp_context *context,
if (domain)
len += strlen(domain) + 1;
-
+ else if (fqdn_flags & 0x04)
+ len--;
+
if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
{
- *(p++) = fqdn_flags;
+ *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
*(p++) = 255;
*(p++) = 255;
@@ -2328,8 +2354,10 @@ static void do_options(struct dhcp_context *context,
{
p = do_rfc1035_name(p, hostname);
if (domain)
- p = do_rfc1035_name(p, domain);
- *p++ = 0;
+ {
+ p = do_rfc1035_name(p, domain);
+ *p++ = 0;
+ }
}
else
{
@@ -2352,16 +2380,22 @@ static void do_options(struct dhcp_context *context,
{
int optno = opt->opt;
+ /* netids match and not encapsulated? */
+ if (!(opt->flags & DHOPT_TAGOK))
+ continue;
+
/* was it asked for, or are we sending it anyway? */
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
continue;
- /* prohibit some used-internally options */
+ /* prohibit some used-internally options. T1 and T2 already handled. */
if (optno == OPTION_CLIENT_FQDN ||
optno == OPTION_MAXMESSAGE ||
optno == OPTION_OVERLOAD ||
optno == OPTION_PAD ||
- optno == OPTION_END)
+ optno == OPTION_END ||
+ optno == OPTION_T1 ||
+ optno == OPTION_T2)
continue;
if (optno == OPTION_SNAME && done_server)
@@ -2370,10 +2404,6 @@ static void do_options(struct dhcp_context *context,
if (optno == OPTION_FILENAME && done_file)
continue;
- /* netids match and not encapsulated? */
- if (opt != option_find2(netid, config_opts, optno))
- continue;
-
/* For the options we have default values on
dhc-option=<optionno> means "don't include this option"
not "include a zero-length option" */
@@ -2411,8 +2441,8 @@ static void do_options(struct dhcp_context *context,
/* Now send options to be encapsulated in arbitrary options,
eg dhcp-option=encap:172,17,.......
- Also hand vendor-identifying vendor-encapsulated options,
- dhcp-option = rfc3925-encap:13,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. */
for (opt = config_opts; opt; opt = opt->next)
@@ -2440,7 +2470,7 @@ static void do_options(struct dhcp_context *context,
continue;
o->flags |= DHOPT_ENCAP_DONE;
- if (match_netid(o->netid, netid, 1) &&
+ if (match_netid(o->netid, tagif, 1) &&
((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
{
o->flags |= DHOPT_ENCAP_MATCH;
@@ -2474,12 +2504,12 @@ static void do_options(struct dhcp_context *context,
}
}
- force_encap = prune_vendor_opts(netid);
+ force_encap = prune_vendor_opts(tagif);
if (context && pxe_arch != -1)
{
pxe_misc(mess, end, uuid);
- config_opts = pxe_opts(pxe_arch, netid, context->local);
+ config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
}
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&