Merged in rt38396.
Changes since 4.1-ESV-R12
+- Added a parameter, authoring-byte-order, to the lease file. This value
+ is automatically added to the top of new lease files by the server and
+ indicates the internal byte order (big endian or little endian) of the
+ server. This permits lease files generated on a server with one form of
+ byte order to be used on a server with the opposite form.
+ [ISC-Bugs #38396]
+
- Added dhcpv6 and delayed-ack to settings listed in the "Features:"
section of the configure script output. Additionally, all of the
features reported on will now always show either a "yes" or "no"
Lexical scanner for dhcpd config file... */
/*
- * Copyright (c) 2004-2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2015 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
* Permission to use, copy, modify, and distribute this software for any
}
if (!strcasecmp (atom + 1, "uthoritative"))
return AUTHORITATIVE;
+ if (!strcasecmp(atom + 1, "uthoring-byte-order"))
+ return AUTHORING_BYTE_ORDER;
break;
}
if (!strcasecmp (atom + 1, "nd"))
return BALANCE;
if (!strcasecmp (atom + 1, "ound"))
return BOUND;
+ if (!strcasecmp(atom+1, "ig-endian")) {
+ return TOKEN_BIG_ENDIAN;
+ }
break;
case 'c':
if (!strcasecmp(atom + 1, "ase"))
if (!strcasecmp(atom+1, "l")) {
return LL;
}
+ if (!strcasecmp(atom+1, "ittle-endian")) {
+ return TOKEN_LITTLE_ENDIAN;
+ }
break;
case 'm':
if (!strncasecmp (atom + 1, "ax", 2)) {
#define cur_time cur_tv.tv_sec
extern int ddns_update_style;
+extern int authoring_byte_order;
extern const char *path_dhcpd_conf;
extern const char *path_dhcpd_db;
void parse_ia_pd_declaration(struct parse *);
void parse_server_duid(struct parse *cfile);
void parse_server_duid_conf(struct parse *cfile);
+uint32_t parse_byte_order_uint32(const void *source);
/* ddns.c */
int ddns_updates(struct packet *, struct lease *, struct lease *,
CONFLICT_DONE = 660,
INITIAL_DELAY = 664,
GETHOSTBYNAME = 665,
- TOKEN_INFINIBAND = 668
+ TOKEN_INFINIBAND = 668,
+ AUTHORING_BYTE_ORDER = 669,
+ TOKEN_LITTLE_ENDIAN = 670,
+ TOKEN_BIG_ENDIAN = 671
};
#define is_identifier(x) ((x) >= FIRST_TOKEN && \
static int parse_binding_value(struct parse *cfile,
struct binding_value *value);
+static void parse_authoring_byte_order (struct parse *cfile);
+
#if defined (TRACING)
trace_type_t *trace_readconf_type;
trace_type_t *trace_readleases_type;
} else if (token == SERVER_DUID) {
parse_server_duid(cfile);
#endif /* DHCPv6 */
+ } else if (token == AUTHORING_BYTE_ORDER) {
+ parse_authoring_byte_order(cfile);
} else {
log_error ("Corrupt lease file - possible data loss!");
skip_to_semi (cfile);
}
#endif /* defined (FAILOVER_PROTOCOL) */
+/*!
+ * \brief Parses an authoring-byte-order statement
+ *
+ * A valid statement looks like this:
+ *
+ * authoring-byte-order :==
+ * AUTHORING_BYTE_ORDER TOKEN_LITTLE_ENDIAN | TOKEN_BIG_ENDIAN ;
+ *
+ * If the global, authoring_byte_order is not zero, then either the statement
+ * has already been parsed or the function, parse_byte_order_uint32, has
+ * been called which set it to the default. In either case, this is invalid
+ * so we'll log it and bail.
+ *
+ * If the value is different from the current server's byte order, then we'll
+ * log that fact and set authoring_byte_order to given value. This causes all
+ * invocations of the function, parse_byte_order_uint32, to perform byte-order
+ * conversion before returning the value.
+ *
+ * \param cfile the current parse file
+ *
+*/
+void parse_authoring_byte_order (struct parse *cfile)
+{
+ enum dhcp_token token;
+ const char *val;
+ unsigned int len;
+
+ /* Either we've seen it already or it's after the first lease */
+ if (authoring_byte_order != 0) {
+ parse_warn (cfile,
+ "authoring-byte-order specified too late.\n"
+ "It must occur before the first lease in file\n");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ token = next_token(&val, (unsigned *)0, cfile);
+ switch(token) {
+ case TOKEN_LITTLE_ENDIAN:
+ authoring_byte_order = LITTLE_ENDIAN;
+ break;
+ case TOKEN_BIG_ENDIAN:
+ authoring_byte_order = BIG_ENDIAN;
+ break;
+ default:
+ parse_warn(cfile, "authoring-byte-order is invalid: "
+ " it must be big-endian or little-endian.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ if (authoring_byte_order != DHCP_BYTE_ORDER) {
+ log_error ("WARNING: Lease file authored using different"
+ " byte order, will attempt to convert");
+ }
+
+ token = next_token(&val, &len, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "corrupt lease file; expecting a semicolon");
+ skip_to_semi(cfile);
+ return;
+ }
+}
+
/* Permit_list_match returns 1 if every element of the permit list in lhs
also appears in rhs. Note that this doesn't by itself mean that the
two lists are equal - to check for equality, permit_list_match has to
return;
}
- memcpy(&iaid, val, 4);
+ iaid = parse_byte_order_uint32(val);
+
ia = NULL;
if (ia_allocate(&ia, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
log_fatal("Out of memory.");
return;
}
- memcpy(&iaid, val, 4);
+ iaid = parse_byte_order_uint32(val);
+
ia = NULL;
if (ia_allocate(&ia, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
log_fatal("Out of memory.");
return;
}
- memcpy(&iaid, val, 4);
+ iaid = parse_byte_order_uint32(val);
+
ia = NULL;
if (ia_allocate(&ia, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
log_fatal("Out of memory.");
}
}
+/*!
+ * \brief Creates a byte-order corrected uint32 from a buffer
+ *
+ * This function creates an integer value from a buffer, converting from
+ * the byte order specified by authoring-byte-order to the current server's
+ * byte order if they are different. The conversion works in either direction.
+ *
+ * If the parameter, authoring-byte-order hasn't yet been encountered we will
+ * emit a warning and then default the byte order to match the current server's
+ * byte order (i.e. no conversion will done).
+ *
+ * \param source buffer containing the "raw" four byte data
+ * \return uint32_t containing the corrected value
+*/
+uint32_t parse_byte_order_uint32(const void *source) {
+ uint32_t value;
+
+ /* use memcpy to avoid any alignment monkey business */
+ memcpy(&value, source, 4);
+
+ if (authoring_byte_order == 0) {
+ log_error ("WARNING: "
+ "authoring-byte-order not in the lease file.\n"
+ "Assuming file byte order matches this server.\n");
+ authoring_byte_order = DHCP_BYTE_ORDER;
+ }
+
+ if (authoring_byte_order != DHCP_BYTE_ORDER) {
+ value = (((value >> 24) & 0xff) | // move byte 3 to byte 0
+ ((value << 8) & 0xff0000) | // move byte 1 to byte 2
+ ((value >> 8) & 0xff00) | // move byte 2 to byte 1
+ ((value << 24) & 0xff000000)); // byte 0 to byte 3
+ }
+
+ return (value);
+}
+
#endif /* DHCPv6 */
#if defined (TRACING)
if (!trace_playback ()) {
#endif
+ /* Unset authoring_byte_order so we'll know if it was specified
+ in the lease file or not. */
+ authoring_byte_order = 0;
+
/* Read in the existing lease file... */
status = read_conf_file (path_dhcpd_db,
(struct group *)0, 0, 1);
errno = 0;
fprintf (db_file, "# The format of this file is documented in the %s",
"dhcpd.leases(5) manual page.\n");
+
if (errno)
goto fail;
if (errno)
goto fail;
+ fprintf (db_file, "# authoring-byte-order entry is generated,"
+ " DO NOT DELETE\n");
+ if (errno)
+ goto fail;
+
+ fprintf (db_file, "authoring-byte-order %s;\n\n",
+ (DHCP_BYTE_ORDER == LITTLE_ENDIAN ?
+ "little-endian" : "big-endian"));
+ if (errno)
+ goto fail;
+
+
/* At this point we have a new lease file that, so far, could not
* be described as either corrupt nor valid.
*/
int ddns_update_style;
#endif /* NSUPDATE */
+int authoring_byte_order = 0; /* 0 = not set */
+
const char *path_dhcpd_conf = _PATH_DHCPD_CONF;
const char *path_dhcpd_db = _PATH_DHCPD_DB;
const char *path_dhcpd_pid = _PATH_DHCPD_PID;
active lease are \fBrelease\fR and \fBexpiry\fR. More than one event
can be specified - if so, the events are separated by '|' characters.
.PP
+The \fIauthoring-byte-order\fR statement
+.RS 0.25i
+.PP
+.B authoring-byte-order \fR[ \fIbig-endian\fR | \fIlittle-endian\fR ] \fB;\fR
+.PP
+This statement is automatically added to the top of new lease files by
+the server. It indicates the internal byte order of the server. This
+permits lease files generated on a server with one form of byte order
+to be read by a server with a different form. Lease files which do not
+contain this entry are simply treated as having the same byte order as
+the server reading them. If you are migrating lease files generated
+by a server that predates this statement and is of a different byte
+order than the your destination server, you can manually add this
+statement. It must proceed any lease entries. Valid values for this
+parameter are \fIlittle-endian\fR and \fIbig-endian\fR.
+.RE
+.PP
.SH THE DHCPv4 LEASE DECLARATION
.PP
.B lease \fIip-address\fB { \fIstatements...\fB }
*/
#include <config.h>
+#include <dhcpd.h>
#include <atf-c.h>
/* That is an example ATF test case, tailored to ISC DHCP sources.
}
+
+ATF_TC(parse_byte_order);
+
+ATF_TC_HEAD(parse_byte_order, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests byte-order conversion.");
+}
+
+ATF_TC_BODY(parse_byte_order, tc)
+{
+ uint32_t ret_value = 0;
+ uint32_t source_value = 0xaabbccdd;
+
+ /* With order set to 0, function should default to no conversion */
+ authoring_byte_order = 0;
+ ret_value = parse_byte_order_uint32(&source_value);
+ if (ret_value != source_value) {
+ atf_tc_fail("default/non-conversion failed!");
+ }
+
+ /* With matching byte order, function should not do the conversion */
+ authoring_byte_order = DHCP_BYTE_ORDER;
+ ret_value = parse_byte_order_uint32(&source_value);
+ if (ret_value != source_value) {
+ atf_tc_fail("matching/non-conversion failed!");
+ }
+
+ /* With opposite byte order, function should do the conversion */
+ authoring_byte_order = (DHCP_BYTE_ORDER == LITTLE_ENDIAN ?
+ BIG_ENDIAN : LITTLE_ENDIAN);
+ ret_value = parse_byte_order_uint32(&source_value);
+ if (ret_value != 0xddccbbaa) {
+ atf_tc_fail("conversion failed!");
+ }
+
+ /* Converting the converted value should give us the original value */
+ ret_value = parse_byte_order_uint32(&ret_value);
+ if (ret_value != source_value) {
+ atf_tc_fail("round trip conversion failed!");
+ }
+
+ atf_tc_pass();
+}
+
+
/* This macro defines main() method that will call specified
test cases. tp and simple_test_case names can be whatever you want
as long as it is a valid variable identifier. */
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, simple_test_case);
+ ATF_TP_ADD_TC(tp, parse_byte_order);
return (atf_no_error());
}