]> git.ipfire.org Git - thirdparty/dhcp.git/blob - server/failover.c
ddee88276b024c875cd379b43a525fe1f89a8d9f
[thirdparty/dhcp.git] / server / failover.c
1 /* failover.c
2
3 Failover protocol support code... */
4
5 /*
6 * Copyright (c) 1999-2000 Internet Software Consortium.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
38 * To learn more about the Internet Software Consortium, see
39 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
40 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
41 * ``http://www.nominum.com''.
42 */
43
44 #ifndef lint
45 static char copyright[] =
46 "$Id: failover.c,v 1.13 2000/05/04 18:58:15 mellon Exp $ Copyright (c) 1999-2000 The Internet Software Consortium. All rights reserved.\n";
47 #endif /* not lint */
48
49 #include "dhcpd.h"
50 #include "version.h"
51 #include <omapip/omapip_p.h>
52
53 #if defined (FAILOVER_PROTOCOL)
54 static struct hash_table *failover_hash;
55 static dhcp_failover_state_t *failover_states;
56 static isc_result_t do_a_failover_option (omapi_object_t *,
57 dhcp_failover_link_t *);
58
59 void dhcp_failover_startup ()
60 {
61 dhcp_failover_state_t *state;
62 isc_result_t status;
63
64 for (state = failover_states; state; state = state -> next) {
65 dhcp_failover_state_transition (state, "startup");
66 if (state -> i_am == secondary) {
67 status = (dhcp_failover_listen
68 ((omapi_object_t *)state));
69 if (status != ISC_R_SUCCESS) {
70 add_timeout (cur_time + 90,
71 dhcp_failover_listener_restart,
72 state);
73 }
74 } else {
75 status = (dhcp_failover_link_initiate
76 ((omapi_object_t *)state));
77 if (status != ISC_R_SUCCESS) {
78 add_timeout (cur_time + 90,
79 dhcp_failover_reconnect, state);
80 }
81 }
82 if (status != ISC_R_SUCCESS) {
83 log_error ("failover peer %s: %s", state -> name,
84 isc_result_totext (status));
85 }
86 }
87 }
88
89 void dhcp_failover_write_all_states ()
90 {
91 dhcp_failover_state_t *state;
92
93 for (state = failover_states; state; state = state -> next) {
94 write_failover_state (state);
95 }
96 }
97
98 isc_result_t enter_failover_peer (peer)
99 dhcp_failover_state_t *peer;
100 {
101 dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
102 isc_result_t status;
103
104 status = find_failover_peer (&dup, peer -> name);
105 if (status == ISC_R_NOTFOUND) {
106 if (failover_states) {
107 omapi_object_reference
108 ((omapi_object_t **)&peer -> next,
109 (omapi_object_t *)failover_states, MDL);
110 omapi_object_dereference
111 ((omapi_object_t **)&failover_states, MDL);
112 }
113 omapi_object_reference ((omapi_object_t **)&failover_states,
114 (omapi_object_t *)peer, MDL);
115 return ISC_R_SUCCESS;
116 }
117 if (status == ISC_R_SUCCESS)
118 return ISC_R_EXISTS;
119
120 return status;
121 }
122
123 isc_result_t find_failover_peer (peer, name)
124 dhcp_failover_state_t **peer;
125 const char *name;
126 {
127 dhcp_failover_state_t *p;
128
129 for (p = failover_states; p; p = p -> next)
130 if (!strcmp (name, p -> name))
131 break;
132 if (p)
133 return omapi_object_reference ((omapi_object_t **)peer,
134 (omapi_object_t *)p, MDL);
135 return ISC_R_NOTFOUND;
136 }
137
138 /* The failover protocol has three objects associated with it. For
139 each failover partner declaration in the dhcpd.conf file, primary
140 or secondary, there is a failover_state object. For any primary or
141 secondary state object that has a connection to its peer, there is
142 also a failover_link object, which has its own input state seperate
143 from the failover protocol state for managing the actual bytes
144 coming in off the wire. Finally, there will be one listener object
145 for every distinct port number associated with a secondary
146 failover_state object. Normally all secondary failover_state
147 objects are expected to listen on the same port number, so there
148 need be only one listener object, but if different port numbers are
149 specified for each failover object, there could be as many as one
150 listener object for each secondary failover_state object. */
151
152 /* This, then, is the implemention of the failover link object. */
153
154 isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
155 {
156 isc_result_t status;
157 dhcp_failover_link_t *obj;
158 omapi_value_t *value = (omapi_value_t *)0;
159 dhcp_failover_state_t *state;
160 omapi_object_t *o;
161 int i;
162 struct data_string ds;
163 omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
164 omapi_addr_t local_addr;
165
166 /* Find the failover state in the object chain. */
167 for (o = h; o -> outer; o = o -> outer)
168 ;
169 for (; o; o = o -> inner) {
170 if (o -> type == dhcp_type_failover_state)
171 break;
172 }
173 if (!o)
174 return ISC_R_INVALIDARG;
175 state = (dhcp_failover_state_t *)o;
176
177 obj = (dhcp_failover_link_t *)dmalloc (sizeof *obj, MDL);
178 if (!obj)
179 return ISC_R_NOMEMORY;
180 memset (obj, 0, sizeof *obj);
181 obj -> refcnt = 1;
182 obj -> type = dhcp_type_failover_link;
183 option_cache_reference (&obj -> peer_address, state -> address, MDL);
184 obj -> peer_port = state -> port;
185 omapi_object_reference ((omapi_object_t **)&obj -> state_object,
186 (omapi_object_t *)state, MDL);
187
188 memset (&ds, 0, sizeof ds);
189 if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
190 (struct option_state *)0,
191 (struct option_state *)0,
192 &global_scope, obj -> peer_address, MDL)) {
193 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
194 return ISC_R_UNEXPECTED;
195 }
196
197 /* Make an omapi address list out of a buffer containing zero or more
198 IPv4 addresses. */
199 status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
200 if (status != ISC_R_SUCCESS) {
201 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
202 return status;
203 }
204
205 for (i = 0; i < addrs -> count; i++) {
206 addrs -> addresses [i].addrtype = AF_INET;
207 addrs -> addresses [i].addrlen = sizeof (struct in_addr);
208 memcpy (addrs -> addresses [i].address,
209 &ds.data [i * 4], sizeof (struct in_addr));
210 addrs -> addresses [i].port = obj -> peer_port;
211 }
212 data_string_forget (&ds, MDL);
213
214 /* Now figure out the local address that we're supposed to use. */
215 if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
216 (struct option_state *)0,
217 (struct option_state *)0,
218 &global_scope, state -> server_addr,
219 MDL)) {
220 memset (&local_addr, 0, sizeof local_addr);
221 local_addr.addrtype = AF_INET;
222 local_addr.addrlen = sizeof (struct in_addr);
223 if (!state -> server_identifier.len) {
224 log_fatal ("failover peer %s: no identifier.",
225 state -> name);
226 }
227 } else {
228 if (ds.len != sizeof (struct in_addr)) {
229 data_string_forget (&ds, MDL);
230 omapi_object_dereference ((omapi_object_t **)&obj,
231 MDL);
232 omapi_addr_list_dereference (&addrs, MDL);
233 return ISC_R_INVALIDARG;
234 }
235 local_addr.addrtype = AF_INET;
236 local_addr.addrlen = ds.len;
237 memcpy (local_addr.address, ds.data, ds.len);
238 if (!state -> server_identifier.len)
239 data_string_copy (&state -> server_identifier,
240 &ds, MDL);
241 data_string_forget (&ds, MDL);
242 local_addr.port = 0; /* Let the O.S. choose. */
243 }
244
245 status = omapi_connect_list ((omapi_object_t *)obj,
246 addrs, &local_addr);
247 omapi_addr_list_dereference (&addrs, MDL);
248
249 /* If we didn't succeed in starting to connect, fail. */
250 if (status != ISC_R_SUCCESS) {
251 loselose:
252 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
253 return status;
254 }
255 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
256 return ISC_R_SUCCESS;
257 }
258
259 isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
260 const char *name, va_list ap)
261 {
262 isc_result_t status;
263 dhcp_failover_link_t *link;
264 omapi_object_t *c;
265 u_int16_t nlen;
266 u_int32_t vlen;
267 dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
268
269 if (h -> type != dhcp_type_failover_link) {
270 /* XXX shouldn't happen. Put an assert here? */
271 return ISC_R_UNEXPECTED;
272 }
273 link = (dhcp_failover_link_t *)h;
274
275 if (!strcmp (name, "connect")) {
276 /* If we're primary, send the connect message. */
277 status = dhcp_failover_send_connect (h);
278 if (status != ISC_R_SUCCESS)
279 omapi_disconnect (h -> outer, 1);
280 return status;
281 }
282
283 if (!strcmp (name, "disconnect")) {
284 if (link -> state_object &&
285 link -> state_object -> i_am == primary) {
286 add_timeout (cur_time + 90, dhcp_failover_reconnect,
287 link -> state_object);
288 /* XXX this is a reference! */
289 }
290 return ISC_R_SUCCESS;
291 }
292
293 /* Not a signal we recognize? */
294 if (strcmp (name, "ready")) {
295 if (h -> inner && h -> inner -> type -> signal_handler)
296 return (*(h -> inner -> type -> signal_handler))
297 (h -> inner, name, ap);
298 return ISC_R_NOTFOUND;
299 }
300
301 if (!h -> outer || h -> outer -> type != omapi_type_connection)
302 return ISC_R_INVALIDARG;
303 c = h -> outer;
304
305 /* We get here because we requested that we be woken up after
306 some number of bytes were read, and that number of bytes
307 has in fact been read. */
308 switch (link -> state) {
309 case dhcp_flink_start:
310 link -> state = dhcp_flink_message_length_wait;
311 if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
312 break;
313 case dhcp_flink_message_length_wait:
314 next_message:
315 link -> state = dhcp_flink_message_wait;
316 link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
317 if (!link -> imsg) {
318 dhcp_flink_fail:
319 if (link -> imsg) {
320 dfree (link -> imsg, MDL);
321 link -> imsg = (failover_message_t *)0;
322 }
323 link -> state = dhcp_flink_disconnected;
324 omapi_disconnect (c, 1);
325 /* XXX just blow away the protocol state now?
326 XXX or will disconnect blow it away? */
327 return ISC_R_UNEXPECTED;
328 }
329 memset (link -> imsg, 0, sizeof (failover_message_t));
330 /* Get the length: */
331 omapi_connection_get_uint16 (c, &link -> imsg_len);
332 link -> imsg_count = 0; /* Bytes read. */
333
334 /* Maximum of 2048 bytes in any failover message. */
335 if (link -> imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE)
336 goto dhcp_flink_fail;
337
338 if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
339 ISC_R_SUCCESS)
340 break;
341 case dhcp_flink_message_wait:
342 /* Read in the message. At this point we have the
343 entire message in the input buffer. For each
344 incoming value ID, set a bit in the bitmask
345 indicating that we've gotten it. Maybe flag an
346 error message if the bit is already set. Once
347 we're done reading, we can check the bitmask to
348 make sure that the required fields for each message
349 have been included. */
350
351 link -> imsg_count += 2; /* Count the length as read. */
352
353 /* Get message type. */
354 omapi_connection_copyout (&link -> imsg -> type, c, 1);
355 link -> imsg_count++;
356
357 /* Get message payload offset. */
358 omapi_connection_copyout (&link -> imsg_payoff, c, 1);
359 link -> imsg_count++;
360
361 /* Get message time. */
362 omapi_connection_get_uint32 (c, &link -> imsg -> time);
363 link -> imsg_count += 4;
364
365 /* Get transaction ID. */
366 omapi_connection_get_uint32 (c, &link -> imsg -> xid);
367 link -> imsg_count += 4;
368
369 /* Skip over any portions of the message header that we
370 don't understand. */
371 if (link -> imsg_payoff - link -> imsg_count) {
372 omapi_connection_copyout ((unsigned char *)0, c,
373 (link -> imsg_payoff -
374 link -> imsg_count));
375 link -> imsg_count = link -> imsg_payoff;
376 }
377
378 /* Now start sucking options off the wire. */
379 while (link -> imsg_count < link -> imsg_len) {
380 if (do_a_failover_option (c, link) != ISC_R_SUCCESS)
381 goto dhcp_flink_fail;
382 }
383
384 /* If it's a connect message, try to associate it with
385 a state object. */
386 /* XXX this should be authenticated! */
387 if (link -> imsg -> type == FTM_CONNECT) {
388 /* See if we can find a failover_state object that
389 matches this connection. This message should only
390 be received by a secondary from a primary. */
391 for (s = failover_states; s; s = s -> next) {
392 if (dhcp_failover_state_match
393 (s, (u_int8_t *)&link -> imsg -> server_addr,
394 sizeof link -> imsg -> server_addr))
395 state = s;
396 }
397
398 /* If we can't find a failover protocol state
399 for this remote host, drop the connection */
400 if (!state) {
401 /* XXX Send a refusal message first?
402 XXX Look in protocol spec for guidance. */
403 log_error ("Failover CONNECT from %s %d.%d.%d.%d",
404 "unknown server",
405 ((u_int8_t *)
406 (&link -> imsg -> server_addr)) [0],
407 ((u_int8_t *)
408 (&link -> imsg -> server_addr)) [1],
409 ((u_int8_t *)
410 (&link -> imsg -> server_addr)) [2],
411 ((u_int8_t *)
412 (&link -> imsg -> server_addr)) [3]);
413 dhcp_failover_send_connectack
414 ((omapi_object_t *)link,
415 FTR_INVALID_PARTNER);
416 omapi_disconnect (c, 0);
417 link -> state = dhcp_flink_disconnected;
418 return ISC_R_SUCCESS;
419 }
420
421 if (!link -> state_object)
422 omapi_object_reference
423 ((omapi_object_t **)&link -> state_object,
424 (omapi_object_t *)state, MDL);
425 option_cache_reference
426 (&link -> peer_address, state -> address, MDL);
427 }
428
429 /* If we don't have a state object at this point, it's
430 some kind of bogus situation, so just drop the
431 connection. */
432 if (!link -> state_object) {
433 omapi_disconnect (c, 1);
434 link -> state = dhcp_flink_disconnected;
435 return ISC_R_INVALIDARG;
436 }
437
438 /* Once we have the entire message, and we've validated
439 it as best we can here, pass it to the parent. */
440 omapi_signal ((omapi_object_t *)link -> state_object,
441 "message", link);
442 link -> state = dhcp_flink_message_length_wait;
443 dfree (link -> imsg, MDL);
444 link -> imsg = (failover_message_t *)0;
445 /* XXX This is dangerous because we could get into a tight
446 XXX loop reading input without servicing any other stuff.
447 XXX There needs to be a way to relinquish control but
448 XXX get it back immediately if there's no other work to
449 XXX do. */
450 if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
451 goto next_message;
452 break;
453
454 default:
455 /* XXX should never get here. Assertion? */
456 break;
457 }
458 return ISC_R_SUCCESS;
459 }
460
461 static isc_result_t do_a_failover_option (c, link)
462 omapi_object_t *c;
463 dhcp_failover_link_t *link;
464 {
465 u_int16_t option_code;
466 u_int16_t option_len;
467 unsigned char *op;
468 unsigned op_size;
469 unsigned op_count;
470 int i;
471 isc_result_t status;
472
473 if (link -> imsg_count + 2 > link -> imsg_len) {
474 log_error ("FAILOVER: message overflow at option code.");
475 return ISC_R_PROTOCOLERROR;
476 }
477
478 /* Get option code. */
479 omapi_connection_get_uint16 (c, &option_code);
480 link -> imsg_count += 2;
481
482 if (link -> imsg_count + 2 > link -> imsg_len) {
483 log_error ("FAILOVER: message overflow at length.");
484 return ISC_R_PROTOCOLERROR;
485 }
486
487 /* Get option length. */
488 omapi_connection_get_uint16 (c, &option_len);
489 link -> imsg_count += 2;
490
491 if (link -> imsg_count + option_len > link -> imsg_len) {
492 log_error ("FAILOVER: message overflow at data.");
493 return ISC_R_PROTOCOLERROR;
494 }
495
496 /* If it's an unknown code, skip over it. */
497 if (option_code > FTO_MAX) {
498 #if defined (FAILOVER_PROTOCOL_DEBUG) && defined (FAILOVER_DEBUG_VERBOSE)
499 log_debug (" option code %d len %d (not recognized)",
500 option_code, option_len);
501 #endif
502 omapi_connection_copyout ((unsigned char *)0, c, option_len);
503 link -> imsg_count += option_len;
504 return ISC_R_SUCCESS;
505 }
506
507 /* If it's the digest, do it now. */
508 if (ft_options [option_code].type == FT_DIGEST) {
509 link -> imsg_count += option_len;
510 if (link -> imsg_count != link -> imsg_len) {
511 log_error ("FAILOVER: digest not at end of message");
512 return ISC_R_PROTOCOLERROR;
513 }
514 #if defined (FAILOVER_PROTOCOL_DEBUG) && defined (FAILOVER_DEBUG_VERBOSE)
515 log_debug (" option %s len %d",
516 ft_options [option_code].name, option_len);
517 #endif
518 /* For now, just dump it. */
519 omapi_connection_copyout ((unsigned char *)0, c, option_len);
520 return ISC_R_SUCCESS;
521 }
522
523 /* Only accept an option once. */
524 if (link -> imsg -> options_present & ft_options [option_code].bit) {
525 log_error ("FAILOVER: duplicate option %s",
526 ft_options [option_code].name);
527 return ISC_R_PROTOCOLERROR;
528 }
529
530 /* Make sure the option is appropriate for this type of message.
531 Really, any option is generally allowed for any message, and the
532 cases where this is not true are too complicated to represent in
533 this way - what this code is doing is to just avoid saving the
534 value of an option we don't have any way to use, which allows
535 us to make the failover_message structure smaller. */
536 if (ft_options [option_code].bit &&
537 !(fto_allowed [link -> imsg -> type] &
538 ft_options [option_code].bit)) {
539 omapi_connection_copyout ((unsigned char *)0, c, option_len);
540 link -> imsg_count += option_len;
541 return ISC_R_SUCCESS;
542 }
543
544 /* Figure out how many elements, how big they are, and where
545 to store them. */
546 if (ft_options [option_code].num_present) {
547 /* If this option takes a fixed number of elements,
548 we expect the space for them to be preallocated,
549 and we can just read the data in. */
550
551 op = ((unsigned char *)link -> imsg) +
552 ft_options [option_code].offset;
553 op_size = ft_sizes [ft_options [option_code].type];
554 op_count = ft_options [option_code].num_present;
555
556 if (option_len != op_size * op_count) {
557 log_error ("FAILOVER: option size (%d:%d), option %s",
558 option_len,
559 (ft_sizes [ft_options [option_code].type] *
560 ft_options [option_code].num_present),
561 ft_options [option_code].name);
562 return ISC_R_PROTOCOLERROR;
563 }
564 } else {
565 failover_option_t *fo;
566
567 /* FT_DDNS* are special - one or two bytes of status
568 followed by the client FQDN. */
569 if (ft_options [option_code].type == FT_DDNS1 ||
570 ft_options [option_code].type == FT_DDNS1) {
571 ddns_fqdn_t *ddns =
572 ((ddns_fqdn_t *)
573 (((char *)&link -> imsg) +
574 ft_options [option_code].offset));
575
576 op_count = (ft_options [option_code].type == FT_DDNS1
577 ? 1 : 2);
578
579 omapi_connection_copyout (&ddns -> codes [0],
580 c, op_count);
581 link -> imsg_count += op_count;
582 if (op_count == 1)
583 ddns -> codes [1] = 0;
584 op_size = 1;
585 op_count = option_len - op_count;
586
587 ddns -> length = op_count;
588 ddns -> data = dmalloc (op_count, MDL);
589 if (!ddns -> data) {
590 log_error ("FAILOVER: no memory getting%s(%d)",
591 " DNS data ", op_count);
592
593 /* Actually, NO_MEMORY, but if we lose here
594 we have to drop the connection. */
595 return ISC_R_PROTOCOLERROR;
596 }
597 omapi_connection_copyout (ddns -> data, c, op_count);
598 goto out;
599 }
600
601 /* A zero for num_present means that any number of
602 elements can appear, so we have to figure out how
603 many we got from the length of the option, and then
604 fill out a failover_option structure describing the
605 data. */
606 op_size = ft_sizes [ft_options [option_code].type];
607
608 /* Make sure that option data length is a multiple of the
609 size of the data type being sent. */
610 if (op_size > 1 && option_len % op_size) {
611 log_error ("FAILOVER: option_len %d not %s%d",
612 option_len, "multiple of ", op_size);
613 return ISC_R_PROTOCOLERROR;
614 }
615
616 op_count = option_len / op_size;
617
618 fo = ((failover_option_t *)
619 (((char *)&link -> imsg) +
620 ft_options [option_code].offset));
621
622 fo -> count = op_count;
623 fo -> data = dmalloc (option_len, MDL);
624 if (!fo -> data) {
625 log_error ("FAILOVER: no memory getting %s (%d)",
626 "option data", op_count);
627
628 return ISC_R_PROTOCOLERROR;
629 }
630 op = fo -> data;
631 }
632
633 /* For single-byte message values and multi-byte values that
634 don't need swapping, just read them in all at once. */
635 if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
636 omapi_connection_copyout ((unsigned char *)op, c, option_len);
637 link -> imsg_count += option_len;
638 goto out;
639 }
640
641 /* For values that require swapping, read them in one at a time
642 using routines that swap bytes. */
643 for (i = 0; i < op_count; i++) {
644 switch (ft_options [option_code].type) {
645 case FT_UINT32:
646 omapi_connection_get_uint32 (c, (u_int32_t *)op);
647 op += 4;
648 link -> imsg_count += 4;
649 break;
650
651 case FT_UINT16:
652 omapi_connection_get_uint16 (c, (u_int16_t *)op);
653 op += 2;
654 link -> imsg_count += 2;
655 break;
656
657 default:
658 /* Everything else should have been handled
659 already. */
660 log_error ("FAILOVER: option %s: bad type %d",
661 ft_options [option_code].name,
662 ft_options [option_code].type);
663 return ISC_R_PROTOCOLERROR;
664 }
665 }
666 out:
667 /* Remember that we got this option. */
668 link -> imsg -> options_present |= ft_options [option_code].bit;
669 return ISC_R_SUCCESS;
670 }
671
672 isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
673 omapi_object_t *id,
674 omapi_data_string_t *name,
675 omapi_typed_data_t *value)
676 {
677 if (h -> type != omapi_type_protocol)
678 return ISC_R_INVALIDARG;
679
680 /* Never valid to set these. */
681 if (!omapi_ds_strcmp (name, "link-port") ||
682 !omapi_ds_strcmp (name, "link-name") ||
683 !omapi_ds_strcmp (name, "link-state"))
684 return ISC_R_NOPERM;
685
686 if (h -> inner && h -> inner -> type -> set_value)
687 return (*(h -> inner -> type -> set_value))
688 (h -> inner, id, name, value);
689 return ISC_R_NOTFOUND;
690 }
691
692 isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
693 omapi_object_t *id,
694 omapi_data_string_t *name,
695 omapi_value_t **value)
696 {
697 dhcp_failover_link_t *link;
698
699 if (h -> type != omapi_type_protocol)
700 return ISC_R_INVALIDARG;
701 link = (dhcp_failover_link_t *)h;
702
703 if (!omapi_ds_strcmp (name, "link-port")) {
704 return omapi_make_int_value (value, name,
705 (int)link -> peer_port, MDL);
706 } else if (!omapi_ds_strcmp (name, "link-state")) {
707 if (link -> state < 0 ||
708 link -> state >= dhcp_flink_state_max)
709 return omapi_make_string_value (value, name,
710 "invalid link state",
711 MDL);
712 return omapi_make_string_value
713 (value, name,
714 dhcp_flink_state_names [link -> state], MDL);
715 }
716
717 if (h -> inner && h -> inner -> type -> get_value)
718 return (*(h -> inner -> type -> get_value))
719 (h -> inner, id, name, value);
720 return ISC_R_NOTFOUND;
721 }
722
723 isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
724 const char *file, int line)
725 {
726 dhcp_failover_link_t *link;
727 if (h -> type != dhcp_type_failover_link)
728 return ISC_R_INVALIDARG;
729 link = (dhcp_failover_link_t *)h;
730 if (link -> imsg) {
731 dfree (link -> imsg, file, line);
732 link -> imsg = (failover_message_t *)0;
733 }
734 if (link -> state_object)
735 omapi_object_dereference
736 ((omapi_object_t **)&link -> state_object, MDL);
737 return ISC_R_SUCCESS;
738 }
739
740 /* Write all the published values associated with the object through the
741 specified connection. */
742
743 isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
744 omapi_object_t *id,
745 omapi_object_t *l)
746 {
747 dhcp_failover_link_t *link;
748 isc_result_t status;
749
750 if (l -> type != dhcp_type_failover_link)
751 return ISC_R_INVALIDARG;
752 link = (dhcp_failover_link_t *)l;
753
754 status = omapi_connection_put_name (c, "link-port");
755 if (status != ISC_R_SUCCESS)
756 return status;
757 status = omapi_connection_put_uint32 (c, sizeof (int));
758 if (status != ISC_R_SUCCESS)
759 return status;
760 status = omapi_connection_put_uint32 (c, link -> peer_port);
761 if (status != ISC_R_SUCCESS)
762 return status;
763
764 status = omapi_connection_put_name (c, "link-state");
765 if (status != ISC_R_SUCCESS)
766 return status;
767 if (link -> state < 0 ||
768 link -> state >= dhcp_flink_state_max)
769 status = omapi_connection_put_string (c, "invalid link state");
770 else
771 status = (omapi_connection_put_string
772 (c, dhcp_flink_state_names [link -> state]));
773 if (status != ISC_R_SUCCESS)
774 return status;
775
776 if (link -> inner && link -> inner -> type -> stuff_values)
777 return (*(link -> inner -> type -> stuff_values)) (c, id,
778 link -> inner);
779 return ISC_R_SUCCESS;
780 }
781
782 /* Set up a listener for the omapi protocol. The handle stored points to
783 a listener object, not a protocol object. */
784
785 isc_result_t dhcp_failover_listen (omapi_object_t *h)
786 {
787 isc_result_t status;
788 dhcp_failover_listener_t *obj;
789 omapi_value_t *value = (omapi_value_t *)0;
790 omapi_addr_t local_addr;
791 unsigned long port;
792
793 status = omapi_get_value_str (h, (omapi_object_t *)0,
794 "local-port", &value);
795 if (status != ISC_R_SUCCESS)
796 return status;
797 if (!value -> value) {
798 omapi_value_dereference (&value, MDL);
799 return ISC_R_INVALIDARG;
800 }
801
802 status = omapi_get_int_value (&port, value -> value);
803 omapi_value_dereference (&value, MDL);
804 if (status != ISC_R_SUCCESS)
805 return status;
806 local_addr.port = port;
807
808 status = omapi_get_value_str (h, (omapi_object_t *)0,
809 "local-address", &value);
810 if (status != ISC_R_SUCCESS)
811 return status;
812 if (!value -> value) {
813 nogood:
814 omapi_value_dereference (&value, MDL);
815 return ISC_R_INVALIDARG;
816 }
817
818 if (value -> value -> type != omapi_datatype_data ||
819 value -> value -> u.buffer.len != sizeof (struct in_addr))
820 goto nogood;
821
822 memcpy (local_addr.address, value -> value -> u.buffer.value,
823 value -> value -> u.buffer.len);
824 local_addr.addrlen = value -> value -> u.buffer.len;
825 local_addr.addrtype = AF_INET;
826
827 omapi_value_dereference (&value, MDL);
828
829 obj = (dhcp_failover_listener_t *)dmalloc (sizeof *obj, MDL);
830 if (!obj)
831 return ISC_R_NOMEMORY;
832 memset (obj, 0, sizeof *obj);
833 obj -> refcnt = 1;
834 obj -> type = dhcp_type_failover_listener;
835 obj -> local_port = local_addr.port;
836
837 status = omapi_listen_addr ((omapi_object_t *)obj, &local_addr, 1);
838 if (status != ISC_R_SUCCESS)
839 return status;
840
841 status = omapi_object_reference (&h -> outer,
842 (omapi_object_t *)obj, MDL);
843 if (status != ISC_R_SUCCESS) {
844 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
845 return status;
846 }
847 status = omapi_object_reference (&obj -> inner, h, MDL);
848 if (status != ISC_R_SUCCESS) {
849 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
850 return status;
851 }
852
853 status = omapi_object_dereference ((omapi_object_t **)&obj, MDL);
854 return status;
855 }
856
857 /* Signal handler for protocol listener - if we get a connect signal,
858 create a new protocol connection, otherwise pass the signal down. */
859
860 isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
861 const char *name, va_list ap)
862 {
863 isc_result_t status;
864 omapi_connection_object_t *c;
865 dhcp_failover_link_t *obj;
866 dhcp_failover_listener_t *p;
867 dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
868
869 if (!o || o -> type != dhcp_type_failover_listener)
870 return ISC_R_INVALIDARG;
871 p = (dhcp_failover_listener_t *)o;
872
873 /* Not a signal we recognize? */
874 if (strcmp (name, "connect")) {
875 if (p -> inner && p -> inner -> type -> signal_handler)
876 return (*(p -> inner -> type -> signal_handler))
877 (p -> inner, name, ap);
878 return ISC_R_NOTFOUND;
879 }
880
881 c = va_arg (ap, omapi_connection_object_t *);
882 if (!c || c -> type != omapi_type_connection)
883 return ISC_R_INVALIDARG;
884
885 obj = (dhcp_failover_link_t *)dmalloc (sizeof *obj, MDL);
886 if (!obj)
887 return ISC_R_NOMEMORY;
888 memset (obj, 0, sizeof *obj);
889 obj -> refcnt = 1;
890 obj -> type = dhcp_type_failover_link;
891 obj -> peer_port = ntohs (c -> remote_addr.sin_port);
892
893 status = omapi_object_reference (&obj -> outer,
894 (omapi_object_t *)c, MDL);
895 if (status != ISC_R_SUCCESS) {
896 lose:
897 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
898 omapi_disconnect ((omapi_object_t *)c, 1);
899 return status;
900 }
901
902 status = omapi_object_reference (&c -> inner,
903 (omapi_object_t *)obj, MDL);
904 if (status != ISC_R_SUCCESS)
905 goto lose;
906
907 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
908 return status;
909 }
910
911 isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
912 omapi_object_t *id,
913 omapi_data_string_t *name,
914 omapi_typed_data_t *value)
915 {
916 if (h -> type != dhcp_type_failover_listener)
917 return ISC_R_INVALIDARG;
918
919 if (h -> inner && h -> inner -> type -> set_value)
920 return (*(h -> inner -> type -> set_value))
921 (h -> inner, id, name, value);
922 return ISC_R_NOTFOUND;
923 }
924
925 isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
926 omapi_object_t *id,
927 omapi_data_string_t *name,
928 omapi_value_t **value)
929 {
930 if (h -> type != dhcp_type_failover_listener)
931 return ISC_R_INVALIDARG;
932
933 if (h -> inner && h -> inner -> type -> get_value)
934 return (*(h -> inner -> type -> get_value))
935 (h -> inner, id, name, value);
936 return ISC_R_NOTFOUND;
937 }
938
939 isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
940 const char *file, int line)
941 {
942 if (h -> type != dhcp_type_failover_listener)
943 return ISC_R_INVALIDARG;
944 return ISC_R_SUCCESS;
945 }
946
947 /* Write all the published values associated with the object through the
948 specified connection. */
949
950 isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
951 omapi_object_t *id,
952 omapi_object_t *p)
953 {
954 int i;
955
956 if (p -> type != dhcp_type_failover_listener)
957 return ISC_R_INVALIDARG;
958
959 if (p -> inner && p -> inner -> type -> stuff_values)
960 return (*(p -> inner -> type -> stuff_values)) (c, id,
961 p -> inner);
962 return ISC_R_SUCCESS;
963 }
964
965 /* Set up master state machine for the failover protocol. */
966
967 isc_result_t dhcp_failover_register (omapi_object_t *h)
968 {
969 isc_result_t status;
970 dhcp_failover_state_t *obj;
971 unsigned long port;
972 omapi_value_t *value = (omapi_value_t *)0;
973
974 status = omapi_get_value_str (h, (omapi_object_t *)0,
975 "local-port", &value);
976 if (status != ISC_R_SUCCESS)
977 return status;
978 if (!value -> value) {
979 omapi_value_dereference (&value, MDL);
980 return ISC_R_INVALIDARG;
981 }
982
983 status = omapi_get_int_value (&port, value -> value);
984 omapi_value_dereference (&value, MDL);
985 if (status != ISC_R_SUCCESS)
986 return status;
987
988 obj = (dhcp_failover_state_t *)dmalloc (sizeof *obj, MDL);
989 if (!obj)
990 return ISC_R_NOMEMORY;
991 memset (obj, 0, sizeof *obj);
992 obj -> refcnt = 1;
993 obj -> type = dhcp_type_failover_state;
994 obj -> listen_port = port;
995
996 status = omapi_listen ((omapi_object_t *)obj, port, 1);
997 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
998 if (status != ISC_R_SUCCESS)
999 return status;
1000
1001 status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
1002 MDL);
1003 if (status != ISC_R_SUCCESS) {
1004 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
1005 return status;
1006 }
1007 status = omapi_object_reference (&obj -> inner, h, MDL);
1008 if (status != ISC_R_SUCCESS) {
1009 omapi_object_dereference ((omapi_object_t **)&obj, MDL);
1010 return status;
1011 }
1012
1013 return status;
1014 }
1015
1016 /* Signal handler for protocol state machine. */
1017
1018 isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
1019 const char *name, va_list ap)
1020 {
1021 isc_result_t status;
1022 omapi_connection_object_t *c;
1023 omapi_protocol_object_t *obj;
1024 dhcp_failover_state_t *state;
1025 dhcp_failover_link_t *link;
1026 char *peer_name;
1027
1028 if (!o || o -> type != dhcp_type_failover_state)
1029 return ISC_R_INVALIDARG;
1030 state = (dhcp_failover_state_t *)o;
1031
1032 /* Not a signal we recognize? */
1033 if (strcmp (name, "disconnect") &&
1034 strcmp (name, "message")) {
1035 if (state -> inner && state -> inner -> type -> signal_handler)
1036 return (*(state -> inner -> type -> signal_handler))
1037 (state -> inner, name, ap);
1038 return ISC_R_NOTFOUND;
1039 }
1040
1041 /* Handle connect signals by seeing what state we're in
1042 and potentially doing a state transition. */
1043 if (!strcmp (name, "disconnect")) {
1044 link = va_arg (ap, dhcp_failover_link_t *);
1045
1046 omapi_object_dereference (&state -> link_to_peer, MDL);
1047 dhcp_failover_state_transition (state, "disconnect");
1048 if (state -> i_am == primary)
1049 add_timeout (cur_time + 90, dhcp_failover_reconnect,
1050 state); /* XXX this is a reference! */
1051 } else if (!strcmp (name, "message")) {
1052 link = va_arg (ap, dhcp_failover_link_t *);
1053
1054 if (link -> imsg -> type == FTM_CONNECT) {
1055 /* If we already have a link to the peer, it must be
1056 dead, so drop it.
1057 XXX Is this the right thing to do? */
1058 if (state -> link_to_peer) {
1059 omapi_disconnect ((omapi_object_t *)
1060 state -> link_to_peer, 1);
1061 omapi_object_dereference
1062 (&state -> link_to_peer, MDL);
1063 dhcp_failover_state_transition (state,
1064 "disconnect");
1065 }
1066 omapi_object_reference (&state -> link_to_peer,
1067 (omapi_object_t *)link, MDL);
1068 status = (dhcp_failover_send_connectack
1069 ((omapi_object_t *)link, 0));
1070 if (status != ISC_R_SUCCESS) {
1071 omapi_object_dereference
1072 (&state -> link_to_peer, MDL);
1073 omapi_disconnect (link -> outer, 1);
1074 return ISC_R_SUCCESS;
1075 }
1076 dhcp_failover_state_transition (state, "connect");
1077 } else if (link -> imsg -> type == FTM_CONNECTACK) {
1078 if (link -> imsg -> reject_reason) {
1079 log_error ("Failover CONNECTACK from %d.%d.%d.%d%s%s",
1080 ((u_int8_t *)
1081 (&link -> imsg -> server_addr)) [0],
1082 ((u_int8_t *)
1083 (&link -> imsg -> server_addr)) [1],
1084 ((u_int8_t *)
1085 (&link -> imsg -> server_addr)) [2],
1086 ((u_int8_t *)
1087 (&link -> imsg -> server_addr)) [3],
1088 " rejected: ",
1089 (dhcp_failover_reject_reason_print
1090 (link -> imsg -> reject_reason)));
1091 omapi_disconnect (link -> outer, 1);
1092 return ISC_R_SUCCESS;
1093 }
1094
1095 if (!dhcp_failover_state_match
1096 (state,
1097 (u_int8_t *)&link -> imsg -> server_addr,
1098 sizeof link -> imsg -> server_addr)) {
1099 log_error ("Failover CONNECTACK from %s %d.%d.%d.%d",
1100 "unknown server",
1101 ((u_int8_t *)
1102 (&link -> imsg -> server_addr)) [0],
1103 ((u_int8_t *)
1104 (&link -> imsg -> server_addr)) [1],
1105 ((u_int8_t *)
1106 (&link -> imsg -> server_addr)) [2],
1107 ((u_int8_t *)
1108 (&link -> imsg -> server_addr)) [3]);
1109 dhcp_failover_send_disconnect ((omapi_object_t *)link,
1110 FTR_INVALID_PARTNER, 0);
1111 omapi_disconnect (link -> outer, 0);
1112 }
1113
1114 if (state -> link_to_peer) {
1115 log_error ("Failover CONNECTACK %s %d.%d.%d.%d",
1116 "while already connected",
1117 ((u_int8_t *)
1118 (&link -> imsg -> server_addr)) [0],
1119 ((u_int8_t *)
1120 (&link -> imsg -> server_addr)) [1],
1121 ((u_int8_t *)
1122 (&link -> imsg -> server_addr)) [2],
1123 ((u_int8_t *)
1124 (&link -> imsg -> server_addr)) [3]);
1125 dhcp_failover_send_disconnect ((omapi_object_t *)link,
1126 FTR_DUP_CONNECTION, 0);
1127 omapi_disconnect (link -> outer, 0);
1128 }
1129 omapi_object_reference (&state -> link_to_peer,
1130 (omapi_object_t *)link, MDL);
1131 dhcp_failover_state_transition (state, "connect");
1132 } else if (link -> imsg -> type == FTM_DISCONNECT) {
1133 if (link -> imsg -> reject_reason) {
1134 log_error ("Failover DISCONNECT from %d.%d.%d.%d%s%s",
1135 ((u_int8_t *)
1136 (&link -> imsg -> server_addr)) [0],
1137 ((u_int8_t *)
1138 (&link -> imsg -> server_addr)) [1],
1139 ((u_int8_t *)
1140 (&link -> imsg -> server_addr)) [2],
1141 ((u_int8_t *)
1142 (&link -> imsg -> server_addr)) [3],
1143 ": ",
1144 (dhcp_failover_reject_reason_print
1145 (link -> imsg -> reject_reason)));
1146 }
1147 omapi_disconnect (link -> outer, 1);
1148 return ISC_R_SUCCESS;
1149 } else if (link -> imsg -> type == FTM_BNDUPD) {
1150 dhcp_failover_process_bind_update (state,
1151 link -> imsg);
1152 } else if (link -> imsg -> type == FTM_BNDACK) {
1153 dhcp_failover_process_bind_ack (state, link -> imsg);
1154 } else if (link -> imsg -> type == FTM_POOLREQ) {
1155 dhcp_failover_pool_rebalance (state);
1156 } else if (link -> imsg -> type == FTM_POOLRESP) {
1157 log_info ("pool response: %d leases",
1158 link -> imsg -> addresses_transferred);
1159 }
1160 }
1161
1162 /* Handle all the events we care about... */
1163 return ISC_R_SUCCESS;
1164 }
1165
1166 isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
1167 const char *name)
1168 {
1169 /* XXX Check these state transitions against the spec! */
1170 if (!strcmp (name, "disconnect")) {
1171 switch (state -> my_state) {
1172 case potential_conflict_nic:
1173 case partner_down:
1174 case communications_interrupted:
1175 /* Already in the right state? */
1176 return ISC_R_SUCCESS;
1177
1178 case potential_conflict:
1179 return dhcp_failover_set_state
1180 (state, potential_conflict_nic);
1181
1182 case recover:
1183 /* XXX I don't think it makes sense to make a
1184 XXX transition from recover to communications-
1185 XXX interrupted, because then when the connect
1186 XXX occurred, we'd make a transition into
1187 XXX normal, not recover. */
1188 case normal:
1189 return dhcp_failover_set_state
1190 (state, communications_interrupted);
1191
1192 /* XXX ??? */
1193 case unknown_state:
1194 default:
1195 return dhcp_failover_set_state
1196 (state, potential_conflict_nic);
1197
1198 }
1199 } else if (!strcmp (name, "connect")) {
1200 switch (state -> my_state) {
1201 case communications_interrupted:
1202 return dhcp_failover_set_state (state, normal);
1203
1204 case partner_down:
1205 return dhcp_failover_set_state (state, recover);
1206
1207 case potential_conflict_nic:
1208 return dhcp_failover_set_state (state,
1209 potential_conflict);
1210
1211 /* We should never be in these states when we make
1212 a "connect" transition. */
1213 case recover:
1214 case potential_conflict:
1215 case normal:
1216 return ISC_R_INVALIDARG;
1217
1218 /* We should never be in the unknown_state or an
1219 unknown state either. */
1220 case unknown_state:
1221 default:
1222 return dhcp_failover_set_state (state,
1223 potential_conflict);
1224 }
1225 } else if (!strcmp (name, "startup")) {
1226 switch (state -> my_state) {
1227 case communications_interrupted:
1228 case partner_down:
1229 case potential_conflict_nic:
1230 return ISC_R_SUCCESS;
1231
1232 case normal:
1233 case recover:
1234 return dhcp_failover_set_state
1235 (state, communications_interrupted);
1236
1237 case potential_conflict:
1238 return dhcp_failover_set_state
1239 (state, potential_conflict_nic);
1240
1241 case unknown_state:
1242 return dhcp_failover_set_state
1243 (state, communications_interrupted);
1244
1245 default:
1246 return dhcp_failover_set_state
1247 (state, potential_conflict_nic);
1248 }
1249 }
1250 return ISC_R_INVALIDARG;
1251 }
1252
1253 isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
1254 enum failover_state new_state)
1255 {
1256 enum failover_state saved_state;
1257 TIME saved_stos;
1258
1259 /* First make the transition out of the current state. */
1260 switch (state -> my_state) {
1261 case normal:
1262 case recover:
1263 case potential_conflict:
1264 /* Any updates that haven't been acked yet, we have to
1265 resend, just in case. */
1266 if (state -> ack_queue_tail) {
1267 struct lease *lp;
1268
1269 /* Zap the flags. */
1270 for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
1271 lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
1272 ON_UPDATE_QUEUE);
1273
1274 /* Now hook the ack queue to the beginning of the update
1275 queue. */
1276 if (state -> update_queue_head) {
1277 omapi_object_reference
1278 ((omapi_object_t **)
1279 &state -> ack_queue_tail -> next_pending,
1280 (omapi_object_t *)state -> update_queue_head,
1281 MDL);
1282 omapi_object_dereference ((omapi_object_t **)
1283 &state -> update_queue_head,
1284 MDL);
1285 }
1286 omapi_object_reference ((omapi_object_t **)
1287 &state -> update_queue_head,
1288 (omapi_object_t *)state -> ack_queue_head,
1289 MDL);
1290 if (!state -> update_queue_tail)
1291 omapi_object_reference
1292 ((omapi_object_t **)
1293 &state -> update_queue_tail,
1294 (omapi_object_t *)state -> ack_queue_tail, MDL);
1295 omapi_object_dereference ((omapi_object_t **)
1296 &state -> ack_queue_tail, MDL);
1297 omapi_object_dereference ((omapi_object_t **)
1298 &state -> ack_queue_head, MDL);
1299 state -> cur_unacked_updates = 0;
1300 }
1301 cancel_timeout (dhcp_failover_keepalive, state);
1302 break;
1303
1304 case partner_down:
1305 case communications_interrupted:
1306 case potential_conflict_nic:
1307 default:
1308 break;
1309 }
1310
1311 /* Tentatively make the transition. */
1312 saved_state = state -> my_state;
1313 saved_stos = state -> my_stos;
1314 state -> my_stos = cur_time;
1315 state -> my_state = new_state;
1316
1317 if (!write_failover_state (state) || !commit_leases ()) {
1318 /* XXX What to do? What to do? */
1319 log_error ("Unable to record current failover state for %s",
1320 state -> name);
1321 /* XXX for now, we don't make the state transition, but this is
1322 XXX kind of a scary choice. */
1323 state -> my_state = saved_state;
1324 state -> my_stos = saved_stos;
1325 return ISC_R_IOERROR;
1326 }
1327
1328 log_info ("failover peer %s moves from %s to %s",
1329 state -> name, dhcp_failover_state_name_print (saved_state),
1330 dhcp_failover_state_name_print (state -> my_state));
1331
1332 switch (new_state)
1333 {
1334 case normal:
1335 case recover:
1336 case potential_conflict:
1337 /* If we go into communications-interrupted and then back into
1338 potential-conflict, do we need to start over, or just continue
1339 with the reconciliation process? This came up at one of the
1340 teleconferences, so it's probably answered in the draft. */
1341 if (state -> max_flying_updates > state -> cur_unacked_updates &&
1342 state -> update_queue_head) {
1343 dhcp_failover_send_updates (state);
1344 }
1345 dhcp_failover_state_pool_check (state);
1346 break;
1347 default:
1348 break;
1349 }
1350
1351 return ISC_R_SUCCESS;
1352 }
1353
1354 int dhcp_failover_pool_rebalance (dhcp_failover_state_t *state)
1355 {
1356 int lts;
1357 int leases_queued = 0;
1358 struct lease *lp;
1359 struct shared_network *s;
1360 struct pool *p;
1361
1362 if (state -> my_state != normal || state -> i_am == secondary)
1363 return 0;
1364
1365 for (s = shared_networks; s; s = s -> next) {
1366 for (p = s -> pools; p; p = p -> next) {
1367 if (p -> failover_peer != state)
1368 continue;
1369 log_info ("pool %lx total %d local free %d peer free %d",
1370 (unsigned long)p, p -> lease_count,
1371 p -> local_leases, p -> peer_leases);
1372
1373 lts = ((p -> local_leases +
1374 p -> peer_leases) / 2) - p -> peer_leases;
1375 if (lts > 1) {
1376 struct lease lt;
1377
1378 leases_queued += lts;
1379 for (lp = p -> last_lease; lp && lts;
1380 lp = lp -> prev) {
1381 if (!(lp -> flags & PEER_IS_OWNER)) {
1382 lp -> flags |= PEER_IS_OWNER;
1383 lp -> tstp = cur_time;
1384 if (!write_lease (lp) ||
1385 !commit_leases () ||
1386 !dhcp_failover_queue_update (lp)) {
1387 log_info ("%s lease %s on giveaway",
1388 "unable to commit",
1389 piaddr (lp -> ip_addr));
1390 }
1391 }
1392 }
1393 }
1394
1395 if (lts > 1) {
1396 log_info ("lease imbalance - lts = %d", lts);
1397 leases_queued -= lts;
1398 }
1399 }
1400 }
1401 dhcp_failover_send_poolresp (state, leases_queued);
1402 return leases_queued;
1403 }
1404
1405 int dhcp_failover_pool_check (struct pool *pool)
1406 {
1407 int lts;
1408 struct lease *lp;
1409
1410 if (!pool -> failover_peer ||
1411 pool -> failover_peer -> i_am == primary ||
1412 pool -> failover_peer -> my_state != normal)
1413 return 0;
1414
1415 log_info ("pool %lx total %d local free %d peer free %d",
1416 (unsigned long)pool, pool -> lease_count,
1417 pool -> local_leases, pool -> peer_leases);
1418
1419 lts = ((pool -> local_leases +
1420 pool -> peer_leases) / 2) - pool -> local_leases;
1421 if (lts > 1) {
1422 /* XXX What about multiple pools? */
1423 dhcp_failover_send_poolreq (pool -> failover_peer);
1424 return 1;
1425 }
1426 return 0;
1427 }
1428
1429 int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
1430 {
1431 struct lease *lp;
1432 struct shared_network *s;
1433 struct pool *p;
1434
1435 for (s = shared_networks; s; s = s -> next) {
1436 for (p = s -> pools; p; p = p -> next) {
1437 if (p -> failover_peer != state)
1438 continue;
1439 /* Only need to request rebalance on one pool. */
1440 if (dhcp_failover_pool_check (p))
1441 return 1;
1442 }
1443 }
1444 return 0;
1445 }
1446
1447 isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
1448 {
1449 struct lease *lp = (struct lease *)0;
1450 isc_result_t status;
1451
1452 /* Can't update peer if we're not talking to it! */
1453 if (state -> my_state != normal &&
1454 state -> my_state != recover &&
1455 state -> my_state != potential_conflict)
1456 return ISC_R_SUCCESS;
1457
1458 while (state -> max_flying_updates > state -> cur_unacked_updates &&
1459 state -> update_queue_head) {
1460 /* Grab the head of the update queue. */
1461 omapi_object_reference ((omapi_object_t **)&lp,
1462 (omapi_object_t *)
1463 state -> update_queue_head, MDL);
1464
1465 /* Send the update to the peer. */
1466 status = dhcp_failover_send_bind_update (state, lp);
1467 if (status != ISC_R_SUCCESS) {
1468 omapi_object_dereference ((omapi_object_t **)&lp, MDL);
1469 return status;
1470 }
1471 lp -> flags &= ~ON_UPDATE_QUEUE;
1472
1473 /* Take it off the head of the update queue and put the next
1474 item in the update queue at the head. */
1475 omapi_object_dereference ((omapi_object_t **)
1476 &state -> update_queue_head, MDL);
1477 if (lp -> next_pending) {
1478 omapi_object_reference ((omapi_object_t **)
1479 &state -> update_queue_head,
1480 (omapi_object_t *)
1481 lp -> next_pending, MDL);
1482 omapi_object_dereference ((omapi_object_t **)
1483 &lp -> next_pending, MDL);
1484 } else {
1485 omapi_object_dereference ((omapi_object_t **)
1486 &state -> update_queue_tail,
1487 MDL);
1488 }
1489
1490 if (state -> ack_queue_head) {
1491 omapi_object_reference
1492 ((omapi_object_t **)
1493 &state -> ack_queue_tail -> next_pending,
1494 (omapi_object_t *)lp, MDL);
1495 omapi_object_dereference ((omapi_object_t **)
1496 &state -> ack_queue_tail,
1497 MDL);
1498 } else {
1499 omapi_object_reference ((omapi_object_t **)
1500 &state -> ack_queue_head,
1501 (omapi_object_t *)lp, MDL);
1502 }
1503 omapi_object_reference ((omapi_object_t **)
1504 &state -> ack_queue_tail,
1505 (omapi_object_t *)lp, MDL);
1506 lp -> flags |= ON_ACK_QUEUE;
1507 omapi_object_dereference ((omapi_object_t **)&lp, MDL);
1508
1509 /* Count the object as an unacked update. */
1510 state -> cur_unacked_updates++;
1511 }
1512 return ISC_R_SUCCESS;
1513 }
1514
1515 /* Queue an update for a lease. Always returns 1 at this point - it's
1516 not an error for this to be called on a lease for which there's no
1517 failover peer. */
1518
1519 int dhcp_failover_queue_update (struct lease *lease)
1520 {
1521 dhcp_failover_state_t *state;
1522
1523 if (!lease -> pool ||
1524 !lease -> pool -> failover_peer)
1525 return 1;
1526
1527 /* If it's already on the update queue, leave it there. */
1528 if (lease -> flags & ON_UPDATE_QUEUE)
1529 return 1;
1530
1531 /* Get the failover state structure for this lease. */
1532 state = lease -> pool -> failover_peer;
1533
1534 /* If it's on the ack queue, take it off. */
1535 if (lease -> flags & ON_ACK_QUEUE)
1536 dhcp_failover_ack_queue_remove (state, lease);
1537
1538 if (state -> update_queue_head) {
1539 omapi_object_reference
1540 ((omapi_object_t **)
1541 &state -> update_queue_tail -> next_pending,
1542 (omapi_object_t *)lease, MDL);
1543 omapi_object_dereference ((omapi_object_t **)
1544 &state -> update_queue_tail, MDL);
1545 } else {
1546 omapi_object_reference ((omapi_object_t **)
1547 &state -> update_queue_head,
1548 (omapi_object_t *)lease, MDL);
1549 }
1550 omapi_object_reference ((omapi_object_t **)
1551 &state -> update_queue_tail,
1552 (omapi_object_t *)lease, MDL);
1553 lease -> flags |= ON_UPDATE_QUEUE;
1554 dhcp_failover_send_updates (state);
1555 return 1;
1556 }
1557
1558 void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
1559 struct lease *lease)
1560 {
1561 struct lease *lp;
1562
1563 if (state -> ack_queue_head == lease) {
1564 omapi_object_dereference ((omapi_object_t **)
1565 &state -> ack_queue_head, MDL);
1566 if (lease -> next_pending) {
1567 omapi_object_reference ((omapi_object_t **)
1568 &state -> ack_queue_head,
1569 (omapi_object_t *)
1570 lease -> next_pending, MDL);
1571 } else {
1572 omapi_object_dereference ((omapi_object_t **)
1573 &state -> ack_queue_tail,
1574 MDL);
1575 }
1576 lease -> flags &= ~ON_ACK_QUEUE;
1577 return;
1578 }
1579 for (lp = state -> ack_queue_head;
1580 lp -> next_pending != lease; lp = lp -> next_pending)
1581 ;
1582 if (lp) {
1583 omapi_object_dereference ((omapi_object_t **)
1584 &lp -> next_pending, MDL);
1585 if (lease -> next_pending)
1586 omapi_object_reference ((omapi_object_t **)
1587 &lp -> next_pending,
1588 (omapi_object_t *)
1589 lease -> next_pending, MDL);
1590 else {
1591 omapi_object_dereference ((omapi_object_t **)
1592 &state -> ack_queue_tail,
1593 MDL);
1594 omapi_object_reference ((omapi_object_t **)
1595 &state -> ack_queue_tail,
1596 (omapi_object_t *)lp, MDL);
1597 }
1598 }
1599 lease -> flags &= ~ON_ACK_QUEUE;
1600 }
1601
1602 isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
1603 omapi_object_t *id,
1604 omapi_data_string_t *name,
1605 omapi_typed_data_t *value)
1606 {
1607 if (h -> type != dhcp_type_failover_state)
1608 return ISC_R_INVALIDARG;
1609
1610 if (h -> inner && h -> inner -> type -> set_value)
1611 return (*(h -> inner -> type -> set_value))
1612 (h -> inner, id, name, value);
1613 return ISC_R_NOTFOUND;
1614 }
1615
1616 void dhcp_failover_keepalive (void *vs)
1617 {
1618 dhcp_failover_state_t *state = vs;
1619 }
1620
1621 void dhcp_failover_reconnect (void *vs)
1622 {
1623 dhcp_failover_state_t *state = vs;
1624 isc_result_t status;
1625
1626 status = dhcp_failover_link_initiate ((omapi_object_t *)state);
1627 if (status != ISC_R_SUCCESS) {
1628 log_info ("failover peer %s: %s", state -> name,
1629 isc_result_totext (status));
1630 add_timeout (cur_time + 90,
1631 dhcp_failover_listener_restart, state);
1632 }
1633 }
1634
1635 void dhcp_failover_listener_restart (void *vs)
1636 {
1637 dhcp_failover_state_t *state = vs;
1638 isc_result_t status;
1639
1640 status = dhcp_failover_listen ((omapi_object_t *)state);
1641 if (status != ISC_R_SUCCESS) {
1642 log_info ("failover peer %s: %s", state -> name,
1643 isc_result_totext (status));
1644 add_timeout (cur_time + 90,
1645 dhcp_failover_listener_restart, state);
1646 }
1647 }
1648
1649 isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
1650 omapi_object_t *id,
1651 omapi_data_string_t *name,
1652 omapi_value_t **value)
1653 {
1654 dhcp_failover_state_t *s;
1655 struct option_cache *oc;
1656 struct data_string ds;
1657 isc_result_t status;
1658
1659 if (h -> type != dhcp_type_failover_state)
1660 return ISC_R_INVALIDARG;
1661 s = (dhcp_failover_state_t *)h;
1662
1663 if (!omapi_ds_strcmp (name, "name")) {
1664 if (s -> name)
1665 return omapi_make_string_value (value,
1666 name, s -> name, MDL);
1667 return ISC_R_NOTFOUND;
1668 } else if (!omapi_ds_strcmp (name, "partner-address")) {
1669 oc = s -> address;
1670 getaddr:
1671 memset (&ds, 0, sizeof ds);
1672 if (!evaluate_option_cache (&ds, (struct packet *)0,
1673 (struct lease *)0,
1674 (struct option_state *)0,
1675 (struct option_state *)0,
1676 &global_scope, oc, MDL)) {
1677 return ISC_R_NOTFOUND;
1678 }
1679 status = omapi_make_const_value (value,
1680 name, ds.data, ds.len, MDL);
1681 /* Disgusting kludge: */
1682 if (oc == s -> server_addr && !s -> server_identifier.len)
1683 data_string_copy (&s -> server_identifier, &ds, MDL);
1684 data_string_forget (&ds, MDL);
1685 return status;
1686 } else if (!omapi_ds_strcmp (name, "local-address")) {
1687 oc = s -> server_addr;
1688 goto getaddr;
1689 } else if (!omapi_ds_strcmp (name, "partner-port")) {
1690 return omapi_make_int_value (value, name, s -> port, MDL);
1691 } else if (!omapi_ds_strcmp (name, "local-port")) {
1692 return omapi_make_int_value (value,
1693 name, s -> listen_port, MDL);
1694 } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
1695 return omapi_make_uint_value (value, name,
1696 s -> max_flying_updates, MDL);
1697 } else if (!omapi_ds_strcmp (name, "mclt")) {
1698 return omapi_make_uint_value (value, name, s -> mclt, MDL);
1699 } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
1700 return omapi_make_int_value (value, name,
1701 s -> load_balance_max_secs, MDL);
1702 } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
1703 if (s -> hba)
1704 return omapi_make_const_value (value, name,
1705 s -> hba, 32, MDL);
1706 return ISC_R_NOTFOUND;
1707 } else if (!omapi_ds_strcmp (name, "partner-state")) {
1708 return omapi_make_uint_value (value, name,
1709 s -> partner_state, MDL);
1710 } else if (!omapi_ds_strcmp (name, "local-state")) {
1711 return omapi_make_uint_value (value, name,
1712 s -> my_state, MDL);
1713 } else if (!omapi_ds_strcmp (name, "partner-stos")) {
1714 return omapi_make_int_value (value, name,
1715 s -> partner_stos, MDL);
1716 } else if (!omapi_ds_strcmp (name, "local-stos")) {
1717 return omapi_make_int_value (value, name,
1718 s -> my_stos, MDL);
1719 } else if (!omapi_ds_strcmp (name, "hierarchy")) {
1720 return omapi_make_uint_value (value, name, s -> i_am, MDL);
1721 } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
1722 return omapi_make_int_value (value, name,
1723 s -> last_packet_sent, MDL);
1724 } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
1725 return omapi_make_int_value (value, name,
1726 s -> last_timestamp_received,
1727 MDL);
1728 } else if (!omapi_ds_strcmp (name, "skew")) {
1729 return omapi_make_int_value (value, name, s -> skew, MDL);
1730 } else if (!omapi_ds_strcmp (name, "max-transmit-idle")) {
1731 return omapi_make_uint_value (value, name,
1732 s -> max_transmit_idle, MDL);
1733 } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
1734 return omapi_make_uint_value (value, name,
1735 s -> max_response_delay, MDL);
1736 } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
1737 return omapi_make_int_value (value, name,
1738 s -> cur_unacked_updates, MDL);
1739 }
1740
1741 if (h -> inner && h -> inner -> type -> get_value)
1742 return (*(h -> inner -> type -> get_value))
1743 (h -> inner, id, name, value);
1744 return ISC_R_NOTFOUND;
1745 }
1746
1747 isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
1748 const char *file, int line)
1749 {
1750 dhcp_failover_state_t *s;
1751
1752 if (h -> type != dhcp_type_failover_state)
1753 return ISC_R_INVALIDARG;
1754 s = (dhcp_failover_state_t *)h;
1755 if (s -> link_to_peer)
1756 omapi_object_dereference (&s -> link_to_peer, MDL);
1757 if (s -> name)
1758 dfree (s -> name, MDL);
1759 if (s -> address)
1760 option_cache_dereference (&s -> address, MDL);
1761 if (s -> server_addr)
1762 option_cache_dereference (&s -> server_addr, MDL);
1763
1764 return ISC_R_SUCCESS;
1765 }
1766
1767 /* Write all the published values associated with the object through the
1768 specified connection. */
1769
1770 isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
1771 omapi_object_t *id,
1772 omapi_object_t *h)
1773 {
1774 dhcp_failover_state_t *s;
1775 omapi_connection_object_t *conn;
1776 isc_result_t status;
1777
1778 if (c -> type != omapi_type_connection)
1779 return ISC_R_INVALIDARG;
1780 conn = (omapi_connection_object_t *)c;
1781
1782 if (h -> type != dhcp_type_failover_state)
1783 return ISC_R_INVALIDARG;
1784 s = (dhcp_failover_state_t *)h;
1785
1786 status = omapi_connection_put_name (c, "name");
1787 if (status != ISC_R_SUCCESS)
1788 return status;
1789 status = omapi_connection_put_string (c, s -> name);
1790 if (status != ISC_R_SUCCESS)
1791 return status;
1792
1793 status = omapi_connection_put_name (c, "partner-address");
1794 if (status != ISC_R_SUCCESS)
1795 return status;
1796 status = omapi_connection_put_uint32 (c, sizeof s -> address);
1797 if (status != ISC_R_SUCCESS)
1798 return status;
1799 status = omapi_connection_copyin (c, (u_int8_t *)&s -> address,
1800 sizeof s -> address);
1801 if (status != ISC_R_SUCCESS)
1802 return status;
1803
1804 status = omapi_connection_put_name (c, "partner-port");
1805 if (status != ISC_R_SUCCESS)
1806 return status;
1807 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1808 if (status != ISC_R_SUCCESS)
1809 return status;
1810 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> port);
1811 if (status != ISC_R_SUCCESS)
1812 return status;
1813
1814 status = omapi_connection_put_name (c, "local-address");
1815 if (status != ISC_R_SUCCESS)
1816 return status;
1817 status = omapi_connection_put_uint32 (c, sizeof s -> server_addr);
1818 if (status != ISC_R_SUCCESS)
1819 return status;
1820 status = omapi_connection_copyin (c, (u_int8_t *)&s -> server_addr,
1821 sizeof s -> server_addr);
1822 if (status != ISC_R_SUCCESS)
1823 return status;
1824
1825 status = omapi_connection_put_name (c, "local-port");
1826 if (status != ISC_R_SUCCESS)
1827 return status;
1828 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1829 if (status != ISC_R_SUCCESS)
1830 return status;
1831 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> listen_port);
1832 if (status != ISC_R_SUCCESS)
1833 return status;
1834
1835 status = omapi_connection_put_name (c, "max-outstanding-updates");
1836 if (status != ISC_R_SUCCESS)
1837 return status;
1838 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1839 if (status != ISC_R_SUCCESS)
1840 return status;
1841 status = omapi_connection_put_uint32 (c, s -> max_flying_updates);
1842 if (status != ISC_R_SUCCESS)
1843 return status;
1844
1845 status = omapi_connection_put_name (c, "mclt");
1846 if (status != ISC_R_SUCCESS)
1847 return status;
1848 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1849 if (status != ISC_R_SUCCESS)
1850 return status;
1851 status = omapi_connection_put_uint32 (c, s -> mclt);
1852 if (status != ISC_R_SUCCESS)
1853 return status;
1854
1855 status = omapi_connection_put_name (c, "load-balance-max-secs");
1856 if (status != ISC_R_SUCCESS)
1857 return status;
1858 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1859 if (status != ISC_R_SUCCESS)
1860 return status;
1861 status = (omapi_connection_put_uint32
1862 (c, (u_int32_t)s -> load_balance_max_secs));
1863 if (status != ISC_R_SUCCESS)
1864 return status;
1865
1866
1867 if (s -> hba) {
1868 status = omapi_connection_put_name (c, "load-balance-hba");
1869 if (status != ISC_R_SUCCESS)
1870 return status;
1871 status = omapi_connection_put_uint32 (c, 32);
1872 if (status != ISC_R_SUCCESS)
1873 return status;
1874 status = omapi_connection_copyin (c, s -> hba, 32);
1875 if (status != ISC_R_SUCCESS)
1876 return status;
1877 }
1878
1879 status = omapi_connection_put_name (c, "partner-state");
1880 if (status != ISC_R_SUCCESS)
1881 return status;
1882 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1883 if (status != ISC_R_SUCCESS)
1884 return status;
1885 status = omapi_connection_put_uint32 (c, s -> partner_state);
1886 if (status != ISC_R_SUCCESS)
1887 return status;
1888
1889 status = omapi_connection_put_name (c, "local-state");
1890 if (status != ISC_R_SUCCESS)
1891 return status;
1892 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1893 if (status != ISC_R_SUCCESS)
1894 return status;
1895 status = omapi_connection_put_uint32 (c, s -> my_state);
1896 if (status != ISC_R_SUCCESS)
1897 return status;
1898
1899 status = omapi_connection_put_name (c, "partner-stos");
1900 if (status != ISC_R_SUCCESS)
1901 return status;
1902 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1903 if (status != ISC_R_SUCCESS)
1904 return status;
1905 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner_stos);
1906 if (status != ISC_R_SUCCESS)
1907 return status;
1908
1909 status = omapi_connection_put_name (c, "local-stos");
1910 if (status != ISC_R_SUCCESS)
1911 return status;
1912 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1913 if (status != ISC_R_SUCCESS)
1914 return status;
1915 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> my_stos);
1916 if (status != ISC_R_SUCCESS)
1917 return status;
1918
1919 status = omapi_connection_put_name (c, "hierarchy");
1920 if (status != ISC_R_SUCCESS)
1921 return status;
1922 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1923 if (status != ISC_R_SUCCESS)
1924 return status;
1925 status = omapi_connection_put_uint32 (c, s -> i_am);
1926 if (status != ISC_R_SUCCESS)
1927 return status;
1928
1929 status = omapi_connection_put_name (c, "last-packet-sent");
1930 if (status != ISC_R_SUCCESS)
1931 return status;
1932 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1933 if (status != ISC_R_SUCCESS)
1934 return status;
1935 status = (omapi_connection_put_uint32
1936 (c, (u_int32_t)s -> last_packet_sent));
1937 if (status != ISC_R_SUCCESS)
1938 return status;
1939
1940 status = omapi_connection_put_name (c, "last-timestamp-received");
1941 if (status != ISC_R_SUCCESS)
1942 return status;
1943 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1944 if (status != ISC_R_SUCCESS)
1945 return status;
1946 status = (omapi_connection_put_uint32
1947 (c, (u_int32_t)s -> last_timestamp_received));
1948 if (status != ISC_R_SUCCESS)
1949 return status;
1950
1951 status = omapi_connection_put_name (c, "skew");
1952 if (status != ISC_R_SUCCESS)
1953 return status;
1954 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1955 if (status != ISC_R_SUCCESS)
1956 return status;
1957 status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
1958 if (status != ISC_R_SUCCESS)
1959 return status;
1960
1961 status = omapi_connection_put_name (c, "max-transmit-idle");
1962 if (status != ISC_R_SUCCESS)
1963 return status;
1964 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1965 if (status != ISC_R_SUCCESS)
1966 return status;
1967 status = (omapi_connection_put_uint32
1968 (c, (u_int32_t)s -> max_transmit_idle));
1969 if (status != ISC_R_SUCCESS)
1970 return status;
1971
1972 status = omapi_connection_put_name (c, "max-response-delay");
1973 if (status != ISC_R_SUCCESS)
1974 return status;
1975 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1976 if (status != ISC_R_SUCCESS)
1977 return status;
1978 status = (omapi_connection_put_uint32
1979 (c, (u_int32_t)s -> max_response_delay));
1980 if (status != ISC_R_SUCCESS)
1981 return status;
1982
1983 status = omapi_connection_put_name (c, "cur-unacked-updates");
1984 if (status != ISC_R_SUCCESS)
1985 return status;
1986 status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
1987 if (status != ISC_R_SUCCESS)
1988 return status;
1989 status = (omapi_connection_put_uint32
1990 (c, (u_int32_t)s -> cur_unacked_updates));
1991 if (status != ISC_R_SUCCESS)
1992 return status;
1993
1994 if (h -> inner && h -> inner -> type -> stuff_values)
1995 return (*(h -> inner -> type -> stuff_values)) (c, id,
1996 h -> inner);
1997 return ISC_R_SUCCESS;
1998 }
1999
2000 isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
2001 omapi_object_t *id,
2002 omapi_object_t *ref)
2003 {
2004 omapi_value_t *tv = (omapi_value_t *)0;
2005 isc_result_t status;
2006 dhcp_failover_state_t *s;
2007
2008 /* First see if we were sent a handle. */
2009 status = omapi_get_value_str (ref, id, "handle", &tv);
2010 if (status == ISC_R_SUCCESS) {
2011 status = omapi_handle_td_lookup (sp, tv -> value);
2012
2013 omapi_value_dereference (&tv, MDL);
2014 if (status != ISC_R_SUCCESS)
2015 return status;
2016
2017 /* Don't return the object if the type is wrong. */
2018 if ((*sp) -> type != dhcp_type_failover_state) {
2019 omapi_object_dereference (sp, MDL);
2020 return ISC_R_INVALIDARG;
2021 }
2022 }
2023
2024 /* Look the failover state up by peer name. */
2025 status = omapi_get_value_str (ref, id, "peer_name", &tv);
2026 if (status == ISC_R_SUCCESS) {
2027 for (s = failover_states; s; s = s -> next) {
2028 unsigned l = strlen (s -> name);
2029 if (l == tv -> value -> u.buffer.len ||
2030 !memcmp (s -> name,
2031 tv -> value -> u.buffer.value, l))
2032 break;
2033 }
2034 omapi_value_dereference (&tv, MDL);
2035
2036 /* If we already have a lease, and it's not the same one,
2037 then the query was invalid. */
2038 if (*sp && *sp != (omapi_object_t *)s) {
2039 omapi_object_dereference (sp, MDL);
2040 return ISC_R_KEYCONFLICT;
2041 } else if (!s) {
2042 if (*sp)
2043 omapi_object_dereference (sp, MDL);
2044 return ISC_R_NOTFOUND;
2045 } else if (!*sp)
2046 /* XXX fix so that hash lookup itself creates
2047 XXX the reference. */
2048 omapi_object_reference (sp, (omapi_object_t *)s, MDL);
2049 }
2050
2051 /* If we get to here without finding a lease, no valid key was
2052 specified. */
2053 if (!*sp)
2054 return ISC_R_NOKEYS;
2055 return ISC_R_SUCCESS;
2056 }
2057
2058 isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
2059 omapi_object_t *id)
2060 {
2061 return ISC_R_NOTIMPLEMENTED;
2062 }
2063
2064 isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
2065 omapi_object_t *id)
2066 {
2067 return ISC_R_NOTIMPLEMENTED;
2068 }
2069
2070 int dhcp_failover_state_match (dhcp_failover_state_t *state,
2071 u_int8_t *addr, unsigned addrlen)
2072 {
2073 struct option_cache *oc;
2074 struct data_string ds;
2075 int i;
2076
2077 memset (&ds, 0, sizeof ds);
2078 if (evaluate_option_cache (&ds, (struct packet *)0,
2079 (struct lease *)0,
2080 (struct option_state *)0,
2081 (struct option_state *)0,
2082 &global_scope,
2083 state -> address, MDL)) {
2084 for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
2085 if (!memcmp (&ds.data [i],
2086 addr, addrlen)) {
2087 data_string_forget (&ds, MDL);
2088 return 1;
2089 }
2090 }
2091 data_string_forget (&ds, MDL);
2092 }
2093 return 0;
2094 }
2095
2096 const char *dhcp_failover_reject_reason_print (int reason)
2097 {
2098 switch (reason) {
2099 case FTR_ILLEGAL_IP_ADDR:
2100 return "Illegal IP address (not part of any address pool).";
2101
2102 case FTR_FATAL_CONFLICT:
2103 return "Fatal conflict exists: address in use by other client.";
2104
2105 case FTR_MISSING_BINDINFO:
2106 return "Missing binding information.";
2107
2108 case FTR_TIMEMISMATCH:
2109 return "Connection rejected, time mismatch too great.";
2110
2111 case FTR_INVALID_MCLT:
2112 return "Connection rejected, invalid MCLT.";
2113
2114 case FTR_MISC_REJECT:
2115 return "Connection rejected, unknown reason.";
2116
2117 case FTR_DUP_CONNECTION:
2118 return "Connection rejected, duplicate connection.";
2119
2120 case FTR_INVALID_PARTNER:
2121 return "Connection rejected, invalid failover partner.";
2122
2123 case FTR_TLS_UNSUPPORTED:
2124 return "TLS not supported.";
2125
2126 case FTR_TLS_UNCONFIGURED:
2127 return "TLS supported but not configured.";
2128
2129 case FTR_TLS_REQUIRED:
2130 return "TLS required but not supported by partner.";
2131
2132 case FTR_DIGEST_UNSUPPORTED:
2133 return "Message digest not supported.";
2134
2135 case FTR_DIGEST_UNCONFIGURED:
2136 return "Message digest not configured.";
2137
2138 case FTR_VERSION_MISMATCH:
2139 return "Protocol version mismatch.";
2140
2141 case FTR_MISSING_BIND_INFO:
2142 return "Missing binding information.";
2143
2144 case FTR_OUTDATED_BIND_INFO:
2145 return "Outdated binding information.";
2146
2147 case FTR_LESS_CRIT_BIND_INFO:
2148 return "Less critical binding information.";
2149
2150 case FTR_NO_TRAFFIC:
2151 return "No traffic within sufficient time.";
2152
2153 case FTR_HBA_CONFLICT:
2154 return "Hash bucket assignment conflict.";
2155
2156 default:
2157 case FTR_UNKNOWN:
2158 return "Unknown: Error occurred but does not match any reason code.";
2159 }
2160 }
2161
2162 const char *dhcp_failover_state_name_print (enum failover_state state)
2163 {
2164 switch (state) {
2165 default:
2166 case unknown_state:
2167 return "unknown-state";
2168
2169 case partner_down:
2170 return "partner-down";
2171
2172 case normal:
2173 return "normal";
2174
2175 case communications_interrupted:
2176 return "communications-interrupted";
2177
2178 case potential_conflict_nic:
2179 return "potential-conflict-nic";
2180
2181 case potential_conflict:
2182 return "potential-conflict";
2183
2184 case recover:
2185 return "recover";
2186 }
2187 }
2188
2189 failover_option_t *dhcp_failover_option_printf (unsigned code,
2190 char *obuf,
2191 unsigned *obufix,
2192 unsigned obufmax,
2193 const char *fmt, ...) {
2194 va_list va;
2195 char tbuf [256];
2196
2197 va_start (va, fmt);
2198 #if defined (HAVE_SNPRINTF)
2199 /* Presumably if we have snprintf, we also have
2200 vsnprintf. */
2201 vsnprintf (tbuf, sizeof tbuf, fmt, va);
2202 #else
2203 vsprintf (tbuf, fmt, va);
2204 #endif
2205 va_end (va);
2206
2207 return dhcp_failover_make_option (code, obuf, obufix, obufmax,
2208 strlen (tbuf), tbuf);
2209 }
2210
2211 failover_option_t *dhcp_failover_make_option (unsigned code,
2212 char *obuf, unsigned *obufix,
2213 unsigned obufmax, ...)
2214 {
2215 va_list va;
2216 struct failover_option_info *info;
2217 int i;
2218 unsigned size, count;
2219 unsigned val;
2220 u_int8_t *iaddr;
2221 unsigned ilen;
2222 u_int8_t *bval;
2223 char *txt;
2224 #if defined (DEBUG_FAILOVER_MESSAGES)
2225 char tbuf [256];
2226 #endif
2227
2228 /* Note that the failover_option structure is used differently on
2229 input than on output - on input, count is an element count, and
2230 on output it's the number of bytes total in the option, including
2231 the option code and option length. */
2232 failover_option_t option, *op;
2233
2234
2235 /* Bogus option code? */
2236 if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
2237 return &null_failover_option;
2238 }
2239 info = &ft_options [code];
2240
2241 va_start (va, obufmax);
2242
2243 /* Get the number of elements and the size of the buffer we need
2244 to allocate. */
2245 if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
2246 count = info -> type == FT_DDNS ? 1 : 2;
2247 size = va_arg (va, int) + count;
2248 } else {
2249 /* Find out how many items in this list. */
2250 if (info -> num_present)
2251 count = info -> num_present;
2252 else
2253 count = va_arg (va, int);
2254
2255 /* Figure out size. */
2256 switch (info -> type) {
2257 case FT_UINT8:
2258 case FT_BYTES:
2259 case FT_DIGEST:
2260 size = count;
2261 break;
2262
2263 case FT_TEXT_OR_BYTES:
2264 case FT_TEXT:
2265 txt = va_arg (va, char *);
2266 size = count;
2267 break;
2268
2269 case FT_IPADDR:
2270 ilen = va_arg (va, unsigned);
2271 size = count * ilen;
2272 break;
2273
2274 case FT_UINT32:
2275 size = count * 4;
2276 break;
2277
2278 case FT_UINT16:
2279 size = count * 2;
2280 break;
2281
2282 default:
2283 /* shouldn't get here. */
2284 log_fatal ("bogus type in failover_make_option: %d",
2285 info -> type);
2286 break;
2287 }
2288 }
2289
2290 size += 4;
2291
2292 /* Allocate a buffer for the option. */
2293 option.count = size;
2294 option.data = dmalloc (option.count, MDL);
2295 if (!option.data)
2296 return &null_failover_option;
2297
2298 /* Put in the option code and option length. */
2299 putUShort (option.data, code);
2300 putUShort (&option.data [2], size - 4);
2301
2302 #if defined (DEBUG_FAILOVER_MESSAGES)
2303 sprintf (tbuf, " (%s<%d>", info -> name, option.count);
2304 failover_print (obuf, obufix, obufmax, tbuf);
2305 #endif
2306
2307 /* Now put in the data. */
2308 switch (info -> type) {
2309 case FT_UINT8:
2310 for (i = 0; i < count; i++) {
2311 val = va_arg (va, unsigned);
2312 #if defined (DEBUG_FAILOVER_MESSAGES)
2313 sprintf (tbuf, " %d", val);
2314 failover_print (obuf, obufix, obufmax, tbuf);
2315 #endif
2316 option.data [i + 4] = val;
2317 }
2318 break;
2319
2320 case FT_IPADDR:
2321 for (i = 0; i < count; i++) {
2322 iaddr = va_arg (va, u_int8_t *);
2323 if (ilen != 4) {
2324 dfree (option.data, MDL);
2325 log_error ("IP addrlen=%d, should be 4.",
2326 ilen);
2327 return &null_failover_option;
2328 }
2329
2330 #if defined (DEBUG_FAILOVER_MESSAGES)
2331 sprintf (tbuf, " %u.%u.%u.%u", iaddr [0], iaddr [1],
2332 iaddr [2], iaddr [3]);
2333 failover_print (obuf, obufix, obufmax, tbuf);
2334 #endif
2335 memcpy (&option.data [4 + i * ilen], iaddr, ilen);
2336 }
2337 break;
2338
2339 case FT_UINT32:
2340 for (i = 0; i < count; i++) {
2341 val = va_arg (va, unsigned);
2342 #if defined (DEBUG_FAILOVER_MESSAGES)
2343 sprintf (tbuf, " %d", val);
2344 failover_print (obuf, obufix, obufmax, tbuf);
2345 #endif
2346 putULong (&option.data [4 + i * 4], val);
2347 }
2348 break;
2349
2350 case FT_BYTES:
2351 case FT_DIGEST:
2352 bval = va_arg (va, u_int8_t *);
2353 #if defined (DEBUG_FAILOVER_MESSAGES)
2354 for (i = 0; i < count; i++) {
2355 sprintf (tbuf, " %d", bval [i]);
2356 failover_print (obuf, obufix, obufmax, tbuf);
2357 }
2358 #endif
2359 memcpy (&option.data [4], bval, count);
2360 break;
2361
2362 /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
2363 terminated. Note that the caller should be careful not to
2364 provide a format and data that amount to more than 256 bytes
2365 of data, since it will be truncated on platforms that
2366 support snprintf, and will mung the stack on those platforms
2367 that do not support snprintf. Also, callers should not pass
2368 data acquired from the network without specifically checking
2369 it to make sure it won't bash the stack. */
2370 case FT_TEXT_OR_BYTES:
2371 case FT_TEXT:
2372 #if defined (DEBUG_FAILOVER_MESSAGES)
2373 sprintf (tbuf, "\"%s\"", txt);
2374 failover_print (obuf, obufix, obufmax, tbuf);
2375 #endif
2376 memcpy (&option.data [4], txt, count);
2377 break;
2378
2379 case FT_DDNS:
2380 case FT_DDNS1:
2381 option.data [4] = va_arg (va, unsigned);
2382 if (count == 2)
2383 option.data [5] = va_arg (va, unsigned);
2384 bval = va_arg (va, u_int8_t *);
2385 memcpy (&option.data [4 + count], bval, size - count - 4);
2386 #if defined (DEBUG_FAILOVER_MESSAGES)
2387 for (i = 4; i < size; i++) {
2388 sprintf (tbuf, " %d", option.data [i]);
2389 failover_print (obuf, obufix, obufmax, tbuf);
2390 }
2391 #endif
2392 break;
2393
2394 case FT_UINT16:
2395 for (i = 0; i < count; i++) {
2396 val = va_arg (va, u_int32_t);
2397 #if defined (DEBUG_FAILOVER_MESSAGES)
2398 sprintf (tbuf, " %d", val);
2399 failover_print (obuf, obufix, obufmax, tbuf);
2400 #endif
2401 putUShort (&option.data [4 + i * 2], val);
2402 }
2403 break;
2404
2405 case FT_UNDEF:
2406 default:
2407 }
2408
2409 failover_print (obuf, obufix, obufmax, ")");
2410
2411 /* Now allocate a place to store what we just set up. */
2412 op = dmalloc (sizeof (failover_option_t), MDL);
2413 if (!op) {
2414 dfree (option.data, MDL);
2415 return &null_failover_option;
2416 }
2417
2418 *op = option;
2419 return op;
2420 }
2421
2422 /* Send a failover message header. */
2423
2424 isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
2425 omapi_object_t *connection,
2426 int msg_type, ...)
2427 {
2428 unsigned count = 0;
2429 unsigned size = 0;
2430 int bad_option = 0;
2431 int opix = 0;
2432 va_list list;
2433 failover_option_t *option;
2434 unsigned char *opbuf;
2435 isc_result_t status = ISC_R_SUCCESS;
2436 unsigned char cbuf;
2437
2438 /* Run through the argument list once to compute the length of
2439 the option portion of the message. */
2440 va_start (list, msg_type);
2441 while ((option = va_arg (list, failover_option_t *))) {
2442 if (option != &skip_failover_option)
2443 size += option -> count;
2444 if (option == &null_failover_option)
2445 bad_option = 1;
2446 }
2447 va_end (list);
2448
2449 /* Allocate an option buffer, unless we got an error. */
2450 if (!bad_option) {
2451 opbuf = dmalloc (size, MDL);
2452 if (!opbuf)
2453 status = ISC_R_NOMEMORY;
2454 }
2455
2456 va_start (list, msg_type);
2457 while ((option = va_arg (list, failover_option_t *))) {
2458 if (option == &skip_failover_option)
2459 continue;
2460 if (!bad_option && opbuf)
2461 memcpy (&opbuf [opix],
2462 option -> data, option -> count);
2463 if (option != &null_failover_option &&
2464 option != &skip_failover_option) {
2465 opix += option -> count;
2466 dfree (option -> data, MDL);
2467 dfree (option, MDL);
2468 }
2469 }
2470
2471 if (bad_option)
2472 return ISC_R_INVALIDARG;
2473
2474 /* Now send the message header. */
2475
2476 /* Message length. */
2477 status = omapi_connection_put_uint16 (connection, size + 12);
2478 if (status != ISC_R_SUCCESS)
2479 goto err;
2480
2481 /* Message type. */
2482 cbuf = msg_type;
2483 status = omapi_connection_copyin (connection, &cbuf, 1);
2484 if (status != ISC_R_SUCCESS)
2485 goto err;
2486
2487 /* Payload offset. */
2488 cbuf = 12;
2489 status = omapi_connection_copyin (connection, &cbuf, 1);
2490 if (status != ISC_R_SUCCESS)
2491 goto err;
2492
2493 /* Current time. */
2494 status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
2495 if (status != ISC_R_SUCCESS)
2496 goto err;
2497
2498 /* Transaction ID. */
2499 status = omapi_connection_put_uint32 (connection, link -> xid++);
2500 if (status != ISC_R_SUCCESS)
2501 goto err;
2502
2503
2504 /* Payload. */
2505 status = omapi_connection_copyin (connection, opbuf, size);
2506 if (status != ISC_R_SUCCESS)
2507 goto err;
2508 dfree (opbuf, MDL);
2509 return status;
2510
2511 err:
2512 dfree (opbuf, MDL);
2513 omapi_disconnect (connection, 1);
2514 return status;
2515 }
2516
2517 /* Send a connect message. */
2518
2519 isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
2520 {
2521 dhcp_failover_link_t *link;
2522 dhcp_failover_state_t *state;
2523 isc_result_t status;
2524 #if defined (DEBUG_FAILOVER_MESSAGES)
2525 char obuf [64];
2526 unsigned obufix = 0;
2527
2528 # define FMA obuf, &obufix, sizeof obuf
2529 failover_print (FMA, "(connect");
2530 #else
2531 # define FMA (unsigned char *)0, (unsigned *)0, 0
2532 #endif
2533
2534 if (!l || l -> type != dhcp_type_failover_link)
2535 return ISC_R_INVALIDARG;
2536 link = (dhcp_failover_link_t *)l;
2537 state = link -> state_object;
2538 if (!l -> outer || l -> outer -> type != omapi_type_connection)
2539 return ISC_R_INVALIDARG;
2540
2541 status = (dhcp_failover_put_message
2542 (link, l -> outer,
2543 FTM_CONNECT,
2544 dhcp_failover_make_option (FTO_SERVER_ADDR, FMA,
2545 state -> server_identifier.len,
2546 state -> server_identifier.data),
2547 dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
2548 state -> max_flying_updates),
2549 dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
2550 state -> max_response_delay),
2551 dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
2552 "isc-%s", DHCP_VERSION),
2553 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
2554 DHCP_FAILOVER_VERSION),
2555 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
2556 0, 0),
2557 dhcp_failover_make_option (FTO_MCLT, FMA,
2558 state -> mclt),
2559 dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba),
2560 (failover_option_t *)0));
2561
2562 #if defined (DEBUG_FAILOVER_MESSAGES)
2563 if (status != ISC_R_SUCCESS)
2564 failover_print (FMA, " (failed)");
2565 failover_print (FMA, ")");
2566 if (obufix) {
2567 log_debug ("%s", obuf);
2568 }
2569 #endif
2570 return status;
2571 }
2572
2573 isc_result_t dhcp_failover_send_connectack (omapi_object_t *l, int reason)
2574 {
2575 dhcp_failover_link_t *link;
2576 dhcp_failover_state_t *state;
2577 isc_result_t status;
2578 #if defined (DEBUG_FAILOVER_MESSAGES)
2579 char obuf [64];
2580 unsigned obufix = 0;
2581
2582 # define FMA obuf, &obufix, sizeof obuf
2583 failover_print (FMA, "(connectack");
2584 #else
2585 # define FMA (unsigned char *)0, (unsigned *)0, 0
2586 #endif
2587
2588 if (!l || l -> type != dhcp_type_failover_link)
2589 return ISC_R_INVALIDARG;
2590 link = (dhcp_failover_link_t *)l;
2591 state = link -> state_object;
2592 if (!l -> outer || l -> outer -> type != omapi_type_connection)
2593 return ISC_R_INVALIDARG;
2594
2595 status = (dhcp_failover_put_message
2596 (link, l -> outer,
2597 FTM_CONNECTACK,
2598 dhcp_failover_make_option (FTO_SERVER_ADDR, FMA,
2599 state -> server_identifier.len,
2600 state -> server_identifier.data),
2601 dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
2602 state -> max_flying_updates),
2603 dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
2604 state -> max_response_delay),
2605 dhcp_failover_option_printf (FTO_VENDOR_CLASS, FMA,
2606 "isc-%s", DHCP_VERSION),
2607 dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
2608 DHCP_FAILOVER_VERSION),
2609 dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
2610 0, 0),
2611 (reason
2612 ? dhcp_failover_make_option (FTO_REJECT_REASON,
2613 FMA, reason)
2614 : &skip_failover_option),
2615 (failover_option_t *)0));
2616
2617 #if defined (DEBUG_FAILOVER_MESSAGES)
2618 if (status != ISC_R_SUCCESS)
2619 failover_print (FMA, " (failed)");
2620 failover_print (FMA, ")");
2621 if (obufix) {
2622 log_debug ("%s", obuf);
2623 }
2624 #endif
2625 return status;
2626 }
2627
2628 isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
2629 int reason,
2630 const char *message)
2631 {
2632 dhcp_failover_link_t *link;
2633 dhcp_failover_state_t *state;
2634 isc_result_t status;
2635 #if defined (DEBUG_FAILOVER_MESSAGES)
2636 char obuf [64];
2637 unsigned obufix = 0;
2638
2639 # define FMA obuf, &obufix, sizeof obuf
2640 failover_print (FMA, "(disconnect");
2641 #else
2642 # define FMA (unsigned char *)0, (unsigned *)0, 0
2643 #endif
2644
2645 if (!l || l -> type != dhcp_type_failover_link)
2646 return ISC_R_INVALIDARG;
2647 link = (dhcp_failover_link_t *)l;
2648 state = link -> state_object;
2649 if (!l -> outer || l -> outer -> type != omapi_type_connection)
2650 return ISC_R_INVALIDARG;
2651
2652 if (!message && reason)
2653 message = dhcp_failover_reject_reason_print (reason);
2654
2655 status = (dhcp_failover_put_message
2656 (link, l -> outer,
2657 FTM_DISCONNECT,
2658 dhcp_failover_make_option (FTO_REJECT_REASON,
2659 FMA, reason),
2660 (message
2661 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
2662 strlen (message), message)
2663 : &skip_failover_option),
2664 (failover_option_t *)0));
2665
2666 #if defined (DEBUG_FAILOVER_MESSAGES)
2667 if (status != ISC_R_SUCCESS)
2668 failover_print (FMA, " (failed)");
2669 failover_print (FMA, ")");
2670 if (obufix) {
2671 log_debug ("%s", obuf);
2672 }
2673 #endif
2674 return status;
2675 }
2676
2677 /* Send a Bind Update message. */
2678
2679 isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
2680 struct lease *lease)
2681 {
2682 dhcp_failover_link_t *link;
2683 isc_result_t status;
2684 #if defined (DEBUG_FAILOVER_MESSAGES)
2685 char obuf [64];
2686 unsigned obufix = 0;
2687 int binding_status;
2688
2689 # define FMA obuf, &obufix, sizeof obuf
2690 failover_print (FMA, "(bndupd");
2691 #else
2692 # define FMA (unsigned char *)0, (unsigned *)0, 0
2693 #endif
2694
2695 if (!state -> link_to_peer ||
2696 state -> link_to_peer -> type != dhcp_type_failover_link)
2697 return ISC_R_INVALIDARG;
2698 link = (dhcp_failover_link_t *)state -> link_to_peer;
2699
2700 if (!link -> outer || link -> outer -> type != omapi_type_connection)
2701 return ISC_R_INVALIDARG;
2702
2703 /* Kludge up the binding status. */
2704 if (lease -> flags & ABANDONED)
2705 binding_status = FTS_ABANDONED;
2706 else if (lease -> tsfp <= cur_time) {
2707 if (lease -> flags & PEER_IS_OWNER) {
2708 if (state -> i_am == primary)
2709 binding_status = FTS_BACKUP;
2710 else
2711 binding_status = FTS_FREE;
2712 } else {
2713 if (state -> i_am == primary)
2714 binding_status = FTS_FREE;
2715 else
2716 binding_status = FTS_BACKUP;
2717 }
2718 } else if (lease -> ends <= cur_time) {
2719 binding_status = FTS_EXPIRED;
2720 } else
2721 binding_status = FTS_ACTIVE;
2722
2723 /* Send the update. */
2724 status = (dhcp_failover_put_message
2725 (link, link -> outer,
2726 FTM_BNDUPD,
2727 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
2728 lease -> ip_addr.len,
2729 lease -> ip_addr.iabuf),
2730 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
2731 binding_status),
2732 lease -> uid_len
2733 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
2734 lease -> uid_len,
2735 lease -> uid)
2736 : &skip_failover_option,
2737 lease -> hardware_addr.hlen
2738 ? dhcp_failover_make_option (FTO_CHADDR, FMA,
2739 lease -> hardware_addr.hlen,
2740 lease -> hardware_addr.hbuf)
2741 : &skip_failover_option,
2742 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
2743 lease -> ends),
2744 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
2745 lease -> tstp),
2746 dhcp_failover_make_option (FTO_STOS, FMA,
2747 lease -> starts),
2748 dhcp_failover_make_option (FTO_CLTT, FMA,
2749 lease -> cltt),
2750 &skip_failover_option, /* XXX DDNS */
2751 &skip_failover_option, /* XXX request options */
2752 &skip_failover_option, /* XXX reply options */
2753 (failover_option_t *)0));
2754
2755 #if defined (DEBUG_FAILOVER_MESSAGES)
2756 if (status != ISC_R_SUCCESS)
2757 failover_print (FMA, " (failed)");
2758 failover_print (FMA, ")");
2759 if (obufix) {
2760 log_debug ("%s", obuf);
2761 }
2762 #endif
2763 return status;
2764 }
2765
2766 /* Send a Bind ACK message. */
2767
2768 isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
2769 struct lease *lease,
2770 failover_message_t *msg,
2771 int reason, const char *message)
2772 {
2773 dhcp_failover_link_t *link;
2774 isc_result_t status;
2775 #if defined (DEBUG_FAILOVER_MESSAGES)
2776 char obuf [64];
2777 unsigned obufix = 0;
2778 int binding_status;
2779
2780 # define FMA obuf, &obufix, sizeof obuf
2781 failover_print (FMA, "(bndack");
2782 #else
2783 # define FMA (unsigned char *)0, (unsigned *)0, 0
2784 #endif
2785
2786 if (!state -> link_to_peer ||
2787 state -> link_to_peer -> type != dhcp_type_failover_link)
2788 return ISC_R_INVALIDARG;
2789 link = (dhcp_failover_link_t *)state -> link_to_peer;
2790
2791 if (!link -> outer || link -> outer -> type != omapi_type_connection)
2792 return ISC_R_INVALIDARG;
2793
2794 /* Kludge up the binding status. */
2795 if (lease -> flags & ABANDONED)
2796 binding_status = FTS_ABANDONED;
2797 else if (lease -> tsfp <= cur_time) {
2798 if (lease -> flags & PEER_IS_OWNER) {
2799 if (state -> i_am == primary)
2800 binding_status = FTS_BACKUP;
2801 else
2802 binding_status = FTS_FREE;
2803 } else {
2804 if (state -> i_am == primary)
2805 binding_status = FTS_FREE;
2806 else
2807 binding_status = FTS_BACKUP;
2808 }
2809 } else if (lease -> ends <= cur_time) {
2810 binding_status = FTS_EXPIRED;
2811 } else
2812 binding_status = FTS_ACTIVE;
2813
2814 if (!message && reason)
2815 message = dhcp_failover_reject_reason_print (reason);
2816
2817 /* Send the update. */
2818 status = (dhcp_failover_put_message
2819 (link, link -> outer,
2820 FTM_BNDACK,
2821 dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
2822 lease -> ip_addr.len,
2823 lease -> ip_addr.iabuf),
2824 dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
2825 binding_status),
2826 lease -> uid_len
2827 ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
2828 lease -> uid_len,
2829 lease -> uid)
2830 : &skip_failover_option,
2831 lease -> hardware_addr.hlen
2832 ? dhcp_failover_make_option (FTO_CHADDR, FMA,
2833 lease -> hardware_addr.hlen,
2834 lease -> hardware_addr.hbuf)
2835 : &skip_failover_option,
2836 dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
2837 msg -> expiry),
2838 dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
2839 msg -> potential_expiry),
2840 dhcp_failover_make_option (FTO_STOS, FMA,
2841 msg -> stos),
2842 dhcp_failover_make_option (FTO_CLTT, FMA,
2843 msg -> client_ltt),
2844 reason
2845 ? dhcp_failover_make_option (FTO_REJECT_REASON,
2846 FMA, reason)
2847 : &skip_failover_option,
2848 (message
2849 ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
2850 strlen (message), message)
2851 : &skip_failover_option),
2852 &skip_failover_option, /* XXX DDNS */
2853 &skip_failover_option, /* XXX request options */
2854 &skip_failover_option, /* XXX reply options */
2855 (failover_option_t *)0));
2856
2857 #if defined (DEBUG_FAILOVER_MESSAGES)
2858 if (status != ISC_R_SUCCESS)
2859 failover_print (FMA, " (failed)");
2860 failover_print (FMA, ")");
2861 if (obufix) {
2862 log_debug ("%s", obuf);
2863 }
2864 #endif
2865 return status;
2866 }
2867
2868 isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
2869 {
2870 dhcp_failover_link_t *link;
2871 isc_result_t status;
2872 #if defined (DEBUG_FAILOVER_MESSAGES)
2873 char obuf [64];
2874 unsigned obufix = 0;
2875
2876 # define FMA obuf, &obufix, sizeof obuf
2877 failover_print (FMA, "(poolreq");
2878 #else
2879 # define FMA (unsigned char *)0, (unsigned *)0, 0
2880 #endif
2881
2882 if (!state -> link_to_peer ||
2883 state -> link_to_peer -> type != dhcp_type_failover_link)
2884 return ISC_R_INVALIDARG;
2885 link = (dhcp_failover_link_t *)state -> link_to_peer;
2886
2887 if (!link -> outer || link -> outer -> type != omapi_type_connection)
2888 return ISC_R_INVALIDARG;
2889
2890 status = (dhcp_failover_put_message
2891 (link, link -> outer,
2892 FTM_POOLREQ,
2893 (failover_option_t *)0));
2894
2895 #if defined (DEBUG_FAILOVER_MESSAGES)
2896 if (status != ISC_R_SUCCESS)
2897 failover_print (FMA, " (failed)");
2898 failover_print (FMA, ")");
2899 if (obufix) {
2900 log_debug ("%s", obuf);
2901 }
2902 #endif
2903 return status;
2904 }
2905
2906 isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
2907 int leases)
2908 {
2909 dhcp_failover_link_t *link;
2910 isc_result_t status;
2911 #if defined (DEBUG_FAILOVER_MESSAGES)
2912 char obuf [64];
2913 unsigned obufix = 0;
2914
2915 # define FMA obuf, &obufix, sizeof obuf
2916 failover_print (FMA, "(poolreq");
2917 #else
2918 # define FMA (unsigned char *)0, (unsigned *)0, 0
2919 #endif
2920
2921 if (!state -> link_to_peer ||
2922 state -> link_to_peer -> type != dhcp_type_failover_link)
2923 return ISC_R_INVALIDARG;
2924 link = (dhcp_failover_link_t *)state -> link_to_peer;
2925
2926 if (!link -> outer || link -> outer -> type != omapi_type_connection)
2927 return ISC_R_INVALIDARG;
2928
2929 status = (dhcp_failover_put_message
2930 (link, link -> outer,
2931 FTM_POOLREQ,
2932 dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
2933 leases),
2934 (failover_option_t *)0));
2935
2936 #if defined (DEBUG_FAILOVER_MESSAGES)
2937 if (status != ISC_R_SUCCESS)
2938 failover_print (FMA, " (failed)");
2939 failover_print (FMA, ")");
2940 if (obufix) {
2941 log_debug ("%s", obuf);
2942 }
2943 #endif
2944 return status;
2945 }
2946
2947 isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
2948 failover_message_t *msg)
2949 {
2950 struct lease lt, *lease;
2951 struct iaddr ia;
2952
2953 ia.len = sizeof msg -> assigned_addr;
2954 memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
2955
2956 lease = find_lease_by_ip_addr (ia);
2957 if (!lease) {
2958 log_info ("bind update on %s from %s: no such lease.",
2959 piaddr (ia), state -> name);
2960 return ISC_R_SUCCESS;
2961 }
2962
2963 /* XXX check for conflicts. */
2964
2965 /* Install the new info. */
2966 lt = *lease;
2967 lt.on_expiry = 0;
2968 lt.on_release = 0;
2969 lt.on_commit = 0;
2970
2971 if (msg -> options_present & FTB_CHADDR) {
2972 if (msg -> chaddr.count > sizeof lt.hardware_addr.hbuf) {
2973 log_info ("bind update on %s from %s: chaddr too long",
2974 piaddr (ia), state -> name);
2975 return ISC_R_SUCCESS;
2976 }
2977 lt.hardware_addr.hlen = msg -> chaddr.count;
2978 memcpy (lt.hardware_addr.hbuf, msg -> chaddr.data,
2979 msg -> chaddr.count);
2980 }
2981
2982 if (msg -> options_present & FTB_CLIENT_IDENTIFIER) {
2983 lt.uid_len = msg -> client_identifier.count;
2984 if (lt.uid_len > sizeof lt.uid_buf) {
2985 lt.uid_max = lt.uid_len;
2986 lt.uid = dmalloc (lt.uid_len, MDL);
2987 if (!lt.uid)
2988 return ISC_R_SUCCESS;
2989 } else {
2990 lt.uid_max = sizeof lt.uid_buf;
2991 lt.uid = lt.uid_buf;
2992 }
2993 memcpy (lt.uid, msg -> client_identifier.data, lt.uid_len);
2994 }
2995
2996 /* XXX Times may need to be adjusted based on clock skew! */
2997 if (msg -> options_present & FTB_STOS) {
2998 lt.starts = msg -> stos;
2999 }
3000 if (msg -> options_present & FTB_LEASE_EXPIRY) {
3001 lt.ends = msg -> expiry;
3002 }
3003 if (msg -> options_present & FTB_CLTT) {
3004 lt.cltt = msg -> client_ltt;
3005 }
3006 if (msg -> options_present & FTB_POTENTIAL_EXPIRY) {
3007 lt.tsfp = msg -> potential_expiry;
3008 }
3009
3010 if (msg -> options_present & FTB_BINDING_STATUS) {
3011 if (state -> i_am == primary) {
3012 if (msg -> binding_status == FTS_BACKUP)
3013 lease -> flags |= PEER_IS_OWNER;
3014 else if (msg -> binding_status == FTS_FREE)
3015 lease -> flags &= ~PEER_IS_OWNER;
3016 } else {
3017 if (msg -> binding_status == FTS_BACKUP)
3018 lease -> flags &= PEER_IS_OWNER;
3019 else if (msg -> binding_status == FTS_FREE)
3020 lease -> flags |= ~PEER_IS_OWNER;
3021 }
3022 }
3023
3024 /* Try to install the new information. */
3025 if (!supersede_lease (lease, &lt, 1, 0)) {
3026 dhcp_failover_send_bind_ack (state, lease, msg,
3027 FTR_MISC_REJECT,
3028 "database update failed.");
3029 } else
3030 dhcp_failover_send_bind_ack (state, lease, msg, 0, 0);
3031
3032 return ISC_R_SUCCESS;
3033 }
3034
3035 isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
3036 failover_message_t *msg)
3037 {
3038 struct lease lt, *lease;
3039 struct iaddr ia;
3040
3041 ia.len = sizeof msg -> assigned_addr;
3042 memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
3043
3044 lease = find_lease_by_ip_addr (ia);
3045 if (!lease) {
3046 log_info ("bind update on %s from %s: no such lease.",
3047 piaddr (ia), state -> name);
3048 return ISC_R_SUCCESS;
3049 }
3050
3051 /* XXX check for conflicts. */
3052
3053 /* Install the new info. */
3054 lt = *lease;
3055 lt.on_expiry = 0;
3056 lt.on_release = 0;
3057 lt.on_commit = 0;
3058
3059 /* XXX Times may need to be adjusted based on clock skew! */
3060 if (msg -> options_present & FTB_POTENTIAL_EXPIRY) {
3061 lt.tsfp = msg -> potential_expiry;
3062 }
3063
3064 /* Try to install the new information. */
3065 supersede_lease (lease, &lt, 1, 0);
3066
3067 state -> cur_unacked_updates--;
3068 dhcp_failover_ack_queue_remove (state, lease);
3069
3070 /* If there are updates pending, we've created space to send at
3071 least one. */
3072 dhcp_failover_send_updates (state);
3073
3074 return ISC_R_SUCCESS;
3075 }
3076
3077 #if defined (DEBUG_FAILOVER_MESSAGES)
3078 /* Print hunks of failover messages, doing line breaks as appropriate.
3079 Note that this assumes syslog is being used, rather than, e.g., the
3080 Windows NT logging facility, where just dumping the whole message in
3081 one hunk would be more appropriate. */
3082
3083 void failover_print (char *obuf,
3084 unsigned *obufix, unsigned obufmax, const char *s)
3085 {
3086 int len = strlen (s);
3087
3088 while (len + *obufix + 1 >= obufmax) {
3089 log_debug ("%s", obuf);
3090 if (!*obufix) {
3091 log_debug ("%s", s);
3092 *obufix = 0;
3093 return;
3094 }
3095 *obufix = 0;
3096 }
3097 strcpy (&obuf [*obufix], s);
3098 *obufix += len;
3099 }
3100 #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
3101
3102 /* Taken from draft-ietf-dhc-loadb-01.txt: */
3103 /* A "mixing table" of 256 distinct values, in pseudo-random order. */
3104 unsigned char loadb_mx_tbl[256] = {
3105 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
3106 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
3107 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
3108 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
3109 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
3110 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
3111 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
3112 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
3113 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
3114 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
3115 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
3116 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
3117 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
3118 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
3119 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
3120 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
3121 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
3122 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
3123 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
3124 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
3125 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
3126 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
3127 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
3128 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
3129 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
3130 170, 68, 6, 169, 234, 151 };
3131
3132 static unsigned char loadb_p_hash (const unsigned char *, unsigned);
3133 static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
3134 {
3135 unsigned char hash = len;
3136 int i;
3137 for(i = len; i > 0; )
3138 hash = loadb_mx_tbl [hash ^ (key [--i])];
3139 return hash;
3140 }
3141
3142 int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
3143 {
3144 struct option_cache *oc;
3145 struct data_string ds;
3146 unsigned char hbaix;
3147 int hm;
3148
3149 if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
3150 return 1;
3151 }
3152
3153 oc = lookup_option (&dhcp_universe, packet -> options,
3154 DHO_DHCP_CLIENT_IDENTIFIER);
3155 memset (&ds, 0, sizeof ds);
3156 if (oc &&
3157 evaluate_option_cache (&ds, packet, (struct lease *)0,
3158 packet -> options, (struct option_state *)0,
3159 &global_scope, oc, MDL)) {
3160 hbaix = loadb_p_hash (ds.data, ds.len);
3161 } else {
3162 hbaix = loadb_p_hash (packet -> raw -> chaddr,
3163 packet -> raw -> hlen);
3164 }
3165 hm = (state -> hba [hbaix / 8] & (1 << (hbaix & 3)));
3166 if (state -> i_am == primary)
3167 return hm;
3168 else
3169 return !hm;
3170 }
3171 #endif /* defined (FAILOVER_PROTOCOL) */