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)
1105  */
1106  res = get_cluster_mcast_addr (cluster_name,
1107  ringnumber,
1108  totem_config->ip_version,
1109  &totem_config->interfaces[ringnumber].mcast_addr);
1110  if (res != 0) {
1111  *error_string = "Can't autogenerate multicast address";
1112  return -1;
1113  }
1114  }
1115 
1116  snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.broadcast", ringnumber);
1117  if (icmap_get_string(tmp_key, &str) == CS_OK) {
1118  if (strcmp (str, "yes") == 0) {
1119  totem_config->broadcast_use = 1;
1120  }
1121  free(str);
1122  }
1123 
1124  /*
1125  * Get mcast port
1126  */
1127  snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastport", ringnumber);
1128  if (icmap_get_uint16(tmp_key, &totem_config->interfaces[ringnumber].ip_port) != CS_OK) {
1129  if (totem_config->broadcast_use) {
1130  totem_config->interfaces[ringnumber].ip_port = DEFAULT_PORT + (2 * ringnumber);
1131  } else {
1132  totem_config->interfaces[ringnumber].ip_port = DEFAULT_PORT;
1133  }
1134  }
1135 
1136  /*
1137  * Get the TTL
1138  */
1139  totem_config->interfaces[ringnumber].ttl = 1;
1140 
1141  snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.ttl", ringnumber);
1142 
1143  if (icmap_get_uint8(tmp_key, &u8) == CS_OK) {
1144  totem_config->interfaces[ringnumber].ttl = u8;
1145  }
1146 
1147  snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.member.", ringnumber);
1148  member_iter = icmap_iter_init(tmp_key);
1149  while ((member_iter_key = icmap_iter_next(member_iter, NULL, NULL)) != NULL) {
1150  if (member_count == 0) {
1151  if (icmap_get_string("nodelist.node.0.ring0_addr", &str) == CS_OK) {
1152  free(str);
1154  break;
1155  } else {
1157  }
1158  }
1159 
1160  if (icmap_get_string(member_iter_key, &str) == CS_OK) {
1161  res = totemip_parse (&totem_config->interfaces[ringnumber].member_list[member_count++],
1162  str, totem_config->ip_version);
1163  }
1164  }
1165  icmap_iter_finalize(member_iter);
1166 
1167  totem_config->interfaces[ringnumber].member_count = member_count;
1168  totem_config->interface_count++;
1169  }
1170  icmap_iter_finalize(iter);
1171 
1172  /*
1173  * Use broadcast is global, so if set, make sure to fill mcast addr correctly
1174  */
1175  if (totem_config->broadcast_use) {
1176  for (ringnumber = 0; ringnumber < totem_config->interface_count; ringnumber++) {
1177  totemip_parse (&totem_config->interfaces[ringnumber].mcast_addr,
1178  "255.255.255.255", 0);
1179  }
1180  }
1181 
1182  /*
1183  * Store automatically generated items back to icmap
1184  */
1185  for (i = 0; i < totem_config->interface_count; i++) {
1186  snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastaddr", i);
1187  if (icmap_get_string(tmp_key, &str) == CS_OK) {
1188  free(str);
1189  } else {
1190  str = (char *)totemip_print(&totem_config->interfaces[i].mcast_addr);
1191  icmap_set_string(tmp_key, str);
1192  }
1193 
1194  snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastport", i);
1195  if (icmap_get_uint16(tmp_key, &u16) != CS_OK) {
1196  icmap_set_uint16(tmp_key, totem_config->interfaces[i].ip_port);
1197  }
1198  }
1199 
1200  totem_config->transport_number = TOTEM_TRANSPORT_UDP;
1201  if (icmap_get_string("totem.transport", &str) == CS_OK) {
1202  if (strcmp (str, "udpu") == 0) {
1203  totem_config->transport_number = TOTEM_TRANSPORT_UDPU;
1204  }
1205 
1206  if (strcmp (str, "iba") == 0) {
1207  totem_config->transport_number = TOTEM_TRANSPORT_RDMA;
1208  }
1209  free(str);
1210  }
1211 
1212  free(cluster_name);
1213 
1214  /*
1215  * Check existence of nodelist
1216  */
1217  if (icmap_get_string("nodelist.node.0.ring0_addr", &str) == CS_OK) {
1218  free(str);
1219  /*
1220  * find local node
1221  */
1222  local_node_pos = find_local_node_in_nodelist(totem_config);
1223  if (local_node_pos != -1) {
1224  icmap_set_uint32("nodelist.local_node_pos", local_node_pos);
1225 
1226  snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", local_node_pos);
1227 
1228  nodeid_set = (totem_config->node_id != 0);
1229  if (icmap_get_uint32(tmp_key, &totem_config->node_id) == CS_OK && nodeid_set) {
1231  }
1232 
1233  /*
1234  * Make localnode ring0_addr read only, so we can be sure that local
1235  * node never changes. If rebinding to other IP would be in future
1236  * supported, this must be changed and handled properly!
1237  */
1238  snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", local_node_pos);
1239  icmap_set_ro_access(tmp_key, 0, 1);
1240  icmap_set_ro_access("nodelist.local_node_pos", 0, 1);
1241  }
1242 
1243  put_nodelist_members_to_config(totem_config, 0);
1244  }
1245 
1246  /*
1247  * Get things that might change in the future (and can depend on totem_config->interfaces);
1248  */
1249  totem_volatile_config_read(totem_config, NULL);
1250 
1251  icmap_set_uint8("config.totemconfig_reload_in_progress", 0);
1252 
1253  add_totem_config_notification(totem_config);
1254 
1255  return 0;
1256 }
1257 
1258 
1260  struct totem_config *totem_config,
1261  const char **error_string)
1262 {
1263  static char local_error_reason[512];
1264  char parse_error[512];
1265  const char *error_reason = local_error_reason;
1266  int i, j;
1267  unsigned int interface_max = INTERFACE_MAX;
1268  unsigned int port1, port2;
1269 
1270  if (totem_config->interface_count == 0) {
1271  error_reason = "No interfaces defined";
1272  goto parse_error;
1273  }
1274 
1275  for (i = 0; i < totem_config->interface_count; i++) {
1276  /*
1277  * Some error checking of parsed data to make sure its valid
1278  */
1279 
1280  struct totem_ip_address null_addr;
1281  memset (&null_addr, 0, sizeof (struct totem_ip_address));
1282 
1283  if ((totem_config->transport_number == 0) &&
1284  memcmp (&totem_config->interfaces[i].mcast_addr, &null_addr,
1285  sizeof (struct totem_ip_address)) == 0) {
1286  error_reason = "No multicast address specified";
1287  goto parse_error;
1288  }
1289 
1290  if (totem_config->interfaces[i].ip_port == 0) {
1291  error_reason = "No multicast port specified";
1292  goto parse_error;
1293  }
1294 
1295  if (totem_config->interfaces[i].ttl > 255) {
1296  error_reason = "Invalid TTL (should be 0..255)";
1297  goto parse_error;
1298  }
1299  if (totem_config->transport_number != TOTEM_TRANSPORT_UDP &&
1300  totem_config->interfaces[i].ttl != 1) {
1301  error_reason = "Can only set ttl on multicast transport types";
1302  goto parse_error;
1303  }
1304 
1305  if (totem_config->interfaces[i].mcast_addr.family == AF_INET6 &&
1306  totem_config->node_id == 0) {
1307 
1308  error_reason = "An IPV6 network requires that a node ID be specified.";
1309  goto parse_error;
1310  }
1311 
1312  if (totem_config->broadcast_use == 0 && totem_config->transport_number == TOTEM_TRANSPORT_UDP) {
1313  if (totem_config->interfaces[i].mcast_addr.family != totem_config->interfaces[i].bindnet.family) {
1314  error_reason = "Multicast address family does not match bind address family";
1315  goto parse_error;
1316  }
1317 
1318  if (totemip_is_mcast (&totem_config->interfaces[i].mcast_addr) != 0) {
1319  error_reason = "mcastaddr is not a correct multicast address.";
1320  goto parse_error;
1321  }
1322  }
1323 
1324  if (totem_config->interfaces[0].bindnet.family != totem_config->interfaces[i].bindnet.family) {
1325  error_reason = "Not all bind address belong to the same IP family";
1326  goto parse_error;
1327  }
1328 
1329  /*
1330  * Ensure mcast address/port differs
1331  */
1332  if (totem_config->transport_number == TOTEM_TRANSPORT_UDP) {
1333  for (j = i + 1; j < totem_config->interface_count; j++) {
1334  port1 = totem_config->interfaces[i].ip_port;
1335  port2 = totem_config->interfaces[j].ip_port;
1336  if (totemip_equal(&totem_config->interfaces[i].mcast_addr,
1337  &totem_config->interfaces[j].mcast_addr) &&
1338  (((port1 > port2 ? port1 : port2) - (port1 < port2 ? port1 : port2)) <= 1)) {
1339  error_reason = "Interfaces multicast address/port pair must differ";
1340  goto parse_error;
1341  }
1342  }
1343  }
1344  }
1345 
1346  if (totem_config->version != 2) {
1347  error_reason = "This totem parser can only parse version 2 configurations.";
1348  goto parse_error;
1349  }
1350 
1351  if (totem_volatile_config_validate(totem_config, error_string) == -1) {
1352  return (-1);
1353  }
1354 
1355  if (check_for_duplicate_nodeids(totem_config, error_string) == -1) {
1356  return (-1);
1357  }
1358 
1359  /*
1360  * RRP values validation
1361  */
1362  if (strcmp (totem_config->rrp_mode, "none") &&
1363  strcmp (totem_config->rrp_mode, "active") &&
1364  strcmp (totem_config->rrp_mode, "passive")) {
1365  snprintf (local_error_reason, sizeof(local_error_reason),
1366  "The RRP mode \"%s\" specified is invalid. It must be none, active, or passive.\n", totem_config->rrp_mode);
1367  goto parse_error;
1368  }
1369 
1370  if (strcmp (totem_config->rrp_mode, "none") == 0) {
1371  interface_max = 1;
1372  }
1373  if (interface_max < totem_config->interface_count) {
1374  snprintf (parse_error, sizeof(parse_error),
1375  "%d is too many configured interfaces for the rrp_mode setting %s.",
1376  totem_config->interface_count,
1377  totem_config->rrp_mode);
1378  error_reason = parse_error;
1379  goto parse_error;
1380  }
1381 
1382  if (totem_config->net_mtu == 0) {
1383  totem_config->net_mtu = 1500;
1384  }
1385 
1386  return 0;
1387 
1388 parse_error:
1389  snprintf (error_string_response, sizeof(error_string_response),
1390  "parse error in config: %s\n", error_reason);
1391  *error_string = error_string_response;
1392  return (-1);
1393 
1394 }
1395 
1396 static int read_keyfile (
1397  const char *key_location,
1398  struct totem_config *totem_config,
1399  const char **error_string)
1400 {
1401  int fd;
1402  int res;
1403  ssize_t expected_key_len = sizeof (totem_config->private_key);
1404  int saved_errno;
1405  char error_str[100];
1406  const char *error_ptr;
1407 
1408  fd = open (key_location, O_RDONLY);
1409  if (fd == -1) {
1410  error_ptr = qb_strerror_r(errno, error_str, sizeof(error_str));
1411  snprintf (error_string_response, sizeof(error_string_response),
1412  "Could not open %s: %s\n",
1413  key_location, error_ptr);
1414  goto parse_error;
1415  }
1416 
1417  res = read (fd, totem_config->private_key, expected_key_len);
1418  saved_errno = errno;
1419  close (fd);
1420 
1421  if (res == -1) {
1422  error_ptr = qb_strerror_r (saved_errno, error_str, sizeof(error_str));
1423  snprintf (error_string_response, sizeof(error_string_response),
1424  "Could not read %s: %s\n",
1425  key_location, error_ptr);
1426  goto parse_error;
1427  }
1428 
1429  totem_config->private_key_len = expected_key_len;
1430 
1431  if (res != expected_key_len) {
1432  snprintf (error_string_response, sizeof(error_string_response),
1433  "Could only read %d bits of 1024 bits from %s.\n",
1434  res * 8, key_location);
1435  goto parse_error;
1436  }
1437 
1438  return 0;
1439 
1440 parse_error:
1441  *error_string = error_string_response;
1442  return (-1);
1443 }
1444 
1446  struct totem_config *totem_config,
1447  const char **error_string)
1448 {
1449  int got_key = 0;
1450  char *key_location = NULL;
1451  int res;
1452  size_t key_len;
1453 
1454  memset (totem_config->private_key, 0, 128);
1455  totem_config->private_key_len = 128;
1456 
1457  if (strcmp(totem_config->crypto_cipher_type, "none") == 0 &&
1458  strcmp(totem_config->crypto_hash_type, "none") == 0) {
1459  return (0);
1460  }
1461 
1462  /* cmap may store the location of the key file */
1463  if (icmap_get_string("totem.keyfile", &key_location) == CS_OK) {
1464  res = read_keyfile(key_location, totem_config, error_string);
1465  free(key_location);
1466  if (res) {
1467  goto key_error;
1468  }
1469  got_key = 1;
1470  } else { /* Or the key itself may be in the cmap */
1471  if (icmap_get("totem.key", NULL, &key_len, NULL) == CS_OK) {
1472  if (key_len > sizeof (totem_config->private_key)) {
1473  sprintf(error_string_response, "key is too long");
1474  goto key_error;
1475  }
1476  if (icmap_get("totem.key", totem_config->private_key, &key_len, NULL) == CS_OK) {
1477  totem_config->private_key_len = key_len;
1478  got_key = 1;
1479  } else {
1480  sprintf(error_string_response, "can't store private key");
1481  goto key_error;
1482  }
1483  }
1484  }
1485 
1486  /* In desperation we read the default filename */
1487  if (!got_key) {
1488  const char *filename = getenv("COROSYNC_TOTEM_AUTHKEY_FILE");
1489  if (!filename)
1490  filename = COROSYSCONFDIR "/authkey";
1491  res = read_keyfile(filename, totem_config, error_string);
1492  if (res)
1493  goto key_error;
1494 
1495  }
1496 
1497  return (0);
1498 
1499 key_error:
1500  *error_string = error_string_response;
1501  return (-1);
1502 
1503 }
1504 
1505 static void debug_dump_totem_config(const struct totem_config *totem_config)
1506 {
1507 
1508  log_printf(LOGSYS_LEVEL_DEBUG, "Token Timeout (%d ms) retransmit timeout (%d ms)",
1509  totem_config->token_timeout, totem_config->token_retransmit_timeout);
1510  log_printf(LOGSYS_LEVEL_DEBUG, "token hold (%d ms) retransmits before loss (%d retrans)",
1511  totem_config->token_hold_timeout, totem_config->token_retransmits_before_loss_const);
1512  log_printf(LOGSYS_LEVEL_DEBUG, "join (%d ms) send_join (%d ms) consensus (%d ms) merge (%d ms)",
1513  totem_config->join_timeout, totem_config->send_join_timeout, totem_config->consensus_timeout,
1514  totem_config->merge_timeout);
1515  log_printf(LOGSYS_LEVEL_DEBUG, "downcheck (%d ms) fail to recv const (%d msgs)",
1516  totem_config->downcheck_timeout, totem_config->fail_to_recv_const);
1518  "seqno unchanged const (%d rotations) Maximum network MTU %d",
1519  totem_config->seqno_unchanged_const, totem_config->net_mtu);
1521  "window size per rotation (%d messages) maximum messages per rotation (%d messages)",
1522  totem_config->window_size, totem_config->max_messages);
1523  log_printf(LOGSYS_LEVEL_DEBUG, "missed count const (%d messages)", totem_config->miss_count_const);
1524  log_printf(LOGSYS_LEVEL_DEBUG, "RRP token expired timeout (%d ms)",
1525  totem_config->rrp_token_expired_timeout);
1526  log_printf(LOGSYS_LEVEL_DEBUG, "RRP token problem counter (%d ms)",
1527  totem_config->rrp_problem_count_timeout);
1528  log_printf(LOGSYS_LEVEL_DEBUG, "RRP threshold (%d problem count)",
1529  totem_config->rrp_problem_count_threshold);
1530  log_printf(LOGSYS_LEVEL_DEBUG, "RRP multicast threshold (%d problem count)",
1531  totem_config->rrp_problem_count_mcast_threshold);
1532  log_printf(LOGSYS_LEVEL_DEBUG, "RRP automatic recovery check timeout (%d ms)",
1533  totem_config->rrp_autorecovery_check_timeout);
1534  log_printf(LOGSYS_LEVEL_DEBUG, "RRP mode set to %s.",
1535  totem_config->rrp_mode);
1536  log_printf(LOGSYS_LEVEL_DEBUG, "heartbeat_failures_allowed (%d)",
1537  totem_config->heartbeat_failures_allowed);
1538  log_printf(LOGSYS_LEVEL_DEBUG, "max_network_delay (%d ms)", totem_config->max_network_delay);
1539 }
1540 
1541 static void totem_change_notify(
1542  int32_t event,
1543  const char *key_name,
1544  struct icmap_notify_value new_val,
1545  struct icmap_notify_value old_val,
1546  void *user_data)
1547 {
1548  struct totem_config *totem_config = (struct totem_config *)user_data;
1549  uint32_t *param;
1550  uint8_t reloading;
1551  const char *deleted_key = NULL;
1552  const char *error_string;
1553 
1554  /*
1555  * If a full reload is in progress then don't do anything until it's done and
1556  * can reconfigure it all atomically
1557  */
1558  if (icmap_get_uint8("config.reload_in_progress", &reloading) == CS_OK && reloading)
1559  return;
1560 
1561  param = totem_get_param_by_name((struct totem_config *)user_data, key_name);
1562  /*
1563  * Process change only if changed key is found in totem_config (-> param is not NULL)
1564  * or for special key token_coefficient. token_coefficient key is not stored in
1565  * totem_config, but it is used for computation of token timeout.
1566  */
1567  if (!param && strcmp(key_name, "totem.token_coefficient") != 0)
1568  return;
1569 
1570  /*
1571  * Values other than UINT32 are not supported, or needed (yet)
1572  */
1573  switch (event) {
1574  case ICMAP_TRACK_DELETE:
1575  deleted_key = key_name;
1576  break;
1577  case ICMAP_TRACK_ADD:
1578  case ICMAP_TRACK_MODIFY:
1579  deleted_key = NULL;
1580  break;
1581  default:
1582  break;
1583  }
1584 
1585  totem_volatile_config_read (totem_config, deleted_key);
1586  log_printf(LOGSYS_LEVEL_DEBUG, "Totem related config key changed. Dumping actual totem config.");
1587  debug_dump_totem_config(totem_config);
1588  if (totem_volatile_config_validate(totem_config, &error_string) == -1) {
1589  log_printf (LOGSYS_LEVEL_ERROR, "%s", error_string);
1590  /*
1591  * TODO: Consider corosync exit and/or load defaults for volatile
1592  * values. For now, log error seems to be enough
1593  */
1594  }
1595 }
1596 
1597 static void totem_reload_notify(
1598  int32_t event,
1599  const char *key_name,
1600  struct icmap_notify_value new_val,
1601  struct icmap_notify_value old_val,
1602  void *user_data)
1603 {
1604  struct totem_config *totem_config = (struct totem_config *)user_data;
1605  uint32_t local_node_pos;
1606  const char *error_string;
1607 
1608  /* Reload has completed */
1609  if (*(uint8_t *)new_val.data == 0) {
1610  put_nodelist_members_to_config (totem_config, 1);
1611  totem_volatile_config_read (totem_config, NULL);
1612  log_printf(LOGSYS_LEVEL_DEBUG, "Configuration reloaded. Dumping actual totem config.");
1613  debug_dump_totem_config(totem_config);
1614  if (totem_volatile_config_validate(totem_config, &error_string) == -1) {
1615  log_printf (LOGSYS_LEVEL_ERROR, "%s", error_string);
1616  /*
1617  * TODO: Consider corosync exit and/or load defaults for volatile
1618  * values. For now, log error seems to be enough
1619  */
1620  }
1621 
1622  /* Reinstate the local_node_pos */
1623  local_node_pos = find_local_node_in_nodelist(totem_config);
1624  if (local_node_pos != -1) {
1625  icmap_set_uint32("nodelist.local_node_pos", local_node_pos);
1626  }
1627 
1628  icmap_set_uint8("config.totemconfig_reload_in_progress", 0);
1629  } else {
1630  icmap_set_uint8("config.totemconfig_reload_in_progress", 1);
1631  }
1632 }
1633 
1634 static void add_totem_config_notification(struct totem_config *totem_config)
1635 {
1637 
1638  icmap_track_add("totem.",
1640  totem_change_notify,
1641  totem_config,
1642  &icmap_track);
1643 
1644  icmap_track_add("config.reload_in_progress",
1646  totem_reload_notify,
1647  totem_config,
1648  &icmap_track);
1649 
1650  icmap_track_add("nodelist.node.",
1652  nodelist_dynamic_notify,
1653  (void *)totem_config,
1654  &icmap_track);
1655 }
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:1445
#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:1259
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