]>
Commit | Line | Data |
---|---|---|
9a1955a7 JF |
1 | /* |
2 | * Copyright (c) 2015-2017 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 | ||
c5081836 JF |
35 | #include <limits.h> |
36 | ||
9a1955a7 JF |
37 | #include "qdevice-config.h" |
38 | #include "qdevice-log.h" | |
39 | #include "qdevice-net-instance.h" | |
40 | #include "qnet-config.h" | |
41 | #include "utils.h" | |
42 | #include "qdevice-net-poll-array-user-data.h" | |
43 | #include "qdevice-ipc.h" | |
44 | ||
45 | /* | |
46 | * Needed for creating nspr handle from unix fd | |
47 | */ | |
48 | #include <private/pprio.h> | |
49 | ||
50 | int | |
51 | qdevice_net_instance_init(struct qdevice_net_instance *instance, | |
52 | enum tlv_tls_supported tls_supported, | |
53 | enum tlv_decision_algorithm_type decision_algorithm, uint32_t heartbeat_interval, | |
54 | uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval, | |
55 | const char *host_addr, uint16_t host_port, const char *cluster_name, | |
56 | const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout, | |
57 | int force_ip_version, int cmap_fd, int votequorum_fd, int local_socket_fd, | |
58 | const struct qdevice_advanced_settings *advanced_settings, | |
59 | int heuristics_pipe_cmd_send_fd, int heuristics_pipe_cmd_recv_fd, | |
60 | int heuristics_pipe_log_recv_fd) | |
61 | { | |
62 | ||
63 | memset(instance, 0, sizeof(*instance)); | |
64 | ||
65 | instance->advanced_settings = advanced_settings; | |
66 | instance->decision_algorithm = decision_algorithm; | |
67 | instance->heartbeat_interval = heartbeat_interval; | |
68 | instance->sync_heartbeat_interval = sync_heartbeat_interval; | |
69 | instance->cast_vote_timer_interval = cast_vote_timer_interval; | |
70 | instance->cast_vote_timer = NULL; | |
71 | instance->host_addr = host_addr; | |
72 | instance->host_port = host_port; | |
73 | instance->cluster_name = cluster_name; | |
74 | instance->connect_timeout = connect_timeout; | |
75 | instance->last_msg_seq_num = 1; | |
76 | instance->echo_request_expected_msg_seq_num = 1; | |
77 | instance->echo_reply_received_msg_seq_num = 1; | |
78 | instance->force_ip_version = force_ip_version; | |
79 | instance->last_echo_reply_received_time = ((time_t) -1); | |
80 | instance->connected_since_time = ((time_t) -1); | |
81 | ||
82 | memcpy(&instance->tie_breaker, tie_breaker, sizeof(*tie_breaker)); | |
83 | ||
84 | dynar_init(&instance->receive_buffer, advanced_settings->net_initial_msg_receive_size); | |
85 | ||
86 | send_buffer_list_init(&instance->send_buffer_list, advanced_settings->net_max_send_buffers, | |
87 | advanced_settings->net_initial_msg_send_size); | |
88 | ||
89 | timer_list_init(&instance->main_timer_list); | |
90 | ||
91 | pr_poll_array_init(&instance->poll_array, sizeof(struct qdevice_net_poll_array_user_data)); | |
92 | ||
93 | instance->tls_supported = tls_supported; | |
94 | ||
95 | if ((instance->cmap_poll_fd = PR_CreateSocketPollFd(cmap_fd)) == NULL) { | |
96 | qdevice_log_nss(LOG_CRIT, "Can't create NSPR cmap poll fd"); | |
97 | return (-1); | |
98 | } | |
99 | ||
100 | if ((instance->votequorum_poll_fd = PR_CreateSocketPollFd(votequorum_fd)) == NULL) { | |
101 | qdevice_log_nss(LOG_CRIT, "Can't create NSPR votequorum poll fd"); | |
102 | return (-1); | |
103 | } | |
104 | ||
105 | if ((instance->ipc_socket_poll_fd = PR_CreateSocketPollFd(local_socket_fd)) == NULL) { | |
106 | qdevice_log_nss(LOG_CRIT, "Can't create NSPR IPC socket poll fd"); | |
107 | return (-1); | |
108 | } | |
109 | ||
110 | if ((instance->heuristics_pipe_cmd_send_poll_fd = | |
111 | PR_CreateSocketPollFd(heuristics_pipe_cmd_send_fd)) == NULL) { | |
112 | qdevice_log_nss(LOG_CRIT, "Can't create NSPR heuristics pipe command send poll fd"); | |
113 | return (-1); | |
114 | } | |
115 | ||
116 | if ((instance->heuristics_pipe_cmd_recv_poll_fd = | |
117 | PR_CreateSocketPollFd(heuristics_pipe_cmd_recv_fd)) == NULL) { | |
118 | qdevice_log_nss(LOG_CRIT, "Can't create NSPR heuristics pipe command recv poll fd"); | |
119 | return (-1); | |
120 | } | |
121 | ||
122 | if ((instance->heuristics_pipe_log_recv_poll_fd = | |
123 | PR_CreateSocketPollFd(heuristics_pipe_log_recv_fd)) == NULL) { | |
124 | qdevice_log_nss(LOG_CRIT, "Can't create NSPR heuristics pipe log recv poll fd"); | |
125 | return (-1); | |
126 | } | |
127 | ||
128 | return (0); | |
129 | } | |
130 | ||
131 | void | |
132 | qdevice_net_instance_clean(struct qdevice_net_instance *instance) | |
133 | { | |
134 | ||
135 | dynar_clean(&instance->receive_buffer); | |
136 | ||
137 | send_buffer_list_free(&instance->send_buffer_list); | |
138 | ||
139 | instance->skipping_msg = 0; | |
140 | instance->msg_already_received_bytes = 0; | |
141 | instance->echo_request_expected_msg_seq_num = instance->echo_reply_received_msg_seq_num; | |
142 | instance->using_tls = 0; | |
143 | instance->tls_client_cert_sent = 0; | |
144 | instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT; | |
145 | ||
146 | instance->schedule_disconnect = 0; | |
147 | instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNDEFINED; | |
148 | instance->last_echo_reply_received_time = ((time_t) -1); | |
149 | instance->connected_since_time = ((time_t) -1); | |
150 | } | |
151 | ||
152 | int | |
153 | qdevice_net_instance_destroy(struct qdevice_net_instance *instance) | |
154 | { | |
155 | struct unix_socket_client *ipc_client; | |
156 | const struct unix_socket_client_list *ipc_client_list; | |
157 | struct qdevice_ipc_user_data *qdevice_ipc_user_data; | |
158 | PRFileDesc *prfd; | |
159 | ||
160 | ipc_client_list = &instance->qdevice_instance_ptr->local_ipc.clients; | |
161 | ||
162 | TAILQ_FOREACH(ipc_client, ipc_client_list, entries) { | |
163 | qdevice_ipc_user_data = (struct qdevice_ipc_user_data *)ipc_client->user_data; | |
164 | prfd = (PRFileDesc *)qdevice_ipc_user_data->model_data; | |
165 | ||
166 | if (PR_DestroySocketPollFd(prfd) != PR_SUCCESS) { | |
167 | qdevice_log_nss(LOG_WARNING, "Unable to destroy client IPC poll socket fd"); | |
168 | } | |
169 | } | |
170 | ||
171 | dynar_destroy(&instance->receive_buffer); | |
172 | ||
173 | send_buffer_list_free(&instance->send_buffer_list); | |
174 | ||
175 | pr_poll_array_destroy(&instance->poll_array); | |
176 | ||
177 | timer_list_free(&instance->main_timer_list); | |
178 | ||
179 | free((void *)instance->cluster_name); | |
180 | free((void *)instance->host_addr); | |
181 | ||
182 | if (PR_DestroySocketPollFd(instance->votequorum_poll_fd) != PR_SUCCESS) { | |
183 | qdevice_log_nss(LOG_WARNING, "Unable to close votequorum connection fd"); | |
184 | } | |
185 | ||
186 | if (PR_DestroySocketPollFd(instance->cmap_poll_fd) != PR_SUCCESS) { | |
187 | qdevice_log_nss(LOG_WARNING, "Unable to close votequorum connection fd"); | |
188 | } | |
189 | ||
190 | if (PR_DestroySocketPollFd(instance->ipc_socket_poll_fd) != PR_SUCCESS) { | |
191 | qdevice_log_nss(LOG_WARNING, "Unable to close local socket poll fd"); | |
192 | } | |
193 | ||
194 | if (PR_DestroySocketPollFd(instance->heuristics_pipe_cmd_send_poll_fd) != PR_SUCCESS) { | |
195 | qdevice_log_nss(LOG_WARNING, "Unable to close heuristics pipe command send poll fd"); | |
196 | return (-1); | |
197 | } | |
198 | ||
199 | if (PR_DestroySocketPollFd(instance->heuristics_pipe_cmd_recv_poll_fd) != PR_SUCCESS) { | |
200 | qdevice_log_nss(LOG_WARNING, "Unable to close heuristics pipe command recv poll fd"); | |
201 | return (-1); | |
202 | } | |
203 | ||
204 | if (PR_DestroySocketPollFd(instance->heuristics_pipe_log_recv_poll_fd) != PR_SUCCESS) { | |
205 | qdevice_log_nss(LOG_WARNING, "Unable to close heuristics pipe log recv poll fd"); | |
206 | return (-1); | |
207 | } | |
208 | ||
209 | return (0); | |
210 | } | |
211 | ||
212 | int | |
213 | qdevice_net_instance_init_from_cmap(struct qdevice_instance *instance) | |
214 | { | |
215 | char *str; | |
216 | cmap_handle_t cmap_handle; | |
217 | enum tlv_tls_supported tls_supported; | |
218 | int i; | |
c5081836 | 219 | long long int lli; |
9a1955a7 JF |
220 | enum tlv_decision_algorithm_type decision_algorithm; |
221 | struct tlv_tie_breaker tie_breaker; | |
222 | uint32_t heartbeat_interval; | |
223 | uint32_t sync_heartbeat_interval; | |
224 | uint32_t cast_vote_timer_interval; | |
225 | char *host_addr; | |
226 | int host_port; | |
9a1955a7 JF |
227 | char *cluster_name; |
228 | uint32_t connect_timeout; | |
229 | struct qdevice_net_instance *net_instance; | |
230 | int force_ip_version; | |
231 | ||
232 | cmap_handle = instance->cmap_handle; | |
233 | ||
234 | net_instance = malloc(sizeof(*net_instance)); | |
235 | if (net_instance == NULL) { | |
236 | qdevice_log(LOG_ERR, "Can't alloc qdevice_net_instance"); | |
237 | return (-1); | |
238 | } | |
239 | ||
240 | /* | |
241 | * Check tls | |
242 | */ | |
243 | tls_supported = QDEVICE_NET_DEFAULT_TLS_SUPPORTED; | |
244 | ||
245 | if (cmap_get_string(cmap_handle, "quorum.device.net.tls", &str) == CS_OK) { | |
246 | if ((i = utils_parse_bool_str(str)) == -1) { | |
247 | if (strcasecmp(str, "required") != 0) { | |
248 | free(str); | |
249 | qdevice_log(LOG_ERR, "quorum.device.net.tls value is not valid."); | |
250 | ||
251 | goto error_free_instance; | |
252 | } else { | |
253 | tls_supported = TLV_TLS_REQUIRED; | |
254 | } | |
255 | } else { | |
256 | if (i == 1) { | |
257 | tls_supported = TLV_TLS_SUPPORTED; | |
258 | } else { | |
259 | tls_supported = TLV_TLS_UNSUPPORTED; | |
260 | } | |
261 | } | |
262 | ||
263 | free(str); | |
264 | } | |
265 | ||
266 | /* | |
267 | * Host | |
268 | */ | |
269 | if (cmap_get_string(cmap_handle, "quorum.device.net.host", &str) != CS_OK) { | |
270 | qdevice_log(LOG_ERR, "Qdevice net daemon address is not defined (quorum.device.net.host)"); | |
271 | goto error_free_instance; | |
272 | } | |
273 | host_addr = str; | |
274 | ||
275 | if (cmap_get_string(cmap_handle, "quorum.device.net.port", &str) == CS_OK) { | |
5aaabea8 | 276 | if (utils_strtonum(str, 1, UINT16_MAX, &lli) == -1) { |
c5081836 | 277 | qdevice_log(LOG_ERR, "quorum.device.net.port must be in range 1-%u", UINT16_MAX); |
9a1955a7 JF |
278 | free(str); |
279 | goto error_free_host_addr; | |
280 | } | |
281 | free(str); | |
c5081836 JF |
282 | |
283 | host_port = lli; | |
9a1955a7 JF |
284 | } else { |
285 | host_port = QNETD_DEFAULT_HOST_PORT; | |
286 | } | |
287 | ||
288 | /* | |
289 | * Cluster name | |
290 | */ | |
291 | if (cmap_get_string(cmap_handle, "totem.cluster_name", &str) != CS_OK) { | |
292 | qdevice_log(LOG_ERR, "Cluster name (totem.cluster_name) has to be defined."); | |
293 | goto error_free_host_addr; | |
294 | } | |
295 | cluster_name = str; | |
296 | ||
297 | /* | |
298 | * Adjust qdevice timeouts to better suit qnetd | |
299 | */ | |
300 | cast_vote_timer_interval = instance->heartbeat_interval * 0.5; | |
301 | heartbeat_interval = instance->heartbeat_interval * 0.8; | |
302 | if (heartbeat_interval < instance->advanced_settings->net_heartbeat_interval_min) { | |
303 | qdevice_log(LOG_WARNING, "Heartbeat interval too small %"PRIu32". Adjusting to %"PRIu32".", | |
304 | heartbeat_interval, instance->advanced_settings->net_heartbeat_interval_min); | |
305 | heartbeat_interval = instance->advanced_settings->net_heartbeat_interval_min; | |
306 | } | |
307 | if (heartbeat_interval > instance->advanced_settings->net_heartbeat_interval_max) { | |
308 | qdevice_log(LOG_WARNING, "Heartbeat interval too big %"PRIu32". Adjusting to %"PRIu32".", | |
309 | heartbeat_interval, instance->advanced_settings->net_heartbeat_interval_max); | |
310 | heartbeat_interval = instance->advanced_settings->net_heartbeat_interval_max; | |
311 | } | |
312 | sync_heartbeat_interval = instance->sync_heartbeat_interval * 0.8; | |
313 | ||
314 | /* | |
315 | * Choose decision algorithm | |
316 | */ | |
317 | if (cmap_get_string(cmap_handle, "quorum.device.net.algorithm", &str) != CS_OK) { | |
318 | decision_algorithm = QDEVICE_NET_DEFAULT_ALGORITHM; | |
319 | } else { | |
320 | if (strcmp(str, "test") == 0) { | |
321 | decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_TEST; | |
322 | } else if (strcmp(str, "ffsplit") == 0) { | |
323 | decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_FFSPLIT; | |
324 | } else if (strcmp(str, "2nodelms") == 0) { | |
325 | decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_2NODELMS; | |
326 | } else if (strcmp(str, "lms") == 0) { | |
327 | decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_LMS; | |
328 | } else { | |
329 | qdevice_log(LOG_ERR, "Unknown decision algorithm %s", str); | |
330 | free(str); | |
331 | goto error_free_cluster_name; | |
332 | } | |
333 | ||
334 | free(str); | |
335 | } | |
336 | ||
337 | if (decision_algorithm == TLV_DECISION_ALGORITHM_TYPE_TEST && | |
338 | !instance->advanced_settings->net_test_algorithm_enabled) { | |
339 | qdevice_log(LOG_ERR, "Test algorithm is not enabled. You can force enable it by " | |
340 | "passing -S net_test_algorithm_enabled=on to %s command", QDEVICE_PROGRAM_NAME); | |
341 | ||
342 | goto error_free_cluster_name; | |
343 | } | |
344 | /* | |
345 | * Load tie_breaker mode | |
346 | */ | |
347 | memset(&tie_breaker, 0, sizeof(tie_breaker)); | |
348 | ||
349 | if (cmap_get_string(cmap_handle, "quorum.device.net.tie_breaker", &str) != CS_OK) { | |
350 | tie_breaker.mode = QDEVICE_NET_DEFAULT_TIE_BREAKER_MODE; | |
351 | } else { | |
352 | if (strcmp(str, "lowest") == 0) { | |
353 | tie_breaker.mode = TLV_TIE_BREAKER_MODE_LOWEST; | |
354 | } else if (strcmp(str, "highest") == 0) { | |
355 | tie_breaker.mode = TLV_TIE_BREAKER_MODE_HIGHEST; | |
356 | } else { | |
c5081836 | 357 | if (utils_strtonum(str, 1, UINT32_MAX, &lli) == -1) { |
9a1955a7 JF |
358 | qdevice_log(LOG_ERR, "tie_breaker must be lowest|highest|valid_node_id"); |
359 | free(str); | |
360 | goto error_free_cluster_name; | |
361 | } | |
362 | ||
363 | tie_breaker.mode = TLV_TIE_BREAKER_MODE_NODE_ID; | |
c5081836 | 364 | tie_breaker.node_id = lli; |
9a1955a7 JF |
365 | } |
366 | ||
367 | free(str); | |
368 | } | |
369 | ||
370 | /* | |
371 | * Get connect timeout | |
372 | */ | |
373 | if (cmap_get_string(cmap_handle, "quorum.device.net.connect_timeout", &str) != CS_OK) { | |
374 | connect_timeout = heartbeat_interval; | |
375 | } else { | |
c5081836 JF |
376 | if (utils_strtonum(str, instance->advanced_settings->net_min_connect_timeout, |
377 | instance->advanced_settings->net_max_connect_timeout, &lli) == -1) { | |
9a1955a7 JF |
378 | qdevice_log(LOG_ERR, "connect_timeout must be valid number in " |
379 | "range <%"PRIu32",%"PRIu32">", | |
380 | instance->advanced_settings->net_min_connect_timeout, | |
381 | instance->advanced_settings->net_max_connect_timeout); | |
382 | free(str); | |
383 | goto error_free_cluster_name; | |
384 | } | |
385 | ||
c5081836 | 386 | connect_timeout = (uint32_t)lli; |
9a1955a7 JF |
387 | |
388 | free(str); | |
389 | } | |
390 | ||
391 | if (cmap_get_string(cmap_handle, "quorum.device.net.force_ip_version", &str) != CS_OK) { | |
392 | force_ip_version = 0; | |
393 | } else { | |
c5081836 JF |
394 | if ((utils_strtonum(str, 0, 6, &lli) == -1) || |
395 | (lli != 0 && lli != 4 && lli != 6)) { | |
9a1955a7 JF |
396 | qdevice_log(LOG_ERR, "force_ip_version must be one of 0|4|6"); |
397 | free(str); | |
398 | goto error_free_cluster_name; | |
399 | } | |
400 | ||
c5081836 | 401 | force_ip_version = lli; |
9a1955a7 JF |
402 | |
403 | free(str); | |
404 | } | |
405 | ||
406 | /* | |
407 | * Really initialize instance | |
408 | */ | |
409 | if (qdevice_net_instance_init(net_instance, | |
410 | tls_supported, decision_algorithm, | |
411 | heartbeat_interval, sync_heartbeat_interval, cast_vote_timer_interval, | |
412 | host_addr, host_port, cluster_name, &tie_breaker, connect_timeout, | |
413 | force_ip_version, | |
414 | instance->cmap_poll_fd, instance->votequorum_poll_fd, | |
415 | instance->local_ipc.socket, instance->advanced_settings, | |
416 | instance->heuristics_instance.pipe_cmd_send, | |
417 | instance->heuristics_instance.pipe_cmd_recv, | |
418 | instance->heuristics_instance.pipe_log_recv) == -1) { | |
419 | qdevice_log(LOG_ERR, "Can't initialize qdevice-net instance"); | |
420 | goto error_free_instance; | |
421 | } | |
422 | ||
423 | net_instance->qdevice_instance_ptr = instance; | |
424 | instance->model_data = net_instance; | |
425 | ||
426 | return (0); | |
427 | ||
428 | error_free_cluster_name: | |
429 | free(cluster_name); | |
430 | error_free_host_addr: | |
431 | free(host_addr); | |
432 | error_free_instance: | |
433 | free(net_instance); | |
434 | return (-1); | |
435 | } |