1468 lines
42 KiB
C
1468 lines
42 KiB
C
/* failover.c
|
|
|
|
Failover protocol support code... */
|
|
|
|
/*
|
|
* Copyright (c) 1999-2000 Internet Software Consortium.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of The Internet Software Consortium nor the names
|
|
* of its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
|
|
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* This software has been written for the Internet Software Consortium
|
|
* by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
|
|
* To learn more about the Internet Software Consortium, see
|
|
* ``http://www.isc.org/''. To learn more about Vixie Enterprises,
|
|
* see ``http://www.vix.com''. To learn more about Nominum, Inc., see
|
|
* ``http://www.nominum.com''.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char copyright[] =
|
|
"$Id: failover.c,v 1.1.1.1 2000/04/22 07:12:05 mellon Exp $ Copyright (c) 1999-2000 The Internet Software Consortium. All rights reserved.\n";
|
|
#endif /* not lint */
|
|
|
|
#include "dhcpd.h"
|
|
#include "version.h"
|
|
#include <omapip/omapip_p.h>
|
|
|
|
#if defined (FAILOVER_PROTOCOL)
|
|
static struct hash_table *failover_hash;
|
|
static dhcp_failover_state_t *failover_states;
|
|
static isc_result_t do_a_failover_option (omapi_object_t *,
|
|
dhcp_failover_link_t *);
|
|
|
|
void enter_failover_peer (peer)
|
|
dhcp_failover_state_t *peer;
|
|
{
|
|
}
|
|
|
|
isc_result_t find_failover_peer (peer, name)
|
|
dhcp_failover_state_t **peer;
|
|
char *name;
|
|
{
|
|
dhcp_failover_state_t *p;
|
|
|
|
for (p = failover_states; p; p = p -> next)
|
|
if (!strcmp (name, p -> name))
|
|
break;
|
|
if (p)
|
|
return omapi_object_reference ((omapi_object_t **)peer,
|
|
(omapi_object_t *)p, MDL);
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
/* The failover protocol has three objects associated with it. For
|
|
each failover partner declaration in the dhcpd.conf file, primary
|
|
or secondary, there is a failover_state object. For any primary or
|
|
secondary state object that has a connection to its peer, there is
|
|
also a failover_link object, which has its own input state seperate
|
|
from the failover protocol state for managing the actual bytes
|
|
coming in off the wire. Finally, there will be one listener object
|
|
for every distinct port number associated with a secondary
|
|
failover_state object. Normally all secondary failover_state
|
|
objects are expected to listen on the same port number, so there
|
|
need be only one listener object, but if different port numbers are
|
|
specified for each failover object, there could be as many as one
|
|
listener object for each secondary failover_state object. */
|
|
|
|
/* This, then, is the implemention of the failover link object. */
|
|
|
|
isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
|
|
{
|
|
isc_result_t status;
|
|
dhcp_failover_link_t *obj;
|
|
unsigned long port;
|
|
omapi_value_t *value = (omapi_value_t *)0;
|
|
dhcp_failover_state_t *state;
|
|
omapi_object_t *o;
|
|
int i;
|
|
struct data_string ds;
|
|
|
|
/* Find the failover state in the object chain. */
|
|
for (o = h; o -> outer; o = o -> outer)
|
|
;
|
|
for (; o; o = o -> inner) {
|
|
if (o -> type == dhcp_type_failover_state)
|
|
break;
|
|
}
|
|
if (!o)
|
|
return ISC_R_INVALIDARG;
|
|
state = (dhcp_failover_state_t *)o;
|
|
|
|
obj = (dhcp_failover_link_t *)dmalloc (sizeof *obj, MDL);
|
|
if (!obj)
|
|
return ISC_R_NOMEMORY;
|
|
memset (obj, 0, sizeof *obj);
|
|
obj -> refcnt = 1;
|
|
obj -> type = dhcp_type_failover_link;
|
|
option_cache_reference (&obj -> peer_address, state -> address, MDL);
|
|
obj -> peer_port = state -> port;
|
|
|
|
memset (&ds, 0, sizeof ds);
|
|
if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
|
|
(struct option_state *)0,
|
|
(struct option_state *)0,
|
|
&global_scope, obj -> peer_address, MDL)) {
|
|
option_cache_dereference (&obj -> peer_address, MDL);
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
return ISC_R_UNEXPECTED;
|
|
}
|
|
|
|
/* Kludge around the fact that omapi_connect expects an ASCII string
|
|
representing the address to which to connect. */
|
|
status = ISC_R_INVALIDARG;
|
|
for (i = 0; i + 4 <= ds.len; i += 4) {
|
|
char peer_name [40];
|
|
sprintf (peer_name, "%d.%d.%d.%d",
|
|
ds.data [i], ds.data [i + 1],
|
|
ds.data [i + 2], ds.data [i + 3]);
|
|
status = omapi_connect ((omapi_object_t *)obj,
|
|
peer_name, port);
|
|
if (status == ISC_R_SUCCESS)
|
|
break;
|
|
}
|
|
data_string_forget (&ds, MDL);
|
|
|
|
/* If we didn't connect to any of the addresses, give up. */
|
|
if (status != ISC_R_SUCCESS) {
|
|
lose:
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
option_cache_dereference (&obj -> peer_address, MDL);
|
|
return status;
|
|
}
|
|
status = omapi_object_reference (&h -> outer,
|
|
(omapi_object_t *)obj, MDL);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto lose;
|
|
status = omapi_object_reference (&obj -> inner, h, MDL);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto lose;
|
|
|
|
/* Send the introductory message. */
|
|
status = dhcp_failover_send_connect ((omapi_object_t *)obj);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto lose;
|
|
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
|
|
const char *name, va_list ap)
|
|
{
|
|
isc_result_t status;
|
|
dhcp_failover_link_t *link;
|
|
omapi_object_t *c;
|
|
u_int16_t nlen;
|
|
u_int32_t vlen;
|
|
|
|
if (h -> type != dhcp_type_failover_link) {
|
|
/* XXX shouldn't happen. Put an assert here? */
|
|
return ISC_R_UNEXPECTED;
|
|
}
|
|
link = (dhcp_failover_link_t *)h;
|
|
|
|
/* Not a signal we recognize? */
|
|
if (strcmp (name, "ready")) {
|
|
if (h -> inner && h -> inner -> type -> signal_handler)
|
|
return (*(h -> inner -> type -> signal_handler)) (h,
|
|
name,
|
|
ap);
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
if (!h -> outer || h -> outer -> type != omapi_type_connection)
|
|
return ISC_R_INVALIDARG;
|
|
c = h -> outer;
|
|
|
|
/* We get here because we requested that we be woken up after
|
|
some number of bytes were read, and that number of bytes
|
|
has in fact been read. */
|
|
switch (link -> state) {
|
|
case dhcp_flink_start:
|
|
link -> state = dhcp_flink_message_length_wait;
|
|
if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
|
|
break;
|
|
case dhcp_flink_message_length_wait:
|
|
link -> state = dhcp_flink_message_wait;
|
|
link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
|
|
if (!link -> imsg) {
|
|
dhcp_flink_fail:
|
|
if (link -> imsg) {
|
|
dfree (link -> imsg, MDL);
|
|
link -> imsg = (failover_message_t *)0;
|
|
}
|
|
link -> state = dhcp_flink_disconnected;
|
|
omapi_disconnect (c, 1);
|
|
/* XXX just blow away the protocol state now?
|
|
XXX or will disconnect blow it away? */
|
|
return ISC_R_UNEXPECTED;
|
|
}
|
|
memset (link -> imsg, 0, sizeof (link -> imsg));
|
|
/* Get the length: */
|
|
omapi_connection_get_uint16 (c, &link -> imsg_len);
|
|
link -> imsg_count = 0; /* Bytes read. */
|
|
|
|
/* Maximum of 2048 bytes in any failover message. */
|
|
if (link -> imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE)
|
|
goto dhcp_flink_fail;
|
|
|
|
if ((omapi_connection_require (c, link -> imsg_len)) !=
|
|
ISC_R_SUCCESS)
|
|
break;
|
|
case dhcp_flink_message_wait:
|
|
/* Read in the message. At this point we have the
|
|
entire message in the input buffer. For each
|
|
incoming value ID, set a bit in the bitmask
|
|
indicating that we've gotten it. Maybe flag an
|
|
error message if the bit is already set. Once
|
|
we're done reading, we can check the bitmask to
|
|
make sure that the required fields for each message
|
|
have been included. */
|
|
|
|
link -> imsg_count += 2; /* Count the length as read. */
|
|
|
|
/* Get message type. */
|
|
omapi_connection_copyout (&link -> imsg -> type, c, 1);
|
|
link -> imsg_count++;
|
|
|
|
/* Get message payload offset. */
|
|
omapi_connection_copyout (&link -> imsg_payoff, c, 1);
|
|
link -> imsg_count++;
|
|
|
|
/* Get message time. */
|
|
omapi_connection_get_uint32 (c, &link -> imsg -> time);
|
|
link -> imsg_count += 4;
|
|
|
|
/* Get transaction ID. */
|
|
omapi_connection_get_uint32 (c, &link -> imsg -> xid);
|
|
link -> imsg_count += 4;
|
|
|
|
/* Skip over any portions of the message header that we
|
|
don't understand. */
|
|
if (link -> imsg_payoff - link -> imsg_count) {
|
|
omapi_connection_copyout ((unsigned char *)0, c,
|
|
(link -> imsg_payoff -
|
|
link -> imsg_count));
|
|
link -> imsg_count = link -> imsg_payoff;
|
|
}
|
|
|
|
/* Get transaction ID. */
|
|
omapi_connection_get_uint32 (c, &link -> imsg -> xid);
|
|
link -> imsg_count += 4;
|
|
|
|
/* Now start sucking options off the wire. */
|
|
while (link -> imsg_count < link -> imsg_len) {
|
|
if (do_a_failover_option (c, link) != ISC_R_SUCCESS)
|
|
goto dhcp_flink_fail;
|
|
}
|
|
|
|
/* Once we have the entire message, and we've validated
|
|
it as best we can here, pass it to the parent. */
|
|
omapi_signal_in (h -> outer, "message", link);
|
|
break;
|
|
|
|
default:
|
|
/* XXX should never get here. Assertion? */
|
|
break;
|
|
}
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
static isc_result_t do_a_failover_option (c, link)
|
|
omapi_object_t *c;
|
|
dhcp_failover_link_t *link;
|
|
{
|
|
u_int16_t option_code;
|
|
u_int16_t option_len;
|
|
unsigned char *op;
|
|
unsigned op_size;
|
|
unsigned op_count;
|
|
int i;
|
|
|
|
if (link -> imsg_count + 2 > link -> imsg_len) {
|
|
log_error ("FAILOVER: message overflow at option code.");
|
|
return ISC_R_PROTOCOLERROR;
|
|
}
|
|
|
|
/* Get option code. */
|
|
omapi_connection_get_uint16 (c, &option_code);
|
|
link -> imsg_count += 2;
|
|
|
|
/* Get option length. */
|
|
omapi_connection_get_uint16 (c, &option_len);
|
|
link -> imsg_count += 2;
|
|
|
|
if (link -> imsg_count + option_len > link -> imsg_len) {
|
|
log_error ("FAILOVER: message overflow at %s",
|
|
" length.");
|
|
return ISC_R_PROTOCOLERROR;
|
|
}
|
|
|
|
/* If it's an unknown code, skip over it. */
|
|
if (option_code > FTO_MAX) {
|
|
#if defined (FAILOVER_PROTOCOL_DEBUG) && defined (FAILOVER_DEBUG_VERBOSE)
|
|
log_debug (" option code %d len %d (not recognized)",
|
|
option_code, option_len);
|
|
#endif
|
|
omapi_connection_copyout ((unsigned char *)0, c, option_len);
|
|
link -> imsg_count += option_len;
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
/* If it's the digest, do it now. */
|
|
if (ft_options [option_code].type == FT_DIGEST) {
|
|
link -> imsg_count += option_len;
|
|
if (link -> imsg_count != link -> imsg_len) {
|
|
log_error ("FAILOVER: digest not at end of message");
|
|
return ISC_R_PROTOCOLERROR;
|
|
}
|
|
#if defined (FAILOVER_PROTOCOL_DEBUG) && defined (FAILOVER_DEBUG_VERBOSE)
|
|
log_debug (" option %s len %d",
|
|
ft_options [option_code].name, option_len);
|
|
#endif
|
|
/* For now, just dump it. */
|
|
omapi_connection_copyout ((unsigned char *)0, c, option_len);
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
/* Only accept an option once. */
|
|
if (link -> imsg -> options_present & ft_options [option_code].bit) {
|
|
log_error ("FAILOVER: duplicate option %s",
|
|
ft_options [option_code].name);
|
|
return ISC_R_PROTOCOLERROR;
|
|
}
|
|
|
|
/* Make sure the option is appropriate for this type of message.
|
|
Really, any option is generally allowed for any message, and the
|
|
cases where this is not true are too complicated to represent in
|
|
this way - what this code is doing is to just avoid saving the
|
|
value of an option we don't have any way to use, which allows
|
|
us to make the failover_message structure smaller. */
|
|
if (ft_options [option_code].bit &&
|
|
!(fto_allowed [option_code] & ft_options [option_code].bit)) {
|
|
omapi_connection_copyout ((unsigned char *)0, c, option_len);
|
|
link -> imsg_count += option_len;
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
/* Figure out how many elements, how big they are, and where
|
|
to store them. */
|
|
if (ft_options [option_code].num_present) {
|
|
/* If this option takes a fixed number of elements,
|
|
we expect the space for them to be preallocated,
|
|
and we can just read the data in. */
|
|
|
|
op = ((unsigned char *)&link -> imsg) +
|
|
ft_options [option_code].offset;
|
|
op_size = ft_sizes [ft_options [option_code].type];
|
|
op_count = ft_options [option_code].num_present;
|
|
|
|
if (option_len != op_size * op_count) {
|
|
log_error ("FAILOVER: option size (%d:%d), option %s",
|
|
option_len,
|
|
(ft_sizes [ft_options [option_code].type] *
|
|
ft_options [option_code].num_present),
|
|
ft_options [option_code].name);
|
|
return ISC_R_PROTOCOLERROR;
|
|
}
|
|
} else {
|
|
failover_option_t *fo;
|
|
|
|
/* FT_DDNS* are special - one or two bytes of status
|
|
followed by the client FQDN. */
|
|
if (ft_options [option_code].type == FT_DDNS1 ||
|
|
ft_options [option_code].type == FT_DDNS1) {
|
|
ddns_fqdn_t *ddns =
|
|
((ddns_fqdn_t *)
|
|
(((char *)&link -> imsg) +
|
|
ft_options [option_code].offset));
|
|
|
|
op_count = (ft_options [option_code].type == FT_DDNS1
|
|
? 1 : 2);
|
|
|
|
omapi_connection_copyout (&ddns -> codes [0],
|
|
c, op_count);
|
|
if (op_count == 1)
|
|
ddns -> codes [1] = 0;
|
|
op_size = 1;
|
|
op_count = option_len - op_count;
|
|
|
|
ddns -> length = op_count;
|
|
ddns -> data = dmalloc (op_count, MDL);
|
|
if (!ddns -> data) {
|
|
log_error ("FAILOVER: no memory getting%s(%d)",
|
|
" DNS data ", op_count);
|
|
|
|
/* Actually, NO_MEMORY, but if we lose here
|
|
we have to drop the connection. */
|
|
return ISC_R_PROTOCOLERROR;
|
|
}
|
|
omapi_connection_copyout (ddns -> data, c, op_count);
|
|
goto out;
|
|
}
|
|
|
|
/* A zero for num_present means that any number of
|
|
elements can appear, so we have to figure out how
|
|
many we got from the length of the option, and then
|
|
fill out a failover_option structure describing the
|
|
data. */
|
|
op_size = ft_sizes [ft_options [option_code].type];
|
|
|
|
/* Make sure that option data length is a multiple of the
|
|
size of the data type being sent. */
|
|
if (op_size > 1 && option_len % op_size) {
|
|
log_error ("FAILOVER: option_len %d not %s%d",
|
|
option_len, "multiple of ", op_size);
|
|
return ISC_R_PROTOCOLERROR;
|
|
}
|
|
|
|
op_count = option_len / op_size;
|
|
|
|
fo = ((failover_option_t *)
|
|
(((char *)&link -> imsg) +
|
|
ft_options [option_code].offset));
|
|
|
|
fo -> count = op_count;
|
|
fo -> data = dmalloc (option_len, MDL);
|
|
if (!fo -> data) {
|
|
log_error ("FAILOVER: no memory getting %s (%d)",
|
|
"option data", op_count);
|
|
|
|
return ISC_R_PROTOCOLERROR;
|
|
}
|
|
op = fo -> data;
|
|
}
|
|
|
|
/* For single-byte message values and multi-byte values that
|
|
don't need swapping, just read them in all at once. */
|
|
if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
|
|
omapi_connection_copyout ((unsigned char *)op, c, option_len);
|
|
goto out;
|
|
}
|
|
|
|
/* For values that require swapping, read them in one at a time
|
|
using routines that swap bytes. */
|
|
for (i = 0; i < op_count; i++) {
|
|
switch (ft_options [option_code].type) {
|
|
case FT_UINT32:
|
|
omapi_connection_get_uint32 (c, (u_int32_t *)op);
|
|
op += 4;
|
|
break;
|
|
|
|
case FT_UINT16:
|
|
omapi_connection_get_uint16 (c, (u_int16_t *)op);
|
|
op += 2;
|
|
break;
|
|
|
|
default:
|
|
/* Everything else should have been handled
|
|
already. */
|
|
log_error ("FAILOVER: option %s: bad type %d",
|
|
ft_options [option_code].name,
|
|
ft_options [option_code].type);
|
|
return ISC_R_PROTOCOLERROR;
|
|
}
|
|
}
|
|
out:
|
|
/* Remember that we got this option. */
|
|
link -> imsg -> options_present |= ft_options [option_code].bit;
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
|
|
omapi_object_t *id,
|
|
omapi_data_string_t *name,
|
|
omapi_typed_data_t *value)
|
|
{
|
|
if (h -> type != omapi_type_protocol)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
/* Never valid to set these. */
|
|
if (!omapi_ds_strcmp (name, "link-port") ||
|
|
!omapi_ds_strcmp (name, "link-name") ||
|
|
!omapi_ds_strcmp (name, "link-state"))
|
|
return ISC_R_NOPERM;
|
|
|
|
if (h -> inner && h -> inner -> type -> set_value)
|
|
return (*(h -> inner -> type -> set_value))
|
|
(h -> inner, id, name, value);
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
|
|
omapi_object_t *id,
|
|
omapi_data_string_t *name,
|
|
omapi_value_t **value)
|
|
{
|
|
dhcp_failover_link_t *link;
|
|
|
|
if (h -> type != omapi_type_protocol)
|
|
return ISC_R_INVALIDARG;
|
|
link = (dhcp_failover_link_t *)h;
|
|
|
|
if (!omapi_ds_strcmp (name, "link-port")) {
|
|
return omapi_make_int_value (value, name,
|
|
(int)link -> peer_port, MDL);
|
|
} else if (!omapi_ds_strcmp (name, "link-state")) {
|
|
if (link -> state < 0 ||
|
|
link -> state >= dhcp_flink_state_max)
|
|
return omapi_make_string_value (value, name,
|
|
"invalid link state",
|
|
MDL);
|
|
return omapi_make_string_value
|
|
(value, name,
|
|
dhcp_flink_state_names [link -> state], MDL);
|
|
}
|
|
|
|
if (h -> inner && h -> inner -> type -> get_value)
|
|
return (*(h -> inner -> type -> get_value))
|
|
(h -> inner, id, name, value);
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
|
|
const char *file, int line)
|
|
{
|
|
dhcp_failover_link_t *link;
|
|
if (h -> type != dhcp_type_failover_link)
|
|
return ISC_R_INVALIDARG;
|
|
link = (dhcp_failover_link_t *)h;
|
|
if (link -> imsg) {
|
|
dfree (link -> imsg, file, line);
|
|
link -> imsg = (failover_message_t *)0;
|
|
}
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
/* Write all the published values associated with the object through the
|
|
specified connection. */
|
|
|
|
isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
|
|
omapi_object_t *id,
|
|
omapi_object_t *l)
|
|
{
|
|
dhcp_failover_link_t *link;
|
|
isc_result_t status;
|
|
|
|
if (l -> type != dhcp_type_failover_link)
|
|
return ISC_R_INVALIDARG;
|
|
link = (dhcp_failover_link_t *)l;
|
|
|
|
status = omapi_connection_put_name (c, "link-port");
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
status = omapi_connection_put_uint32 (c, sizeof (int));
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
status = omapi_connection_put_uint32 (c, link -> peer_port);
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
|
|
status = omapi_connection_put_name (c, "link-state");
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
if (link -> state < 0 ||
|
|
link -> state >= dhcp_flink_state_max)
|
|
status = omapi_connection_put_string (c, "invalid link state");
|
|
else
|
|
status = (omapi_connection_put_string
|
|
(c, dhcp_flink_state_names [link -> state]));
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
|
|
if (link -> inner && link -> inner -> type -> stuff_values)
|
|
return (*(link -> inner -> type -> stuff_values)) (c, id,
|
|
link -> inner);
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
/* Set up a listener for the omapi protocol. The handle stored points to
|
|
a listener object, not a protocol object. */
|
|
|
|
isc_result_t dhcp_failover_listen (omapi_object_t *h)
|
|
{
|
|
isc_result_t status;
|
|
dhcp_failover_listener_t *obj;
|
|
unsigned long port;
|
|
omapi_value_t *value = (omapi_value_t *)0;
|
|
|
|
status = omapi_get_value_str (h, (omapi_object_t *)0,
|
|
"local-port", &value);
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
if (!value -> value) {
|
|
omapi_value_dereference (&value, MDL);
|
|
return ISC_R_INVALIDARG;
|
|
}
|
|
|
|
status = omapi_get_int_value (&port, value -> value);
|
|
omapi_value_dereference (&value, MDL);
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
|
|
obj = (dhcp_failover_listener_t *)dmalloc (sizeof *obj, MDL);
|
|
if (!obj)
|
|
return ISC_R_NOMEMORY;
|
|
memset (obj, 0, sizeof *obj);
|
|
obj -> refcnt = 1;
|
|
obj -> type = dhcp_type_failover_listener;
|
|
obj -> local_port = port;
|
|
|
|
status = omapi_listen ((omapi_object_t *)obj, port, 1);
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
|
|
status = omapi_object_reference (&h -> outer,
|
|
(omapi_object_t *)obj, MDL);
|
|
if (status != ISC_R_SUCCESS) {
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
return status;
|
|
}
|
|
status = omapi_object_reference (&obj -> inner, h, MDL);
|
|
if (status != ISC_R_SUCCESS) {
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Signal handler for protocol listener - if we get a connect signal,
|
|
create a new protocol connection, otherwise pass the signal down. */
|
|
|
|
isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
|
|
const char *name, va_list ap)
|
|
{
|
|
isc_result_t status;
|
|
omapi_connection_object_t *c;
|
|
dhcp_failover_link_t *obj;
|
|
dhcp_failover_listener_t *p;
|
|
dhcp_failover_state_t *state;
|
|
|
|
if (!o || o -> type != dhcp_type_failover_listener)
|
|
return ISC_R_INVALIDARG;
|
|
p = (dhcp_failover_listener_t *)o;
|
|
|
|
/* Not a signal we recognize? */
|
|
if (strcmp (name, "connect")) {
|
|
if (p -> inner && p -> inner -> type -> signal_handler)
|
|
return (*(p -> inner -> type -> signal_handler))
|
|
(p -> inner, name, ap);
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
c = va_arg (ap, omapi_connection_object_t *);
|
|
if (!c || c -> type != omapi_type_connection)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
/* See if we can find a secondary failover_state object that
|
|
matches this connection. */
|
|
for (state = failover_states; state; state = state -> next) {
|
|
int i;
|
|
struct data_string ds;
|
|
|
|
memset (&ds, 0, sizeof ds);
|
|
if (evaluate_option_cache (&ds, (struct packet *)0,
|
|
(struct lease *)0,
|
|
(struct option_state *)0,
|
|
(struct option_state *)0,
|
|
&global_scope,
|
|
state -> address, MDL)) {
|
|
for (i = 0; i < ds.len; i += 4) {
|
|
if (!memcmp (&ds.data [i],
|
|
&c -> remote_addr, 4))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If we can't find a failover protocol state for this remote
|
|
host, drop the connection */
|
|
if (!state) {
|
|
/* XXX Send a refusal message first?
|
|
XXX Look in protocol spec for guidance. */
|
|
/* XXX An error message from a connect signal should
|
|
XXX drop the connection - make sure this is what
|
|
XXX actually happens! */
|
|
return ISC_R_INVALIDARG;
|
|
}
|
|
|
|
obj = (dhcp_failover_link_t *)dmalloc (sizeof *obj, MDL);
|
|
if (!obj)
|
|
return ISC_R_NOMEMORY;
|
|
memset (obj, 0, sizeof *obj);
|
|
obj -> refcnt = 1;
|
|
obj -> type = dhcp_type_failover_link;
|
|
option_cache_reference (&obj -> peer_address, state -> address, MDL);
|
|
obj -> peer_port = ntohs (c -> remote_addr.sin_port);
|
|
|
|
status = omapi_object_reference (&obj -> outer,
|
|
(omapi_object_t *)c, MDL);
|
|
if (status != ISC_R_SUCCESS) {
|
|
lose:
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
omapi_disconnect ((omapi_object_t *)c, 1);
|
|
return status;
|
|
}
|
|
|
|
status = omapi_object_reference (&c -> inner,
|
|
(omapi_object_t *)obj, MDL);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto lose;
|
|
|
|
/* Notify the master state machine of the arrival of a new
|
|
connection. */
|
|
status = omapi_signal_in ((omapi_object_t *)state, "connect", obj);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto lose;
|
|
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
return status;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
|
|
omapi_object_t *id,
|
|
omapi_data_string_t *name,
|
|
omapi_typed_data_t *value)
|
|
{
|
|
if (h -> type != dhcp_type_failover_listener)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
if (h -> inner && h -> inner -> type -> set_value)
|
|
return (*(h -> inner -> type -> set_value))
|
|
(h -> inner, id, name, value);
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
|
|
omapi_object_t *id,
|
|
omapi_data_string_t *name,
|
|
omapi_value_t **value)
|
|
{
|
|
if (h -> type != dhcp_type_failover_listener)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
if (h -> inner && h -> inner -> type -> get_value)
|
|
return (*(h -> inner -> type -> get_value))
|
|
(h -> inner, id, name, value);
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
|
|
const char *file, int line)
|
|
{
|
|
if (h -> type != dhcp_type_failover_listener)
|
|
return ISC_R_INVALIDARG;
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
/* Write all the published values associated with the object through the
|
|
specified connection. */
|
|
|
|
isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
|
|
omapi_object_t *id,
|
|
omapi_object_t *p)
|
|
{
|
|
int i;
|
|
|
|
if (p -> type != dhcp_type_failover_listener)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
if (p -> inner && p -> inner -> type -> stuff_values)
|
|
return (*(p -> inner -> type -> stuff_values)) (c, id,
|
|
p -> inner);
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
/* Set up master state machine for the failover protocol. */
|
|
|
|
isc_result_t dhcp_failover_register (omapi_object_t *h)
|
|
{
|
|
isc_result_t status;
|
|
dhcp_failover_state_t *obj;
|
|
unsigned long port;
|
|
omapi_value_t *value = (omapi_value_t *)0;
|
|
|
|
status = omapi_get_value_str (h, (omapi_object_t *)0,
|
|
"local-port", &value);
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
if (!value -> value) {
|
|
omapi_value_dereference (&value, MDL);
|
|
return ISC_R_INVALIDARG;
|
|
}
|
|
|
|
status = omapi_get_int_value (&port, value -> value);
|
|
omapi_value_dereference (&value, MDL);
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
|
|
obj = (dhcp_failover_state_t *)dmalloc (sizeof *obj, MDL);
|
|
if (!obj)
|
|
return ISC_R_NOMEMORY;
|
|
memset (obj, 0, sizeof *obj);
|
|
obj -> refcnt = 1;
|
|
obj -> type = dhcp_type_failover_state;
|
|
obj -> listen_port = port;
|
|
|
|
status = omapi_listen ((omapi_object_t *)obj, port, 1);
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
|
|
status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
|
|
MDL);
|
|
if (status != ISC_R_SUCCESS) {
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
return status;
|
|
}
|
|
status = omapi_object_reference (&obj -> inner, h, MDL);
|
|
if (status != ISC_R_SUCCESS) {
|
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Signal handler for protocol state machine. */
|
|
|
|
isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
|
|
const char *name, va_list ap)
|
|
{
|
|
isc_result_t status;
|
|
omapi_connection_object_t *c;
|
|
omapi_protocol_object_t *obj;
|
|
dhcp_failover_state_t *state;
|
|
char *peer_name;
|
|
|
|
if (!o || o -> type != dhcp_type_failover_state)
|
|
return ISC_R_INVALIDARG;
|
|
state = (dhcp_failover_state_t *)o;
|
|
|
|
/* Not a signal we recognize? */
|
|
if (strcmp (name, "connect") &&
|
|
strcmp (name, "disconnect") &&
|
|
strcmp (name, "message")) {
|
|
if (state -> inner && state -> inner -> type -> signal_handler)
|
|
return (*(state -> inner -> type -> signal_handler))
|
|
(state -> inner, name, ap);
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
/* Handle all the events we care about... */
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
|
|
omapi_object_t *id,
|
|
omapi_data_string_t *name,
|
|
omapi_typed_data_t *value)
|
|
{
|
|
if (h -> type != dhcp_type_failover_state)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
if (h -> inner && h -> inner -> type -> set_value)
|
|
return (*(h -> inner -> type -> set_value))
|
|
(h -> inner, id, name, value);
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
|
|
omapi_object_t *id,
|
|
omapi_data_string_t *name,
|
|
omapi_value_t **value)
|
|
{
|
|
if (h -> type != dhcp_type_failover_state)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
if (h -> inner && h -> inner -> type -> get_value)
|
|
return (*(h -> inner -> type -> get_value))
|
|
(h -> inner, id, name, value);
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
|
|
const char *file, int line)
|
|
{
|
|
if (h -> type != dhcp_type_failover_state)
|
|
return ISC_R_INVALIDARG;
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
/* Write all the published values associated with the object through the
|
|
specified connection. */
|
|
|
|
isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
|
|
omapi_object_t *id,
|
|
omapi_object_t *p)
|
|
{
|
|
int i;
|
|
|
|
if (p -> type != dhcp_type_failover_state)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
if (p -> inner && p -> inner -> type -> stuff_values)
|
|
return (*(p -> inner -> type -> stuff_values)) (c, id,
|
|
p -> inner);
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
|
|
omapi_object_t *id,
|
|
omapi_object_t *ref)
|
|
{
|
|
omapi_value_t *tv = (omapi_value_t *)0;
|
|
isc_result_t status;
|
|
dhcp_failover_state_t *s;
|
|
|
|
/* First see if we were sent a handle. */
|
|
status = omapi_get_value_str (ref, id, "handle", &tv);
|
|
if (status == ISC_R_SUCCESS) {
|
|
status = omapi_handle_td_lookup (sp, tv -> value);
|
|
|
|
omapi_value_dereference (&tv, MDL);
|
|
if (status != ISC_R_SUCCESS)
|
|
return status;
|
|
|
|
/* Don't return the object if the type is wrong. */
|
|
if ((*sp) -> type != dhcp_type_failover_state) {
|
|
omapi_object_dereference (sp, MDL);
|
|
return ISC_R_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
/* Look the failover state up by peer name. */
|
|
status = omapi_get_value_str (ref, id, "peer_name", &tv);
|
|
if (status == ISC_R_SUCCESS) {
|
|
for (s = failover_states; s; s = s -> next) {
|
|
unsigned l = strlen (s -> name);
|
|
if (l == tv -> value -> u.buffer.len ||
|
|
!memcmp (s -> name,
|
|
tv -> value -> u.buffer.value, l))
|
|
break;
|
|
}
|
|
omapi_value_dereference (&tv, MDL);
|
|
|
|
/* If we already have a lease, and it's not the same one,
|
|
then the query was invalid. */
|
|
if (*sp && *sp != (omapi_object_t *)s) {
|
|
omapi_object_dereference (sp, MDL);
|
|
return ISC_R_KEYCONFLICT;
|
|
} else if (!s) {
|
|
if (*sp)
|
|
omapi_object_dereference (sp, MDL);
|
|
return ISC_R_NOTFOUND;
|
|
} else if (!*sp)
|
|
/* XXX fix so that hash lookup itself creates
|
|
XXX the reference. */
|
|
omapi_object_reference (sp, (omapi_object_t *)s, MDL);
|
|
}
|
|
|
|
/* If we get to here without finding a lease, no valid key was
|
|
specified. */
|
|
if (!*sp)
|
|
return ISC_R_NOKEYS;
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
|
|
omapi_object_t *id)
|
|
{
|
|
return ISC_R_NOTIMPLEMENTED;
|
|
}
|
|
|
|
isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
|
|
omapi_object_t *id)
|
|
{
|
|
return ISC_R_NOTIMPLEMENTED;
|
|
}
|
|
|
|
failover_option_t *dhcp_failover_make_option (unsigned code,
|
|
char *obuf, unsigned *obufix,
|
|
unsigned obufmax, ...)
|
|
{
|
|
va_list va;
|
|
struct failover_option_info *info;
|
|
int i;
|
|
unsigned size, count;
|
|
unsigned val;
|
|
struct iaddr *ival;
|
|
u_int8_t *bval;
|
|
char *fmt;
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
char tbuf [256];
|
|
#endif
|
|
/* Note that the failover_option structure is used differently on
|
|
input than on output - on input, count is an element count, and
|
|
on output it's the number of bytes total in the option, including
|
|
the option code and option length. */
|
|
failover_option_t option, *op;
|
|
|
|
|
|
/* Bogus option code? */
|
|
if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
|
|
return &null_failover_option;
|
|
}
|
|
info = &ft_options [code];
|
|
|
|
va_start (va, obufmax);
|
|
|
|
/* Get the number of elements and the size of the buffer we need
|
|
to allocate. */
|
|
if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
|
|
count = info -> type == FT_DDNS ? 1 : 2;
|
|
size = va_arg (va, int) + count;
|
|
} else {
|
|
/* Find out how many items in this list. */
|
|
if (info -> num_present)
|
|
count = info -> num_present;
|
|
else
|
|
count = va_arg (va, int);
|
|
|
|
/* Figure out size. */
|
|
switch (info -> type) {
|
|
case FT_UINT8:
|
|
case FT_BYTES:
|
|
case FT_DIGEST:
|
|
size = count;
|
|
break;
|
|
|
|
case FT_TEXT_OR_BYTES:
|
|
case FT_TEXT:
|
|
fmt = va_arg (va, char *);
|
|
#if defined (HAVE_SNPRINTF)
|
|
/* Presumably if we have snprintf, we also have
|
|
vsnprintf. */
|
|
vsnprintf (tbuf, sizeof tbuf, fmt, va);
|
|
#else
|
|
vsprintf (tbuf, fmt, va);
|
|
#endif
|
|
size = strlen (tbuf);
|
|
count = size;
|
|
break;
|
|
|
|
case FT_IPADDR:
|
|
case FT_UINT32:
|
|
size = count * 4;
|
|
break;
|
|
|
|
case FT_UINT16:
|
|
size = count * 2;
|
|
break;
|
|
|
|
default:
|
|
/* shouldn't get here. */
|
|
log_fatal ("bogus type in failover_make_option: %d",
|
|
info -> type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
size += 4;
|
|
|
|
/* Allocate a buffer for the option. */
|
|
option.count = size;
|
|
option.data = dmalloc (option.count, MDL);
|
|
if (!option.data)
|
|
return &null_failover_option;
|
|
|
|
/* Put in the option code and option length. */
|
|
putUShort (option.data, code);
|
|
putUShort (&option.data [4], size - 4);
|
|
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
sprintf (tbuf, " (%s<%d>", info -> name, option.count);
|
|
failover_print (obuf, obufix, obufmax, tbuf);
|
|
#endif
|
|
|
|
/* Now put in the data. */
|
|
switch (info -> type) {
|
|
case FT_UINT8:
|
|
for (i = 0; i < count; i++) {
|
|
val = va_arg (va, unsigned);
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
sprintf (tbuf, " %d", val);
|
|
failover_print (obuf, obufix, obufmax, tbuf);
|
|
#endif
|
|
option.data [i + 4] = val;
|
|
}
|
|
break;
|
|
|
|
case FT_IPADDR:
|
|
for (i = 0; i < count; i++) {
|
|
ival = va_arg (va, struct iaddr *);
|
|
if (ival -> len != 4) {
|
|
dfree (option.data, MDL);
|
|
log_error ("IP addrlen=%d, should be 4.",
|
|
ival -> len);
|
|
return &null_failover_option;
|
|
}
|
|
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
sprintf (tbuf, " %s", piaddr (*ival));
|
|
failover_print (obuf, obufix, obufmax, tbuf);
|
|
#endif
|
|
memcpy (&option.data [4 + i * 4], ival -> iabuf, 4);
|
|
}
|
|
break;
|
|
|
|
case FT_UINT32:
|
|
for (i = 0; i < count; i++) {
|
|
val = va_arg (va, unsigned);
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
sprintf (tbuf, " %d", val);
|
|
failover_print (obuf, obufix, obufmax, tbuf);
|
|
#endif
|
|
putULong (&option.data [4 + i * 4], val);
|
|
}
|
|
break;
|
|
|
|
case FT_BYTES:
|
|
case FT_DIGEST:
|
|
bval = va_arg (va, u_int8_t *);
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
for (i = 0; i < count; i++) {
|
|
sprintf (tbuf, " %d", bval [i]);
|
|
failover_print (obuf, obufix, obufmax, tbuf);
|
|
}
|
|
#endif
|
|
memcpy (&option.data [4], bval, count);
|
|
break;
|
|
|
|
/* On output, TEXT_OR_BYTES is _always_ text, and always NUL
|
|
terminated. Note that the caller should be careful not to
|
|
provide a format and data that amount to more than 256 bytes
|
|
of data, since it will be truncated on platforms that
|
|
support snprintf, and will mung the stack on those platforms
|
|
that do not support snprintf. Also, callers should not pass
|
|
data acquired from the network without specifically checking
|
|
it to make sure it won't bash the stack. */
|
|
case FT_TEXT_OR_BYTES:
|
|
case FT_TEXT:
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
failover_print (obuf, obufix, obufmax, " \"");
|
|
failover_print (obuf, obufix, obufmax, tbuf);
|
|
failover_print (obuf, obufix, obufmax, "\"");
|
|
#endif
|
|
memcpy (&option.data [4], tbuf, count);
|
|
break;
|
|
|
|
case FT_DDNS:
|
|
case FT_DDNS1:
|
|
option.data [4] = va_arg (va, unsigned);
|
|
if (count == 2)
|
|
option.data [5] = va_arg (va, unsigned);
|
|
bval = va_arg (va, u_int8_t *);
|
|
memcpy (&option.data [4 + count], bval, size - count - 4);
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
for (i = 4; i < size; i++) {
|
|
sprintf (tbuf, " %d", option.data [i]);
|
|
failover_print (obuf, obufix, obufmax, tbuf);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case FT_UINT16:
|
|
for (i = 0; i < count; i++) {
|
|
val = va_arg (va, u_int32_t);
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
sprintf (tbuf, " %d", val);
|
|
failover_print (obuf, obufix, obufmax, tbuf);
|
|
#endif
|
|
putUShort (&option.data [4 + i * 2], val);
|
|
}
|
|
break;
|
|
|
|
case FT_UNDEF:
|
|
default:
|
|
}
|
|
|
|
failover_print (obuf, obufix, obufmax, ")");
|
|
|
|
/* Now allocate a place to store what we just set up. */
|
|
op = dmalloc (sizeof (failover_option_t), MDL);
|
|
if (!op) {
|
|
dfree (option.data, MDL);
|
|
return &null_failover_option;
|
|
}
|
|
|
|
*op = option;
|
|
return op;
|
|
}
|
|
|
|
/* Send a failover message header. */
|
|
|
|
isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
|
|
omapi_object_t *connection,
|
|
int msg_type, ...)
|
|
{
|
|
unsigned count = 0;
|
|
unsigned size = 0;
|
|
int bad_option = 0;
|
|
int opix = 0;
|
|
va_list list;
|
|
failover_option_t *option;
|
|
unsigned char *opbuf;
|
|
isc_result_t status = ISC_R_SUCCESS;
|
|
unsigned char cbuf;
|
|
|
|
/* Run through the argument list once to compute the length of
|
|
the option portion of the message. */
|
|
va_start (list, msg_type);
|
|
while ((option = va_arg (list, failover_option_t *))) {
|
|
if (option != &skip_failover_option)
|
|
size += option -> count;
|
|
if (option == &null_failover_option)
|
|
bad_option = 1;
|
|
}
|
|
va_end (list);
|
|
|
|
/* Allocate an option buffer, unless we got an error. */
|
|
if (!bad_option) {
|
|
opbuf = dmalloc (size, MDL);
|
|
if (!opbuf)
|
|
status = ISC_R_NOMEMORY;
|
|
}
|
|
|
|
va_start (list, msg_type);
|
|
while ((option = va_arg (list, failover_option_t *))) {
|
|
if (option == &skip_failover_option)
|
|
continue;
|
|
if (!bad_option && opbuf)
|
|
memcpy (&opbuf [opix],
|
|
option -> data, option -> count);
|
|
opix += option -> count;
|
|
dfree (option -> data, MDL);
|
|
dfree (option, MDL);
|
|
}
|
|
|
|
if (bad_option || !opbuf)
|
|
return status;
|
|
|
|
/* Now send the message header. */
|
|
|
|
/* Message length. */
|
|
status = omapi_connection_put_uint16 (connection, size + 12);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto err;
|
|
|
|
/* Message type. */
|
|
cbuf = msg_type;
|
|
status = omapi_connection_copyin (connection, &cbuf, 1);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto err;
|
|
|
|
/* Payload offset. */
|
|
cbuf = 12;
|
|
status = omapi_connection_copyin (connection, &cbuf, 1);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto err;
|
|
|
|
/* Current time. */
|
|
status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto err;
|
|
|
|
/* Transaction ID. */
|
|
status = omapi_connection_put_uint32 (connection, link -> xid++);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto err;
|
|
|
|
|
|
/* Payload. */
|
|
status = omapi_connection_copyin (connection, opbuf, size);
|
|
if (status != ISC_R_SUCCESS)
|
|
goto err;
|
|
dfree (opbuf, MDL);
|
|
return status;
|
|
|
|
err:
|
|
dfree (opbuf, MDL);
|
|
omapi_disconnect (connection, 1);
|
|
return status;
|
|
}
|
|
|
|
/* Send a connect message. */
|
|
|
|
isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
|
|
{
|
|
dhcp_failover_link_t *link;
|
|
dhcp_failover_state_t *state;
|
|
isc_result_t status;
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
char obuf [64];
|
|
unsigned obufix = 0;
|
|
|
|
# define FMA obuf, &obufix, sizeof obuf
|
|
failover_print (FMA, "(connect");
|
|
#else
|
|
# define FMA (unsigned char *)0, (unsigned *)0, 0
|
|
#endif
|
|
|
|
|
|
if (!l || l -> type != dhcp_type_failover_link)
|
|
return ISC_R_INVALIDARG;
|
|
link = (dhcp_failover_link_t *)l;
|
|
state = link -> state_object;
|
|
if (!l -> outer || l -> outer -> type != omapi_type_connection)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
status = (dhcp_failover_put_message
|
|
(link, l -> outer,
|
|
FTM_CONNECT,
|
|
dhcp_failover_make_option (FTO_SERVER_ADDR, FMA,
|
|
&state -> server_addr),
|
|
dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
|
|
state -> max_flying_updates),
|
|
dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
|
|
state -> max_response_delay),
|
|
dhcp_failover_make_option (FTO_VENDOR_CLASS, FMA,
|
|
"isc-%s", DHCP_VERSION),
|
|
dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
|
|
DHCP_FAILOVER_VERSION),
|
|
dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
|
|
0, 0),
|
|
dhcp_failover_make_option (FTO_MCLT, FMA,
|
|
state -> mclt),
|
|
dhcp_failover_make_option (FTO_HBA, FMA,
|
|
state -> hba),
|
|
(failover_option_t *)0));
|
|
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
if (status != ISC_R_SUCCESS)
|
|
failover_print (FMA, " (failed)");
|
|
failover_print (FMA, ")");
|
|
if (obufix) {
|
|
log_debug ("%s", obuf);
|
|
}
|
|
#endif
|
|
return status;
|
|
}
|
|
|
|
/* Send a Bind Update message. */
|
|
|
|
isc_result_t dhcp_failover_update_peer (struct lease *lease, int releasep)
|
|
{
|
|
dhcp_failover_link_t *link;
|
|
dhcp_failover_state_t *state;
|
|
isc_result_t status;
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
char obuf [64];
|
|
unsigned obufix = 0;
|
|
|
|
# define FMA obuf, &obufix, sizeof obuf
|
|
failover_print (FMA, "(bndupd");
|
|
#else
|
|
# define FMA (unsigned char *)0, (unsigned *)0, 0
|
|
#endif
|
|
|
|
if (!lease -> pool ||
|
|
!lease -> pool -> failover_peer ||
|
|
!lease -> pool -> failover_peer -> outer)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
state = lease -> pool -> failover_peer;
|
|
if (state -> outer -> type != dhcp_type_failover_link)
|
|
return ISC_R_INVALIDARG;
|
|
link = (dhcp_failover_link_t *)state -> outer;
|
|
|
|
if (!link -> outer || link -> outer -> type != omapi_type_connection)
|
|
return ISC_R_INVALIDARG;
|
|
|
|
status = (dhcp_failover_put_message
|
|
(link, link -> outer,
|
|
FTM_BNDUPD,
|
|
dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
|
|
lease -> ip_addr.iabuf),
|
|
dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
|
|
0 /* ??? */),
|
|
lease -> uid_len
|
|
? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
|
|
lease -> uid,
|
|
lease -> uid_len)
|
|
: &skip_failover_option,
|
|
dhcp_failover_make_option (FTO_CHADDR, FMA,
|
|
lease -> hardware_addr.hlen + 1,
|
|
lease -> hardware_addr.hbuf),
|
|
dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
|
|
lease -> ends),
|
|
dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
|
|
lease -> tstp),
|
|
dhcp_failover_make_option (FTO_STOS, FMA,
|
|
lease -> starts),
|
|
dhcp_failover_make_option (FTO_CLTT, FMA,
|
|
lease -> cltt),
|
|
&skip_failover_option, /* XXX DDNS */
|
|
&skip_failover_option, /* XXX request options */
|
|
&skip_failover_option, /* XXX reply options */
|
|
(failover_option_t *)0));
|
|
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
if (status != ISC_R_SUCCESS)
|
|
failover_print (FMA, " (failed)");
|
|
failover_print (FMA, ")");
|
|
if (obufix) {
|
|
log_debug ("%s", obuf);
|
|
}
|
|
#endif
|
|
return status;
|
|
}
|
|
|
|
#if defined (DEBUG_FAILOVER_MESSAGES)
|
|
/* Print hunks of failover messages, doing line breaks as appropriate.
|
|
Note that this assumes syslog is being used, rather than, e.g., the
|
|
Windows NT logging facility, where just dumping the whole message in
|
|
one hunk would be more appropriate. */
|
|
|
|
void failover_print (char *obuf,
|
|
unsigned *obufix, unsigned obufmax, const char *s)
|
|
{
|
|
int len = strlen (s);
|
|
|
|
if (len + *obufix + 1 >= obufmax) {
|
|
if (!*obufix) {
|
|
log_debug ("%s", s);
|
|
return;
|
|
}
|
|
log_debug ("%s", obuf);
|
|
*obufix = 0;
|
|
} else {
|
|
strcpy (&obuf [*obufix], s);
|
|
obufix += len;
|
|
}
|
|
}
|
|
#endif /* defined (DEBUG_FAILOVER_MESSAGES) */
|
|
|
|
void update_partner (struct lease *lease)
|
|
{
|
|
}
|
|
#endif /* defined (FAILOVER_PROTOCOL) */
|