]> git.proxmox.com Git - mirror_corosync.git/blob - exec/totemconfig.c
totem: Add Kronosnet transport.
[mirror_corosync.git] / exec / totemconfig.c
1 /*
2 * Copyright (c) 2002-2005 MontaVista Software, Inc.
3 * Copyright (c) 2006-2013 Red Hat, Inc.
4 *
5 * All rights reserved.
6 *
7 * Author: Steven Dake (sdake@redhat.com)
8 * Jan Friesse (jfriesse@redhat.com)
9 *
10 * This software licensed under BSD license, the text of which follows:
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 *
15 * - Redistributions of source code must retain the above copyright notice,
16 * this list of conditions and the following disclaimer.
17 * - Redistributions in binary form must reproduce the above copyright notice,
18 * this list of conditions and the following disclaimer in the documentation
19 * and/or other materials provided with the distribution.
20 * - Neither the name of the MontaVista Software, Inc. nor the names of its
21 * contributors may be used to endorse or promote products derived from this
22 * software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
34 * THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 #include <config.h>
38
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <errno.h>
43 #include <unistd.h>
44 #include <sys/socket.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #include <sys/param.h>
51
52 #include <corosync/swab.h>
53 #include <corosync/list.h>
54 #include <qb/qbdefs.h>
55 #include <corosync/totem/totem.h>
56 #include <corosync/config.h>
57 #include <corosync/logsys.h>
58 #include <corosync/icmap.h>
59
60 #include "util.h"
61 #include "totemconfig.h"
62
63 #define TOKEN_RETRANSMITS_BEFORE_LOSS_CONST 4
64 #define TOKEN_TIMEOUT 1000
65 #define TOKEN_COEFFICIENT 650
66 #define JOIN_TIMEOUT 50
67 #define MERGE_TIMEOUT 200
68 #define DOWNCHECK_TIMEOUT 1000
69 #define FAIL_TO_RECV_CONST 2500
70 #define SEQNO_UNCHANGED_CONST 30
71 #define MINIMUM_TIMEOUT (int)(1000/HZ)*3
72 #define MAX_NETWORK_DELAY 50
73 #define WINDOW_SIZE 50
74 #define MAX_MESSAGES 17
75 #define MISS_COUNT_CONST 5
76
77 /* These currently match the defaults in libknet.h */
78 #define KNET_PING_INTERVAL 1000
79 #define KNET_PING_TIMEOUT 2000
80 #define KNET_PING_PRECISION 2048
81
82 #define DEFAULT_PORT 5405
83
84 static char error_string_response[512];
85
86 static void add_totem_config_notification(struct totem_config *totem_config);
87
88
89 /* All the volatile parameters are uint32s, luckily */
90 static uint32_t *totem_get_param_by_name(struct totem_config *totem_config, const char *param_name)
91 {
92 if (strcmp(param_name, "totem.token") == 0)
93 return &totem_config->token_timeout;
94 if (strcmp(param_name, "totem.token_retransmit") == 0)
95 return &totem_config->token_retransmit_timeout;
96 if (strcmp(param_name, "totem.hold") == 0)
97 return &totem_config->token_hold_timeout;
98 if (strcmp(param_name, "totem.token_retransmits_before_loss_const") == 0)
99 return &totem_config->token_retransmits_before_loss_const;
100 if (strcmp(param_name, "totem.join") == 0)
101 return &totem_config->join_timeout;
102 if (strcmp(param_name, "totem.send_join") == 0)
103 return &totem_config->send_join_timeout;
104 if (strcmp(param_name, "totem.consensus") == 0)
105 return &totem_config->consensus_timeout;
106 if (strcmp(param_name, "totem.merge") == 0)
107 return &totem_config->merge_timeout;
108 if (strcmp(param_name, "totem.downcheck") == 0)
109 return &totem_config->downcheck_timeout;
110 if (strcmp(param_name, "totem.fail_recv_const") == 0)
111 return &totem_config->fail_to_recv_const;
112 if (strcmp(param_name, "totem.seqno_unchanged_const") == 0)
113 return &totem_config->seqno_unchanged_const;
114 if (strcmp(param_name, "totem.heartbeat_failures_allowed") == 0)
115 return &totem_config->heartbeat_failures_allowed;
116 if (strcmp(param_name, "totem.max_network_delay") == 0)
117 return &totem_config->max_network_delay;
118 if (strcmp(param_name, "totem.window_size") == 0)
119 return &totem_config->window_size;
120 if (strcmp(param_name, "totem.max_messages") == 0)
121 return &totem_config->max_messages;
122 if (strcmp(param_name, "totem.miss_count_const") == 0)
123 return &totem_config->miss_count_const;
124
125 return NULL;
126 }
127
128 /*
129 * Read key_name from icmap. If key is not found or key_name == delete_key or if allow_zero is false
130 * and readed value is zero, default value is used and stored into totem_config.
131 */
132 static void totem_volatile_config_set_value (struct totem_config *totem_config,
133 const char *key_name, const char *deleted_key, unsigned int default_value,
134 int allow_zero_value)
135 {
136 char runtime_key_name[ICMAP_KEYNAME_MAXLEN];
137
138 if (icmap_get_uint32(key_name, totem_get_param_by_name(totem_config, key_name)) != CS_OK ||
139 (deleted_key != NULL && strcmp(deleted_key, key_name) == 0) ||
140 (!allow_zero_value && *totem_get_param_by_name(totem_config, key_name) == 0)) {
141 *totem_get_param_by_name(totem_config, key_name) = default_value;
142 }
143
144 /*
145 * Store totem_config value to cmap runtime section
146 */
147 if (strlen("runtime.config.") + strlen(key_name) >= ICMAP_KEYNAME_MAXLEN) {
148 /*
149 * This shouldn't happen
150 */
151 return ;
152 }
153
154 strcpy(runtime_key_name, "runtime.config.");
155 strcat(runtime_key_name, key_name);
156
157 icmap_set_uint32(runtime_key_name, *totem_get_param_by_name(totem_config, key_name));
158 }
159
160
161 /*
162 * Read and validate config values from cmap and store them into totem_config. If key doesn't exists,
163 * default value is stored. deleted_key is name of key beeing processed by delete operation
164 * from cmap. It is considered as non existing even if it can be read. Can be NULL.
165 */
166 static void totem_volatile_config_read (struct totem_config *totem_config, const char *deleted_key)
167 {
168 uint32_t u32;
169
170 totem_volatile_config_set_value(totem_config, "totem.token_retransmits_before_loss_const", deleted_key,
171 TOKEN_RETRANSMITS_BEFORE_LOSS_CONST, 0);
172
173 totem_volatile_config_set_value(totem_config, "totem.token", deleted_key, TOKEN_TIMEOUT, 0);
174
175 if (totem_config->interface_count > 0 && totem_config->interfaces[0].member_count > 2) {
176 u32 = TOKEN_COEFFICIENT;
177 icmap_get_uint32("totem.token_coefficient", &u32);
178 totem_config->token_timeout += (totem_config->interfaces[0].member_count - 2) * u32;
179
180 /*
181 * Store totem_config value to cmap runtime section
182 */
183 icmap_set_uint32("runtime.config.totem.token", totem_config->token_timeout);
184 }
185
186 totem_volatile_config_set_value(totem_config, "totem.max_network_delay", deleted_key, MAX_NETWORK_DELAY, 0);
187
188 totem_volatile_config_set_value(totem_config, "totem.window_size", deleted_key, WINDOW_SIZE, 0);
189
190 totem_volatile_config_set_value(totem_config, "totem.max_messages", deleted_key, MAX_MESSAGES, 0);
191
192 totem_volatile_config_set_value(totem_config, "totem.miss_count_const", deleted_key, MISS_COUNT_CONST, 0);
193
194 totem_volatile_config_set_value(totem_config, "totem.token_retransmit", deleted_key,
195 (int)(totem_config->token_timeout / (totem_config->token_retransmits_before_loss_const + 0.2)), 0);
196
197 totem_volatile_config_set_value(totem_config, "totem.hold", deleted_key,
198 (int)(totem_config->token_retransmit_timeout * 0.8 - (1000/HZ)), 0);
199
200 totem_volatile_config_set_value(totem_config, "totem.join", deleted_key, JOIN_TIMEOUT, 0);
201
202 totem_volatile_config_set_value(totem_config, "totem.consensus", deleted_key,
203 (int)(float)(1.2 * totem_config->token_timeout), 0);
204
205 totem_volatile_config_set_value(totem_config, "totem.merge", deleted_key, MERGE_TIMEOUT, 0);
206
207 totem_volatile_config_set_value(totem_config, "totem.downcheck", deleted_key, DOWNCHECK_TIMEOUT, 0);
208
209 totem_volatile_config_set_value(totem_config, "totem.fail_recv_const", deleted_key, FAIL_TO_RECV_CONST, 0);
210
211 totem_volatile_config_set_value(totem_config, "totem.seqno_unchanged_const", deleted_key,
212 SEQNO_UNCHANGED_CONST, 0);
213
214 totem_volatile_config_set_value(totem_config, "totem.send_join", deleted_key, 0, 1);
215
216 totem_volatile_config_set_value(totem_config, "totem.heartbeat_failures_allowed", deleted_key, 0, 1);
217 }
218
219 static int totem_volatile_config_validate (
220 struct totem_config *totem_config,
221 const char **error_string)
222 {
223 static char local_error_reason[512];
224 const char *error_reason = local_error_reason;
225
226 if (totem_config->max_network_delay < MINIMUM_TIMEOUT) {
227 snprintf (local_error_reason, sizeof(local_error_reason),
228 "The max_network_delay parameter (%d ms) may not be less than (%d ms).",
229 totem_config->max_network_delay, MINIMUM_TIMEOUT);
230 goto parse_error;
231 }
232
233 if (totem_config->token_timeout < MINIMUM_TIMEOUT) {
234 snprintf (local_error_reason, sizeof(local_error_reason),
235 "The token timeout parameter (%d ms) may not be less than (%d ms).",
236 totem_config->token_timeout, MINIMUM_TIMEOUT);
237 goto parse_error;
238 }
239
240 if (totem_config->token_retransmit_timeout < MINIMUM_TIMEOUT) {
241 snprintf (local_error_reason, sizeof(local_error_reason),
242 "The token retransmit timeout parameter (%d ms) may not be less than (%d ms).",
243 totem_config->token_retransmit_timeout, MINIMUM_TIMEOUT);
244 goto parse_error;
245 }
246
247 if (totem_config->token_hold_timeout < MINIMUM_TIMEOUT) {
248 snprintf (local_error_reason, sizeof(local_error_reason),
249 "The token hold timeout parameter (%d ms) may not be less than (%d ms).",
250 totem_config->token_hold_timeout, MINIMUM_TIMEOUT);
251 goto parse_error;
252 }
253
254 if (totem_config->join_timeout < MINIMUM_TIMEOUT) {
255 snprintf (local_error_reason, sizeof(local_error_reason),
256 "The join timeout parameter (%d ms) may not be less than (%d ms).",
257 totem_config->join_timeout, MINIMUM_TIMEOUT);
258 goto parse_error;
259 }
260
261 if (totem_config->consensus_timeout < MINIMUM_TIMEOUT) {
262 snprintf (local_error_reason, sizeof(local_error_reason),
263 "The consensus timeout parameter (%d ms) may not be less than (%d ms).",
264 totem_config->consensus_timeout, MINIMUM_TIMEOUT);
265 goto parse_error;
266 }
267
268 if (totem_config->consensus_timeout < totem_config->join_timeout) {
269 snprintf (local_error_reason, sizeof(local_error_reason),
270 "The consensus timeout parameter (%d ms) may not be less than join timeout (%d ms).",
271 totem_config->consensus_timeout, totem_config->join_timeout);
272 goto parse_error;
273 }
274
275 if (totem_config->merge_timeout < MINIMUM_TIMEOUT) {
276 snprintf (local_error_reason, sizeof(local_error_reason),
277 "The merge timeout parameter (%d ms) may not be less than (%d ms).",
278 totem_config->merge_timeout, MINIMUM_TIMEOUT);
279 goto parse_error;
280 }
281
282 if (totem_config->downcheck_timeout < MINIMUM_TIMEOUT) {
283 snprintf (local_error_reason, sizeof(local_error_reason),
284 "The downcheck timeout parameter (%d ms) may not be less than (%d ms).",
285 totem_config->downcheck_timeout, MINIMUM_TIMEOUT);
286 goto parse_error;
287 }
288
289 return 0;
290
291 parse_error:
292 snprintf (error_string_response, sizeof(error_string_response),
293 "parse error in config: %s\n", error_reason);
294 *error_string = error_string_response;
295 return (-1);
296
297 }
298
299 static int totem_get_crypto(struct totem_config *totem_config)
300 {
301 char *str;
302 const char *tmp_cipher;
303 const char *tmp_hash;
304
305 tmp_hash = "none";
306 tmp_cipher = "none";
307
308 if (icmap_get_string("totem.crypto_cipher", &str) == CS_OK) {
309 if (strcmp(str, "none") == 0) {
310 tmp_cipher = "none";
311 }
312 if (strcmp(str, "aes256") == 0) {
313 tmp_cipher = "aes256";
314 }
315 if (strcmp(str, "aes192") == 0) {
316 tmp_cipher = "aes192";
317 }
318 if (strcmp(str, "aes128") == 0) {
319 tmp_cipher = "aes128";
320 }
321 if (strcmp(str, "aes256") == 0) {
322 tmp_cipher = "aes256";
323 }
324 if (strcmp(str, "3des") == 0) {
325 tmp_cipher = "3des";
326 }
327 free(str);
328 }
329
330 if (icmap_get_string("totem.crypto_hash", &str) == CS_OK) {
331 if (strcmp(str, "none") == 0) {
332 tmp_hash = "none";
333 }
334 if (strcmp(str, "md5") == 0) {
335 tmp_hash = "md5";
336 }
337 if (strcmp(str, "sha1") == 0) {
338 tmp_hash = "sha1";
339 }
340 if (strcmp(str, "sha256") == 0) {
341 tmp_hash = "sha256";
342 }
343 if (strcmp(str, "sha384") == 0) {
344 tmp_hash = "sha384";
345 }
346 if (strcmp(str, "sha512") == 0) {
347 tmp_hash = "sha512";
348 }
349 free(str);
350 }
351
352 if ((strcmp(tmp_cipher, "none") != 0) &&
353 (strcmp(tmp_hash, "none") == 0)) {
354 return -1;
355 }
356
357 free(totem_config->crypto_cipher_type);
358 free(totem_config->crypto_hash_type);
359
360 totem_config->crypto_cipher_type = strdup(tmp_cipher);
361 totem_config->crypto_hash_type = strdup(tmp_hash);
362
363 return 0;
364 }
365
366 static int totem_config_get_ip_version(void)
367 {
368 int res;
369 char *str;
370
371 res = AF_INET;
372 if (icmap_get_string("totem.ip_version", &str) == CS_OK) {
373 if (strcmp(str, "ipv4") == 0) {
374 res = AF_INET;
375 }
376 if (strcmp(str, "ipv6") == 0) {
377 res = AF_INET6;
378 }
379 free(str);
380 }
381
382 return (res);
383 }
384
385 static uint16_t generate_cluster_id (const char *cluster_name)
386 {
387 int i;
388 int value = 0;
389
390 for (i = 0; i < strlen(cluster_name); i++) {
391 value <<= 1;
392 value += cluster_name[i];
393 }
394
395 return (value & 0xFFFF);
396 }
397
398 static int get_cluster_mcast_addr (
399 const char *cluster_name,
400 unsigned int linknumber,
401 int ip_version,
402 struct totem_ip_address *res)
403 {
404 uint16_t clusterid;
405 char addr[INET6_ADDRSTRLEN + 1];
406 int err;
407
408 if (cluster_name == NULL) {
409 return (-1);
410 }
411
412 clusterid = generate_cluster_id(cluster_name) + linknumber;
413 memset (res, 0, sizeof(*res));
414
415 switch (ip_version) {
416 case AF_INET:
417 snprintf(addr, sizeof(addr), "239.192.%d.%d", clusterid >> 8, clusterid % 0xFF);
418 break;
419 case AF_INET6:
420 snprintf(addr, sizeof(addr), "ff15::%x", clusterid);
421 break;
422 default:
423 /*
424 * Unknown family
425 */
426 return (-1);
427 }
428
429 err = totemip_parse (res, addr, ip_version);
430
431 return (err);
432 }
433
434 static unsigned int generate_nodeid_for_duplicate_test(
435 struct totem_config *totem_config,
436 char *addr)
437 {
438 unsigned int nodeid;
439 struct totem_ip_address totemip;
440
441 /* AF_INET hard-coded here because auto-generated nodeids
442 are only for IPv4 */
443 if (totemip_parse(&totemip, addr, AF_INET) != 0)
444 return -1;
445
446 memcpy (&nodeid, &totemip.addr, sizeof (unsigned int));
447
448 #if __BYTE_ORDER == __LITTLE_ENDIAN
449 nodeid = swab32 (nodeid);
450 #endif
451
452 if (totem_config->clear_node_high_bit) {
453 nodeid &= 0x7FFFFFFF;
454 }
455 return nodeid;
456 }
457
458 static int check_for_duplicate_nodeids(
459 struct totem_config *totem_config,
460 const char **error_string)
461 {
462 icmap_iter_t iter;
463 icmap_iter_t subiter;
464 const char *iter_key;
465 int res = 0;
466 int retval = 0;
467 char tmp_key[ICMAP_KEYNAME_MAXLEN];
468 char *ring0_addr=NULL;
469 char *ring0_addr1=NULL;
470 unsigned int node_pos;
471 unsigned int node_pos1;
472 unsigned int nodeid;
473 unsigned int nodeid1;
474 int autogenerated;
475
476 iter = icmap_iter_init("nodelist.node.");
477 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
478 res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key);
479 if (res != 2) {
480 continue;
481 }
482
483 if (strcmp(tmp_key, "ring0_addr") != 0) {
484 continue;
485 }
486
487 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos);
488 autogenerated = 0;
489 if (icmap_get_uint32(tmp_key, &nodeid) != CS_OK) {
490
491 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", node_pos);
492 if (icmap_get_string(tmp_key, &ring0_addr) != CS_OK) {
493 continue;
494 }
495
496 /* Generate nodeid so we can check that auto-generated nodeids don't clash either */
497 nodeid = generate_nodeid_for_duplicate_test(totem_config, ring0_addr);
498 if (nodeid == -1) {
499 continue;
500 }
501 autogenerated = 1;
502 }
503
504 node_pos1 = 0;
505 subiter = icmap_iter_init("nodelist.node.");
506 while (((iter_key = icmap_iter_next(subiter, NULL, NULL)) != NULL) && (node_pos1 < node_pos)) {
507 res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos1, tmp_key);
508 if ((res != 2) || (node_pos1 >= node_pos)) {
509 continue;
510 }
511
512 if (strcmp(tmp_key, "ring0_addr") != 0) {
513 continue;
514 }
515
516 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos1);
517 if (icmap_get_uint32(tmp_key, &nodeid1) != CS_OK) {
518
519 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", node_pos1);
520 if (icmap_get_string(tmp_key, &ring0_addr1) != CS_OK) {
521 continue;
522 }
523 nodeid1 = generate_nodeid_for_duplicate_test(totem_config, ring0_addr1);
524 if (nodeid1 == -1) {
525 continue;
526 }
527 }
528
529 if (nodeid == nodeid1) {
530 retval = -1;
531 snprintf (error_string_response, sizeof(error_string_response),
532 "Nodeid %u%s%s%s appears twice in corosync.conf", nodeid,
533 autogenerated?"(autogenerated from ":"",
534 autogenerated?ring0_addr:"",
535 autogenerated?")":"");
536 log_printf (LOGSYS_LEVEL_ERROR, error_string_response);
537 *error_string = error_string_response;
538 break;
539 }
540 }
541 icmap_iter_finalize(subiter);
542 }
543 icmap_iter_finalize(iter);
544 return retval;
545 }
546
547
548 static int find_local_node_in_nodelist(struct totem_config *totem_config)
549 {
550 icmap_iter_t iter;
551 const char *iter_key;
552 int res = 0;
553 unsigned int node_pos;
554 int local_node_pos = -1;
555 struct totem_ip_address bind_addr;
556 int interface_up, interface_num;
557 char tmp_key[ICMAP_KEYNAME_MAXLEN];
558 char *node_addr_str;
559 struct totem_ip_address node_addr;
560
561 res = totemip_iface_check(&totem_config->interfaces[0].bindnet,
562 &bind_addr, &interface_up, &interface_num,
563 totem_config->clear_node_high_bit);
564 if (res == -1) {
565 return (-1);
566 }
567
568 iter = icmap_iter_init("nodelist.node.");
569 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
570 res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key);
571 if (res != 2) {
572 continue;
573 }
574
575 if (strcmp(tmp_key, "ring0_addr") != 0) {
576 continue;
577 }
578
579 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", node_pos);
580 if (icmap_get_string(tmp_key, &node_addr_str) != CS_OK) {
581 continue;
582 }
583
584 res = totemip_parse (&node_addr, node_addr_str, totem_config->ip_version);
585 free(node_addr_str);
586 if (res == -1) {
587 continue ;
588 }
589
590 if (totemip_equal(&bind_addr, &node_addr)) {
591 local_node_pos = node_pos;
592 }
593 }
594 icmap_iter_finalize(iter);
595
596 return (local_node_pos);
597 }
598
599 /*
600 * Compute difference between two set of totem interface arrays. set1 and set2
601 * are changed so for same ring, ip existing in both set1 and set2 are cleared
602 * (set to 0), and ips which are only in set1 or set2 remains untouched.
603 * totempg_node_add/remove is called.
604 */
605 static void compute_interfaces_diff(int interface_count,
606 struct totem_interface *set1,
607 struct totem_interface *set2)
608 {
609 int ring_no, set1_pos, set2_pos;
610 struct totem_ip_address empty_ip_address;
611
612 memset(&empty_ip_address, 0, sizeof(empty_ip_address));
613
614 for (ring_no = 0; ring_no < interface_count; ring_no++) {
615 for (set1_pos = 0; set1_pos < set1[ring_no].member_count; set1_pos++) {
616 for (set2_pos = 0; set2_pos < set2[ring_no].member_count; set2_pos++) {
617 /*
618 * For current ring_no remove all set1 items existing
619 * in set2
620 */
621 if (memcmp(&set1[ring_no].member_list[set1_pos],
622 &set2[ring_no].member_list[set2_pos],
623 sizeof(struct totem_ip_address)) == 0) {
624 memset(&set1[ring_no].member_list[set1_pos], 0,
625 sizeof(struct totem_ip_address));
626 memset(&set2[ring_no].member_list[set2_pos], 0,
627 sizeof(struct totem_ip_address));
628 }
629 }
630 }
631 }
632
633 for (ring_no = 0; ring_no < interface_count; ring_no++) {
634 for (set1_pos = 0; set1_pos < set1[ring_no].member_count; set1_pos++) {
635 /*
636 * All items which remained in set1 doesn't exists in set2 any longer so
637 * node has to be removed.
638 */
639 if (memcmp(&set1[ring_no].member_list[set1_pos], &empty_ip_address, sizeof(empty_ip_address)) != 0) {
640 log_printf(LOGSYS_LEVEL_DEBUG,
641 "removing dynamic member %s for ring %u",
642 totemip_print(&set1[ring_no].member_list[set1_pos]),
643 ring_no);
644
645 totempg_member_remove(&set1[ring_no].member_list[set1_pos], ring_no);
646 }
647 }
648 for (set2_pos = 0; set2_pos < set2[ring_no].member_count; set2_pos++) {
649 /*
650 * All items which remained in set2 doesn't existed in set1 so this is no node
651 * and has to be added.
652 */
653 if (memcmp(&set2[ring_no].member_list[set2_pos], &empty_ip_address, sizeof(empty_ip_address)) != 0) {
654 log_printf(LOGSYS_LEVEL_DEBUG,
655 "adding dynamic member %s for ring %u",
656 totemip_print(&set2[ring_no].member_list[set2_pos]),
657 ring_no);
658
659 totempg_member_add(&set2[ring_no].member_list[set2_pos], ring_no);
660 }
661 }
662 }
663 }
664
665 static void put_nodelist_members_to_config(struct totem_config *totem_config, int reload)
666 {
667 icmap_iter_t iter, iter2;
668 const char *iter_key, *iter_key2;
669 int res = 0;
670 unsigned int node_pos;
671 char tmp_key[ICMAP_KEYNAME_MAXLEN];
672 char tmp_key2[ICMAP_KEYNAME_MAXLEN];
673 char *node_addr_str;
674 int member_count;
675 unsigned int linknumber = 0;
676 int i, j;
677 struct totem_interface *orig_interfaces = NULL;
678 struct totem_interface *new_interfaces = NULL;
679
680 if (reload) {
681 /*
682 * We need to compute diff only for reload. Also for initial configuration
683 * not all totem structures are initialized so corosync will crash during
684 * member_add/remove
685 */
686 orig_interfaces = malloc (sizeof (struct totem_interface) * INTERFACE_MAX);
687 assert(orig_interfaces != NULL);
688 new_interfaces = malloc (sizeof (struct totem_interface) * INTERFACE_MAX);
689 assert(new_interfaces != NULL);
690
691 memcpy(orig_interfaces, totem_config->interfaces, sizeof (struct totem_interface) * INTERFACE_MAX);
692 }
693
694 /* Clear out nodelist so we can put the new one in if needed */
695 for (i = 0; i < totem_config->interface_count; i++) {
696 for (j = 0; j < PROCESSOR_COUNT_MAX; j++) {
697 memset(&totem_config->interfaces[i].member_list[j], 0, sizeof(struct totem_ip_address));
698 }
699 totem_config->interfaces[i].member_count = 0;
700 }
701
702 iter = icmap_iter_init("nodelist.node.");
703 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
704 res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key);
705 if (res != 2) {
706 continue;
707 }
708
709 if (strcmp(tmp_key, "ring0_addr") != 0) {
710 continue;
711 }
712
713 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.", node_pos);
714 iter2 = icmap_iter_init(tmp_key);
715 while ((iter_key2 = icmap_iter_next(iter2, NULL, NULL)) != NULL) {
716 unsigned int nodeid;
717
718 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos);
719 if (icmap_get_uint32(tmp_key, &nodeid) != CS_OK) {
720 }
721
722 res = sscanf(iter_key2, "nodelist.node.%u.ring%u%s", &node_pos, &linknumber, tmp_key2);
723 if (res != 3 || strcmp(tmp_key2, "_addr") != 0) {
724 continue;
725 }
726
727 if (icmap_get_string(iter_key2, &node_addr_str) != CS_OK) {
728 continue;
729 }
730
731 member_count = totem_config->interfaces[linknumber].member_count;
732
733 res = totemip_parse(&totem_config->interfaces[linknumber].member_list[member_count],
734 node_addr_str, totem_config->ip_version);
735 if (res != -1) {
736 totem_config->interfaces[linknumber].member_list[member_count].nodeid = nodeid;
737 totem_config->interfaces[linknumber].member_count++;
738 }
739 free(node_addr_str);
740 }
741
742 icmap_iter_finalize(iter2);
743 }
744
745 icmap_iter_finalize(iter);
746
747 if (reload) {
748 memcpy(new_interfaces, totem_config->interfaces, sizeof (struct totem_interface) * INTERFACE_MAX);
749
750 compute_interfaces_diff(totem_config->interface_count, orig_interfaces, new_interfaces);
751
752 free(new_interfaces);
753 free(orig_interfaces);
754 }
755 }
756
757 static void nodelist_dynamic_notify(
758 int32_t event,
759 const char *key_name,
760 struct icmap_notify_value new_val,
761 struct icmap_notify_value old_val,
762 void *user_data)
763 {
764 int res;
765 unsigned int ring_no;
766 unsigned int member_no;
767 char tmp_str[ICMAP_KEYNAME_MAXLEN];
768 uint8_t reloading;
769 struct totem_config *totem_config = (struct totem_config *)user_data;
770
771 /*
772 * If a full reload is in progress then don't do anything until it's done and
773 * can reconfigure it all atomically
774 */
775 if (icmap_get_uint8("config.totemconfig_reload_in_progress", &reloading) == CS_OK && reloading) {
776 return ;
777 }
778
779 res = sscanf(key_name, "nodelist.node.%u.ring%u%s", &member_no, &ring_no, tmp_str);
780 if (res != 3)
781 return ;
782
783 if (strcmp(tmp_str, "_addr") != 0) {
784 return;
785 }
786
787 put_nodelist_members_to_config(totem_config, 1);
788 }
789
790
791 /*
792 * Tries to find node (node_pos) in config nodelist which address matches any
793 * local interface. Address can be stored in ring0_addr or if ipaddr_key_prefix is not NULL
794 * key with prefix ipaddr_key is used (there can be multiuple of them)
795 * This function differs * from find_local_node_in_nodelist because it doesn't need bindnetaddr,
796 * but doesn't work when bind addr is network address (so IP must be exact
797 * match).
798 *
799 * Returns 1 on success (address was found, node_pos is then correctly set) or 0 on failure.
800 */
801 int totem_config_find_local_addr_in_nodelist(const char *ipaddr_key_prefix, unsigned int *node_pos)
802 {
803 struct list_head addrs;
804 struct totem_ip_if_address *if_addr;
805 icmap_iter_t iter, iter2;
806 const char *iter_key, *iter_key2;
807 struct list_head *list;
808 const char *ipaddr_key;
809 int ip_version;
810 struct totem_ip_address node_addr;
811 char *node_addr_str;
812 int node_found = 0;
813 int res = 0;
814 char tmp_key[ICMAP_KEYNAME_MAXLEN];
815
816 if (totemip_getifaddrs(&addrs) == -1) {
817 return 0;
818 }
819
820 ip_version = totem_config_get_ip_version();
821
822 iter = icmap_iter_init("nodelist.node.");
823
824 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
825 res = sscanf(iter_key, "nodelist.node.%u.%s", node_pos, tmp_key);
826 if (res != 2) {
827 continue;
828 }
829
830 if (strcmp(tmp_key, "ring0_addr") != 0) {
831 continue;
832 }
833
834 if (icmap_get_string(iter_key, &node_addr_str) != CS_OK) {
835 continue ;
836 }
837
838 free(node_addr_str);
839
840 /*
841 * ring0_addr found -> let's iterate thru ipaddr_key_prefix
842 */
843 snprintf(tmp_key, sizeof(tmp_key), "nodelist.node.%u.%s", *node_pos,
844 (ipaddr_key_prefix != NULL ? ipaddr_key_prefix : "ring0_addr"));
845
846 iter2 = icmap_iter_init(tmp_key);
847 while ((iter_key2 = icmap_iter_next(iter2, NULL, NULL)) != NULL) {
848 /*
849 * ring0_addr must be exact match, not prefix
850 */
851 ipaddr_key = (ipaddr_key_prefix != NULL ? iter_key2 : tmp_key);
852 if (icmap_get_string(ipaddr_key, &node_addr_str) != CS_OK) {
853 continue ;
854 }
855
856 if (totemip_parse(&node_addr, node_addr_str, ip_version) == -1) {
857 free(node_addr_str);
858 continue ;
859 }
860 free(node_addr_str);
861
862 /*
863 * Try to match ip with if_addrs
864 */
865 node_found = 0;
866 for (list = addrs.next; list != &addrs; list = list->next) {
867 if_addr = list_entry(list, struct totem_ip_if_address, list);
868
869 if (totemip_equal(&node_addr, &if_addr->ip_addr)) {
870 node_found = 1;
871 break;
872 }
873 }
874
875 if (node_found) {
876 break ;
877 }
878 }
879
880 icmap_iter_finalize(iter2);
881
882 if (node_found) {
883 break ;
884 }
885 }
886
887 icmap_iter_finalize(iter);
888 totemip_freeifaddrs(&addrs);
889
890 return (node_found);
891 }
892
893 static void config_convert_nodelist_to_interface(struct totem_config *totem_config)
894 {
895 int res = 0;
896 unsigned int node_pos;
897 char tmp_key[ICMAP_KEYNAME_MAXLEN];
898 char tmp_key2[ICMAP_KEYNAME_MAXLEN];
899 char *node_addr_str;
900 unsigned int linknumber = 0;
901 icmap_iter_t iter;
902 const char *iter_key;
903
904 if (totem_config_find_local_addr_in_nodelist(NULL, &node_pos)) {
905 /*
906 * We found node, so create interface section
907 */
908 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.", node_pos);
909 iter = icmap_iter_init(tmp_key);
910 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
911 res = sscanf(iter_key, "nodelist.node.%u.ring%u%s", &node_pos, &linknumber, tmp_key2);
912 if (res != 3 || strcmp(tmp_key2, "_addr") != 0) {
913 continue ;
914 }
915
916 if (icmap_get_string(iter_key, &node_addr_str) != CS_OK) {
917 continue;
918 }
919
920 snprintf(tmp_key2, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.bindnetaddr", linknumber);
921 icmap_set_string(tmp_key2, node_addr_str);
922 free(node_addr_str);
923 }
924 icmap_iter_finalize(iter);
925 }
926 }
927
928
929 extern int totem_config_read (
930 struct totem_config *totem_config,
931 const char **error_string,
932 uint64_t *warnings)
933 {
934 int res = 0;
935 char *str;
936 unsigned int linknumber = 0;
937 int member_count = 0;
938 icmap_iter_t iter, member_iter;
939 const char *iter_key;
940 const char *member_iter_key;
941 char linknumber_key[ICMAP_KEYNAME_MAXLEN];
942 char tmp_key[ICMAP_KEYNAME_MAXLEN];
943 uint8_t u8;
944 uint16_t u16;
945 uint32_t u32;
946 char *cluster_name = NULL;
947 int i;
948 int local_node_pos;
949 int nodeid_set;
950
951 *warnings = 0;
952
953 memset (totem_config, 0, sizeof (struct totem_config));
954 totem_config->interfaces = malloc (sizeof (struct totem_interface) * INTERFACE_MAX);
955 if (totem_config->interfaces == 0) {
956 *error_string = "Out of memory trying to allocate ethernet interface storage area";
957 return -1;
958 }
959
960 memset (totem_config->interfaces, 0,
961 sizeof (struct totem_interface) * INTERFACE_MAX);
962
963 strcpy (totem_config->link_mode, "passive");
964
965 icmap_get_uint32("totem.version", (uint32_t *)&totem_config->version);
966
967 if (totem_get_crypto(totem_config) != 0) {
968 *error_string = "crypto_cipher requires crypto_hash with value other than none";
969 return -1;
970 }
971
972 if (icmap_get_string("totem.link_mode", &str) == CS_OK) {
973 if (strlen(str) >= TOTEM_LINK_MODE_BYTES) {
974 *error_string = "totem.link_mode is too long";
975 free(str);
976
977 return -1;
978 }
979 strcpy (totem_config->link_mode, str);
980 free(str);
981 }
982
983 icmap_get_uint32("totem.nodeid", &totem_config->node_id);
984
985 totem_config->clear_node_high_bit = 0;
986 if (icmap_get_string("totem.clear_node_high_bit", &str) == CS_OK) {
987 if (strcmp (str, "yes") == 0) {
988 totem_config->clear_node_high_bit = 1;
989 }
990 free(str);
991 }
992
993 icmap_get_uint32("totem.threads", &totem_config->threads);
994
995 icmap_get_uint32("totem.netmtu", &totem_config->net_mtu);
996
997 if (icmap_get_string("totem.cluster_name", &cluster_name) != CS_OK) {
998 cluster_name = NULL;
999 }
1000
1001 totem_config->ip_version = totem_config_get_ip_version();
1002
1003 if (icmap_get_string("totem.interface.0.bindnetaddr", &str) != CS_OK) {
1004 /*
1005 * We were not able to find ring 0 bindnet addr. Try to use nodelist informations
1006 */
1007 config_convert_nodelist_to_interface(totem_config);
1008 } else {
1009 free(str);
1010 }
1011
1012 /*
1013 * Broadcast option is global but set in interface section,
1014 * so reset before processing interfaces.
1015 */
1016 totem_config->broadcast_use = 0;
1017
1018 iter = icmap_iter_init("totem.interface.");
1019 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
1020 res = sscanf(iter_key, "totem.interface.%[^.].%s", linknumber_key, tmp_key);
1021 if (res != 2) {
1022 continue;
1023 }
1024
1025 if (strcmp(tmp_key, "bindnetaddr") != 0) {
1026 continue;
1027 }
1028
1029 member_count = 0;
1030
1031 linknumber = atoi(linknumber_key);
1032
1033 if (linknumber >= INTERFACE_MAX) {
1034 free(cluster_name);
1035
1036 snprintf (error_string_response, sizeof(error_string_response),
1037 "parse error in config: interface ring number %u is bigger than allowed maximum %u\n",
1038 linknumber, INTERFACE_MAX - 1);
1039
1040 *error_string = error_string_response;
1041 return -1;
1042 }
1043
1044 /*
1045 * Get the bind net address
1046 */
1047 if (icmap_get_string(iter_key, &str) == CS_OK) {
1048 res = totemip_parse (&totem_config->interfaces[linknumber].bindnet, str,
1049 totem_config->ip_version);
1050 free(str);
1051 }
1052
1053 /*
1054 * Get interface multicast address
1055 */
1056 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastaddr", linknumber);
1057 if (icmap_get_string(tmp_key, &str) == CS_OK) {
1058 res = totemip_parse (&totem_config->interfaces[linknumber].mcast_addr, str, totem_config->ip_version);
1059 free(str);
1060 } else {
1061 /*
1062 * User not specified address -> autogenerate one from cluster_name key
1063 * (if available). Return code is intentionally ignored, because
1064 * udpu doesn't need mcastaddr and validity of mcastaddr for udp is
1065 * checked later anyway.
1066 */
1067 (void)get_cluster_mcast_addr (cluster_name,
1068 linknumber,
1069 totem_config->ip_version,
1070 &totem_config->interfaces[linknumber].mcast_addr);
1071 }
1072
1073 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.broadcast", linknumber);
1074 if (icmap_get_string(tmp_key, &str) == CS_OK) {
1075 if (strcmp (str, "yes") == 0) {
1076 totem_config->broadcast_use = 1;
1077 }
1078 free(str);
1079 }
1080
1081 /*
1082 * Get mcast port
1083 */
1084 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastport", linknumber);
1085 if (icmap_get_uint16(tmp_key, &totem_config->interfaces[linknumber].ip_port) != CS_OK) {
1086 if (totem_config->broadcast_use) {
1087 totem_config->interfaces[linknumber].ip_port = DEFAULT_PORT + (2 * linknumber);
1088 } else {
1089 totem_config->interfaces[linknumber].ip_port = DEFAULT_PORT;
1090 }
1091 }
1092
1093 /*
1094 * Get the TTL
1095 */
1096 totem_config->interfaces[linknumber].ttl = 1;
1097
1098 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.ttl", linknumber);
1099
1100 if (icmap_get_uint8(tmp_key, &u8) == CS_OK) {
1101 totem_config->interfaces[linknumber].ttl = u8;
1102 }
1103
1104 /*
1105 * Get the knet link params
1106 */
1107 totem_config->interfaces[linknumber].knet_link_priority = 1;
1108 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_link_priority", linknumber);
1109
1110 if (icmap_get_uint8(tmp_key, &u8) == CS_OK) {
1111 totem_config->interfaces[linknumber].knet_link_priority = u8;
1112 }
1113
1114 totem_config->interfaces[linknumber].knet_ping_interval = KNET_PING_INTERVAL;
1115 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_ping_interval", linknumber);
1116 if (icmap_get_uint32(tmp_key, &u32) == CS_OK) {
1117 totem_config->interfaces[linknumber].knet_ping_interval = u32;
1118 }
1119 totem_config->interfaces[linknumber].knet_ping_timeout = KNET_PING_TIMEOUT;
1120 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_ping_timeout", linknumber);
1121 if (icmap_get_uint32(tmp_key, &u32) == CS_OK) {
1122 totem_config->interfaces[linknumber].knet_ping_timeout = u32;
1123 }
1124 totem_config->interfaces[linknumber].knet_ping_precision = KNET_PING_PRECISION;
1125 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_ping_precision", linknumber);
1126 if (icmap_get_uint32(tmp_key, &u32) == CS_OK) {
1127 totem_config->interfaces[linknumber].knet_ping_precision = u32;
1128 }
1129
1130 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.member.", linknumber);
1131 member_iter = icmap_iter_init(tmp_key);
1132 while ((member_iter_key = icmap_iter_next(member_iter, NULL, NULL)) != NULL) {
1133 if (member_count == 0) {
1134 if (icmap_get_string("nodelist.node.0.ring0_addr", &str) == CS_OK) {
1135 free(str);
1136 *warnings |= TOTEM_CONFIG_WARNING_MEMBERS_IGNORED;
1137 break;
1138 } else {
1139 *warnings |= TOTEM_CONFIG_WARNING_MEMBERS_DEPRECATED;
1140 }
1141 }
1142
1143 if (icmap_get_string(member_iter_key, &str) == CS_OK) {
1144 res = totemip_parse (&totem_config->interfaces[linknumber].member_list[member_count++],
1145 str, totem_config->ip_version);
1146 }
1147 }
1148 icmap_iter_finalize(member_iter);
1149
1150 totem_config->interfaces[linknumber].member_count = member_count;
1151 totem_config->interface_count++;
1152 }
1153 icmap_iter_finalize(iter);
1154
1155 /*
1156 * Use broadcast is global, so if set, make sure to fill mcast addr correctly
1157 */
1158 if (totem_config->broadcast_use) {
1159 for (linknumber = 0; linknumber < totem_config->interface_count; linknumber++) {
1160 totemip_parse (&totem_config->interfaces[linknumber].mcast_addr,
1161 "255.255.255.255", 0);
1162 }
1163 }
1164
1165 /*
1166 * Store automatically generated items back to icmap
1167 */
1168 for (i = 0; i < totem_config->interface_count; i++) {
1169 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastaddr", i);
1170 if (icmap_get_string(tmp_key, &str) == CS_OK) {
1171 free(str);
1172 } else {
1173 str = (char *)totemip_print(&totem_config->interfaces[i].mcast_addr);
1174 icmap_set_string(tmp_key, str);
1175 }
1176
1177 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastport", i);
1178 if (icmap_get_uint16(tmp_key, &u16) != CS_OK) {
1179 icmap_set_uint16(tmp_key, totem_config->interfaces[i].ip_port);
1180 }
1181 }
1182
1183 totem_config->transport_number = TOTEM_TRANSPORT_KNET;
1184 if (icmap_get_string("totem.transport", &str) == CS_OK) {
1185 if (strcmp (str, "udpu") == 0) {
1186 totem_config->transport_number = TOTEM_TRANSPORT_UDPU;
1187 }
1188
1189 if (strcmp (str, "udp") == 0) {
1190 totem_config->transport_number = TOTEM_TRANSPORT_UDP;
1191 }
1192
1193 if (strcmp (str, "knet") == 0) {
1194 totem_config->transport_number = TOTEM_TRANSPORT_KNET;
1195 }
1196
1197 free(str);
1198 }
1199
1200 free(cluster_name);
1201
1202 /*
1203 * Check existence of nodelist
1204 */
1205 if (icmap_get_string("nodelist.node.0.ring0_addr", &str) == CS_OK) {
1206 free(str);
1207 /*
1208 * find local node
1209 */
1210 local_node_pos = find_local_node_in_nodelist(totem_config);
1211 if (local_node_pos != -1) {
1212 icmap_set_uint32("nodelist.local_node_pos", local_node_pos);
1213
1214 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", local_node_pos);
1215
1216 nodeid_set = (totem_config->node_id != 0);
1217 if (icmap_get_uint32(tmp_key, &totem_config->node_id) == CS_OK && nodeid_set) {
1218 *warnings |= TOTEM_CONFIG_WARNING_TOTEM_NODEID_IGNORED;
1219 }
1220
1221 /*
1222 * Make localnode ring0_addr read only, so we can be sure that local
1223 * node never changes. If rebinding to other IP would be in future
1224 * supported, this must be changed and handled properly!
1225 */
1226 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", local_node_pos);
1227 icmap_set_ro_access(tmp_key, 0, 1);
1228 icmap_set_ro_access("nodelist.local_node_pos", 0, 1);
1229 }
1230
1231 put_nodelist_members_to_config(totem_config, 0);
1232 }
1233
1234 /*
1235 * Get things that might change in the future (and can depend on totem_config->interfaces);
1236 */
1237 totem_volatile_config_read(totem_config, NULL);
1238
1239 icmap_set_uint8("config.totemconfig_reload_in_progress", 0);
1240
1241 add_totem_config_notification(totem_config);
1242
1243 return 0;
1244 }
1245
1246
1247 int totem_config_validate (
1248 struct totem_config *totem_config,
1249 const char **error_string)
1250 {
1251 static char local_error_reason[512];
1252 char parse_error[512];
1253 const char *error_reason = local_error_reason;
1254 int i, j;
1255 unsigned int interface_max = INTERFACE_MAX;
1256 unsigned int port1, port2;
1257
1258 if (totem_config->interface_count == 0) {
1259 error_reason = "No interfaces defined";
1260 goto parse_error;
1261 }
1262
1263 for (i = 0; i < totem_config->interface_count; i++) {
1264 /*
1265 * Some error checking of parsed data to make sure its valid
1266 */
1267
1268 struct totem_ip_address null_addr;
1269 memset (&null_addr, 0, sizeof (struct totem_ip_address));
1270
1271 if ((totem_config->transport_number == 0) &&
1272 memcmp (&totem_config->interfaces[i].mcast_addr, &null_addr,
1273 sizeof (struct totem_ip_address)) == 0) {
1274 error_reason = "No multicast address specified";
1275 goto parse_error;
1276 }
1277
1278 if (totem_config->interfaces[i].ip_port == 0) {
1279 error_reason = "No multicast port specified";
1280 goto parse_error;
1281 }
1282
1283 if (totem_config->interfaces[i].ttl > 255) {
1284 error_reason = "Invalid TTL (should be 0..255)";
1285 goto parse_error;
1286 }
1287 if (totem_config->transport_number != TOTEM_TRANSPORT_UDP &&
1288 totem_config->interfaces[i].ttl != 1) {
1289 error_reason = "Can only set ttl on multicast transport types";
1290 goto parse_error;
1291 }
1292 if (totem_config->interfaces[i].knet_link_priority > 255) {
1293 error_reason = "Invalid link priority (should be 0..255)";
1294 goto parse_error;
1295 }
1296 if (totem_config->transport_number != TOTEM_TRANSPORT_KNET &&
1297 totem_config->interfaces[i].knet_link_priority != 1) {
1298 error_reason = "Can only set link priority on knet transport type";
1299 goto parse_error;
1300 }
1301
1302 if (totem_config->interfaces[i].mcast_addr.family == AF_INET6 &&
1303 totem_config->node_id == 0) {
1304
1305 error_reason = "An IPV6 network requires that a node ID be specified.";
1306 goto parse_error;
1307 }
1308
1309 if (totem_config->broadcast_use == 0 && totem_config->transport_number == TOTEM_TRANSPORT_UDP) {
1310 if (totem_config->interfaces[i].mcast_addr.family != totem_config->interfaces[i].bindnet.family) {
1311 error_reason = "Multicast address family does not match bind address family";
1312 goto parse_error;
1313 }
1314
1315 if (totemip_is_mcast (&totem_config->interfaces[i].mcast_addr) != 0) {
1316 error_reason = "mcastaddr is not a correct multicast address.";
1317 goto parse_error;
1318 }
1319 }
1320
1321 if (totem_config->interfaces[0].bindnet.family != totem_config->interfaces[i].bindnet.family) {
1322 error_reason = "Not all bind address belong to the same IP family";
1323 goto parse_error;
1324 }
1325
1326 /*
1327 * Ensure mcast address/port differs
1328 */
1329 if (totem_config->transport_number == TOTEM_TRANSPORT_UDP) {
1330 for (j = i + 1; j < totem_config->interface_count; j++) {
1331 port1 = totem_config->interfaces[i].ip_port;
1332 port2 = totem_config->interfaces[j].ip_port;
1333 if (totemip_equal(&totem_config->interfaces[i].mcast_addr,
1334 &totem_config->interfaces[j].mcast_addr) &&
1335 (((port1 > port2 ? port1 : port2) - (port1 < port2 ? port1 : port2)) <= 1)) {
1336 error_reason = "Interfaces multicast address/port pair must differ";
1337 goto parse_error;
1338 }
1339 }
1340 }
1341 }
1342
1343 if (totem_config->version != 2) {
1344 error_reason = "This totem parser can only parse version 2 configurations.";
1345 goto parse_error;
1346 }
1347
1348 if (totem_volatile_config_validate(totem_config, error_string) == -1) {
1349 return (-1);
1350 }
1351
1352 if (check_for_duplicate_nodeids(totem_config, error_string) == -1) {
1353 return (-1);
1354 }
1355
1356 /*
1357 * KNET Link values validation
1358 */
1359 if (strcmp (totem_config->link_mode, "active") &&
1360 strcmp (totem_config->link_mode, "rr") &&
1361 strcmp (totem_config->link_mode, "passive")) {
1362 snprintf (local_error_reason, sizeof(local_error_reason),
1363 "The Knet link mode \"%s\" specified is invalid. It must be active, passive or rr.\n", totem_config->link_mode);
1364 goto parse_error;
1365 }
1366
1367 /* Only Knet does multiple interfaces */
1368 if (totem_config->transport_number != TOTEM_TRANSPORT_KNET) {
1369 interface_max = 1;
1370 }
1371
1372 if (interface_max < totem_config->interface_count) {
1373 snprintf (parse_error, sizeof(parse_error),
1374 "%d is too many configured interfaces for non-Knet transport.",
1375 totem_config->interface_count);
1376 error_reason = parse_error;
1377 goto parse_error;
1378 }
1379
1380 /* Only knet allows crypto */
1381 if (totem_config->transport_number != TOTEM_TRANSPORT_KNET) {
1382 if ((strcmp(totem_config->crypto_cipher_type, "none") != 0) ||
1383 (strcmp(totem_config->crypto_hash_type, "none") != 0)) {
1384
1385 snprintf (parse_error, sizeof(parse_error),
1386 "crypto_cipher & crypto_hash are only valid for the Knet transport.");
1387 error_reason = parse_error;
1388 goto parse_error;
1389 }
1390 }
1391
1392 if (totem_config->net_mtu == 0) {
1393 totem_config->net_mtu = 1500;
1394 }
1395
1396 return 0;
1397
1398 parse_error:
1399 snprintf (error_string_response, sizeof(error_string_response),
1400 "parse error in config: %s\n", error_reason);
1401 *error_string = error_string_response;
1402 return (-1);
1403
1404 }
1405
1406 static int read_keyfile (
1407 const char *key_location,
1408 struct totem_config *totem_config,
1409 const char **error_string)
1410 {
1411 int fd;
1412 int res;
1413 ssize_t expected_key_len = sizeof (totem_config->private_key);
1414 int saved_errno;
1415 char error_str[100];
1416 const char *error_ptr;
1417
1418 fd = open (key_location, O_RDONLY);
1419 if (fd == -1) {
1420 error_ptr = qb_strerror_r(errno, error_str, sizeof(error_str));
1421 snprintf (error_string_response, sizeof(error_string_response),
1422 "Could not open %s: %s\n",
1423 key_location, error_ptr);
1424 goto parse_error;
1425 }
1426
1427 res = read (fd, totem_config->private_key, expected_key_len);
1428 saved_errno = errno;
1429 close (fd);
1430
1431 if (res == -1) {
1432 error_ptr = qb_strerror_r (saved_errno, error_str, sizeof(error_str));
1433 snprintf (error_string_response, sizeof(error_string_response),
1434 "Could not read %s: %s\n",
1435 key_location, error_ptr);
1436 goto parse_error;
1437 }
1438
1439 totem_config->private_key_len = expected_key_len;
1440
1441 if (res != expected_key_len) {
1442 snprintf (error_string_response, sizeof(error_string_response),
1443 "Could only read %d bits of 1024 bits from %s.\n",
1444 res * 8, key_location);
1445 goto parse_error;
1446 }
1447
1448 return 0;
1449
1450 parse_error:
1451 *error_string = error_string_response;
1452 return (-1);
1453 }
1454
1455 int totem_config_keyread (
1456 struct totem_config *totem_config,
1457 const char **error_string)
1458 {
1459 int got_key = 0;
1460 char *key_location = NULL;
1461 int res;
1462 size_t key_len;
1463
1464 memset (totem_config->private_key, 0, 128);
1465 totem_config->private_key_len = 128;
1466
1467 if (strcmp(totem_config->crypto_cipher_type, "none") == 0 &&
1468 strcmp(totem_config->crypto_hash_type, "none") == 0) {
1469 return (0);
1470 }
1471
1472 /* cmap may store the location of the key file */
1473 if (icmap_get_string("totem.keyfile", &key_location) == CS_OK) {
1474 res = read_keyfile(key_location, totem_config, error_string);
1475 free(key_location);
1476 if (res) {
1477 goto key_error;
1478 }
1479 got_key = 1;
1480 } else { /* Or the key itself may be in the cmap */
1481 if (icmap_get("totem.key", NULL, &key_len, NULL) == CS_OK) {
1482 if (key_len > sizeof (totem_config->private_key)) {
1483 sprintf(error_string_response, "key is too long");
1484 goto key_error;
1485 }
1486 if (icmap_get("totem.key", totem_config->private_key, &key_len, NULL) == CS_OK) {
1487 totem_config->private_key_len = key_len;
1488 got_key = 1;
1489 } else {
1490 sprintf(error_string_response, "can't store private key");
1491 goto key_error;
1492 }
1493 }
1494 }
1495
1496 /* In desperation we read the default filename */
1497 if (!got_key) {
1498 const char *filename = getenv("COROSYNC_TOTEM_AUTHKEY_FILE");
1499 if (!filename)
1500 filename = COROSYSCONFDIR "/authkey";
1501 res = read_keyfile(filename, totem_config, error_string);
1502 if (res)
1503 goto key_error;
1504
1505 }
1506
1507 return (0);
1508
1509 key_error:
1510 *error_string = error_string_response;
1511 return (-1);
1512
1513 }
1514
1515 static void debug_dump_totem_config(const struct totem_config *totem_config)
1516 {
1517
1518 log_printf(LOGSYS_LEVEL_DEBUG, "Token Timeout (%d ms) retransmit timeout (%d ms)",
1519 totem_config->token_timeout, totem_config->token_retransmit_timeout);
1520 log_printf(LOGSYS_LEVEL_DEBUG, "token hold (%d ms) retransmits before loss (%d retrans)",
1521 totem_config->token_hold_timeout, totem_config->token_retransmits_before_loss_const);
1522 log_printf(LOGSYS_LEVEL_DEBUG, "join (%d ms) send_join (%d ms) consensus (%d ms) merge (%d ms)",
1523 totem_config->join_timeout, totem_config->send_join_timeout, totem_config->consensus_timeout,
1524 totem_config->merge_timeout);
1525 log_printf(LOGSYS_LEVEL_DEBUG, "downcheck (%d ms) fail to recv const (%d msgs)",
1526 totem_config->downcheck_timeout, totem_config->fail_to_recv_const);
1527 log_printf(LOGSYS_LEVEL_DEBUG,
1528 "seqno unchanged const (%d rotations) Maximum network MTU %d",
1529 totem_config->seqno_unchanged_const, totem_config->net_mtu);
1530 log_printf(LOGSYS_LEVEL_DEBUG,
1531 "window size per rotation (%d messages) maximum messages per rotation (%d messages)",
1532 totem_config->window_size, totem_config->max_messages);
1533 log_printf(LOGSYS_LEVEL_DEBUG, "missed count const (%d messages)", totem_config->miss_count_const);
1534 log_printf(LOGSYS_LEVEL_DEBUG, "heartbeat_failures_allowed (%d)",
1535 totem_config->heartbeat_failures_allowed);
1536 log_printf(LOGSYS_LEVEL_DEBUG, "max_network_delay (%d ms)", totem_config->max_network_delay);
1537 }
1538
1539 static void totem_change_notify(
1540 int32_t event,
1541 const char *key_name,
1542 struct icmap_notify_value new_val,
1543 struct icmap_notify_value old_val,
1544 void *user_data)
1545 {
1546 struct totem_config *totem_config = (struct totem_config *)user_data;
1547 uint32_t *param;
1548 uint8_t reloading;
1549 const char *deleted_key = NULL;
1550 const char *error_string;
1551
1552 /*
1553 * If a full reload is in progress then don't do anything until it's done and
1554 * can reconfigure it all atomically
1555 */
1556 if (icmap_get_uint8("config.reload_in_progress", &reloading) == CS_OK && reloading)
1557 return;
1558
1559 param = totem_get_param_by_name((struct totem_config *)user_data, key_name);
1560 /*
1561 * Process change only if changed key is found in totem_config (-> param is not NULL)
1562 * or for special key token_coefficient. token_coefficient key is not stored in
1563 * totem_config, but it is used for computation of token timeout.
1564 */
1565 if (!param && strcmp(key_name, "totem.token_coefficient") != 0)
1566 return;
1567
1568 /*
1569 * Values other than UINT32 are not supported, or needed (yet)
1570 */
1571 switch (event) {
1572 case ICMAP_TRACK_DELETE:
1573 deleted_key = key_name;
1574 break;
1575 case ICMAP_TRACK_ADD:
1576 case ICMAP_TRACK_MODIFY:
1577 deleted_key = NULL;
1578 break;
1579 default:
1580 break;
1581 }
1582
1583 totem_volatile_config_read (totem_config, deleted_key);
1584 log_printf(LOGSYS_LEVEL_DEBUG, "Totem related config key changed. Dumping actual totem config.");
1585 debug_dump_totem_config(totem_config);
1586 if (totem_volatile_config_validate(totem_config, &error_string) == -1) {
1587 log_printf (LOGSYS_LEVEL_ERROR, "%s", error_string);
1588 /*
1589 * TODO: Consider corosync exit and/or load defaults for volatile
1590 * values. For now, log error seems to be enough
1591 */
1592 }
1593 }
1594
1595 static void totem_reload_notify(
1596 int32_t event,
1597 const char *key_name,
1598 struct icmap_notify_value new_val,
1599 struct icmap_notify_value old_val,
1600 void *user_data)
1601 {
1602 struct totem_config *totem_config = (struct totem_config *)user_data;
1603 uint32_t local_node_pos;
1604 const char *error_string;
1605
1606 /* Reload has completed */
1607 if (*(uint8_t *)new_val.data == 0) {
1608 put_nodelist_members_to_config (totem_config, 1);
1609 totem_volatile_config_read (totem_config, NULL);
1610 log_printf(LOGSYS_LEVEL_DEBUG, "Configuration reloaded. Dumping actual totem config.");
1611 debug_dump_totem_config(totem_config);
1612 if (totem_volatile_config_validate(totem_config, &error_string) == -1) {
1613 log_printf (LOGSYS_LEVEL_ERROR, "%s", error_string);
1614 /*
1615 * TODO: Consider corosync exit and/or load defaults for volatile
1616 * values. For now, log error seems to be enough
1617 */
1618 }
1619
1620 /* Reinstate the local_node_pos */
1621 local_node_pos = find_local_node_in_nodelist(totem_config);
1622 if (local_node_pos != -1) {
1623 icmap_set_uint32("nodelist.local_node_pos", local_node_pos);
1624 }
1625
1626 icmap_set_uint8("config.totemconfig_reload_in_progress", 0);
1627 } else {
1628 icmap_set_uint8("config.totemconfig_reload_in_progress", 1);
1629 }
1630 }
1631
1632 static void add_totem_config_notification(struct totem_config *totem_config)
1633 {
1634 icmap_track_t icmap_track;
1635
1636 icmap_track_add("totem.",
1637 ICMAP_TRACK_ADD | ICMAP_TRACK_DELETE | ICMAP_TRACK_MODIFY | ICMAP_TRACK_PREFIX,
1638 totem_change_notify,
1639 totem_config,
1640 &icmap_track);
1641
1642 icmap_track_add("config.reload_in_progress",
1643 ICMAP_TRACK_ADD | ICMAP_TRACK_MODIFY,
1644 totem_reload_notify,
1645 totem_config,
1646 &icmap_track);
1647
1648 icmap_track_add("nodelist.node.",
1649 ICMAP_TRACK_ADD | ICMAP_TRACK_DELETE | ICMAP_TRACK_MODIFY | ICMAP_TRACK_PREFIX,
1650 nodelist_dynamic_notify,
1651 (void *)totem_config,
1652 &icmap_track);
1653 }