From: Thomas Markwalder Date: Tue, 13 Oct 2015 10:34:15 +0000 (-0400) Subject: [master] Added authoring-btye-order parameter to lease file handling X-Git-Tag: v4_3_4~60 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=347d4962d01dbbf991668eac48fcdbe5c74bd4ce;p=thirdparty%2Fdhcp.git [master] Added authoring-btye-order parameter to lease file handling Merged in rt38396. --- diff --git a/RELNOTES b/RELNOTES index db930e0a4..b87f33071 100644 --- a/RELNOTES +++ b/RELNOTES @@ -54,6 +54,13 @@ by Eric Young (eay@cryptsoft.com). Changes since 4.3.3 +- 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" diff --git a/common/conflex.c b/common/conflex.c index f23a8c783..a1ba95fda 100644 --- a/common/conflex.c +++ b/common/conflex.c @@ -783,6 +783,8 @@ intern(char *atom, enum dhcp_token dfv) { return ATSFP; break; } + if (!strcasecmp(atom + 1, "uthoring-byte-order")) + return AUTHORING_BYTE_ORDER; if (!strncasecmp(atom + 1, "ut", 2)) { if (isascii(atom[3]) && (tolower((unsigned char)atom[3]) == 'h')) { @@ -827,6 +829,9 @@ intern(char *atom, enum dhcp_token dfv) { 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")) @@ -1128,6 +1133,9 @@ intern(char *atom, enum dhcp_token dfv) { 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)) { diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 1fd12dbb4..89f0e55f8 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -2037,6 +2037,7 @@ extern int dont_use_fsync; extern int server_id_check; extern int prefix_length_mode; +extern int authoring_byte_order; extern const char *path_dhcpd_conf; extern const char *path_dhcpd_db; @@ -2134,6 +2135,7 @@ void parse_ia_pd_declaration(struct parse *); void parse_server_duid(struct parse *cfile); void parse_server_duid_conf(struct parse *cfile); void parse_pool6_statement (struct parse *, struct group *, int); +uint32_t parse_byte_order_uint32(const void *source); /* ddns.c */ int ddns_updates(struct packet *, struct lease *, struct lease *, diff --git a/includes/dhctoken.h b/includes/dhctoken.h index c4b77a7c3..c1db9da39 100644 --- a/includes/dhctoken.h +++ b/includes/dhctoken.h @@ -368,7 +368,10 @@ enum dhcp_token { POOL6 = 669, V6RELAY = 670, V6RELOPT = 671, - PARSE_VENDOR_OPT = 672 + PARSE_VENDOR_OPT = 672, + AUTHORING_BYTE_ORDER = 673, + TOKEN_LITTLE_ENDIAN = 674, + TOKEN_BIG_ENDIAN = 675 }; #define is_identifier(x) ((x) >= FIRST_TOKEN && \ diff --git a/server/confpars.c b/server/confpars.c index 42a1712d9..6358fe2db 100644 --- a/server/confpars.c +++ b/server/confpars.c @@ -35,6 +35,8 @@ static unsigned char global_host_once = 1; 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; @@ -302,6 +304,8 @@ isc_result_t lease_file_subparse (struct parse *cfile) } 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); @@ -1391,6 +1395,70 @@ void parse_failover_state (cfile, state, stos) #endif /* defined (FAILOVER_PROTOCOL) */ /*! + * \brief Parses an authoring-byte-order statement + * + * A valid statement looks like this: + * + * authoring-byte-order :== + * PARSE_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; + } +} + +/*! * * \brief Parse allow and deny statements * @@ -4634,7 +4702,8 @@ parse_ia_na_declaration(struct parse *cfile) { 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."); @@ -5086,7 +5155,8 @@ parse_ia_ta_declaration(struct parse *cfile) { 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."); @@ -5539,7 +5609,8 @@ parse_ia_pd_declaration(struct parse *cfile) { 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."); @@ -6233,5 +6304,42 @@ parse_server_duid_conf(struct parse *cfile) { } } +/*! + * \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 */ diff --git a/server/db.c b/server/db.c index 5238ed86c..103be8aab 100644 --- a/server/db.c +++ b/server/db.c @@ -1051,6 +1051,10 @@ void db_startup (testp) #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); @@ -1155,6 +1159,7 @@ int new_lease_file () 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; @@ -1163,6 +1168,18 @@ int new_lease_file () 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. */ diff --git a/server/dhcpd.c b/server/dhcpd.c index ab73443a7..792684d15 100644 --- a/server/dhcpd.c +++ b/server/dhcpd.c @@ -80,6 +80,8 @@ int dont_use_fsync = 0; /* 0 = default, use fsync, 1 = don't use fsync */ int server_id_check = 0; /* 0 = default, don't check server id, 1 = do check */ int prefix_length_mode = PLM_EXACT; +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; diff --git a/server/dhcpd.leases.5 b/server/dhcpd.leases.5 index f490a634f..025b9bf8d 100644 --- a/server/dhcpd.leases.5 +++ b/server/dhcpd.leases.5 @@ -181,6 +181,23 @@ certain event occurs. The possible events that can occur for an 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 } diff --git a/server/tests/simple_unittest.c b/server/tests/simple_unittest.c index e6d04b900..5e1db9197 100644 --- a/server/tests/simple_unittest.c +++ b/server/tests/simple_unittest.c @@ -15,6 +15,7 @@ */ #include +#include #include /* That is an example ATF test case, tailored to ISC DHCP sources. @@ -66,12 +67,58 @@ ATF_TC_BODY(simple_test_case, tc) } + +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()); }