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