2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-31 22:35:25 +00:00
+- Add support for a simple check that the server id in a request message
+  to a failover peer matches the server id of the server.  This support
+  is enabled by editing the file includes/site.h and uncommenting the
+  definition for SERVER_ID_CHECK.  The option has several restrictions
+  and issues - please read the comment in the site.h file before
+  enabling it.
+  [ISC-Bugs #31463]
This commit is contained in:
Shawn Routhier
2012-11-16 15:02:13 -08:00
parent 3aa562f8ad
commit f8380d3ff2
5 changed files with 207 additions and 115 deletions

View File

@@ -161,6 +161,14 @@ work on other platforms. Please report any problems and suggested fixes to
variables. variables.
[ISC-Bugs #29068] [ISC-Bugs #29068]
- Add support for a simple check that the server id in a request message
to a failover peer matches the server id of the server. This support
is enabled by editing the file includes/site.h and uncommenting the
definition for SERVER_ID_CHECK. The option has several restrictions
and issues - please read the comment in the site.h file before
enabling it.
[ISC-Bugs #31463]
Changes since 4.2.3 Changes since 4.2.3
! Add a check for a null pointer before calling the regexec function. ! Add a check for a null pointer before calling the regexec function.

View File

@@ -2163,7 +2163,11 @@ unsigned cons_agent_information_options (struct option_state *,
unsigned, unsigned); unsigned, unsigned);
void get_server_source_address(struct in_addr *from, void get_server_source_address(struct in_addr *from,
struct option_state *options, struct option_state *options,
struct option_state *out_options,
struct packet *packet); struct packet *packet);
void setup_server_source_address(struct in_addr *from,
struct option_state *options,
struct packet *packet);
/* dhcpleasequery.c */ /* dhcpleasequery.c */
void dhcpleasequery (struct packet *, int); void dhcpleasequery (struct packet *, int);

View File

@@ -248,3 +248,30 @@
computed for a NAK may not match that computed for an ACK. */ computed for a NAK may not match that computed for an ACK. */
#define SERVER_ID_FOR_NAK #define SERVER_ID_FOR_NAK
/* When processing a request do a simple check to compare the
server id the client sent with the one the server would send.
In order to minimize the complexity of the code the server
only checks for a server id option in the global and subnet
scopes. Complicated configurations may result in differnet
server ids for this check and when the server id for a reply
packet is determined, which would prohibit the server from
responding.
The primary use for this option is when a client broadcasts
a request but requires the response to come from one of the
failover peers. An example of this would be when a client
reboots while its lease is still active - in this case both
servers will normally respond. Most of the time the client
won't check the server id and can use either of the responses.
However if the client does check the server id it may reject
the response if it came from the wrong peer. If the timing
is such that the "wrong" peer responds first most of the time
the client may not get an address for some time.
Currently this option is only available when failover is in
use.
Care should be taken before enabling this option. */
/* #define SERVER_ID_CHECK */

View File

@@ -472,8 +472,10 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
* safe. * safe.
*/ */
sprintf (smbuf, " (%s)", piaddr (sip)); sprintf (smbuf, " (%s)", piaddr (sip));
} else } else {
smbuf [0] = 0; smbuf [0] = 0;
sip.len = 0;
}
/* %Audit% This is log output. %2004.06.17,Safe% /* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log. * If we truncate we hope the user can get a hint from the log.
@@ -554,6 +556,27 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
goto out; goto out;
} }
#if defined(SERVER_ID_CHECK)
/* Do a quick check on the server source address to see if
it is ours. sip is the incoming servrer id. To avoid
problems with confused clients we do some sanity checks
to verify sip's length and that it isn't all zeros.
We then get the server id we would likely use for this
packet and compare them. If they don't match it we assume
we didn't send the offer and so we don't process the request.
*/
if ((sip.len == 4) &&
(memcmp(sip.iabuf, "\0\0\0\0", sip.len) != 0)) {
struct in_addr from;
setup_server_source_address(&from, NULL, packet);
if (memcmp(sip.iabuf, &from, sip.len) != 0) {
log_debug("%s: not our server id", msgbuf);
goto out;
}
}
#endif /* if defined(SERVER_ID_CHECK) */
/* At this point it's possible that we will get a broadcast /* At this point it's possible that we will get a broadcast
DHCPREQUEST for a lease that we didn't offer, because DHCPREQUEST for a lease that we didn't offer, because
both we and the peer are in a position to offer it. both we and the peer are in a position to offer it.
@@ -1139,7 +1162,7 @@ void dhcpinform (packet, ms_nulltp)
option_cache_dereference (&oc, MDL); option_cache_dereference (&oc, MDL);
} }
get_server_source_address(&from, options, packet); get_server_source_address(&from, options, options, packet);
/* Use the subnet mask from the subnet declaration if no other /* Use the subnet mask from the subnet declaration if no other
mask has been provided. */ mask has been provided. */
@@ -1336,7 +1359,6 @@ void nak_lease (packet, cip)
struct sockaddr_in to; struct sockaddr_in to;
struct in_addr from; struct in_addr from;
int result; int result;
int got_source = 0;
struct dhcp_packet raw; struct dhcp_packet raw;
unsigned char nak = DHCPNAK; unsigned char nak = DHCPNAK;
struct packet outgoing; struct packet outgoing;
@@ -1388,95 +1410,22 @@ void nak_lease (packet, cip)
save_option (&dhcp_universe, options, oc); save_option (&dhcp_universe, options, oc);
option_cache_dereference (&oc, MDL); option_cache_dereference (&oc, MDL);
#if defined(SERVER_ID_FOR_NAK)
/* /*
* Check to see if there is a server id we should use for the NAK. * If we are configured to do so we try to find a server id
* In order to minimize the effort involved we only check the * option even for NAKS by calling setup_server_source_address().
* global, shared_network and first subnet and pool on the * This function will set up an options list from the global
* shared_network (if they exist). We skip the other subnets * and subnet scopes before trying to get the source address.
* and pools and don't check on the host declarations. *
* * Otherwise we simply call get_server_source_address()
* We get the shared subnet from the packet and execute the statements * directly, without a server options list, this means
* then check for a server id. As we only want the server ID we * we'll get the source address from the interface address.
* execute the statements into a separate options area and then
* free that area when we finish
*/ */
if (packet->shared_network != NULL) { #if defined(SERVER_ID_FOR_NAK)
struct option_state *sid_options = NULL; setup_server_source_address(&from, options, packet);
struct data_string d; #else
struct option_cache *soc = NULL; get_server_source_address(&from, NULL, options, packet);
option_state_allocate (&sid_options, MDL);
/*
* If we have a subnet and group start with that else start
* with the shared network group. The first will recurse and
* include the second.
*/
if ((packet->shared_network->subnets != NULL) &&
(packet->shared_network->subnets->group != NULL)) {
execute_statements_in_scope(NULL, packet, NULL, NULL,
packet->options, sid_options,
&global_scope,
packet->shared_network->subnets->group,
NULL);
} else {
execute_statements_in_scope(NULL, packet, NULL, NULL,
packet->options, sid_options,
&global_scope,
packet->shared_network->group,
NULL);
}
/* do the pool if there is one */
if (packet->shared_network->pools != NULL) {
execute_statements_in_scope(NULL, packet, NULL, NULL,
packet->options, sid_options,
&global_scope,
packet->shared_network->pools->group,
packet->shared_network->group);
}
memset(&d, 0, sizeof(d));
i = DHO_DHCP_SERVER_IDENTIFIER;
oc = lookup_option(&dhcp_universe, sid_options, i);
if ((oc != NULL) &&
(evaluate_option_cache(&d, packet, NULL, NULL,
packet->options, sid_options,
&global_scope, oc, MDL))) {
if (d.len == sizeof(from)) {
/* We have a server id and it's the proper length
* for an address save the address and try to add
* it to the options list we are building for the
* response packet.
*/
memcpy(&from, d.data, sizeof(from));
got_source = 1;
if (option_cache_allocate(&soc, MDL) &&
(make_const_data(&soc->expression,
(unsigned char *)&from,
sizeof(from),
0, 1, MDL))) {
option_code_hash_lookup(&soc->option,
dhcp_universe.code_hash,
&i, 0, MDL);
save_option(&dhcp_universe, options,
soc);
}
if (soc != NULL)
option_cache_dereference(&soc, MDL);
}
data_string_forget(&d, MDL);
}
oc = NULL;
option_state_dereference (&sid_options, MDL);
}
#endif /* if defined(SERVER_ID_FOR_NAK) */ #endif /* if defined(SERVER_ID_FOR_NAK) */
if (got_source == 0) {
get_server_source_address(&from, options, packet);
}
/* If there were agent options in the incoming packet, return /* If there were agent options in the incoming packet, return
* them. We do not check giaddr to detect the presence of a * them. We do not check giaddr to detect the presence of a
@@ -2634,7 +2583,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
enqueue = ISC_FALSE; enqueue = ISC_FALSE;
/* Install the new information on 'lt' onto the lease at /* Install the new information on 'lt' onto the lease at
* 'lease'.  We will not 'commit' this information to disk * 'lease'. We will not 'commit' this information to disk
* yet (fsync()), we will 'propogate' the information if * yet (fsync()), we will 'propogate' the information if
* this is BOOTP or a DHCPACK, but we will not 'pimmediate'ly * this is BOOTP or a DHCPACK, but we will not 'pimmediate'ly
* transmit failover binding updates (this is delayed until * transmit failover binding updates (this is delayed until
@@ -2742,7 +2691,8 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
option_cache_dereference (&oc, MDL); option_cache_dereference (&oc, MDL);
} }
get_server_source_address(&from, state->options, packet); get_server_source_address(&from, state->options,
state->options, packet);
memcpy(state->from.iabuf, &from, sizeof(from)); memcpy(state->from.iabuf, &from, sizeof(from));
state->from.len = sizeof(from); state->from.len = sizeof(from);
@@ -4510,23 +4460,47 @@ int locate_network (packet)
/* /*
* Try to figure out the source address to send packets from. * Try to figure out the source address to send packets from.
* *
* If the packet we received specified the server address, then we * from is the address structure we use to return any address
* will use that. * we find.
* *
* Otherwise, use the first address from the interface. If we do * options is the option cache to search. This may include
* this, we also save this into the option cache as the server * options from the incoming packet and configuration information.
* address. *
* out_options is the outgoing option cache. This cache
* may be the same as options. If send_options isn't NULL
* we may save the server address option into it. We do so
* if send_options is different than options or if the option
* wasn't in options and we needed to find the address elsewhere.
*
* packet is the state structure for the incoming packet
*
* When finding the address we first check to see if it is
* in the options list. If it isn't we use the first address
* from the interface.
*
* While this is slightly more complicated than I'd like it allows
* us to use the same code in several different places. ack,
* inform and lease query use it to find the address and fill
* in the options if we get the address from the interface.
* nack uses it to find the address and copy it to the outgoing
* cache. dhcprequest uses it to find the address for comparison
* and doesn't need to add it to an outgoing list.
*/ */
void void
get_server_source_address(struct in_addr *from, get_server_source_address(struct in_addr *from,
struct option_state *options, struct option_state *options,
struct option_state *out_options,
struct packet *packet) { struct packet *packet) {
unsigned option_num; unsigned option_num;
struct option_cache *oc; struct option_cache *oc = NULL;
struct data_string d; struct data_string d;
struct in_addr *a; struct in_addr *a = NULL;
isc_boolean_t found = ISC_FALSE;
int allocate = 0;
memset(&d, 0, sizeof(d)); memset(&d, 0, sizeof(d));
memset(from, 0, sizeof(*from));
option_num = DHO_DHCP_SERVER_IDENTIFIER; option_num = DHO_DHCP_SERVER_IDENTIFIER;
oc = lookup_option(&dhcp_universe, options, option_num); oc = lookup_option(&dhcp_universe, options, option_num);
@@ -4535,32 +4509,111 @@ get_server_source_address(struct in_addr *from,
packet->options, options, packet->options, options,
&global_scope, oc, MDL)) { &global_scope, oc, MDL)) {
if (d.len == sizeof(*from)) { if (d.len == sizeof(*from)) {
found = ISC_TRUE;
memcpy(from, d.data, sizeof(*from)); memcpy(from, d.data, sizeof(*from));
data_string_forget(&d, MDL);
return; /*
* Arrange to save a copy of the data
* to the outgoing list.
*/
if ((out_options != NULL) &&
(options != out_options)) {
a = from;
allocate = 1;
}
} }
data_string_forget(&d, MDL); data_string_forget(&d, MDL);
} }
oc = NULL; oc = NULL;
} }
if (packet->interface->address_count > 0) { if ((found == ISC_FALSE) &&
if (option_cache_allocate(&oc, MDL)) { (packet->interface->address_count > 0)) {
a = &packet->interface->addresses[0];
if (make_const_data(&oc->expression,
(unsigned char *)a, sizeof(*a),
0, 0, MDL)) {
option_code_hash_lookup(&oc->option,
dhcp_universe.code_hash,
&option_num, 0, MDL);
save_option(&dhcp_universe, options, oc);
}
option_cache_dereference(&oc, MDL);
}
*from = packet->interface->addresses[0]; *from = packet->interface->addresses[0];
} else {
memset(from, 0, sizeof(*from)); if (out_options != NULL) {
a = &packet->interface->addresses[0];
}
} }
if ((a != NULL) &&
(option_cache_allocate(&oc, MDL))) {
if (make_const_data(&oc->expression,
(unsigned char *)a, sizeof(*a),
0, allocate, MDL)) {
option_code_hash_lookup(&oc->option,
dhcp_universe.code_hash,
&option_num, 0, MDL);
save_option(&dhcp_universe, out_options, oc);
}
option_cache_dereference(&oc, MDL);
}
return;
}
/*
* Set up an option state list to try and find a server option.
* We don't go through all possible options - in particualr we
* skip the hosts and we don't include the lease to avoid
* making changes to it. This means that we won't get the
* correct server id if the admin puts them on hosts or
* builds the server id with information from the lease.
*
* As this is a fallback function (used to handle NAKs or
* sort out server id mismatch in failover) and requires
* configuration by the admin, it should be okay.
*/
void
setup_server_source_address(struct in_addr *from,
struct option_state *options,
struct packet *packet) {
struct option_state *sid_options = NULL;
if (packet->shared_network != NULL) {
option_state_allocate (&sid_options, MDL);
/*
* If we have a subnet and group start with that else start
* with the shared network group. The first will recurse and
* include the second.
*/
if ((packet->shared_network->subnets != NULL) &&
(packet->shared_network->subnets->group != NULL)) {
execute_statements_in_scope(NULL, packet, NULL, NULL,
packet->options, sid_options,
&global_scope,
packet->shared_network->subnets->group,
NULL);
} else {
execute_statements_in_scope(NULL, packet, NULL, NULL,
packet->options, sid_options,
&global_scope,
packet->shared_network->group,
NULL);
}
/* do the pool if there is one */
if (packet->shared_network->pools != NULL) {
execute_statements_in_scope(NULL, packet, NULL, NULL,
packet->options, sid_options,
&global_scope,
packet->shared_network->pools->group,
packet->shared_network->group);
}
/* currently we don't bother with classes or hosts as
* neither seems to be useful in this case */
}
/* Make the call to get the server address */
get_server_source_address(from, sid_options, options, packet);
/* get rid of the option cache */
if (sid_options != NULL)
option_state_dereference(&sid_options, MDL);
} }
/* /*

View File

@@ -616,7 +616,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
/* /*
* Figure out which address to use to send from. * Figure out which address to use to send from.
*/ */
get_server_source_address(&siaddr, options, packet); get_server_source_address(&siaddr, options, options, packet);
/* /*
* Set up the option buffer. * Set up the option buffer.