Annotation of embedaddon/dhcp/server/failover.c, revision 1.1.1.1
1.1 misho 1: /* failover.c
2:
3: Failover protocol support code... */
4:
5: /*
1.1.1.1 ! misho 6: * Copyright (c) 2011-2012 by Internet Systems Consortium, Inc. ("ISC")
1.1 misho 7: * Copyright (c) 2004-2009 by Internet Systems Consortium, Inc. ("ISC")
8: * Copyright (c) 1999-2003 by Internet Software Consortium
9: *
10: * Permission to use, copy, modify, and distribute this software for any
11: * purpose with or without fee is hereby granted, provided that the above
12: * copyright notice and this permission notice appear in all copies.
13: *
14: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
17: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20: * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21: *
22: * Internet Systems Consortium, Inc.
23: * 950 Charter Street
24: * Redwood City, CA 94063
25: * <info@isc.org>
26: * https://www.isc.org/
27: *
28: * This software has been written for Internet Systems Consortium
29: * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
30: * To learn more about Internet Systems Consortium, see
31: * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
32: * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
33: * ``http://www.nominum.com''.
34: */
35:
1.1.1.1 ! misho 36: #include "cdefs.h"
1.1 misho 37: #include "dhcpd.h"
38: #include <omapip/omapip_p.h>
39:
40: #if defined (FAILOVER_PROTOCOL)
41: dhcp_failover_state_t *failover_states;
42: static isc_result_t do_a_failover_option (omapi_object_t *,
43: dhcp_failover_link_t *);
44: dhcp_failover_listener_t *failover_listeners;
45:
46: static isc_result_t failover_message_reference (failover_message_t **,
47: failover_message_t *,
48: const char *file, int line);
49: static isc_result_t failover_message_dereference (failover_message_t **,
50: const char *file, int line);
51:
52: static void dhcp_failover_pool_balance(dhcp_failover_state_t *state);
53: static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state);
54: static int dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
55: isc_boolean_t *sendreq);
56: static inline int secondary_not_hoarding(dhcp_failover_state_t *state,
57: struct pool *p);
58:
59:
60: void dhcp_failover_startup ()
61: {
62: dhcp_failover_state_t *state;
63: isc_result_t status;
64: struct timeval tv;
65:
66: for (state = failover_states; state; state = state -> next) {
67: dhcp_failover_state_transition (state, "startup");
68:
69: if (state -> pool_count == 0) {
70: log_error ("failover peer declaration with no %s",
71: "referring pools.");
72: log_error ("In order to use failover, you MUST %s",
73: "refer to your main failover declaration");
74: log_error ("in each pool declaration. You MUST %s",
75: "NOT use range declarations outside");
76: log_fatal ("of pool declarations.");
77: }
78: /* In case the peer is already running, immediately try
79: to establish a connection with it. */
80: status = dhcp_failover_link_initiate ((omapi_object_t *)state);
81: if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
82: #if defined (DEBUG_FAILOVER_TIMING)
83: log_info ("add_timeout +90 dhcp_failover_reconnect");
84: #endif
85: tv . tv_sec = cur_time + 90;
86: tv . tv_usec = 0;
87: add_timeout (&tv,
88: dhcp_failover_reconnect, state,
89: (tvref_t)
90: dhcp_failover_state_reference,
91: (tvunref_t)
92: dhcp_failover_state_dereference);
93: log_error ("failover peer %s: %s", state -> name,
94: isc_result_totext (status));
95: }
96:
97: status = (dhcp_failover_listen
98: ((omapi_object_t *)state));
99: if (status != ISC_R_SUCCESS) {
100: #if defined (DEBUG_FAILOVER_TIMING)
101: log_info ("add_timeout +90 %s",
102: "dhcp_failover_listener_restart");
103: #endif
104: tv . tv_sec = cur_time + 90;
105: tv . tv_usec = 0;
106: add_timeout (&tv,
107: dhcp_failover_listener_restart,
108: state,
109: (tvref_t)omapi_object_reference,
110: (tvunref_t)omapi_object_dereference);
111: }
112: }
113: }
114:
115: int dhcp_failover_write_all_states ()
116: {
117: dhcp_failover_state_t *state;
118:
119: for (state = failover_states; state; state = state -> next) {
120: if (!write_failover_state (state))
121: return 0;
122: }
123: return 1;
124: }
125:
126: isc_result_t enter_failover_peer (peer)
127: dhcp_failover_state_t *peer;
128: {
129: dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
130: isc_result_t status;
131:
132: status = find_failover_peer (&dup, peer -> name, MDL);
133: if (status == ISC_R_NOTFOUND) {
134: if (failover_states) {
135: dhcp_failover_state_reference (&peer -> next,
136: failover_states, MDL);
137: dhcp_failover_state_dereference (&failover_states,
138: MDL);
139: }
140: dhcp_failover_state_reference (&failover_states, peer, MDL);
141: return ISC_R_SUCCESS;
142: }
143: dhcp_failover_state_dereference (&dup, MDL);
144: if (status == ISC_R_SUCCESS)
145: return ISC_R_EXISTS;
146: return status;
147: }
148:
149: isc_result_t find_failover_peer (peer, name, file, line)
150: dhcp_failover_state_t **peer;
151: const char *name;
152: const char *file;
153: int line;
154: {
155: dhcp_failover_state_t *p;
156:
157: for (p = failover_states; p; p = p -> next)
158: if (!strcmp (name, p -> name))
159: break;
160: if (p)
161: return dhcp_failover_state_reference (peer, p, file, line);
162: return ISC_R_NOTFOUND;
163: }
164:
165: /* The failover protocol has three objects associated with it. For
166: each failover partner declaration in the dhcpd.conf file, primary
167: or secondary, there is a failover_state object. For any primary or
168: secondary state object that has a connection to its peer, there is
169: also a failover_link object, which has its own input state separate
170: from the failover protocol state for managing the actual bytes
171: coming in off the wire. Finally, there will be one listener object
172: for every distinct port number associated with a secondary
173: failover_state object. Normally all secondary failover_state
174: objects are expected to listen on the same port number, so there
175: need be only one listener object, but if different port numbers are
176: specified for each failover object, there could be as many as one
177: listener object for each secondary failover_state object. */
178:
179: /* This, then, is the implementation of the failover link object. */
180:
181: isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
182: {
183: isc_result_t status;
184: dhcp_failover_link_t *obj;
185: dhcp_failover_state_t *state;
186: omapi_object_t *o;
187: int i;
188: struct data_string ds;
189: omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
190: omapi_addr_t local_addr;
191:
192: /* Find the failover state in the object chain. */
193: for (o = h; o -> outer; o = o -> outer)
194: ;
195: for (; o; o = o -> inner) {
196: if (o -> type == dhcp_type_failover_state)
197: break;
198: }
199: if (!o)
200: return ISC_R_INVALIDARG;
201: state = (dhcp_failover_state_t *)o;
202:
203: obj = (dhcp_failover_link_t *)0;
204: status = dhcp_failover_link_allocate (&obj, MDL);
205: if (status != ISC_R_SUCCESS)
206: return status;
207: option_cache_reference (&obj -> peer_address,
208: state -> partner.address, MDL);
209: obj -> peer_port = state -> partner.port;
210: dhcp_failover_state_reference (&obj -> state_object, state, MDL);
211:
212: memset (&ds, 0, sizeof ds);
213: if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
214: (struct client_state *)0,
215: (struct option_state *)0,
216: (struct option_state *)0,
217: &global_scope, obj -> peer_address, MDL)) {
218: dhcp_failover_link_dereference (&obj, MDL);
219: return ISC_R_UNEXPECTED;
220: }
221:
222: /* Make an omapi address list out of a buffer containing zero or more
223: IPv4 addresses. */
224: status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
225: if (status != ISC_R_SUCCESS) {
226: dhcp_failover_link_dereference (&obj, MDL);
227: return status;
228: }
229:
230: for (i = 0; i < addrs -> count; i++) {
231: addrs -> addresses [i].addrtype = AF_INET;
232: addrs -> addresses [i].addrlen = sizeof (struct in_addr);
233: memcpy (addrs -> addresses [i].address,
234: &ds.data [i * 4], sizeof (struct in_addr));
235: addrs -> addresses [i].port = obj -> peer_port;
236: }
237: data_string_forget (&ds, MDL);
238:
239: /* Now figure out the local address that we're supposed to use. */
240: if (!state -> me.address ||
241: !evaluate_option_cache (&ds, (struct packet *)0,
242: (struct lease *)0,
243: (struct client_state *)0,
244: (struct option_state *)0,
245: (struct option_state *)0,
246: &global_scope, state -> me.address,
247: MDL)) {
248: memset (&local_addr, 0, sizeof local_addr);
249: local_addr.addrtype = AF_INET;
250: local_addr.addrlen = sizeof (struct in_addr);
251: if (!state -> server_identifier.len) {
252: log_fatal ("failover peer %s: no local address.",
253: state -> name);
254: }
255: } else {
256: if (ds.len != sizeof (struct in_addr)) {
257: log_error("failover peer %s: 'address' parameter "
258: "fails to resolve to an IPv4 address",
259: state->name);
260: data_string_forget (&ds, MDL);
261: dhcp_failover_link_dereference (&obj, MDL);
262: omapi_addr_list_dereference (&addrs, MDL);
263: return ISC_R_INVALIDARG;
264: }
265: local_addr.addrtype = AF_INET;
266: local_addr.addrlen = ds.len;
267: memcpy (local_addr.address, ds.data, ds.len);
268: if (!state -> server_identifier.len)
269: data_string_copy (&state -> server_identifier,
270: &ds, MDL);
271: data_string_forget (&ds, MDL);
272: local_addr.port = 0; /* Let the O.S. choose. */
273: }
274:
275: status = omapi_connect_list ((omapi_object_t *)obj,
276: addrs, &local_addr);
277: omapi_addr_list_dereference (&addrs, MDL);
278:
279: dhcp_failover_link_dereference (&obj, MDL);
280: return status;
281: }
282:
283: isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
284: const char *name, va_list ap)
285: {
286: isc_result_t status;
287: dhcp_failover_link_t *link;
288: omapi_object_t *c;
289: dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
290: char *sname;
291: int slen;
292: struct timeval tv;
293:
294: if (h -> type != dhcp_type_failover_link) {
295: /* XXX shouldn't happen. Put an assert here? */
296: return ISC_R_UNEXPECTED;
297: }
298: link = (dhcp_failover_link_t *)h;
299:
300: if (!strcmp (name, "connect")) {
301: if (link -> state_object -> i_am == primary) {
302: status = dhcp_failover_send_connect (h);
303: if (status != ISC_R_SUCCESS) {
304: log_info ("dhcp_failover_send_connect: %s",
305: isc_result_totext (status));
306: omapi_disconnect (h -> outer, 1);
307: }
308: } else
309: status = ISC_R_SUCCESS;
310: /* Allow the peer fifteen seconds to send us a
311: startup message. */
312: #if defined (DEBUG_FAILOVER_TIMING)
313: log_info ("add_timeout +15 %s",
314: "dhcp_failover_link_startup_timeout");
315: #endif
316: tv . tv_sec = cur_time + 15;
317: tv . tv_usec = 0;
318: add_timeout (&tv,
319: dhcp_failover_link_startup_timeout,
320: link,
321: (tvref_t)dhcp_failover_link_reference,
322: (tvunref_t)dhcp_failover_link_dereference);
323: return status;
324: }
325:
326: if (!strcmp (name, "disconnect")) {
327: if (link -> state_object) {
328: dhcp_failover_state_reference (&state,
329: link -> state_object, MDL);
330: link -> state = dhcp_flink_disconnected;
331:
332: /* Make the transition. */
333: if (state->link_to_peer == link)
334: dhcp_failover_state_transition(link->state_object, name);
335:
336: /* Schedule an attempt to reconnect. */
337: #if defined (DEBUG_FAILOVER_TIMING)
338: log_info("add_timeout +5 dhcp_failover_reconnect");
339: #endif
340: tv.tv_sec = cur_time + 5;
341: tv.tv_usec = cur_tv.tv_usec;
342: add_timeout(&tv, dhcp_failover_reconnect, state,
343: (tvref_t)dhcp_failover_state_reference,
344: (tvunref_t)dhcp_failover_state_dereference);
345:
346: dhcp_failover_state_dereference (&state, MDL);
347: }
348: return ISC_R_SUCCESS;
349: }
350:
351: if (!strcmp (name, "status")) {
352: if (link -> state_object) {
353: isc_result_t status;
354:
355: status = va_arg(ap, isc_result_t);
356:
357: if ((status == ISC_R_HOSTUNREACH) || (status == ISC_R_TIMEDOUT)) {
358: dhcp_failover_state_reference (&state,
359: link -> state_object, MDL);
360: link -> state = dhcp_flink_disconnected;
361:
362: /* Make the transition. */
363: dhcp_failover_state_transition (link -> state_object,
364: "disconnect");
365:
366: /* Start trying to reconnect. */
367: #if defined (DEBUG_FAILOVER_TIMING)
368: log_info ("add_timeout +5 %s",
369: "dhcp_failover_reconnect");
370: #endif
371: tv . tv_sec = cur_time + 5;
372: tv . tv_usec = 0;
373: add_timeout (&tv, dhcp_failover_reconnect,
374: state,
375: (tvref_t)dhcp_failover_state_reference,
376: (tvunref_t)dhcp_failover_state_dereference);
377: }
378: dhcp_failover_state_dereference (&state, MDL);
379: }
380: return ISC_R_SUCCESS;
381: }
382:
383: /* Not a signal we recognize? */
384: if (strcmp (name, "ready")) {
385: if (h -> inner && h -> inner -> type -> signal_handler)
386: return (*(h -> inner -> type -> signal_handler))
387: (h -> inner, name, ap);
388: return ISC_R_NOTFOUND;
389: }
390:
391: if (!h -> outer || h -> outer -> type != omapi_type_connection)
392: return ISC_R_INVALIDARG;
393: c = h -> outer;
394:
395: /* We get here because we requested that we be woken up after
396: some number of bytes were read, and that number of bytes
397: has in fact been read. */
398: switch (link -> state) {
399: case dhcp_flink_start:
400: link -> state = dhcp_flink_message_length_wait;
401: if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
402: break;
403: case dhcp_flink_message_length_wait:
404: next_message:
405: link -> state = dhcp_flink_message_wait;
406: link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
407: if (!link -> imsg) {
408: status = ISC_R_NOMEMORY;
409: dhcp_flink_fail:
410: if (link -> imsg) {
411: failover_message_dereference (&link->imsg,
412: MDL);
413: }
414: link -> state = dhcp_flink_disconnected;
415: log_info ("message length wait: %s",
416: isc_result_totext (status));
417: omapi_disconnect (c, 1);
418: /* XXX just blow away the protocol state now?
419: XXX or will disconnect blow it away? */
420: return ISC_R_UNEXPECTED;
421: }
422: memset (link -> imsg, 0, sizeof (failover_message_t));
423: link -> imsg -> refcnt = 1;
424: /* Get the length: */
425: omapi_connection_get_uint16 (c, &link -> imsg_len);
426: link -> imsg_count = 0; /* Bytes read. */
427:
428: /* Ensure the message is of valid length. */
429: if (link->imsg_len < DHCP_FAILOVER_MIN_MESSAGE_SIZE ||
430: link->imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) {
431: status = ISC_R_UNEXPECTED;
432: goto dhcp_flink_fail;
433: }
434:
435: if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
436: ISC_R_SUCCESS)
437: break;
438: case dhcp_flink_message_wait:
439: /* Read in the message. At this point we have the
440: entire message in the input buffer. For each
441: incoming value ID, set a bit in the bitmask
442: indicating that we've gotten it. Maybe flag an
443: error message if the bit is already set. Once
444: we're done reading, we can check the bitmask to
445: make sure that the required fields for each message
446: have been included. */
447:
448: link -> imsg_count += 2; /* Count the length as read. */
449:
450: /* Get message type. */
451: omapi_connection_copyout (&link -> imsg -> type, c, 1);
452: link -> imsg_count++;
453:
454: /* Get message payload offset. */
455: omapi_connection_copyout (&link -> imsg_payoff, c, 1);
456: link -> imsg_count++;
457:
458: /* Get message time. */
459: omapi_connection_get_uint32 (c, &link -> imsg -> time);
460: link -> imsg_count += 4;
461:
462: /* Get transaction ID. */
463: omapi_connection_get_uint32 (c, &link -> imsg -> xid);
464: link -> imsg_count += 4;
465:
466: #if defined (DEBUG_FAILOVER_MESSAGES)
467: # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
468: if (link->imsg->type == FTM_CONTACT)
469: goto skip_contact;
470: # endif
471: log_info ("link: message %s payoff %d time %ld xid %ld",
472: dhcp_failover_message_name (link -> imsg -> type),
473: link -> imsg_payoff,
474: (unsigned long)link -> imsg -> time,
475: (unsigned long)link -> imsg -> xid);
476: # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
477: skip_contact:
478: # endif
479: #endif
480: /* Skip over any portions of the message header that we
481: don't understand. */
482: if (link -> imsg_payoff - link -> imsg_count) {
483: omapi_connection_copyout ((unsigned char *)0, c,
484: (link -> imsg_payoff -
485: link -> imsg_count));
486: link -> imsg_count = link -> imsg_payoff;
487: }
488:
489: /* Now start sucking options off the wire. */
490: while (link -> imsg_count < link -> imsg_len) {
491: status = do_a_failover_option (c, link);
492: if (status != ISC_R_SUCCESS)
493: goto dhcp_flink_fail;
494: }
495:
496: /* If it's a connect message, try to associate it with
497: a state object. */
498: /* XXX this should be authenticated! */
499: if (link -> imsg -> type == FTM_CONNECT) {
500: const char *errmsg;
501: int reason;
502:
503: if (!(link->imsg->options_present &
504: FTB_RELATIONSHIP_NAME)) {
505: errmsg = "missing relationship-name";
506: reason = FTR_INVALID_PARTNER;
507: goto badconnect;
508: }
509:
510: /* See if we can find a failover_state object that
511: matches this connection. This message should only
512: be received by a secondary from a primary. */
513: for (s = failover_states; s; s = s -> next) {
514: if (dhcp_failover_state_match_by_name(s,
515: &link->imsg->relationship_name))
516: state = s;
517: }
518:
519: /* If we can't find a failover protocol state
520: for this remote host, drop the connection */
521: if (!state) {
522: errmsg = "unknown failover relationship name";
523: reason = FTR_INVALID_PARTNER;
524:
525: badconnect:
526: /* XXX Send a refusal message first?
527: XXX Look in protocol spec for guidance. */
528:
529: if (state != NULL) {
530: sname = state->name;
531: slen = strlen(sname);
532: } else if (link->imsg->options_present &
533: FTB_RELATIONSHIP_NAME) {
534: sname = (char *)link->imsg->
535: relationship_name.data;
536: slen = link->imsg->relationship_name.count;
537: } else {
538: sname = "unknown";
539: slen = strlen(sname);
540: }
541:
542: log_error("Failover CONNECT from %.*s: %s",
543: slen, sname, errmsg);
544: dhcp_failover_send_connectack
545: ((omapi_object_t *)link, state,
546: reason, errmsg);
547: log_info ("failover: disconnect: %s", errmsg);
548: omapi_disconnect (c, 0);
549: link -> state = dhcp_flink_disconnected;
550: return ISC_R_SUCCESS;
551: }
552:
553: if ((cur_time > link -> imsg -> time &&
554: cur_time - link -> imsg -> time > 60) ||
555: (cur_time < link -> imsg -> time &&
556: link -> imsg -> time - cur_time > 60)) {
557: errmsg = "time offset too large";
558: reason = FTR_TIMEMISMATCH;
559: goto badconnect;
560: }
561:
562: if (!(link -> imsg -> options_present & FTB_HBA) ||
563: link -> imsg -> hba.count != 32) {
564: errmsg = "invalid HBA";
565: reason = FTR_HBA_CONFLICT; /* XXX */
566: goto badconnect;
567: }
568: if (state -> hba)
569: dfree (state -> hba, MDL);
570: state -> hba = dmalloc (32, MDL);
571: if (!state -> hba) {
572: errmsg = "no memory";
573: reason = FTR_MISC_REJECT;
574: goto badconnect;
575: }
576: memcpy (state -> hba, link -> imsg -> hba.data, 32);
577:
578: if (!link -> state_object)
579: dhcp_failover_state_reference
580: (&link -> state_object, state, MDL);
581: if (!link -> peer_address)
582: option_cache_reference
583: (&link -> peer_address,
584: state -> partner.address, MDL);
585: }
586:
587: /* If we don't have a state object at this point, it's
588: some kind of bogus situation, so just drop the
589: connection. */
590: if (!link -> state_object) {
591: log_info ("failover: connect: no matching state.");
592: omapi_disconnect (c, 1);
593: link -> state = dhcp_flink_disconnected;
594: return ISC_R_INVALIDARG;
595: }
596:
597: /* Once we have the entire message, and we've validated
598: it as best we can here, pass it to the parent. */
599: omapi_signal ((omapi_object_t *)link -> state_object,
600: "message", link);
601: link -> state = dhcp_flink_message_length_wait;
602: if (link -> imsg)
603: failover_message_dereference (&link -> imsg, MDL);
604: /* XXX This is dangerous because we could get into a tight
605: XXX loop reading input without servicing any other stuff.
606: XXX There needs to be a way to relinquish control but
607: XXX get it back immediately if there's no other work to
608: XXX do. */
609: if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
610: goto next_message;
611: break;
612:
613: default:
614: log_fatal("Impossible case at %s:%d.", MDL);
615: break;
616: }
617: return ISC_R_SUCCESS;
618: }
619:
620: static isc_result_t do_a_failover_option (c, link)
621: omapi_object_t *c;
622: dhcp_failover_link_t *link;
623: {
624: u_int16_t option_code;
625: u_int16_t option_len;
626: unsigned char *op;
627: unsigned op_size;
628: unsigned op_count;
629: int i;
630:
631: if (link -> imsg_count + 2 > link -> imsg_len) {
632: log_error ("FAILOVER: message overflow at option code.");
633: return ISC_R_PROTOCOLERROR;
634: }
635:
636: /* Get option code. */
637: omapi_connection_get_uint16 (c, &option_code);
638: link -> imsg_count += 2;
639:
640: if (link -> imsg_count + 2 > link -> imsg_len) {
641: log_error ("FAILOVER: message overflow at length.");
642: return ISC_R_PROTOCOLERROR;
643: }
644:
645: /* Get option length. */
646: omapi_connection_get_uint16 (c, &option_len);
647: link -> imsg_count += 2;
648:
649: if (link -> imsg_count + option_len > link -> imsg_len) {
650: log_error ("FAILOVER: message overflow at data.");
651: return ISC_R_PROTOCOLERROR;
652: }
653:
654: /* If it's an unknown code, skip over it. */
655: if ((option_code > FTO_MAX) ||
656: (ft_options[option_code].type == FT_UNDEF)) {
657: #if defined (DEBUG_FAILOVER_MESSAGES)
658: log_debug (" option code %d (%s) len %d (not recognized)",
659: option_code,
660: dhcp_failover_option_name (option_code),
661: option_len);
662: #endif
663: omapi_connection_copyout ((unsigned char *)0, c, option_len);
664: link -> imsg_count += option_len;
665: return ISC_R_SUCCESS;
666: }
667:
668: /* If it's the digest, do it now. */
669: if (ft_options [option_code].type == FT_DIGEST) {
670: link -> imsg_count += option_len;
671: if (link -> imsg_count != link -> imsg_len) {
672: log_error ("FAILOVER: digest not at end of message");
673: return ISC_R_PROTOCOLERROR;
674: }
675: #if defined (DEBUG_FAILOVER_MESSAGES)
676: log_debug (" option %s len %d",
677: ft_options [option_code].name, option_len);
678: #endif
679: /* For now, just dump it. */
680: omapi_connection_copyout ((unsigned char *)0, c, option_len);
681: return ISC_R_SUCCESS;
682: }
683:
684: /* Only accept an option once. */
685: if (link -> imsg -> options_present & ft_options [option_code].bit) {
686: log_error ("FAILOVER: duplicate option %s",
687: ft_options [option_code].name);
688: return ISC_R_PROTOCOLERROR;
689: }
690:
691: /* Make sure the option is appropriate for this type of message.
692: Really, any option is generally allowed for any message, and the
693: cases where this is not true are too complicated to represent in
694: this way - what this code is doing is to just avoid saving the
695: value of an option we don't have any way to use, which allows
696: us to make the failover_message structure smaller. */
697: if (ft_options [option_code].bit &&
698: !(fto_allowed [link -> imsg -> type] &
699: ft_options [option_code].bit)) {
700: omapi_connection_copyout ((unsigned char *)0, c, option_len);
701: link -> imsg_count += option_len;
702: return ISC_R_SUCCESS;
703: }
704:
705: /* Figure out how many elements, how big they are, and where
706: to store them. */
707: if (ft_options [option_code].num_present) {
708: /* If this option takes a fixed number of elements,
709: we expect the space for them to be preallocated,
710: and we can just read the data in. */
711:
712: op = ((unsigned char *)link -> imsg) +
713: ft_options [option_code].offset;
714: op_size = ft_sizes [ft_options [option_code].type];
715: op_count = ft_options [option_code].num_present;
716:
717: if (option_len != op_size * op_count) {
718: log_error ("FAILOVER: option size (%d:%d), option %s",
719: option_len,
720: (ft_sizes [ft_options [option_code].type] *
721: ft_options [option_code].num_present),
722: ft_options [option_code].name);
723: return ISC_R_PROTOCOLERROR;
724: }
725: } else {
726: failover_option_t *fo;
727:
728: /* FT_DDNS* are special - one or two bytes of status
729: followed by the client FQDN. */
730: if (ft_options [option_code].type == FT_DDNS1 ||
731: ft_options [option_code].type == FT_DDNS1) {
732: ddns_fqdn_t *ddns =
733: ((ddns_fqdn_t *)
734: (((char *)link -> imsg) +
735: ft_options [option_code].offset));
736:
737: op_count = (ft_options [option_code].type == FT_DDNS1
738: ? 1 : 2);
739:
740: omapi_connection_copyout (&ddns -> codes [0],
741: c, op_count);
742: link -> imsg_count += op_count;
743: if (op_count == 1)
744: ddns -> codes [1] = 0;
745: op_size = 1;
746: op_count = option_len - op_count;
747:
748: ddns -> length = op_count;
749: ddns -> data = dmalloc (op_count, MDL);
750: if (!ddns -> data) {
751: log_error ("FAILOVER: no memory getting%s(%d)",
752: " DNS data ", op_count);
753:
754: /* Actually, NO_MEMORY, but if we lose here
755: we have to drop the connection. */
756: return ISC_R_PROTOCOLERROR;
757: }
758: omapi_connection_copyout (ddns -> data, c, op_count);
759: goto out;
760: }
761:
762: /* A zero for num_present means that any number of
763: elements can appear, so we have to figure out how
764: many we got from the length of the option, and then
765: fill out a failover_option structure describing the
766: data. */
767: op_size = ft_sizes [ft_options [option_code].type];
768:
769: /* Make sure that option data length is a multiple of the
770: size of the data type being sent. */
771: if (op_size > 1 && option_len % op_size) {
772: log_error ("FAILOVER: option_len %d not %s%d",
773: option_len, "multiple of ", op_size);
774: return ISC_R_PROTOCOLERROR;
775: }
776:
777: op_count = option_len / op_size;
778:
779: fo = ((failover_option_t *)
780: (((char *)link -> imsg) +
781: ft_options [option_code].offset));
782:
783: fo -> count = op_count;
784: fo -> data = dmalloc (option_len, MDL);
785: if (!fo -> data) {
786: log_error ("FAILOVER: no memory getting %s (%d)",
787: "option data", op_count);
788:
789: return ISC_R_PROTOCOLERROR;
790: }
791: op = fo -> data;
792: }
793:
794: /* For single-byte message values and multi-byte values that
795: don't need swapping, just read them in all at once. */
796: if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
797: omapi_connection_copyout ((unsigned char *)op, c, option_len);
798: link -> imsg_count += option_len;
799:
800: /*
801: * As of 3.1.0, many option codes were changed to conform to
802: * draft revision 12 (which alphabetized, then renumbered all
803: * the option codes without preserving the version option code
804: * nor bumping its value). As it turns out, the message codes
805: * for CONNECT and CONNECTACK turn out the same, so it tries
806: * its darndest to connect, and falls short (when TLS_REQUEST
807: * comes up size 2 rather than size 1 as draft revision 12 also
808: * mandates).
809: *
810: * The VENDOR_CLASS code in 3.0.x was 11, which is now the HBA
811: * code. Both work out to be arbitrarily long text-or-byte
812: * strings, so they pass parsing.
813: *
814: * Note that it is possible (or intentional), if highly
815: * improbable, for the HBA bit array to exactly match
816: * isc-V3.0.x. Warning here is not an issue; if it really is
817: * 3.0.x, there will be a protocol error later on. If it isn't
818: * actually 3.0.x, then I guess the lucky user will have to
819: * live with a weird warning.
820: */
821: if ((option_code == 11) && (option_len > 9) &&
822: (strncmp((const char *)op, "isc-V3.0.", 9) == 0)) {
823: log_error("WARNING: failover as of versions 3.1.0 and "
824: "on are not reverse compatible with "
825: "versions 3.0.x.");
826: }
827:
828: goto out;
829: }
830:
831: /* For values that require swapping, read them in one at a time
832: using routines that swap bytes. */
833: for (i = 0; i < op_count; i++) {
834: switch (ft_options [option_code].type) {
835: case FT_UINT32:
836: omapi_connection_get_uint32 (c, (u_int32_t *)op);
837: op += 4;
838: link -> imsg_count += 4;
839: break;
840:
841: case FT_UINT16:
842: omapi_connection_get_uint16 (c, (u_int16_t *)op);
843: op += 2;
844: link -> imsg_count += 2;
845: break;
846:
847: default:
848: /* Everything else should have been handled
849: already. */
850: log_error ("FAILOVER: option %s: bad type %d",
851: ft_options [option_code].name,
852: ft_options [option_code].type);
853: return ISC_R_PROTOCOLERROR;
854: }
855: }
856: out:
857: /* Remember that we got this option. */
858: link -> imsg -> options_present |= ft_options [option_code].bit;
859: return ISC_R_SUCCESS;
860: }
861:
862: isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
863: omapi_object_t *id,
864: omapi_data_string_t *name,
865: omapi_typed_data_t *value)
866: {
867: if (h -> type != omapi_type_protocol)
868: return ISC_R_INVALIDARG;
869:
870: /* Never valid to set these. */
871: if (!omapi_ds_strcmp (name, "link-port") ||
872: !omapi_ds_strcmp (name, "link-name") ||
873: !omapi_ds_strcmp (name, "link-state"))
874: return ISC_R_NOPERM;
875:
876: if (h -> inner && h -> inner -> type -> set_value)
877: return (*(h -> inner -> type -> set_value))
878: (h -> inner, id, name, value);
879: return ISC_R_NOTFOUND;
880: }
881:
882: isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
883: omapi_object_t *id,
884: omapi_data_string_t *name,
885: omapi_value_t **value)
886: {
887: dhcp_failover_link_t *link;
888:
889: if (h -> type != omapi_type_protocol)
890: return ISC_R_INVALIDARG;
891: link = (dhcp_failover_link_t *)h;
892:
893: if (!omapi_ds_strcmp (name, "link-port")) {
894: return omapi_make_int_value (value, name,
895: (int)link -> peer_port, MDL);
896: } else if (!omapi_ds_strcmp (name, "link-state")) {
897: if (link -> state < 0 ||
898: link -> state >= dhcp_flink_state_max)
899: return omapi_make_string_value (value, name,
900: "invalid link state",
901: MDL);
902: return omapi_make_string_value
903: (value, name,
904: dhcp_flink_state_names [link -> state], MDL);
905: }
906:
907: if (h -> inner && h -> inner -> type -> get_value)
908: return (*(h -> inner -> type -> get_value))
909: (h -> inner, id, name, value);
910: return ISC_R_NOTFOUND;
911: }
912:
913: isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
914: const char *file, int line)
915: {
916: dhcp_failover_link_t *link;
917: if (h -> type != dhcp_type_failover_link)
918: return ISC_R_INVALIDARG;
919: link = (dhcp_failover_link_t *)h;
920:
921: if (link -> peer_address)
922: option_cache_dereference (&link -> peer_address, file, line);
923: if (link -> imsg)
924: failover_message_dereference (&link -> imsg, file, line);
925: if (link -> state_object)
926: dhcp_failover_state_dereference (&link -> state_object,
927: file, line);
928: return ISC_R_SUCCESS;
929: }
930:
931: /* Write all the published values associated with the object through the
932: specified connection. */
933:
934: isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
935: omapi_object_t *id,
936: omapi_object_t *l)
937: {
938: dhcp_failover_link_t *link;
939: isc_result_t status;
940:
941: if (l -> type != dhcp_type_failover_link)
942: return ISC_R_INVALIDARG;
943: link = (dhcp_failover_link_t *)l;
944:
945: status = omapi_connection_put_name (c, "link-port");
946: if (status != ISC_R_SUCCESS)
947: return status;
948: status = omapi_connection_put_uint32 (c, sizeof (int));
949: if (status != ISC_R_SUCCESS)
950: return status;
951: status = omapi_connection_put_uint32 (c, link -> peer_port);
952: if (status != ISC_R_SUCCESS)
953: return status;
954:
955: status = omapi_connection_put_name (c, "link-state");
956: if (status != ISC_R_SUCCESS)
957: return status;
958: if (link -> state < 0 ||
959: link -> state >= dhcp_flink_state_max)
960: status = omapi_connection_put_string (c, "invalid link state");
961: else
962: status = (omapi_connection_put_string
963: (c, dhcp_flink_state_names [link -> state]));
964: if (status != ISC_R_SUCCESS)
965: return status;
966:
967: if (link -> inner && link -> inner -> type -> stuff_values)
968: return (*(link -> inner -> type -> stuff_values)) (c, id,
969: link -> inner);
970: return ISC_R_SUCCESS;
971: }
972:
973: /* Set up a listener for the omapi protocol. The handle stored points to
974: a listener object, not a protocol object. */
975:
976: isc_result_t dhcp_failover_listen (omapi_object_t *h)
977: {
978: isc_result_t status;
979: dhcp_failover_listener_t *obj, *l;
980: omapi_value_t *value = (omapi_value_t *)0;
981: omapi_addr_t local_addr;
982: unsigned long port;
983:
984: status = omapi_get_value_str (h, (omapi_object_t *)0,
985: "local-port", &value);
986: if (status != ISC_R_SUCCESS)
987: return status;
988: if (!value -> value) {
989: omapi_value_dereference (&value, MDL);
990: return ISC_R_INVALIDARG;
991: }
992:
993: status = omapi_get_int_value (&port, value -> value);
994: omapi_value_dereference (&value, MDL);
995: if (status != ISC_R_SUCCESS)
996: return status;
997: local_addr.port = port;
998:
999: status = omapi_get_value_str (h, (omapi_object_t *)0,
1000: "local-address", &value);
1001: if (status != ISC_R_SUCCESS)
1002: return status;
1003: if (!value -> value) {
1004: nogood:
1005: omapi_value_dereference (&value, MDL);
1006: return ISC_R_INVALIDARG;
1007: }
1008:
1009: if (value -> value -> type != omapi_datatype_data ||
1010: value -> value -> u.buffer.len != sizeof (struct in_addr))
1011: goto nogood;
1012:
1013: memcpy (local_addr.address, value -> value -> u.buffer.value,
1014: value -> value -> u.buffer.len);
1015: local_addr.addrlen = value -> value -> u.buffer.len;
1016: local_addr.addrtype = AF_INET;
1017:
1018: omapi_value_dereference (&value, MDL);
1019:
1020: /* Are we already listening on this port and address? */
1021: for (l = failover_listeners; l; l = l -> next) {
1022: if (l -> address.port == local_addr.port &&
1023: l -> address.addrtype == local_addr.addrtype &&
1024: l -> address.addrlen == local_addr.addrlen &&
1025: !memcmp (l -> address.address, local_addr.address,
1026: local_addr.addrlen))
1027: break;
1028: }
1029: /* Already listening. */
1030: if (l)
1031: return ISC_R_SUCCESS;
1032:
1033: obj = (dhcp_failover_listener_t *)0;
1034: status = dhcp_failover_listener_allocate (&obj, MDL);
1035: if (status != ISC_R_SUCCESS)
1036: return status;
1037: obj -> address = local_addr;
1038:
1039: status = omapi_listen_addr ((omapi_object_t *)obj, &obj -> address, 1);
1040: if (status != ISC_R_SUCCESS)
1041: return status;
1042:
1043: status = omapi_object_reference (&h -> outer,
1044: (omapi_object_t *)obj, MDL);
1045: if (status != ISC_R_SUCCESS) {
1046: dhcp_failover_listener_dereference (&obj, MDL);
1047: return status;
1048: }
1049: status = omapi_object_reference (&obj -> inner, h, MDL);
1050: if (status != ISC_R_SUCCESS) {
1051: dhcp_failover_listener_dereference (&obj, MDL);
1052: return status;
1053: }
1054:
1055: /* Put this listener on the list. */
1056: if (failover_listeners) {
1057: dhcp_failover_listener_reference (&obj -> next,
1058: failover_listeners, MDL);
1059: dhcp_failover_listener_dereference (&failover_listeners, MDL);
1060: }
1061: dhcp_failover_listener_reference (&failover_listeners, obj, MDL);
1062:
1063: return dhcp_failover_listener_dereference (&obj, MDL);
1064: }
1065:
1066: /* Signal handler for protocol listener - if we get a connect signal,
1067: create a new protocol connection, otherwise pass the signal down. */
1068:
1069: isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
1070: const char *name, va_list ap)
1071: {
1072: isc_result_t status;
1073: omapi_connection_object_t *c;
1074: dhcp_failover_link_t *obj;
1075: dhcp_failover_listener_t *p;
1076: dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
1077:
1078: if (!o || o -> type != dhcp_type_failover_listener)
1079: return ISC_R_INVALIDARG;
1080: p = (dhcp_failover_listener_t *)o;
1081:
1082: /* Not a signal we recognize? */
1083: if (strcmp (name, "connect")) {
1084: if (p -> inner && p -> inner -> type -> signal_handler)
1085: return (*(p -> inner -> type -> signal_handler))
1086: (p -> inner, name, ap);
1087: return ISC_R_NOTFOUND;
1088: }
1089:
1090: c = va_arg (ap, omapi_connection_object_t *);
1091: if (!c || c -> type != omapi_type_connection)
1092: return ISC_R_INVALIDARG;
1093:
1094: /* See if we can find a failover_state object that
1095: matches this connection. */
1096: for (s = failover_states; s; s = s -> next) {
1097: if (dhcp_failover_state_match
1098: (s, (u_int8_t *)&c -> remote_addr.sin_addr,
1099: sizeof c -> remote_addr.sin_addr)) {
1100: state = s;
1101: break;
1102: }
1103: }
1104: if (!state) {
1105: log_info ("failover: listener: no matching state");
1106: omapi_disconnect ((omapi_object_t *)c, 1);
1107: return(ISC_R_NOTFOUND);
1108: }
1109:
1110: obj = (dhcp_failover_link_t *)0;
1111: status = dhcp_failover_link_allocate (&obj, MDL);
1112: if (status != ISC_R_SUCCESS)
1113: return status;
1114: obj -> peer_port = ntohs (c -> remote_addr.sin_port);
1115:
1116: status = omapi_object_reference (&obj -> outer,
1117: (omapi_object_t *)c, MDL);
1118: if (status != ISC_R_SUCCESS) {
1119: lose:
1120: dhcp_failover_link_dereference (&obj, MDL);
1121: log_info ("failover: listener: picayune failure.");
1122: omapi_disconnect ((omapi_object_t *)c, 1);
1123: return status;
1124: }
1125:
1126: status = omapi_object_reference (&c -> inner,
1127: (omapi_object_t *)obj, MDL);
1128: if (status != ISC_R_SUCCESS)
1129: goto lose;
1130:
1131: status = dhcp_failover_state_reference (&obj -> state_object,
1132: state, MDL);
1133: if (status != ISC_R_SUCCESS)
1134: goto lose;
1135:
1136: omapi_signal_in ((omapi_object_t *)obj, "connect");
1137:
1138: return dhcp_failover_link_dereference (&obj, MDL);
1139: }
1140:
1141: isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
1142: omapi_object_t *id,
1143: omapi_data_string_t *name,
1144: omapi_typed_data_t *value)
1145: {
1146: if (h -> type != dhcp_type_failover_listener)
1147: return ISC_R_INVALIDARG;
1148:
1149: if (h -> inner && h -> inner -> type -> set_value)
1150: return (*(h -> inner -> type -> set_value))
1151: (h -> inner, id, name, value);
1152: return ISC_R_NOTFOUND;
1153: }
1154:
1155: isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
1156: omapi_object_t *id,
1157: omapi_data_string_t *name,
1158: omapi_value_t **value)
1159: {
1160: if (h -> type != dhcp_type_failover_listener)
1161: return ISC_R_INVALIDARG;
1162:
1163: if (h -> inner && h -> inner -> type -> get_value)
1164: return (*(h -> inner -> type -> get_value))
1165: (h -> inner, id, name, value);
1166: return ISC_R_NOTFOUND;
1167: }
1168:
1169: isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
1170: const char *file, int line)
1171: {
1172: dhcp_failover_listener_t *l;
1173:
1174: if (h -> type != dhcp_type_failover_listener)
1175: return ISC_R_INVALIDARG;
1176: l = (dhcp_failover_listener_t *)h;
1177: if (l -> next)
1178: dhcp_failover_listener_dereference (&l -> next, file, line);
1179:
1180: return ISC_R_SUCCESS;
1181: }
1182:
1183: /* Write all the published values associated with the object through the
1184: specified connection. */
1185:
1186: isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
1187: omapi_object_t *id,
1188: omapi_object_t *p)
1189: {
1190: if (p -> type != dhcp_type_failover_listener)
1191: return ISC_R_INVALIDARG;
1192:
1193: if (p -> inner && p -> inner -> type -> stuff_values)
1194: return (*(p -> inner -> type -> stuff_values)) (c, id,
1195: p -> inner);
1196: return ISC_R_SUCCESS;
1197: }
1198:
1199: /* Set up master state machine for the failover protocol. */
1200:
1201: isc_result_t dhcp_failover_register (omapi_object_t *h)
1202: {
1203: isc_result_t status;
1204: dhcp_failover_state_t *obj;
1205: unsigned long port;
1206: omapi_value_t *value = (omapi_value_t *)0;
1207:
1208: status = omapi_get_value_str (h, (omapi_object_t *)0,
1209: "local-port", &value);
1210: if (status != ISC_R_SUCCESS)
1211: return status;
1212: if (!value -> value) {
1213: omapi_value_dereference (&value, MDL);
1214: return ISC_R_INVALIDARG;
1215: }
1216:
1217: status = omapi_get_int_value (&port, value -> value);
1218: omapi_value_dereference (&value, MDL);
1219: if (status != ISC_R_SUCCESS)
1220: return status;
1221:
1222: obj = (dhcp_failover_state_t *)0;
1223: dhcp_failover_state_allocate (&obj, MDL);
1224: obj -> me.port = port;
1225:
1226: status = omapi_listen ((omapi_object_t *)obj, port, 1);
1227: if (status != ISC_R_SUCCESS) {
1228: dhcp_failover_state_dereference (&obj, MDL);
1229: return status;
1230: }
1231:
1232: status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
1233: MDL);
1234: if (status != ISC_R_SUCCESS) {
1235: dhcp_failover_state_dereference (&obj, MDL);
1236: return status;
1237: }
1238: status = omapi_object_reference (&obj -> inner, h, MDL);
1239: dhcp_failover_state_dereference (&obj, MDL);
1240: return status;
1241: }
1242:
1243: /* Signal handler for protocol state machine. */
1244:
1245: isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
1246: const char *name, va_list ap)
1247: {
1248: isc_result_t status;
1249: dhcp_failover_state_t *state;
1250: dhcp_failover_link_t *link;
1251: struct timeval tv;
1252:
1253: if (!o || o -> type != dhcp_type_failover_state)
1254: return ISC_R_INVALIDARG;
1255: state = (dhcp_failover_state_t *)o;
1256:
1257: /* Not a signal we recognize? */
1258: if (strcmp (name, "disconnect") &&
1259: strcmp (name, "message")) {
1260: if (state -> inner && state -> inner -> type -> signal_handler)
1261: return (*(state -> inner -> type -> signal_handler))
1262: (state -> inner, name, ap);
1263: return ISC_R_NOTFOUND;
1264: }
1265:
1266: /* Handle connect signals by seeing what state we're in
1267: and potentially doing a state transition. */
1268: if (!strcmp (name, "disconnect")) {
1269: link = va_arg (ap, dhcp_failover_link_t *);
1270:
1271: dhcp_failover_link_dereference (&state -> link_to_peer, MDL);
1272: dhcp_failover_state_transition (state, "disconnect");
1273: if (state -> i_am == primary) {
1274: #if defined (DEBUG_FAILOVER_TIMING)
1275: log_info ("add_timeout +90 %s",
1276: "dhcp_failover_reconnect");
1277: #endif
1278: tv . tv_sec = cur_time + 90;
1279: tv . tv_usec = 0;
1280: add_timeout (&tv, dhcp_failover_reconnect,
1281: state,
1282: (tvref_t)dhcp_failover_state_reference,
1283: (tvunref_t)
1284: dhcp_failover_state_dereference);
1285: }
1286: } else if (!strcmp (name, "message")) {
1287: link = va_arg (ap, dhcp_failover_link_t *);
1288:
1289: if (link -> imsg -> type == FTM_CONNECT) {
1290: /* If we already have a link to the peer, it must be
1291: dead, so drop it.
1292: XXX Is this the right thing to do?
1293: XXX Probably not - what if both peers start at
1294: XXX the same time? */
1295: if (state -> link_to_peer) {
1296: dhcp_failover_send_connectack
1297: ((omapi_object_t *)link, state,
1298: FTR_DUP_CONNECTION,
1299: "already connected");
1300: omapi_disconnect (link -> outer, 1);
1301: return ISC_R_SUCCESS;
1302: }
1303: if (!(link -> imsg -> options_present & FTB_MCLT)) {
1304: dhcp_failover_send_connectack
1305: ((omapi_object_t *)link, state,
1306: FTR_INVALID_MCLT,
1307: "no MCLT provided");
1308: omapi_disconnect (link -> outer, 1);
1309: return ISC_R_SUCCESS;
1310: }
1311:
1312: dhcp_failover_link_reference (&state -> link_to_peer,
1313: link, MDL);
1314: status = (dhcp_failover_send_connectack
1315: ((omapi_object_t *)link, state, 0, 0));
1316: if (status != ISC_R_SUCCESS) {
1317: dhcp_failover_link_dereference
1318: (&state -> link_to_peer, MDL);
1319: log_info ("dhcp_failover_send_connectack: %s",
1320: isc_result_totext (status));
1321: omapi_disconnect (link -> outer, 1);
1322: return ISC_R_SUCCESS;
1323: }
1324: if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1325: state -> partner.max_flying_updates =
1326: link -> imsg -> max_unacked;
1327: if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
1328: state -> partner.max_response_delay =
1329: link -> imsg -> receive_timer;
1330: state -> mclt = link -> imsg -> mclt;
1331: dhcp_failover_send_state (state);
1332: cancel_timeout (dhcp_failover_link_startup_timeout,
1333: link);
1334: } else if (link -> imsg -> type == FTM_CONNECTACK) {
1335: const char *errmsg;
1336: char errbuf[1024];
1337: int reason;
1338:
1339: cancel_timeout (dhcp_failover_link_startup_timeout,
1340: link);
1341:
1342: if (!(link->imsg->options_present &
1343: FTB_RELATIONSHIP_NAME)) {
1344: errmsg = "missing relationship-name";
1345: reason = FTR_INVALID_PARTNER;
1346: goto badconnectack;
1347: }
1348:
1349: if (link->imsg->options_present & FTB_REJECT_REASON) {
1350: /* XXX: add message option to text output. */
1351: log_error ("Failover CONNECT to %s rejected: %s",
1352: state ? state->name : "unknown",
1353: (dhcp_failover_reject_reason_print
1354: (link -> imsg -> reject_reason)));
1355: /* XXX print message from peer if peer sent message. */
1356: omapi_disconnect (link -> outer, 1);
1357: return ISC_R_SUCCESS;
1358: }
1359:
1360: if (!dhcp_failover_state_match_by_name(state,
1361: &link->imsg->relationship_name)) {
1362: /* XXX: Overflow results in log truncation, safe. */
1363: snprintf(errbuf, sizeof(errbuf), "remote failover "
1364: "relationship name %.*s does not match",
1365: (int)link->imsg->relationship_name.count,
1366: link->imsg->relationship_name.data);
1367: errmsg = errbuf;
1368: reason = FTR_INVALID_PARTNER;
1369: badconnectack:
1370: log_error("Failover CONNECTACK from %s: %s",
1371: state->name, errmsg);
1372: dhcp_failover_send_disconnect ((omapi_object_t *)link,
1373: reason, errmsg);
1374: omapi_disconnect (link -> outer, 0);
1375: return ISC_R_SUCCESS;
1376: }
1377:
1378: if (state -> link_to_peer) {
1379: errmsg = "already connected";
1380: reason = FTR_DUP_CONNECTION;
1381: goto badconnectack;
1382: }
1383:
1384: if ((cur_time > link -> imsg -> time &&
1385: cur_time - link -> imsg -> time > 60) ||
1386: (cur_time < link -> imsg -> time &&
1387: link -> imsg -> time - cur_time > 60)) {
1388: errmsg = "time offset too large";
1389: reason = FTR_TIMEMISMATCH;
1390: goto badconnectack;
1391: }
1392:
1393: dhcp_failover_link_reference (&state -> link_to_peer,
1394: link, MDL);
1395: #if 0
1396: /* XXX This is probably the right thing to do, but
1397: XXX for release three, to make the smallest possible
1398: XXX change, we are doing this when the peer state
1399: XXX changes instead. */
1400: if (state -> me.state == startup)
1401: dhcp_failover_set_state (state,
1402: state -> saved_state);
1403: else
1404: #endif
1405: dhcp_failover_send_state (state);
1406:
1407: if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1408: state -> partner.max_flying_updates =
1409: link -> imsg -> max_unacked;
1410: if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
1411: state -> partner.max_response_delay =
1412: link -> imsg -> receive_timer;
1413: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1414: log_info ("add_timeout +%d %s",
1415: (int)state -> partner.max_response_delay / 3,
1416: "dhcp_failover_send_contact");
1417: #endif
1418: tv . tv_sec = cur_time +
1419: (int)state -> partner.max_response_delay / 3;
1420: tv . tv_usec = 0;
1421: add_timeout (&tv,
1422: dhcp_failover_send_contact, state,
1423: (tvref_t)dhcp_failover_state_reference,
1424: (tvunref_t)dhcp_failover_state_dereference);
1425: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1426: log_info ("add_timeout +%d %s",
1427: (int)state -> me.max_response_delay,
1428: "dhcp_failover_timeout");
1429: #endif
1430: tv . tv_sec = cur_time +
1431: (int)state -> me.max_response_delay;
1432: tv . tv_usec = 0;
1433: add_timeout (&tv,
1434: dhcp_failover_timeout, state,
1435: (tvref_t)dhcp_failover_state_reference,
1436: (tvunref_t)dhcp_failover_state_dereference);
1437: } else if (link -> imsg -> type == FTM_DISCONNECT) {
1438: if (link -> imsg -> reject_reason) {
1439: log_error ("Failover DISCONNECT from %s: %s",
1440: state ? state->name : "unknown",
1441: (dhcp_failover_reject_reason_print
1442: (link -> imsg -> reject_reason)));
1443: }
1444: omapi_disconnect (link -> outer, 1);
1445: } else if (link -> imsg -> type == FTM_BNDUPD) {
1446: dhcp_failover_process_bind_update (state,
1447: link -> imsg);
1448: } else if (link -> imsg -> type == FTM_BNDACK) {
1449: dhcp_failover_process_bind_ack (state, link -> imsg);
1450: } else if (link -> imsg -> type == FTM_UPDREQ) {
1451: dhcp_failover_process_update_request (state,
1452: link -> imsg);
1453: } else if (link -> imsg -> type == FTM_UPDREQALL) {
1454: dhcp_failover_process_update_request_all
1455: (state, link -> imsg);
1456: } else if (link -> imsg -> type == FTM_UPDDONE) {
1457: dhcp_failover_process_update_done (state,
1458: link -> imsg);
1459: } else if (link -> imsg -> type == FTM_POOLREQ) {
1460: dhcp_failover_pool_reqbalance(state);
1461: } else if (link -> imsg -> type == FTM_POOLRESP) {
1462: log_info ("pool response: %ld leases",
1463: (unsigned long)
1464: link -> imsg -> addresses_transferred);
1465: } else if (link -> imsg -> type == FTM_STATE) {
1466: dhcp_failover_peer_state_changed (state,
1467: link -> imsg);
1468: }
1469:
1470: /* Add a timeout so that if the partner doesn't send
1471: another message for the maximum transmit idle time
1472: plus a grace of one second, we close the
1473: connection. */
1474: if (state -> link_to_peer &&
1475: state -> link_to_peer == link &&
1476: state -> link_to_peer -> state != dhcp_flink_disconnected)
1477: {
1478: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1479: log_info ("add_timeout +%d %s",
1480: (int)state -> me.max_response_delay,
1481: "dhcp_failover_timeout");
1482: #endif
1483: tv . tv_sec = cur_time +
1484: (int)state -> me.max_response_delay;
1485: tv . tv_usec = 0;
1486: add_timeout (&tv,
1487: dhcp_failover_timeout, state,
1488: (tvref_t)dhcp_failover_state_reference,
1489: (tvunref_t)dhcp_failover_state_dereference);
1490:
1491: }
1492: }
1493:
1494: /* Handle all the events we care about... */
1495: return ISC_R_SUCCESS;
1496: }
1497:
1498: isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
1499: const char *name)
1500: {
1501: isc_result_t status;
1502:
1503: /* XXX Check these state transitions against the spec! */
1504: if (!strcmp (name, "disconnect")) {
1505: if (state -> link_to_peer) {
1506: log_info ("peer %s: disconnected", state -> name);
1507: if (state -> link_to_peer -> state_object)
1508: dhcp_failover_state_dereference
1509: (&state -> link_to_peer -> state_object, MDL);
1510: dhcp_failover_link_dereference (&state -> link_to_peer,
1511: MDL);
1512: }
1513: cancel_timeout (dhcp_failover_send_contact, state);
1514: cancel_timeout (dhcp_failover_timeout, state);
1515: cancel_timeout (dhcp_failover_startup_timeout, state);
1516:
1517: switch (state -> me.state == startup ?
1518: state -> saved_state : state -> me.state) {
1519: /* In these situations, we remain in the current
1520: * state, or if in startup enter those states.
1521: */
1522: case communications_interrupted:
1523: case conflict_done:
1524: case partner_down:
1525: case paused:
1526: case recover:
1527: case recover_done:
1528: case recover_wait:
1529: case resolution_interrupted:
1530: case shut_down:
1531: /* Already in the right state? */
1532: if (state -> me.state == startup)
1533: return (dhcp_failover_set_state
1534: (state, state -> saved_state));
1535: return ISC_R_SUCCESS;
1536:
1537: case potential_conflict:
1538: return dhcp_failover_set_state
1539: (state, resolution_interrupted);
1540:
1541: case normal:
1542: return dhcp_failover_set_state
1543: (state, communications_interrupted);
1544:
1545: case unknown_state:
1546: return dhcp_failover_set_state
1547: (state, resolution_interrupted);
1548:
1549: default:
1550: log_fatal("Impossible case at %s:%d.", MDL);
1551: break; /* can't happen. */
1552: }
1553: } else if (!strcmp (name, "connect")) {
1554: switch (state -> me.state) {
1555: case communications_interrupted:
1556: status = dhcp_failover_set_state (state, normal);
1557: dhcp_failover_send_updates (state);
1558: return status;
1559:
1560: case resolution_interrupted:
1561: return dhcp_failover_set_state (state,
1562: potential_conflict);
1563:
1564: case conflict_done:
1565: case partner_down:
1566: case potential_conflict:
1567: case normal:
1568: case recover:
1569: case shut_down:
1570: case paused:
1571: case unknown_state:
1572: case recover_done:
1573: case startup:
1574: case recover_wait:
1575: return dhcp_failover_send_state (state);
1576:
1577: default:
1578: log_fatal("Impossible case at %s:%d.", MDL);
1579: break;
1580: }
1581: } else if (!strcmp (name, "startup")) {
1582: dhcp_failover_set_state (state, startup);
1583: return ISC_R_SUCCESS;
1584: } else if (!strcmp (name, "connect-timeout")) {
1585: switch (state -> me.state) {
1586: case communications_interrupted:
1587: case partner_down:
1588: case resolution_interrupted:
1589: case paused:
1590: case startup:
1591: case shut_down:
1592: case conflict_done:
1593: return ISC_R_SUCCESS;
1594:
1595: case normal:
1596: case recover:
1597: case recover_wait:
1598: case recover_done:
1599: case unknown_state:
1600: return dhcp_failover_set_state
1601: (state, communications_interrupted);
1602:
1603: case potential_conflict:
1604: return dhcp_failover_set_state
1605: (state, resolution_interrupted);
1606:
1607: default:
1608: log_fatal("Impossible case at %s:%d.", MDL);
1609: break;
1610: }
1611: }
1612: return ISC_R_INVALIDARG;
1613: }
1614:
1615: isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state)
1616: {
1617: switch (state -> me.state) {
1618: case unknown_state:
1619: state -> service_state = not_responding;
1620: state -> nrr = " (my state unknown)";
1621: break;
1622:
1623: case partner_down:
1624: state -> service_state = service_partner_down;
1625: state -> nrr = "";
1626: break;
1627:
1628: case normal:
1629: state -> service_state = cooperating;
1630: state -> nrr = "";
1631: break;
1632:
1633: case communications_interrupted:
1634: state -> service_state = not_cooperating;
1635: state -> nrr = "";
1636: break;
1637:
1638: case resolution_interrupted:
1639: case potential_conflict:
1640: case conflict_done:
1641: state -> service_state = not_responding;
1642: state -> nrr = " (resolving conflicts)";
1643: break;
1644:
1645: case recover:
1646: state -> service_state = not_responding;
1647: state -> nrr = " (recovering)";
1648: break;
1649:
1650: case shut_down:
1651: state -> service_state = not_responding;
1652: state -> nrr = " (shut down)";
1653: break;
1654:
1655: case paused:
1656: state -> service_state = not_responding;
1657: state -> nrr = " (paused)";
1658: break;
1659:
1660: case recover_wait:
1661: state -> service_state = not_responding;
1662: state -> nrr = " (recover wait)";
1663: break;
1664:
1665: case recover_done:
1666: state -> service_state = not_responding;
1667: state -> nrr = " (recover done)";
1668: break;
1669:
1670: case startup:
1671: state -> service_state = service_startup;
1672: state -> nrr = " (startup)";
1673: break;
1674:
1675: default:
1676: log_fatal("Impossible case at %s:%d.\n", MDL);
1677: break;
1678: }
1679:
1680: /* Some peer states can require us not to respond, even if our
1681: state doesn't. */
1682: /* XXX hm. I suspect this isn't true anymore. */
1683: if (state -> service_state != not_responding) {
1684: switch (state -> partner.state) {
1685: case partner_down:
1686: state -> service_state = not_responding;
1687: state -> nrr = " (peer demands: recovering)";
1688: break;
1689:
1690: case potential_conflict:
1691: case conflict_done:
1692: case resolution_interrupted:
1693: state -> service_state = not_responding;
1694: state -> nrr = " (peer demands: resolving conflicts)";
1695: break;
1696:
1697: /* Other peer states don't affect our behaviour. */
1698: default:
1699: break;
1700: }
1701: }
1702:
1703: return ISC_R_SUCCESS;
1704: }
1705:
1706: isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
1707: enum failover_state new_state)
1708: {
1709: enum failover_state saved_state;
1710: TIME saved_stos;
1711: struct pool *p;
1712: struct shared_network *s;
1713: struct lease *l;
1714: struct timeval tv;
1715:
1716: /* If we're in certain states where we're sending updates, and the peer
1717: * state changes, we need to re-schedule any pending updates just to
1718: * be on the safe side. This results in retransmission.
1719: */
1720: switch (state -> me.state) {
1721: case normal:
1722: case potential_conflict:
1723: case partner_down:
1724: if (state -> ack_queue_tail) {
1725: struct lease *lp;
1726:
1727: /* Zap the flags. */
1728: for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
1729: lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
1730: ON_UPDATE_QUEUE);
1731:
1732: /* Now hook the ack queue to the beginning of the update
1733: queue. */
1734: if (state -> update_queue_head) {
1735: lease_reference (&state -> ack_queue_tail -> next_pending,
1736: state -> update_queue_head, MDL);
1737: lease_dereference (&state -> update_queue_head, MDL);
1738: }
1739: lease_reference (&state -> update_queue_head,
1740: state -> ack_queue_head, MDL);
1741: if (!state -> update_queue_tail) {
1742: #if defined (POINTER_DEBUG)
1743: if (state -> ack_queue_tail -> next_pending) {
1744: log_error ("next pending on ack queue tail.");
1745: abort ();
1746: }
1747: #endif
1748: lease_reference (&state -> update_queue_tail,
1749: state -> ack_queue_tail, MDL);
1750: }
1751: lease_dereference (&state -> ack_queue_tail, MDL);
1752: lease_dereference (&state -> ack_queue_head, MDL);
1753: state -> cur_unacked_updates = 0;
1754: }
1755: /* We will re-queue a timeout later, if applicable. */
1756: cancel_timeout (dhcp_failover_keepalive, state);
1757: break;
1758:
1759: default:
1760: break;
1761: }
1762:
1763: /* Tentatively make the transition. */
1764: saved_state = state -> me.state;
1765: saved_stos = state -> me.stos;
1766:
1767: /* Keep the old stos if we're going into recover_wait or if we're
1768: coming into or out of startup. */
1769: if (new_state != recover_wait && new_state != startup &&
1770: saved_state != startup)
1771: state -> me.stos = cur_time;
1772:
1773: /* If we're in shutdown, peer is in partner_down, and we're moving
1774: to recover, we can skip waiting for MCLT to expire. This happens
1775: when a server is moved administratively into shutdown prior to
1776: actually shutting down. Of course, if there are any updates
1777: pending we can't actually do this. */
1778: if (new_state == recover && saved_state == shut_down &&
1779: state -> partner.state == partner_down &&
1780: !state -> update_queue_head && !state -> ack_queue_head)
1781: state -> me.stos = cur_time - state -> mclt;
1782:
1783: state -> me.state = new_state;
1784: if (new_state == startup && saved_state != startup)
1785: state -> saved_state = saved_state;
1786:
1787: /* If we can't record the new state, we can't make a state transition. */
1788: if (!write_failover_state (state) || !commit_leases ()) {
1789: log_error ("Unable to record current failover state for %s",
1790: state -> name);
1791: state -> me.state = saved_state;
1792: state -> me.stos = saved_stos;
1793: return ISC_R_IOERROR;
1794: }
1795:
1796: log_info ("failover peer %s: I move from %s to %s",
1797: state -> name, dhcp_failover_state_name_print (saved_state),
1798: dhcp_failover_state_name_print (state -> me.state));
1799:
1800: /* If we were in startup and we just left it, cancel the timeout. */
1801: if (new_state != startup && saved_state == startup)
1802: cancel_timeout (dhcp_failover_startup_timeout, state);
1803:
1804: /* Set our service state. */
1805: dhcp_failover_set_service_state (state);
1806:
1807: /* Tell the peer about it. */
1808: if (state -> link_to_peer)
1809: dhcp_failover_send_state (state);
1810:
1811: switch (new_state) {
1812: case normal:
1813: /* Upon entering normal state, the server is expected to retransmit
1814: * all pending binding updates. This is a good opportunity to
1815: * rebalance the pool (potentially making new pending updates),
1816: * which also schedules the next pool rebalance.
1817: */
1818: dhcp_failover_pool_balance(state);
1819: dhcp_failover_generate_update_queue(state, 0);
1820:
1821: if (state->update_queue_tail != NULL) {
1822: dhcp_failover_send_updates(state);
1823: log_info("Sending updates to %s.", state->name);
1824: }
1825:
1826: break;
1827:
1828: case potential_conflict:
1829: if (state -> i_am == primary)
1830: dhcp_failover_send_update_request (state);
1831: break;
1832:
1833: case startup:
1834: #if defined (DEBUG_FAILOVER_TIMING)
1835: log_info ("add_timeout +15 %s",
1836: "dhcp_failover_startup_timeout");
1837: #endif
1838: tv . tv_sec = cur_time + 15;
1839: tv . tv_usec = 0;
1840: add_timeout (&tv,
1841: dhcp_failover_startup_timeout,
1842: state,
1843: (tvref_t)omapi_object_reference,
1844: (tvunref_t)
1845: omapi_object_dereference);
1846: break;
1847:
1848: /* If we come back in recover_wait and there's still waiting
1849: to do, set a timeout. */
1850: case recover_wait:
1851: if (state -> me.stos + state -> mclt > cur_time) {
1852: #if defined (DEBUG_FAILOVER_TIMING)
1853: log_info ("add_timeout +%d %s",
1854: (int)(cur_time -
1855: state -> me.stos + state -> mclt),
1856: "dhcp_failover_startup_timeout");
1857: #endif
1858: tv . tv_sec = (int)(state -> me.stos + state -> mclt);
1859: tv . tv_usec = 0;
1860: add_timeout (&tv,
1861: dhcp_failover_recover_done,
1862: state,
1863: (tvref_t)omapi_object_reference,
1864: (tvunref_t)
1865: omapi_object_dereference);
1866: } else
1867: dhcp_failover_recover_done (state);
1868: break;
1869:
1870: case recover:
1871: /* XXX: We're supposed to calculate if updreq or updreqall is
1872: * needed. In theory, we should only have to updreqall if we
1873: * are positive we lost our stable storage.
1874: */
1875: if (state -> link_to_peer)
1876: dhcp_failover_send_update_request_all (state);
1877: break;
1878:
1879: case partner_down:
1880: /* For every expired lease, set a timeout for it to become free. */
1881: for (s = shared_networks; s; s = s -> next) {
1882: for (p = s -> pools; p; p = p -> next) {
1883: if (p -> failover_peer == state) {
1884: for (l = p->expired ; l ; l = l->next) {
1885: l->tsfp = state->me.stos + state->mclt;
1886: l->sort_time = (l->tsfp > l->ends) ?
1887: l->tsfp : l->ends;
1888: }
1889: if (p->expired &&
1890: (p->expired->sort_time < p->next_event_time)) {
1891:
1892: p->next_event_time = p->expired->sort_time;
1893: #if defined (DEBUG_FAILOVER_TIMING)
1894: log_info ("add_timeout +%d %s",
1895: (int)(cur_time - p->next_event_time),
1896: "pool_timer");
1897: #endif
1898: tv.tv_sec = p->next_event_time;
1899: tv.tv_usec = 0;
1900: add_timeout(&tv, pool_timer, p,
1901: (tvref_t)pool_reference,
1902: (tvunref_t)pool_dereference);
1903: }
1904: }
1905: }
1906: }
1907: break;
1908:
1909:
1910: default:
1911: break;
1912: }
1913:
1914: return ISC_R_SUCCESS;
1915: }
1916:
1917: isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state,
1918: failover_message_t *msg)
1919: {
1920: enum failover_state previous_state = state -> partner.state;
1921: enum failover_state new_state;
1922: int startupp;
1923:
1924: new_state = msg -> server_state;
1925: startupp = (msg -> server_flags & FTF_SERVER_STARTUP) ? 1 : 0;
1926:
1927: if (state -> partner.state == new_state && state -> me.state) {
1928: switch (state -> me.state) {
1929: case startup:
1930: dhcp_failover_set_state (state, state -> saved_state);
1931: return ISC_R_SUCCESS;
1932:
1933: case unknown_state:
1934: case normal:
1935: case potential_conflict:
1936: case recover_done:
1937: case shut_down:
1938: case paused:
1939: case recover_wait:
1940: return ISC_R_SUCCESS;
1941:
1942: /* If we get a peer state change when we're
1943: disconnected, we always process it. */
1944: case partner_down:
1945: case communications_interrupted:
1946: case resolution_interrupted:
1947: case recover:
1948: case conflict_done:
1949: break;
1950:
1951: default:
1952: log_fatal("Impossible case at %s:%d.", MDL);
1953: break;
1954: }
1955: }
1956:
1957: state -> partner.state = new_state;
1958:
1959: log_info ("failover peer %s: peer moves from %s to %s",
1960: state -> name,
1961: dhcp_failover_state_name_print (previous_state),
1962: dhcp_failover_state_name_print (state -> partner.state));
1963:
1964: if (!write_failover_state (state) || !commit_leases ()) {
1965: /* This is bad, but it's not fatal. Of course, if we
1966: can't write to the lease database, we're not going to
1967: get much done anyway. */
1968: log_error ("Unable to record current failover state for %s",
1969: state -> name);
1970: }
1971:
1972: /* Quickly validate the new state as being one of the 13 known
1973: * states.
1974: */
1975: switch (new_state) {
1976: case unknown_state:
1977: case startup:
1978: case normal:
1979: case communications_interrupted:
1980: case partner_down:
1981: case potential_conflict:
1982: case recover:
1983: case paused:
1984: case shut_down:
1985: case recover_done:
1986: case resolution_interrupted:
1987: case conflict_done:
1988: case recover_wait:
1989: break;
1990:
1991: default:
1992: log_error("failover peer %s: Invalid state: %d", state->name,
1993: new_state);
1994: dhcp_failover_set_state(state, shut_down);
1995: return ISC_R_SUCCESS;
1996: }
1997:
1998: /* Do any state transitions that are required as a result of the
1999: peer's state transition. */
2000:
2001: switch (state -> me.state == startup ?
2002: state -> saved_state : state -> me.state) {
2003: case normal:
2004: switch (new_state) {
2005: case normal:
2006: dhcp_failover_state_pool_check (state);
2007: break;
2008:
2009: case partner_down:
2010: if (state -> me.state == startup)
2011: dhcp_failover_set_state (state, recover);
2012: else
2013: dhcp_failover_set_state (state,
2014: potential_conflict);
2015: break;
2016:
2017: case potential_conflict:
2018: case resolution_interrupted:
2019: case conflict_done:
2020: /* None of these transitions should ever occur. */
2021: log_error("Peer %s: Invalid state transition %s "
2022: "to %s.", state->name,
2023: dhcp_failover_state_name_print(previous_state),
2024: dhcp_failover_state_name_print(new_state));
2025: dhcp_failover_set_state (state, shut_down);
2026: break;
2027:
2028: case recover:
2029: case shut_down:
2030: dhcp_failover_set_state (state, partner_down);
2031: break;
2032:
2033: case paused:
2034: dhcp_failover_set_state (state,
2035: communications_interrupted);
2036: break;
2037:
2038: default:
2039: /* recover_wait, recover_done, unknown_state, startup,
2040: * communications_interrupted
2041: */
2042: break;
2043: }
2044: break;
2045:
2046: case recover:
2047: switch (new_state) {
2048: case recover:
2049: log_info ("failover peer %s: requesting %s",
2050: state -> name, "full update from peer");
2051: /* Don't send updreqall if we're really in the
2052: startup state, because that will result in two
2053: being sent. */
2054: if (state -> me.state == recover)
2055: dhcp_failover_send_update_request_all (state);
2056: break;
2057:
2058: case potential_conflict:
2059: case resolution_interrupted:
2060: case conflict_done:
2061: case normal:
2062: dhcp_failover_set_state (state, potential_conflict);
2063: break;
2064:
2065: case partner_down:
2066: case communications_interrupted:
2067: /* We're supposed to send an update request at this
2068: point. */
2069: /* XXX we don't currently have code here to do any
2070: XXX clever detection of when we should send an
2071: XXX UPDREQALL message rather than an UPDREQ
2072: XXX message. What to do, what to do? */
2073: /* Currently when we enter recover state, no matter
2074: * the reason, we send an UPDREQALL. So, it makes
2075: * the most sense to stick to that until something
2076: * better is done.
2077: * Furthermore, we only want to send the update
2078: * request if we are not in startup state.
2079: */
2080: if (state -> me.state == recover)
2081: dhcp_failover_send_update_request_all (state);
2082: break;
2083:
2084: case shut_down:
2085: /* XXX We're not explicitly told what to do in this
2086: XXX case, but this transition is consistent with
2087: XXX what is elsewhere in the draft. */
2088: dhcp_failover_set_state (state, partner_down);
2089: break;
2090:
2091: /* We can't really do anything in this case. */
2092: default:
2093: /* paused, recover_done, recover_wait, unknown_state,
2094: * startup.
2095: */
2096: break;
2097: }
2098: break;
2099:
2100: case potential_conflict:
2101: switch (new_state) {
2102: case normal:
2103: /* This is an illegal transition. */
2104: log_error("Peer %s moves to normal during conflict "
2105: "resolution - panic, shutting down.",
2106: state->name);
2107: dhcp_failover_set_state(state, shut_down);
2108: break;
2109:
2110: case conflict_done:
2111: if (previous_state == potential_conflict)
2112: dhcp_failover_send_update_request (state);
2113: else
2114: log_error("Peer %s: Unexpected move to "
2115: "conflict-done.", state->name);
2116: break;
2117:
2118: case recover_done:
2119: case recover_wait:
2120: case potential_conflict:
2121: case partner_down:
2122: case communications_interrupted:
2123: case resolution_interrupted:
2124: case paused:
2125: break;
2126:
2127: case recover:
2128: dhcp_failover_set_state (state, recover);
2129: break;
2130:
2131: case shut_down:
2132: dhcp_failover_set_state (state, partner_down);
2133: break;
2134:
2135: default:
2136: /* unknown_state, startup */
2137: break;
2138: }
2139: break;
2140:
2141: case conflict_done:
2142: switch (new_state) {
2143: case normal:
2144: case shut_down:
2145: dhcp_failover_set_state(state, new_state);
2146: break;
2147:
2148: default:
2149: log_fatal("Peer %s: Invalid attempt to move from %s "
2150: "to %s while local state is conflict-done.",
2151: state->name,
2152: dhcp_failover_state_name_print(previous_state),
2153: dhcp_failover_state_name_print(new_state));
2154: }
2155: break;
2156:
2157: case partner_down:
2158: /* Take no action if other server is starting up. */
2159: if (startupp)
2160: break;
2161:
2162: switch (new_state) {
2163: /* This is where we should be. */
2164: case recover:
2165: case recover_wait:
2166: break;
2167:
2168: case recover_done:
2169: dhcp_failover_set_state (state, normal);
2170: break;
2171:
2172: case normal:
2173: case potential_conflict:
2174: case partner_down:
2175: case communications_interrupted:
2176: case resolution_interrupted:
2177: case conflict_done:
2178: dhcp_failover_set_state (state, potential_conflict);
2179: break;
2180:
2181: default:
2182: /* shut_down, paused, unknown_state, startup */
2183: break;
2184: }
2185: break;
2186:
2187: case communications_interrupted:
2188: switch (new_state) {
2189: case paused:
2190: /* Stick with the status quo. */
2191: break;
2192:
2193: /* If we're in communications-interrupted and an
2194: amnesic peer connects, go to the partner_down
2195: state immediately. */
2196: case recover:
2197: dhcp_failover_set_state (state, partner_down);
2198: break;
2199:
2200: case normal:
2201: case communications_interrupted:
2202: case recover_done:
2203: case recover_wait:
2204: /* XXX so we don't need to do this specially in
2205: XXX the CONNECT and CONNECTACK handlers. */
2206: dhcp_failover_send_updates (state);
2207: dhcp_failover_set_state (state, normal);
2208: break;
2209:
2210: case potential_conflict:
2211: case partner_down:
2212: case resolution_interrupted:
2213: case conflict_done:
2214: dhcp_failover_set_state (state, potential_conflict);
2215: break;
2216:
2217: case shut_down:
2218: dhcp_failover_set_state (state, partner_down);
2219: break;
2220:
2221: default:
2222: /* unknown_state, startup */
2223: break;
2224: }
2225: break;
2226:
2227: case resolution_interrupted:
2228: switch (new_state) {
2229: case normal:
2230: case recover:
2231: case potential_conflict:
2232: case partner_down:
2233: case communications_interrupted:
2234: case resolution_interrupted:
2235: case conflict_done:
2236: case recover_done:
2237: case recover_wait:
2238: dhcp_failover_set_state (state, potential_conflict);
2239: break;
2240:
2241: case shut_down:
2242: dhcp_failover_set_state (state, partner_down);
2243: break;
2244:
2245: default:
2246: /* paused, unknown_state, startup */
2247: break;
2248: }
2249: break;
2250:
2251: /* Make no transitions while in recover_wait...just wait. */
2252: case recover_wait:
2253: break;
2254:
2255: case recover_done:
2256: switch (new_state) {
2257: case recover_done:
2258: log_error("Both servers have entered recover-done!");
2259: case normal:
2260: dhcp_failover_set_state (state, normal);
2261: break;
2262:
2263: case shut_down:
2264: dhcp_failover_set_state (state, partner_down);
2265: break;
2266:
2267: default:
2268: /* potential_conflict, partner_down,
2269: * communications_interrupted, resolution_interrupted,
2270: * paused, recover, recover_wait, unknown_state,
2271: * startup.
2272: */
2273: break;
2274: }
2275: break;
2276:
2277: /* We are essentially dead in the water when we're in
2278: either shut_down or paused states, and do not do any
2279: automatic state transitions. */
2280: case shut_down:
2281: case paused:
2282: break;
2283:
2284: /* XXX: Shouldn't this be a fatal condition? */
2285: case unknown_state:
2286: break;
2287:
2288: default:
2289: log_fatal("Impossible condition at %s:%d.", MDL);
2290: break;
2291:
2292: }
2293:
2294: /* If we didn't make a transition out of startup as a result of
2295: the peer's state change, do it now as a result of the fact that
2296: we got a state change from the peer. */
2297: if (state -> me.state == startup && state -> saved_state != startup)
2298: dhcp_failover_set_state (state, state -> saved_state);
2299:
2300: /* For now, just set the service state based on the peer's state
2301: if necessary. */
2302: dhcp_failover_set_service_state (state);
2303:
2304: return ISC_R_SUCCESS;
2305: }
2306:
2307: /*
2308: * Balance operation manual entry; startup, entrance to normal state. No
2309: * sense sending a POOLREQ at this stage; the peer is likely about to schedule
2310: * their own rebalance event upon entering normal themselves.
2311: */
2312: static void
2313: dhcp_failover_pool_balance(dhcp_failover_state_t *state)
2314: {
2315: /* Cancel pending event. */
2316: cancel_timeout(dhcp_failover_pool_rebalance, state);
2317: state->sched_balance = 0;
2318:
2319: dhcp_failover_pool_dobalance(state, NULL);
2320: }
2321:
2322: /*
2323: * Balance operation entry from timer event. Once per timer interval is
2324: * the only time we want to emit POOLREQs (asserting an interrupt in our
2325: * peer).
2326: */
2327: void
2328: dhcp_failover_pool_rebalance(void *failover_state)
2329: {
2330: dhcp_failover_state_t *state;
2331: isc_boolean_t sendreq = ISC_FALSE;
2332:
2333: state = (dhcp_failover_state_t *)failover_state;
2334:
2335: /* Clear scheduled event indicator. */
2336: state->sched_balance = 0;
2337:
2338: if (dhcp_failover_pool_dobalance(state, &sendreq))
2339: dhcp_failover_send_updates(state);
2340:
2341: if (sendreq)
2342: dhcp_failover_send_poolreq(state);
2343: }
2344:
2345: /*
2346: * Balance operation entry from POOLREQ protocol message. Do not permit a
2347: * POOLREQ to send back a POOLREQ. Ping pong.
2348: */
2349: static void
2350: dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state)
2351: {
2352: int queued;
2353:
2354: /* Cancel pending event. */
2355: cancel_timeout(dhcp_failover_pool_rebalance, state);
2356: state->sched_balance = 0;
2357:
2358: queued = dhcp_failover_pool_dobalance(state, NULL);
2359:
2360: dhcp_failover_send_poolresp(state, queued);
2361:
2362: if (queued)
2363: dhcp_failover_send_updates(state);
2364: else
2365: log_info("peer %s: Got POOLREQ, answering negatively! "
2366: "Peer may be out of leases or database inconsistent.",
2367: state->name);
2368: }
2369:
2370: /*
2371: * Do the meat of the work common to all forms of pool rebalance. If the
2372: * caller deems it appropriate to transmit POOLREQ messages, it can use the
2373: * sendreq pointer to pass in the address of a FALSE value which this function
2374: * will conditionally turn TRUE if a POOLREQ is determined to be necessary.
2375: * A NULL value may be passed, in which case no action is taken.
2376: */
2377: static int
2378: dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
2379: isc_boolean_t *sendreq)
2380: {
2381: int lts, total, thresh, hold, panic, pass;
2382: int leases_queued = 0;
2383: struct lease *lp = (struct lease *)0;
2384: struct lease *next = (struct lease *)0;
2385: struct shared_network *s;
2386: struct pool *p;
2387: binding_state_t peer_lease_state;
1.1.1.1 ! misho 2388: /* binding_state_t my_lease_state; */
! 2389: /* XXX Why is this my_lease_state never used? */
1.1 misho 2390: struct lease **lq;
2391: int (*log_func)(const char *, ...);
2392: const char *result, *reqlog;
2393:
2394: if (state -> me.state != normal)
2395: return 0;
2396:
2397: state->last_balance = cur_time;
2398:
2399: for (s = shared_networks ; s ; s = s->next) {
2400: for (p = s->pools ; p ; p = p->next) {
2401: if (p->failover_peer != state)
2402: continue;
2403:
2404: /* Right now we're giving the peer half of the free leases.
2405: If we have more leases than the peer (i.e., more than
2406: half), then the number of leases we have, less the number
2407: of leases the peer has, will be how many more leases we
2408: have than the peer has. So if we send half that number
2409: to the peer, we should be even. */
2410: if (p->failover_peer->i_am == primary) {
2411: lts = (p->free_leases - p->backup_leases) / 2;
2412: peer_lease_state = FTS_BACKUP;
1.1.1.1 ! misho 2413: /* my_lease_state = FTS_FREE; */
1.1 misho 2414: lq = &p->free;
2415: } else {
2416: lts = (p->backup_leases - p->free_leases) / 2;
2417: peer_lease_state = FTS_FREE;
1.1.1.1 ! misho 2418: /* my_lease_state = FTS_BACKUP; */
1.1 misho 2419: lq = &p->backup;
2420: }
2421:
2422: total = p->backup_leases + p->free_leases;
2423:
2424: thresh = ((total * state->max_lease_misbalance) + 50) / 100;
2425: hold = ((total * state->max_lease_ownership) + 50) / 100;
2426:
2427: /*
2428: * If we need leases (so lts is negative) more than negative
2429: * double the thresh%, panic and send poolreq to hopefully wake
2430: * up the peer (but more likely the db is inconsistent). But,
2431: * if this comes out zero, switch to -1 so that the POOLREQ is
2432: * sent on lts == -2 rather than right away at -1.
2433: *
2434: * Note that we do not subtract -1 from panic all the time
2435: * because thresh% and hold% may come out to the same number,
2436: * and that is correct operation...where thresh% and hold% are
2437: * both -1, we want to send poolreq when lts reaches -3. So,
2438: * "-3 < -2", lts < panic.
2439: */
2440: panic = thresh * -2;
2441:
2442: if (panic == 0)
2443: panic = -1;
2444:
2445: if ((sendreq != NULL) && (lts < panic)) {
2446: reqlog = " (requesting peer rebalance!)";
2447: *sendreq = ISC_TRUE;
2448: } else
2449: reqlog = "";
2450:
2451: log_info("balancing pool %lx %s total %d free %d "
2452: "backup %d lts %d max-own (+/-)%d%s",
2453: (unsigned long)p,
2454: (p->shared_network ?
2455: p->shared_network->name : ""), p->lease_count,
2456: p->free_leases, p->backup_leases, lts, hold,
2457: reqlog);
2458:
2459: /* In the first pass, try to allocate leases to the
2460: * peer which it would normally be responsible for (if
2461: * the lease has a hardware address or client-identifier,
2462: * and the load-balance-algorithm chooses the peer to
2463: * answer that address), up to a hold% excess in the peer's
2464: * favor. In the second pass, just send the oldest (first
2465: * on the list) leases up to a hold% excess in our favor.
2466: *
2467: * This could make for additional pool rebalance
2468: * events, but preserving MAC possession should be
2469: * worth it.
2470: */
2471: pass = 0;
2472: lease_reference(&lp, *lq, MDL);
2473:
2474: while (lp) {
2475: if (next)
2476: lease_dereference(&next, MDL);
2477: if (lp->next)
2478: lease_reference(&next, lp->next, MDL);
2479:
2480: /*
2481: * Stop if the pool is 'balanced enough.'
2482: *
2483: * The pool is balanced enough if:
2484: *
2485: * 1) We're on the first run through and the peer has
2486: * its fair share of leases already (lts reaches
2487: * -hold).
2488: * 2) We're on the second run through, we are shifting
2489: * never-used leases, and there is a perfectly even
2490: * balance (lts reaches zero).
2491: * 3) Second run through, we are shifting previously
2492: * used leases, and the local system has its fair
2493: * share but no more (lts reaches hold).
2494: *
2495: * Note that this is implemented below in 3,2,1 order.
2496: */
2497: if (pass) {
2498: if (lp->ends) {
2499: if (lts <= hold)
2500: break;
2501: } else {
2502: if (lts <= 0)
2503: break;
2504: }
2505: } else if (lts <= -hold)
2506: break;
2507:
2508: if (pass || peer_wants_lease(lp)) {
2509: --lts;
2510: ++leases_queued;
2511: lp->next_binding_state = peer_lease_state;
2512: lp->tstp = cur_time;
2513: lp->starts = cur_time;
2514:
2515: if (!supersede_lease(lp, NULL, 0, 1, 0) ||
2516: !write_lease(lp))
2517: log_error("can't commit lease %s on "
2518: "giveaway", piaddr(lp->ip_addr));
2519: }
2520:
2521: lease_dereference(&lp, MDL);
2522: if (next)
2523: lease_reference(&lp, next, MDL);
2524: else if (!pass) {
2525: pass = 1;
2526: lease_reference(&lp, *lq, MDL);
2527: }
2528: }
2529:
2530: if (next)
2531: lease_dereference(&next, MDL);
2532: if (lp)
2533: lease_dereference(&lp, MDL);
2534:
2535: if (lts > thresh) {
2536: result = "IMBALANCED";
2537: log_func = log_error;
2538: } else {
2539: result = "balanced";
2540: log_func = log_info;
2541: }
2542:
2543: log_func("%s pool %lx %s total %d free %d backup %d "
2544: "lts %d max-misbal %d", result, (unsigned long)p,
2545: (p->shared_network ?
2546: p->shared_network->name : ""), p->lease_count,
2547: p->free_leases, p->backup_leases, lts, thresh);
2548:
2549: /* Recalculate next rebalance event timer. */
2550: dhcp_failover_pool_check(p);
2551: }
2552: }
2553:
2554: if (leases_queued)
2555: commit_leases();
2556:
2557: return leases_queued;
2558: }
2559:
2560: /* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
2561: * states, on both servers. Check the scheduled time to rebalance the pool
2562: * and lower it if applicable.
2563: */
2564: void
2565: dhcp_failover_pool_check(struct pool *pool)
2566: {
2567: dhcp_failover_state_t *peer;
2568: TIME est1, est2;
2569: struct timeval tv;
2570:
2571: peer = pool->failover_peer;
2572:
2573: if(!peer || peer->me.state != normal)
2574: return;
2575:
2576: /* Estimate the time left until lease exhaustion.
2577: * The first lease on the backup or free lists is also the oldest
2578: * lease. It is reasonable to guess that it will take at least
2579: * as much time for a pool to run out of leases, as the present
2580: * age of the oldest lease (seconds since it expired).
2581: *
2582: * Note that this isn't so sane of an assumption if the oldest
2583: * lease is a virgin (ends = 0), we wind up sending this against
2584: * the max_balance bounds check.
2585: */
2586: if(pool->free && pool->free->ends < cur_time)
2587: est1 = cur_time - pool->free->ends;
2588: else
2589: est1 = 0;
2590:
2591: if(pool->backup && pool->backup->ends < cur_time)
2592: est2 = cur_time - pool->backup->ends;
2593: else
2594: est2 = 0;
2595:
2596: /* We don't want to schedule rebalance for when we think we'll run
2597: * out of leases, we want to schedule the rebalance for when we think
2598: * the disparity will be 'large enough' to warrant action.
2599: */
2600: est1 = ((est1 * peer->max_lease_misbalance) + 50) / 100;
2601: est2 = ((est2 * peer->max_lease_misbalance) + 50) / 100;
2602:
2603: /* Guess when the local system will begin issuing POOLREQ panic
2604: * attacks because "max_lease_misbalance*2" has been exceeded.
2605: */
2606: if(peer->i_am == primary)
2607: est1 *= 2;
2608: else
2609: est2 *= 2;
2610:
2611: /* Select the smallest time. */
2612: if(est1 > est2)
2613: est1 = est2;
2614:
2615: /* Bounded by the maximum configured value. */
2616: if(est1 > peer->max_balance)
2617: est1 = peer->max_balance;
2618:
2619: /* Project this time into the future. */
2620: est1 += cur_time;
2621:
2622: /* Do not move the time down under the minimum. */
2623: est2 = peer->last_balance + peer->min_balance;
2624: if(peer->last_balance && (est1 < est2))
2625: est1 = est2;
2626:
2627: /* Introduce a random delay. */
2628: est1 += random() % 5;
2629:
2630: /* Do not move the time forward, or reset to the same time. */
2631: if(peer->sched_balance) {
2632: if (est1 >= peer->sched_balance)
2633: return;
2634:
2635: /* We are about to schedule the time down, cancel the
2636: * current timeout.
2637: */
2638: cancel_timeout(dhcp_failover_pool_rebalance, peer);
2639: }
2640:
2641: /* The time is different, and lower, use it. */
2642: peer->sched_balance = est1;
2643:
2644: #if defined(DEBUG_FAILOVER_TIMING)
2645: log_info("add_timeout +%d dhcp_failover_pool_rebalance",
2646: (int)(est1 - cur_time));
2647: #endif
2648: tv.tv_sec = est1;
2649: tv.tv_usec = 0;
2650: add_timeout(&tv, dhcp_failover_pool_rebalance, peer,
2651: (tvref_t)dhcp_failover_state_reference,
2652: (tvunref_t)dhcp_failover_state_dereference);
2653: }
2654:
2655: int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
2656: {
2657: struct shared_network *s;
2658: struct pool *p;
2659:
2660: for (s = shared_networks; s; s = s -> next) {
2661: for (p = s -> pools; p; p = p -> next) {
2662: if (p -> failover_peer != state)
2663: continue;
2664: dhcp_failover_pool_check (p);
2665: }
2666: }
2667: return 0;
2668: }
2669:
2670: isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
2671: {
2672: struct lease *lp = (struct lease *)0;
2673: isc_result_t status;
2674:
2675: /* Can't update peer if we're not talking to it! */
2676: if (!state -> link_to_peer)
2677: return ISC_R_SUCCESS;
2678:
2679: /* If there are acks pending, transmit them prior to potentially
2680: * sending new updates for the same lease.
2681: */
2682: if (state->toack_queue_head != NULL)
2683: dhcp_failover_send_acks(state);
2684:
2685: while ((state -> partner.max_flying_updates >
2686: state -> cur_unacked_updates) && state -> update_queue_head) {
2687: /* Grab the head of the update queue. */
2688: lease_reference (&lp, state -> update_queue_head, MDL);
2689:
2690: /* Send the update to the peer. */
2691: status = dhcp_failover_send_bind_update (state, lp);
2692: if (status != ISC_R_SUCCESS) {
2693: lease_dereference (&lp, MDL);
2694: return status;
2695: }
2696: lp -> flags &= ~ON_UPDATE_QUEUE;
2697:
2698: /* Take it off the head of the update queue and put the next
2699: item in the update queue at the head. */
2700: lease_dereference (&state -> update_queue_head, MDL);
2701: if (lp -> next_pending) {
2702: lease_reference (&state -> update_queue_head,
2703: lp -> next_pending, MDL);
2704: lease_dereference (&lp -> next_pending, MDL);
2705: } else {
2706: lease_dereference (&state -> update_queue_tail, MDL);
2707: }
2708:
2709: if (state -> ack_queue_head) {
2710: lease_reference
2711: (&state -> ack_queue_tail -> next_pending,
2712: lp, MDL);
2713: lease_dereference (&state -> ack_queue_tail, MDL);
2714: } else {
2715: lease_reference (&state -> ack_queue_head, lp, MDL);
2716: }
2717: #if defined (POINTER_DEBUG)
2718: if (lp -> next_pending) {
2719: log_error ("ack_queue_tail: lp -> next_pending");
2720: abort ();
2721: }
2722: #endif
2723: lease_reference (&state -> ack_queue_tail, lp, MDL);
2724: lp -> flags |= ON_ACK_QUEUE;
2725: lease_dereference (&lp, MDL);
2726:
2727: /* Count the object as an unacked update. */
2728: state -> cur_unacked_updates++;
2729: }
2730: return ISC_R_SUCCESS;
2731: }
2732:
2733: /* Queue an update for a lease. Always returns 1 at this point - it's
2734: not an error for this to be called on a lease for which there's no
2735: failover peer. */
2736:
2737: int dhcp_failover_queue_update (struct lease *lease, int immediate)
2738: {
2739: dhcp_failover_state_t *state;
2740:
2741: if (!lease -> pool ||
2742: !lease -> pool -> failover_peer)
2743: return 1;
2744:
2745: /* If it's already on the update queue, leave it there. */
2746: if (lease -> flags & ON_UPDATE_QUEUE)
2747: return 1;
2748:
2749: /* Get the failover state structure for this lease. */
2750: state = lease -> pool -> failover_peer;
2751:
2752: /* If it's on the ack queue, take it off. */
2753: if (lease -> flags & ON_ACK_QUEUE)
2754: dhcp_failover_ack_queue_remove (state, lease);
2755:
2756: if (state -> update_queue_head) {
2757: lease_reference (&state -> update_queue_tail -> next_pending,
2758: lease, MDL);
2759: lease_dereference (&state -> update_queue_tail, MDL);
2760: } else {
2761: lease_reference (&state -> update_queue_head, lease, MDL);
2762: }
2763: #if defined (POINTER_DEBUG)
2764: if (lease -> next_pending) {
2765: log_error ("next pending on update queue lease.");
2766: #if defined (DEBUG_RC_HISTORY)
2767: dump_rc_history (lease);
2768: #endif
2769: abort ();
2770: }
2771: #endif
2772: lease_reference (&state -> update_queue_tail, lease, MDL);
2773: lease -> flags |= ON_UPDATE_QUEUE;
2774: if (immediate)
2775: dhcp_failover_send_updates (state);
2776: return 1;
2777: }
2778:
2779: int dhcp_failover_send_acks (dhcp_failover_state_t *state)
2780: {
2781: failover_message_t *msg = (failover_message_t *)0;
2782:
2783: /* Must commit all leases prior to acking them. */
2784: if (!commit_leases ())
2785: return 0;
2786:
2787: while (state -> toack_queue_head) {
2788: failover_message_reference
2789: (&msg, state -> toack_queue_head, MDL);
2790: failover_message_dereference
2791: (&state -> toack_queue_head, MDL);
2792: if (msg -> next) {
2793: failover_message_reference
2794: (&state -> toack_queue_head, msg -> next, MDL);
2795: }
2796:
2797: dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0);
2798:
2799: failover_message_dereference (&msg, MDL);
2800: }
2801:
2802: if (state -> toack_queue_tail)
2803: failover_message_dereference (&state -> toack_queue_tail, MDL);
2804: state -> pending_acks = 0;
2805:
2806: return 1;
2807: }
2808:
2809: void dhcp_failover_toack_queue_timeout (void *vs)
2810: {
2811: dhcp_failover_state_t *state = vs;
2812:
2813: #if defined (DEBUG_FAILOVER_TIMING)
2814: log_info ("dhcp_failover_toack_queue_timeout");
2815: #endif
2816:
2817: dhcp_failover_send_acks (state);
2818: }
2819:
2820: /* Queue an ack for a message. There is currently no way to queue a
2821: negative ack -- these need to be sent directly. */
2822:
2823: int dhcp_failover_queue_ack (dhcp_failover_state_t *state,
2824: failover_message_t *msg)
2825: {
2826: struct timeval tv;
2827:
2828: if (state -> toack_queue_head) {
2829: failover_message_reference
2830: (&state -> toack_queue_tail -> next, msg, MDL);
2831: failover_message_dereference (&state -> toack_queue_tail, MDL);
2832: } else {
2833: failover_message_reference (&state -> toack_queue_head,
2834: msg, MDL);
2835: }
2836: failover_message_reference (&state -> toack_queue_tail, msg, MDL);
2837:
2838: state -> pending_acks++;
2839:
2840: /* Flush the toack queue whenever we exceed half the number of
2841: allowed unacked updates. */
2842: if (state -> pending_acks >= state -> partner.max_flying_updates / 2) {
2843: dhcp_failover_send_acks (state);
2844: }
2845:
2846: /* Schedule a timeout to flush the ack queue. */
2847: if (state -> pending_acks > 0) {
2848: #if defined (DEBUG_FAILOVER_TIMING)
2849: log_info ("add_timeout +2 %s",
2850: "dhcp_failover_toack_queue_timeout");
2851: #endif
2852: tv . tv_sec = cur_time + 2;
2853: tv . tv_usec = 0;
2854: add_timeout (&tv,
2855: dhcp_failover_toack_queue_timeout, state,
2856: (tvref_t)dhcp_failover_state_reference,
2857: (tvunref_t)dhcp_failover_state_dereference);
2858: }
2859:
2860: return 1;
2861: }
2862:
2863: void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
2864: struct lease *lease)
2865: {
2866: struct lease *lp;
2867:
2868: if (!(lease -> flags & ON_ACK_QUEUE))
2869: return;
2870:
2871: if (state -> ack_queue_head == lease) {
2872: lease_dereference (&state -> ack_queue_head, MDL);
2873: if (lease -> next_pending) {
2874: lease_reference (&state -> ack_queue_head,
2875: lease -> next_pending, MDL);
2876: lease_dereference (&lease -> next_pending, MDL);
2877: } else {
2878: lease_dereference (&state -> ack_queue_tail, MDL);
2879: }
2880: } else {
2881: for (lp = state -> ack_queue_head;
2882: lp && lp -> next_pending != lease;
2883: lp = lp -> next_pending)
2884: ;
2885:
2886: if (!lp)
2887: return;
2888:
2889: lease_dereference (&lp -> next_pending, MDL);
2890: if (lease -> next_pending) {
2891: lease_reference (&lp -> next_pending,
2892: lease -> next_pending, MDL);
2893: lease_dereference (&lease -> next_pending, MDL);
2894: } else {
2895: lease_dereference (&state -> ack_queue_tail, MDL);
2896: if (lp -> next_pending) {
2897: log_error ("state -> ack_queue_tail");
2898: abort ();
2899: }
2900: lease_reference (&state -> ack_queue_tail, lp, MDL);
2901: }
2902: }
2903:
2904: lease -> flags &= ~ON_ACK_QUEUE;
2905: /* Multiple acks on one XID is an error and may cause badness. */
2906: lease->last_xid = 0;
2907: /* XXX: this violates draft-failover. We can't send another
2908: * update just because we forgot about an old one that hasn't
2909: * been acked yet.
2910: */
2911: state -> cur_unacked_updates--;
2912:
2913: /*
2914: * When updating leases as a result of an ack, we defer the commit
2915: * for performance reasons. When there are no more acks pending,
2916: * do a commit.
2917: */
2918: if (state -> cur_unacked_updates == 0) {
2919: commit_leases();
2920: }
2921: }
2922:
2923: isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
2924: omapi_object_t *id,
2925: omapi_data_string_t *name,
2926: omapi_typed_data_t *value)
2927: {
2928: isc_result_t status;
2929:
2930: if (h -> type != dhcp_type_failover_state)
2931: return ISC_R_INVALIDARG;
2932:
2933: /* This list of successful returns is completely wrong, but the
2934: fastest way to make dhcpctl do something vaguely sane when
2935: you try to change the local state. */
2936:
2937: if (!omapi_ds_strcmp (name, "name")) {
2938: return ISC_R_SUCCESS;
2939: } else if (!omapi_ds_strcmp (name, "partner-address")) {
2940: return ISC_R_SUCCESS;
2941: } else if (!omapi_ds_strcmp (name, "local-address")) {
2942: return ISC_R_SUCCESS;
2943: } else if (!omapi_ds_strcmp (name, "partner-port")) {
2944: return ISC_R_SUCCESS;
2945: } else if (!omapi_ds_strcmp (name, "local-port")) {
2946: return ISC_R_SUCCESS;
2947: } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
2948: return ISC_R_SUCCESS;
2949: } else if (!omapi_ds_strcmp (name, "mclt")) {
2950: return ISC_R_SUCCESS;
2951: } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
2952: return ISC_R_SUCCESS;
2953: } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
2954: return ISC_R_SUCCESS;
2955: } else if (!omapi_ds_strcmp (name, "partner-state")) {
2956: return ISC_R_SUCCESS;
2957: } else if (!omapi_ds_strcmp (name, "local-state")) {
2958: unsigned long l;
2959: status = omapi_get_int_value (&l, value);
2960: if (status != ISC_R_SUCCESS)
2961: return status;
2962: return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l);
2963: } else if (!omapi_ds_strcmp (name, "partner-stos")) {
2964: return ISC_R_SUCCESS;
2965: } else if (!omapi_ds_strcmp (name, "local-stos")) {
2966: return ISC_R_SUCCESS;
2967: } else if (!omapi_ds_strcmp (name, "hierarchy")) {
2968: return ISC_R_SUCCESS;
2969: } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
2970: return ISC_R_SUCCESS;
2971: } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
2972: return ISC_R_SUCCESS;
2973: } else if (!omapi_ds_strcmp (name, "skew")) {
2974: return ISC_R_SUCCESS;
2975: } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
2976: return ISC_R_SUCCESS;
2977: } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
2978: return ISC_R_SUCCESS;
2979: }
2980:
2981: if (h -> inner && h -> inner -> type -> set_value)
2982: return (*(h -> inner -> type -> set_value))
2983: (h -> inner, id, name, value);
2984: return ISC_R_NOTFOUND;
2985: }
2986:
2987: void dhcp_failover_keepalive (void *vs)
2988: {
2989: }
2990:
2991: void dhcp_failover_reconnect (void *vs)
2992: {
2993: dhcp_failover_state_t *state = vs;
2994: isc_result_t status;
2995: struct timeval tv;
2996:
2997: #if defined (DEBUG_FAILOVER_TIMING)
2998: log_info ("dhcp_failover_reconnect");
2999: #endif
3000: /* If we already connected the other way, let the connection
3001: recovery code initiate any retry that may be required. */
3002: if (state -> link_to_peer)
3003: return;
3004:
3005: status = dhcp_failover_link_initiate ((omapi_object_t *)state);
3006: if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
3007: log_info ("failover peer %s: %s", state -> name,
3008: isc_result_totext (status));
3009: #if defined (DEBUG_FAILOVER_TIMING)
3010: log_info("add_timeout +90 dhcp_failover_reconnect");
3011: #endif
3012: tv . tv_sec = cur_time + 90;
3013: tv . tv_usec = 0;
3014: add_timeout(&tv, dhcp_failover_reconnect, state,
3015: (tvref_t)dhcp_failover_state_reference,
3016: (tvunref_t)dhcp_failover_state_dereference);
3017: }
3018: }
3019:
3020: void dhcp_failover_startup_timeout (void *vs)
3021: {
3022: dhcp_failover_state_t *state = vs;
3023:
3024: #if defined (DEBUG_FAILOVER_TIMING)
3025: log_info ("dhcp_failover_startup_timeout");
3026: #endif
3027:
3028: dhcp_failover_state_transition (state, "disconnect");
3029: }
3030:
3031: void dhcp_failover_link_startup_timeout (void *vl)
3032: {
3033: dhcp_failover_link_t *link = vl;
3034: omapi_object_t *p;
3035:
3036: for (p = (omapi_object_t *)link; p -> inner; p = p -> inner)
3037: ;
3038: for (; p; p = p -> outer)
3039: if (p -> type == omapi_type_connection)
3040: break;
3041: if (p) {
3042: log_info ("failover: link startup timeout");
3043: omapi_disconnect (p, 1);
3044: }
3045: }
3046:
3047: void dhcp_failover_listener_restart (void *vs)
3048: {
3049: dhcp_failover_state_t *state = vs;
3050: isc_result_t status;
3051: struct timeval tv;
3052:
3053: #if defined (DEBUG_FAILOVER_TIMING)
3054: log_info ("dhcp_failover_listener_restart");
3055: #endif
3056:
3057: status = dhcp_failover_listen ((omapi_object_t *)state);
3058: if (status != ISC_R_SUCCESS) {
3059: log_info ("failover peer %s: %s", state -> name,
3060: isc_result_totext (status));
3061: #if defined (DEBUG_FAILOVER_TIMING)
3062: log_info ("add_timeout +90 %s",
3063: "dhcp_failover_listener_restart");
3064: #endif
3065: tv . tv_sec = cur_time + 90;
3066: tv . tv_usec = 0;
3067: add_timeout (&tv,
3068: dhcp_failover_listener_restart, state,
3069: (tvref_t)dhcp_failover_state_reference,
3070: (tvunref_t)dhcp_failover_state_dereference);
3071: }
3072: }
3073:
3074: isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
3075: omapi_object_t *id,
3076: omapi_data_string_t *name,
3077: omapi_value_t **value)
3078: {
3079: dhcp_failover_state_t *s;
3080: struct option_cache *oc;
3081: struct data_string ds;
3082: isc_result_t status;
3083:
3084: if (h -> type != dhcp_type_failover_state)
3085: return ISC_R_INVALIDARG;
3086: s = (dhcp_failover_state_t *)h;
3087:
3088: if (!omapi_ds_strcmp (name, "name")) {
3089: if (s -> name)
3090: return omapi_make_string_value (value,
3091: name, s -> name, MDL);
3092: return ISC_R_NOTFOUND;
3093: } else if (!omapi_ds_strcmp (name, "partner-address")) {
3094: oc = s -> partner.address;
3095: getaddr:
3096: memset (&ds, 0, sizeof ds);
3097: if (!evaluate_option_cache (&ds, (struct packet *)0,
3098: (struct lease *)0,
3099: (struct client_state *)0,
3100: (struct option_state *)0,
3101: (struct option_state *)0,
3102: &global_scope, oc, MDL)) {
3103: return ISC_R_NOTFOUND;
3104: }
3105: status = omapi_make_const_value (value,
3106: name, ds.data, ds.len, MDL);
3107: /* Disgusting kludge: */
3108: if (oc == s -> me.address && !s -> server_identifier.len)
3109: data_string_copy (&s -> server_identifier, &ds, MDL);
3110: data_string_forget (&ds, MDL);
3111: return status;
3112: } else if (!omapi_ds_strcmp (name, "local-address")) {
3113: oc = s -> me.address;
3114: goto getaddr;
3115: } else if (!omapi_ds_strcmp (name, "partner-port")) {
3116: return omapi_make_int_value (value, name,
3117: s -> partner.port, MDL);
3118: } else if (!omapi_ds_strcmp (name, "local-port")) {
3119: return omapi_make_int_value (value,
3120: name, s -> me.port, MDL);
3121: } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
3122: return omapi_make_uint_value (value, name,
3123: s -> me.max_flying_updates,
3124: MDL);
3125: } else if (!omapi_ds_strcmp (name, "mclt")) {
3126: return omapi_make_uint_value (value, name, s -> mclt, MDL);
3127: } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
3128: return omapi_make_int_value (value, name,
3129: s -> load_balance_max_secs, MDL);
3130: } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
3131: if (s -> hba)
3132: return omapi_make_const_value (value, name,
3133: s -> hba, 32, MDL);
3134: return ISC_R_NOTFOUND;
3135: } else if (!omapi_ds_strcmp (name, "partner-state")) {
3136: return omapi_make_uint_value (value, name,
3137: s -> partner.state, MDL);
3138: } else if (!omapi_ds_strcmp (name, "local-state")) {
3139: return omapi_make_uint_value (value, name,
3140: s -> me.state, MDL);
3141: } else if (!omapi_ds_strcmp (name, "partner-stos")) {
3142: return omapi_make_int_value (value, name,
3143: s -> partner.stos, MDL);
3144: } else if (!omapi_ds_strcmp (name, "local-stos")) {
3145: return omapi_make_int_value (value, name,
3146: s -> me.stos, MDL);
3147: } else if (!omapi_ds_strcmp (name, "hierarchy")) {
3148: return omapi_make_uint_value (value, name, s -> i_am, MDL);
3149: } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
3150: return omapi_make_int_value (value, name,
3151: s -> last_packet_sent, MDL);
3152: } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
3153: return omapi_make_int_value (value, name,
3154: s -> last_timestamp_received,
3155: MDL);
3156: } else if (!omapi_ds_strcmp (name, "skew")) {
3157: return omapi_make_int_value (value, name, s -> skew, MDL);
3158: } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
3159: return omapi_make_uint_value (value, name,
3160: s -> me.max_response_delay,
3161: MDL);
3162: } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
3163: return omapi_make_int_value (value, name,
3164: s -> cur_unacked_updates, MDL);
3165: }
3166:
3167: if (h -> inner && h -> inner -> type -> get_value)
3168: return (*(h -> inner -> type -> get_value))
3169: (h -> inner, id, name, value);
3170: return ISC_R_NOTFOUND;
3171: }
3172:
3173: isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
3174: const char *file, int line)
3175: {
3176: dhcp_failover_state_t *s;
3177:
3178: if (h -> type != dhcp_type_failover_state)
3179: return ISC_R_INVALIDARG;
3180: s = (dhcp_failover_state_t *)h;
3181:
3182: if (s -> link_to_peer)
3183: dhcp_failover_link_dereference (&s -> link_to_peer, file, line);
3184: if (s -> name) {
3185: dfree (s -> name, MDL);
3186: s -> name = (char *)0;
3187: }
3188: if (s -> partner.address)
3189: option_cache_dereference (&s -> partner.address, file, line);
3190: if (s -> me.address)
3191: option_cache_dereference (&s -> me.address, file, line);
3192: if (s -> hba) {
3193: dfree (s -> hba, file, line);
3194: s -> hba = (u_int8_t *)0;
3195: }
3196: if (s -> update_queue_head)
3197: lease_dereference (&s -> update_queue_head, file, line);
3198: if (s -> update_queue_tail)
3199: lease_dereference (&s -> update_queue_tail, file, line);
3200: if (s -> ack_queue_head)
3201: lease_dereference (&s -> ack_queue_head, file, line);
3202: if (s -> ack_queue_tail)
3203: lease_dereference (&s -> ack_queue_tail, file, line);
3204: if (s -> send_update_done)
3205: lease_dereference (&s -> send_update_done, file, line);
3206: if (s -> toack_queue_head)
3207: failover_message_dereference (&s -> toack_queue_head,
3208: file, line);
3209: if (s -> toack_queue_tail)
3210: failover_message_dereference (&s -> toack_queue_tail,
3211: file, line);
3212: return ISC_R_SUCCESS;
3213: }
3214:
3215: /* Write all the published values associated with the object through the
3216: specified connection. */
3217:
3218: isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
3219: omapi_object_t *id,
3220: omapi_object_t *h)
3221: {
3222: dhcp_failover_state_t *s;
3223: isc_result_t status;
3224:
3225: if (c -> type != omapi_type_connection)
3226: return ISC_R_INVALIDARG;
3227:
3228: if (h -> type != dhcp_type_failover_state)
3229: return ISC_R_INVALIDARG;
3230: s = (dhcp_failover_state_t *)h;
3231:
3232: status = omapi_connection_put_name (c, "name");
3233: if (status != ISC_R_SUCCESS)
3234: return status;
3235: status = omapi_connection_put_string (c, s -> name);
3236: if (status != ISC_R_SUCCESS)
3237: return status;
3238:
3239: status = omapi_connection_put_name (c, "partner-address");
3240: if (status != ISC_R_SUCCESS)
3241: return status;
3242: status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
3243: if (status != ISC_R_SUCCESS)
3244: return status;
3245: status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
3246: sizeof s -> partner.address);
3247: if (status != ISC_R_SUCCESS)
3248: return status;
3249:
3250: status = omapi_connection_put_name (c, "partner-port");
3251: if (status != ISC_R_SUCCESS)
3252: return status;
3253: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3254: if (status != ISC_R_SUCCESS)
3255: return status;
3256: status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
3257: if (status != ISC_R_SUCCESS)
3258: return status;
3259:
3260: status = omapi_connection_put_name (c, "local-address");
3261: if (status != ISC_R_SUCCESS)
3262: return status;
3263: status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
3264: if (status != ISC_R_SUCCESS)
3265: return status;
3266: status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
3267: sizeof s -> me.address);
3268: if (status != ISC_R_SUCCESS)
3269: return status;
3270:
3271: status = omapi_connection_put_name (c, "local-port");
3272: if (status != ISC_R_SUCCESS)
3273: return status;
3274: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3275: if (status != ISC_R_SUCCESS)
3276: return status;
3277: status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
3278: if (status != ISC_R_SUCCESS)
3279: return status;
3280:
3281: status = omapi_connection_put_name (c, "max-outstanding-updates");
3282: if (status != ISC_R_SUCCESS)
3283: return status;
3284: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3285: if (status != ISC_R_SUCCESS)
3286: return status;
3287: status = omapi_connection_put_uint32 (c,
3288: s -> me.max_flying_updates);
3289: if (status != ISC_R_SUCCESS)
3290: return status;
3291:
3292: status = omapi_connection_put_name (c, "mclt");
3293: if (status != ISC_R_SUCCESS)
3294: return status;
3295: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3296: if (status != ISC_R_SUCCESS)
3297: return status;
3298: status = omapi_connection_put_uint32 (c, s -> mclt);
3299: if (status != ISC_R_SUCCESS)
3300: return status;
3301:
3302: status = omapi_connection_put_name (c, "load-balance-max-secs");
3303: if (status != ISC_R_SUCCESS)
3304: return status;
3305: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3306: if (status != ISC_R_SUCCESS)
3307: return status;
3308: status = (omapi_connection_put_uint32
3309: (c, (u_int32_t)s -> load_balance_max_secs));
3310: if (status != ISC_R_SUCCESS)
3311: return status;
3312:
3313:
3314: if (s -> hba) {
3315: status = omapi_connection_put_name (c, "load-balance-hba");
3316: if (status != ISC_R_SUCCESS)
3317: return status;
3318: status = omapi_connection_put_uint32 (c, 32);
3319: if (status != ISC_R_SUCCESS)
3320: return status;
3321: status = omapi_connection_copyin (c, s -> hba, 32);
3322: if (status != ISC_R_SUCCESS)
3323: return status;
3324: }
3325:
3326: status = omapi_connection_put_name (c, "partner-state");
3327: if (status != ISC_R_SUCCESS)
3328: return status;
3329: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3330: if (status != ISC_R_SUCCESS)
3331: return status;
3332: status = omapi_connection_put_uint32 (c, s -> partner.state);
3333: if (status != ISC_R_SUCCESS)
3334: return status;
3335:
3336: status = omapi_connection_put_name (c, "local-state");
3337: if (status != ISC_R_SUCCESS)
3338: return status;
3339: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3340: if (status != ISC_R_SUCCESS)
3341: return status;
3342: status = omapi_connection_put_uint32 (c, s -> me.state);
3343: if (status != ISC_R_SUCCESS)
3344: return status;
3345:
3346: status = omapi_connection_put_name (c, "partner-stos");
3347: if (status != ISC_R_SUCCESS)
3348: return status;
3349: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3350: if (status != ISC_R_SUCCESS)
3351: return status;
3352: status = omapi_connection_put_uint32 (c,
3353: (u_int32_t)s -> partner.stos);
3354: if (status != ISC_R_SUCCESS)
3355: return status;
3356:
3357: status = omapi_connection_put_name (c, "local-stos");
3358: if (status != ISC_R_SUCCESS)
3359: return status;
3360: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3361: if (status != ISC_R_SUCCESS)
3362: return status;
3363: status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
3364: if (status != ISC_R_SUCCESS)
3365: return status;
3366:
3367: status = omapi_connection_put_name (c, "hierarchy");
3368: if (status != ISC_R_SUCCESS)
3369: return status;
3370: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3371: if (status != ISC_R_SUCCESS)
3372: return status;
3373: status = omapi_connection_put_uint32 (c, s -> i_am);
3374: if (status != ISC_R_SUCCESS)
3375: return status;
3376:
3377: status = omapi_connection_put_name (c, "last-packet-sent");
3378: if (status != ISC_R_SUCCESS)
3379: return status;
3380: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3381: if (status != ISC_R_SUCCESS)
3382: return status;
3383: status = (omapi_connection_put_uint32
3384: (c, (u_int32_t)s -> last_packet_sent));
3385: if (status != ISC_R_SUCCESS)
3386: return status;
3387:
3388: status = omapi_connection_put_name (c, "last-timestamp-received");
3389: if (status != ISC_R_SUCCESS)
3390: return status;
3391: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3392: if (status != ISC_R_SUCCESS)
3393: return status;
3394: status = (omapi_connection_put_uint32
3395: (c, (u_int32_t)s -> last_timestamp_received));
3396: if (status != ISC_R_SUCCESS)
3397: return status;
3398:
3399: status = omapi_connection_put_name (c, "skew");
3400: if (status != ISC_R_SUCCESS)
3401: return status;
3402: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3403: if (status != ISC_R_SUCCESS)
3404: return status;
3405: status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
3406: if (status != ISC_R_SUCCESS)
3407: return status;
3408:
3409: status = omapi_connection_put_name (c, "max-response-delay");
3410: if (status != ISC_R_SUCCESS)
3411: return status;
3412: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3413: if (status != ISC_R_SUCCESS)
3414: return status;
3415: status = (omapi_connection_put_uint32
3416: (c, (u_int32_t)s -> me.max_response_delay));
3417: if (status != ISC_R_SUCCESS)
3418: return status;
3419:
3420: status = omapi_connection_put_name (c, "cur-unacked-updates");
3421: if (status != ISC_R_SUCCESS)
3422: return status;
3423: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3424: if (status != ISC_R_SUCCESS)
3425: return status;
3426: status = (omapi_connection_put_uint32
3427: (c, (u_int32_t)s -> cur_unacked_updates));
3428: if (status != ISC_R_SUCCESS)
3429: return status;
3430:
3431: if (h -> inner && h -> inner -> type -> stuff_values)
3432: return (*(h -> inner -> type -> stuff_values)) (c, id,
3433: h -> inner);
3434: return ISC_R_SUCCESS;
3435: }
3436:
3437: isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
3438: omapi_object_t *id,
3439: omapi_object_t *ref)
3440: {
3441: omapi_value_t *tv = (omapi_value_t *)0;
3442: isc_result_t status;
3443: dhcp_failover_state_t *s;
3444:
3445: if (!ref)
3446: return ISC_R_NOKEYS;
3447:
3448: /* First see if we were sent a handle. */
3449: status = omapi_get_value_str (ref, id, "handle", &tv);
3450: if (status == ISC_R_SUCCESS) {
3451: status = omapi_handle_td_lookup (sp, tv -> value);
3452:
3453: omapi_value_dereference (&tv, MDL);
3454: if (status != ISC_R_SUCCESS)
3455: return status;
3456:
3457: /* Don't return the object if the type is wrong. */
3458: if ((*sp) -> type != dhcp_type_failover_state) {
3459: omapi_object_dereference (sp, MDL);
3460: return ISC_R_INVALIDARG;
3461: }
3462: }
3463:
3464: /* Look the failover state up by peer name. */
3465: status = omapi_get_value_str (ref, id, "name", &tv);
3466: if (status == ISC_R_SUCCESS) {
3467: for (s = failover_states; s; s = s -> next) {
3468: unsigned l = strlen (s -> name);
3469: if (l == tv -> value -> u.buffer.len &&
3470: !memcmp (s -> name,
3471: tv -> value -> u.buffer.value, l))
3472: break;
3473: }
3474: omapi_value_dereference (&tv, MDL);
3475:
3476: /* If we already have a lease, and it's not the same one,
3477: then the query was invalid. */
3478: if (*sp && *sp != (omapi_object_t *)s) {
3479: omapi_object_dereference (sp, MDL);
3480: return ISC_R_KEYCONFLICT;
3481: } else if (!s) {
3482: if (*sp)
3483: omapi_object_dereference (sp, MDL);
3484: return ISC_R_NOTFOUND;
3485: } else if (!*sp)
3486: /* XXX fix so that hash lookup itself creates
3487: XXX the reference. */
3488: omapi_object_reference (sp, (omapi_object_t *)s, MDL);
3489: }
3490:
3491: /* If we get to here without finding a lease, no valid key was
3492: specified. */
3493: if (!*sp)
3494: return ISC_R_NOKEYS;
3495: return ISC_R_SUCCESS;
3496: }
3497:
3498: isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
3499: omapi_object_t *id)
3500: {
3501: return ISC_R_NOTIMPLEMENTED;
3502: }
3503:
3504: isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
3505: omapi_object_t *id)
3506: {
3507: return ISC_R_NOTIMPLEMENTED;
3508: }
3509:
3510: int dhcp_failover_state_match (dhcp_failover_state_t *state,
3511: u_int8_t *addr, unsigned addrlen)
3512: {
3513: struct data_string ds;
3514: int i;
3515:
3516: memset (&ds, 0, sizeof ds);
3517: if (evaluate_option_cache (&ds, (struct packet *)0,
3518: (struct lease *)0,
3519: (struct client_state *)0,
3520: (struct option_state *)0,
3521: (struct option_state *)0,
3522: &global_scope,
3523: state -> partner.address, MDL)) {
3524: for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
3525: if (!memcmp (&ds.data [i],
3526: addr, addrlen)) {
3527: data_string_forget (&ds, MDL);
3528: return 1;
3529: }
3530: }
3531: data_string_forget (&ds, MDL);
3532: }
3533: return 0;
3534: }
3535:
3536: int
3537: dhcp_failover_state_match_by_name(state, name)
3538: dhcp_failover_state_t *state;
3539: failover_option_t *name;
3540: {
3541: if ((strlen(state->name) == name->count) &&
3542: (memcmp(state->name, name->data, name->count) == 0))
3543: return 1;
3544:
3545: return 0;
3546: }
3547:
3548: const char *dhcp_failover_reject_reason_print (int reason)
3549: {
3550: static char resbuf[sizeof("Undefined-255: This reason code is not defined "
3551: "in the protocol standard.")];
3552:
3553: if ((reason > 0xff) || (reason < 0))
3554: return "Reason code out of range.";
3555:
3556: switch (reason) {
3557: case FTR_ILLEGAL_IP_ADDR:
3558: return "Illegal IP address (not part of any address pool).";
3559:
3560: case FTR_FATAL_CONFLICT:
3561: return "Fatal conflict exists: address in use by other client.";
3562:
3563: case FTR_MISSING_BINDINFO:
3564: return "Missing binding information.";
3565:
3566: case FTR_TIMEMISMATCH:
3567: return "Connection rejected, time mismatch too great.";
3568:
3569: case FTR_INVALID_MCLT:
3570: return "Connection rejected, invalid MCLT.";
3571:
3572: case FTR_MISC_REJECT:
3573: return "Connection rejected, unknown reason.";
3574:
3575: case FTR_DUP_CONNECTION:
3576: return "Connection rejected, duplicate connection.";
3577:
3578: case FTR_INVALID_PARTNER:
3579: return "Connection rejected, invalid failover partner.";
3580:
3581: case FTR_TLS_UNSUPPORTED:
3582: return "TLS not supported.";
3583:
3584: case FTR_TLS_UNCONFIGURED:
3585: return "TLS supported but not configured.";
3586:
3587: case FTR_TLS_REQUIRED:
3588: return "TLS required but not supported by partner.";
3589:
3590: case FTR_DIGEST_UNSUPPORTED:
3591: return "Message digest not supported.";
3592:
3593: case FTR_DIGEST_UNCONFIGURED:
3594: return "Message digest not configured.";
3595:
3596: case FTR_VERSION_MISMATCH:
3597: return "Protocol version mismatch.";
3598:
3599: case FTR_OUTDATED_BIND_INFO:
3600: return "Outdated binding information.";
3601:
3602: case FTR_LESS_CRIT_BIND_INFO:
3603: return "Less critical binding information.";
3604:
3605: case FTR_NO_TRAFFIC:
3606: return "No traffic within sufficient time.";
3607:
3608: case FTR_HBA_CONFLICT:
3609: return "Hash bucket assignment conflict.";
3610:
3611: case FTR_IP_NOT_RESERVED:
3612: return "IP not reserved on this server.";
3613:
3614: case FTR_IP_DIGEST_FAILURE:
3615: return "Message digest failed to compare.";
3616:
3617: case FTR_IP_MISSING_DIGEST:
3618: return "Missing message digest.";
3619:
3620: case FTR_UNKNOWN:
3621: return "Unknown Error.";
3622:
3623: default:
3624: sprintf(resbuf, "Undefined-%d: This reason code is not defined in the "
3625: "protocol standard.", reason);
3626: return resbuf;
3627: }
3628: }
3629:
3630: const char *dhcp_failover_state_name_print (enum failover_state state)
3631: {
3632: switch (state) {
3633: default:
3634: case unknown_state:
3635: return "unknown-state";
3636:
3637: case partner_down:
3638: return "partner-down";
3639:
3640: case normal:
3641: return "normal";
3642:
3643: case conflict_done:
3644: return "conflict-done";
3645:
3646: case communications_interrupted:
3647: return "communications-interrupted";
3648:
3649: case resolution_interrupted:
3650: return "resolution-interrupted";
3651:
3652: case potential_conflict:
3653: return "potential-conflict";
3654:
3655: case recover:
3656: return "recover";
3657:
3658: case recover_done:
3659: return "recover-done";
3660:
3661: case recover_wait:
3662: return "recover-wait";
3663:
3664: case shut_down:
3665: return "shutdown";
3666:
3667: case paused:
3668: return "paused";
3669:
3670: case startup:
3671: return "startup";
3672: }
3673: }
3674:
3675: const char *dhcp_failover_message_name (unsigned type)
3676: {
3677: static char messbuf[sizeof("unknown-message-255")];
3678:
3679: if (type > 0xff)
3680: return "invalid-message";
3681:
3682: switch (type) {
3683: case FTM_POOLREQ:
3684: return "pool-request";
3685:
3686: case FTM_POOLRESP:
3687: return "pool-response";
3688:
3689: case FTM_BNDUPD:
3690: return "bind-update";
3691:
3692: case FTM_BNDACK:
3693: return "bind-ack";
3694:
3695: case FTM_CONNECT:
3696: return "connect";
3697:
3698: case FTM_CONNECTACK:
3699: return "connect-ack";
3700:
3701: case FTM_UPDREQ:
3702: return "update-request";
3703:
3704: case FTM_UPDDONE:
3705: return "update-done";
3706:
3707: case FTM_UPDREQALL:
3708: return "update-request-all";
3709:
3710: case FTM_STATE:
3711: return "state";
3712:
3713: case FTM_CONTACT:
3714: return "contact";
3715:
3716: case FTM_DISCONNECT:
3717: return "disconnect";
3718:
3719: default:
3720: sprintf(messbuf, "unknown-message-%u", type);
3721: return messbuf;
3722: }
3723: }
3724:
3725: const char *dhcp_failover_option_name (unsigned type)
3726: {
3727: static char optbuf[sizeof("unknown-option-65535")];
3728:
3729: if (type > 0xffff)
3730: return "invalid-option";
3731:
3732: switch (type) {
3733: case FTO_ADDRESSES_TRANSFERRED:
3734: return "addresses-transferred";
3735:
3736: case FTO_ASSIGNED_IP_ADDRESS:
3737: return "assigned-ip-address";
3738:
3739: case FTO_BINDING_STATUS:
3740: return "binding-status";
3741:
3742: case FTO_CLIENT_IDENTIFIER:
3743: return "client-identifier";
3744:
3745: case FTO_CHADDR:
3746: return "chaddr";
3747:
3748: case FTO_CLTT:
3749: return "cltt";
3750:
3751: case FTO_DDNS:
3752: return "ddns";
3753:
3754: case FTO_DELAYED_SERVICE:
3755: return "delayed-service";
3756:
3757: case FTO_HBA:
3758: return "hba";
3759:
3760: case FTO_IP_FLAGS:
3761: return "ip-flags";
3762:
3763: case FTO_LEASE_EXPIRY:
3764: return "lease-expiry";
3765:
3766: case FTO_MAX_UNACKED:
3767: return "max-unacked";
3768:
3769: case FTO_MCLT:
3770: return "mclt";
3771:
3772: case FTO_MESSAGE:
3773: return "message";
3774:
3775: case FTO_MESSAGE_DIGEST:
3776: return "message-digest";
3777:
3778: case FTO_POTENTIAL_EXPIRY:
3779: return "potential-expiry";
3780:
3781: case FTO_PROTOCOL_VERSION:
3782: return "protocol-version";
3783:
3784: case FTO_RECEIVE_TIMER:
3785: return "receive-timer";
3786:
3787: case FTO_REJECT_REASON:
3788: return "reject-reason";
3789:
3790: case FTO_RELATIONSHIP_NAME:
3791: return "relationship-name";
3792:
3793: case FTO_REPLY_OPTIONS:
3794: return "reply-options";
3795:
3796: case FTO_REQUEST_OPTIONS:
3797: return "request-options";
3798:
3799: case FTO_SERVER_FLAGS:
3800: return "server-flags";
3801:
3802: case FTO_SERVER_STATE:
3803: return "server-state";
3804:
3805: case FTO_STOS:
3806: return "stos";
3807:
3808: case FTO_TLS_REPLY:
3809: return "tls-reply";
3810:
3811: case FTO_TLS_REQUEST:
3812: return "tls-request";
3813:
3814: case FTO_VENDOR_CLASS:
3815: return "vendor-class";
3816:
3817: case FTO_VENDOR_OPTIONS:
3818: return "vendor-options";
3819:
3820: default:
3821: sprintf(optbuf, "unknown-option-%u", type);
3822: return optbuf;
3823: }
3824: }
3825:
3826: failover_option_t *dhcp_failover_option_printf (unsigned code,
3827: char *obuf,
3828: unsigned *obufix,
3829: unsigned obufmax,
3830: const char *fmt, ...)
3831: {
3832: va_list va;
3833: char tbuf [256];
3834:
3835: /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3836: * It is unclear what the effects of truncation here are, or
3837: * how that condition should be handled. It seems that this
3838: * function is used for formatting messages in the failover
3839: * command channel. For now the safest thing is for
3840: * overflow-truncation to cause a fatal log.
3841: */
3842: va_start (va, fmt);
3843: if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
3844: log_fatal ("%s: vsnprintf would truncate",
3845: "dhcp_failover_make_option");
3846: va_end (va);
3847:
3848: return dhcp_failover_make_option (code, obuf, obufix, obufmax,
3849: strlen (tbuf), tbuf);
3850: }
3851:
3852: failover_option_t *dhcp_failover_make_option (unsigned code,
3853: char *obuf, unsigned *obufix,
3854: unsigned obufmax, ...)
3855: {
3856: va_list va;
3857: struct failover_option_info *info;
3858: int i;
3859: unsigned size, count;
3860: unsigned val;
3861: u_int8_t *iaddr;
3862: unsigned ilen = 0;
3863: u_int8_t *bval;
3864: char *txt = NULL;
3865: #if defined (DEBUG_FAILOVER_MESSAGES)
3866: char tbuf [256];
3867: #endif
3868:
3869: /* Note that the failover_option structure is used differently on
3870: input than on output - on input, count is an element count, and
3871: on output it's the number of bytes total in the option, including
3872: the option code and option length. */
3873: failover_option_t option, *op;
3874:
3875:
3876: /* Bogus option code? */
3877: if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
3878: return &null_failover_option;
3879: }
3880: info = &ft_options [code];
3881:
3882: va_start (va, obufmax);
3883:
3884: /* Get the number of elements and the size of the buffer we need
3885: to allocate. */
3886: if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
3887: count = info -> type == FT_DDNS ? 1 : 2;
3888: size = va_arg (va, int) + count;
3889: } else {
3890: /* Find out how many items in this list. */
3891: if (info -> num_present)
3892: count = info -> num_present;
3893: else
3894: count = va_arg (va, int);
3895:
3896: /* Figure out size. */
3897: switch (info -> type) {
3898: case FT_UINT8:
3899: case FT_BYTES:
3900: case FT_DIGEST:
3901: size = count;
3902: break;
3903:
3904: case FT_TEXT_OR_BYTES:
3905: case FT_TEXT:
3906: txt = va_arg (va, char *);
3907: size = count;
3908: break;
3909:
3910: case FT_IPADDR:
3911: ilen = va_arg (va, unsigned);
3912: size = count * ilen;
3913: break;
3914:
3915: case FT_UINT32:
3916: size = count * 4;
3917: break;
3918:
3919: case FT_UINT16:
3920: size = count * 2;
3921: break;
3922:
3923: default:
3924: /* shouldn't get here. */
3925: log_fatal ("bogus type in failover_make_option: %d",
3926: info -> type);
3927: return &null_failover_option;
3928: }
3929: }
3930:
3931: size += 4;
3932:
3933: /* Allocate a buffer for the option. */
3934: option.count = size;
3935: option.data = dmalloc (option.count, MDL);
3936: if (!option.data) {
3937: va_end (va);
3938: return &null_failover_option;
3939: }
3940:
3941: /* Put in the option code and option length. */
3942: putUShort (option.data, code);
3943: putUShort (&option.data [2], size - 4);
3944:
3945: #if defined (DEBUG_FAILOVER_MESSAGES)
3946: /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3947: * It is unclear what the effects of truncation here are, or
3948: * how that condition should be handled. It seems that this
3949: * message may be sent over the failover command channel.
3950: * For now the safest thing is for overflow-truncation to cause
3951: * a fatal log.
3952: */
3953: if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
3954: option.count) >= sizeof tbuf)
3955: log_fatal ("dhcp_failover_make_option: tbuf overflow");
3956: failover_print (obuf, obufix, obufmax, tbuf);
3957: #endif
3958:
3959: /* Now put in the data. */
3960: switch (info -> type) {
3961: case FT_UINT8:
3962: for (i = 0; i < count; i++) {
3963: val = va_arg (va, unsigned);
3964: #if defined (DEBUG_FAILOVER_MESSAGES)
3965: /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3966: sprintf (tbuf, " %d", val);
3967: failover_print (obuf, obufix, obufmax, tbuf);
3968: #endif
3969: option.data [i + 4] = val;
3970: }
3971: break;
3972:
3973: case FT_IPADDR:
3974: for (i = 0; i < count; i++) {
3975: iaddr = va_arg (va, u_int8_t *);
3976: if (ilen != 4) {
3977: dfree (option.data, MDL);
3978: log_error ("IP addrlen=%d, should be 4.",
3979: ilen);
3980: va_end (va);
3981: return &null_failover_option;
3982: }
3983:
3984: #if defined (DEBUG_FAILOVER_MESSAGES)
3985: /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3986: sprintf (tbuf, " %u.%u.%u.%u",
3987: iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
3988: failover_print (obuf, obufix, obufmax, tbuf);
3989: #endif
3990: memcpy (&option.data [4 + i * ilen], iaddr, ilen);
3991: }
3992: break;
3993:
3994: case FT_UINT32:
3995: for (i = 0; i < count; i++) {
3996: val = va_arg (va, unsigned);
3997: #if defined (DEBUG_FAILOVER_MESSAGES)
3998: /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3999: sprintf (tbuf, " %d", val);
4000: failover_print (obuf, obufix, obufmax, tbuf);
4001: #endif
4002: putULong (&option.data [4 + i * 4], val);
4003: }
4004: break;
4005:
4006: case FT_BYTES:
4007: case FT_DIGEST:
4008: bval = va_arg (va, u_int8_t *);
4009: #if defined (DEBUG_FAILOVER_MESSAGES)
4010: for (i = 0; i < count; i++) {
4011: /* 23 bytes plus nul, safe. */
4012: sprintf (tbuf, " %d", bval [i]);
4013: failover_print (obuf, obufix, obufmax, tbuf);
4014: }
4015: #endif
4016: memcpy (&option.data [4], bval, count);
4017: break;
4018:
4019: /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
4020: terminated. Note that the caller should be careful not
4021: to provide a format and data that amount to more than 256
4022: bytes of data, since it will cause a fatal error. */
4023: case FT_TEXT_OR_BYTES:
4024: case FT_TEXT:
4025: #if defined (DEBUG_FAILOVER_MESSAGES)
4026: /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
4027: * It is unclear what the effects of truncation here are, or
4028: * how that condition should be handled. It seems that this
4029: * function is used for formatting messages in the failover
4030: * command channel. For now the safest thing is for
4031: * overflow-truncation to cause a fatal log.
4032: */
4033: if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
4034: log_fatal ("dhcp_failover_make_option: tbuf overflow");
4035: failover_print (obuf, obufix, obufmax, tbuf);
4036: #endif
4037: memcpy (&option.data [4], txt, count);
4038: break;
4039:
4040: case FT_DDNS:
4041: case FT_DDNS1:
4042: option.data [4] = va_arg (va, unsigned);
4043: if (count == 2)
4044: option.data [5] = va_arg (va, unsigned);
4045: bval = va_arg (va, u_int8_t *);
4046: memcpy (&option.data [4 + count], bval, size - count - 4);
4047: #if defined (DEBUG_FAILOVER_MESSAGES)
4048: for (i = 4; i < size; i++) {
4049: /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4050: sprintf (tbuf, " %d", option.data [i]);
4051: failover_print (obuf, obufix, obufmax, tbuf);
4052: }
4053: #endif
4054: break;
4055:
4056: case FT_UINT16:
4057: for (i = 0; i < count; i++) {
4058: val = va_arg (va, u_int32_t);
4059: #if defined (DEBUG_FAILOVER_MESSAGES)
4060: /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4061: sprintf (tbuf, " %d", val);
4062: failover_print (obuf, obufix, obufmax, tbuf);
4063: #endif
4064: putUShort (&option.data [4 + i * 2], val);
4065: }
4066: break;
4067:
4068: case FT_UNDEF:
4069: default:
4070: break;
4071: }
4072:
4073: #if defined DEBUG_FAILOVER_MESSAGES
4074: failover_print (obuf, obufix, obufmax, ")");
4075: #endif
4076: va_end (va);
4077:
4078: /* Now allocate a place to store what we just set up. */
4079: op = dmalloc (sizeof (failover_option_t), MDL);
4080: if (!op) {
4081: dfree (option.data, MDL);
4082: return &null_failover_option;
4083: }
4084:
4085: *op = option;
4086: return op;
4087: }
4088:
4089: /* Send a failover message header. */
4090:
4091: isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
4092: omapi_object_t *connection,
4093: int msg_type, u_int32_t xid, ...)
4094: {
4095: unsigned size = 0;
4096: int bad_option = 0;
4097: int opix = 0;
4098: va_list list;
4099: failover_option_t *option;
4100: unsigned char *opbuf;
4101: isc_result_t status = ISC_R_SUCCESS;
4102: unsigned char cbuf;
4103: struct timeval tv;
4104:
4105: /* Run through the argument list once to compute the length of
4106: the option portion of the message. */
4107: va_start (list, xid);
4108: while ((option = va_arg (list, failover_option_t *))) {
4109: if (option != &skip_failover_option)
4110: size += option -> count;
4111: if (option == &null_failover_option)
4112: bad_option = 1;
4113: }
4114: va_end (list);
4115:
4116: /* Allocate an option buffer, unless we got an error. */
4117: if (!bad_option && size) {
4118: opbuf = dmalloc (size, MDL);
4119: if (!opbuf)
4120: status = ISC_R_NOMEMORY;
4121: } else
4122: opbuf = (unsigned char *)0;
4123:
4124: va_start (list, xid);
4125: while ((option = va_arg (list, failover_option_t *))) {
4126: if (option == &skip_failover_option)
4127: continue;
4128: if (!bad_option && opbuf)
4129: memcpy (&opbuf [opix],
4130: option -> data, option -> count);
4131: if (option != &null_failover_option &&
4132: option != &skip_failover_option) {
4133: opix += option -> count;
4134: dfree (option -> data, MDL);
4135: dfree (option, MDL);
4136: }
4137: }
4138: va_end(list);
4139:
4140: if (bad_option)
4141: return ISC_R_INVALIDARG;
4142:
4143: /* Now send the message header. */
4144:
4145: /* Message length. */
4146: status = omapi_connection_put_uint16 (connection, size + 12);
4147: if (status != ISC_R_SUCCESS)
4148: goto err;
4149:
4150: /* Message type. */
4151: cbuf = msg_type;
4152: status = omapi_connection_copyin (connection, &cbuf, 1);
4153: if (status != ISC_R_SUCCESS)
4154: goto err;
4155:
4156: /* Payload offset. */
4157: cbuf = 12;
4158: status = omapi_connection_copyin (connection, &cbuf, 1);
4159: if (status != ISC_R_SUCCESS)
4160: goto err;
4161:
4162: /* Current time. */
4163: status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
4164: if (status != ISC_R_SUCCESS)
4165: goto err;
4166:
4167: /* Transaction ID. */
4168: status = omapi_connection_put_uint32(connection, xid);
4169: if (status != ISC_R_SUCCESS)
4170: goto err;
4171:
4172: /* Payload. */
4173: if (opbuf) {
4174: status = omapi_connection_copyin (connection, opbuf, size);
4175: if (status != ISC_R_SUCCESS)
4176: goto err;
4177: dfree (opbuf, MDL);
4178: }
4179: if (link -> state_object &&
4180: link -> state_object -> link_to_peer == link) {
4181: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
4182: log_info ("add_timeout +%d %s",
4183: (int)(link -> state_object ->
4184: partner.max_response_delay) / 3,
4185: "dhcp_failover_send_contact");
4186: #endif
4187: tv . tv_sec = cur_time +
4188: (int)(link -> state_object ->
4189: partner.max_response_delay) / 3;
4190: tv . tv_usec = 0;
4191: add_timeout (&tv,
4192: dhcp_failover_send_contact, link -> state_object,
4193: (tvref_t)dhcp_failover_state_reference,
4194: (tvunref_t)dhcp_failover_state_dereference);
4195: }
4196: return status;
4197:
4198: err:
4199: if (opbuf)
4200: dfree (opbuf, MDL);
4201: log_info ("dhcp_failover_put_message: something went wrong.");
4202: omapi_disconnect (connection, 1);
4203: return status;
4204: }
4205:
4206: void dhcp_failover_timeout (void *vstate)
4207: {
4208: dhcp_failover_state_t *state = vstate;
4209: dhcp_failover_link_t *link;
4210:
4211: #if defined (DEBUG_FAILOVER_TIMING)
4212: log_info ("dhcp_failover_timeout");
4213: #endif
4214:
4215: if (!state || state -> type != dhcp_type_failover_state)
4216: return;
4217: link = state -> link_to_peer;
4218: if (!link ||
4219: !link -> outer ||
4220: link -> outer -> type != omapi_type_connection)
4221: return;
4222:
4223: log_error ("timeout waiting for failover peer %s", state -> name);
4224:
4225: /* If we haven't gotten a timely response, blow away the connection.
4226: This will cause the state to change automatically. */
4227: omapi_disconnect (link -> outer, 1);
4228: }
4229:
4230: void dhcp_failover_send_contact (void *vstate)
4231: {
4232: dhcp_failover_state_t *state = vstate;
4233: dhcp_failover_link_t *link;
4234: isc_result_t status;
4235:
4236: #if defined(DEBUG_FAILOVER_MESSAGES) && \
4237: defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
4238: char obuf [64];
4239: unsigned obufix = 0;
4240:
4241: failover_print(obuf, &obufix, sizeof(obuf), "(contact");
4242: #endif
4243:
4244: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
4245: log_info ("dhcp_failover_send_contact");
4246: #endif
4247:
4248: if (!state || state -> type != dhcp_type_failover_state)
4249: return;
4250: link = state -> link_to_peer;
4251: if (!link ||
4252: !link -> outer ||
4253: link -> outer -> type != omapi_type_connection)
4254: return;
4255:
4256: status = (dhcp_failover_put_message
4257: (link, link -> outer,
4258: FTM_CONTACT, link->xid++,
4259: (failover_option_t *)0));
4260:
4261: #if defined(DEBUG_FAILOVER_MESSAGES) && \
4262: defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
4263: if (status != ISC_R_SUCCESS)
4264: failover_print(obuf, &obufix, sizeof(obuf), " (failed)");
4265: failover_print(obuf, &obufix, sizeof(obuf), ")");
4266: if (obufix) {
4267: log_debug ("%s", obuf);
4268: }
1.1.1.1 ! misho 4269: #else
! 4270: IGNORE_UNUSED(status);
1.1 misho 4271: #endif
4272: return;
4273: }
4274:
4275: isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state)
4276: {
4277: dhcp_failover_link_t *link;
4278: isc_result_t status;
4279:
4280: #if defined (DEBUG_FAILOVER_MESSAGES)
4281: char obuf [64];
4282: unsigned obufix = 0;
4283:
4284: # define FMA obuf, &obufix, sizeof obuf
4285: failover_print (FMA, "(state");
4286: #else
4287: # define FMA (char *)0, (unsigned *)0, 0
4288: #endif
4289:
4290: if (!state || state -> type != dhcp_type_failover_state)
4291: return ISC_R_INVALIDARG;
4292: link = state -> link_to_peer;
4293: if (!link ||
4294: !link -> outer ||
4295: link -> outer -> type != omapi_type_connection)
4296: return ISC_R_INVALIDARG;
4297:
4298: status = (dhcp_failover_put_message
4299: (link, link -> outer,
4300: FTM_STATE, link->xid++,
4301: dhcp_failover_make_option (FTO_SERVER_STATE, FMA,
4302: (state -> me.state == startup
4303: ? state -> saved_state
4304: : state -> me.state)),
4305: dhcp_failover_make_option
4306: (FTO_SERVER_FLAGS, FMA,
4307: (state -> service_state == service_startup
4308: ? FTF_SERVER_STARTUP : 0)),
4309: dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos),
4310: (failover_option_t *)0));
4311:
4312: #if defined (DEBUG_FAILOVER_MESSAGES)
4313: if (status != ISC_R_SUCCESS)
4314: failover_print (FMA, " (failed)");
4315: failover_print (FMA, ")");
4316: if (obufix) {
4317: log_debug ("%s", obuf);
4318: }
1.1.1.1 ! misho 4319: #else
! 4320: IGNORE_UNUSED(status);
1.1 misho 4321: #endif
4322: return ISC_R_SUCCESS;
4323: }
4324:
4325: /* Send a connect message. */
4326:
4327: isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
4328: {
4329: dhcp_failover_link_t *link;
4330: dhcp_failover_state_t *state;
4331: isc_result_t status;
4332: #if defined (DEBUG_FAILOVER_MESSAGES)
4333: char obuf [64];
4334: unsigned obufix = 0;
4335:
4336: # define FMA obuf, &obufix, sizeof obuf
4337: failover_print (FMA, "(connect");
4338: #else
4339: # define FMA (char *)0, (unsigned *)0, 0
4340: #endif
4341:
4342: if (!l || l -> type != dhcp_type_failover_link)
4343: return ISC_R_INVALIDARG;
4344: link = (dhcp_failover_link_t *)l;
4345: state = link -> state_object;
4346: if (!l -> outer || l -> outer -> type != omapi_type_connection)
4347: return ISC_R_INVALIDARG;
4348:
4349: status =
4350: (dhcp_failover_put_message
4351: (link, l -> outer,
4352: FTM_CONNECT, link->xid++,
4353: dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
4354: strlen(state->name), state->name),
4355: dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4356: state -> me.max_flying_updates),
4357: dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4358: state -> me.max_response_delay),
4359: dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
4360: "isc-%s", PACKAGE_VERSION),
4361: dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4362: DHCP_FAILOVER_VERSION),
4363: dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
4364: 0, 0),
4365: dhcp_failover_make_option (FTO_MCLT, FMA,
4366: state -> mclt),
4367: (state -> hba
4368: ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
4369: : &skip_failover_option),
4370: (failover_option_t *)0));
4371:
4372: #if defined (DEBUG_FAILOVER_MESSAGES)
4373: if (status != ISC_R_SUCCESS)
4374: failover_print (FMA, " (failed)");
4375: failover_print (FMA, ")");
4376: if (obufix) {
4377: log_debug ("%s", obuf);
4378: }
4379: #endif
4380: return status;
4381: }
4382:
4383: isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
4384: dhcp_failover_state_t *state,
4385: int reason, const char *errmsg)
4386: {
4387: dhcp_failover_link_t *link;
4388: isc_result_t status;
4389: #if defined (DEBUG_FAILOVER_MESSAGES)
4390: char obuf [64];
4391: unsigned obufix = 0;
4392:
4393: # define FMA obuf, &obufix, sizeof obuf
4394: failover_print (FMA, "(connectack");
4395: #else
4396: # define FMA (char *)0, (unsigned *)0, 0
4397: #endif
4398:
4399: if (!l || l -> type != dhcp_type_failover_link)
4400: return ISC_R_INVALIDARG;
4401: link = (dhcp_failover_link_t *)l;
4402: if (!l -> outer || l -> outer -> type != omapi_type_connection)
4403: return ISC_R_INVALIDARG;
4404:
4405: status =
4406: (dhcp_failover_put_message
4407: (link, l -> outer,
4408: FTM_CONNECTACK, link->imsg->xid,
4409: state
4410: ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
4411: strlen(state->name), state->name)
4412: : (link->imsg->options_present & FTB_RELATIONSHIP_NAME)
4413: ? &link->imsg->relationship_name
4414: : &skip_failover_option,
4415: state
4416: ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4417: state -> me.max_flying_updates)
4418: : &skip_failover_option,
4419: state
4420: ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4421: state -> me.max_response_delay)
4422: : &skip_failover_option,
4423: dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
4424: "isc-%s", PACKAGE_VERSION),
4425: dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4426: DHCP_FAILOVER_VERSION),
4427: (link->imsg->options_present & FTB_TLS_REQUEST)
4428: ? dhcp_failover_make_option(FTO_TLS_REPLY, FMA,
4429: 0, 0)
4430: : &skip_failover_option,
4431: reason
4432: ? dhcp_failover_make_option (FTO_REJECT_REASON,
4433: FMA, reason)
4434: : &skip_failover_option,
4435: (reason && errmsg)
4436: ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4437: strlen (errmsg), errmsg)
4438: : &skip_failover_option,
4439: (failover_option_t *)0));
4440:
4441: #if defined (DEBUG_FAILOVER_MESSAGES)
4442: if (status != ISC_R_SUCCESS)
4443: failover_print (FMA, " (failed)");
4444: failover_print (FMA, ")");
4445: if (obufix) {
4446: log_debug ("%s", obuf);
4447: }
4448: #endif
4449: return status;
4450: }
4451:
4452: isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
4453: int reason,
4454: const char *message)
4455: {
4456: dhcp_failover_link_t *link;
4457: isc_result_t status;
4458: #if defined (DEBUG_FAILOVER_MESSAGES)
4459: char obuf [64];
4460: unsigned obufix = 0;
4461:
4462: # define FMA obuf, &obufix, sizeof obuf
4463: failover_print (FMA, "(disconnect");
4464: #else
4465: # define FMA (char *)0, (unsigned *)0, 0
4466: #endif
4467:
4468: if (!l || l -> type != dhcp_type_failover_link)
4469: return ISC_R_INVALIDARG;
4470: link = (dhcp_failover_link_t *)l;
4471: if (!l -> outer || l -> outer -> type != omapi_type_connection)
4472: return ISC_R_INVALIDARG;
4473:
4474: if (!message && reason)
4475: message = dhcp_failover_reject_reason_print (reason);
4476:
4477: status = (dhcp_failover_put_message
4478: (link, l -> outer,
4479: FTM_DISCONNECT, link->xid++,
4480: dhcp_failover_make_option (FTO_REJECT_REASON,
4481: FMA, reason),
4482: (message
4483: ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4484: strlen (message), message)
4485: : &skip_failover_option),
4486: (failover_option_t *)0));
4487:
4488: #if defined (DEBUG_FAILOVER_MESSAGES)
4489: if (status != ISC_R_SUCCESS)
4490: failover_print (FMA, " (failed)");
4491: failover_print (FMA, ")");
4492: if (obufix) {
4493: log_debug ("%s", obuf);
4494: }
4495: #endif
4496: return status;
4497: }
4498:
4499: /* Send a Bind Update message. */
4500:
4501: isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
4502: struct lease *lease)
4503: {
4504: dhcp_failover_link_t *link;
4505: isc_result_t status;
4506: int flags = 0;
4507: binding_state_t transmit_state;
4508: #if defined (DEBUG_FAILOVER_MESSAGES)
4509: char obuf [64];
4510: unsigned obufix = 0;
4511:
4512: # define FMA obuf, &obufix, sizeof obuf
4513: failover_print (FMA, "(bndupd");
4514: #else
4515: # define FMA (char *)0, (unsigned *)0, 0
4516: #endif
4517:
4518: if (!state -> link_to_peer ||
4519: state -> link_to_peer -> type != dhcp_type_failover_link)
4520: return ISC_R_INVALIDARG;
4521: link = (dhcp_failover_link_t *)state -> link_to_peer;
4522:
4523: if (!link -> outer || link -> outer -> type != omapi_type_connection)
4524: return ISC_R_INVALIDARG;
4525:
4526: transmit_state = lease->desired_binding_state;
4527: if (lease->flags & RESERVED_LEASE) {
4528: /* If we are listing an allocable (not yet ACTIVE etc) lease
4529: * as reserved, toggle to the peer's 'free state', per the
4530: * draft. This gives the peer permission to alloc it to the
4531: * chaddr/uid-named client.
4532: */
4533: if ((state->i_am == primary) && (transmit_state == FTS_FREE))
4534: transmit_state = FTS_BACKUP;
4535: else if ((state->i_am == secondary) &&
4536: (transmit_state == FTS_BACKUP))
4537: transmit_state = FTS_FREE;
4538:
4539: flags |= FTF_IP_FLAG_RESERVE;
4540: }
4541: if (lease->flags & BOOTP_LEASE)
4542: flags |= FTF_IP_FLAG_BOOTP;
4543:
4544: /* last_xid == 0 is illegal, seek past zero if we hit it. */
4545: if (link->xid == 0)
4546: link->xid = 1;
4547:
4548: lease->last_xid = link->xid++;
4549:
4550: /* Send the update. */
4551: status = (dhcp_failover_put_message
4552: (link, link -> outer,
4553: FTM_BNDUPD, lease->last_xid,
4554: dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4555: lease -> ip_addr.len,
4556: lease -> ip_addr.iabuf),
4557: dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4558: lease -> desired_binding_state),
4559: lease -> uid_len
4560: ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4561: lease -> uid_len,
4562: lease -> uid)
4563: : &skip_failover_option,
4564: lease -> hardware_addr.hlen
4565: ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4566: lease -> hardware_addr.hlen,
4567: lease -> hardware_addr.hbuf)
4568: : &skip_failover_option,
4569: dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4570: lease -> ends),
4571: dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4572: lease -> tstp),
4573: dhcp_failover_make_option (FTO_STOS, FMA,
4574: lease -> starts),
4575: (lease->cltt != 0) ?
4576: dhcp_failover_make_option(FTO_CLTT, FMA, lease->cltt) :
4577: &skip_failover_option, /* No CLTT */
4578: flags ? dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
4579: flags) :
4580: &skip_failover_option, /* No IP_FLAGS */
4581: &skip_failover_option, /* XXX DDNS */
4582: &skip_failover_option, /* XXX request options */
4583: &skip_failover_option, /* XXX reply options */
4584: (failover_option_t *)0));
4585:
4586: #if defined (DEBUG_FAILOVER_MESSAGES)
4587: if (status != ISC_R_SUCCESS)
4588: failover_print (FMA, " (failed)");
4589: failover_print (FMA, ")");
4590: if (obufix) {
4591: log_debug ("%s", obuf);
4592: }
4593: #endif
4594: return status;
4595: }
4596:
4597: /* Send a Bind ACK message. */
4598:
4599: isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
4600: failover_message_t *msg,
4601: int reason, const char *message)
4602: {
4603: dhcp_failover_link_t *link;
4604: isc_result_t status;
4605: #if defined (DEBUG_FAILOVER_MESSAGES)
4606: char obuf [64];
4607: unsigned obufix = 0;
4608:
4609: # define FMA obuf, &obufix, sizeof obuf
4610: failover_print (FMA, "(bndack");
4611: #else
4612: # define FMA (char *)0, (unsigned *)0, 0
4613: #endif
4614:
4615: if (!state -> link_to_peer ||
4616: state -> link_to_peer -> type != dhcp_type_failover_link)
4617: return ISC_R_INVALIDARG;
4618: link = (dhcp_failover_link_t *)state -> link_to_peer;
4619:
4620: if (!link -> outer || link -> outer -> type != omapi_type_connection)
4621: return ISC_R_INVALIDARG;
4622:
4623: if (!message && reason)
4624: message = dhcp_failover_reject_reason_print (reason);
4625:
4626: /* Send the update. */
4627: status = (dhcp_failover_put_message
4628: (link, link -> outer,
4629: FTM_BNDACK, msg->xid,
4630: dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
4631: sizeof msg -> assigned_addr,
4632: &msg -> assigned_addr),
4633: #ifdef DO_BNDACK_SHOULD_NOT
4634: dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
4635: msg -> binding_status),
4636: (msg -> options_present & FTB_CLIENT_IDENTIFIER)
4637: ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
4638: msg -> client_identifier.count,
4639: msg -> client_identifier.data)
4640: : &skip_failover_option,
4641: (msg -> options_present & FTB_CHADDR)
4642: ? dhcp_failover_make_option (FTO_CHADDR, FMA,
4643: msg -> chaddr.count,
4644: msg -> chaddr.data)
4645: : &skip_failover_option,
4646: dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
4647: msg -> expiry),
4648: dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
4649: msg -> potential_expiry),
4650: dhcp_failover_make_option (FTO_STOS, FMA,
4651: msg -> stos),
4652: (msg->options_present & FTB_CLTT) ?
4653: dhcp_failover_make_option(FTO_CLTT, FMA, msg->cltt) :
4654: &skip_failover_option, /* No CLTT in the msg to ack. */
4655: ((msg->options_present & FTB_IP_FLAGS) && msg->ip_flags) ?
4656: dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
4657: msg->ip_flags)
4658: : &skip_failover_option,
4659: #endif /* DO_BNDACK_SHOULD_NOT */
4660: reason
4661: ? dhcp_failover_make_option(FTO_REJECT_REASON, FMA, reason)
4662: : &skip_failover_option,
4663: (reason && message)
4664: ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4665: strlen (message), message)
4666: : &skip_failover_option,
4667: #ifdef DO_BNDACK_SHOULD_NOT
4668: &skip_failover_option, /* XXX DDNS */
4669: &skip_failover_option, /* XXX request options */
4670: &skip_failover_option, /* XXX reply options */
4671: #endif /* DO_BNDACK_SHOULD_NOT */
4672: (failover_option_t *)0));
4673:
4674: #if defined (DEBUG_FAILOVER_MESSAGES)
4675: if (status != ISC_R_SUCCESS)
4676: failover_print (FMA, " (failed)");
4677: failover_print (FMA, ")");
4678: if (obufix) {
4679: log_debug ("%s", obuf);
4680: }
4681: #endif
4682: return status;
4683: }
4684:
4685: isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
4686: {
4687: dhcp_failover_link_t *link;
4688: isc_result_t status;
4689: #if defined (DEBUG_FAILOVER_MESSAGES)
4690: char obuf [64];
4691: unsigned obufix = 0;
4692:
4693: # define FMA obuf, &obufix, sizeof obuf
4694: failover_print (FMA, "(poolreq");
4695: #else
4696: # define FMA (char *)0, (unsigned *)0, 0
4697: #endif
4698:
4699: if (!state -> link_to_peer ||
4700: state -> link_to_peer -> type != dhcp_type_failover_link)
4701: return ISC_R_INVALIDARG;
4702: link = (dhcp_failover_link_t *)state -> link_to_peer;
4703:
4704: if (!link -> outer || link -> outer -> type != omapi_type_connection)
4705: return ISC_R_INVALIDARG;
4706:
4707: status = (dhcp_failover_put_message
4708: (link, link -> outer,
4709: FTM_POOLREQ, link->xid++,
4710: (failover_option_t *)0));
4711:
4712: #if defined (DEBUG_FAILOVER_MESSAGES)
4713: if (status != ISC_R_SUCCESS)
4714: failover_print (FMA, " (failed)");
4715: failover_print (FMA, ")");
4716: if (obufix) {
4717: log_debug ("%s", obuf);
4718: }
4719: #endif
4720: return status;
4721: }
4722:
4723: isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
4724: int leases)
4725: {
4726: dhcp_failover_link_t *link;
4727: isc_result_t status;
4728: #if defined (DEBUG_FAILOVER_MESSAGES)
4729: char obuf [64];
4730: unsigned obufix = 0;
4731:
4732: # define FMA obuf, &obufix, sizeof obuf
4733: failover_print (FMA, "(poolresp");
4734: #else
4735: # define FMA (char *)0, (unsigned *)0, 0
4736: #endif
4737:
4738: if (!state -> link_to_peer ||
4739: state -> link_to_peer -> type != dhcp_type_failover_link)
4740: return ISC_R_INVALIDARG;
4741: link = (dhcp_failover_link_t *)state -> link_to_peer;
4742:
4743: if (!link -> outer || link -> outer -> type != omapi_type_connection)
4744: return ISC_R_INVALIDARG;
4745:
4746: status = (dhcp_failover_put_message
4747: (link, link -> outer,
4748: FTM_POOLRESP, link->imsg->xid,
4749: dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
4750: leases),
4751: (failover_option_t *)0));
4752:
4753: #if defined (DEBUG_FAILOVER_MESSAGES)
4754: if (status != ISC_R_SUCCESS)
4755: failover_print (FMA, " (failed)");
4756: failover_print (FMA, ")");
4757: if (obufix) {
4758: log_debug ("%s", obuf);
4759: }
4760: #endif
4761: return status;
4762: }
4763:
4764: isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state)
4765: {
4766: dhcp_failover_link_t *link;
4767: isc_result_t status;
4768: #if defined (DEBUG_FAILOVER_MESSAGES)
4769: char obuf [64];
4770: unsigned obufix = 0;
4771:
4772: # define FMA obuf, &obufix, sizeof obuf
4773: failover_print (FMA, "(updreq");
4774: #else
4775: # define FMA (char *)0, (unsigned *)0, 0
4776: #endif
4777:
4778: if (!state -> link_to_peer ||
4779: state -> link_to_peer -> type != dhcp_type_failover_link)
4780: return ISC_R_INVALIDARG;
4781: link = (dhcp_failover_link_t *)state -> link_to_peer;
4782:
4783: if (!link -> outer || link -> outer -> type != omapi_type_connection)
4784: return ISC_R_INVALIDARG;
4785:
4786: if (state -> curUPD)
4787: return ISC_R_ALREADYRUNNING;
4788:
4789: status = (dhcp_failover_put_message
4790: (link, link -> outer,
4791: FTM_UPDREQ, link->xid++,
4792: (failover_option_t *)0));
4793:
4794: if (status == ISC_R_SUCCESS)
4795: state -> curUPD = FTM_UPDREQ;
4796:
4797: #if defined (DEBUG_FAILOVER_MESSAGES)
4798: if (status != ISC_R_SUCCESS)
4799: failover_print (FMA, " (failed)");
4800: failover_print (FMA, ")");
4801: if (obufix) {
4802: log_debug ("%s", obuf);
4803: }
4804: #endif
4805: log_info ("Sent update request message to %s", state -> name);
4806: return status;
4807: }
4808:
4809: isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t
4810: *state)
4811: {
4812: dhcp_failover_link_t *link;
4813: isc_result_t status;
4814: #if defined (DEBUG_FAILOVER_MESSAGES)
4815: char obuf [64];
4816: unsigned obufix = 0;
4817:
4818: # define FMA obuf, &obufix, sizeof obuf
4819: failover_print (FMA, "(updreqall");
4820: #else
4821: # define FMA (char *)0, (unsigned *)0, 0
4822: #endif
4823:
4824: if (!state -> link_to_peer ||
4825: state -> link_to_peer -> type != dhcp_type_failover_link)
4826: return ISC_R_INVALIDARG;
4827: link = (dhcp_failover_link_t *)state -> link_to_peer;
4828:
4829: if (!link -> outer || link -> outer -> type != omapi_type_connection)
4830: return ISC_R_INVALIDARG;
4831:
4832: /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
4833: if (state -> curUPD && (state -> curUPD != FTM_UPDREQ))
4834: return ISC_R_ALREADYRUNNING;
4835:
4836: status = (dhcp_failover_put_message
4837: (link, link -> outer,
4838: FTM_UPDREQALL, link->xid++,
4839: (failover_option_t *)0));
4840:
4841: if (status == ISC_R_SUCCESS)
4842: state -> curUPD = FTM_UPDREQALL;
4843:
4844: #if defined (DEBUG_FAILOVER_MESSAGES)
4845: if (status != ISC_R_SUCCESS)
4846: failover_print (FMA, " (failed)");
4847: failover_print (FMA, ")");
4848: if (obufix) {
4849: log_debug ("%s", obuf);
4850: }
4851: #endif
4852: log_info ("Sent update request all message to %s", state -> name);
4853: return status;
4854: }
4855:
4856: isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state)
4857: {
4858: dhcp_failover_link_t *link;
4859: isc_result_t status;
4860: #if defined (DEBUG_FAILOVER_MESSAGES)
4861: char obuf [64];
4862: unsigned obufix = 0;
4863:
4864: # define FMA obuf, &obufix, sizeof obuf
4865: failover_print (FMA, "(upddone");
4866: #else
4867: # define FMA (char *)0, (unsigned *)0, 0
4868: #endif
4869:
4870: if (!state -> link_to_peer ||
4871: state -> link_to_peer -> type != dhcp_type_failover_link)
4872: return ISC_R_INVALIDARG;
4873: link = (dhcp_failover_link_t *)state -> link_to_peer;
4874:
4875: if (!link -> outer || link -> outer -> type != omapi_type_connection)
4876: return ISC_R_INVALIDARG;
4877:
4878: status = (dhcp_failover_put_message
4879: (link, link -> outer,
4880: FTM_UPDDONE, state->updxid,
4881: (failover_option_t *)0));
4882:
4883: #if defined (DEBUG_FAILOVER_MESSAGES)
4884: if (status != ISC_R_SUCCESS)
4885: failover_print (FMA, " (failed)");
4886: failover_print (FMA, ")");
4887: if (obufix) {
4888: log_debug ("%s", obuf);
4889: }
4890: #endif
4891:
4892: log_info ("Sent update done message to %s", state -> name);
4893:
4894: state->updxid--; /* Paranoia, just so it mismatches. */
4895:
4896: /* There may be uncommitted leases at this point (since
4897: dhcp_failover_process_bind_ack() doesn't commit leases);
4898: commit the lease file. */
4899: commit_leases();
4900:
4901: return status;
4902: }
4903:
4904: /*
4905: * failover_lease_is_better() compares the binding update in 'msg' with
4906: * the current lease in 'lease'. If the determination is that the binding
4907: * update shouldn't be allowed to update/crush more critical binding info
4908: * on the lease, the lease is preferred. A value of true is returned if the
4909: * local lease is preferred, or false if the remote binding update is
4910: * preferred.
4911: *
4912: * For now this function is hopefully simplistic and trivial. It may be that
4913: * a more detailed system of preferences is required, so this is something we
4914: * should monitor as we gain experience with these dueling events.
4915: */
4916: static isc_boolean_t
4917: failover_lease_is_better(dhcp_failover_state_t *state, struct lease *lease,
4918: failover_message_t *msg)
4919: {
4920: binding_state_t local_state;
4921: TIME msg_cltt;
4922:
4923: if (lease->binding_state != lease->desired_binding_state)
4924: local_state = lease->desired_binding_state;
4925: else
4926: local_state = lease->binding_state;
4927:
4928: if ((msg->options_present & FTB_CLTT) != 0)
4929: msg_cltt = msg->cltt;
4930: else
4931: msg_cltt = 0;
4932:
4933: switch(local_state) {
4934: case FTS_ACTIVE:
4935: if (msg->binding_status == FTS_ACTIVE) {
4936: if (msg_cltt < lease->cltt)
4937: return ISC_TRUE;
4938: else if (msg_cltt > lease->cltt)
4939: return ISC_FALSE;
4940: else if (state->i_am == primary)
4941: return ISC_TRUE;
4942: else
4943: return ISC_FALSE;
4944: } else if (msg->binding_status == FTS_EXPIRED) {
4945: return ISC_FALSE;
4946: }
4947: /* FALL THROUGH */
4948:
4949: case FTS_FREE:
4950: case FTS_BACKUP:
4951: case FTS_EXPIRED:
4952: case FTS_RELEASED:
4953: case FTS_ABANDONED:
4954: case FTS_RESET:
4955: if (msg->binding_status == FTS_ACTIVE)
4956: return ISC_FALSE;
4957: else if (state->i_am == primary)
4958: return ISC_TRUE;
4959: else
4960: return ISC_FALSE;
4961: /* FALL THROUGH to impossible condition */
4962:
4963: default:
4964: log_fatal("Impossible condition at %s:%d.", MDL);
4965: }
4966:
4967: log_fatal("Impossible condition at %s:%d.", MDL);
4968: /* Silence compiler warning. */
4969: return ISC_FALSE;
4970: }
4971:
4972: isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
4973: failover_message_t *msg)
4974: {
4975: struct lease *lt, *lease;
4976: struct iaddr ia;
4977: int reason = FTR_MISC_REJECT;
4978: const char *message;
4979: int new_binding_state;
4980: int send_to_backup = 0;
4981: int required_options;
4982: isc_boolean_t chaddr_changed = ISC_FALSE;
4983: isc_boolean_t ident_changed = ISC_FALSE;
4984:
4985: /* Validate the binding update. */
4986: required_options = FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS;
4987: if ((msg->options_present & required_options) != required_options) {
4988: message = "binding update lacks required options";
4989: reason = FTR_MISSING_BINDINFO;
4990: goto bad;
4991: }
4992:
4993: ia.len = sizeof msg -> assigned_addr;
4994: memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4995:
4996: lease = (struct lease *)0;
4997: lt = (struct lease *)0;
4998: if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4999: message = "unknown IP address";
5000: reason = FTR_ILLEGAL_IP_ADDR;
5001: goto bad;
5002: }
5003:
5004: /*
5005: * If this lease is covered by a different failover peering
5006: * relationship, assert an error.
5007: */
5008: if ((lease->pool == NULL) || (lease->pool->failover_peer == NULL) ||
5009: (lease->pool->failover_peer != state)) {
5010: message = "IP address is covered by a different failover "
5011: "relationship state";
5012: reason = FTR_ILLEGAL_IP_ADDR;
5013: goto bad;
5014: }
5015:
5016: /*
5017: * Dueling updates: This happens when both servers send a BNDUPD
5018: * at the same time. We want the best update to win, which means
5019: * we reject if we think ours is better, or cancel if we think the
5020: * peer's is better. We only assert a problem if the lease is on
5021: * the ACK queue, not on the UPDATE queue. This means that after
5022: * accepting this server's BNDUPD, we will send our own BNDUPD
5023: * /after/ sending the BNDACK (this order was recently enforced in
5024: * queue processing).
5025: */
5026: if ((lease->flags & ON_ACK_QUEUE) != 0) {
5027: if (failover_lease_is_better(state, lease, msg)) {
5028: message = "incoming update is less critical than "
5029: "outgoing update";
5030: reason = FTR_LESS_CRIT_BIND_INFO;
5031: goto bad;
5032: } else {
5033: /* This makes it so we ignore any spurious ACKs. */
5034: dhcp_failover_ack_queue_remove(state, lease);
5035: }
5036: }
5037:
5038: /* Install the new info. Start by taking a copy to markup. */
5039: if (!lease_copy (<, lease, MDL)) {
5040: message = "no memory";
5041: goto bad;
5042: }
5043:
5044: if (msg -> options_present & FTB_CHADDR) {
5045: if (msg->binding_status == FTS_ABANDONED) {
5046: message = "BNDUPD to ABANDONED with a CHADDR";
5047: goto bad;
5048: }
5049: if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
5050: message = "chaddr too long";
5051: goto bad;
5052: }
5053:
5054: if ((lt->hardware_addr.hlen != msg->chaddr.count) ||
5055: (memcmp(lt->hardware_addr.hbuf, msg->chaddr.data,
5056: msg->chaddr.count) != 0))
5057: chaddr_changed = ISC_TRUE;
5058:
5059: lt -> hardware_addr.hlen = msg -> chaddr.count;
5060: memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
5061: msg -> chaddr.count);
5062: } else if (msg->binding_status == FTS_ACTIVE ||
5063: msg->binding_status == FTS_EXPIRED ||
5064: msg->binding_status == FTS_RELEASED) {
5065: message = "BNDUPD without CHADDR";
5066: reason = FTR_MISSING_BINDINFO;
5067: goto bad;
5068: } else if (msg->binding_status == FTS_ABANDONED) {
5069: chaddr_changed = ISC_TRUE;
5070: lt->hardware_addr.hlen = 0;
5071: if (lt->scope)
5072: binding_scope_dereference(<->scope, MDL);
5073: }
5074:
5075: /* There is no explicit message content to indicate that the client
5076: * supplied no client-identifier. So if we don't hear of a value,
5077: * we discard the last one.
5078: */
5079: if (msg->options_present & FTB_CLIENT_IDENTIFIER) {
5080: if (msg->binding_status == FTS_ABANDONED) {
5081: message = "BNDUPD to ABANDONED with client-id";
5082: goto bad;
5083: }
5084:
5085: if ((lt->uid_len != msg->client_identifier.count) ||
5086: (lt->uid == NULL) || /* Sanity; should never happen. */
5087: (memcmp(lt->uid, msg->client_identifier.data,
5088: lt->uid_len) != 0))
5089: ident_changed = ISC_TRUE;
5090:
5091: lt->uid_len = msg->client_identifier.count;
5092:
5093: /* Allocate the lt->uid buffer if we haven't already, or
5094: * re-allocate the lt-uid buffer if we have one that is not
5095: * large enough. Otherwise, just use the extant buffer.
5096: */
5097: if (!lt->uid || lt->uid == lt->uid_buf ||
5098: lt->uid_len > lt->uid_max) {
5099: if (lt->uid && lt->uid != lt->uid_buf)
5100: dfree(lt->uid, MDL);
5101:
5102: if (lt->uid_len > sizeof(lt->uid_buf)) {
5103: lt->uid_max = lt->uid_len;
5104: lt->uid = dmalloc(lt->uid_len, MDL);
5105: if (!lt->uid) {
5106: message = "no memory";
5107: goto bad;
5108: }
5109: } else {
5110: lt->uid_max = sizeof(lt->uid_buf);
5111: lt->uid = lt->uid_buf;
5112: }
5113: }
5114: memcpy (lt -> uid,
5115: msg -> client_identifier.data, lt -> uid_len);
5116: } else if (lt->uid && msg->binding_status != FTS_RESET &&
5117: msg->binding_status != FTS_FREE &&
5118: msg->binding_status != FTS_BACKUP) {
5119: ident_changed = ISC_TRUE;
5120: if (lt->uid != lt->uid_buf)
5121: dfree (lt->uid, MDL);
5122: lt->uid = NULL;
5123: lt->uid_max = lt->uid_len = 0;
5124: }
5125:
5126: /*
5127: * A server's configuration can assign a 'binding scope';
5128: *
5129: * set var = "value";
5130: *
5131: * The problem with these binding scopes is that they are refreshed
5132: * when the server processes a client's DHCP packet. A local binding
5133: * scope is trash, then, when the lease has been assigned by the
5134: * partner server. There is no real way to detect this, a peer may
5135: * be updating us (as through potential conflict) with a binding we
5136: * sent them, but we can trivially detect the /problematic/ case;
5137: *
5138: * lease is free.
5139: * primary allocates lease to client A, assigns ddns name A.
5140: * primary fails.
5141: * secondary enters partner down.
5142: * lease expires, and is set free.
5143: * lease is allocated to client B and given ddns name B.
5144: * primary recovers.
5145: *
5146: * The binding update in this case will be active->active, but the
5147: * client identification on the lease will have changed. The ddns
5148: * update on client A will have leaked if we just remove the binding
5149: * scope blindly.
5150: */
5151: if (msg->binding_status == FTS_ACTIVE &&
5152: (chaddr_changed || ident_changed)) {
5153: ddns_removals(lease, NULL);
5154:
5155: if (lease->scope != NULL)
5156: binding_scope_dereference(&lease->scope, MDL);
5157: }
5158:
5159: /* XXX Times may need to be adjusted based on clock skew! */
5160: if (msg -> options_present & FTB_STOS) {
5161: lt -> starts = msg -> stos;
5162: }
5163: if (msg -> options_present & FTB_LEASE_EXPIRY) {
5164: lt -> ends = msg -> expiry;
5165: }
5166: if (msg->options_present & FTB_POTENTIAL_EXPIRY) {
5167: lt->atsfp = lt->tsfp = msg->potential_expiry;
5168: }
5169: if (msg->options_present & FTB_IP_FLAGS) {
5170: if (msg->ip_flags & FTF_IP_FLAG_RESERVE) {
5171: if ((((state->i_am == primary) &&
5172: (lease->binding_state == FTS_FREE)) ||
5173: ((state->i_am == secondary) &&
5174: (lease->binding_state == FTS_BACKUP))) &&
5175: !(lease->flags & RESERVED_LEASE)) {
5176: message = "Address is not reserved.";
5177: reason = FTR_IP_NOT_RESERVED;
5178: goto bad;
5179: }
5180:
5181: lt->flags |= RESERVED_LEASE;
5182: } else
5183: lt->flags &= ~RESERVED_LEASE;
5184:
5185: if (msg->ip_flags & FTF_IP_FLAG_BOOTP) {
5186: if ((((state->i_am == primary) &&
5187: (lease->binding_state == FTS_FREE)) ||
5188: ((state->i_am == secondary) &&
5189: (lease->binding_state == FTS_BACKUP))) &&
5190: !(lease->flags & BOOTP_LEASE)) {
5191: message = "Address is not allocated to BOOTP.";
5192: goto bad;
5193: }
5194: lt->flags |= BOOTP_LEASE;
5195: } else
5196: lt->flags &= ~BOOTP_LEASE;
5197:
5198: if (msg->ip_flags & ~(FTF_IP_FLAG_RESERVE | FTF_IP_FLAG_BOOTP))
5199: log_info("Unknown IP-flags set in BNDUPD (0x%x).",
5200: msg->ip_flags);
5201: } else /* Flags may only not appear if the values are zero. */
5202: lt->flags &= ~(RESERVED_LEASE | BOOTP_LEASE);
5203:
5204: #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
5205: log_info ("processing state transition for %s: %s to %s",
5206: piaddr (lease -> ip_addr),
5207: binding_state_print (lease -> binding_state),
5208: binding_state_print (msg -> binding_status));
5209: #endif
5210:
5211: /* If we're in normal state, make sure the state transition
5212: we got is valid. */
5213: if (state -> me.state == normal) {
5214: new_binding_state =
5215: (normal_binding_state_transition_check
5216: (lease, state, msg -> binding_status,
5217: msg -> potential_expiry));
5218: /* XXX if the transition the peer asked for isn't
5219: XXX allowed, maybe we should make the transition
5220: XXX into potential-conflict at this point. */
5221: } else {
5222: new_binding_state =
5223: (conflict_binding_state_transition_check
5224: (lease, state, msg -> binding_status,
5225: msg -> potential_expiry));
5226: }
5227: if (new_binding_state != msg -> binding_status) {
5228: char outbuf [100];
5229:
5230: if (snprintf (outbuf, sizeof outbuf,
5231: "%s: invalid state transition: %s to %s",
5232: piaddr (lease -> ip_addr),
5233: binding_state_print (lease -> binding_state),
5234: binding_state_print (msg -> binding_status))
5235: >= sizeof outbuf)
5236: log_fatal ("%s: impossible outbuf overflow",
5237: "dhcp_failover_process_bind_update");
5238:
5239: dhcp_failover_send_bind_ack (state, msg,
5240: FTR_FATAL_CONFLICT,
5241: outbuf);
5242: goto out;
5243: }
5244: if (new_binding_state == FTS_EXPIRED ||
5245: new_binding_state == FTS_RELEASED ||
5246: new_binding_state == FTS_RESET) {
5247: lt -> next_binding_state = FTS_FREE;
5248:
5249: /* Mac address affinity. Assign the lease to
5250: * BACKUP state if we are the primary and the
5251: * peer is more likely to reallocate this lease
5252: * to a returning client.
5253: */
5254: if ((state->i_am == primary) &&
5255: !(lt->flags & (RESERVED_LEASE | BOOTP_LEASE)))
5256: send_to_backup = peer_wants_lease(lt);
5257: } else {
5258: lt -> next_binding_state = new_binding_state;
5259: }
5260: msg -> binding_status = lt -> next_binding_state;
5261:
5262: /* Try to install the new information. */
5263: if (!supersede_lease (lease, lt, 0, 0, 0) ||
5264: !write_lease (lease)) {
5265: message = "database update failed";
5266: bad:
5267: dhcp_failover_send_bind_ack (state, msg, reason, message);
5268: goto out;
5269: } else {
5270: dhcp_failover_queue_ack (state, msg);
5271: }
5272:
5273: /* If it is probably wise, assign lease to backup state if the peer
5274: * is not already hoarding leases.
5275: */
5276: if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
5277: lease->next_binding_state = FTS_BACKUP;
5278: lease->tstp = cur_time;
5279: lease->starts = cur_time;
5280:
5281: if (!supersede_lease(lease, NULL, 0, 1, 0) ||
5282: !write_lease(lease))
5283: log_error("can't commit lease %s for mac addr "
5284: "affinity", piaddr(lease->ip_addr));
5285:
5286: dhcp_failover_send_updates(state);
5287: }
5288:
5289: out:
5290: if (lt)
5291: lease_dereference (<, MDL);
5292: if (lease)
5293: lease_dereference (&lease, MDL);
5294:
5295: return ISC_R_SUCCESS;
5296: }
5297:
5298: /* This was hairy enough I didn't want to do it all in an if statement.
5299: *
5300: * Returns: Truth is the secondary is allowed to get more leases based upon
5301: * MAC address affinity. False otherwise.
5302: */
5303: static inline int
5304: secondary_not_hoarding(dhcp_failover_state_t *state, struct pool *p) {
5305: int total;
5306: int hold;
5307: int lts;
5308:
5309: total = p->free_leases + p->backup_leases;
5310:
5311: /* How many leases is one side or the other allowed to "hold"? */
5312: hold = ((total * state->max_lease_ownership) + 50) / 100;
5313:
5314: /* If we were to send leases (or if the secondary were to send us
5315: * leases in the negative direction), how many would that be?
5316: */
5317: lts = (p->free_leases - p->backup_leases) / 2;
5318:
5319: /* The peer is not hoarding leases if we would send them more leases
5320: * (or they would take fewer leases) than the maximum they are allowed
5321: * to hold (the negative hold).
5322: */
5323: return(lts > -hold);
5324: }
5325:
5326: isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
5327: failover_message_t *msg)
5328: {
5329: struct lease *lt = (struct lease *)0;
5330: struct lease *lease = (struct lease *)0;
5331: struct iaddr ia;
5332: const char *message = "no memory";
5333: u_int32_t pot_expire;
5334: int send_to_backup = ISC_FALSE;
5335: struct timeval tv;
5336:
5337: ia.len = sizeof msg -> assigned_addr;
5338: memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
5339:
5340: if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
5341: message = "no such lease";
5342: goto bad;
5343: }
5344:
5345: /* XXX check for conflicts. */
5346: if (msg -> options_present & FTB_REJECT_REASON) {
5347: log_error ("bind update on %s from %s rejected: %.*s",
5348: piaddr (ia), state -> name,
5349: (int)((msg -> options_present & FTB_MESSAGE)
5350: ? msg -> message.count
5351: : strlen (dhcp_failover_reject_reason_print
5352: (msg -> reject_reason))),
5353: (msg -> options_present & FTB_MESSAGE)
5354: ? (const char *)(msg -> message.data)
5355: : (dhcp_failover_reject_reason_print
5356: (msg -> reject_reason)));
5357: goto unqueue;
5358: }
5359:
5360: /* Silently discard acks for leases we did not update (or multiple
5361: * acks).
5362: */
5363: if (!lease->last_xid)
5364: goto unqueue;
5365:
5366: if (lease->last_xid != msg->xid) {
5367: message = "xid mismatch";
5368: goto bad;
5369: }
5370:
5371: /* XXX Times may need to be adjusted based on clock skew! */
5372: if (msg->options_present & FTO_POTENTIAL_EXPIRY)
5373: pot_expire = msg->potential_expiry;
5374: else
5375: pot_expire = lease->tstp;
5376:
5377: /* If the lease was desired to enter a binding state, we set
5378: * such a value upon transmitting a bndupd. We do not clear it
5379: * if we receive a bndupd in the meantime (or change the state
5380: * of the lease again ourselves), but we do set binding_state
5381: * if we get a bndupd.
5382: *
5383: * So desired_binding_state tells us what we sent a bndupd for,
5384: * and binding_state tells us what we have since determined in
5385: * the meantime.
5386: */
5387: if (lease->desired_binding_state == FTS_EXPIRED ||
5388: lease->desired_binding_state == FTS_RESET ||
5389: lease->desired_binding_state == FTS_RELEASED)
5390: {
5391: /* It is not a problem to do this directly as we call
5392: * supersede_lease immediately after: the lease is requeued
5393: * even if its sort order (tsfp) has changed.
5394: */
5395: lease->atsfp = lease->tsfp = pot_expire;
5396: if ((state->i_am == secondary) &&
5397: (lease->flags & RESERVED_LEASE))
5398: lease->next_binding_state = FTS_BACKUP;
5399: else
5400: lease->next_binding_state = FTS_FREE;
5401: /* Clear this condition for the next go-round. */
5402: lease->desired_binding_state = lease->next_binding_state;
5403: supersede_lease(lease, (struct lease *)0, 0, 0, 0);
5404: write_lease(lease);
5405:
5406: /* Lease has returned to FREE state from the
5407: * transitional states. If the lease 'belongs'
5408: * to a client that would be served by the
5409: * peer, process a binding update now to send
5410: * the lease to backup state. But not if we
5411: * think we already have.
5412: */
5413: if (state->i_am == primary &&
5414: !(lease->flags & (RESERVED_LEASE | BOOTP_LEASE)) &&
5415: peer_wants_lease(lease))
5416: send_to_backup = ISC_TRUE;
5417:
5418: if (!send_to_backup && state->me.state == normal)
5419: commit_leases();
5420: } else {
5421: /* XXX It could be a problem to do this directly if the lease
5422: * XXX is sorted by tsfp.
5423: */
5424: lease->atsfp = lease->tsfp = pot_expire;
5425: if (lease->desired_binding_state != lease->binding_state) {
5426: lease->next_binding_state =
5427: lease->desired_binding_state;
5428: supersede_lease(lease,
5429: (struct lease *)0, 0, 0, 0);
5430: }
5431: write_lease(lease);
5432: /* Commit the lease only after a two-second timeout,
5433: so that if we get a bunch of acks in quick
5434: succession (e.g., when stealing leases from the
5435: secondary), we do not do an immediate commit for
5436: each one. */
5437: tv.tv_sec = cur_time + 2;
5438: tv.tv_usec = 0;
5439: add_timeout(&tv, commit_leases_timeout, (void *)0, 0, 0);
5440: }
5441:
5442: unqueue:
5443: dhcp_failover_ack_queue_remove (state, lease);
5444:
5445: /* If we are supposed to send an update done after we send
5446: this lease, go ahead and send it. */
5447: if (state -> send_update_done == lease) {
5448: lease_dereference (&state -> send_update_done, MDL);
5449: dhcp_failover_send_update_done (state);
5450: }
5451:
5452: /* Now that the lease is off the ack queue, consider putting it
5453: * back on the update queue for mac address affinity.
5454: */
5455: if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
5456: lease->next_binding_state = FTS_BACKUP;
5457: lease->tstp = lease->starts = cur_time;
5458:
5459: if (!supersede_lease(lease, NULL, 0, 1, 0) ||
5460: !write_lease(lease))
5461: log_error("can't commit lease %s for "
5462: "client affinity", piaddr(lease->ip_addr));
5463:
5464: if (state->me.state == normal)
5465: commit_leases();
5466: }
5467:
5468: /* If there are updates pending, we've created space to send at
5469: least one. */
5470: dhcp_failover_send_updates (state);
5471:
5472: out:
5473: lease_dereference (&lease, MDL);
5474: if (lt)
5475: lease_dereference (<, MDL);
5476:
5477: return ISC_R_SUCCESS;
5478:
5479: bad:
5480: log_info ("bind update on %s got ack from %s: %s.",
5481: piaddr (ia), state -> name, message);
5482: goto out;
5483: }
5484:
5485: isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
5486: int everythingp)
5487: {
5488: struct shared_network *s;
5489: struct pool *p;
5490: struct lease *l;
5491: int i;
5492: #define FREE_LEASES 0
5493: #define ACTIVE_LEASES 1
5494: #define EXPIRED_LEASES 2
5495: #define ABANDONED_LEASES 3
5496: #define BACKUP_LEASES 4
5497: #define RESERVED_LEASES 5
5498: struct lease **lptr[RESERVED_LEASES+1];
5499:
5500: /* Loop through each pool in each shared network and call the
5501: expiry routine on the pool. */
5502: for (s = shared_networks; s; s = s -> next) {
5503: for (p = s -> pools; p; p = p -> next) {
5504: if (p->failover_peer != state)
5505: continue;
5506:
5507: lptr[FREE_LEASES] = &p->free;
5508: lptr[ACTIVE_LEASES] = &p->active;
5509: lptr[EXPIRED_LEASES] = &p->expired;
5510: lptr[ABANDONED_LEASES] = &p->abandoned;
5511: lptr[BACKUP_LEASES] = &p->backup;
5512: lptr[RESERVED_LEASES] = &p->reserved;
5513:
5514: for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
5515: for (l = *(lptr [i]); l; l = l -> next) {
5516: if ((l->flags & ON_QUEUE) == 0 &&
5517: (everythingp ||
5518: (l->tstp > l->atsfp) ||
5519: (i == EXPIRED_LEASES))) {
5520: l -> desired_binding_state = l -> binding_state;
5521: dhcp_failover_queue_update (l, 0);
5522: }
5523: }
5524: }
5525: }
5526: }
5527: return ISC_R_SUCCESS;
5528: }
5529:
5530: isc_result_t
5531: dhcp_failover_process_update_request (dhcp_failover_state_t *state,
5532: failover_message_t *msg)
5533: {
5534: if (state->send_update_done) {
5535: log_info("Received update request while old update still "
5536: "flying! Silently discarding old request.");
5537: lease_dereference(&state->send_update_done, MDL);
5538: }
5539:
5540: /* Generate a fresh update queue. */
5541: dhcp_failover_generate_update_queue (state, 0);
5542:
5543: state->updxid = msg->xid;
5544:
5545: /* If there's anything on the update queue (there shouldn't be
5546: anything on the ack queue), trigger an update done message
5547: when we get an ack for that lease. */
5548: if (state -> update_queue_tail) {
5549: lease_reference (&state -> send_update_done,
5550: state -> update_queue_tail, MDL);
5551: dhcp_failover_send_updates (state);
5552: log_info ("Update request from %s: sending update",
5553: state -> name);
5554: } else {
5555: /* Otherwise, there are no updates to send, so we can
5556: just send an UPDDONE message immediately. */
5557: dhcp_failover_send_update_done (state);
5558: log_info ("Update request from %s: nothing pending",
5559: state -> name);
5560: }
5561:
5562: return ISC_R_SUCCESS;
5563: }
5564:
5565: isc_result_t
5566: dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
5567: failover_message_t *msg)
5568: {
5569: if (state->send_update_done) {
5570: log_info("Received update request while old update still "
5571: "flying! Silently discarding old request.");
5572: lease_dereference(&state->send_update_done, MDL);
5573: }
5574:
5575: /* Generate a fresh update queue that includes every lease. */
5576: dhcp_failover_generate_update_queue (state, 1);
5577:
5578: state->updxid = msg->xid;
5579:
5580: if (state -> update_queue_tail) {
5581: lease_reference (&state -> send_update_done,
5582: state -> update_queue_tail, MDL);
5583: dhcp_failover_send_updates (state);
5584: log_info ("Update request all from %s: sending update",
5585: state -> name);
5586: } else {
5587: /* This should really never happen, but it could happen
5588: on a server that currently has no leases configured. */
5589: dhcp_failover_send_update_done (state);
5590: log_info ("Update request all from %s: nothing pending",
5591: state -> name);
5592: }
5593:
5594: return ISC_R_SUCCESS;
5595: }
5596:
5597: isc_result_t
5598: dhcp_failover_process_update_done (dhcp_failover_state_t *state,
5599: failover_message_t *msg)
5600: {
5601: struct timeval tv;
5602:
5603: log_info ("failover peer %s: peer update completed.",
5604: state -> name);
5605:
5606: state -> curUPD = 0;
5607:
5608: switch (state -> me.state) {
5609: case unknown_state:
5610: case partner_down:
5611: case normal:
5612: case communications_interrupted:
5613: case resolution_interrupted:
5614: case shut_down:
5615: case paused:
5616: case recover_done:
5617: case startup:
5618: case recover_wait:
5619: break; /* shouldn't happen. */
5620:
5621: /* We got the UPDDONE, so we can go into normal state! */
5622: case potential_conflict:
5623: if (state->partner.state == conflict_done) {
5624: if (state->i_am == secondary) {
5625: dhcp_failover_set_state (state, normal);
5626: } else {
5627: log_error("Secondary is in conflict_done "
5628: "state after conflict resolution, "
5629: "this is illegal.");
5630: dhcp_failover_set_state (state, shut_down);
5631: }
5632: } else {
5633: if (state->i_am == primary)
5634: dhcp_failover_set_state (state, conflict_done);
5635: else
5636: log_error("Spurious update-done message.");
5637: }
5638:
5639: break;
5640:
5641: case conflict_done:
5642: log_error("Spurious update-done message.");
5643: break;
5644:
5645: case recover:
5646: /* Wait for MCLT to expire before moving to recover_done,
5647: except that if both peers come up in recover, there is
5648: no point in waiting for MCLT to expire - this probably
5649: indicates the initial startup of a newly-configured
5650: failover pair. */
5651: if (state -> me.stos + state -> mclt > cur_time &&
5652: state -> partner.state != recover &&
5653: state -> partner.state != recover_done) {
5654: dhcp_failover_set_state (state, recover_wait);
5655: #if defined (DEBUG_FAILOVER_TIMING)
5656: log_info ("add_timeout +%d %s",
5657: (int)(cur_time -
5658: state -> me.stos + state -> mclt),
5659: "dhcp_failover_recover_done");
5660: #endif
5661: tv . tv_sec = (int)(state -> me.stos + state -> mclt);
5662: tv . tv_usec = 0;
5663: add_timeout (&tv,
5664: dhcp_failover_recover_done,
5665: state,
5666: (tvref_t)omapi_object_reference,
5667: (tvunref_t)
5668: omapi_object_dereference);
5669: } else
5670: dhcp_failover_recover_done (state);
5671: }
5672:
5673: return ISC_R_SUCCESS;
5674: }
5675:
5676: void dhcp_failover_recover_done (void *sp)
5677: {
5678: dhcp_failover_state_t *state = sp;
5679:
5680: #if defined (DEBUG_FAILOVER_TIMING)
5681: log_info ("dhcp_failover_recover_done");
5682: #endif
5683:
5684: dhcp_failover_set_state (state, recover_done);
5685: }
5686:
5687: #if defined (DEBUG_FAILOVER_MESSAGES)
5688: /* Print hunks of failover messages, doing line breaks as appropriate.
5689: Note that this assumes syslog is being used, rather than, e.g., the
5690: Windows NT logging facility, where just dumping the whole message in
5691: one hunk would be more appropriate. */
5692:
5693: void failover_print (char *obuf,
5694: unsigned *obufix, unsigned obufmax, const char *s)
5695: {
5696: int len = strlen (s);
5697:
5698: while (len + *obufix + 1 >= obufmax) {
5699: log_debug ("%s", obuf);
5700: if (!*obufix) {
5701: log_debug ("%s", s);
5702: *obufix = 0;
5703: return;
5704: }
5705: *obufix = 0;
5706: }
5707: strcpy (&obuf [*obufix], s);
5708: *obufix += len;
5709: }
5710: #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
5711:
5712: /* Taken from draft-ietf-dhc-loadb-01.txt: */
5713: /* A "mixing table" of 256 distinct values, in pseudo-random order. */
5714: unsigned char loadb_mx_tbl[256] = {
5715: 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
5716: 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
5717: 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
5718: 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
5719: 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
5720: 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
5721: 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
5722: 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
5723: 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
5724: 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
5725: 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
5726: 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
5727: 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
5728: 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
5729: 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
5730: 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
5731: 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5732: 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5733: 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5734: 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5735: 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5736: 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5737: 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5738: 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5739: 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5740: 170, 68, 6, 169, 234, 151 };
5741:
5742: static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5743: static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
5744: {
5745: unsigned char hash = len;
5746: int i;
5747: for(i = len; i > 0; )
5748: hash = loadb_mx_tbl [hash ^ (key [--i])];
5749: return hash;
5750: }
5751:
5752: int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
5753: {
5754: struct option_cache *oc;
5755: struct data_string ds;
5756: unsigned char hbaix;
5757: int hm;
5758:
5759: if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
5760: return 1;
5761: }
5762:
5763: /* If we don't have a hash bucket array, we can't tell if this
5764: one's ours, so we assume it's not. */
5765: if (!state -> hba)
5766: return 0;
5767:
5768: oc = lookup_option (&dhcp_universe, packet -> options,
5769: DHO_DHCP_CLIENT_IDENTIFIER);
5770: memset (&ds, 0, sizeof ds);
5771: if (oc &&
5772: evaluate_option_cache (&ds, packet, (struct lease *)0,
5773: (struct client_state *)0,
5774: packet -> options, (struct option_state *)0,
5775: &global_scope, oc, MDL)) {
5776: hbaix = loadb_p_hash (ds.data, ds.len);
5777:
5778: data_string_forget(&ds, MDL);
5779: } else {
5780: hbaix = loadb_p_hash (packet -> raw -> chaddr,
5781: packet -> raw -> hlen);
5782: }
5783:
5784: hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
5785:
5786: if (state -> i_am == primary)
5787: return hm;
5788: else
5789: return !hm;
5790: }
5791:
5792: /* The inverse of load_balance_mine ("load balance theirs"). We can't
5793: * use the regular load_balance_mine() and invert it because of the case
5794: * where there might not be an HBA, and we want to indicate false here
5795: * in this case only.
5796: */
5797: int
5798: peer_wants_lease(struct lease *lp)
5799: {
5800: dhcp_failover_state_t *state;
5801: unsigned char hbaix;
5802: int hm;
5803:
5804: if (!lp->pool)
5805: return 0;
5806:
5807: state = lp->pool->failover_peer;
5808:
5809: if (!state || !state->hba)
5810: return 0;
5811:
5812: if (lp->uid_len)
5813: hbaix = loadb_p_hash(lp->uid, lp->uid_len);
5814: else if (lp->hardware_addr.hlen > 1)
5815: /* Skip the first byte, which is the hardware type, and is
5816: * not included during actual load balancing checks above
5817: * since it is separate from the packet header chaddr field.
5818: * The remainder of the hardware address should be identical
5819: * to the chaddr contents.
5820: */
5821: hbaix = loadb_p_hash(lp->hardware_addr.hbuf + 1,
5822: lp->hardware_addr.hlen - 1);
5823: else /* impossible to categorize into LBA */
5824: return 0;
5825:
5826: hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
5827:
5828: if (state->i_am == primary)
5829: return !hm;
5830: else
5831: return hm;
5832: }
5833:
5834: /* This deals with what to do with bind updates when
5835: we're in the normal state
5836:
5837: Note that tsfp had better be set from the latest bind update
5838: _before_ this function is called! */
5839:
5840: binding_state_t
5841: normal_binding_state_transition_check (struct lease *lease,
5842: dhcp_failover_state_t *state,
5843: binding_state_t binding_state,
5844: u_int32_t tsfp)
5845: {
5846: binding_state_t new_state;
5847:
5848: /* If there is no transition, it's no problem. */
5849: if (binding_state == lease -> binding_state)
5850: return binding_state;
5851:
5852: switch (lease -> binding_state) {
5853: case FTS_FREE:
5854: case FTS_ABANDONED:
5855: switch (binding_state) {
5856: case FTS_ACTIVE:
5857: case FTS_ABANDONED:
5858: case FTS_BACKUP:
5859: case FTS_EXPIRED:
5860: case FTS_RELEASED:
5861: case FTS_RESET:
5862: /* If the lease was free, and our peer is primary,
5863: then it can make it active, or abandoned, or
5864: backup. Abandoned is treated like free in
5865: this case. */
5866: if (state -> i_am == secondary)
5867: return binding_state;
5868:
5869: /* Otherwise, it can't legitimately do any sort of
5870: state transition. Because the lease was free,
5871: and the error has already been made, we allow the
5872: peer to change its state anyway, but log a warning
5873: message in hopes that the error will be fixed. */
5874: case FTS_FREE: /* for compiler */
5875: new_state = binding_state;
5876: goto out;
5877:
5878: default:
5879: log_fatal ("Impossible case at %s:%d.", MDL);
5880: return FTS_RESET;
5881: }
5882: case FTS_ACTIVE:
5883: /* The secondary can't change the state of an active
5884: lease. */
5885: if (state -> i_am == primary) {
5886: /* Except that the client may send the DHCPRELEASE
5887: to the secondary, and we have to accept that. */
5888: if (binding_state == FTS_RELEASED)
5889: return binding_state;
5890: new_state = lease -> binding_state;
5891: goto out;
5892: }
5893:
5894: /* So this is only for transitions made by the primary: */
5895: switch (binding_state) {
5896: case FTS_FREE:
5897: case FTS_BACKUP:
5898: /* Can't set a lease to free or backup until the
5899: peer agrees that it's expired. */
5900: if (tsfp > cur_time) {
5901: new_state = lease -> binding_state;
5902: goto out;
5903: }
5904: return binding_state;
5905:
5906: case FTS_EXPIRED:
5907: /* XXX 65 should be the clock skew between the peers
5908: XXX plus a fudge factor. This code will result
5909: XXX in problems if MCLT is really short or the
5910: XXX max-lease-time is really short (less than the
5911: XXX fudge factor. */
5912: if (lease -> ends - 65 > cur_time) {
5913: new_state = lease -> binding_state;
5914: goto out;
5915: }
5916:
5917: case FTS_RELEASED:
5918: case FTS_ABANDONED:
5919: case FTS_RESET:
5920: case FTS_ACTIVE:
5921: return binding_state;
5922:
5923: default:
5924: log_fatal ("Impossible case at %s:%d.", MDL);
5925: return FTS_RESET;
5926: }
5927: break;
5928: case FTS_EXPIRED:
5929: switch (binding_state) {
5930: case FTS_BACKUP:
5931: case FTS_FREE:
5932: /* Can't set a lease to free or backup until the
5933: peer agrees that it's expired. */
5934: if (tsfp > cur_time) {
5935: new_state = lease -> binding_state;
5936: goto out;
5937: }
5938: return binding_state;
5939:
5940: case FTS_ACTIVE:
5941: case FTS_RELEASED:
5942: case FTS_ABANDONED:
5943: case FTS_RESET:
5944: case FTS_EXPIRED:
5945: return binding_state;
5946:
5947: default:
5948: log_fatal ("Impossible case at %s:%d.", MDL);
5949: return FTS_RESET;
5950: }
5951: case FTS_RELEASED:
5952: switch (binding_state) {
5953: case FTS_FREE:
5954: case FTS_BACKUP:
5955:
5956: /* These are invalid state transitions - should we
5957: prevent them? */
5958: case FTS_EXPIRED:
5959: case FTS_ABANDONED:
5960: case FTS_RESET:
5961: case FTS_ACTIVE:
5962: case FTS_RELEASED:
5963: return binding_state;
5964:
5965: default:
5966: log_fatal ("Impossible case at %s:%d.", MDL);
5967: return FTS_RESET;
5968: }
5969: case FTS_RESET:
5970: switch (binding_state) {
5971: case FTS_FREE:
5972: case FTS_BACKUP:
5973: /* Can't set a lease to free or backup until the
5974: peer agrees that it's expired. */
5975: if (tsfp > cur_time) {
5976: new_state = lease -> binding_state;
5977: goto out;
5978: }
5979: return binding_state;
5980:
5981: case FTS_ACTIVE:
5982: case FTS_EXPIRED:
5983: case FTS_RELEASED:
5984: case FTS_ABANDONED:
5985: case FTS_RESET:
5986: return binding_state;
5987:
5988: default:
5989: log_fatal ("Impossible case at %s:%d.", MDL);
5990: return FTS_RESET;
5991: }
5992: case FTS_BACKUP:
5993: switch (binding_state) {
5994: case FTS_ACTIVE:
5995: case FTS_ABANDONED:
5996: case FTS_EXPIRED:
5997: case FTS_RELEASED:
5998: case FTS_RESET:
5999: /* If the lease was in backup, and our peer
6000: is secondary, then it can make it active
6001: or abandoned. */
6002: if (state -> i_am == primary)
6003: return binding_state;
6004:
6005: /* Either the primary or the secondary can
6006: reasonably move a lease from the backup
6007: state to the free state. */
6008: case FTS_FREE:
6009: return binding_state;
6010:
6011: case FTS_BACKUP:
6012: new_state = lease -> binding_state;
6013: goto out;
6014:
6015: default:
6016: log_fatal ("Impossible case at %s:%d.", MDL);
6017: return FTS_RESET;
6018: }
6019:
6020: default:
6021: log_fatal ("Impossible case at %s:%d.", MDL);
6022: return FTS_RESET;
6023: }
6024: out:
6025: return new_state;
6026: }
6027:
6028: /* Determine whether the state transition is okay when we're potentially
6029: in conflict with the peer. */
6030: binding_state_t
6031: conflict_binding_state_transition_check (struct lease *lease,
6032: dhcp_failover_state_t *state,
6033: binding_state_t binding_state,
6034: u_int32_t tsfp)
6035: {
6036: binding_state_t new_state;
6037:
6038: /* If there is no transition, it's no problem. */
6039: if (binding_state == lease -> binding_state)
6040: new_state = binding_state;
6041: else {
6042: switch (lease -> binding_state) {
6043: /* If we think the lease is not in use, then the
6044: state into which the partner put it is just fine,
6045: whatever it is. */
6046: case FTS_FREE:
6047: case FTS_ABANDONED:
6048: case FTS_EXPIRED:
6049: case FTS_RELEASED:
6050: case FTS_RESET:
6051: case FTS_BACKUP:
6052: new_state = binding_state;
6053: break;
6054:
6055: /* If we think the lease *is* in use, then we're not
6056: going to take the partner's change if the partner
6057: thinks it's free. */
6058: case FTS_ACTIVE:
6059: switch (binding_state) {
6060: case FTS_FREE:
6061: case FTS_BACKUP:
6062: new_state = lease -> binding_state;
6063: break;
6064:
6065: case FTS_EXPIRED:
6066: /* If we don't agree about expiry, it's
6067: * invalid. 65 should allow for max
6068: * clock skew (60) plus some fudge.
6069: * XXX: should we refetch cur_time?
6070: */
6071: if ((lease->ends - 65) > cur_time)
6072: new_state = lease->binding_state;
6073: else
6074: new_state = binding_state;
6075: break;
6076:
6077: /* RELEASED, RESET, and ABANDONED indicate
6078: * that our partner has information about
6079: * this lease that we did not witness. Our
6080: * partner wins.
6081: */
6082: case FTS_RELEASED:
6083: case FTS_RESET:
6084: case FTS_ABANDONED:
6085: new_state = binding_state;
6086: break;
6087:
6088: default:
6089: log_fatal ("Impossible case at %s:%d.", MDL);
6090: return FTS_RESET;
6091: }
6092: break;
6093:
6094: default:
6095: log_fatal ("Impossible case at %s:%d.", MDL);
6096: return FTS_RESET;
6097: }
6098: }
6099: return new_state;
6100: }
6101:
6102: /* We can reallocate a lease under the following circumstances:
6103:
6104: (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
6105: FTS_BACKUP, and we're secondary.
6106: (2) We're in partner_down, and the lease is not active, and we
6107: can be sure that the other server didn't make it active.
6108: We can only be sure that the server didn't make it active
6109: when we are in the partner_down state and one of the following
6110: two conditions holds:
6111: (a) in the case that the time sent from the peer is earlier than
6112: the time we entered the partner_down state, at least MCLT has
6113: gone by since we entered partner_down, or
6114: (b) in the case that the time sent from the peer is later than
6115: the time when we entered partner_down, the current time is
6116: later than the time sent from the peer by at least MCLT. */
6117:
6118: int lease_mine_to_reallocate (struct lease *lease)
6119: {
6120: dhcp_failover_state_t *peer;
6121:
6122: if (lease && lease->pool &&
6123: (peer = lease->pool->failover_peer)) {
6124: switch (lease->binding_state) {
6125: case FTS_ACTIVE:
6126: /* ACTIVE leases may not be reallocated. */
6127: return 0;
6128:
6129: case FTS_FREE:
6130: case FTS_ABANDONED:
6131: /* FREE leases may only be allocated by the primary,
6132: * unless the secondary is acting in partner_down
6133: * state and stos+mclt or tsfp+mclt has expired,
6134: * whichever is greater.
6135: *
6136: * ABANDONED are treated the same as FREE for all
6137: * purposes here. Note that servers will only try
6138: * for ABANDONED leases as a last resort anyway.
6139: */
6140: if (peer -> i_am == primary)
6141: return 1;
6142:
6143: return(peer->service_state == service_partner_down &&
6144: ((lease->tsfp < peer->me.stos) ?
6145: (peer->me.stos + peer->mclt < cur_time) :
6146: (lease->tsfp + peer->mclt < cur_time)));
6147:
6148: case FTS_RESET:
6149: case FTS_RELEASED:
6150: case FTS_EXPIRED:
6151: /* These three lease states go onto the 'expired'
6152: * queue. Upon entry into partner-down state, this
6153: * queue of leases has their tsfp values modified
6154: * to equal stos+mclt, the point at which the server
6155: * is allowed to remove them from these transitional
6156: * states without an acknowledgement.
6157: *
6158: * Note that although tsfp has been possibly extended
6159: * past the actual tsfp we received from the peer, we
6160: * don't have to take any special action. Since tsfp
6161: * is now in the past (or now), we can guarantee that
6162: * this server will only allocate a lease time equal
6163: * to MCLT, rather than a TSFP-optimal lease, which is
6164: * the only danger for a lease in one of these states.
6165: */
6166: return((peer->service_state == service_partner_down) &&
6167: (lease->tsfp < cur_time));
6168:
6169: case FTS_BACKUP:
6170: /* Only the secondary may allocate BACKUP leases,
6171: * unless in partner_down state in which case at
6172: * least TSFP+MCLT or STOS+MCLT must have expired,
6173: * whichever is greater.
6174: */
6175: if (peer->i_am == secondary)
6176: return 1;
6177:
6178: return((peer->service_state == service_partner_down) &&
6179: ((lease->tsfp < peer->me.stos) ?
6180: (peer->me.stos + peer->mclt < cur_time) :
6181: (lease->tsfp + peer->mclt < cur_time)));
6182:
6183: default:
6184: /* All lease states appear above. */
6185: log_fatal("Impossible case at %s:%d.", MDL);
6186: break;
6187: }
6188: return 0;
6189: }
6190: if (lease)
6191: return(lease->binding_state == FTS_FREE ||
6192: lease->binding_state == FTS_BACKUP);
6193: else
6194: return 0;
6195: }
6196:
6197: static isc_result_t failover_message_reference (failover_message_t **mp,
6198: failover_message_t *m,
6199: const char *file, int line)
6200: {
6201: *mp = m;
6202: m -> refcnt++;
6203: return ISC_R_SUCCESS;
6204: }
6205:
6206: static isc_result_t failover_message_dereference (failover_message_t **mp,
6207: const char *file, int line)
6208: {
6209: failover_message_t *m;
6210: m = (*mp);
6211: m -> refcnt--;
6212: if (m -> refcnt == 0) {
6213: if (m -> next)
6214: failover_message_dereference (&m -> next,
6215: file, line);
6216: if (m -> chaddr.data)
6217: dfree (m -> chaddr.data, file, line);
6218: if (m -> client_identifier.data)
6219: dfree (m -> client_identifier.data, file, line);
6220: if (m -> hba.data)
6221: dfree (m -> hba.data, file, line);
6222: if (m -> message.data)
6223: dfree (m -> message.data, file, line);
6224: if (m -> reply_options.data)
6225: dfree (m -> reply_options.data, file, line);
6226: if (m -> request_options.data)
6227: dfree (m -> request_options.data, file, line);
6228: if (m -> vendor_class.data)
6229: dfree (m -> vendor_class.data, file, line);
6230: if (m -> vendor_options.data)
6231: dfree (m -> vendor_options.data, file, line);
6232: if (m -> ddns.data)
6233: dfree (m -> ddns.data, file, line);
6234: dfree (*mp, file, line);
6235: }
6236: *mp = 0;
6237: return ISC_R_SUCCESS;
6238: }
6239:
6240: OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
6241: dhcp_type_failover_state)
6242: OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
6243: dhcp_type_failover_listener)
6244: OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
6245: dhcp_type_failover_link)
6246: #endif /* defined (FAILOVER_PROTOCOL) */
6247:
6248: const char *binding_state_print (enum failover_state state)
6249: {
6250: switch (state) {
6251: case FTS_FREE:
6252: return "free";
6253: break;
6254:
6255: case FTS_ACTIVE:
6256: return "active";
6257: break;
6258:
6259: case FTS_EXPIRED:
6260: return "expired";
6261: break;
6262:
6263: case FTS_RELEASED:
6264: return "released";
6265: break;
6266:
6267: case FTS_ABANDONED:
6268: return "abandoned";
6269: break;
6270:
6271: case FTS_RESET:
6272: return "reset";
6273: break;
6274:
6275: case FTS_BACKUP:
6276: return "backup";
6277: break;
6278:
6279: default:
6280: return "unknown";
6281: break;
6282: }
6283: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>