1: /* failover.c
2:
3: Failover protocol support code... */
4:
5: /*
6: * Copyright (c) 2011 by Internet Systems Consortium, Inc. ("ISC")
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:
36: #include "dhcpd.h"
37: #include <omapip/omapip_p.h>
38:
39: #if defined (FAILOVER_PROTOCOL)
40: dhcp_failover_state_t *failover_states;
41: static isc_result_t do_a_failover_option (omapi_object_t *,
42: dhcp_failover_link_t *);
43: dhcp_failover_listener_t *failover_listeners;
44:
45: static isc_result_t failover_message_reference (failover_message_t **,
46: failover_message_t *,
47: const char *file, int line);
48: static isc_result_t failover_message_dereference (failover_message_t **,
49: const char *file, int line);
50:
51: static void dhcp_failover_pool_balance(dhcp_failover_state_t *state);
52: static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state);
53: static int dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
54: isc_boolean_t *sendreq);
55: static inline int secondary_not_hoarding(dhcp_failover_state_t *state,
56: struct pool *p);
57:
58:
59: void dhcp_failover_startup ()
60: {
61: dhcp_failover_state_t *state;
62: isc_result_t status;
63: struct timeval tv;
64:
65: for (state = failover_states; state; state = state -> next) {
66: dhcp_failover_state_transition (state, "startup");
67:
68: if (state -> pool_count == 0) {
69: log_error ("failover peer declaration with no %s",
70: "referring pools.");
71: log_error ("In order to use failover, you MUST %s",
72: "refer to your main failover declaration");
73: log_error ("in each pool declaration. You MUST %s",
74: "NOT use range declarations outside");
75: log_fatal ("of pool declarations.");
76: }
77: /* In case the peer is already running, immediately try
78: to establish a connection with it. */
79: status = dhcp_failover_link_initiate ((omapi_object_t *)state);
80: if (status != ISC_R_SUCCESS && status != ISC_R_INCOMPLETE) {
81: #if defined (DEBUG_FAILOVER_TIMING)
82: log_info ("add_timeout +90 dhcp_failover_reconnect");
83: #endif
84: tv . tv_sec = cur_time + 90;
85: tv . tv_usec = 0;
86: add_timeout (&tv,
87: dhcp_failover_reconnect, state,
88: (tvref_t)
89: dhcp_failover_state_reference,
90: (tvunref_t)
91: dhcp_failover_state_dereference);
92: log_error ("failover peer %s: %s", state -> name,
93: isc_result_totext (status));
94: }
95:
96: status = (dhcp_failover_listen
97: ((omapi_object_t *)state));
98: if (status != ISC_R_SUCCESS) {
99: #if defined (DEBUG_FAILOVER_TIMING)
100: log_info ("add_timeout +90 %s",
101: "dhcp_failover_listener_restart");
102: #endif
103: tv . tv_sec = cur_time + 90;
104: tv . tv_usec = 0;
105: add_timeout (&tv,
106: dhcp_failover_listener_restart,
107: state,
108: (tvref_t)omapi_object_reference,
109: (tvunref_t)omapi_object_dereference);
110: }
111: }
112: }
113:
114: int dhcp_failover_write_all_states ()
115: {
116: dhcp_failover_state_t *state;
117:
118: for (state = failover_states; state; state = state -> next) {
119: if (!write_failover_state (state))
120: return 0;
121: }
122: return 1;
123: }
124:
125: isc_result_t enter_failover_peer (peer)
126: dhcp_failover_state_t *peer;
127: {
128: dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
129: isc_result_t status;
130:
131: status = find_failover_peer (&dup, peer -> name, MDL);
132: if (status == ISC_R_NOTFOUND) {
133: if (failover_states) {
134: dhcp_failover_state_reference (&peer -> next,
135: failover_states, MDL);
136: dhcp_failover_state_dereference (&failover_states,
137: MDL);
138: }
139: dhcp_failover_state_reference (&failover_states, peer, MDL);
140: return ISC_R_SUCCESS;
141: }
142: dhcp_failover_state_dereference (&dup, MDL);
143: if (status == ISC_R_SUCCESS)
144: return ISC_R_EXISTS;
145: return status;
146: }
147:
148: isc_result_t find_failover_peer (peer, name, file, line)
149: dhcp_failover_state_t **peer;
150: const char *name;
151: const char *file;
152: int line;
153: {
154: dhcp_failover_state_t *p;
155:
156: for (p = failover_states; p; p = p -> next)
157: if (!strcmp (name, p -> name))
158: break;
159: if (p)
160: return dhcp_failover_state_reference (peer, p, file, line);
161: return ISC_R_NOTFOUND;
162: }
163:
164: /* The failover protocol has three objects associated with it. For
165: each failover partner declaration in the dhcpd.conf file, primary
166: or secondary, there is a failover_state object. For any primary or
167: secondary state object that has a connection to its peer, there is
168: also a failover_link object, which has its own input state separate
169: from the failover protocol state for managing the actual bytes
170: coming in off the wire. Finally, there will be one listener object
171: for every distinct port number associated with a secondary
172: failover_state object. Normally all secondary failover_state
173: objects are expected to listen on the same port number, so there
174: need be only one listener object, but if different port numbers are
175: specified for each failover object, there could be as many as one
176: listener object for each secondary failover_state object. */
177:
178: /* This, then, is the implementation of the failover link object. */
179:
180: isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
181: {
182: isc_result_t status;
183: dhcp_failover_link_t *obj;
184: dhcp_failover_state_t *state;
185: omapi_object_t *o;
186: int i;
187: struct data_string ds;
188: omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
189: omapi_addr_t local_addr;
190:
191: /* Find the failover state in the object chain. */
192: for (o = h; o -> outer; o = o -> outer)
193: ;
194: for (; o; o = o -> inner) {
195: if (o -> type == dhcp_type_failover_state)
196: break;
197: }
198: if (!o)
199: return ISC_R_INVALIDARG;
200: state = (dhcp_failover_state_t *)o;
201:
202: obj = (dhcp_failover_link_t *)0;
203: status = dhcp_failover_link_allocate (&obj, MDL);
204: if (status != ISC_R_SUCCESS)
205: return status;
206: option_cache_reference (&obj -> peer_address,
207: state -> partner.address, MDL);
208: obj -> peer_port = state -> partner.port;
209: dhcp_failover_state_reference (&obj -> state_object, state, MDL);
210:
211: memset (&ds, 0, sizeof ds);
212: if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
213: (struct client_state *)0,
214: (struct option_state *)0,
215: (struct option_state *)0,
216: &global_scope, obj -> peer_address, MDL)) {
217: dhcp_failover_link_dereference (&obj, MDL);
218: return ISC_R_UNEXPECTED;
219: }
220:
221: /* Make an omapi address list out of a buffer containing zero or more
222: IPv4 addresses. */
223: status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
224: if (status != ISC_R_SUCCESS) {
225: dhcp_failover_link_dereference (&obj, MDL);
226: return status;
227: }
228:
229: for (i = 0; i < addrs -> count; i++) {
230: addrs -> addresses [i].addrtype = AF_INET;
231: addrs -> addresses [i].addrlen = sizeof (struct in_addr);
232: memcpy (addrs -> addresses [i].address,
233: &ds.data [i * 4], sizeof (struct in_addr));
234: addrs -> addresses [i].port = obj -> peer_port;
235: }
236: data_string_forget (&ds, MDL);
237:
238: /* Now figure out the local address that we're supposed to use. */
239: if (!state -> me.address ||
240: !evaluate_option_cache (&ds, (struct packet *)0,
241: (struct lease *)0,
242: (struct client_state *)0,
243: (struct option_state *)0,
244: (struct option_state *)0,
245: &global_scope, state -> me.address,
246: MDL)) {
247: memset (&local_addr, 0, sizeof local_addr);
248: local_addr.addrtype = AF_INET;
249: local_addr.addrlen = sizeof (struct in_addr);
250: if (!state -> server_identifier.len) {
251: log_fatal ("failover peer %s: no local address.",
252: state -> name);
253: }
254: } else {
255: if (ds.len != sizeof (struct in_addr)) {
256: log_error("failover peer %s: 'address' parameter "
257: "fails to resolve to an IPv4 address",
258: state->name);
259: data_string_forget (&ds, MDL);
260: dhcp_failover_link_dereference (&obj, MDL);
261: omapi_addr_list_dereference (&addrs, MDL);
262: return ISC_R_INVALIDARG;
263: }
264: local_addr.addrtype = AF_INET;
265: local_addr.addrlen = ds.len;
266: memcpy (local_addr.address, ds.data, ds.len);
267: if (!state -> server_identifier.len)
268: data_string_copy (&state -> server_identifier,
269: &ds, MDL);
270: data_string_forget (&ds, MDL);
271: local_addr.port = 0; /* Let the O.S. choose. */
272: }
273:
274: status = omapi_connect_list ((omapi_object_t *)obj,
275: addrs, &local_addr);
276: omapi_addr_list_dereference (&addrs, MDL);
277:
278: dhcp_failover_link_dereference (&obj, MDL);
279: return status;
280: }
281:
282: isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
283: const char *name, va_list ap)
284: {
285: isc_result_t status;
286: dhcp_failover_link_t *link;
287: omapi_object_t *c;
288: dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
289: char *sname;
290: int slen;
291: struct timeval tv;
292:
293: if (h -> type != dhcp_type_failover_link) {
294: /* XXX shouldn't happen. Put an assert here? */
295: return ISC_R_UNEXPECTED;
296: }
297: link = (dhcp_failover_link_t *)h;
298:
299: if (!strcmp (name, "connect")) {
300: if (link -> state_object -> i_am == primary) {
301: status = dhcp_failover_send_connect (h);
302: if (status != ISC_R_SUCCESS) {
303: log_info ("dhcp_failover_send_connect: %s",
304: isc_result_totext (status));
305: omapi_disconnect (h -> outer, 1);
306: }
307: } else
308: status = ISC_R_SUCCESS;
309: /* Allow the peer fifteen seconds to send us a
310: startup message. */
311: #if defined (DEBUG_FAILOVER_TIMING)
312: log_info ("add_timeout +15 %s",
313: "dhcp_failover_link_startup_timeout");
314: #endif
315: tv . tv_sec = cur_time + 15;
316: tv . tv_usec = 0;
317: add_timeout (&tv,
318: dhcp_failover_link_startup_timeout,
319: link,
320: (tvref_t)dhcp_failover_link_reference,
321: (tvunref_t)dhcp_failover_link_dereference);
322: return status;
323: }
324:
325: if (!strcmp (name, "disconnect")) {
326: if (link -> state_object) {
327: dhcp_failover_state_reference (&state,
328: link -> state_object, MDL);
329: link -> state = dhcp_flink_disconnected;
330:
331: /* Make the transition. */
332: if (state->link_to_peer == link)
333: dhcp_failover_state_transition(link->state_object, name);
334:
335: /* Schedule an attempt to reconnect. */
336: #if defined (DEBUG_FAILOVER_TIMING)
337: log_info("add_timeout +5 dhcp_failover_reconnect");
338: #endif
339: tv.tv_sec = cur_time + 5;
340: tv.tv_usec = cur_tv.tv_usec;
341: add_timeout(&tv, dhcp_failover_reconnect, state,
342: (tvref_t)dhcp_failover_state_reference,
343: (tvunref_t)dhcp_failover_state_dereference);
344:
345: dhcp_failover_state_dereference (&state, MDL);
346: }
347: return ISC_R_SUCCESS;
348: }
349:
350: if (!strcmp (name, "status")) {
351: if (link -> state_object) {
352: isc_result_t status;
353:
354: status = va_arg(ap, isc_result_t);
355:
356: if ((status == ISC_R_HOSTUNREACH) || (status == ISC_R_TIMEDOUT)) {
357: dhcp_failover_state_reference (&state,
358: link -> state_object, MDL);
359: link -> state = dhcp_flink_disconnected;
360:
361: /* Make the transition. */
362: dhcp_failover_state_transition (link -> state_object,
363: "disconnect");
364:
365: /* Start trying to reconnect. */
366: #if defined (DEBUG_FAILOVER_TIMING)
367: log_info ("add_timeout +5 %s",
368: "dhcp_failover_reconnect");
369: #endif
370: tv . tv_sec = cur_time + 5;
371: tv . tv_usec = 0;
372: add_timeout (&tv, dhcp_failover_reconnect,
373: state,
374: (tvref_t)dhcp_failover_state_reference,
375: (tvunref_t)dhcp_failover_state_dereference);
376: }
377: dhcp_failover_state_dereference (&state, MDL);
378: }
379: return ISC_R_SUCCESS;
380: }
381:
382: /* Not a signal we recognize? */
383: if (strcmp (name, "ready")) {
384: if (h -> inner && h -> inner -> type -> signal_handler)
385: return (*(h -> inner -> type -> signal_handler))
386: (h -> inner, name, ap);
387: return ISC_R_NOTFOUND;
388: }
389:
390: if (!h -> outer || h -> outer -> type != omapi_type_connection)
391: return ISC_R_INVALIDARG;
392: c = h -> outer;
393:
394: /* We get here because we requested that we be woken up after
395: some number of bytes were read, and that number of bytes
396: has in fact been read. */
397: switch (link -> state) {
398: case dhcp_flink_start:
399: link -> state = dhcp_flink_message_length_wait;
400: if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
401: break;
402: case dhcp_flink_message_length_wait:
403: next_message:
404: link -> state = dhcp_flink_message_wait;
405: link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
406: if (!link -> imsg) {
407: status = ISC_R_NOMEMORY;
408: dhcp_flink_fail:
409: if (link -> imsg) {
410: failover_message_dereference (&link->imsg,
411: MDL);
412: }
413: link -> state = dhcp_flink_disconnected;
414: log_info ("message length wait: %s",
415: isc_result_totext (status));
416: omapi_disconnect (c, 1);
417: /* XXX just blow away the protocol state now?
418: XXX or will disconnect blow it away? */
419: return ISC_R_UNEXPECTED;
420: }
421: memset (link -> imsg, 0, sizeof (failover_message_t));
422: link -> imsg -> refcnt = 1;
423: /* Get the length: */
424: omapi_connection_get_uint16 (c, &link -> imsg_len);
425: link -> imsg_count = 0; /* Bytes read. */
426:
427: /* Ensure the message is of valid length. */
428: if (link->imsg_len < DHCP_FAILOVER_MIN_MESSAGE_SIZE ||
429: link->imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) {
430: status = ISC_R_UNEXPECTED;
431: goto dhcp_flink_fail;
432: }
433:
434: if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
435: ISC_R_SUCCESS)
436: break;
437: case dhcp_flink_message_wait:
438: /* Read in the message. At this point we have the
439: entire message in the input buffer. For each
440: incoming value ID, set a bit in the bitmask
441: indicating that we've gotten it. Maybe flag an
442: error message if the bit is already set. Once
443: we're done reading, we can check the bitmask to
444: make sure that the required fields for each message
445: have been included. */
446:
447: link -> imsg_count += 2; /* Count the length as read. */
448:
449: /* Get message type. */
450: omapi_connection_copyout (&link -> imsg -> type, c, 1);
451: link -> imsg_count++;
452:
453: /* Get message payload offset. */
454: omapi_connection_copyout (&link -> imsg_payoff, c, 1);
455: link -> imsg_count++;
456:
457: /* Get message time. */
458: omapi_connection_get_uint32 (c, &link -> imsg -> time);
459: link -> imsg_count += 4;
460:
461: /* Get transaction ID. */
462: omapi_connection_get_uint32 (c, &link -> imsg -> xid);
463: link -> imsg_count += 4;
464:
465: #if defined (DEBUG_FAILOVER_MESSAGES)
466: # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
467: if (link->imsg->type == FTM_CONTACT)
468: goto skip_contact;
469: # endif
470: log_info ("link: message %s payoff %d time %ld xid %ld",
471: dhcp_failover_message_name (link -> imsg -> type),
472: link -> imsg_payoff,
473: (unsigned long)link -> imsg -> time,
474: (unsigned long)link -> imsg -> xid);
475: # if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
476: skip_contact:
477: # endif
478: #endif
479: /* Skip over any portions of the message header that we
480: don't understand. */
481: if (link -> imsg_payoff - link -> imsg_count) {
482: omapi_connection_copyout ((unsigned char *)0, c,
483: (link -> imsg_payoff -
484: link -> imsg_count));
485: link -> imsg_count = link -> imsg_payoff;
486: }
487:
488: /* Now start sucking options off the wire. */
489: while (link -> imsg_count < link -> imsg_len) {
490: status = do_a_failover_option (c, link);
491: if (status != ISC_R_SUCCESS)
492: goto dhcp_flink_fail;
493: }
494:
495: /* If it's a connect message, try to associate it with
496: a state object. */
497: /* XXX this should be authenticated! */
498: if (link -> imsg -> type == FTM_CONNECT) {
499: const char *errmsg;
500: int reason;
501:
502: if (!(link->imsg->options_present &
503: FTB_RELATIONSHIP_NAME)) {
504: errmsg = "missing relationship-name";
505: reason = FTR_INVALID_PARTNER;
506: goto badconnect;
507: }
508:
509: /* See if we can find a failover_state object that
510: matches this connection. This message should only
511: be received by a secondary from a primary. */
512: for (s = failover_states; s; s = s -> next) {
513: if (dhcp_failover_state_match_by_name(s,
514: &link->imsg->relationship_name))
515: state = s;
516: }
517:
518: /* If we can't find a failover protocol state
519: for this remote host, drop the connection */
520: if (!state) {
521: errmsg = "unknown failover relationship name";
522: reason = FTR_INVALID_PARTNER;
523:
524: badconnect:
525: /* XXX Send a refusal message first?
526: XXX Look in protocol spec for guidance. */
527:
528: if (state != NULL) {
529: sname = state->name;
530: slen = strlen(sname);
531: } else if (link->imsg->options_present &
532: FTB_RELATIONSHIP_NAME) {
533: sname = (char *)link->imsg->
534: relationship_name.data;
535: slen = link->imsg->relationship_name.count;
536: } else {
537: sname = "unknown";
538: slen = strlen(sname);
539: }
540:
541: log_error("Failover CONNECT from %.*s: %s",
542: slen, sname, errmsg);
543: dhcp_failover_send_connectack
544: ((omapi_object_t *)link, state,
545: reason, errmsg);
546: log_info ("failover: disconnect: %s", errmsg);
547: omapi_disconnect (c, 0);
548: link -> state = dhcp_flink_disconnected;
549: return ISC_R_SUCCESS;
550: }
551:
552: if ((cur_time > link -> imsg -> time &&
553: cur_time - link -> imsg -> time > 60) ||
554: (cur_time < link -> imsg -> time &&
555: link -> imsg -> time - cur_time > 60)) {
556: errmsg = "time offset too large";
557: reason = FTR_TIMEMISMATCH;
558: goto badconnect;
559: }
560:
561: if (!(link -> imsg -> options_present & FTB_HBA) ||
562: link -> imsg -> hba.count != 32) {
563: errmsg = "invalid HBA";
564: reason = FTR_HBA_CONFLICT; /* XXX */
565: goto badconnect;
566: }
567: if (state -> hba)
568: dfree (state -> hba, MDL);
569: state -> hba = dmalloc (32, MDL);
570: if (!state -> hba) {
571: errmsg = "no memory";
572: reason = FTR_MISC_REJECT;
573: goto badconnect;
574: }
575: memcpy (state -> hba, link -> imsg -> hba.data, 32);
576:
577: if (!link -> state_object)
578: dhcp_failover_state_reference
579: (&link -> state_object, state, MDL);
580: if (!link -> peer_address)
581: option_cache_reference
582: (&link -> peer_address,
583: state -> partner.address, MDL);
584: }
585:
586: /* If we don't have a state object at this point, it's
587: some kind of bogus situation, so just drop the
588: connection. */
589: if (!link -> state_object) {
590: log_info ("failover: connect: no matching state.");
591: omapi_disconnect (c, 1);
592: link -> state = dhcp_flink_disconnected;
593: return ISC_R_INVALIDARG;
594: }
595:
596: /* Once we have the entire message, and we've validated
597: it as best we can here, pass it to the parent. */
598: omapi_signal ((omapi_object_t *)link -> state_object,
599: "message", link);
600: link -> state = dhcp_flink_message_length_wait;
601: if (link -> imsg)
602: failover_message_dereference (&link -> imsg, MDL);
603: /* XXX This is dangerous because we could get into a tight
604: XXX loop reading input without servicing any other stuff.
605: XXX There needs to be a way to relinquish control but
606: XXX get it back immediately if there's no other work to
607: XXX do. */
608: if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
609: goto next_message;
610: break;
611:
612: default:
613: log_fatal("Impossible case at %s:%d.", MDL);
614: break;
615: }
616: return ISC_R_SUCCESS;
617: }
618:
619: static isc_result_t do_a_failover_option (c, link)
620: omapi_object_t *c;
621: dhcp_failover_link_t *link;
622: {
623: u_int16_t option_code;
624: u_int16_t option_len;
625: unsigned char *op;
626: unsigned op_size;
627: unsigned op_count;
628: int i;
629:
630: if (link -> imsg_count + 2 > link -> imsg_len) {
631: log_error ("FAILOVER: message overflow at option code.");
632: return ISC_R_PROTOCOLERROR;
633: }
634:
635: /* Get option code. */
636: omapi_connection_get_uint16 (c, &option_code);
637: link -> imsg_count += 2;
638:
639: if (link -> imsg_count + 2 > link -> imsg_len) {
640: log_error ("FAILOVER: message overflow at length.");
641: return ISC_R_PROTOCOLERROR;
642: }
643:
644: /* Get option length. */
645: omapi_connection_get_uint16 (c, &option_len);
646: link -> imsg_count += 2;
647:
648: if (link -> imsg_count + option_len > link -> imsg_len) {
649: log_error ("FAILOVER: message overflow at data.");
650: return ISC_R_PROTOCOLERROR;
651: }
652:
653: /* If it's an unknown code, skip over it. */
654: if ((option_code > FTO_MAX) ||
655: (ft_options[option_code].type == FT_UNDEF)) {
656: #if defined (DEBUG_FAILOVER_MESSAGES)
657: log_debug (" option code %d (%s) len %d (not recognized)",
658: option_code,
659: dhcp_failover_option_name (option_code),
660: option_len);
661: #endif
662: omapi_connection_copyout ((unsigned char *)0, c, option_len);
663: link -> imsg_count += option_len;
664: return ISC_R_SUCCESS;
665: }
666:
667: /* If it's the digest, do it now. */
668: if (ft_options [option_code].type == FT_DIGEST) {
669: link -> imsg_count += option_len;
670: if (link -> imsg_count != link -> imsg_len) {
671: log_error ("FAILOVER: digest not at end of message");
672: return ISC_R_PROTOCOLERROR;
673: }
674: #if defined (DEBUG_FAILOVER_MESSAGES)
675: log_debug (" option %s len %d",
676: ft_options [option_code].name, option_len);
677: #endif
678: /* For now, just dump it. */
679: omapi_connection_copyout ((unsigned char *)0, c, option_len);
680: return ISC_R_SUCCESS;
681: }
682:
683: /* Only accept an option once. */
684: if (link -> imsg -> options_present & ft_options [option_code].bit) {
685: log_error ("FAILOVER: duplicate option %s",
686: ft_options [option_code].name);
687: return ISC_R_PROTOCOLERROR;
688: }
689:
690: /* Make sure the option is appropriate for this type of message.
691: Really, any option is generally allowed for any message, and the
692: cases where this is not true are too complicated to represent in
693: this way - what this code is doing is to just avoid saving the
694: value of an option we don't have any way to use, which allows
695: us to make the failover_message structure smaller. */
696: if (ft_options [option_code].bit &&
697: !(fto_allowed [link -> imsg -> type] &
698: ft_options [option_code].bit)) {
699: omapi_connection_copyout ((unsigned char *)0, c, option_len);
700: link -> imsg_count += option_len;
701: return ISC_R_SUCCESS;
702: }
703:
704: /* Figure out how many elements, how big they are, and where
705: to store them. */
706: if (ft_options [option_code].num_present) {
707: /* If this option takes a fixed number of elements,
708: we expect the space for them to be preallocated,
709: and we can just read the data in. */
710:
711: op = ((unsigned char *)link -> imsg) +
712: ft_options [option_code].offset;
713: op_size = ft_sizes [ft_options [option_code].type];
714: op_count = ft_options [option_code].num_present;
715:
716: if (option_len != op_size * op_count) {
717: log_error ("FAILOVER: option size (%d:%d), option %s",
718: option_len,
719: (ft_sizes [ft_options [option_code].type] *
720: ft_options [option_code].num_present),
721: ft_options [option_code].name);
722: return ISC_R_PROTOCOLERROR;
723: }
724: } else {
725: failover_option_t *fo;
726:
727: /* FT_DDNS* are special - one or two bytes of status
728: followed by the client FQDN. */
729: if (ft_options [option_code].type == FT_DDNS1 ||
730: ft_options [option_code].type == FT_DDNS1) {
731: ddns_fqdn_t *ddns =
732: ((ddns_fqdn_t *)
733: (((char *)link -> imsg) +
734: ft_options [option_code].offset));
735:
736: op_count = (ft_options [option_code].type == FT_DDNS1
737: ? 1 : 2);
738:
739: omapi_connection_copyout (&ddns -> codes [0],
740: c, op_count);
741: link -> imsg_count += op_count;
742: if (op_count == 1)
743: ddns -> codes [1] = 0;
744: op_size = 1;
745: op_count = option_len - op_count;
746:
747: ddns -> length = op_count;
748: ddns -> data = dmalloc (op_count, MDL);
749: if (!ddns -> data) {
750: log_error ("FAILOVER: no memory getting%s(%d)",
751: " DNS data ", op_count);
752:
753: /* Actually, NO_MEMORY, but if we lose here
754: we have to drop the connection. */
755: return ISC_R_PROTOCOLERROR;
756: }
757: omapi_connection_copyout (ddns -> data, c, op_count);
758: goto out;
759: }
760:
761: /* A zero for num_present means that any number of
762: elements can appear, so we have to figure out how
763: many we got from the length of the option, and then
764: fill out a failover_option structure describing the
765: data. */
766: op_size = ft_sizes [ft_options [option_code].type];
767:
768: /* Make sure that option data length is a multiple of the
769: size of the data type being sent. */
770: if (op_size > 1 && option_len % op_size) {
771: log_error ("FAILOVER: option_len %d not %s%d",
772: option_len, "multiple of ", op_size);
773: return ISC_R_PROTOCOLERROR;
774: }
775:
776: op_count = option_len / op_size;
777:
778: fo = ((failover_option_t *)
779: (((char *)link -> imsg) +
780: ft_options [option_code].offset));
781:
782: fo -> count = op_count;
783: fo -> data = dmalloc (option_len, MDL);
784: if (!fo -> data) {
785: log_error ("FAILOVER: no memory getting %s (%d)",
786: "option data", op_count);
787:
788: return ISC_R_PROTOCOLERROR;
789: }
790: op = fo -> data;
791: }
792:
793: /* For single-byte message values and multi-byte values that
794: don't need swapping, just read them in all at once. */
795: if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
796: omapi_connection_copyout ((unsigned char *)op, c, option_len);
797: link -> imsg_count += option_len;
798:
799: /*
800: * As of 3.1.0, many option codes were changed to conform to
801: * draft revision 12 (which alphabetized, then renumbered all
802: * the option codes without preserving the version option code
803: * nor bumping its value). As it turns out, the message codes
804: * for CONNECT and CONNECTACK turn out the same, so it tries
805: * its darndest to connect, and falls short (when TLS_REQUEST
806: * comes up size 2 rather than size 1 as draft revision 12 also
807: * mandates).
808: *
809: * The VENDOR_CLASS code in 3.0.x was 11, which is now the HBA
810: * code. Both work out to be arbitrarily long text-or-byte
811: * strings, so they pass parsing.
812: *
813: * Note that it is possible (or intentional), if highly
814: * improbable, for the HBA bit array to exactly match
815: * isc-V3.0.x. Warning here is not an issue; if it really is
816: * 3.0.x, there will be a protocol error later on. If it isn't
817: * actually 3.0.x, then I guess the lucky user will have to
818: * live with a weird warning.
819: */
820: if ((option_code == 11) && (option_len > 9) &&
821: (strncmp((const char *)op, "isc-V3.0.", 9) == 0)) {
822: log_error("WARNING: failover as of versions 3.1.0 and "
823: "on are not reverse compatible with "
824: "versions 3.0.x.");
825: }
826:
827: goto out;
828: }
829:
830: /* For values that require swapping, read them in one at a time
831: using routines that swap bytes. */
832: for (i = 0; i < op_count; i++) {
833: switch (ft_options [option_code].type) {
834: case FT_UINT32:
835: omapi_connection_get_uint32 (c, (u_int32_t *)op);
836: op += 4;
837: link -> imsg_count += 4;
838: break;
839:
840: case FT_UINT16:
841: omapi_connection_get_uint16 (c, (u_int16_t *)op);
842: op += 2;
843: link -> imsg_count += 2;
844: break;
845:
846: default:
847: /* Everything else should have been handled
848: already. */
849: log_error ("FAILOVER: option %s: bad type %d",
850: ft_options [option_code].name,
851: ft_options [option_code].type);
852: return ISC_R_PROTOCOLERROR;
853: }
854: }
855: out:
856: /* Remember that we got this option. */
857: link -> imsg -> options_present |= ft_options [option_code].bit;
858: return ISC_R_SUCCESS;
859: }
860:
861: isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
862: omapi_object_t *id,
863: omapi_data_string_t *name,
864: omapi_typed_data_t *value)
865: {
866: if (h -> type != omapi_type_protocol)
867: return ISC_R_INVALIDARG;
868:
869: /* Never valid to set these. */
870: if (!omapi_ds_strcmp (name, "link-port") ||
871: !omapi_ds_strcmp (name, "link-name") ||
872: !omapi_ds_strcmp (name, "link-state"))
873: return ISC_R_NOPERM;
874:
875: if (h -> inner && h -> inner -> type -> set_value)
876: return (*(h -> inner -> type -> set_value))
877: (h -> inner, id, name, value);
878: return ISC_R_NOTFOUND;
879: }
880:
881: isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
882: omapi_object_t *id,
883: omapi_data_string_t *name,
884: omapi_value_t **value)
885: {
886: dhcp_failover_link_t *link;
887:
888: if (h -> type != omapi_type_protocol)
889: return ISC_R_INVALIDARG;
890: link = (dhcp_failover_link_t *)h;
891:
892: if (!omapi_ds_strcmp (name, "link-port")) {
893: return omapi_make_int_value (value, name,
894: (int)link -> peer_port, MDL);
895: } else if (!omapi_ds_strcmp (name, "link-state")) {
896: if (link -> state < 0 ||
897: link -> state >= dhcp_flink_state_max)
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;
957: if (link -> state < 0 ||
958: link -> state >= dhcp_flink_state_max)
959: status = omapi_connection_put_string (c, "invalid link state");
960: else
961: status = (omapi_connection_put_string
962: (c, dhcp_flink_state_names [link -> state]));
963: if (status != ISC_R_SUCCESS)
964: return status;
965:
966: if (link -> inner && link -> inner -> type -> stuff_values)
967: return (*(link -> inner -> type -> stuff_values)) (c, id,
968: link -> inner);
969: return ISC_R_SUCCESS;
970: }
971:
972: /* Set up a listener for the omapi protocol. The handle stored points to
973: a listener object, not a protocol object. */
974:
975: isc_result_t dhcp_failover_listen (omapi_object_t *h)
976: {
977: isc_result_t status;
978: dhcp_failover_listener_t *obj, *l;
979: omapi_value_t *value = (omapi_value_t *)0;
980: omapi_addr_t local_addr;
981: unsigned long port;
982:
983: status = omapi_get_value_str (h, (omapi_object_t *)0,
984: "local-port", &value);
985: if (status != ISC_R_SUCCESS)
986: return status;
987: if (!value -> value) {
988: omapi_value_dereference (&value, MDL);
989: return ISC_R_INVALIDARG;
990: }
991:
992: status = omapi_get_int_value (&port, value -> value);
993: omapi_value_dereference (&value, MDL);
994: if (status != ISC_R_SUCCESS)
995: return status;
996: local_addr.port = port;
997:
998: status = omapi_get_value_str (h, (omapi_object_t *)0,
999: "local-address", &value);
1000: if (status != ISC_R_SUCCESS)
1001: return status;
1002: if (!value -> value) {
1003: nogood:
1004: omapi_value_dereference (&value, MDL);
1005: return ISC_R_INVALIDARG;
1006: }
1007:
1008: if (value -> value -> type != omapi_datatype_data ||
1009: value -> value -> u.buffer.len != sizeof (struct in_addr))
1010: goto nogood;
1011:
1012: memcpy (local_addr.address, value -> value -> u.buffer.value,
1013: value -> value -> u.buffer.len);
1014: local_addr.addrlen = value -> value -> u.buffer.len;
1015: local_addr.addrtype = AF_INET;
1016:
1017: omapi_value_dereference (&value, MDL);
1018:
1019: /* Are we already listening on this port and address? */
1020: for (l = failover_listeners; l; l = l -> next) {
1021: if (l -> address.port == local_addr.port &&
1022: l -> address.addrtype == local_addr.addrtype &&
1023: l -> address.addrlen == local_addr.addrlen &&
1024: !memcmp (l -> address.address, local_addr.address,
1025: local_addr.addrlen))
1026: break;
1027: }
1028: /* Already listening. */
1029: if (l)
1030: return ISC_R_SUCCESS;
1031:
1032: obj = (dhcp_failover_listener_t *)0;
1033: status = dhcp_failover_listener_allocate (&obj, MDL);
1034: if (status != ISC_R_SUCCESS)
1035: return status;
1036: obj -> address = local_addr;
1037:
1038: status = omapi_listen_addr ((omapi_object_t *)obj, &obj -> address, 1);
1039: if (status != ISC_R_SUCCESS)
1040: return status;
1041:
1042: status = omapi_object_reference (&h -> outer,
1043: (omapi_object_t *)obj, MDL);
1044: if (status != ISC_R_SUCCESS) {
1045: dhcp_failover_listener_dereference (&obj, MDL);
1046: return status;
1047: }
1048: status = omapi_object_reference (&obj -> inner, h, MDL);
1049: if (status != ISC_R_SUCCESS) {
1050: dhcp_failover_listener_dereference (&obj, MDL);
1051: return status;
1052: }
1053:
1054: /* Put this listener on the list. */
1055: if (failover_listeners) {
1056: dhcp_failover_listener_reference (&obj -> next,
1057: failover_listeners, MDL);
1058: dhcp_failover_listener_dereference (&failover_listeners, MDL);
1059: }
1060: dhcp_failover_listener_reference (&failover_listeners, obj, MDL);
1061:
1062: return dhcp_failover_listener_dereference (&obj, MDL);
1063: }
1064:
1065: /* Signal handler for protocol listener - if we get a connect signal,
1066: create a new protocol connection, otherwise pass the signal down. */
1067:
1068: isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
1069: const char *name, va_list ap)
1070: {
1071: isc_result_t status;
1072: omapi_connection_object_t *c;
1073: dhcp_failover_link_t *obj;
1074: dhcp_failover_listener_t *p;
1075: dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
1076:
1077: if (!o || o -> type != dhcp_type_failover_listener)
1078: return ISC_R_INVALIDARG;
1079: p = (dhcp_failover_listener_t *)o;
1080:
1081: /* Not a signal we recognize? */
1082: if (strcmp (name, "connect")) {
1083: if (p -> inner && p -> inner -> type -> signal_handler)
1084: return (*(p -> inner -> type -> signal_handler))
1085: (p -> inner, name, ap);
1086: return ISC_R_NOTFOUND;
1087: }
1088:
1089: c = va_arg (ap, omapi_connection_object_t *);
1090: if (!c || c -> type != omapi_type_connection)
1091: return ISC_R_INVALIDARG;
1092:
1093: /* See if we can find a failover_state object that
1094: matches this connection. */
1095: for (s = failover_states; s; s = s -> next) {
1096: if (dhcp_failover_state_match
1097: (s, (u_int8_t *)&c -> remote_addr.sin_addr,
1098: sizeof c -> remote_addr.sin_addr)) {
1099: state = s;
1100: break;
1101: }
1102: }
1103: if (!state) {
1104: log_info ("failover: listener: no matching state");
1105: omapi_disconnect ((omapi_object_t *)c, 1);
1106: return(ISC_R_NOTFOUND);
1107: }
1108:
1109: obj = (dhcp_failover_link_t *)0;
1110: status = dhcp_failover_link_allocate (&obj, MDL);
1111: if (status != ISC_R_SUCCESS)
1112: return status;
1113: obj -> peer_port = ntohs (c -> remote_addr.sin_port);
1114:
1115: status = omapi_object_reference (&obj -> outer,
1116: (omapi_object_t *)c, MDL);
1117: if (status != ISC_R_SUCCESS) {
1118: lose:
1119: dhcp_failover_link_dereference (&obj, MDL);
1120: log_info ("failover: listener: picayune failure.");
1121: omapi_disconnect ((omapi_object_t *)c, 1);
1122: return status;
1123: }
1124:
1125: status = omapi_object_reference (&c -> inner,
1126: (omapi_object_t *)obj, MDL);
1127: if (status != ISC_R_SUCCESS)
1128: goto lose;
1129:
1130: status = dhcp_failover_state_reference (&obj -> state_object,
1131: state, MDL);
1132: if (status != ISC_R_SUCCESS)
1133: goto lose;
1134:
1135: omapi_signal_in ((omapi_object_t *)obj, "connect");
1136:
1137: return dhcp_failover_link_dereference (&obj, MDL);
1138: }
1139:
1140: isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
1141: omapi_object_t *id,
1142: omapi_data_string_t *name,
1143: omapi_typed_data_t *value)
1144: {
1145: if (h -> type != dhcp_type_failover_listener)
1146: return ISC_R_INVALIDARG;
1147:
1148: if (h -> inner && h -> inner -> type -> set_value)
1149: return (*(h -> inner -> type -> set_value))
1150: (h -> inner, id, name, value);
1151: return ISC_R_NOTFOUND;
1152: }
1153:
1154: isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
1155: omapi_object_t *id,
1156: omapi_data_string_t *name,
1157: omapi_value_t **value)
1158: {
1159: if (h -> type != dhcp_type_failover_listener)
1160: return ISC_R_INVALIDARG;
1161:
1162: if (h -> inner && h -> inner -> type -> get_value)
1163: return (*(h -> inner -> type -> get_value))
1164: (h -> inner, id, name, value);
1165: return ISC_R_NOTFOUND;
1166: }
1167:
1168: isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
1169: const char *file, int line)
1170: {
1171: dhcp_failover_listener_t *l;
1172:
1173: if (h -> type != dhcp_type_failover_listener)
1174: return ISC_R_INVALIDARG;
1175: l = (dhcp_failover_listener_t *)h;
1176: if (l -> next)
1177: dhcp_failover_listener_dereference (&l -> next, file, line);
1178:
1179: return ISC_R_SUCCESS;
1180: }
1181:
1182: /* Write all the published values associated with the object through the
1183: specified connection. */
1184:
1185: isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
1186: omapi_object_t *id,
1187: omapi_object_t *p)
1188: {
1189: if (p -> type != dhcp_type_failover_listener)
1190: return ISC_R_INVALIDARG;
1191:
1192: if (p -> inner && p -> inner -> type -> stuff_values)
1193: return (*(p -> inner -> type -> stuff_values)) (c, id,
1194: p -> inner);
1195: return ISC_R_SUCCESS;
1196: }
1197:
1198: /* Set up master state machine for the failover protocol. */
1199:
1200: isc_result_t dhcp_failover_register (omapi_object_t *h)
1201: {
1202: isc_result_t status;
1203: dhcp_failover_state_t *obj;
1204: unsigned long port;
1205: omapi_value_t *value = (omapi_value_t *)0;
1206:
1207: status = omapi_get_value_str (h, (omapi_object_t *)0,
1208: "local-port", &value);
1209: if (status != ISC_R_SUCCESS)
1210: return status;
1211: if (!value -> value) {
1212: omapi_value_dereference (&value, MDL);
1213: return ISC_R_INVALIDARG;
1214: }
1215:
1216: status = omapi_get_int_value (&port, value -> value);
1217: omapi_value_dereference (&value, MDL);
1218: if (status != ISC_R_SUCCESS)
1219: return status;
1220:
1221: obj = (dhcp_failover_state_t *)0;
1222: dhcp_failover_state_allocate (&obj, MDL);
1223: obj -> me.port = port;
1224:
1225: status = omapi_listen ((omapi_object_t *)obj, port, 1);
1226: if (status != ISC_R_SUCCESS) {
1227: dhcp_failover_state_dereference (&obj, MDL);
1228: return status;
1229: }
1230:
1231: status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
1232: MDL);
1233: if (status != ISC_R_SUCCESS) {
1234: dhcp_failover_state_dereference (&obj, MDL);
1235: return status;
1236: }
1237: status = omapi_object_reference (&obj -> inner, h, MDL);
1238: dhcp_failover_state_dereference (&obj, MDL);
1239: return status;
1240: }
1241:
1242: /* Signal handler for protocol state machine. */
1243:
1244: isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
1245: const char *name, va_list ap)
1246: {
1247: isc_result_t status;
1248: dhcp_failover_state_t *state;
1249: dhcp_failover_link_t *link;
1250: struct timeval tv;
1251:
1252: if (!o || o -> type != dhcp_type_failover_state)
1253: return ISC_R_INVALIDARG;
1254: state = (dhcp_failover_state_t *)o;
1255:
1256: /* Not a signal we recognize? */
1257: if (strcmp (name, "disconnect") &&
1258: strcmp (name, "message")) {
1259: if (state -> inner && state -> inner -> type -> signal_handler)
1260: return (*(state -> inner -> type -> signal_handler))
1261: (state -> inner, name, ap);
1262: return ISC_R_NOTFOUND;
1263: }
1264:
1265: /* Handle connect signals by seeing what state we're in
1266: and potentially doing a state transition. */
1267: if (!strcmp (name, "disconnect")) {
1268: link = va_arg (ap, dhcp_failover_link_t *);
1269:
1270: dhcp_failover_link_dereference (&state -> link_to_peer, MDL);
1271: dhcp_failover_state_transition (state, "disconnect");
1272: if (state -> i_am == primary) {
1273: #if defined (DEBUG_FAILOVER_TIMING)
1274: log_info ("add_timeout +90 %s",
1275: "dhcp_failover_reconnect");
1276: #endif
1277: tv . tv_sec = cur_time + 90;
1278: tv . tv_usec = 0;
1279: add_timeout (&tv, dhcp_failover_reconnect,
1280: state,
1281: (tvref_t)dhcp_failover_state_reference,
1282: (tvunref_t)
1283: dhcp_failover_state_dereference);
1284: }
1285: } else if (!strcmp (name, "message")) {
1286: link = va_arg (ap, dhcp_failover_link_t *);
1287:
1288: if (link -> imsg -> type == FTM_CONNECT) {
1289: /* If we already have a link to the peer, it must be
1290: dead, so drop it.
1291: XXX Is this the right thing to do?
1292: XXX Probably not - what if both peers start at
1293: XXX the same time? */
1294: if (state -> link_to_peer) {
1295: dhcp_failover_send_connectack
1296: ((omapi_object_t *)link, state,
1297: FTR_DUP_CONNECTION,
1298: "already connected");
1299: omapi_disconnect (link -> outer, 1);
1300: return ISC_R_SUCCESS;
1301: }
1302: if (!(link -> imsg -> options_present & FTB_MCLT)) {
1303: dhcp_failover_send_connectack
1304: ((omapi_object_t *)link, state,
1305: FTR_INVALID_MCLT,
1306: "no MCLT provided");
1307: omapi_disconnect (link -> outer, 1);
1308: return ISC_R_SUCCESS;
1309: }
1310:
1311: dhcp_failover_link_reference (&state -> link_to_peer,
1312: link, MDL);
1313: status = (dhcp_failover_send_connectack
1314: ((omapi_object_t *)link, state, 0, 0));
1315: if (status != ISC_R_SUCCESS) {
1316: dhcp_failover_link_dereference
1317: (&state -> link_to_peer, MDL);
1318: log_info ("dhcp_failover_send_connectack: %s",
1319: isc_result_totext (status));
1320: omapi_disconnect (link -> outer, 1);
1321: return ISC_R_SUCCESS;
1322: }
1323: if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1324: state -> partner.max_flying_updates =
1325: link -> imsg -> max_unacked;
1326: if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
1327: state -> partner.max_response_delay =
1328: link -> imsg -> receive_timer;
1329: state -> mclt = link -> imsg -> mclt;
1330: dhcp_failover_send_state (state);
1331: cancel_timeout (dhcp_failover_link_startup_timeout,
1332: link);
1333: } else if (link -> imsg -> type == FTM_CONNECTACK) {
1334: const char *errmsg;
1335: char errbuf[1024];
1336: int reason;
1337:
1338: cancel_timeout (dhcp_failover_link_startup_timeout,
1339: link);
1340:
1341: if (!(link->imsg->options_present &
1342: FTB_RELATIONSHIP_NAME)) {
1343: errmsg = "missing relationship-name";
1344: reason = FTR_INVALID_PARTNER;
1345: goto badconnectack;
1346: }
1347:
1348: if (link->imsg->options_present & FTB_REJECT_REASON) {
1349: /* XXX: add message option to text output. */
1350: log_error ("Failover CONNECT to %s rejected: %s",
1351: state ? state->name : "unknown",
1352: (dhcp_failover_reject_reason_print
1353: (link -> imsg -> reject_reason)));
1354: /* XXX print message from peer if peer sent message. */
1355: omapi_disconnect (link -> outer, 1);
1356: return ISC_R_SUCCESS;
1357: }
1358:
1359: if (!dhcp_failover_state_match_by_name(state,
1360: &link->imsg->relationship_name)) {
1361: /* XXX: Overflow results in log truncation, safe. */
1362: snprintf(errbuf, sizeof(errbuf), "remote failover "
1363: "relationship name %.*s does not match",
1364: (int)link->imsg->relationship_name.count,
1365: link->imsg->relationship_name.data);
1366: errmsg = errbuf;
1367: reason = FTR_INVALID_PARTNER;
1368: badconnectack:
1369: log_error("Failover CONNECTACK from %s: %s",
1370: state->name, errmsg);
1371: dhcp_failover_send_disconnect ((omapi_object_t *)link,
1372: reason, errmsg);
1373: omapi_disconnect (link -> outer, 0);
1374: return ISC_R_SUCCESS;
1375: }
1376:
1377: if (state -> link_to_peer) {
1378: errmsg = "already connected";
1379: reason = FTR_DUP_CONNECTION;
1380: goto badconnectack;
1381: }
1382:
1383: if ((cur_time > link -> imsg -> time &&
1384: cur_time - link -> imsg -> time > 60) ||
1385: (cur_time < link -> imsg -> time &&
1386: link -> imsg -> time - cur_time > 60)) {
1387: errmsg = "time offset too large";
1388: reason = FTR_TIMEMISMATCH;
1389: goto badconnectack;
1390: }
1391:
1392: dhcp_failover_link_reference (&state -> link_to_peer,
1393: link, MDL);
1394: #if 0
1395: /* XXX This is probably the right thing to do, but
1396: XXX for release three, to make the smallest possible
1397: XXX change, we are doing this when the peer state
1398: XXX changes instead. */
1399: if (state -> me.state == startup)
1400: dhcp_failover_set_state (state,
1401: state -> saved_state);
1402: else
1403: #endif
1404: dhcp_failover_send_state (state);
1405:
1406: if (link -> imsg -> options_present & FTB_MAX_UNACKED)
1407: state -> partner.max_flying_updates =
1408: link -> imsg -> max_unacked;
1409: if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
1410: state -> partner.max_response_delay =
1411: link -> imsg -> receive_timer;
1412: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1413: log_info ("add_timeout +%d %s",
1414: (int)state -> partner.max_response_delay / 3,
1415: "dhcp_failover_send_contact");
1416: #endif
1417: tv . tv_sec = cur_time +
1418: (int)state -> partner.max_response_delay / 3;
1419: tv . tv_usec = 0;
1420: add_timeout (&tv,
1421: dhcp_failover_send_contact, state,
1422: (tvref_t)dhcp_failover_state_reference,
1423: (tvunref_t)dhcp_failover_state_dereference);
1424: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1425: log_info ("add_timeout +%d %s",
1426: (int)state -> me.max_response_delay,
1427: "dhcp_failover_timeout");
1428: #endif
1429: tv . tv_sec = cur_time +
1430: (int)state -> me.max_response_delay;
1431: tv . tv_usec = 0;
1432: add_timeout (&tv,
1433: dhcp_failover_timeout, state,
1434: (tvref_t)dhcp_failover_state_reference,
1435: (tvunref_t)dhcp_failover_state_dereference);
1436: } else if (link -> imsg -> type == FTM_DISCONNECT) {
1437: if (link -> imsg -> reject_reason) {
1438: log_error ("Failover DISCONNECT from %s: %s",
1439: state ? state->name : "unknown",
1440: (dhcp_failover_reject_reason_print
1441: (link -> imsg -> reject_reason)));
1442: }
1443: omapi_disconnect (link -> outer, 1);
1444: } else if (link -> imsg -> type == FTM_BNDUPD) {
1445: dhcp_failover_process_bind_update (state,
1446: link -> imsg);
1447: } else if (link -> imsg -> type == FTM_BNDACK) {
1448: dhcp_failover_process_bind_ack (state, link -> imsg);
1449: } else if (link -> imsg -> type == FTM_UPDREQ) {
1450: dhcp_failover_process_update_request (state,
1451: link -> imsg);
1452: } else if (link -> imsg -> type == FTM_UPDREQALL) {
1453: dhcp_failover_process_update_request_all
1454: (state, link -> imsg);
1455: } else if (link -> imsg -> type == FTM_UPDDONE) {
1456: dhcp_failover_process_update_done (state,
1457: link -> imsg);
1458: } else if (link -> imsg -> type == FTM_POOLREQ) {
1459: dhcp_failover_pool_reqbalance(state);
1460: } else if (link -> imsg -> type == FTM_POOLRESP) {
1461: log_info ("pool response: %ld leases",
1462: (unsigned long)
1463: link -> imsg -> addresses_transferred);
1464: } else if (link -> imsg -> type == FTM_STATE) {
1465: dhcp_failover_peer_state_changed (state,
1466: link -> imsg);
1467: }
1468:
1469: /* Add a timeout so that if the partner doesn't send
1470: another message for the maximum transmit idle time
1471: plus a grace of one second, we close the
1472: connection. */
1473: if (state -> link_to_peer &&
1474: state -> link_to_peer == link &&
1475: state -> link_to_peer -> state != dhcp_flink_disconnected)
1476: {
1477: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
1478: log_info ("add_timeout +%d %s",
1479: (int)state -> me.max_response_delay,
1480: "dhcp_failover_timeout");
1481: #endif
1482: tv . tv_sec = cur_time +
1483: (int)state -> me.max_response_delay;
1484: tv . tv_usec = 0;
1485: add_timeout (&tv,
1486: dhcp_failover_timeout, state,
1487: (tvref_t)dhcp_failover_state_reference,
1488: (tvunref_t)dhcp_failover_state_dereference);
1489:
1490: }
1491: }
1492:
1493: /* Handle all the events we care about... */
1494: return ISC_R_SUCCESS;
1495: }
1496:
1497: isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
1498: const char *name)
1499: {
1500: isc_result_t status;
1501:
1502: /* XXX Check these state transitions against the spec! */
1503: if (!strcmp (name, "disconnect")) {
1504: if (state -> link_to_peer) {
1505: log_info ("peer %s: disconnected", state -> name);
1506: if (state -> link_to_peer -> state_object)
1507: dhcp_failover_state_dereference
1508: (&state -> link_to_peer -> state_object, MDL);
1509: dhcp_failover_link_dereference (&state -> link_to_peer,
1510: MDL);
1511: }
1512: cancel_timeout (dhcp_failover_send_contact, state);
1513: cancel_timeout (dhcp_failover_timeout, state);
1514: cancel_timeout (dhcp_failover_startup_timeout, state);
1515:
1516: switch (state -> me.state == startup ?
1517: state -> saved_state : state -> me.state) {
1518: /* In these situations, we remain in the current
1519: * state, or if in startup enter those states.
1520: */
1521: case communications_interrupted:
1522: case conflict_done:
1523: case partner_down:
1524: case paused:
1525: case recover:
1526: case recover_done:
1527: case recover_wait:
1528: case resolution_interrupted:
1529: case shut_down:
1530: /* Already in the right state? */
1531: if (state -> me.state == startup)
1532: return (dhcp_failover_set_state
1533: (state, state -> saved_state));
1534: return ISC_R_SUCCESS;
1535:
1536: case potential_conflict:
1537: return dhcp_failover_set_state
1538: (state, resolution_interrupted);
1539:
1540: case normal:
1541: return dhcp_failover_set_state
1542: (state, communications_interrupted);
1543:
1544: case unknown_state:
1545: return dhcp_failover_set_state
1546: (state, resolution_interrupted);
1547:
1548: default:
1549: log_fatal("Impossible case at %s:%d.", MDL);
1550: break; /* can't happen. */
1551: }
1552: } else if (!strcmp (name, "connect")) {
1553: switch (state -> me.state) {
1554: case communications_interrupted:
1555: status = dhcp_failover_set_state (state, normal);
1556: dhcp_failover_send_updates (state);
1557: return status;
1558:
1559: case resolution_interrupted:
1560: return dhcp_failover_set_state (state,
1561: potential_conflict);
1562:
1563: case conflict_done:
1564: case partner_down:
1565: case potential_conflict:
1566: case normal:
1567: case recover:
1568: case shut_down:
1569: case paused:
1570: case unknown_state:
1571: case recover_done:
1572: case startup:
1573: case recover_wait:
1574: return dhcp_failover_send_state (state);
1575:
1576: default:
1577: log_fatal("Impossible case at %s:%d.", MDL);
1578: break;
1579: }
1580: } else if (!strcmp (name, "startup")) {
1581: dhcp_failover_set_state (state, startup);
1582: return ISC_R_SUCCESS;
1583: } else if (!strcmp (name, "connect-timeout")) {
1584: switch (state -> me.state) {
1585: case communications_interrupted:
1586: case partner_down:
1587: case resolution_interrupted:
1588: case paused:
1589: case startup:
1590: case shut_down:
1591: case conflict_done:
1592: return ISC_R_SUCCESS;
1593:
1594: case normal:
1595: case recover:
1596: case recover_wait:
1597: case recover_done:
1598: case unknown_state:
1599: return dhcp_failover_set_state
1600: (state, communications_interrupted);
1601:
1602: case potential_conflict:
1603: return dhcp_failover_set_state
1604: (state, resolution_interrupted);
1605:
1606: default:
1607: log_fatal("Impossible case at %s:%d.", MDL);
1608: break;
1609: }
1610: }
1611: return ISC_R_INVALIDARG;
1612: }
1613:
1614: isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state)
1615: {
1616: switch (state -> me.state) {
1617: case unknown_state:
1618: state -> service_state = not_responding;
1619: state -> nrr = " (my state unknown)";
1620: break;
1621:
1622: case partner_down:
1623: state -> service_state = service_partner_down;
1624: state -> nrr = "";
1625: break;
1626:
1627: case normal:
1628: state -> service_state = cooperating;
1629: state -> nrr = "";
1630: break;
1631:
1632: case communications_interrupted:
1633: state -> service_state = not_cooperating;
1634: state -> nrr = "";
1635: break;
1636:
1637: case resolution_interrupted:
1638: case potential_conflict:
1639: case conflict_done:
1640: state -> service_state = not_responding;
1641: state -> nrr = " (resolving conflicts)";
1642: break;
1643:
1644: case recover:
1645: state -> service_state = not_responding;
1646: state -> nrr = " (recovering)";
1647: break;
1648:
1649: case shut_down:
1650: state -> service_state = not_responding;
1651: state -> nrr = " (shut down)";
1652: break;
1653:
1654: case paused:
1655: state -> service_state = not_responding;
1656: state -> nrr = " (paused)";
1657: break;
1658:
1659: case recover_wait:
1660: state -> service_state = not_responding;
1661: state -> nrr = " (recover wait)";
1662: break;
1663:
1664: case recover_done:
1665: state -> service_state = not_responding;
1666: state -> nrr = " (recover done)";
1667: break;
1668:
1669: case startup:
1670: state -> service_state = service_startup;
1671: state -> nrr = " (startup)";
1672: break;
1673:
1674: default:
1675: log_fatal("Impossible case at %s:%d.\n", MDL);
1676: break;
1677: }
1678:
1679: /* Some peer states can require us not to respond, even if our
1680: state doesn't. */
1681: /* XXX hm. I suspect this isn't true anymore. */
1682: if (state -> service_state != not_responding) {
1683: switch (state -> partner.state) {
1684: case partner_down:
1685: state -> service_state = not_responding;
1686: state -> nrr = " (peer demands: recovering)";
1687: break;
1688:
1689: case potential_conflict:
1690: case conflict_done:
1691: case resolution_interrupted:
1692: state -> service_state = not_responding;
1693: state -> nrr = " (peer demands: resolving conflicts)";
1694: break;
1695:
1696: /* Other peer states don't affect our behaviour. */
1697: default:
1698: break;
1699: }
1700: }
1701:
1702: return ISC_R_SUCCESS;
1703: }
1704:
1705: isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
1706: enum failover_state new_state)
1707: {
1708: enum failover_state saved_state;
1709: TIME saved_stos;
1710: struct pool *p;
1711: struct shared_network *s;
1712: struct lease *l;
1713: struct timeval tv;
1714:
1715: /* If we're in certain states where we're sending updates, and the peer
1716: * state changes, we need to re-schedule any pending updates just to
1717: * be on the safe side. This results in retransmission.
1718: */
1719: switch (state -> me.state) {
1720: case normal:
1721: case potential_conflict:
1722: case partner_down:
1723: if (state -> ack_queue_tail) {
1724: struct lease *lp;
1725:
1726: /* Zap the flags. */
1727: for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
1728: lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
1729: ON_UPDATE_QUEUE);
1730:
1731: /* Now hook the ack queue to the beginning of the update
1732: queue. */
1733: if (state -> update_queue_head) {
1734: lease_reference (&state -> ack_queue_tail -> next_pending,
1735: state -> update_queue_head, MDL);
1736: lease_dereference (&state -> update_queue_head, MDL);
1737: }
1738: lease_reference (&state -> update_queue_head,
1739: state -> ack_queue_head, MDL);
1740: if (!state -> update_queue_tail) {
1741: #if defined (POINTER_DEBUG)
1742: if (state -> ack_queue_tail -> next_pending) {
1743: log_error ("next pending on ack queue tail.");
1744: abort ();
1745: }
1746: #endif
1747: lease_reference (&state -> update_queue_tail,
1748: state -> ack_queue_tail, MDL);
1749: }
1750: lease_dereference (&state -> ack_queue_tail, MDL);
1751: lease_dereference (&state -> ack_queue_head, MDL);
1752: state -> cur_unacked_updates = 0;
1753: }
1754: /* We will re-queue a timeout later, if applicable. */
1755: cancel_timeout (dhcp_failover_keepalive, state);
1756: break;
1757:
1758: default:
1759: break;
1760: }
1761:
1762: /* Tentatively make the transition. */
1763: saved_state = state -> me.state;
1764: saved_stos = state -> me.stos;
1765:
1766: /* Keep the old stos if we're going into recover_wait or if we're
1767: coming into or out of startup. */
1768: if (new_state != recover_wait && new_state != startup &&
1769: saved_state != startup)
1770: state -> me.stos = cur_time;
1771:
1772: /* If we're in shutdown, peer is in partner_down, and we're moving
1773: to recover, we can skip waiting for MCLT to expire. This happens
1774: when a server is moved administratively into shutdown prior to
1775: actually shutting down. Of course, if there are any updates
1776: pending we can't actually do this. */
1777: if (new_state == recover && saved_state == shut_down &&
1778: state -> partner.state == partner_down &&
1779: !state -> update_queue_head && !state -> ack_queue_head)
1780: state -> me.stos = cur_time - state -> mclt;
1781:
1782: state -> me.state = new_state;
1783: if (new_state == startup && saved_state != startup)
1784: state -> saved_state = saved_state;
1785:
1786: /* If we can't record the new state, we can't make a state transition. */
1787: if (!write_failover_state (state) || !commit_leases ()) {
1788: log_error ("Unable to record current failover state for %s",
1789: state -> name);
1790: state -> me.state = saved_state;
1791: state -> me.stos = saved_stos;
1792: return ISC_R_IOERROR;
1793: }
1794:
1795: log_info ("failover peer %s: I move from %s to %s",
1796: state -> name, dhcp_failover_state_name_print (saved_state),
1797: dhcp_failover_state_name_print (state -> me.state));
1798:
1799: /* If we were in startup and we just left it, cancel the timeout. */
1800: if (new_state != startup && saved_state == startup)
1801: cancel_timeout (dhcp_failover_startup_timeout, state);
1802:
1803: /* Set our service state. */
1804: dhcp_failover_set_service_state (state);
1805:
1806: /* Tell the peer about it. */
1807: if (state -> link_to_peer)
1808: dhcp_failover_send_state (state);
1809:
1810: switch (new_state) {
1811: case normal:
1812: /* Upon entering normal state, the server is expected to retransmit
1813: * all pending binding updates. This is a good opportunity to
1814: * rebalance the pool (potentially making new pending updates),
1815: * which also schedules the next pool rebalance.
1816: */
1817: dhcp_failover_pool_balance(state);
1818: dhcp_failover_generate_update_queue(state, 0);
1819:
1820: if (state->update_queue_tail != NULL) {
1821: dhcp_failover_send_updates(state);
1822: log_info("Sending updates to %s.", state->name);
1823: }
1824:
1825: break;
1826:
1827: case potential_conflict:
1828: if (state -> i_am == primary)
1829: dhcp_failover_send_update_request (state);
1830: break;
1831:
1832: case startup:
1833: #if defined (DEBUG_FAILOVER_TIMING)
1834: log_info ("add_timeout +15 %s",
1835: "dhcp_failover_startup_timeout");
1836: #endif
1837: tv . tv_sec = cur_time + 15;
1838: tv . tv_usec = 0;
1839: add_timeout (&tv,
1840: dhcp_failover_startup_timeout,
1841: state,
1842: (tvref_t)omapi_object_reference,
1843: (tvunref_t)
1844: omapi_object_dereference);
1845: break;
1846:
1847: /* If we come back in recover_wait and there's still waiting
1848: to do, set a timeout. */
1849: case recover_wait:
1850: if (state -> me.stos + state -> mclt > cur_time) {
1851: #if defined (DEBUG_FAILOVER_TIMING)
1852: log_info ("add_timeout +%d %s",
1853: (int)(cur_time -
1854: state -> me.stos + state -> mclt),
1855: "dhcp_failover_startup_timeout");
1856: #endif
1857: tv . tv_sec = (int)(state -> me.stos + state -> mclt);
1858: tv . tv_usec = 0;
1859: add_timeout (&tv,
1860: dhcp_failover_recover_done,
1861: state,
1862: (tvref_t)omapi_object_reference,
1863: (tvunref_t)
1864: omapi_object_dereference);
1865: } else
1866: dhcp_failover_recover_done (state);
1867: break;
1868:
1869: case recover:
1870: /* XXX: We're supposed to calculate if updreq or updreqall is
1871: * needed. In theory, we should only have to updreqall if we
1872: * are positive we lost our stable storage.
1873: */
1874: if (state -> link_to_peer)
1875: dhcp_failover_send_update_request_all (state);
1876: break;
1877:
1878: case partner_down:
1879: /* For every expired lease, set a timeout for it to become free. */
1880: for (s = shared_networks; s; s = s -> next) {
1881: for (p = s -> pools; p; p = p -> next) {
1882: if (p -> failover_peer == state) {
1883: for (l = p->expired ; l ; l = l->next) {
1884: l->tsfp = state->me.stos + state->mclt;
1885: l->sort_time = (l->tsfp > l->ends) ?
1886: l->tsfp : l->ends;
1887: }
1888: if (p->expired &&
1889: (p->expired->sort_time < p->next_event_time)) {
1890:
1891: p->next_event_time = p->expired->sort_time;
1892: #if defined (DEBUG_FAILOVER_TIMING)
1893: log_info ("add_timeout +%d %s",
1894: (int)(cur_time - p->next_event_time),
1895: "pool_timer");
1896: #endif
1897: tv.tv_sec = p->next_event_time;
1898: tv.tv_usec = 0;
1899: add_timeout(&tv, pool_timer, p,
1900: (tvref_t)pool_reference,
1901: (tvunref_t)pool_dereference);
1902: }
1903: }
1904: }
1905: }
1906: break;
1907:
1908:
1909: default:
1910: break;
1911: }
1912:
1913: return ISC_R_SUCCESS;
1914: }
1915:
1916: isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state,
1917: failover_message_t *msg)
1918: {
1919: enum failover_state previous_state = state -> partner.state;
1920: enum failover_state new_state;
1921: int startupp;
1922:
1923: new_state = msg -> server_state;
1924: startupp = (msg -> server_flags & FTF_SERVER_STARTUP) ? 1 : 0;
1925:
1926: if (state -> partner.state == new_state && state -> me.state) {
1927: switch (state -> me.state) {
1928: case startup:
1929: dhcp_failover_set_state (state, state -> saved_state);
1930: return ISC_R_SUCCESS;
1931:
1932: case unknown_state:
1933: case normal:
1934: case potential_conflict:
1935: case recover_done:
1936: case shut_down:
1937: case paused:
1938: case recover_wait:
1939: return ISC_R_SUCCESS;
1940:
1941: /* If we get a peer state change when we're
1942: disconnected, we always process it. */
1943: case partner_down:
1944: case communications_interrupted:
1945: case resolution_interrupted:
1946: case recover:
1947: case conflict_done:
1948: break;
1949:
1950: default:
1951: log_fatal("Impossible case at %s:%d.", MDL);
1952: break;
1953: }
1954: }
1955:
1956: state -> partner.state = new_state;
1957:
1958: log_info ("failover peer %s: peer moves from %s to %s",
1959: state -> name,
1960: dhcp_failover_state_name_print (previous_state),
1961: dhcp_failover_state_name_print (state -> partner.state));
1962:
1963: if (!write_failover_state (state) || !commit_leases ()) {
1964: /* This is bad, but it's not fatal. Of course, if we
1965: can't write to the lease database, we're not going to
1966: get much done anyway. */
1967: log_error ("Unable to record current failover state for %s",
1968: state -> name);
1969: }
1970:
1971: /* Quickly validate the new state as being one of the 13 known
1972: * states.
1973: */
1974: switch (new_state) {
1975: case unknown_state:
1976: case startup:
1977: case normal:
1978: case communications_interrupted:
1979: case partner_down:
1980: case potential_conflict:
1981: case recover:
1982: case paused:
1983: case shut_down:
1984: case recover_done:
1985: case resolution_interrupted:
1986: case conflict_done:
1987: case recover_wait:
1988: break;
1989:
1990: default:
1991: log_error("failover peer %s: Invalid state: %d", state->name,
1992: new_state);
1993: dhcp_failover_set_state(state, shut_down);
1994: return ISC_R_SUCCESS;
1995: }
1996:
1997: /* Do any state transitions that are required as a result of the
1998: peer's state transition. */
1999:
2000: switch (state -> me.state == startup ?
2001: state -> saved_state : state -> me.state) {
2002: case normal:
2003: switch (new_state) {
2004: case normal:
2005: dhcp_failover_state_pool_check (state);
2006: break;
2007:
2008: case partner_down:
2009: if (state -> me.state == startup)
2010: dhcp_failover_set_state (state, recover);
2011: else
2012: dhcp_failover_set_state (state,
2013: potential_conflict);
2014: break;
2015:
2016: case potential_conflict:
2017: case resolution_interrupted:
2018: case conflict_done:
2019: /* None of these transitions should ever occur. */
2020: log_error("Peer %s: Invalid state transition %s "
2021: "to %s.", state->name,
2022: dhcp_failover_state_name_print(previous_state),
2023: dhcp_failover_state_name_print(new_state));
2024: dhcp_failover_set_state (state, shut_down);
2025: break;
2026:
2027: case recover:
2028: case shut_down:
2029: dhcp_failover_set_state (state, partner_down);
2030: break;
2031:
2032: case paused:
2033: dhcp_failover_set_state (state,
2034: communications_interrupted);
2035: break;
2036:
2037: default:
2038: /* recover_wait, recover_done, unknown_state, startup,
2039: * communications_interrupted
2040: */
2041: break;
2042: }
2043: break;
2044:
2045: case recover:
2046: switch (new_state) {
2047: case recover:
2048: log_info ("failover peer %s: requesting %s",
2049: state -> name, "full update from peer");
2050: /* Don't send updreqall if we're really in the
2051: startup state, because that will result in two
2052: being sent. */
2053: if (state -> me.state == recover)
2054: dhcp_failover_send_update_request_all (state);
2055: break;
2056:
2057: case potential_conflict:
2058: case resolution_interrupted:
2059: case conflict_done:
2060: case normal:
2061: dhcp_failover_set_state (state, potential_conflict);
2062: break;
2063:
2064: case partner_down:
2065: case communications_interrupted:
2066: /* We're supposed to send an update request at this
2067: point. */
2068: /* XXX we don't currently have code here to do any
2069: XXX clever detection of when we should send an
2070: XXX UPDREQALL message rather than an UPDREQ
2071: XXX message. What to do, what to do? */
2072: /* Currently when we enter recover state, no matter
2073: * the reason, we send an UPDREQALL. So, it makes
2074: * the most sense to stick to that until something
2075: * better is done.
2076: * Furthermore, we only want to send the update
2077: * request if we are not in startup state.
2078: */
2079: if (state -> me.state == recover)
2080: dhcp_failover_send_update_request_all (state);
2081: break;
2082:
2083: case shut_down:
2084: /* XXX We're not explicitly told what to do in this
2085: XXX case, but this transition is consistent with
2086: XXX what is elsewhere in the draft. */
2087: dhcp_failover_set_state (state, partner_down);
2088: break;
2089:
2090: /* We can't really do anything in this case. */
2091: default:
2092: /* paused, recover_done, recover_wait, unknown_state,
2093: * startup.
2094: */
2095: break;
2096: }
2097: break;
2098:
2099: case potential_conflict:
2100: switch (new_state) {
2101: case normal:
2102: /* This is an illegal transition. */
2103: log_error("Peer %s moves to normal during conflict "
2104: "resolution - panic, shutting down.",
2105: state->name);
2106: dhcp_failover_set_state(state, shut_down);
2107: break;
2108:
2109: case conflict_done:
2110: if (previous_state == potential_conflict)
2111: dhcp_failover_send_update_request (state);
2112: else
2113: log_error("Peer %s: Unexpected move to "
2114: "conflict-done.", state->name);
2115: break;
2116:
2117: case recover_done:
2118: case recover_wait:
2119: case potential_conflict:
2120: case partner_down:
2121: case communications_interrupted:
2122: case resolution_interrupted:
2123: case paused:
2124: break;
2125:
2126: case recover:
2127: dhcp_failover_set_state (state, recover);
2128: break;
2129:
2130: case shut_down:
2131: dhcp_failover_set_state (state, partner_down);
2132: break;
2133:
2134: default:
2135: /* unknown_state, startup */
2136: break;
2137: }
2138: break;
2139:
2140: case conflict_done:
2141: switch (new_state) {
2142: case normal:
2143: case shut_down:
2144: dhcp_failover_set_state(state, new_state);
2145: break;
2146:
2147: default:
2148: log_fatal("Peer %s: Invalid attempt to move from %s "
2149: "to %s while local state is conflict-done.",
2150: state->name,
2151: dhcp_failover_state_name_print(previous_state),
2152: dhcp_failover_state_name_print(new_state));
2153: }
2154: break;
2155:
2156: case partner_down:
2157: /* Take no action if other server is starting up. */
2158: if (startupp)
2159: break;
2160:
2161: switch (new_state) {
2162: /* This is where we should be. */
2163: case recover:
2164: case recover_wait:
2165: break;
2166:
2167: case recover_done:
2168: dhcp_failover_set_state (state, normal);
2169: break;
2170:
2171: case normal:
2172: case potential_conflict:
2173: case partner_down:
2174: case communications_interrupted:
2175: case resolution_interrupted:
2176: case conflict_done:
2177: dhcp_failover_set_state (state, potential_conflict);
2178: break;
2179:
2180: default:
2181: /* shut_down, paused, unknown_state, startup */
2182: break;
2183: }
2184: break;
2185:
2186: case communications_interrupted:
2187: switch (new_state) {
2188: case paused:
2189: /* Stick with the status quo. */
2190: break;
2191:
2192: /* If we're in communications-interrupted and an
2193: amnesic peer connects, go to the partner_down
2194: state immediately. */
2195: case recover:
2196: dhcp_failover_set_state (state, partner_down);
2197: break;
2198:
2199: case normal:
2200: case communications_interrupted:
2201: case recover_done:
2202: case recover_wait:
2203: /* XXX so we don't need to do this specially in
2204: XXX the CONNECT and CONNECTACK handlers. */
2205: dhcp_failover_send_updates (state);
2206: dhcp_failover_set_state (state, normal);
2207: break;
2208:
2209: case potential_conflict:
2210: case partner_down:
2211: case resolution_interrupted:
2212: case conflict_done:
2213: dhcp_failover_set_state (state, potential_conflict);
2214: break;
2215:
2216: case shut_down:
2217: dhcp_failover_set_state (state, partner_down);
2218: break;
2219:
2220: default:
2221: /* unknown_state, startup */
2222: break;
2223: }
2224: break;
2225:
2226: case resolution_interrupted:
2227: switch (new_state) {
2228: case normal:
2229: case recover:
2230: case potential_conflict:
2231: case partner_down:
2232: case communications_interrupted:
2233: case resolution_interrupted:
2234: case conflict_done:
2235: case recover_done:
2236: case recover_wait:
2237: dhcp_failover_set_state (state, potential_conflict);
2238: break;
2239:
2240: case shut_down:
2241: dhcp_failover_set_state (state, partner_down);
2242: break;
2243:
2244: default:
2245: /* paused, unknown_state, startup */
2246: break;
2247: }
2248: break;
2249:
2250: /* Make no transitions while in recover_wait...just wait. */
2251: case recover_wait:
2252: break;
2253:
2254: case recover_done:
2255: switch (new_state) {
2256: case recover_done:
2257: log_error("Both servers have entered recover-done!");
2258: case normal:
2259: dhcp_failover_set_state (state, normal);
2260: break;
2261:
2262: case shut_down:
2263: dhcp_failover_set_state (state, partner_down);
2264: break;
2265:
2266: default:
2267: /* potential_conflict, partner_down,
2268: * communications_interrupted, resolution_interrupted,
2269: * paused, recover, recover_wait, unknown_state,
2270: * startup.
2271: */
2272: break;
2273: }
2274: break;
2275:
2276: /* We are essentially dead in the water when we're in
2277: either shut_down or paused states, and do not do any
2278: automatic state transitions. */
2279: case shut_down:
2280: case paused:
2281: break;
2282:
2283: /* XXX: Shouldn't this be a fatal condition? */
2284: case unknown_state:
2285: break;
2286:
2287: default:
2288: log_fatal("Impossible condition at %s:%d.", MDL);
2289: break;
2290:
2291: }
2292:
2293: /* If we didn't make a transition out of startup as a result of
2294: the peer's state change, do it now as a result of the fact that
2295: we got a state change from the peer. */
2296: if (state -> me.state == startup && state -> saved_state != startup)
2297: dhcp_failover_set_state (state, state -> saved_state);
2298:
2299: /* For now, just set the service state based on the peer's state
2300: if necessary. */
2301: dhcp_failover_set_service_state (state);
2302:
2303: return ISC_R_SUCCESS;
2304: }
2305:
2306: /*
2307: * Balance operation manual entry; startup, entrance to normal state. No
2308: * sense sending a POOLREQ at this stage; the peer is likely about to schedule
2309: * their own rebalance event upon entering normal themselves.
2310: */
2311: static void
2312: dhcp_failover_pool_balance(dhcp_failover_state_t *state)
2313: {
2314: /* Cancel pending event. */
2315: cancel_timeout(dhcp_failover_pool_rebalance, state);
2316: state->sched_balance = 0;
2317:
2318: dhcp_failover_pool_dobalance(state, NULL);
2319: }
2320:
2321: /*
2322: * Balance operation entry from timer event. Once per timer interval is
2323: * the only time we want to emit POOLREQs (asserting an interrupt in our
2324: * peer).
2325: */
2326: void
2327: dhcp_failover_pool_rebalance(void *failover_state)
2328: {
2329: dhcp_failover_state_t *state;
2330: isc_boolean_t sendreq = ISC_FALSE;
2331:
2332: state = (dhcp_failover_state_t *)failover_state;
2333:
2334: /* Clear scheduled event indicator. */
2335: state->sched_balance = 0;
2336:
2337: if (dhcp_failover_pool_dobalance(state, &sendreq))
2338: dhcp_failover_send_updates(state);
2339:
2340: if (sendreq)
2341: dhcp_failover_send_poolreq(state);
2342: }
2343:
2344: /*
2345: * Balance operation entry from POOLREQ protocol message. Do not permit a
2346: * POOLREQ to send back a POOLREQ. Ping pong.
2347: */
2348: static void
2349: dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state)
2350: {
2351: int queued;
2352:
2353: /* Cancel pending event. */
2354: cancel_timeout(dhcp_failover_pool_rebalance, state);
2355: state->sched_balance = 0;
2356:
2357: queued = dhcp_failover_pool_dobalance(state, NULL);
2358:
2359: dhcp_failover_send_poolresp(state, queued);
2360:
2361: if (queued)
2362: dhcp_failover_send_updates(state);
2363: else
2364: log_info("peer %s: Got POOLREQ, answering negatively! "
2365: "Peer may be out of leases or database inconsistent.",
2366: state->name);
2367: }
2368:
2369: /*
2370: * Do the meat of the work common to all forms of pool rebalance. If the
2371: * caller deems it appropriate to transmit POOLREQ messages, it can use the
2372: * sendreq pointer to pass in the address of a FALSE value which this function
2373: * will conditionally turn TRUE if a POOLREQ is determined to be necessary.
2374: * A NULL value may be passed, in which case no action is taken.
2375: */
2376: static int
2377: dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
2378: isc_boolean_t *sendreq)
2379: {
2380: int lts, total, thresh, hold, panic, pass;
2381: int leases_queued = 0;
2382: struct lease *lp = (struct lease *)0;
2383: struct lease *next = (struct lease *)0;
2384: struct shared_network *s;
2385: struct pool *p;
2386: binding_state_t peer_lease_state;
2387: binding_state_t my_lease_state;
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;
2411: my_lease_state = FTS_FREE;
2412: lq = &p->free;
2413: } else {
2414: lts = (p->backup_leases - p->free_leases) / 2;
2415: peer_lease_state = FTS_FREE;
2416: my_lease_state = FTS_BACKUP;
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: omapi_connection_object_t *conn;
3222: isc_result_t status;
3223:
3224: if (c -> type != omapi_type_connection)
3225: return ISC_R_INVALIDARG;
3226: conn = (omapi_connection_object_t *)c;
3227:
3228: if (h -> type != dhcp_type_failover_state)
3229: return ISC_R_INVALIDARG;
3230: s = (dhcp_failover_state_t *)h;
3231:
3232: status = omapi_connection_put_name (c, "name");
3233: if (status != ISC_R_SUCCESS)
3234: return status;
3235: status = omapi_connection_put_string (c, s -> name);
3236: if (status != ISC_R_SUCCESS)
3237: return status;
3238:
3239: status = omapi_connection_put_name (c, "partner-address");
3240: if (status != ISC_R_SUCCESS)
3241: return status;
3242: status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
3243: if (status != ISC_R_SUCCESS)
3244: return status;
3245: status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
3246: sizeof s -> partner.address);
3247: if (status != ISC_R_SUCCESS)
3248: return status;
3249:
3250: status = omapi_connection_put_name (c, "partner-port");
3251: if (status != ISC_R_SUCCESS)
3252: return status;
3253: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3254: if (status != ISC_R_SUCCESS)
3255: return status;
3256: status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
3257: if (status != ISC_R_SUCCESS)
3258: return status;
3259:
3260: status = omapi_connection_put_name (c, "local-address");
3261: if (status != ISC_R_SUCCESS)
3262: return status;
3263: status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
3264: if (status != ISC_R_SUCCESS)
3265: return status;
3266: status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
3267: sizeof s -> me.address);
3268: if (status != ISC_R_SUCCESS)
3269: return status;
3270:
3271: status = omapi_connection_put_name (c, "local-port");
3272: if (status != ISC_R_SUCCESS)
3273: return status;
3274: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3275: if (status != ISC_R_SUCCESS)
3276: return status;
3277: status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
3278: if (status != ISC_R_SUCCESS)
3279: return status;
3280:
3281: status = omapi_connection_put_name (c, "max-outstanding-updates");
3282: if (status != ISC_R_SUCCESS)
3283: return status;
3284: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3285: if (status != ISC_R_SUCCESS)
3286: return status;
3287: status = omapi_connection_put_uint32 (c,
3288: s -> me.max_flying_updates);
3289: if (status != ISC_R_SUCCESS)
3290: return status;
3291:
3292: status = omapi_connection_put_name (c, "mclt");
3293: if (status != ISC_R_SUCCESS)
3294: return status;
3295: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3296: if (status != ISC_R_SUCCESS)
3297: return status;
3298: status = omapi_connection_put_uint32 (c, s -> mclt);
3299: if (status != ISC_R_SUCCESS)
3300: return status;
3301:
3302: status = omapi_connection_put_name (c, "load-balance-max-secs");
3303: if (status != ISC_R_SUCCESS)
3304: return status;
3305: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3306: if (status != ISC_R_SUCCESS)
3307: return status;
3308: status = (omapi_connection_put_uint32
3309: (c, (u_int32_t)s -> load_balance_max_secs));
3310: if (status != ISC_R_SUCCESS)
3311: return status;
3312:
3313:
3314: if (s -> hba) {
3315: status = omapi_connection_put_name (c, "load-balance-hba");
3316: if (status != ISC_R_SUCCESS)
3317: return status;
3318: status = omapi_connection_put_uint32 (c, 32);
3319: if (status != ISC_R_SUCCESS)
3320: return status;
3321: status = omapi_connection_copyin (c, s -> hba, 32);
3322: if (status != ISC_R_SUCCESS)
3323: return status;
3324: }
3325:
3326: status = omapi_connection_put_name (c, "partner-state");
3327: if (status != ISC_R_SUCCESS)
3328: return status;
3329: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3330: if (status != ISC_R_SUCCESS)
3331: return status;
3332: status = omapi_connection_put_uint32 (c, s -> partner.state);
3333: if (status != ISC_R_SUCCESS)
3334: return status;
3335:
3336: status = omapi_connection_put_name (c, "local-state");
3337: if (status != ISC_R_SUCCESS)
3338: return status;
3339: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3340: if (status != ISC_R_SUCCESS)
3341: return status;
3342: status = omapi_connection_put_uint32 (c, s -> me.state);
3343: if (status != ISC_R_SUCCESS)
3344: return status;
3345:
3346: status = omapi_connection_put_name (c, "partner-stos");
3347: if (status != ISC_R_SUCCESS)
3348: return status;
3349: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3350: if (status != ISC_R_SUCCESS)
3351: return status;
3352: status = omapi_connection_put_uint32 (c,
3353: (u_int32_t)s -> partner.stos);
3354: if (status != ISC_R_SUCCESS)
3355: return status;
3356:
3357: status = omapi_connection_put_name (c, "local-stos");
3358: if (status != ISC_R_SUCCESS)
3359: return status;
3360: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3361: if (status != ISC_R_SUCCESS)
3362: return status;
3363: status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
3364: if (status != ISC_R_SUCCESS)
3365: return status;
3366:
3367: status = omapi_connection_put_name (c, "hierarchy");
3368: if (status != ISC_R_SUCCESS)
3369: return status;
3370: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3371: if (status != ISC_R_SUCCESS)
3372: return status;
3373: status = omapi_connection_put_uint32 (c, s -> i_am);
3374: if (status != ISC_R_SUCCESS)
3375: return status;
3376:
3377: status = omapi_connection_put_name (c, "last-packet-sent");
3378: if (status != ISC_R_SUCCESS)
3379: return status;
3380: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3381: if (status != ISC_R_SUCCESS)
3382: return status;
3383: status = (omapi_connection_put_uint32
3384: (c, (u_int32_t)s -> last_packet_sent));
3385: if (status != ISC_R_SUCCESS)
3386: return status;
3387:
3388: status = omapi_connection_put_name (c, "last-timestamp-received");
3389: if (status != ISC_R_SUCCESS)
3390: return status;
3391: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3392: if (status != ISC_R_SUCCESS)
3393: return status;
3394: status = (omapi_connection_put_uint32
3395: (c, (u_int32_t)s -> last_timestamp_received));
3396: if (status != ISC_R_SUCCESS)
3397: return status;
3398:
3399: status = omapi_connection_put_name (c, "skew");
3400: if (status != ISC_R_SUCCESS)
3401: return status;
3402: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3403: if (status != ISC_R_SUCCESS)
3404: return status;
3405: status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
3406: if (status != ISC_R_SUCCESS)
3407: return status;
3408:
3409: status = omapi_connection_put_name (c, "max-response-delay");
3410: if (status != ISC_R_SUCCESS)
3411: return status;
3412: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3413: if (status != ISC_R_SUCCESS)
3414: return status;
3415: status = (omapi_connection_put_uint32
3416: (c, (u_int32_t)s -> me.max_response_delay));
3417: if (status != ISC_R_SUCCESS)
3418: return status;
3419:
3420: status = omapi_connection_put_name (c, "cur-unacked-updates");
3421: if (status != ISC_R_SUCCESS)
3422: return status;
3423: status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
3424: if (status != ISC_R_SUCCESS)
3425: return status;
3426: status = (omapi_connection_put_uint32
3427: (c, (u_int32_t)s -> cur_unacked_updates));
3428: if (status != ISC_R_SUCCESS)
3429: return status;
3430:
3431: if (h -> inner && h -> inner -> type -> stuff_values)
3432: return (*(h -> inner -> type -> stuff_values)) (c, id,
3433: h -> inner);
3434: return ISC_R_SUCCESS;
3435: }
3436:
3437: isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
3438: omapi_object_t *id,
3439: omapi_object_t *ref)
3440: {
3441: omapi_value_t *tv = (omapi_value_t *)0;
3442: isc_result_t status;
3443: dhcp_failover_state_t *s;
3444:
3445: if (!ref)
3446: return ISC_R_NOKEYS;
3447:
3448: /* First see if we were sent a handle. */
3449: status = omapi_get_value_str (ref, id, "handle", &tv);
3450: if (status == ISC_R_SUCCESS) {
3451: status = omapi_handle_td_lookup (sp, tv -> value);
3452:
3453: omapi_value_dereference (&tv, MDL);
3454: if (status != ISC_R_SUCCESS)
3455: return status;
3456:
3457: /* Don't return the object if the type is wrong. */
3458: if ((*sp) -> type != dhcp_type_failover_state) {
3459: omapi_object_dereference (sp, MDL);
3460: return ISC_R_INVALIDARG;
3461: }
3462: }
3463:
3464: /* Look the failover state up by peer name. */
3465: status = omapi_get_value_str (ref, id, "name", &tv);
3466: if (status == ISC_R_SUCCESS) {
3467: for (s = failover_states; s; s = s -> next) {
3468: unsigned l = strlen (s -> name);
3469: if (l == tv -> value -> u.buffer.len &&
3470: !memcmp (s -> name,
3471: tv -> value -> u.buffer.value, l))
3472: break;
3473: }
3474: omapi_value_dereference (&tv, MDL);
3475:
3476: /* If we already have a lease, and it's not the same one,
3477: then the query was invalid. */
3478: if (*sp && *sp != (omapi_object_t *)s) {
3479: omapi_object_dereference (sp, MDL);
3480: return ISC_R_KEYCONFLICT;
3481: } else if (!s) {
3482: if (*sp)
3483: omapi_object_dereference (sp, MDL);
3484: return ISC_R_NOTFOUND;
3485: } else if (!*sp)
3486: /* XXX fix so that hash lookup itself creates
3487: XXX the reference. */
3488: omapi_object_reference (sp, (omapi_object_t *)s, MDL);
3489: }
3490:
3491: /* If we get to here without finding a lease, no valid key was
3492: specified. */
3493: if (!*sp)
3494: return ISC_R_NOKEYS;
3495: return ISC_R_SUCCESS;
3496: }
3497:
3498: isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
3499: omapi_object_t *id)
3500: {
3501: return ISC_R_NOTIMPLEMENTED;
3502: }
3503:
3504: isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
3505: omapi_object_t *id)
3506: {
3507: return ISC_R_NOTIMPLEMENTED;
3508: }
3509:
3510: int dhcp_failover_state_match (dhcp_failover_state_t *state,
3511: u_int8_t *addr, unsigned addrlen)
3512: {
3513: struct data_string ds;
3514: int i;
3515:
3516: memset (&ds, 0, sizeof ds);
3517: if (evaluate_option_cache (&ds, (struct packet *)0,
3518: (struct lease *)0,
3519: (struct client_state *)0,
3520: (struct option_state *)0,
3521: (struct option_state *)0,
3522: &global_scope,
3523: state -> partner.address, MDL)) {
3524: for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
3525: if (!memcmp (&ds.data [i],
3526: addr, addrlen)) {
3527: data_string_forget (&ds, MDL);
3528: return 1;
3529: }
3530: }
3531: data_string_forget (&ds, MDL);
3532: }
3533: return 0;
3534: }
3535:
3536: int
3537: dhcp_failover_state_match_by_name(state, name)
3538: dhcp_failover_state_t *state;
3539: failover_option_t *name;
3540: {
3541: if ((strlen(state->name) == name->count) &&
3542: (memcmp(state->name, name->data, name->count) == 0))
3543: return 1;
3544:
3545: return 0;
3546: }
3547:
3548: const char *dhcp_failover_reject_reason_print (int reason)
3549: {
3550: static char resbuf[sizeof("Undefined-255: This reason code is not defined "
3551: "in the protocol standard.")];
3552:
3553: if ((reason > 0xff) || (reason < 0))
3554: return "Reason code out of range.";
3555:
3556: switch (reason) {
3557: case FTR_ILLEGAL_IP_ADDR:
3558: return "Illegal IP address (not part of any address pool).";
3559:
3560: case FTR_FATAL_CONFLICT:
3561: return "Fatal conflict exists: address in use by other client.";
3562:
3563: case FTR_MISSING_BINDINFO:
3564: return "Missing binding information.";
3565:
3566: case FTR_TIMEMISMATCH:
3567: return "Connection rejected, time mismatch too great.";
3568:
3569: case FTR_INVALID_MCLT:
3570: return "Connection rejected, invalid MCLT.";
3571:
3572: case FTR_MISC_REJECT:
3573: return "Connection rejected, unknown reason.";
3574:
3575: case FTR_DUP_CONNECTION:
3576: return "Connection rejected, duplicate connection.";
3577:
3578: case FTR_INVALID_PARTNER:
3579: return "Connection rejected, invalid failover partner.";
3580:
3581: case FTR_TLS_UNSUPPORTED:
3582: return "TLS not supported.";
3583:
3584: case FTR_TLS_UNCONFIGURED:
3585: return "TLS supported but not configured.";
3586:
3587: case FTR_TLS_REQUIRED:
3588: return "TLS required but not supported by partner.";
3589:
3590: case FTR_DIGEST_UNSUPPORTED:
3591: return "Message digest not supported.";
3592:
3593: case FTR_DIGEST_UNCONFIGURED:
3594: return "Message digest not configured.";
3595:
3596: case FTR_VERSION_MISMATCH:
3597: return "Protocol version mismatch.";
3598:
3599: case FTR_OUTDATED_BIND_INFO:
3600: return "Outdated binding information.";
3601:
3602: case FTR_LESS_CRIT_BIND_INFO:
3603: return "Less critical binding information.";
3604:
3605: case FTR_NO_TRAFFIC:
3606: return "No traffic within sufficient time.";
3607:
3608: case FTR_HBA_CONFLICT:
3609: return "Hash bucket assignment conflict.";
3610:
3611: case FTR_IP_NOT_RESERVED:
3612: return "IP not reserved on this server.";
3613:
3614: case FTR_IP_DIGEST_FAILURE:
3615: return "Message digest failed to compare.";
3616:
3617: case FTR_IP_MISSING_DIGEST:
3618: return "Missing message digest.";
3619:
3620: case FTR_UNKNOWN:
3621: return "Unknown Error.";
3622:
3623: default:
3624: sprintf(resbuf, "Undefined-%d: This reason code is not defined in the "
3625: "protocol standard.", reason);
3626: return resbuf;
3627: }
3628: }
3629:
3630: const char *dhcp_failover_state_name_print (enum failover_state state)
3631: {
3632: switch (state) {
3633: default:
3634: case unknown_state:
3635: return "unknown-state";
3636:
3637: case partner_down:
3638: return "partner-down";
3639:
3640: case normal:
3641: return "normal";
3642:
3643: case conflict_done:
3644: return "conflict-done";
3645:
3646: case communications_interrupted:
3647: return "communications-interrupted";
3648:
3649: case resolution_interrupted:
3650: return "resolution-interrupted";
3651:
3652: case potential_conflict:
3653: return "potential-conflict";
3654:
3655: case recover:
3656: return "recover";
3657:
3658: case recover_done:
3659: return "recover-done";
3660:
3661: case recover_wait:
3662: return "recover-wait";
3663:
3664: case shut_down:
3665: return "shutdown";
3666:
3667: case paused:
3668: return "paused";
3669:
3670: case startup:
3671: return "startup";
3672: }
3673: }
3674:
3675: const char *dhcp_failover_message_name (unsigned type)
3676: {
3677: static char messbuf[sizeof("unknown-message-255")];
3678:
3679: if (type > 0xff)
3680: return "invalid-message";
3681:
3682: switch (type) {
3683: case FTM_POOLREQ:
3684: return "pool-request";
3685:
3686: case FTM_POOLRESP:
3687: return "pool-response";
3688:
3689: case FTM_BNDUPD:
3690: return "bind-update";
3691:
3692: case FTM_BNDACK:
3693: return "bind-ack";
3694:
3695: case FTM_CONNECT:
3696: return "connect";
3697:
3698: case FTM_CONNECTACK:
3699: return "connect-ack";
3700:
3701: case FTM_UPDREQ:
3702: return "update-request";
3703:
3704: case FTM_UPDDONE:
3705: return "update-done";
3706:
3707: case FTM_UPDREQALL:
3708: return "update-request-all";
3709:
3710: case FTM_STATE:
3711: return "state";
3712:
3713: case FTM_CONTACT:
3714: return "contact";
3715:
3716: case FTM_DISCONNECT:
3717: return "disconnect";
3718:
3719: default:
3720: sprintf(messbuf, "unknown-message-%u", type);
3721: return messbuf;
3722: }
3723: }
3724:
3725: const char *dhcp_failover_option_name (unsigned type)
3726: {
3727: static char optbuf[sizeof("unknown-option-65535")];
3728:
3729: if (type > 0xffff)
3730: return "invalid-option";
3731:
3732: switch (type) {
3733: case FTO_ADDRESSES_TRANSFERRED:
3734: return "addresses-transferred";
3735:
3736: case FTO_ASSIGNED_IP_ADDRESS:
3737: return "assigned-ip-address";
3738:
3739: case FTO_BINDING_STATUS:
3740: return "binding-status";
3741:
3742: case FTO_CLIENT_IDENTIFIER:
3743: return "client-identifier";
3744:
3745: case FTO_CHADDR:
3746: return "chaddr";
3747:
3748: case FTO_CLTT:
3749: return "cltt";
3750:
3751: case FTO_DDNS:
3752: return "ddns";
3753:
3754: case FTO_DELAYED_SERVICE:
3755: return "delayed-service";
3756:
3757: case FTO_HBA:
3758: return "hba";
3759:
3760: case FTO_IP_FLAGS:
3761: return "ip-flags";
3762:
3763: case FTO_LEASE_EXPIRY:
3764: return "lease-expiry";
3765:
3766: case FTO_MAX_UNACKED:
3767: return "max-unacked";
3768:
3769: case FTO_MCLT:
3770: return "mclt";
3771:
3772: case FTO_MESSAGE:
3773: return "message";
3774:
3775: case FTO_MESSAGE_DIGEST:
3776: return "message-digest";
3777:
3778: case FTO_POTENTIAL_EXPIRY:
3779: return "potential-expiry";
3780:
3781: case FTO_PROTOCOL_VERSION:
3782: return "protocol-version";
3783:
3784: case FTO_RECEIVE_TIMER:
3785: return "receive-timer";
3786:
3787: case FTO_REJECT_REASON:
3788: return "reject-reason";
3789:
3790: case FTO_RELATIONSHIP_NAME:
3791: return "relationship-name";
3792:
3793: case FTO_REPLY_OPTIONS:
3794: return "reply-options";
3795:
3796: case FTO_REQUEST_OPTIONS:
3797: return "request-options";
3798:
3799: case FTO_SERVER_FLAGS:
3800: return "server-flags";
3801:
3802: case FTO_SERVER_STATE:
3803: return "server-state";
3804:
3805: case FTO_STOS:
3806: return "stos";
3807:
3808: case FTO_TLS_REPLY:
3809: return "tls-reply";
3810:
3811: case FTO_TLS_REQUEST:
3812: return "tls-request";
3813:
3814: case FTO_VENDOR_CLASS:
3815: return "vendor-class";
3816:
3817: case FTO_VENDOR_OPTIONS:
3818: return "vendor-options";
3819:
3820: default:
3821: sprintf(optbuf, "unknown-option-%u", type);
3822: return optbuf;
3823: }
3824: }
3825:
3826: failover_option_t *dhcp_failover_option_printf (unsigned code,
3827: char *obuf,
3828: unsigned *obufix,
3829: unsigned obufmax,
3830: const char *fmt, ...)
3831: {
3832: va_list va;
3833: char tbuf [256];
3834:
3835: /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3836: * It is unclear what the effects of truncation here are, or
3837: * how that condition should be handled. It seems that this
3838: * function is used for formatting messages in the failover
3839: * command channel. For now the safest thing is for
3840: * overflow-truncation to cause a fatal log.
3841: */
3842: va_start (va, fmt);
3843: if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
3844: log_fatal ("%s: vsnprintf would truncate",
3845: "dhcp_failover_make_option");
3846: va_end (va);
3847:
3848: return dhcp_failover_make_option (code, obuf, obufix, obufmax,
3849: strlen (tbuf), tbuf);
3850: }
3851:
3852: failover_option_t *dhcp_failover_make_option (unsigned code,
3853: char *obuf, unsigned *obufix,
3854: unsigned obufmax, ...)
3855: {
3856: va_list va;
3857: struct failover_option_info *info;
3858: int i;
3859: unsigned size, count;
3860: unsigned val;
3861: u_int8_t *iaddr;
3862: unsigned ilen = 0;
3863: u_int8_t *bval;
3864: char *txt = NULL;
3865: #if defined (DEBUG_FAILOVER_MESSAGES)
3866: char tbuf [256];
3867: #endif
3868:
3869: /* Note that the failover_option structure is used differently on
3870: input than on output - on input, count is an element count, and
3871: on output it's the number of bytes total in the option, including
3872: the option code and option length. */
3873: failover_option_t option, *op;
3874:
3875:
3876: /* Bogus option code? */
3877: if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
3878: return &null_failover_option;
3879: }
3880: info = &ft_options [code];
3881:
3882: va_start (va, obufmax);
3883:
3884: /* Get the number of elements and the size of the buffer we need
3885: to allocate. */
3886: if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
3887: count = info -> type == FT_DDNS ? 1 : 2;
3888: size = va_arg (va, int) + count;
3889: } else {
3890: /* Find out how many items in this list. */
3891: if (info -> num_present)
3892: count = info -> num_present;
3893: else
3894: count = va_arg (va, int);
3895:
3896: /* Figure out size. */
3897: switch (info -> type) {
3898: case FT_UINT8:
3899: case FT_BYTES:
3900: case FT_DIGEST:
3901: size = count;
3902: break;
3903:
3904: case FT_TEXT_OR_BYTES:
3905: case FT_TEXT:
3906: txt = va_arg (va, char *);
3907: size = count;
3908: break;
3909:
3910: case FT_IPADDR:
3911: ilen = va_arg (va, unsigned);
3912: size = count * ilen;
3913: break;
3914:
3915: case FT_UINT32:
3916: size = count * 4;
3917: break;
3918:
3919: case FT_UINT16:
3920: size = count * 2;
3921: break;
3922:
3923: default:
3924: /* shouldn't get here. */
3925: log_fatal ("bogus type in failover_make_option: %d",
3926: info -> type);
3927: return &null_failover_option;
3928: }
3929: }
3930:
3931: size += 4;
3932:
3933: /* Allocate a buffer for the option. */
3934: option.count = size;
3935: option.data = dmalloc (option.count, MDL);
3936: if (!option.data) {
3937: va_end (va);
3938: return &null_failover_option;
3939: }
3940:
3941: /* Put in the option code and option length. */
3942: putUShort (option.data, code);
3943: putUShort (&option.data [2], size - 4);
3944:
3945: #if defined (DEBUG_FAILOVER_MESSAGES)
3946: /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
3947: * It is unclear what the effects of truncation here are, or
3948: * how that condition should be handled. It seems that this
3949: * message may be sent over the failover command channel.
3950: * For now the safest thing is for overflow-truncation to cause
3951: * a fatal log.
3952: */
3953: if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
3954: option.count) >= sizeof tbuf)
3955: log_fatal ("dhcp_failover_make_option: tbuf overflow");
3956: failover_print (obuf, obufix, obufmax, tbuf);
3957: #endif
3958:
3959: /* Now put in the data. */
3960: switch (info -> type) {
3961: case FT_UINT8:
3962: for (i = 0; i < count; i++) {
3963: val = va_arg (va, unsigned);
3964: #if defined (DEBUG_FAILOVER_MESSAGES)
3965: /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
3966: sprintf (tbuf, " %d", val);
3967: failover_print (obuf, obufix, obufmax, tbuf);
3968: #endif
3969: option.data [i + 4] = val;
3970: }
3971: break;
3972:
3973: case FT_IPADDR:
3974: for (i = 0; i < count; i++) {
3975: iaddr = va_arg (va, u_int8_t *);
3976: if (ilen != 4) {
3977: dfree (option.data, MDL);
3978: log_error ("IP addrlen=%d, should be 4.",
3979: ilen);
3980: va_end (va);
3981: return &null_failover_option;
3982: }
3983:
3984: #if defined (DEBUG_FAILOVER_MESSAGES)
3985: /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
3986: sprintf (tbuf, " %u.%u.%u.%u",
3987: iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
3988: failover_print (obuf, obufix, obufmax, tbuf);
3989: #endif
3990: memcpy (&option.data [4 + i * ilen], iaddr, ilen);
3991: }
3992: break;
3993:
3994: case FT_UINT32:
3995: for (i = 0; i < count; i++) {
3996: val = va_arg (va, unsigned);
3997: #if defined (DEBUG_FAILOVER_MESSAGES)
3998: /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
3999: sprintf (tbuf, " %d", val);
4000: failover_print (obuf, obufix, obufmax, tbuf);
4001: #endif
4002: putULong (&option.data [4 + i * 4], val);
4003: }
4004: break;
4005:
4006: case FT_BYTES:
4007: case FT_DIGEST:
4008: bval = va_arg (va, u_int8_t *);
4009: #if defined (DEBUG_FAILOVER_MESSAGES)
4010: for (i = 0; i < count; i++) {
4011: /* 23 bytes plus nul, safe. */
4012: sprintf (tbuf, " %d", bval [i]);
4013: failover_print (obuf, obufix, obufmax, tbuf);
4014: }
4015: #endif
4016: memcpy (&option.data [4], bval, count);
4017: break;
4018:
4019: /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
4020: terminated. Note that the caller should be careful not
4021: to provide a format and data that amount to more than 256
4022: bytes of data, since it will cause a fatal error. */
4023: case FT_TEXT_OR_BYTES:
4024: case FT_TEXT:
4025: #if defined (DEBUG_FAILOVER_MESSAGES)
4026: /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
4027: * It is unclear what the effects of truncation here are, or
4028: * how that condition should be handled. It seems that this
4029: * function is used for formatting messages in the failover
4030: * command channel. For now the safest thing is for
4031: * overflow-truncation to cause a fatal log.
4032: */
4033: if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
4034: log_fatal ("dhcp_failover_make_option: tbuf overflow");
4035: failover_print (obuf, obufix, obufmax, tbuf);
4036: #endif
4037: memcpy (&option.data [4], txt, count);
4038: break;
4039:
4040: case FT_DDNS:
4041: case FT_DDNS1:
4042: option.data [4] = va_arg (va, unsigned);
4043: if (count == 2)
4044: option.data [5] = va_arg (va, unsigned);
4045: bval = va_arg (va, u_int8_t *);
4046: memcpy (&option.data [4 + count], bval, size - count - 4);
4047: #if defined (DEBUG_FAILOVER_MESSAGES)
4048: for (i = 4; i < size; i++) {
4049: /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4050: sprintf (tbuf, " %d", option.data [i]);
4051: failover_print (obuf, obufix, obufmax, tbuf);
4052: }
4053: #endif
4054: break;
4055:
4056: case FT_UINT16:
4057: for (i = 0; i < count; i++) {
4058: val = va_arg (va, u_int32_t);
4059: #if defined (DEBUG_FAILOVER_MESSAGES)
4060: /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
4061: sprintf (tbuf, " %d", val);
4062: failover_print (obuf, obufix, obufmax, tbuf);
4063: #endif
4064: putUShort (&option.data [4 + i * 2], val);
4065: }
4066: break;
4067:
4068: case FT_UNDEF:
4069: default:
4070: break;
4071: }
4072:
4073: #if defined DEBUG_FAILOVER_MESSAGES
4074: failover_print (obuf, obufix, obufmax, ")");
4075: #endif
4076: va_end (va);
4077:
4078: /* Now allocate a place to store what we just set up. */
4079: op = dmalloc (sizeof (failover_option_t), MDL);
4080: if (!op) {
4081: dfree (option.data, MDL);
4082: return &null_failover_option;
4083: }
4084:
4085: *op = option;
4086: return op;
4087: }
4088:
4089: /* Send a failover message header. */
4090:
4091: isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
4092: omapi_object_t *connection,
4093: int msg_type, u_int32_t xid, ...)
4094: {
4095: unsigned size = 0;
4096: int bad_option = 0;
4097: int opix = 0;
4098: va_list list;
4099: failover_option_t *option;
4100: unsigned char *opbuf;
4101: isc_result_t status = ISC_R_SUCCESS;
4102: unsigned char cbuf;
4103: struct timeval tv;
4104:
4105: /* Run through the argument list once to compute the length of
4106: the option portion of the message. */
4107: va_start (list, xid);
4108: while ((option = va_arg (list, failover_option_t *))) {
4109: if (option != &skip_failover_option)
4110: size += option -> count;
4111: if (option == &null_failover_option)
4112: bad_option = 1;
4113: }
4114: va_end (list);
4115:
4116: /* Allocate an option buffer, unless we got an error. */
4117: if (!bad_option && size) {
4118: opbuf = dmalloc (size, MDL);
4119: if (!opbuf)
4120: status = ISC_R_NOMEMORY;
4121: } else
4122: opbuf = (unsigned char *)0;
4123:
4124: va_start (list, xid);
4125: while ((option = va_arg (list, failover_option_t *))) {
4126: if (option == &skip_failover_option)
4127: continue;
4128: if (!bad_option && opbuf)
4129: memcpy (&opbuf [opix],
4130: option -> data, option -> count);
4131: if (option != &null_failover_option &&
4132: option != &skip_failover_option) {
4133: opix += option -> count;
4134: dfree (option -> data, MDL);
4135: dfree (option, MDL);
4136: }
4137: }
4138: va_end(list);
4139:
4140: if (bad_option)
4141: return ISC_R_INVALIDARG;
4142:
4143: /* Now send the message header. */
4144:
4145: /* Message length. */
4146: status = omapi_connection_put_uint16 (connection, size + 12);
4147: if (status != ISC_R_SUCCESS)
4148: goto err;
4149:
4150: /* Message type. */
4151: cbuf = msg_type;
4152: status = omapi_connection_copyin (connection, &cbuf, 1);
4153: if (status != ISC_R_SUCCESS)
4154: goto err;
4155:
4156: /* Payload offset. */
4157: cbuf = 12;
4158: status = omapi_connection_copyin (connection, &cbuf, 1);
4159: if (status != ISC_R_SUCCESS)
4160: goto err;
4161:
4162: /* Current time. */
4163: status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
4164: if (status != ISC_R_SUCCESS)
4165: goto err;
4166:
4167: /* Transaction ID. */
4168: status = omapi_connection_put_uint32(connection, xid);
4169: if (status != ISC_R_SUCCESS)
4170: goto err;
4171:
4172: /* Payload. */
4173: if (opbuf) {
4174: status = omapi_connection_copyin (connection, opbuf, size);
4175: if (status != ISC_R_SUCCESS)
4176: goto err;
4177: dfree (opbuf, MDL);
4178: }
4179: if (link -> state_object &&
4180: link -> state_object -> link_to_peer == link) {
4181: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
4182: log_info ("add_timeout +%d %s",
4183: (int)(link -> state_object ->
4184: partner.max_response_delay) / 3,
4185: "dhcp_failover_send_contact");
4186: #endif
4187: tv . tv_sec = cur_time +
4188: (int)(link -> state_object ->
4189: partner.max_response_delay) / 3;
4190: tv . tv_usec = 0;
4191: add_timeout (&tv,
4192: dhcp_failover_send_contact, link -> state_object,
4193: (tvref_t)dhcp_failover_state_reference,
4194: (tvunref_t)dhcp_failover_state_dereference);
4195: }
4196: return status;
4197:
4198: err:
4199: if (opbuf)
4200: dfree (opbuf, MDL);
4201: log_info ("dhcp_failover_put_message: something went wrong.");
4202: omapi_disconnect (connection, 1);
4203: return status;
4204: }
4205:
4206: void dhcp_failover_timeout (void *vstate)
4207: {
4208: dhcp_failover_state_t *state = vstate;
4209: dhcp_failover_link_t *link;
4210:
4211: #if defined (DEBUG_FAILOVER_TIMING)
4212: log_info ("dhcp_failover_timeout");
4213: #endif
4214:
4215: if (!state || state -> type != dhcp_type_failover_state)
4216: return;
4217: link = state -> link_to_peer;
4218: if (!link ||
4219: !link -> outer ||
4220: link -> outer -> type != omapi_type_connection)
4221: return;
4222:
4223: log_error ("timeout waiting for failover peer %s", state -> name);
4224:
4225: /* If we haven't gotten a timely response, blow away the connection.
4226: This will cause the state to change automatically. */
4227: omapi_disconnect (link -> outer, 1);
4228: }
4229:
4230: void dhcp_failover_send_contact (void *vstate)
4231: {
4232: dhcp_failover_state_t *state = vstate;
4233: dhcp_failover_link_t *link;
4234: isc_result_t status;
4235:
4236: #if defined(DEBUG_FAILOVER_MESSAGES) && \
4237: defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
4238: char obuf [64];
4239: unsigned obufix = 0;
4240:
4241: failover_print(obuf, &obufix, sizeof(obuf), "(contact");
4242: #endif
4243:
4244: #if defined (DEBUG_FAILOVER_CONTACT_TIMING)
4245: log_info ("dhcp_failover_send_contact");
4246: #endif
4247:
4248: if (!state || state -> type != dhcp_type_failover_state)
4249: return;
4250: link = state -> link_to_peer;
4251: if (!link ||
4252: !link -> outer ||
4253: link -> outer -> type != omapi_type_connection)
4254: return;
4255:
4256: status = (dhcp_failover_put_message
4257: (link, link -> outer,
4258: FTM_CONTACT, link->xid++,
4259: (failover_option_t *)0));
4260:
4261: #if defined(DEBUG_FAILOVER_MESSAGES) && \
4262: defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
4263: if (status != ISC_R_SUCCESS)
4264: failover_print(obuf, &obufix, sizeof(obuf), " (failed)");
4265: failover_print(obuf, &obufix, sizeof(obuf), ")");
4266: if (obufix) {
4267: log_debug ("%s", obuf);
4268: }
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: }
4317: #endif
4318: return ISC_R_SUCCESS;
4319: }
4320:
4321: /* Send a connect message. */
4322:
4323: isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
4324: {
4325: dhcp_failover_link_t *link;
4326: dhcp_failover_state_t *state;
4327: isc_result_t status;
4328: #if defined (DEBUG_FAILOVER_MESSAGES)
4329: char obuf [64];
4330: unsigned obufix = 0;
4331:
4332: # define FMA obuf, &obufix, sizeof obuf
4333: failover_print (FMA, "(connect");
4334: #else
4335: # define FMA (char *)0, (unsigned *)0, 0
4336: #endif
4337:
4338: if (!l || l -> type != dhcp_type_failover_link)
4339: return ISC_R_INVALIDARG;
4340: link = (dhcp_failover_link_t *)l;
4341: state = link -> state_object;
4342: if (!l -> outer || l -> outer -> type != omapi_type_connection)
4343: return ISC_R_INVALIDARG;
4344:
4345: status =
4346: (dhcp_failover_put_message
4347: (link, l -> outer,
4348: FTM_CONNECT, link->xid++,
4349: dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
4350: strlen(state->name), state->name),
4351: dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4352: state -> me.max_flying_updates),
4353: dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4354: state -> me.max_response_delay),
4355: dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
4356: "isc-%s", PACKAGE_VERSION),
4357: dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4358: DHCP_FAILOVER_VERSION),
4359: dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
4360: 0, 0),
4361: dhcp_failover_make_option (FTO_MCLT, FMA,
4362: state -> mclt),
4363: (state -> hba
4364: ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
4365: : &skip_failover_option),
4366: (failover_option_t *)0));
4367:
4368: #if defined (DEBUG_FAILOVER_MESSAGES)
4369: if (status != ISC_R_SUCCESS)
4370: failover_print (FMA, " (failed)");
4371: failover_print (FMA, ")");
4372: if (obufix) {
4373: log_debug ("%s", obuf);
4374: }
4375: #endif
4376: return status;
4377: }
4378:
4379: isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
4380: dhcp_failover_state_t *state,
4381: int reason, const char *errmsg)
4382: {
4383: dhcp_failover_link_t *link;
4384: isc_result_t status;
4385: #if defined (DEBUG_FAILOVER_MESSAGES)
4386: char obuf [64];
4387: unsigned obufix = 0;
4388:
4389: # define FMA obuf, &obufix, sizeof obuf
4390: failover_print (FMA, "(connectack");
4391: #else
4392: # define FMA (char *)0, (unsigned *)0, 0
4393: #endif
4394:
4395: if (!l || l -> type != dhcp_type_failover_link)
4396: return ISC_R_INVALIDARG;
4397: link = (dhcp_failover_link_t *)l;
4398: if (!l -> outer || l -> outer -> type != omapi_type_connection)
4399: return ISC_R_INVALIDARG;
4400:
4401: status =
4402: (dhcp_failover_put_message
4403: (link, l -> outer,
4404: FTM_CONNECTACK, link->imsg->xid,
4405: state
4406: ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
4407: strlen(state->name), state->name)
4408: : (link->imsg->options_present & FTB_RELATIONSHIP_NAME)
4409: ? &link->imsg->relationship_name
4410: : &skip_failover_option,
4411: state
4412: ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
4413: state -> me.max_flying_updates)
4414: : &skip_failover_option,
4415: state
4416: ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
4417: state -> me.max_response_delay)
4418: : &skip_failover_option,
4419: dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
4420: "isc-%s", PACKAGE_VERSION),
4421: dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
4422: DHCP_FAILOVER_VERSION),
4423: (link->imsg->options_present & FTB_TLS_REQUEST)
4424: ? dhcp_failover_make_option(FTO_TLS_REPLY, FMA,
4425: 0, 0)
4426: : &skip_failover_option,
4427: reason
4428: ? dhcp_failover_make_option (FTO_REJECT_REASON,
4429: FMA, reason)
4430: : &skip_failover_option,
4431: (reason && errmsg)
4432: ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
4433: strlen (errmsg), errmsg)
4434: : &skip_failover_option,
4435: (failover_option_t *)0));
4436:
4437: #if defined (DEBUG_FAILOVER_MESSAGES)
4438: if (status != ISC_R_SUCCESS)
4439: failover_print (FMA, " (failed)");
4440: failover_print (FMA, ")");
4441: if (obufix) {
4442: log_debug ("%s", obuf);
4443: }
4444: #endif
4445: return status;
4446: }
4447:
4448: isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
4449: int reason,
4450: const char *message)
4451: {
4452: dhcp_failover_link_t *link;
4453: dhcp_failover_state_t *state;
4454: isc_result_t status;
4455: #if defined (DEBUG_FAILOVER_MESSAGES)
4456: char obuf [64];
4457: unsigned obufix = 0;
4458:
4459: # define FMA obuf, &obufix, sizeof obuf
4460: failover_print (FMA, "(disconnect");
4461: #else
4462: # define FMA (char *)0, (unsigned *)0, 0
4463: #endif
4464:
4465: if (!l || l -> type != dhcp_type_failover_link)
4466: return ISC_R_INVALIDARG;
4467: link = (dhcp_failover_link_t *)l;
4468: state = link -> state_object;
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: {
4973: struct lease *lt, *lease;
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;
4979: int required_options;
4980: isc_boolean_t chaddr_changed = ISC_FALSE;
4981: isc_boolean_t ident_changed = ISC_FALSE;
4982:
4983: /* Validate the binding update. */
4984: required_options = FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS;
4985: if ((msg->options_present & required_options) != required_options) {
4986: message = "binding update lacks required options";
4987: reason = FTR_MISSING_BINDINFO;
4988: goto bad;
4989: }
4990:
4991: ia.len = sizeof msg -> assigned_addr;
4992: memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
4993:
4994: lease = (struct lease *)0;
4995: lt = (struct lease *)0;
4996: if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
4997: message = "unknown IP address";
4998: reason = FTR_ILLEGAL_IP_ADDR;
4999: goto bad;
5000: }
5001:
5002: /*
5003: * If this lease is covered by a different failover peering
5004: * relationship, assert an error.
5005: */
5006: if ((lease->pool == NULL) || (lease->pool->failover_peer == NULL) ||
5007: (lease->pool->failover_peer != state)) {
5008: message = "IP address is covered by a different failover "
5009: "relationship state";
5010: reason = FTR_ILLEGAL_IP_ADDR;
5011: goto bad;
5012: }
5013:
5014: /*
5015: * Dueling updates: This happens when both servers send a BNDUPD
5016: * at the same time. We want the best update to win, which means
5017: * we reject if we think ours is better, or cancel if we think the
5018: * peer's is better. We only assert a problem if the lease is on
5019: * the ACK queue, not on the UPDATE queue. This means that after
5020: * accepting this server's BNDUPD, we will send our own BNDUPD
5021: * /after/ sending the BNDACK (this order was recently enforced in
5022: * queue processing).
5023: */
5024: if ((lease->flags & ON_ACK_QUEUE) != 0) {
5025: if (failover_lease_is_better(state, lease, msg)) {
5026: message = "incoming update is less critical than "
5027: "outgoing update";
5028: reason = FTR_LESS_CRIT_BIND_INFO;
5029: goto bad;
5030: } else {
5031: /* This makes it so we ignore any spurious ACKs. */
5032: dhcp_failover_ack_queue_remove(state, lease);
5033: }
5034: }
5035:
5036: /* Install the new info. Start by taking a copy to markup. */
5037: if (!lease_copy (<, lease, MDL)) {
5038: message = "no memory";
5039: goto bad;
5040: }
5041:
5042: if (msg -> options_present & FTB_CHADDR) {
5043: if (msg->binding_status == FTS_ABANDONED) {
5044: message = "BNDUPD to ABANDONED with a CHADDR";
5045: goto bad;
5046: }
5047: if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
5048: message = "chaddr too long";
5049: goto bad;
5050: }
5051:
5052: if ((lt->hardware_addr.hlen != msg->chaddr.count) ||
5053: (memcmp(lt->hardware_addr.hbuf, msg->chaddr.data,
5054: msg->chaddr.count) != 0))
5055: chaddr_changed = ISC_TRUE;
5056:
5057: lt -> hardware_addr.hlen = msg -> chaddr.count;
5058: memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
5059: msg -> chaddr.count);
5060: } else if (msg->binding_status == FTS_ACTIVE ||
5061: msg->binding_status == FTS_EXPIRED ||
5062: msg->binding_status == FTS_RELEASED) {
5063: message = "BNDUPD without CHADDR";
5064: reason = FTR_MISSING_BINDINFO;
5065: goto bad;
5066: } else if (msg->binding_status == FTS_ABANDONED) {
5067: chaddr_changed = ISC_TRUE;
5068: lt->hardware_addr.hlen = 0;
5069: if (lt->scope)
5070: binding_scope_dereference(<->scope, MDL);
5071: }
5072:
5073: /* There is no explicit message content to indicate that the client
5074: * supplied no client-identifier. So if we don't hear of a value,
5075: * we discard the last one.
5076: */
5077: if (msg->options_present & FTB_CLIENT_IDENTIFIER) {
5078: if (msg->binding_status == FTS_ABANDONED) {
5079: message = "BNDUPD to ABANDONED with client-id";
5080: goto bad;
5081: }
5082:
5083: if ((lt->uid_len != msg->client_identifier.count) ||
5084: (lt->uid == NULL) || /* Sanity; should never happen. */
5085: (memcmp(lt->uid, msg->client_identifier.data,
5086: lt->uid_len) != 0))
5087: ident_changed = ISC_TRUE;
5088:
5089: lt->uid_len = msg->client_identifier.count;
5090:
5091: /* Allocate the lt->uid buffer if we haven't already, or
5092: * re-allocate the lt-uid buffer if we have one that is not
5093: * large enough. Otherwise, just use the extant buffer.
5094: */
5095: if (!lt->uid || lt->uid == lt->uid_buf ||
5096: lt->uid_len > lt->uid_max) {
5097: if (lt->uid && lt->uid != lt->uid_buf)
5098: dfree(lt->uid, MDL);
5099:
5100: if (lt->uid_len > sizeof(lt->uid_buf)) {
5101: lt->uid_max = lt->uid_len;
5102: lt->uid = dmalloc(lt->uid_len, MDL);
5103: if (!lt->uid) {
5104: message = "no memory";
5105: goto bad;
5106: }
5107: } else {
5108: lt->uid_max = sizeof(lt->uid_buf);
5109: lt->uid = lt->uid_buf;
5110: }
5111: }
5112: memcpy (lt -> uid,
5113: msg -> client_identifier.data, lt -> uid_len);
5114: } else if (lt->uid && msg->binding_status != FTS_RESET &&
5115: msg->binding_status != FTS_FREE &&
5116: msg->binding_status != FTS_BACKUP) {
5117: ident_changed = ISC_TRUE;
5118: if (lt->uid != lt->uid_buf)
5119: dfree (lt->uid, MDL);
5120: lt->uid = NULL;
5121: lt->uid_max = lt->uid_len = 0;
5122: }
5123:
5124: /*
5125: * A server's configuration can assign a 'binding scope';
5126: *
5127: * set var = "value";
5128: *
5129: * The problem with these binding scopes is that they are refreshed
5130: * when the server processes a client's DHCP packet. A local binding
5131: * scope is trash, then, when the lease has been assigned by the
5132: * partner server. There is no real way to detect this, a peer may
5133: * be updating us (as through potential conflict) with a binding we
5134: * sent them, but we can trivially detect the /problematic/ case;
5135: *
5136: * lease is free.
5137: * primary allocates lease to client A, assigns ddns name A.
5138: * primary fails.
5139: * secondary enters partner down.
5140: * lease expires, and is set free.
5141: * lease is allocated to client B and given ddns name B.
5142: * primary recovers.
5143: *
5144: * The binding update in this case will be active->active, but the
5145: * client identification on the lease will have changed. The ddns
5146: * update on client A will have leaked if we just remove the binding
5147: * scope blindly.
5148: */
5149: if (msg->binding_status == FTS_ACTIVE &&
5150: (chaddr_changed || ident_changed)) {
5151: ddns_removals(lease, NULL);
5152:
5153: if (lease->scope != NULL)
5154: binding_scope_dereference(&lease->scope, MDL);
5155: }
5156:
5157: /* XXX Times may need to be adjusted based on clock skew! */
5158: if (msg -> options_present & FTB_STOS) {
5159: lt -> starts = msg -> stos;
5160: }
5161: if (msg -> options_present & FTB_LEASE_EXPIRY) {
5162: lt -> ends = msg -> expiry;
5163: }
5164: if (msg->options_present & FTB_POTENTIAL_EXPIRY) {
5165: lt->atsfp = lt->tsfp = msg->potential_expiry;
5166: }
5167: if (msg->options_present & FTB_IP_FLAGS) {
5168: if (msg->ip_flags & FTF_IP_FLAG_RESERVE) {
5169: if ((((state->i_am == primary) &&
5170: (lease->binding_state == FTS_FREE)) ||
5171: ((state->i_am == secondary) &&
5172: (lease->binding_state == FTS_BACKUP))) &&
5173: !(lease->flags & RESERVED_LEASE)) {
5174: message = "Address is not reserved.";
5175: reason = FTR_IP_NOT_RESERVED;
5176: goto bad;
5177: }
5178:
5179: lt->flags |= RESERVED_LEASE;
5180: } else
5181: lt->flags &= ~RESERVED_LEASE;
5182:
5183: if (msg->ip_flags & FTF_IP_FLAG_BOOTP) {
5184: if ((((state->i_am == primary) &&
5185: (lease->binding_state == FTS_FREE)) ||
5186: ((state->i_am == secondary) &&
5187: (lease->binding_state == FTS_BACKUP))) &&
5188: !(lease->flags & BOOTP_LEASE)) {
5189: message = "Address is not allocated to BOOTP.";
5190: goto bad;
5191: }
5192: lt->flags |= BOOTP_LEASE;
5193: } else
5194: lt->flags &= ~BOOTP_LEASE;
5195:
5196: if (msg->ip_flags & ~(FTF_IP_FLAG_RESERVE | FTF_IP_FLAG_BOOTP))
5197: log_info("Unknown IP-flags set in BNDUPD (0x%x).",
5198: msg->ip_flags);
5199: } else /* Flags may only not appear if the values are zero. */
5200: lt->flags &= ~(RESERVED_LEASE | BOOTP_LEASE);
5201:
5202: #if defined (DEBUG_LEASE_STATE_TRANSITIONS)
5203: log_info ("processing state transition for %s: %s to %s",
5204: piaddr (lease -> ip_addr),
5205: binding_state_print (lease -> binding_state),
5206: binding_state_print (msg -> binding_status));
5207: #endif
5208:
5209: /* If we're in normal state, make sure the state transition
5210: we got is valid. */
5211: if (state -> me.state == normal) {
5212: new_binding_state =
5213: (normal_binding_state_transition_check
5214: (lease, state, msg -> binding_status,
5215: msg -> potential_expiry));
5216: /* XXX if the transition the peer asked for isn't
5217: XXX allowed, maybe we should make the transition
5218: XXX into potential-conflict at this point. */
5219: } else {
5220: new_binding_state =
5221: (conflict_binding_state_transition_check
5222: (lease, state, msg -> binding_status,
5223: msg -> potential_expiry));
5224: }
5225: if (new_binding_state != msg -> binding_status) {
5226: char outbuf [100];
5227:
5228: if (snprintf (outbuf, sizeof outbuf,
5229: "%s: invalid state transition: %s to %s",
5230: piaddr (lease -> ip_addr),
5231: binding_state_print (lease -> binding_state),
5232: binding_state_print (msg -> binding_status))
5233: >= sizeof outbuf)
5234: log_fatal ("%s: impossible outbuf overflow",
5235: "dhcp_failover_process_bind_update");
5236:
5237: dhcp_failover_send_bind_ack (state, msg,
5238: FTR_FATAL_CONFLICT,
5239: outbuf);
5240: goto out;
5241: }
5242: if (new_binding_state == FTS_EXPIRED ||
5243: new_binding_state == FTS_RELEASED ||
5244: new_binding_state == FTS_RESET) {
5245: lt -> next_binding_state = FTS_FREE;
5246:
5247: /* Mac address affinity. Assign the lease to
5248: * BACKUP state if we are the primary and the
5249: * peer is more likely to reallocate this lease
5250: * to a returning client.
5251: */
5252: if ((state->i_am == primary) &&
5253: !(lt->flags & (RESERVED_LEASE | BOOTP_LEASE)))
5254: send_to_backup = peer_wants_lease(lt);
5255: } else {
5256: lt -> next_binding_state = new_binding_state;
5257: }
5258: msg -> binding_status = lt -> next_binding_state;
5259:
5260: /* Try to install the new information. */
5261: if (!supersede_lease (lease, lt, 0, 0, 0) ||
5262: !write_lease (lease)) {
5263: message = "database update failed";
5264: bad:
5265: dhcp_failover_send_bind_ack (state, msg, reason, message);
5266: goto out;
5267: } else {
5268: dhcp_failover_queue_ack (state, msg);
5269: }
5270:
5271: /* If it is probably wise, assign lease to backup state if the peer
5272: * is not already hoarding leases.
5273: */
5274: if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
5275: lease->next_binding_state = FTS_BACKUP;
5276: lease->tstp = cur_time;
5277: lease->starts = cur_time;
5278:
5279: if (!supersede_lease(lease, NULL, 0, 1, 0) ||
5280: !write_lease(lease))
5281: log_error("can't commit lease %s for mac addr "
5282: "affinity", piaddr(lease->ip_addr));
5283:
5284: dhcp_failover_send_updates(state);
5285: }
5286:
5287: out:
5288: if (lt)
5289: lease_dereference (<, MDL);
5290: if (lease)
5291: lease_dereference (&lease, MDL);
5292:
5293: return ISC_R_SUCCESS;
5294: }
5295:
5296: /* This was hairy enough I didn't want to do it all in an if statement.
5297: *
5298: * Returns: Truth is the secondary is allowed to get more leases based upon
5299: * MAC address affinity. False otherwise.
5300: */
5301: static inline int
5302: secondary_not_hoarding(dhcp_failover_state_t *state, struct pool *p) {
5303: int total;
5304: int hold;
5305: int lts;
5306:
5307: total = p->free_leases + p->backup_leases;
5308:
5309: /* How many leases is one side or the other allowed to "hold"? */
5310: hold = ((total * state->max_lease_ownership) + 50) / 100;
5311:
5312: /* If we were to send leases (or if the secondary were to send us
5313: * leases in the negative direction), how many would that be?
5314: */
5315: lts = (p->free_leases - p->backup_leases) / 2;
5316:
5317: /* The peer is not hoarding leases if we would send them more leases
5318: * (or they would take fewer leases) than the maximum they are allowed
5319: * to hold (the negative hold).
5320: */
5321: return(lts > -hold);
5322: }
5323:
5324: isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
5325: failover_message_t *msg)
5326: {
5327: struct lease *lt = (struct lease *)0;
5328: struct lease *lease = (struct lease *)0;
5329: struct iaddr ia;
5330: const char *message = "no memory";
5331: u_int32_t pot_expire;
5332: int send_to_backup = ISC_FALSE;
5333: struct timeval tv;
5334:
5335: ia.len = sizeof msg -> assigned_addr;
5336: memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
5337:
5338: if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
5339: message = "no such lease";
5340: goto bad;
5341: }
5342:
5343: /* XXX check for conflicts. */
5344: if (msg -> options_present & FTB_REJECT_REASON) {
5345: log_error ("bind update on %s from %s rejected: %.*s",
5346: piaddr (ia), state -> name,
5347: (int)((msg -> options_present & FTB_MESSAGE)
5348: ? msg -> message.count
5349: : strlen (dhcp_failover_reject_reason_print
5350: (msg -> reject_reason))),
5351: (msg -> options_present & FTB_MESSAGE)
5352: ? (const char *)(msg -> message.data)
5353: : (dhcp_failover_reject_reason_print
5354: (msg -> reject_reason)));
5355: goto unqueue;
5356: }
5357:
5358: /* Silently discard acks for leases we did not update (or multiple
5359: * acks).
5360: */
5361: if (!lease->last_xid)
5362: goto unqueue;
5363:
5364: if (lease->last_xid != msg->xid) {
5365: message = "xid mismatch";
5366: goto bad;
5367: }
5368:
5369: /* XXX Times may need to be adjusted based on clock skew! */
5370: if (msg->options_present & FTO_POTENTIAL_EXPIRY)
5371: pot_expire = msg->potential_expiry;
5372: else
5373: pot_expire = lease->tstp;
5374:
5375: /* If the lease was desired to enter a binding state, we set
5376: * such a value upon transmitting a bndupd. We do not clear it
5377: * if we receive a bndupd in the meantime (or change the state
5378: * of the lease again ourselves), but we do set binding_state
5379: * if we get a bndupd.
5380: *
5381: * So desired_binding_state tells us what we sent a bndupd for,
5382: * and binding_state tells us what we have since determined in
5383: * the meantime.
5384: */
5385: if (lease->desired_binding_state == FTS_EXPIRED ||
5386: lease->desired_binding_state == FTS_RESET ||
5387: lease->desired_binding_state == FTS_RELEASED)
5388: {
5389: /* It is not a problem to do this directly as we call
5390: * supersede_lease immediately after: the lease is requeued
5391: * even if its sort order (tsfp) has changed.
5392: */
5393: lease->atsfp = lease->tsfp = pot_expire;
5394: if ((state->i_am == secondary) &&
5395: (lease->flags & RESERVED_LEASE))
5396: lease->next_binding_state = FTS_BACKUP;
5397: else
5398: lease->next_binding_state = FTS_FREE;
5399: /* Clear this condition for the next go-round. */
5400: lease->desired_binding_state = lease->next_binding_state;
5401: supersede_lease(lease, (struct lease *)0, 0, 0, 0);
5402: write_lease(lease);
5403:
5404: /* Lease has returned to FREE state from the
5405: * transitional states. If the lease 'belongs'
5406: * to a client that would be served by the
5407: * peer, process a binding update now to send
5408: * the lease to backup state. But not if we
5409: * think we already have.
5410: */
5411: if (state->i_am == primary &&
5412: !(lease->flags & (RESERVED_LEASE | BOOTP_LEASE)) &&
5413: peer_wants_lease(lease))
5414: send_to_backup = ISC_TRUE;
5415:
5416: if (!send_to_backup && state->me.state == normal)
5417: commit_leases();
5418: } else {
5419: /* XXX It could be a problem to do this directly if the lease
5420: * XXX is sorted by tsfp.
5421: */
5422: lease->atsfp = lease->tsfp = pot_expire;
5423: if (lease->desired_binding_state != lease->binding_state) {
5424: lease->next_binding_state =
5425: lease->desired_binding_state;
5426: supersede_lease(lease,
5427: (struct lease *)0, 0, 0, 0);
5428: }
5429: write_lease(lease);
5430: /* Commit the lease only after a two-second timeout,
5431: so that if we get a bunch of acks in quick
5432: succession (e.g., when stealing leases from the
5433: secondary), we do not do an immediate commit for
5434: each one. */
5435: tv.tv_sec = cur_time + 2;
5436: tv.tv_usec = 0;
5437: add_timeout(&tv, commit_leases_timeout, (void *)0, 0, 0);
5438: }
5439:
5440: unqueue:
5441: dhcp_failover_ack_queue_remove (state, lease);
5442:
5443: /* If we are supposed to send an update done after we send
5444: this lease, go ahead and send it. */
5445: if (state -> send_update_done == lease) {
5446: lease_dereference (&state -> send_update_done, MDL);
5447: dhcp_failover_send_update_done (state);
5448: }
5449:
5450: /* Now that the lease is off the ack queue, consider putting it
5451: * back on the update queue for mac address affinity.
5452: */
5453: if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
5454: lease->next_binding_state = FTS_BACKUP;
5455: lease->tstp = lease->starts = cur_time;
5456:
5457: if (!supersede_lease(lease, NULL, 0, 1, 0) ||
5458: !write_lease(lease))
5459: log_error("can't commit lease %s for "
5460: "client affinity", piaddr(lease->ip_addr));
5461:
5462: if (state->me.state == normal)
5463: commit_leases();
5464: }
5465:
5466: /* If there are updates pending, we've created space to send at
5467: least one. */
5468: dhcp_failover_send_updates (state);
5469:
5470: out:
5471: lease_dereference (&lease, MDL);
5472: if (lt)
5473: lease_dereference (<, MDL);
5474:
5475: return ISC_R_SUCCESS;
5476:
5477: bad:
5478: log_info ("bind update on %s got ack from %s: %s.",
5479: piaddr (ia), state -> name, message);
5480: goto out;
5481: }
5482:
5483: isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
5484: int everythingp)
5485: {
5486: struct shared_network *s;
5487: struct pool *p;
5488: struct lease *l;
5489: int i;
5490: #define FREE_LEASES 0
5491: #define ACTIVE_LEASES 1
5492: #define EXPIRED_LEASES 2
5493: #define ABANDONED_LEASES 3
5494: #define BACKUP_LEASES 4
5495: #define RESERVED_LEASES 5
5496: struct lease **lptr[RESERVED_LEASES+1];
5497:
5498: /* Loop through each pool in each shared network and call the
5499: expiry routine on the pool. */
5500: for (s = shared_networks; s; s = s -> next) {
5501: for (p = s -> pools; p; p = p -> next) {
5502: if (p->failover_peer != state)
5503: continue;
5504:
5505: lptr[FREE_LEASES] = &p->free;
5506: lptr[ACTIVE_LEASES] = &p->active;
5507: lptr[EXPIRED_LEASES] = &p->expired;
5508: lptr[ABANDONED_LEASES] = &p->abandoned;
5509: lptr[BACKUP_LEASES] = &p->backup;
5510: lptr[RESERVED_LEASES] = &p->reserved;
5511:
5512: for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
5513: for (l = *(lptr [i]); l; l = l -> next) {
5514: if ((l->flags & ON_QUEUE) == 0 &&
5515: (everythingp ||
5516: (l->tstp > l->atsfp) ||
5517: (i == EXPIRED_LEASES))) {
5518: l -> desired_binding_state = l -> binding_state;
5519: dhcp_failover_queue_update (l, 0);
5520: }
5521: }
5522: }
5523: }
5524: }
5525: return ISC_R_SUCCESS;
5526: }
5527:
5528: isc_result_t
5529: dhcp_failover_process_update_request (dhcp_failover_state_t *state,
5530: failover_message_t *msg)
5531: {
5532: if (state->send_update_done) {
5533: log_info("Received update request while old update still "
5534: "flying! Silently discarding old request.");
5535: lease_dereference(&state->send_update_done, MDL);
5536: }
5537:
5538: /* Generate a fresh update queue. */
5539: dhcp_failover_generate_update_queue (state, 0);
5540:
5541: state->updxid = msg->xid;
5542:
5543: /* If there's anything on the update queue (there shouldn't be
5544: anything on the ack queue), trigger an update done message
5545: when we get an ack for that lease. */
5546: if (state -> update_queue_tail) {
5547: lease_reference (&state -> send_update_done,
5548: state -> update_queue_tail, MDL);
5549: dhcp_failover_send_updates (state);
5550: log_info ("Update request from %s: sending update",
5551: state -> name);
5552: } else {
5553: /* Otherwise, there are no updates to send, so we can
5554: just send an UPDDONE message immediately. */
5555: dhcp_failover_send_update_done (state);
5556: log_info ("Update request from %s: nothing pending",
5557: state -> name);
5558: }
5559:
5560: return ISC_R_SUCCESS;
5561: }
5562:
5563: isc_result_t
5564: dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
5565: failover_message_t *msg)
5566: {
5567: if (state->send_update_done) {
5568: log_info("Received update request while old update still "
5569: "flying! Silently discarding old request.");
5570: lease_dereference(&state->send_update_done, MDL);
5571: }
5572:
5573: /* Generate a fresh update queue that includes every lease. */
5574: dhcp_failover_generate_update_queue (state, 1);
5575:
5576: state->updxid = msg->xid;
5577:
5578: if (state -> update_queue_tail) {
5579: lease_reference (&state -> send_update_done,
5580: state -> update_queue_tail, MDL);
5581: dhcp_failover_send_updates (state);
5582: log_info ("Update request all from %s: sending update",
5583: state -> name);
5584: } else {
5585: /* This should really never happen, but it could happen
5586: on a server that currently has no leases configured. */
5587: dhcp_failover_send_update_done (state);
5588: log_info ("Update request all from %s: nothing pending",
5589: state -> name);
5590: }
5591:
5592: return ISC_R_SUCCESS;
5593: }
5594:
5595: isc_result_t
5596: dhcp_failover_process_update_done (dhcp_failover_state_t *state,
5597: failover_message_t *msg)
5598: {
5599: struct timeval tv;
5600:
5601: log_info ("failover peer %s: peer update completed.",
5602: state -> name);
5603:
5604: state -> curUPD = 0;
5605:
5606: switch (state -> me.state) {
5607: case unknown_state:
5608: case partner_down:
5609: case normal:
5610: case communications_interrupted:
5611: case resolution_interrupted:
5612: case shut_down:
5613: case paused:
5614: case recover_done:
5615: case startup:
5616: case recover_wait:
5617: break; /* shouldn't happen. */
5618:
5619: /* We got the UPDDONE, so we can go into normal state! */
5620: case potential_conflict:
5621: if (state->partner.state == conflict_done) {
5622: if (state->i_am == secondary) {
5623: dhcp_failover_set_state (state, normal);
5624: } else {
5625: log_error("Secondary is in conflict_done "
5626: "state after conflict resolution, "
5627: "this is illegal.");
5628: dhcp_failover_set_state (state, shut_down);
5629: }
5630: } else {
5631: if (state->i_am == primary)
5632: dhcp_failover_set_state (state, conflict_done);
5633: else
5634: log_error("Spurious update-done message.");
5635: }
5636:
5637: break;
5638:
5639: case conflict_done:
5640: log_error("Spurious update-done message.");
5641: break;
5642:
5643: case recover:
5644: /* Wait for MCLT to expire before moving to recover_done,
5645: except that if both peers come up in recover, there is
5646: no point in waiting for MCLT to expire - this probably
5647: indicates the initial startup of a newly-configured
5648: failover pair. */
5649: if (state -> me.stos + state -> mclt > cur_time &&
5650: state -> partner.state != recover &&
5651: state -> partner.state != recover_done) {
5652: dhcp_failover_set_state (state, recover_wait);
5653: #if defined (DEBUG_FAILOVER_TIMING)
5654: log_info ("add_timeout +%d %s",
5655: (int)(cur_time -
5656: state -> me.stos + state -> mclt),
5657: "dhcp_failover_recover_done");
5658: #endif
5659: tv . tv_sec = (int)(state -> me.stos + state -> mclt);
5660: tv . tv_usec = 0;
5661: add_timeout (&tv,
5662: dhcp_failover_recover_done,
5663: state,
5664: (tvref_t)omapi_object_reference,
5665: (tvunref_t)
5666: omapi_object_dereference);
5667: } else
5668: dhcp_failover_recover_done (state);
5669: }
5670:
5671: return ISC_R_SUCCESS;
5672: }
5673:
5674: void dhcp_failover_recover_done (void *sp)
5675: {
5676: dhcp_failover_state_t *state = sp;
5677:
5678: #if defined (DEBUG_FAILOVER_TIMING)
5679: log_info ("dhcp_failover_recover_done");
5680: #endif
5681:
5682: dhcp_failover_set_state (state, recover_done);
5683: }
5684:
5685: #if defined (DEBUG_FAILOVER_MESSAGES)
5686: /* Print hunks of failover messages, doing line breaks as appropriate.
5687: Note that this assumes syslog is being used, rather than, e.g., the
5688: Windows NT logging facility, where just dumping the whole message in
5689: one hunk would be more appropriate. */
5690:
5691: void failover_print (char *obuf,
5692: unsigned *obufix, unsigned obufmax, const char *s)
5693: {
5694: int len = strlen (s);
5695:
5696: while (len + *obufix + 1 >= obufmax) {
5697: log_debug ("%s", obuf);
5698: if (!*obufix) {
5699: log_debug ("%s", s);
5700: *obufix = 0;
5701: return;
5702: }
5703: *obufix = 0;
5704: }
5705: strcpy (&obuf [*obufix], s);
5706: *obufix += len;
5707: }
5708: #endif /* defined (DEBUG_FAILOVER_MESSAGES) */
5709:
5710: /* Taken from draft-ietf-dhc-loadb-01.txt: */
5711: /* A "mixing table" of 256 distinct values, in pseudo-random order. */
5712: unsigned char loadb_mx_tbl[256] = {
5713: 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
5714: 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
5715: 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
5716: 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
5717: 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
5718: 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
5719: 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
5720: 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
5721: 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
5722: 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
5723: 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
5724: 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
5725: 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
5726: 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
5727: 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
5728: 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
5729: 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
5730: 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
5731: 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
5732: 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
5733: 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
5734: 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
5735: 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
5736: 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
5737: 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
5738: 170, 68, 6, 169, 234, 151 };
5739:
5740: static unsigned char loadb_p_hash (const unsigned char *, unsigned);
5741: static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
5742: {
5743: unsigned char hash = len;
5744: int i;
5745: for(i = len; i > 0; )
5746: hash = loadb_mx_tbl [hash ^ (key [--i])];
5747: return hash;
5748: }
5749:
5750: int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
5751: {
5752: struct option_cache *oc;
5753: struct data_string ds;
5754: unsigned char hbaix;
5755: int hm;
5756:
5757: if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
5758: return 1;
5759: }
5760:
5761: /* If we don't have a hash bucket array, we can't tell if this
5762: one's ours, so we assume it's not. */
5763: if (!state -> hba)
5764: return 0;
5765:
5766: oc = lookup_option (&dhcp_universe, packet -> options,
5767: DHO_DHCP_CLIENT_IDENTIFIER);
5768: memset (&ds, 0, sizeof ds);
5769: if (oc &&
5770: evaluate_option_cache (&ds, packet, (struct lease *)0,
5771: (struct client_state *)0,
5772: packet -> options, (struct option_state *)0,
5773: &global_scope, oc, MDL)) {
5774: hbaix = loadb_p_hash (ds.data, ds.len);
5775:
5776: data_string_forget(&ds, MDL);
5777: } else {
5778: hbaix = loadb_p_hash (packet -> raw -> chaddr,
5779: packet -> raw -> hlen);
5780: }
5781:
5782: hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
5783:
5784: if (state -> i_am == primary)
5785: return hm;
5786: else
5787: return !hm;
5788: }
5789:
5790: /* The inverse of load_balance_mine ("load balance theirs"). We can't
5791: * use the regular load_balance_mine() and invert it because of the case
5792: * where there might not be an HBA, and we want to indicate false here
5793: * in this case only.
5794: */
5795: int
5796: peer_wants_lease(struct lease *lp)
5797: {
5798: dhcp_failover_state_t *state;
5799: unsigned char hbaix;
5800: int hm;
5801:
5802: if (!lp->pool)
5803: return 0;
5804:
5805: state = lp->pool->failover_peer;
5806:
5807: if (!state || !state->hba)
5808: return 0;
5809:
5810: if (lp->uid_len)
5811: hbaix = loadb_p_hash(lp->uid, lp->uid_len);
5812: else if (lp->hardware_addr.hlen > 1)
5813: /* Skip the first byte, which is the hardware type, and is
5814: * not included during actual load balancing checks above
5815: * since it is separate from the packet header chaddr field.
5816: * The remainder of the hardware address should be identical
5817: * to the chaddr contents.
5818: */
5819: hbaix = loadb_p_hash(lp->hardware_addr.hbuf + 1,
5820: lp->hardware_addr.hlen - 1);
5821: else /* impossible to categorize into LBA */
5822: return 0;
5823:
5824: hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
5825:
5826: if (state->i_am == primary)
5827: return !hm;
5828: else
5829: return hm;
5830: }
5831:
5832: /* This deals with what to do with bind updates when
5833: we're in the normal state
5834:
5835: Note that tsfp had better be set from the latest bind update
5836: _before_ this function is called! */
5837:
5838: binding_state_t
5839: normal_binding_state_transition_check (struct lease *lease,
5840: dhcp_failover_state_t *state,
5841: binding_state_t binding_state,
5842: u_int32_t tsfp)
5843: {
5844: binding_state_t new_state;
5845:
5846: /* If there is no transition, it's no problem. */
5847: if (binding_state == lease -> binding_state)
5848: return binding_state;
5849:
5850: switch (lease -> binding_state) {
5851: case FTS_FREE:
5852: case FTS_ABANDONED:
5853: switch (binding_state) {
5854: case FTS_ACTIVE:
5855: case FTS_ABANDONED:
5856: case FTS_BACKUP:
5857: case FTS_EXPIRED:
5858: case FTS_RELEASED:
5859: case FTS_RESET:
5860: /* If the lease was free, and our peer is primary,
5861: then it can make it active, or abandoned, or
5862: backup. Abandoned is treated like free in
5863: this case. */
5864: if (state -> i_am == secondary)
5865: return binding_state;
5866:
5867: /* Otherwise, it can't legitimately do any sort of
5868: state transition. Because the lease was free,
5869: and the error has already been made, we allow the
5870: peer to change its state anyway, but log a warning
5871: message in hopes that the error will be fixed. */
5872: case FTS_FREE: /* for compiler */
5873: new_state = binding_state;
5874: goto out;
5875:
5876: default:
5877: log_fatal ("Impossible case at %s:%d.", MDL);
5878: return FTS_RESET;
5879: }
5880: case FTS_ACTIVE:
5881: /* The secondary can't change the state of an active
5882: lease. */
5883: if (state -> i_am == primary) {
5884: /* Except that the client may send the DHCPRELEASE
5885: to the secondary, and we have to accept that. */
5886: if (binding_state == FTS_RELEASED)
5887: return binding_state;
5888: new_state = lease -> binding_state;
5889: goto out;
5890: }
5891:
5892: /* So this is only for transitions made by the primary: */
5893: switch (binding_state) {
5894: case FTS_FREE:
5895: case FTS_BACKUP:
5896: /* Can't set a lease to free or backup until the
5897: peer agrees that it's expired. */
5898: if (tsfp > cur_time) {
5899: new_state = lease -> binding_state;
5900: goto out;
5901: }
5902: return binding_state;
5903:
5904: case FTS_EXPIRED:
5905: /* XXX 65 should be the clock skew between the peers
5906: XXX plus a fudge factor. This code will result
5907: XXX in problems if MCLT is really short or the
5908: XXX max-lease-time is really short (less than the
5909: XXX fudge factor. */
5910: if (lease -> ends - 65 > cur_time) {
5911: new_state = lease -> binding_state;
5912: goto out;
5913: }
5914:
5915: case FTS_RELEASED:
5916: case FTS_ABANDONED:
5917: case FTS_RESET:
5918: case FTS_ACTIVE:
5919: return binding_state;
5920:
5921: default:
5922: log_fatal ("Impossible case at %s:%d.", MDL);
5923: return FTS_RESET;
5924: }
5925: break;
5926: case FTS_EXPIRED:
5927: switch (binding_state) {
5928: case FTS_BACKUP:
5929: case FTS_FREE:
5930: /* Can't set a lease to free or backup until the
5931: peer agrees that it's expired. */
5932: if (tsfp > cur_time) {
5933: new_state = lease -> binding_state;
5934: goto out;
5935: }
5936: return binding_state;
5937:
5938: case FTS_ACTIVE:
5939: case FTS_RELEASED:
5940: case FTS_ABANDONED:
5941: case FTS_RESET:
5942: case FTS_EXPIRED:
5943: return binding_state;
5944:
5945: default:
5946: log_fatal ("Impossible case at %s:%d.", MDL);
5947: return FTS_RESET;
5948: }
5949: case FTS_RELEASED:
5950: switch (binding_state) {
5951: case FTS_FREE:
5952: case FTS_BACKUP:
5953:
5954: /* These are invalid state transitions - should we
5955: prevent them? */
5956: case FTS_EXPIRED:
5957: case FTS_ABANDONED:
5958: case FTS_RESET:
5959: case FTS_ACTIVE:
5960: case FTS_RELEASED:
5961: return binding_state;
5962:
5963: default:
5964: log_fatal ("Impossible case at %s:%d.", MDL);
5965: return FTS_RESET;
5966: }
5967: case FTS_RESET:
5968: switch (binding_state) {
5969: case FTS_FREE:
5970: case FTS_BACKUP:
5971: /* Can't set a lease to free or backup until the
5972: peer agrees that it's expired. */
5973: if (tsfp > cur_time) {
5974: new_state = lease -> binding_state;
5975: goto out;
5976: }
5977: return binding_state;
5978:
5979: case FTS_ACTIVE:
5980: case FTS_EXPIRED:
5981: case FTS_RELEASED:
5982: case FTS_ABANDONED:
5983: case FTS_RESET:
5984: return binding_state;
5985:
5986: default:
5987: log_fatal ("Impossible case at %s:%d.", MDL);
5988: return FTS_RESET;
5989: }
5990: case FTS_BACKUP:
5991: switch (binding_state) {
5992: case FTS_ACTIVE:
5993: case FTS_ABANDONED:
5994: case FTS_EXPIRED:
5995: case FTS_RELEASED:
5996: case FTS_RESET:
5997: /* If the lease was in backup, and our peer
5998: is secondary, then it can make it active
5999: or abandoned. */
6000: if (state -> i_am == primary)
6001: return binding_state;
6002:
6003: /* Either the primary or the secondary can
6004: reasonably move a lease from the backup
6005: state to the free state. */
6006: case FTS_FREE:
6007: return binding_state;
6008:
6009: case FTS_BACKUP:
6010: new_state = lease -> binding_state;
6011: goto out;
6012:
6013: default:
6014: log_fatal ("Impossible case at %s:%d.", MDL);
6015: return FTS_RESET;
6016: }
6017:
6018: default:
6019: log_fatal ("Impossible case at %s:%d.", MDL);
6020: return FTS_RESET;
6021: }
6022: out:
6023: return new_state;
6024: }
6025:
6026: /* Determine whether the state transition is okay when we're potentially
6027: in conflict with the peer. */
6028: binding_state_t
6029: conflict_binding_state_transition_check (struct lease *lease,
6030: dhcp_failover_state_t *state,
6031: binding_state_t binding_state,
6032: u_int32_t tsfp)
6033: {
6034: binding_state_t new_state;
6035:
6036: /* If there is no transition, it's no problem. */
6037: if (binding_state == lease -> binding_state)
6038: new_state = binding_state;
6039: else {
6040: switch (lease -> binding_state) {
6041: /* If we think the lease is not in use, then the
6042: state into which the partner put it is just fine,
6043: whatever it is. */
6044: case FTS_FREE:
6045: case FTS_ABANDONED:
6046: case FTS_EXPIRED:
6047: case FTS_RELEASED:
6048: case FTS_RESET:
6049: case FTS_BACKUP:
6050: new_state = binding_state;
6051: break;
6052:
6053: /* If we think the lease *is* in use, then we're not
6054: going to take the partner's change if the partner
6055: thinks it's free. */
6056: case FTS_ACTIVE:
6057: switch (binding_state) {
6058: case FTS_FREE:
6059: case FTS_BACKUP:
6060: new_state = lease -> binding_state;
6061: break;
6062:
6063: case FTS_EXPIRED:
6064: /* If we don't agree about expiry, it's
6065: * invalid. 65 should allow for max
6066: * clock skew (60) plus some fudge.
6067: * XXX: should we refetch cur_time?
6068: */
6069: if ((lease->ends - 65) > cur_time)
6070: new_state = lease->binding_state;
6071: else
6072: new_state = binding_state;
6073: break;
6074:
6075: /* RELEASED, RESET, and ABANDONED indicate
6076: * that our partner has information about
6077: * this lease that we did not witness. Our
6078: * partner wins.
6079: */
6080: case FTS_RELEASED:
6081: case FTS_RESET:
6082: case FTS_ABANDONED:
6083: new_state = binding_state;
6084: break;
6085:
6086: default:
6087: log_fatal ("Impossible case at %s:%d.", MDL);
6088: return FTS_RESET;
6089: }
6090: break;
6091:
6092: default:
6093: log_fatal ("Impossible case at %s:%d.", MDL);
6094: return FTS_RESET;
6095: }
6096: }
6097: return new_state;
6098: }
6099:
6100: /* We can reallocate a lease under the following circumstances:
6101:
6102: (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
6103: FTS_BACKUP, and we're secondary.
6104: (2) We're in partner_down, and the lease is not active, and we
6105: can be sure that the other server didn't make it active.
6106: We can only be sure that the server didn't make it active
6107: when we are in the partner_down state and one of the following
6108: two conditions holds:
6109: (a) in the case that the time sent from the peer is earlier than
6110: the time we entered the partner_down state, at least MCLT has
6111: gone by since we entered partner_down, or
6112: (b) in the case that the time sent from the peer is later than
6113: the time when we entered partner_down, the current time is
6114: later than the time sent from the peer by at least MCLT. */
6115:
6116: int lease_mine_to_reallocate (struct lease *lease)
6117: {
6118: dhcp_failover_state_t *peer;
6119:
6120: if (lease && lease->pool &&
6121: (peer = lease->pool->failover_peer)) {
6122: switch (lease->binding_state) {
6123: case FTS_ACTIVE:
6124: /* ACTIVE leases may not be reallocated. */
6125: return 0;
6126:
6127: case FTS_FREE:
6128: case FTS_ABANDONED:
6129: /* FREE leases may only be allocated by the primary,
6130: * unless the secondary is acting in partner_down
6131: * state and stos+mclt or tsfp+mclt has expired,
6132: * whichever is greater.
6133: *
6134: * ABANDONED are treated the same as FREE for all
6135: * purposes here. Note that servers will only try
6136: * for ABANDONED leases as a last resort anyway.
6137: */
6138: if (peer -> i_am == primary)
6139: return 1;
6140:
6141: return(peer->service_state == service_partner_down &&
6142: ((lease->tsfp < peer->me.stos) ?
6143: (peer->me.stos + peer->mclt < cur_time) :
6144: (lease->tsfp + peer->mclt < cur_time)));
6145:
6146: case FTS_RESET:
6147: case FTS_RELEASED:
6148: case FTS_EXPIRED:
6149: /* These three lease states go onto the 'expired'
6150: * queue. Upon entry into partner-down state, this
6151: * queue of leases has their tsfp values modified
6152: * to equal stos+mclt, the point at which the server
6153: * is allowed to remove them from these transitional
6154: * states without an acknowledgement.
6155: *
6156: * Note that although tsfp has been possibly extended
6157: * past the actual tsfp we received from the peer, we
6158: * don't have to take any special action. Since tsfp
6159: * is now in the past (or now), we can guarantee that
6160: * this server will only allocate a lease time equal
6161: * to MCLT, rather than a TSFP-optimal lease, which is
6162: * the only danger for a lease in one of these states.
6163: */
6164: return((peer->service_state == service_partner_down) &&
6165: (lease->tsfp < cur_time));
6166:
6167: case FTS_BACKUP:
6168: /* Only the secondary may allocate BACKUP leases,
6169: * unless in partner_down state in which case at
6170: * least TSFP+MCLT or STOS+MCLT must have expired,
6171: * whichever is greater.
6172: */
6173: if (peer->i_am == secondary)
6174: return 1;
6175:
6176: return((peer->service_state == service_partner_down) &&
6177: ((lease->tsfp < peer->me.stos) ?
6178: (peer->me.stos + peer->mclt < cur_time) :
6179: (lease->tsfp + peer->mclt < cur_time)));
6180:
6181: default:
6182: /* All lease states appear above. */
6183: log_fatal("Impossible case at %s:%d.", MDL);
6184: break;
6185: }
6186: return 0;
6187: }
6188: if (lease)
6189: return(lease->binding_state == FTS_FREE ||
6190: lease->binding_state == FTS_BACKUP);
6191: else
6192: return 0;
6193: }
6194:
6195: static isc_result_t failover_message_reference (failover_message_t **mp,
6196: failover_message_t *m,
6197: const char *file, int line)
6198: {
6199: *mp = m;
6200: m -> refcnt++;
6201: return ISC_R_SUCCESS;
6202: }
6203:
6204: static isc_result_t failover_message_dereference (failover_message_t **mp,
6205: const char *file, int line)
6206: {
6207: failover_message_t *m;
6208: m = (*mp);
6209: m -> refcnt--;
6210: if (m -> refcnt == 0) {
6211: if (m -> next)
6212: failover_message_dereference (&m -> next,
6213: file, line);
6214: if (m -> chaddr.data)
6215: dfree (m -> chaddr.data, file, line);
6216: if (m -> client_identifier.data)
6217: dfree (m -> client_identifier.data, file, line);
6218: if (m -> hba.data)
6219: dfree (m -> hba.data, file, line);
6220: if (m -> message.data)
6221: dfree (m -> message.data, file, line);
6222: if (m -> reply_options.data)
6223: dfree (m -> reply_options.data, file, line);
6224: if (m -> request_options.data)
6225: dfree (m -> request_options.data, file, line);
6226: if (m -> vendor_class.data)
6227: dfree (m -> vendor_class.data, file, line);
6228: if (m -> vendor_options.data)
6229: dfree (m -> vendor_options.data, file, line);
6230: if (m -> ddns.data)
6231: dfree (m -> ddns.data, file, line);
6232: dfree (*mp, file, line);
6233: }
6234: *mp = 0;
6235: return ISC_R_SUCCESS;
6236: }
6237:
6238: OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
6239: dhcp_type_failover_state)
6240: OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
6241: dhcp_type_failover_listener)
6242: OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
6243: dhcp_type_failover_link)
6244: #endif /* defined (FAILOVER_PROTOCOL) */
6245:
6246: const char *binding_state_print (enum failover_state state)
6247: {
6248: switch (state) {
6249: case FTS_FREE:
6250: return "free";
6251: break;
6252:
6253: case FTS_ACTIVE:
6254: return "active";
6255: break;
6256:
6257: case FTS_EXPIRED:
6258: return "expired";
6259: break;
6260:
6261: case FTS_RELEASED:
6262: return "released";
6263: break;
6264:
6265: case FTS_ABANDONED:
6266: return "abandoned";
6267: break;
6268:
6269: case FTS_RESET:
6270: return "reset";
6271: break;
6272:
6273: case FTS_BACKUP:
6274: return "backup";
6275: break;
6276:
6277: default:
6278: return "unknown";
6279: break;
6280: }
6281: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>