1 /*
2  * Copyright (c) 2015-2018 Red Hat, Inc.
3  *
4  * All rights reserved.
5  *
6  * Author: Jan Friesse (jfriesse@redhat.com)
7  *
8  * This software licensed under BSD license, the text of which follows:
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * - Redistributions of source code must retain the above copyright notice,
14  *   this list of conditions and the following disclaimer.
15  * - Redistributions in binary form must reproduce the above copyright notice,
16  *   this list of conditions and the following disclaimer in the documentation
17  *   and/or other materials provided with the distribution.
18  * - Neither the name of the Red Hat, Inc. nor the names of its
19  *   contributors may be used to endorse or promote products derived from this
20  *   software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32  * THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <poll.h>
36 
37 #include "qdevice-model.h"
38 #include "qdevice-model-net.h"
39 #include "qdevice-log.h"
40 #include "qdevice-net-cast-vote-timer.h"
41 #include "qdevice-net-instance.h"
42 #include "qdevice-net-ipc-cmd.h"
43 #include "qdevice-net-algorithm.h"
44 #include "qdevice-net-heuristics.h"
45 #include "qdevice-net-poll.h"
46 #include "qdevice-net-send.h"
47 #include "qdevice-net-votequorum.h"
48 #include "qnet-config.h"
49 #include "nss-sock.h"
50 
51 int
qdevice_model_net_init(struct qdevice_instance * instance)52 qdevice_model_net_init(struct qdevice_instance *instance)
53 {
54 
55 	struct qdevice_net_instance *net_instance;
56 
57 	qdevice_log(LOG_DEBUG, "Initializing qdevice_net_instance");
58 	if (qdevice_net_instance_init_from_cmap(instance) != 0) {
59 		return (-1);
60 	}
61 
62 	net_instance = instance->model_data;
63 
64 	qdevice_log(LOG_DEBUG, "Registering algorithms");
65 	if (qdevice_net_algorithm_register_all() != 0) {
66 		return (-1);
67 	}
68 
69 	qdevice_log(LOG_DEBUG, "Initializing NSS");
70 	if (nss_sock_init_nss((net_instance->tls_supported != TLV_TLS_UNSUPPORTED ?
71 	    instance->advanced_settings->net_nss_db_dir : NULL)) != 0) {
72 		qdevice_log_nss(LOG_ERR, "Can't init nss");
73 		return (-1);
74 	}
75 
76 	if (qdevice_net_cast_vote_timer_update(net_instance, TLV_VOTE_ASK_LATER) != 0) {
77 		qdevice_log(LOG_ERR, "Can't update cast vote timer");
78 		return (-1);
79 	}
80 
81 	if (qdevice_net_algorithm_init(net_instance) != 0) {
82 		qdevice_log(LOG_ERR, "Algorithm init failed");
83 		return (-1);
84 	}
85 
86 	if (qdevice_net_heuristics_init(net_instance) != 0) {
87 		qdevice_log(LOG_ERR, "Can't initialize net heuristics");
88 		return (-1);
89 	}
90 
91 	return (0);
92 }
93 
94 int
qdevice_model_net_destroy(struct qdevice_instance * instance)95 qdevice_model_net_destroy(struct qdevice_instance *instance)
96 {
97 	struct qdevice_net_instance *net_instance;
98 
99 	net_instance = instance->model_data;
100 
101 	qdevice_log(LOG_DEBUG, "Destroying algorithm");
102 	qdevice_net_algorithm_destroy(net_instance);
103 
104 	qdevice_log(LOG_DEBUG, "Destroying qdevice_net_instance");
105 	qdevice_net_instance_destroy(net_instance);
106 
107 	qdevice_log(LOG_DEBUG, "Shutting down NSS");
108 	SSL_ClearSessionCache();
109 
110 	if (NSS_Shutdown() != SECSuccess) {
111 		qdevice_log_nss(LOG_WARNING, "Can't shutdown NSS");
112 	}
113 
114 	if (PR_Cleanup() != PR_SUCCESS) {
115 		qdevice_log_nss(LOG_WARNING, "Can't shutdown NSPR");
116 	}
117 
118 	free(net_instance);
119 
120 	return (0);
121 }
122 
123 static int
qdevice_model_net_timer_connect_timeout(void * data1,void * data2)124 qdevice_model_net_timer_connect_timeout(void *data1, void *data2)
125 {
126 	struct qdevice_net_instance *instance;
127 
128 	instance = (struct qdevice_net_instance *)data1;
129 
130 	qdevice_log(LOG_ERR, "Connect timeout");
131 
132 	instance->schedule_disconnect = 1;
133 
134 	instance->connect_timer = NULL;
135 	instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_CONNECT_TO_THE_SERVER;
136 
137 	return (0);
138 }
139 
140 static PRIntn
qdevice_model_net_get_af(const struct qdevice_net_instance * instance)141 qdevice_model_net_get_af(const struct qdevice_net_instance *instance)
142 {
143 	PRIntn af;
144 
145 	af = PR_AF_UNSPEC;
146 	if (instance->force_ip_version == 4) {
147 		af = PR_AF_INET;
148 	}
149 
150 	if (instance->force_ip_version == 6) {
151 		af = PR_AF_INET6;
152 	}
153 
154 	return (af);
155 }
156 
157 int
qdevice_model_net_run(struct qdevice_instance * instance)158 qdevice_model_net_run(struct qdevice_instance *instance)
159 {
160 	struct qdevice_net_instance *net_instance;
161 	int try_connect;
162 	int res;
163 	enum tlv_vote vote;
164 	int delay_before_reconnect;
165 	int ret_val;
166 
167 	net_instance = instance->model_data;
168 
169 	qdevice_log(LOG_DEBUG, "Executing qdevice-net");
170 
171 	ret_val = -1;
172 
173 	try_connect = 1;
174 	while (try_connect) {
175 		net_instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT;
176 		net_instance->socket = NULL;
177 
178 		net_instance->connect_timer = timer_list_add(&net_instance->main_timer_list,
179 			net_instance->connect_timeout, qdevice_model_net_timer_connect_timeout,
180 			(void *)net_instance, NULL);
181 
182 		if (net_instance->connect_timer == NULL) {
183 			qdevice_log(LOG_CRIT, "Can't schedule connect timer");
184 
185 			try_connect = 0;
186 			break;
187 		}
188 
189 		qdevice_log(LOG_DEBUG, "Trying connect to qnetd server %s:%u (timeout = %ums)",
190 		    net_instance->host_addr, net_instance->host_port, net_instance->connect_timeout);
191 
192 		res = nss_sock_non_blocking_client_init(net_instance->host_addr,
193 		    net_instance->host_port, qdevice_model_net_get_af(net_instance),
194 		    &net_instance->non_blocking_client);
195 		if (res == -1) {
196 			qdevice_log_nss(LOG_ERR, "Can't initialize non blocking client connection");
197 		}
198 
199 		res = nss_sock_non_blocking_client_try_next(&net_instance->non_blocking_client);
200 		if (res == -1) {
201 			qdevice_log_nss(LOG_ERR, "Can't connect to qnetd host");
202 			nss_sock_non_blocking_client_destroy(&net_instance->non_blocking_client);
203 		}
204 
205 		while (qdevice_net_poll(net_instance) == 0) {
206 		};
207 
208 		if (net_instance->connect_timer != NULL) {
209 			timer_list_delete(&net_instance->main_timer_list, net_instance->connect_timer);
210 			net_instance->connect_timer = NULL;
211 		}
212 
213 		if (net_instance->echo_request_timer != NULL) {
214 			timer_list_delete(&net_instance->main_timer_list, net_instance->echo_request_timer);
215 			net_instance->echo_request_timer = NULL;
216 		}
217 
218 		try_connect = qdevice_net_disconnect_reason_try_reconnect(net_instance->disconnect_reason);
219 
220 		/*
221 		 * Unpause cast vote timer, because if it is paused we cannot remove tracking
222 		 */
223 		qdevice_net_cast_vote_timer_set_paused(net_instance, 0);
224 
225 		vote = TLV_VOTE_NO_CHANGE;
226 
227 		if (qdevice_net_algorithm_disconnected(net_instance,
228 		    net_instance->disconnect_reason, &try_connect, &vote) != 0) {
229 			qdevice_log(LOG_ERR, "Algorithm returned error, force exit");
230 			return (-1);
231 		} else {
232 			qdevice_log(LOG_DEBUG, "Algorithm result vote is %s",
233 			    tlv_vote_to_str(vote));
234 		}
235 
236 		if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
237 			qdevice_log(LOG_ERR, "qdevice_model_net_run fatal error. "
238 			    " Can't update cast vote timer vote");
239 		}
240 
241 		if (qdevice_net_disconnect_reason_force_disconnect(net_instance->disconnect_reason)) {
242 			try_connect = 0;
243 		}
244 
245 		/*
246 		 * Return 0 only when local socket was closed -> regular exit
247 		 */
248 		if (net_instance->disconnect_reason == QDEVICE_NET_DISCONNECT_REASON_LOCAL_SOCKET_CLOSED) {
249 			ret_val = 0;
250 		}
251 
252 		if (net_instance->socket != NULL) {
253 			if (PR_Close(net_instance->socket) != PR_SUCCESS) {
254 				qdevice_log_nss(LOG_WARNING, "Unable to close connection");
255 			}
256 			net_instance->socket = NULL;
257 		}
258 
259 		if (!net_instance->non_blocking_client.destroyed) {
260 			nss_sock_non_blocking_client_destroy(&net_instance->non_blocking_client);
261 		}
262 
263 		if (net_instance->non_blocking_client.socket != NULL) {
264 			if (PR_Close(net_instance->non_blocking_client.socket) != PR_SUCCESS) {
265 				qdevice_log_nss(LOG_WARNING, "Unable to close non-blocking client connection");
266 			}
267 			net_instance->non_blocking_client.socket = NULL;
268 		}
269 
270 		if (try_connect &&
271 		    net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT) {
272 			/*
273 			 * Give qnetd server a little time before reconnect
274 			 */
275 			delay_before_reconnect = random() %
276 			    (int)(net_instance->cast_vote_timer_interval * 0.9);
277 
278 			qdevice_log(LOG_DEBUG, "Sleeping for %u ms before reconnect",
279 			    delay_before_reconnect);
280 			(void)poll(NULL, 0, delay_before_reconnect);
281 		}
282 
283 
284 		qdevice_net_instance_clean(net_instance);
285 	}
286 
287 	return (ret_val);
288 }
289 
290 /*
291  * Called when cmap reload (or nodelist) was requested.
292  *
293  * nlist is node list
294  * config_version is valid only if config_version_set != 0
295  *
296  * Should return 0 if processing should continue or -1 to call exit
297  */
298 int
qdevice_model_net_config_node_list_changed(struct qdevice_instance * instance,const struct node_list * nlist,int config_version_set,uint64_t config_version)299 qdevice_model_net_config_node_list_changed(struct qdevice_instance *instance,
300     const struct node_list *nlist, int config_version_set, uint64_t config_version)
301 {
302 	struct qdevice_net_instance *net_instance;
303 	int send_node_list;
304 	enum tlv_vote vote;
305 
306 	net_instance = instance->model_data;
307 
308 	if (net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
309 		/*
310 		 * Nodelist changed, but connection to qnetd not initiated yet.
311 		 */
312 		send_node_list = 0;
313 
314 		if (net_instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
315 			vote = TLV_VOTE_NACK;
316 		} else {
317 			vote = TLV_VOTE_NO_CHANGE;
318 		}
319 	} else {
320 		send_node_list = 1;
321 		vote = TLV_VOTE_NO_CHANGE;
322 	}
323 
324 	if (qdevice_net_algorithm_config_node_list_changed(net_instance, nlist, config_version_set,
325 	    config_version, &send_node_list, &vote) != 0) {
326 		qdevice_log(LOG_ERR, "Algorithm returned error, Disconnecting");
327 
328 		net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_CONFIG_NODE_LIST_CHANGED_ERR;
329 		net_instance->schedule_disconnect = 1;
330 
331 		return (0);
332 	} else {
333 		qdevice_log(LOG_DEBUG, "Algorithm decided to %s node list and result vote is %s",
334 		    (send_node_list ? "send" : "not send"), tlv_vote_to_str(vote));
335 	}
336 
337 	if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
338 		qdevice_log(LOG_CRIT, "qdevice_model_net_config_node_list_changed fatal error. "
339 				" Can't update cast vote timer vote");
340 		net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
341 		net_instance->schedule_disconnect = 1;
342 
343 		return (0);
344 	}
345 
346 	if (send_node_list) {
347 		if (qdevice_net_send_config_node_list(net_instance, nlist, config_version_set,
348 		    config_version, 0) != 0) {
349 			net_instance->schedule_disconnect = 1;
350 			net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
351 
352 			return (0);
353 		}
354 	}
355 
356 	return (0);
357 }
358 
359 /*
360  * Called when cmap reload (or nodelist) was requested, but it was not possible to
361  * get node list.
362  *
363  * Should return 0 if processing should continue or -1 to call exit
364  */
365 int
qdevice_model_net_get_config_node_list_failed(struct qdevice_instance * instance)366 qdevice_model_net_get_config_node_list_failed(struct qdevice_instance *instance)
367 {
368 	struct qdevice_net_instance *net_instance;
369 
370 	net_instance = instance->model_data;
371 
372 	net_instance->schedule_disconnect = 1;
373 	net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
374 
375 	return (0);
376 }
377 
378 int
qdevice_model_net_votequorum_quorum_notify(struct qdevice_instance * instance,uint32_t quorate,uint32_t node_list_entries,votequorum_node_t node_list[])379 qdevice_model_net_votequorum_quorum_notify(struct qdevice_instance *instance,
380     uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[])
381 {
382 	struct qdevice_net_instance *net_instance;
383 	int send_node_list;
384 	enum tlv_vote vote;
385 
386 	net_instance = instance->model_data;
387 
388 	if (net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
389 		/*
390 		 * Nodelist changed, but connection to qnetd not initiated yet.
391 		 */
392 		send_node_list = 0;
393 
394 		if (net_instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
395 			vote = TLV_VOTE_NACK;
396 		} else {
397 			vote = TLV_VOTE_NO_CHANGE;
398 		}
399 	} else {
400 		send_node_list = 1;
401 		vote = TLV_VOTE_NO_CHANGE;
402 	}
403 
404 	if (qdevice_net_algorithm_votequorum_quorum_notify(net_instance, quorate,
405 	    node_list_entries, node_list, &send_node_list, &vote) != 0) {
406 		qdevice_log(LOG_ERR, "Algorithm returned error. Disconnecting.");
407 
408 		net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_QUORUM_NOTIFY_ERR;
409 		net_instance->schedule_disconnect = 1;
410 
411 		return (0);
412 	} else {
413 		qdevice_log(LOG_DEBUG, "Algorithm decided to %s list and result vote is %s",
414 		    (send_node_list ? "send" : "not send"), tlv_vote_to_str(vote));
415 	}
416 
417 	if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
418 		qdevice_log(LOG_CRIT, "qdevice_model_net_votequorum_quorum_notify fatal error. "
419 				" Can't update cast vote timer vote");
420 		net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
421 		net_instance->schedule_disconnect = 1;
422 
423 		return (0);
424 	}
425 
426 	if (send_node_list) {
427 		if (qdevice_net_send_quorum_node_list(net_instance,
428 		    (quorate ? TLV_QUORATE_QUORATE : TLV_QUORATE_INQUORATE),
429 		    node_list_entries, node_list) != 0) {
430 			/*
431 			 * Fatal error -> schedule disconnect
432 			 */
433 			net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
434 			net_instance->schedule_disconnect = 1;
435 
436 			return (0);
437 		}
438 	}
439 
440 	return (0);
441 }
442 
443 int
qdevice_model_net_votequorum_node_list_heuristics_notify(struct qdevice_instance * instance,votequorum_ring_id_t votequorum_ring_id,uint32_t node_list_entries,uint32_t node_list[],enum qdevice_heuristics_exec_result heuristics_exec_result)444 qdevice_model_net_votequorum_node_list_heuristics_notify(struct qdevice_instance *instance,
445     votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[],
446     enum qdevice_heuristics_exec_result heuristics_exec_result)
447 {
448 	struct qdevice_net_instance *net_instance;
449 	struct tlv_ring_id tlv_rid;
450 	enum tlv_vote vote;
451 	enum tlv_heuristics heuristics;
452 	int send_node_list;
453 
454 	net_instance = instance->model_data;
455 
456 	qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid, &votequorum_ring_id);
457 	heuristics = qdevice_net_heuristics_exec_result_to_tlv(heuristics_exec_result);
458 
459 	if (net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
460 		/*
461 		 * Nodelist changed, but connection to qnetd not initiated yet.
462 		 */
463 		send_node_list = 0;
464 
465 		if (net_instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
466 			vote = TLV_VOTE_NACK;
467 		} else {
468 			vote = TLV_VOTE_NO_CHANGE;
469 		}
470 	} else {
471 		send_node_list = 1;
472 		vote = TLV_VOTE_WAIT_FOR_REPLY;
473 	}
474 
475 	if (qdevice_net_algorithm_votequorum_node_list_heuristics_notify(net_instance, &tlv_rid,
476 	    node_list_entries, node_list, &send_node_list, &vote, &heuristics) != 0) {
477 		qdevice_log(LOG_ERR, "Algorithm returned error. Disconnecting.");
478 
479 		net_instance->disconnect_reason =
480 		    QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_NODE_LIST_HEURISTICS_NOTIFY_ERR;
481 		net_instance->schedule_disconnect = 1;
482 
483 		return (0);
484 	} else {
485 		qdevice_log(LOG_DEBUG, "Algorithm decided to %s list, result vote is %s and heuristics is %s",
486 		    (send_node_list ? "send" : "not send"), tlv_vote_to_str(vote),
487 		    tlv_heuristics_to_str(heuristics));
488 	}
489 
490 	if (send_node_list) {
491 		if (qdevice_net_send_membership_node_list(net_instance, &tlv_rid,
492 		    node_list_entries, node_list, heuristics) != 0) {
493 			/*
494 			 * Fatal error -> schedule disconnect
495 			 */
496 			net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
497 			net_instance->schedule_disconnect = 1;
498 
499 			return (0);
500 		}
501 	}
502 
503 	/*
504 	 * Unpause cast vote timer
505 	 */
506 	qdevice_net_cast_vote_timer_set_paused(net_instance, 0);
507 
508 	if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
509 		qdevice_log(LOG_CRIT, "qdevice_model_net_votequorum_node_list_notify fatal error "
510 		    "Can't update cast vote timer");
511 		net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
512 		net_instance->schedule_disconnect = 1;
513 
514 		return (0);
515 	}
516 
517 	net_instance->latest_vq_heuristics_result = heuristics;
518 	net_instance->latest_heuristics_result = heuristics;
519 
520 	if (qdevice_net_heuristics_schedule_timer(net_instance) != 0) {
521 		return (0);
522 	}
523 
524 	return (0);
525 }
526 
527 int
qdevice_model_net_votequorum_node_list_notify(struct qdevice_instance * instance,votequorum_ring_id_t votequorum_ring_id,uint32_t node_list_entries,uint32_t node_list[])528 qdevice_model_net_votequorum_node_list_notify(struct qdevice_instance *instance,
529     votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[])
530 {
531 	struct qdevice_net_instance *net_instance;
532 	struct tlv_ring_id tlv_rid;
533 	enum tlv_vote vote;
534 	int pause_cast_vote_timer;
535 
536 	net_instance = instance->model_data;
537 
538 	/*
539 	 * Stop regular heuristics till qdevice_model_net_votequorum_node_list_heuristics_notify
540 	 * is called
541 	 */
542 	if (qdevice_net_heuristics_stop_timer(net_instance) != 0) {
543 		return (0);
544 	}
545 
546 	pause_cast_vote_timer = 1;
547 	vote = TLV_VOTE_NO_CHANGE;
548 
549 	if (net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS &&
550 	    net_instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
551 		/*
552 		 * Nodelist changed and vote timer still votes ACK. It's needed to start voting
553 		 * NACK.
554 		 */
555 		if (net_instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
556 			vote = TLV_VOTE_NACK;
557 		}
558 	}
559 
560 	qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid, &votequorum_ring_id);
561 
562 	if (qdevice_net_algorithm_votequorum_node_list_notify(net_instance, &tlv_rid,
563 	    node_list_entries, node_list, &pause_cast_vote_timer, &vote) != 0) {
564 		qdevice_log(LOG_ERR, "Algorithm returned error. Disconnecting.");
565 
566 		net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_NODE_LIST_NOTIFY_ERR;
567 		net_instance->schedule_disconnect = 1;
568 
569 		return (0);
570 	} else {
571 		qdevice_log(LOG_DEBUG, "Algorithm decided to %s cast vote timer and result vote is %s ",
572 		    (pause_cast_vote_timer ? "pause" : "not pause"), tlv_vote_to_str(vote));
573 	}
574 
575 	if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
576 		qdevice_log(LOG_CRIT, "qdevice_model_net_votequorum_node_list_notify fatal error "
577 		    "Can't update cast vote timer");
578 		net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
579 		net_instance->schedule_disconnect = 1;
580 
581 		return (0);
582 	}
583 
584 	qdevice_net_cast_vote_timer_set_paused(net_instance, pause_cast_vote_timer);
585 
586 	return (0);
587 }
588 
589 int
qdevice_model_net_votequorum_expected_votes_notify(struct qdevice_instance * instance,uint32_t expected_votes)590 qdevice_model_net_votequorum_expected_votes_notify(struct qdevice_instance *instance,
591     uint32_t expected_votes)
592 {
593 	struct qdevice_net_instance *net_instance;
594 	enum tlv_vote vote;
595 
596 	net_instance = instance->model_data;
597 
598 	qdevice_log(LOG_DEBUG, "qdevice_model_net_votequorum_expected_votes_notify"
599 	    " (expected votes old=%"PRIu32" / new=%"PRIu32")",
600 	    net_instance->qdevice_instance_ptr->vq_expected_votes, expected_votes);
601 
602 	vote = TLV_VOTE_NO_CHANGE;
603 
604 	if (qdevice_net_algorithm_votequorum_expected_votes_notify(net_instance, expected_votes,
605 	    &vote) != 0) {
606 		qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
607 
608 		net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_EXPECTED_VOTES_NOTIFY_ERR;
609 		net_instance->schedule_disconnect = 1;
610 
611 		return (0);
612 	} else {
613 		qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(vote));
614 	}
615 
616 	if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
617 		qdevice_log(LOG_CRIT, "qdevice_model_net_votequorum_expected_votes_notify fatal error. "
618 				" Can't update cast vote timer vote");
619 		net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
620 		net_instance->schedule_disconnect = 1;
621 
622 		return (0);
623 	}
624 
625 	return (0);
626 }
627 
628 int
qdevice_model_net_cmap_changed(struct qdevice_instance * instance,const struct qdevice_cmap_change_events * events)629 qdevice_model_net_cmap_changed(struct qdevice_instance *instance,
630     const struct qdevice_cmap_change_events *events)
631 {
632 	struct qdevice_net_instance *net_instance;
633 	enum qdevice_heuristics_mode active_heuristics_mode;
634 	int heuristics_enabled;
635 
636 	net_instance = instance->model_data;
637 
638 	if (events->heuristics) {
639 		active_heuristics_mode = instance->heuristics_instance.mode;
640 		heuristics_enabled = (active_heuristics_mode == QDEVICE_HEURISTICS_MODE_ENABLED ||
641 		    active_heuristics_mode == QDEVICE_HEURISTICS_MODE_SYNC);
642 
643 		if (net_instance->state == QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS &&
644 		    !net_instance->server_supports_heuristics && heuristics_enabled) {
645 			qdevice_log(LOG_ERR, "Heuristics are enabled but not supported by the server");
646 
647 			net_instance->disconnect_reason =
648 			    QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_OPT;
649 
650 			net_instance->schedule_disconnect = 1;
651 
652 			return (0);
653 		}
654 
655 		if (qdevice_net_heuristics_schedule_timer(net_instance) != 0) {
656 			return (0);
657 		}
658 	}
659 
660 	return (0);
661 }
662 
663 int
qdevice_model_net_ipc_cmd_status(struct qdevice_instance * instance,struct dynar * outbuf,int verbose)664 qdevice_model_net_ipc_cmd_status(struct qdevice_instance *instance,
665     struct dynar *outbuf, int verbose)
666 {
667 	struct qdevice_net_instance *net_instance;
668 
669 	net_instance = instance->model_data;
670 
671 	if (!qdevice_net_ipc_cmd_status(net_instance, outbuf, verbose)) {
672 		return (-1);
673 	}
674 
675 	return (0);
676 }
677 
678 static struct qdevice_model qdevice_model_net = {
679 	.name					= "net",
680 	.init					= qdevice_model_net_init,
681 	.destroy				= qdevice_model_net_destroy,
682 	.run					= qdevice_model_net_run,
683 	.get_config_node_list_failed		= qdevice_model_net_get_config_node_list_failed,
684 	.config_node_list_changed		= qdevice_model_net_config_node_list_changed,
685 	.votequorum_quorum_notify		= qdevice_model_net_votequorum_quorum_notify,
686 	.votequorum_node_list_notify		= qdevice_model_net_votequorum_node_list_notify,
687 	.votequorum_node_list_heuristics_notify	= qdevice_model_net_votequorum_node_list_heuristics_notify,
688 	.votequorum_expected_votes_notify	= qdevice_model_net_votequorum_expected_votes_notify,
689 	.cmap_changed				= qdevice_model_net_cmap_changed,
690 	.ipc_cmd_status				= qdevice_model_net_ipc_cmd_status,
691 };
692 
693 int
qdevice_model_net_register(void)694 qdevice_model_net_register(void)
695 {
696 	return (qdevice_model_register(QDEVICE_MODEL_TYPE_NET, &qdevice_model_net));
697 }
698