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