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