corosync 3.1.5
totemconfig.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2002-2005 MontaVista Software, Inc.
3 * Copyright (c) 2006-2019 Red Hat, Inc.
4 *
5 * All rights reserved.
6 *
7 * Author: Steven Dake (sdake@redhat.com)
8 * Jan Friesse (jfriesse@redhat.com)
9 * Chrissie Caulfield (ccaulfie@redhat.com)
10 *
11 * This software licensed under BSD license, the text of which follows:
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 *
16 * - Redistributions of source code must retain the above copyright notice,
17 * this list of conditions and the following disclaimer.
18 * - Redistributions in binary form must reproduce the above copyright notice,
19 * this list of conditions and the following disclaimer in the documentation
20 * and/or other materials provided with the distribution.
21 * - Neither the name of the MontaVista Software, Inc. nor the names of its
22 * contributors may be used to endorse or promote products derived from this
23 * software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35 * THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <config.h>
39
40#include <stdio.h>
41#include <string.h>
42#include <stdlib.h>
43#include <errno.h>
44#include <unistd.h>
45#include <sys/socket.h>
46#include <sys/types.h>
47#include <sys/stat.h>
48#include <fcntl.h>
49#include <ifaddrs.h>
50#include <netdb.h>
51#include <netinet/in.h>
52#include <arpa/inet.h>
53#include <sys/param.h>
54#include <sys/utsname.h>
55
56#include <corosync/swab.h>
57#include <qb/qblist.h>
58#include <qb/qbdefs.h>
59#include <libknet.h>
61#include <corosync/config.h>
62#include <corosync/logsys.h>
63#include <corosync/icmap.h>
64
65#include "util.h"
66#include "totemconfig.h"
67
68#define TOKEN_RETRANSMITS_BEFORE_LOSS_CONST 4
69#define TOKEN_TIMEOUT 3000
70#define TOKEN_WARNING 75
71#define TOKEN_COEFFICIENT 650
72#define JOIN_TIMEOUT 50
73#define MERGE_TIMEOUT 200
74#define DOWNCHECK_TIMEOUT 1000
75#define FAIL_TO_RECV_CONST 2500
76#define SEQNO_UNCHANGED_CONST 30
77#define MINIMUM_TIMEOUT (int)(1000/HZ)*3
78#define MINIMUM_TIMEOUT_HOLD (int)(MINIMUM_TIMEOUT * 0.8 - (1000/HZ))
79#define MAX_NETWORK_DELAY 50
80#define WINDOW_SIZE 50
81#define MAX_MESSAGES 17
82#define MISS_COUNT_CONST 5
83#define BLOCK_UNLISTED_IPS 1
84#define CANCEL_TOKEN_HOLD_ON_RETRANSMIT 0
85/* This constant is not used for knet */
86#define UDP_NETMTU 1500
87
88/* Currently all but PONG_COUNT match the defaults in libknet.h */
89#define KNET_PING_INTERVAL 1000
90#define KNET_PING_TIMEOUT 2000
91#define KNET_PING_PRECISION 2048
92#define KNET_PONG_COUNT 2
93#define KNET_PMTUD_INTERVAL 30
94#define KNET_DEFAULT_TRANSPORT KNET_TRANSPORT_UDP
95
96#define DEFAULT_PORT 5405
97
98static char error_string_response[768];
99
100static void add_totem_config_notification(struct totem_config *totem_config);
101
102static void *totem_get_param_by_name(struct totem_config *totem_config, const char *param_name)
103{
104 if (strcmp(param_name, "totem.token") == 0)
106 if (strcmp(param_name, "totem.token_warning") == 0)
108 if (strcmp(param_name, "totem.token_retransmit") == 0)
110 if (strcmp(param_name, "totem.hold") == 0)
112 if (strcmp(param_name, "totem.token_retransmits_before_loss_const") == 0)
114 if (strcmp(param_name, "totem.join") == 0)
115 return &totem_config->join_timeout;
116 if (strcmp(param_name, "totem.send_join") == 0)
118 if (strcmp(param_name, "totem.consensus") == 0)
120 if (strcmp(param_name, "totem.merge") == 0)
122 if (strcmp(param_name, "totem.downcheck") == 0)
124 if (strcmp(param_name, "totem.fail_recv_const") == 0)
126 if (strcmp(param_name, "totem.seqno_unchanged_const") == 0)
128 if (strcmp(param_name, "totem.heartbeat_failures_allowed") == 0)
130 if (strcmp(param_name, "totem.max_network_delay") == 0)
132 if (strcmp(param_name, "totem.window_size") == 0)
133 return &totem_config->window_size;
134 if (strcmp(param_name, "totem.max_messages") == 0)
135 return &totem_config->max_messages;
136 if (strcmp(param_name, "totem.miss_count_const") == 0)
138 if (strcmp(param_name, "totem.knet_pmtud_interval") == 0)
140 if (strcmp(param_name, "totem.knet_compression_threshold") == 0)
142 if (strcmp(param_name, "totem.knet_compression_level") == 0)
144 if (strcmp(param_name, "totem.knet_compression_model") == 0)
146 if (strcmp(param_name, "totem.block_unlisted_ips") == 0)
148 if (strcmp(param_name, "totem.cancel_token_hold_on_retransmit") == 0)
150
151 return NULL;
152}
153
154/*
155 * Read key_name from icmap. If key is not found or key_name == delete_key or if allow_zero is false
156 * and readed value is zero, default value is used and stored into totem_config.
157 */
158static void totem_volatile_config_set_uint32_value (struct totem_config *totem_config, icmap_map_t map,
159 const char *key_name, const char *deleted_key, unsigned int default_value,
160 int allow_zero_value)
161{
162 char runtime_key_name[ICMAP_KEYNAME_MAXLEN];
163
164 if (icmap_get_uint32_r(map, key_name, totem_get_param_by_name(totem_config, key_name)) != CS_OK ||
165 (deleted_key != NULL && strcmp(deleted_key, key_name) == 0) ||
166 (!allow_zero_value && *(uint32_t *)totem_get_param_by_name(totem_config, key_name) == 0)) {
167 *(uint32_t *)totem_get_param_by_name(totem_config, key_name) = default_value;
168 }
169
170 /*
171 * Store totem_config value to cmap runtime section
172 */
173 if (strlen("runtime.config.") + strlen(key_name) >= ICMAP_KEYNAME_MAXLEN) {
174 /*
175 * This shouldn't happen
176 */
177 return ;
178 }
179
180 strcpy(runtime_key_name, "runtime.config.");
181 strcat(runtime_key_name, key_name);
182
183 icmap_set_uint32_r(map, runtime_key_name, *(uint32_t *)totem_get_param_by_name(totem_config, key_name));
184}
185
186static void totem_volatile_config_set_int32_value (struct totem_config *totem_config, icmap_map_t map,
187 const char *key_name, const char *deleted_key, int default_value,
188 int allow_zero_value)
189{
190 char runtime_key_name[ICMAP_KEYNAME_MAXLEN];
191
192 if (icmap_get_int32_r(map, key_name, totem_get_param_by_name(totem_config, key_name)) != CS_OK ||
193 (deleted_key != NULL && strcmp(deleted_key, key_name) == 0) ||
194 (!allow_zero_value && *(int32_t *)totem_get_param_by_name(totem_config, key_name) == 0)) {
195 *(int32_t *)totem_get_param_by_name(totem_config, key_name) = default_value;
196 }
197
198 /*
199 * Store totem_config value to cmap runtime section
200 */
201 if (strlen("runtime.config.") + strlen(key_name) >= ICMAP_KEYNAME_MAXLEN) {
202 /*
203 * This shouldn't happen
204 */
205 return ;
206 }
207
208 strcpy(runtime_key_name, "runtime.config.");
209 strcat(runtime_key_name, key_name);
210
211 icmap_set_int32_r(map, runtime_key_name, *(int32_t *)totem_get_param_by_name(totem_config, key_name));
212}
213
214static void totem_volatile_config_set_string_value (struct totem_config *totem_config, icmap_map_t map,
215 const char *key_name, const char *deleted_key, const char *default_value)
216{
217 char runtime_key_name[ICMAP_KEYNAME_MAXLEN];
218 int res;
219 char *new_config_value;
220 const void *config_value;
221
222 config_value = totem_get_param_by_name(totem_config, key_name);
223
224 res = icmap_get_string_r(map, key_name, (char **)&new_config_value);
225 if (res != CS_OK ||
226 (deleted_key != NULL && strcmp(deleted_key, key_name) == 0)) {
227
228 /* Slightly pointless use of strncpy but it keeps coverity happy */
229 strncpy((char *)config_value, default_value, CONFIG_STRING_LEN_MAX);
230 } else {
231 strncpy((char *)config_value, new_config_value, CONFIG_STRING_LEN_MAX);
232 }
233 if (res == CS_OK) {
234 free(new_config_value);
235 }
236
237 /*
238 * Store totem_config value to cmap runtime section
239 */
240 if (strlen("runtime.config.") + strlen(key_name) >= ICMAP_KEYNAME_MAXLEN) {
241 /*
242 * This shouldn't happen
243 */
244 return ;
245 }
246
247 strcpy(runtime_key_name, "runtime.config.");
248 strcat(runtime_key_name, key_name);
249
250 (void)icmap_set_string_r(map, runtime_key_name, (char *)config_value);
251}
252
253/*
254 * Read string value stored in key_name from icmap, use it as a boolean (yes/no) type, convert it
255 * to integer value (1/0) and store into totem_config.
256 *
257 * If key is not found or key_name == delete_key default value is used
258 * and stored into totem_config.
259 */
260static void totem_volatile_config_set_boolean_value (struct totem_config *totem_config, icmap_map_t map,
261 const char *key_name, const char *deleted_key, unsigned int default_value)
262{
263 char runtime_key_name[ICMAP_KEYNAME_MAXLEN];
264 char *str;
265 int val;
266
267 str = NULL;
268 val = default_value;
269
270 if ((deleted_key != NULL && strcmp(deleted_key, key_name) == 0) ||
271 (icmap_get_string_r(map, key_name, &str) != CS_OK)) {
272 /*
273 * Do nothing. str is NULL (icmap_get_string ether not called or
274 * not changed str).
275 */
276 } else {
277 if (strcmp(str, "yes") == 0) {
278 val = 1;
279 } else if (strcmp(str, "no") == 0) {
280 val = 0;
281 }
282 free(str);
283 }
284
285 /*
286 * Store totem_config value to cmap runtime section
287 */
288 if (strlen("runtime.config.") + strlen(key_name) >= ICMAP_KEYNAME_MAXLEN) {
289 /*
290 * This shouldn't happen
291 */
292 return ;
293 }
294
295 strcpy(runtime_key_name, "runtime.config.");
296 strcat(runtime_key_name, key_name);
297
298 *(uint32_t *)totem_get_param_by_name(totem_config, key_name) = val;
299
300 icmap_set_uint32_r(map, runtime_key_name, val);
301}
302
303/*
304 * Read and validate config values from cmap and store them into totem_config. If key doesn't exists,
305 * default value is stored. deleted_key is name of key beeing processed by delete operation
306 * from cmap. It is considered as non existing even if it can be read. Can be NULL.
307 */
308void totem_volatile_config_read (struct totem_config *totem_config, icmap_map_t temp_map, const char *deleted_key)
309{
310 uint32_t u32;
311
312 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.token_retransmits_before_loss_const", deleted_key,
314
315 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.token", deleted_key, TOKEN_TIMEOUT, 0);
316
317 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.token_warning", deleted_key, TOKEN_WARNING, 1);
318
320 u32 = TOKEN_COEFFICIENT;
321 icmap_get_uint32_r(temp_map, "totem.token_coefficient", &u32);
323
324 /*
325 * Store totem_config value to cmap runtime section
326 */
327 icmap_set_uint32_r(temp_map, "runtime.config.totem.token", totem_config->token_timeout);
328 }
329
330 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.max_network_delay", deleted_key, MAX_NETWORK_DELAY, 0);
331
332 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.window_size", deleted_key, WINDOW_SIZE, 0);
333
334 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.max_messages", deleted_key, MAX_MESSAGES, 0);
335
336 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.miss_count_const", deleted_key, MISS_COUNT_CONST, 0);
337 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.knet_pmtud_interval", deleted_key, KNET_PMTUD_INTERVAL, 0);
338
339 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.token_retransmit", deleted_key,
341
342 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.hold", deleted_key,
343 (int)(totem_config->token_retransmit_timeout * 0.8 - (1000/HZ)), 0);
344
345 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.join", deleted_key, JOIN_TIMEOUT, 0);
346
347 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.consensus", deleted_key,
348 (int)(float)(1.2 * totem_config->token_timeout), 0);
349
350 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.merge", deleted_key, MERGE_TIMEOUT, 0);
351
352 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.downcheck", deleted_key, DOWNCHECK_TIMEOUT, 0);
353
354 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.fail_recv_const", deleted_key, FAIL_TO_RECV_CONST, 0);
355
356 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.seqno_unchanged_const", deleted_key,
358
359 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.send_join", deleted_key, 0, 1);
360
361 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.heartbeat_failures_allowed", deleted_key, 0, 1);
362
363 totem_volatile_config_set_uint32_value(totem_config, temp_map, "totem.knet_compression_threshold", deleted_key, 0, 1);
364
365 totem_volatile_config_set_int32_value(totem_config, temp_map, "totem.knet_compression_level", deleted_key, 0, 1);
366
367 totem_volatile_config_set_string_value(totem_config, temp_map, "totem.knet_compression_model", deleted_key, "none");
368
369 totem_volatile_config_set_boolean_value(totem_config, temp_map, "totem.block_unlisted_ips", deleted_key,
371
372 totem_volatile_config_set_boolean_value(totem_config, temp_map, "totem.cancel_token_hold_on_retransmit",
374}
375
378 icmap_map_t temp_map,
379 const char **error_string)
380{
381 /* Static just to keep them off the stack */
382 static char local_error_reason[512];
383 static char addr_str_buf[INET6_ADDRSTRLEN];
384 const char *error_reason = local_error_reason;
385 char name_key[ICMAP_KEYNAME_MAXLEN];
386 char *name_str;
387 int i, j, num_configured, members;
388 uint32_t tmp_config_value;
389
391 snprintf (local_error_reason, sizeof(local_error_reason),
392 "The max_network_delay parameter (%d ms) may not be less than (%d ms).",
394 goto parse_error;
395 }
396
398 snprintf (local_error_reason, sizeof(local_error_reason),
399 "The token timeout parameter (%d ms) may not be less than (%d ms).",
401 goto parse_error;
402 }
403
405 snprintf (local_error_reason, sizeof(local_error_reason),
406 "The token warning parameter (%d%%) must be between 0 (disabled) and 100.",
408 goto parse_error;
409 }
410
412 if (icmap_get_uint32_r(temp_map, "totem.token_retransmit", &tmp_config_value) == CS_OK) {
413 snprintf (local_error_reason, sizeof(local_error_reason),
414 "The token retransmit timeout parameter (%d ms) may not be less than (%d ms).",
416 goto parse_error;
417 } else {
418 snprintf (local_error_reason, sizeof(local_error_reason),
419 "Not appropriate token or token_retransmits_before_loss_const value set");
420 goto parse_error;
421 }
422 }
423
425 snprintf (local_error_reason, sizeof(local_error_reason),
426 "The token hold timeout parameter (%d ms) may not be less than (%d ms).",
428 goto parse_error;
429 }
430
432 snprintf (local_error_reason, sizeof(local_error_reason),
433 "The join timeout parameter (%d ms) may not be less than (%d ms).",
435 goto parse_error;
436 }
437
439 snprintf (local_error_reason, sizeof(local_error_reason),
440 "The consensus timeout parameter (%d ms) may not be less than (%d ms).",
442 goto parse_error;
443 }
444
446 snprintf (local_error_reason, sizeof(local_error_reason),
447 "The consensus timeout parameter (%d ms) may not be less than join timeout (%d ms).",
449 goto parse_error;
450 }
451
453 snprintf (local_error_reason, sizeof(local_error_reason),
454 "The merge timeout parameter (%d ms) may not be less than (%d ms).",
456 goto parse_error;
457 }
458
460 snprintf (local_error_reason, sizeof(local_error_reason),
461 "The downcheck timeout parameter (%d ms) may not be less than (%d ms).",
463 goto parse_error;
464 }
465
466 /* Check that we have nodelist 'name' if there is more than one link */
467 num_configured = 0;
468 members = -1;
469 for (i = 0; i < INTERFACE_MAX; i++) {
471 if (num_configured == 0) {
473 }
474 num_configured++;
475 }
476 }
477
478 if (num_configured > 1) {
479 /*
480 * This assert is here just to make compiler happy
481 */
482 assert(members != -1);
483 for (i=0; i < members; i++) {
484 snprintf(name_key, sizeof(name_key), "nodelist.node.%d.name", i);
485
486 if (icmap_get_string_r(temp_map, name_key, &name_str) != CS_OK) {
487 snprintf (local_error_reason, sizeof(local_error_reason),
488 "for a multi-link configuration, all nodes must have a 'name' attribute");
489 goto parse_error;
490 }
491
492 free(name_str);
493 }
494
495 for (i=0; i < INTERFACE_MAX; i++) {
497 continue;
498 }
499 if (totem_config->interfaces[i].member_count != members) {
500 snprintf (local_error_reason, sizeof(local_error_reason),
501 "Not all nodes have the same number of links");
502 goto parse_error;
503 }
504 }
505 }
506
507 /* Verify that all nodes on the same link have the same IP family */
508 for (i=0; i < INTERFACE_MAX; i++) {
509 for (j=1; j<totem_config->interfaces[i].member_count; j++) {
513 memcpy(addr_str_buf,
515 sizeof(addr_str_buf));
516
517 snprintf (local_error_reason, sizeof(local_error_reason),
518 "Nodes for link %d have different IP families "
519 "(compared %s with %s)", i,
520 addr_str_buf,
522 goto parse_error;
523 }
524 }
525 }
526 }
527
528 return 0;
529
530parse_error:
531 snprintf (error_string_response, sizeof(error_string_response),
532 "parse error in config: %s\n", error_reason);
533 *error_string = error_string_response;
534 return (-1);
535
536}
537
538static int totem_get_crypto(struct totem_config *totem_config, icmap_map_t map, const char **error_string)
539{
540 char *str;
541 const char *tmp_cipher;
542 const char *tmp_hash;
543 const char *tmp_model;
544 char *crypto_model_str;
545 int res = 0;
546
547 tmp_hash = "none";
548 tmp_cipher = "none";
549 tmp_model = "none";
550
551 crypto_model_str = NULL;
552 if (icmap_get_string_r(map, "totem.crypto_model", &crypto_model_str) == CS_OK) {
553 tmp_model = crypto_model_str;
554 } else {
555 tmp_model = "nss";
556 }
557
558 if (icmap_get_string_r(map, "totem.secauth", &str) == CS_OK) {
559 if (strcmp(str, "on") == 0) {
560 tmp_cipher = "aes256";
561 tmp_hash = "sha256";
562 }
563 free(str);
564 }
565
566 if (icmap_get_string_r(map, "totem.crypto_cipher", &str) == CS_OK) {
567 if (strcmp(str, "none") == 0) {
568 tmp_cipher = "none";
569 }
570 if (strcmp(str, "aes256") == 0) {
571 tmp_cipher = "aes256";
572 }
573 if (strcmp(str, "aes192") == 0) {
574 tmp_cipher = "aes192";
575 }
576 if (strcmp(str, "aes128") == 0) {
577 tmp_cipher = "aes128";
578 }
579 free(str);
580 }
581
582 if (icmap_get_string_r(map, "totem.crypto_hash", &str) == CS_OK) {
583 if (strcmp(str, "none") == 0) {
584 tmp_hash = "none";
585 }
586 if (strcmp(str, "md5") == 0) {
587 tmp_hash = "md5";
588 }
589 if (strcmp(str, "sha1") == 0) {
590 tmp_hash = "sha1";
591 }
592 if (strcmp(str, "sha256") == 0) {
593 tmp_hash = "sha256";
594 }
595 if (strcmp(str, "sha384") == 0) {
596 tmp_hash = "sha384";
597 }
598 if (strcmp(str, "sha512") == 0) {
599 tmp_hash = "sha512";
600 }
601 free(str);
602 }
603
604 if ((strcmp(tmp_cipher, "none") != 0) &&
605 (strcmp(tmp_hash, "none") == 0)) {
606 *error_string = "crypto_cipher requires crypto_hash with value other than none";
607 res = -1;
608
609 goto out_free_crypto_model_str;
610 }
611
612 if (strcmp(tmp_model, "none") == 0) {
613 /*
614 * Shouldn't happen because it is handled by coroparse
615 */
616 *error_string = "invalid crypto_model";
617 res = -1;
618
619 goto out_free_crypto_model_str;
620 }
621
622 if (strcmp(tmp_cipher, totem_config->crypto_cipher_type) ||
623 strcmp(tmp_hash, totem_config->crypto_hash_type) ||
624 strcmp(tmp_model, totem_config->crypto_model)) {
626 }
627
628 strncpy(totem_config->crypto_cipher_type, tmp_cipher, CONFIG_STRING_LEN_MAX - 1);
630
631 strncpy(totem_config->crypto_hash_type, tmp_hash, CONFIG_STRING_LEN_MAX - 1);
633
634 strncpy(totem_config->crypto_model, tmp_model, CONFIG_STRING_LEN_MAX - 1);
636
637out_free_crypto_model_str:
638 free(crypto_model_str);
639
640 return (res);
641}
642
643static int nodelist_byname(icmap_map_t map, const char *find_name, int strip_domain)
644{
645 icmap_iter_t iter;
646 const char *iter_key;
647 char name_str[ICMAP_KEYNAME_MAXLEN];
648 int res = 0;
649 unsigned int node_pos;
650 char *name;
651 unsigned int namelen;
652
653 iter = icmap_iter_init_r(map, "nodelist.node.");
654 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
655 res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, name_str);
656 if (res != 2) {
657 continue;
658 }
659 /* ring0_addr is allowed as a fallback */
660 if (strcmp(name_str, "name") && strcmp(name_str, "ring0_addr")) {
661 continue;
662 }
663 if (icmap_get_string_r(map, iter_key, &name) != CS_OK) {
664 continue;
665 }
666 namelen = strlen(name);
667
668 if (strip_domain) {
669 char *dot;
670 dot = strchr(name, '.');
671 if (dot) {
672 namelen = dot - name;
673 }
674 }
675 if (strncmp(find_name, name, namelen) == 0 &&
676 strlen(find_name) == namelen) {
678 return node_pos;
679 }
680 }
682 return -1;
683}
684
685/* Compare two addresses - only address part (sin_addr/sin6_addr) is checked */
686static int ipaddr_equal(const struct sockaddr *addr1, const struct sockaddr *addr2)
687{
688 int addrlen = 0;
689 const void *addr1p, *addr2p;
690
691 if (addr1->sa_family != addr2->sa_family)
692 return 0;
693
694 switch (addr1->sa_family) {
695 case AF_INET:
696 addrlen = sizeof(struct in_addr);
697 addr1p = &((struct sockaddr_in *)addr1)->sin_addr;
698 addr2p = &((struct sockaddr_in *)addr2)->sin_addr;
699 break;
700 case AF_INET6:
701 addrlen = sizeof(struct in6_addr);
702 addr1p = &((struct sockaddr_in6 *)addr1)->sin6_addr;
703 addr2p = &((struct sockaddr_in6 *)addr2)->sin6_addr;
704 break;
705 default:
706 assert(0);
707 }
708
709 return (memcmp(addr1p, addr2p, addrlen) == 0);
710}
711
712
713/* Finds the local node and returns its position in the nodelist.
714 * Uses nodelist.local_node_pos as a cache to save effort
715 */
716static int find_local_node(icmap_map_t map, int use_cache)
717{
718 char nodename2[PATH_MAX];
719 char name_str[ICMAP_KEYNAME_MAXLEN];
720 icmap_iter_t iter;
721 const char *iter_key;
722 unsigned int cached_pos;
723 char *dot = NULL;
724 const char *node;
725 struct ifaddrs *ifa, *ifa_list;
726 struct sockaddr *sa;
727 int found = 0;
728 int node_pos = -1;
729 int res;
730 struct utsname utsname;
731
732 /* Check for cached value first */
733 if (use_cache) {
734 if (icmap_get_uint32("nodelist.local_node_pos", &cached_pos) == CS_OK) {
735 return cached_pos;
736 }
737 }
738
739 res = uname(&utsname);
740 if (res) {
741 return -1;
742 }
743 node = utsname.nodename;
744
745 /* 1. Exact match */
746 node_pos = nodelist_byname(map, node, 0);
747 if (node_pos > -1) {
748 found = 1;
749 goto ret_found;
750 }
751
752 /* 2. Try to match with increasingly more
753 * specific versions of it
754 */
755 strcpy(nodename2, node);
756 dot = strrchr(nodename2, '.');
757 while (dot) {
758 *dot = '\0';
759
760 node_pos = nodelist_byname(map, nodename2, 0);
761 if (node_pos > -1) {
762 found = 1;
763 goto ret_found;
764 }
765 dot = strrchr(nodename2, '.');
766 }
767
768 node_pos = nodelist_byname(map, nodename2, 1);
769 if (node_pos > -1) {
770 found = 1;
771 goto ret_found;
772 }
773
774 /*
775 * The corosync.conf name may not be related to uname at all,
776 * they may match a hostname on some network interface.
777 */
778 if (getifaddrs(&ifa_list))
779 return -1;
780
781 for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
782 socklen_t salen = 0;
783
784 /* Restore this */
785 strcpy(nodename2, node);
786 sa = ifa->ifa_addr;
787 if (!sa) {
788 continue;
789 }
790 if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) {
791 continue;
792 }
793
794 if (sa->sa_family == AF_INET) {
795 salen = sizeof(struct sockaddr_in);
796 }
797 if (sa->sa_family == AF_INET6) {
798 salen = sizeof(struct sockaddr_in6);
799 }
800
801 if (getnameinfo(sa, salen,
802 nodename2, sizeof(nodename2),
803 NULL, 0, 0) == 0) {
804
805 node_pos = nodelist_byname(map, nodename2, 0);
806 if (node_pos > -1) {
807 found = 1;
808 goto out;
809 }
810
811 /* Truncate this name and try again */
812 dot = strchr(nodename2, '.');
813 if (dot) {
814 *dot = '\0';
815
816 node_pos = nodelist_byname(map, nodename2, 0);
817 if (node_pos > -1) {
818 found = 1;
819 goto out;
820 }
821 }
822 }
823
824 /* See if it's the IP address that's in corosync.conf */
825 if (getnameinfo(sa, sizeof(*sa),
826 nodename2, sizeof(nodename2),
827 NULL, 0, NI_NUMERICHOST))
828 continue;
829
830 node_pos = nodelist_byname(map, nodename2, 0);
831 if (node_pos > -1) {
832 found = 1;
833 goto out;
834 }
835 }
836
837 out:
838 if (found) {
839 freeifaddrs(ifa_list);
840 goto ret_found;
841 }
842
843 /*
844 * This section covers the usecase where the nodename specified in cluster.conf
845 * is an alias specified in /etc/hosts. For example:
846 * <ipaddr> hostname alias1 alias2
847 * and <clusternode name="alias2">
848 * the above calls use uname and getnameinfo does not return aliases.
849 * here we take the name specified in cluster.conf, resolve it to an address
850 * and then compare against all known local ip addresses.
851 * if we have a match, we found our nodename. In theory this chunk of code
852 * could replace all the checks above, but let's avoid any possible regressions
853 * and use it as last.
854 */
855
856 iter = icmap_iter_init_r(map, "nodelist.node.");
857 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
858 char *dbnodename = NULL;
859 struct addrinfo hints;
860 struct addrinfo *result = NULL, *rp = NULL;
861
862 res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, name_str);
863 if (res != 2) {
864 continue;
865 }
866 /* 'ring0_addr' is allowed as a fallback, but 'name' will be found first
867 * because the names are in alpha order.
868 */
869 if (strcmp(name_str, "name") && strcmp(name_str, "ring0_addr")) {
870 continue;
871 }
872 if (icmap_get_string_r(map, iter_key, &dbnodename) != CS_OK) {
873 continue;
874 }
875
876 memset(&hints, 0, sizeof(struct addrinfo));
877 hints.ai_family = AF_UNSPEC;
878 hints.ai_socktype = SOCK_DGRAM;
879 hints.ai_flags = 0;
880 hints.ai_protocol = IPPROTO_UDP;
881
882 if (getaddrinfo(dbnodename, NULL, &hints, &result)) {
883 continue;
884 }
885
886 for (rp = result; rp != NULL; rp = rp->ai_next) {
887 for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
888 if (ifa->ifa_addr &&
889 ipaddr_equal(rp->ai_addr, ifa->ifa_addr)) {
890 freeaddrinfo(result);
891 found = 1;
892 goto out2;
893 }
894 }
895 }
896
897 freeaddrinfo(result);
898 }
899out2:
901 freeifaddrs(ifa_list);
902
903ret_found:
904 if (found) {
905 res = icmap_set_uint32_r(map, "nodelist.local_node_pos", node_pos);
906 }
907
908 return node_pos;
909}
910
911static enum totem_ip_version_enum totem_config_get_ip_version(struct totem_config *totem_config)
912{
913 enum totem_ip_version_enum res;
914 char *str;
915
917
919 res = TOTEM_IP_VERSION_4;
920 }
921
922 if (icmap_get_string("totem.ip_version", &str) == CS_OK) {
923 if (strcmp(str, "ipv4") == 0) {
924 res = TOTEM_IP_VERSION_4;
925 }
926 if (strcmp(str, "ipv6") == 0) {
927 res = TOTEM_IP_VERSION_6;
928 }
929 if (strcmp(str, "ipv6-4") == 0) {
931 }
932 if (strcmp(str, "ipv4-6") == 0) {
934 }
935 free(str);
936 }
937
938 return (res);
939}
940
941static uint16_t generate_cluster_id (const char *cluster_name)
942{
943 int i;
944 int value = 0;
945
946 for (i = 0; i < strlen(cluster_name); i++) {
947 value <<= 1;
948 value += cluster_name[i];
949 }
950
951 return (value & 0xFFFF);
952}
953
954static int get_cluster_mcast_addr (
955 const char *cluster_name,
956 unsigned int linknumber,
957 enum totem_ip_version_enum ip_version,
958 struct totem_ip_address *res)
959{
960 uint16_t clusterid;
961 char addr[INET6_ADDRSTRLEN + 1];
962 int err;
963
964 if (cluster_name == NULL) {
965 return (-1);
966 }
967
968 clusterid = generate_cluster_id(cluster_name) + linknumber;
969 memset (res, 0, sizeof(*res));
970
971 switch (ip_version) {
974 snprintf(addr, sizeof(addr), "239.192.%d.%d", clusterid >> 8, clusterid % 0xFF);
975 break;
978 snprintf(addr, sizeof(addr), "ff15::%x", clusterid);
979 break;
980 default:
981 /*
982 * Unknown family
983 */
984 return (-1);
985 }
986
987 err = totemip_parse (res, addr, ip_version);
988
989 return (err);
990}
991
992static unsigned int generate_nodeid(
994 char *addr)
995{
996 unsigned int nodeid;
997 struct totem_ip_address totemip;
998
999 /* AF_INET hard-coded here because auto-generated nodeids
1000 are only for IPv4 */
1001 if (totemip_parse(&totemip, addr, TOTEM_IP_VERSION_4) != 0)
1002 return -1;
1003
1004 memcpy (&nodeid, &totemip.addr, sizeof (unsigned int));
1005
1006#if __BYTE_ORDER == __LITTLE_ENDIAN
1007 nodeid = swab32 (nodeid);
1008#endif
1009
1011 nodeid &= 0x7FFFFFFF;
1012 }
1013 return nodeid;
1014}
1015
1016static int check_for_duplicate_nodeids(
1017 struct totem_config *totem_config,
1018 const char **error_string)
1019{
1020 icmap_iter_t iter;
1021 icmap_iter_t subiter;
1022 const char *iter_key;
1023 int res = 0;
1024 int retval = 0;
1025 char tmp_key[ICMAP_KEYNAME_MAXLEN];
1026 char *ring0_addr=NULL;
1027 char *ring0_addr1=NULL;
1028 unsigned int node_pos;
1029 unsigned int node_pos1;
1030 unsigned int last_node_pos = -1;
1031 unsigned int nodeid;
1032 unsigned int nodeid1;
1033 int autogenerated;
1034
1035 iter = icmap_iter_init("nodelist.node.");
1036 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
1037 res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key);
1038 if (res != 2) {
1039 continue;
1040 }
1041
1042 /*
1043 * This relies on the fact the icmap keys are always returned in order
1044 * so all of the keys for a node will be grouped together. We're basically
1045 * just running the code below once for each node.
1046 */
1047 if (last_node_pos == node_pos) {
1048 continue;
1049 }
1050 last_node_pos = node_pos;
1051
1052 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos);
1053 autogenerated = 0;
1054
1055 /* Generated nodeids are only allowed for UDP/UDPU so ring0_addr is valid here */
1056 if (icmap_get_uint32(tmp_key, &nodeid) != CS_OK) {
1057
1058 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", node_pos);
1059 if (icmap_get_string(tmp_key, &ring0_addr) != CS_OK) {
1060 continue;
1061 }
1062
1063 /* Generate nodeid so we can check that auto-generated nodeids don't clash either */
1064 nodeid = generate_nodeid(totem_config, ring0_addr);
1065 if (nodeid == -1) {
1066 continue;
1067 }
1068 autogenerated = 1;
1069 }
1070
1071 node_pos1 = 0;
1072 subiter = icmap_iter_init("nodelist.node.");
1073 while (((iter_key = icmap_iter_next(subiter, NULL, NULL)) != NULL) && (node_pos1 < node_pos)) {
1074 res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos1, tmp_key);
1075 if ((res != 2) || (node_pos1 >= node_pos)) {
1076 continue;
1077 }
1078
1079 if (strcmp(tmp_key, "nodeid") != 0) {
1080 continue;
1081 }
1082
1083 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos1);
1084 if (icmap_get_uint32(tmp_key, &nodeid1) != CS_OK) {
1085
1086 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", node_pos1);
1087 if (icmap_get_string(tmp_key, &ring0_addr1) != CS_OK) {
1088 continue;
1089 }
1090 nodeid1 = generate_nodeid(totem_config, ring0_addr1);
1091 if (nodeid1 == -1) {
1092 continue;
1093 }
1094 }
1095
1096 if (nodeid == nodeid1) {
1097 retval = -1;
1098 snprintf (error_string_response, sizeof(error_string_response),
1099 "Nodeid %u%s%s%s appears twice in corosync.conf", nodeid,
1100 autogenerated?"(autogenerated from ":"",
1101 autogenerated?ring0_addr:"",
1102 autogenerated?")":"");
1103 *error_string = error_string_response;
1104 break;
1105 }
1106 }
1107 icmap_iter_finalize(subiter);
1108 }
1109 icmap_iter_finalize(iter);
1110 return retval;
1111}
1112
1113
1114/*
1115 * This needs to be done last of all. It would be nice to do it when reading the
1116 * interface params, but the totem params need to have them to be read first. We
1117 * need both, so this is a way round that circular dependancy.
1118 */
1119static void calc_knet_ping_timers(struct totem_config *totem_config)
1120{
1121 char runtime_key_name[ICMAP_KEYNAME_MAXLEN];
1122 int interface;
1123
1124 for (interface = 0; interface < INTERFACE_MAX; interface++) {
1125
1126 if (totem_config->interfaces[interface].configured) {
1127 if (!totem_config->interfaces[interface].knet_pong_count) {
1129 }
1130 if (!totem_config->interfaces[interface].knet_ping_timeout) {
1133 }
1134 snprintf(runtime_key_name, sizeof(runtime_key_name),
1135 "runtime.config.totem.interface.%d.knet_ping_timeout", interface);
1136 icmap_set_uint32(runtime_key_name, totem_config->interfaces[interface].knet_ping_timeout);
1137
1138 if (!totem_config->interfaces[interface].knet_ping_interval) {
1141 }
1142 snprintf(runtime_key_name, sizeof(runtime_key_name),
1143 "runtime.config.totem.interface.%d.knet_ping_interval", interface);
1144 icmap_set_uint32(runtime_key_name, totem_config->interfaces[interface].knet_ping_interval);
1145 }
1146 }
1147}
1148
1149/*
1150 * Compute difference between two set of totem interface arrays and commit it.
1151 * set1 and set2
1152 * are changed so for same ring, ip existing in both set1 and set2 are cleared
1153 * (set to 0), and ips which are only in set1 or set2 remains untouched.
1154 * totempg_node_add/remove is called.
1155 */
1156static void compute_and_set_totempg_interfaces(struct totem_interface *set1,
1157 struct totem_interface *set2)
1158{
1159 int ring_no, set1_pos, set2_pos;
1160 struct totem_ip_address empty_ip_address;
1161
1162 memset(&empty_ip_address, 0, sizeof(empty_ip_address));
1163
1164 for (ring_no = 0; ring_no < INTERFACE_MAX; ring_no++) {
1165 if (!set1[ring_no].configured && !set2[ring_no].configured) {
1166 continue;
1167 }
1168
1169 for (set1_pos = 0; set1_pos < set1[ring_no].member_count; set1_pos++) {
1170 for (set2_pos = 0; set2_pos < set2[ring_no].member_count; set2_pos++) {
1171 /*
1172 * For current ring_no remove all set1 items existing
1173 * in set2
1174 */
1175 if (memcmp(&set1[ring_no].member_list[set1_pos],
1176 &set2[ring_no].member_list[set2_pos],
1177 sizeof(struct totem_ip_address)) == 0) {
1178 memset(&set1[ring_no].member_list[set1_pos], 0,
1179 sizeof(struct totem_ip_address));
1180 memset(&set2[ring_no].member_list[set2_pos], 0,
1181 sizeof(struct totem_ip_address));
1182 }
1183 }
1184 }
1185 }
1186
1187 for (ring_no = 0; ring_no < INTERFACE_MAX; ring_no++) {
1188 for (set1_pos = 0; set1_pos < set1[ring_no].member_count; set1_pos++) {
1189 /*
1190 * All items which remain in set1 and don't exist in set2 any more
1191 * have to be removed.
1192 */
1193 if (memcmp(&set1[ring_no].member_list[set1_pos], &empty_ip_address, sizeof(empty_ip_address)) != 0) {
1195 "removing dynamic member %s for ring %u",
1196 totemip_print(&set1[ring_no].member_list[set1_pos]),
1197 ring_no);
1198
1199 totempg_member_remove(&set1[ring_no].member_list[set1_pos], ring_no);
1200 }
1201 }
1202 if (!set2[ring_no].configured) {
1203 continue;
1204 }
1205 for (set2_pos = 0; set2_pos < set2[ring_no].member_count; set2_pos++) {
1206 /*
1207 * All items which remain in set2 and don't exist in set1 are new nodes
1208 * and have to be added.
1209 */
1210 if (memcmp(&set2[ring_no].member_list[set2_pos], &empty_ip_address, sizeof(empty_ip_address)) != 0) {
1212 "adding dynamic member %s for ring %u",
1213 totemip_print(&set2[ring_no].member_list[set2_pos]),
1214 ring_no);
1215
1216 totempg_member_add(&set2[ring_no].member_list[set2_pos], ring_no);
1217 }
1218 }
1219 }
1220}
1221
1222/*
1223 * Configure parameters for links
1224 */
1225static void configure_link_params(struct totem_config *totem_config, icmap_map_t map)
1226{
1227 int i;
1228 char tmp_key[ICMAP_KEYNAME_MAXLEN];
1229 char *addr_string;
1230 int err;
1231 int local_node_pos = find_local_node(map, 0);
1232
1233 for (i = 0; i<INTERFACE_MAX; i++) {
1235 continue;
1236 }
1237
1238 log_printf(LOGSYS_LEVEL_DEBUG, "Configuring link %d params\n", i);
1239
1240 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring%u_addr", local_node_pos, i);
1241 if (icmap_get_string_r(map, tmp_key, &addr_string) != CS_OK) {
1242 continue;
1243 }
1244
1246 if (err != 0) {
1247 continue;
1248 }
1250
1251 /* In case this is a new link, fill in the defaults if there was no interface{} section for it */
1254
1255 /* knet_ping_interval & knet_ping_timeout are set later once we know all the other params */
1261 totem_config->interfaces[i].knet_transport = KNET_TRANSPORT_UDP;
1264 }
1265}
1266
1267
1268static void configure_totem_links(struct totem_config *totem_config, icmap_map_t map)
1269{
1270 int i;
1271
1272 for (i = 0; i<INTERFACE_MAX; i++) {
1274 continue;
1275 }
1276
1277 log_printf(LOGSYS_LEVEL_INFO, "Configuring link %d\n", i);
1278
1280 }
1281}
1282
1283/* Check for differences in config that can't be done on-the-fly and print an error */
1284static int check_things_have_not_changed(struct totem_config *totem_config, const char **error_string)
1285{
1286 int i,j,k;
1287 const char *ip_str;
1288 char addr_buf[INET6_ADDRSTRLEN];
1289 int changed = 0;
1290
1291 for (i = 0; i<INTERFACE_MAX; i++) {
1297 "New config has different knet transport for link %d. Internal value was NOT changed.\n", i);
1298 changed = 1;
1299 }
1300
1301 /* Check each nodeid in the new configuration and make sure its IP address on this link has not changed */
1302 for (j=0; j < totem_config->interfaces[i].member_count; j++) {
1303 for (k=0; k < totem_config->orig_interfaces[i].member_count; k++) {
1304
1307
1308 /* Found our nodeid - check the IP address */
1309 if (memcmp(&totem_config->interfaces[i].member_list[j],
1311 sizeof(struct totem_ip_address))) {
1312
1314
1315 /* if ip_str is NULL then the old address was invalid and is allowed to change */
1316 if (ip_str) {
1317 strncpy(addr_buf, ip_str, sizeof(addr_buf));
1318 addr_buf[sizeof(addr_buf) - 1] = '\0';
1320 "new config has different address for link %d (addr changed from %s to %s). Internal value was NOT changed.\n",
1321 i, addr_buf, totemip_print(&totem_config->interfaces[i].member_list[j]));
1322 changed = 1;
1323 }
1324 }
1325 }
1326 }
1327 }
1328 }
1329 }
1330
1331 if (changed) {
1332 snprintf (error_string_response, sizeof(error_string_response),
1333 "To reconfigure an interface it must be deleted and recreated. A working interface needs to be available to corosync at all times");
1334 *error_string = error_string_response;
1335 return -1;
1336 }
1337 return 0;
1338}
1339
1340
1341static int put_nodelist_members_to_config(struct totem_config *totem_config, icmap_map_t map,
1342 int reload, const char **error_string)
1343{
1344 icmap_iter_t iter, iter2;
1345 const char *iter_key, *iter_key2;
1346 int res = 0;
1347 unsigned int node_pos;
1348 char tmp_key[ICMAP_KEYNAME_MAXLEN];
1349 char tmp_key2[ICMAP_KEYNAME_MAXLEN];
1350 char *node_addr_str;
1351 int member_count;
1352 unsigned int linknumber = 0;
1353 int i, j;
1354 int last_node_pos = -1;
1355
1356 /* Clear out nodelist so we can put the new one in if needed */
1357 for (i = 0; i < INTERFACE_MAX; i++) {
1358 for (j = 0; j < PROCESSOR_COUNT_MAX; j++) {
1359 memset(&totem_config->interfaces[i].member_list[j], 0, sizeof(struct totem_ip_address));
1360 }
1362 }
1363
1364 iter = icmap_iter_init_r(map, "nodelist.node.");
1365 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
1366 res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key);
1367 if (res != 2) {
1368 continue;
1369 }
1370 /* If it's the same as the last node_pos then skip it */
1371 if (node_pos == last_node_pos) {
1372 continue;
1373 }
1374 last_node_pos = node_pos;
1375
1376 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.", node_pos);
1377 iter2 = icmap_iter_init_r(map, tmp_key);
1378 while ((iter_key2 = icmap_iter_next(iter2, NULL, NULL)) != NULL) {
1379 unsigned int nodeid;
1380 char *str;
1381
1382 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos);
1383 if (icmap_get_uint32_r(map, tmp_key, &nodeid) != CS_OK) {
1384 nodeid = 0;
1385 }
1386
1387 res = sscanf(iter_key2, "nodelist.node.%u.ring%u%s", &node_pos, &linknumber, tmp_key2);
1388 if (res != 3 || strcmp(tmp_key2, "_addr") != 0) {
1389 continue;
1390 }
1391 if (linknumber >= INTERFACE_MAX) {
1392 snprintf (error_string_response, sizeof(error_string_response),
1393 "parse error in config: interface ring number %u is bigger than allowed maximum %u\n",
1394 linknumber, INTERFACE_MAX - 1);
1395 *error_string = error_string_response;
1396
1397 icmap_iter_finalize(iter2);
1398 icmap_iter_finalize(iter);
1399 return (-1);
1400 }
1401
1402 if (icmap_get_string_r(map, iter_key2, &node_addr_str) != CS_OK) {
1403 continue;
1404 }
1405
1406 /* Generate nodeids if they are not provided and transport is UDP/U */
1407 if (!nodeid &&
1410 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", node_pos);
1411 if (icmap_get_string_r(map, tmp_key, &str) == CS_OK) {
1412 nodeid = generate_nodeid(totem_config, str);
1413 if (nodeid == -1) {
1414 sprintf(error_string_response,
1415 "An IPV6 network requires that a node ID be specified "
1416 "for address '%s'.", node_addr_str);
1417 *error_string = error_string_response;
1418 free(str);
1419
1420 return (-1);
1421 }
1422
1424 "Generated nodeid = " CS_PRI_NODE_ID " for %s", nodeid, str);
1425
1426 free(str);
1427 /*
1428 * Put nodeid back to nodelist to make cfgtool work
1429 */
1430 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos);
1431 /*
1432 * Not critical
1433 */
1434 (void)icmap_set_uint32_r(map, tmp_key, nodeid);
1435 }
1436 }
1437
1439 sprintf(error_string_response,
1440 "Knet requires an explicit nodeid to be specified "
1441 "for address '%s'.", node_addr_str);
1442 *error_string = error_string_response;
1443
1444 return (-1);
1445 }
1446
1447 if (totem_config->transport_number == TOTEM_TRANSPORT_KNET && nodeid >= KNET_MAX_HOST) {
1448 sprintf(error_string_response,
1449 "Knet requires nodeid to be less than %u "
1450 "for address '%s'.", KNET_MAX_HOST, node_addr_str);
1451 *error_string = error_string_response;
1452
1453 return (-1);
1454 }
1455
1456 member_count = totem_config->interfaces[linknumber].member_count;
1457 res = totemip_parse(&totem_config->interfaces[linknumber].member_list[member_count],
1458 node_addr_str, totem_config->ip_version);
1459 if (res == 0) {
1460 totem_config->interfaces[linknumber].member_list[member_count].nodeid = nodeid;
1461 totem_config->interfaces[linknumber].member_count++;
1462 totem_config->interfaces[linknumber].configured = 1;
1463 } else {
1464 sprintf(error_string_response, "failed to parse node address '%s'\n", node_addr_str);
1465 *error_string = error_string_response;
1466
1467 memset(&totem_config->interfaces[linknumber].member_list[member_count], 0,
1468 sizeof(struct totem_ip_address));
1469
1470 free(node_addr_str);
1471 icmap_iter_finalize(iter2);
1472 icmap_iter_finalize(iter);
1473 return -1;
1474 }
1475
1476 free(node_addr_str);
1477 }
1478
1479 icmap_iter_finalize(iter2);
1480 }
1481
1482 icmap_iter_finalize(iter);
1483
1484 configure_link_params(totem_config, map);
1485 if (reload) {
1486 log_printf(LOGSYS_LEVEL_DEBUG, "About to reconfigure links from nodelist.\n");
1487
1488 if (check_things_have_not_changed(totem_config, error_string) == -1) {
1489 return -1;
1490 }
1491 }
1492 return 0;
1493}
1494
1495static void config_convert_nodelist_to_interface(icmap_map_t map, struct totem_config *totem_config)
1496{
1497 int res = 0;
1498 int node_pos;
1499 char tmp_key[ICMAP_KEYNAME_MAXLEN];
1500 char tmp_key2[ICMAP_KEYNAME_MAXLEN];
1501 char *node_addr_str;
1502 unsigned int linknumber = 0;
1503 icmap_iter_t iter;
1504 const char *iter_key;
1505
1506 node_pos = find_local_node(map, 1);
1507 if (node_pos > -1) {
1508 /*
1509 * We found node, so create interface section
1510 */
1511 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.", node_pos);
1512 iter = icmap_iter_init_r(map, tmp_key);
1513 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
1514 res = sscanf(iter_key, "nodelist.node.%u.ring%u%s", &node_pos, &linknumber, tmp_key2);
1515 if (res != 3 || strcmp(tmp_key2, "_addr") != 0) {
1516 continue ;
1517 }
1518
1519 if (icmap_get_string_r(map, iter_key, &node_addr_str) != CS_OK) {
1520 continue;
1521 }
1522
1523 snprintf(tmp_key2, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.bindnetaddr", linknumber);
1524 icmap_set_string_r(map, tmp_key2, node_addr_str);
1525 free(node_addr_str);
1526 }
1527 icmap_iter_finalize(iter);
1528 }
1529}
1530
1531static int get_interface_params(struct totem_config *totem_config, icmap_map_t map,
1532 const char **error_string, uint64_t *warnings,
1533 int reload)
1534{
1535 int res = 0;
1536 unsigned int linknumber = 0;
1537 int member_count = 0;
1538 int i;
1539 icmap_iter_t iter, member_iter;
1540 const char *iter_key;
1541 const char *member_iter_key;
1542 char linknumber_key[ICMAP_KEYNAME_MAXLEN];
1543 char tmp_key[ICMAP_KEYNAME_MAXLEN];
1544 uint8_t u8;
1545 uint32_t u32;
1546 char *str;
1547 char *cluster_name = NULL;
1548 enum totem_ip_version_enum tmp_ip_version = TOTEM_IP_VERSION_4;
1549 int ret = 0;
1550
1551 if (reload) {
1552 for (i=0; i<INTERFACE_MAX; i++) {
1553 /*
1554 * Set back to defaults things that might have been configured and
1555 * now have been taken out of corosync.conf. These won't be caught by the
1556 * code below which only looks at interface{} sections that actually exist.
1557 */
1563 }
1564 }
1565 if (icmap_get_string_r(map, "totem.cluster_name", &cluster_name) != CS_OK) {
1566 cluster_name = NULL;
1567 }
1568
1569 iter = icmap_iter_init_r(map, "totem.interface.");
1570 while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) {
1571 res = sscanf(iter_key, "totem.interface.%[^.].%s", linknumber_key, tmp_key);
1572 if (res != 2) {
1573 continue;
1574 }
1575
1576 if (strcmp(tmp_key, "bindnetaddr") != 0 && totem_config->transport_number == TOTEM_TRANSPORT_UDP) {
1577 continue;
1578 }
1579
1580 member_count = 0;
1581 linknumber = atoi(linknumber_key);
1582
1583 if (linknumber >= INTERFACE_MAX) {
1584 snprintf (error_string_response, sizeof(error_string_response),
1585 "parse error in config: interface ring number %u is bigger than allowed maximum %u\n",
1586 linknumber, INTERFACE_MAX - 1);
1587
1588 *error_string = error_string_response;
1589 ret = -1;
1590 goto out;
1591 }
1592
1593 /* These things are only valid for the initial read */
1594 if (!reload) {
1595 /*
1596 * Get the bind net address
1597 */
1598 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.bindnetaddr", linknumber);
1599
1600 if (icmap_get_string_r(map, tmp_key, &str) == CS_OK) {
1601 res = totemip_parse (&totem_config->interfaces[linknumber].bindnet, str,
1603
1604 if (res) {
1605 sprintf(error_string_response, "failed to parse bindnet address '%s'\n", str);
1606 *error_string = error_string_response;
1607 free(str);
1608
1609 ret = -1;
1610 goto out;
1611 }
1612
1613 free(str);
1614 }
1615
1616 /*
1617 * Get interface multicast address
1618 */
1619 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastaddr", linknumber);
1620 if (icmap_get_string_r(map, tmp_key, &str) == CS_OK) {
1621 res = totemip_parse (&totem_config->interfaces[linknumber].mcast_addr, str,
1623
1624 if (res) {
1625 sprintf(error_string_response, "failed to parse mcast address '%s'\n", str);
1626 *error_string = error_string_response;
1627 free(str);
1628
1629 ret = -1;
1630 goto out;
1631 }
1632
1633 free(str);
1635 /*
1636 * User not specified address -> autogenerate one from cluster_name key
1637 * (if available). Return code is intentionally ignored, because
1638 * udpu doesn't need mcastaddr and validity of mcastaddr for udp is
1639 * checked later anyway.
1640 */
1641
1642 if (totem_config->interfaces[0].bindnet.family == AF_INET) {
1643 tmp_ip_version = TOTEM_IP_VERSION_4;
1644 } else if (totem_config->interfaces[0].bindnet.family == AF_INET6) {
1645 tmp_ip_version = TOTEM_IP_VERSION_6;
1646 }
1647
1648 (void)get_cluster_mcast_addr (cluster_name,
1649 linknumber,
1650 tmp_ip_version,
1651 &totem_config->interfaces[linknumber].mcast_addr);
1652 }
1653
1654 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.broadcast", linknumber);
1655 if (icmap_get_string(tmp_key, &str) == CS_OK) {
1656 if (strcmp (str, "yes") == 0) {
1658 }
1659 free(str);
1660 }
1661 }
1662
1663 /* These things are only valid for the initial read OR a newly-defined link */
1664 if (!reload || (totem_config->interfaces[linknumber].configured == 0)) {
1665
1666 /*
1667 * Get mcast port
1668 */
1669 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastport", linknumber);
1670 if (icmap_get_uint16_r(map, tmp_key, &totem_config->interfaces[linknumber].ip_port) != CS_OK) {
1672 totem_config->interfaces[linknumber].ip_port = DEFAULT_PORT + (2 * linknumber);
1673 } else {
1674 totem_config->interfaces[linknumber].ip_port = DEFAULT_PORT + linknumber;
1675 }
1676 }
1677
1678 /*
1679 * Get the TTL
1680 */
1681 totem_config->interfaces[linknumber].ttl = 1;
1682
1683 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.ttl", linknumber);
1684
1685 if (icmap_get_uint8_r(map, tmp_key, &u8) == CS_OK) {
1686 totem_config->interfaces[linknumber].ttl = u8;
1687 }
1688
1690 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_transport", linknumber);
1691 if (icmap_get_string_r(map, tmp_key, &str) == CS_OK) {
1692 if (strcmp(str, "sctp") == 0) {
1693 totem_config->interfaces[linknumber].knet_transport = KNET_TRANSPORT_SCTP;
1694 }
1695 else if (strcmp(str, "udp") == 0) {
1696 totem_config->interfaces[linknumber].knet_transport = KNET_TRANSPORT_UDP;
1697 }
1698 else {
1699 *error_string = "Unrecognised knet_transport. expected 'udp' or 'sctp'";
1700 ret = -1;
1701 goto out;
1702 }
1703 }
1704 }
1705 totem_config->interfaces[linknumber].configured = 1;
1706
1707 /*
1708 * Get the knet link params
1709 */
1711 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_link_priority", linknumber);
1712
1713 if (icmap_get_uint8_r(map, tmp_key, &u8) == CS_OK) {
1714 totem_config->interfaces[linknumber].knet_link_priority = u8;
1715 }
1716
1717 totem_config->interfaces[linknumber].knet_ping_interval = 0; /* real default applied later */
1718 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_ping_interval", linknumber);
1719 if (icmap_get_uint32_r(map, tmp_key, &u32) == CS_OK) {
1720 totem_config->interfaces[linknumber].knet_ping_interval = u32;
1721 }
1722 totem_config->interfaces[linknumber].knet_ping_timeout = 0; /* real default applied later */
1723 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_ping_timeout", linknumber);
1724 if (icmap_get_uint32_r(map, tmp_key, &u32) == CS_OK) {
1725 totem_config->interfaces[linknumber].knet_ping_timeout = u32;
1726 }
1728 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_ping_precision", linknumber);
1729 if (icmap_get_uint32_r(map, tmp_key, &u32) == CS_OK) {
1730 totem_config->interfaces[linknumber].knet_ping_precision = u32;
1731 }
1733 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_pong_count", linknumber);
1734 if (icmap_get_uint32_r(map, tmp_key, &u32) == CS_OK) {
1735 totem_config->interfaces[linknumber].knet_pong_count = u32;
1736 }
1737
1738 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.member.", linknumber);
1739 member_iter = icmap_iter_init_r(map, tmp_key);
1740 while ((member_iter_key = icmap_iter_next(member_iter, NULL, NULL)) != NULL) {
1741 if (member_count == 0) {
1742 if (icmap_get_string("nodelist.node.0.ring0_addr", &str) == CS_OK) {
1743 free(str);
1745 break;
1746 } else {
1748 }
1749 }
1750
1751 if (icmap_get_string_r(map, member_iter_key, &str) == CS_OK) {
1752 res = totemip_parse (&totem_config->interfaces[linknumber].member_list[member_count++],
1753 str, totem_config->ip_version);
1754 if (res) {
1755 sprintf(error_string_response, "failed to parse node address '%s'\n", str);
1756 *error_string = error_string_response;
1757
1758 icmap_iter_finalize(member_iter);
1759 free(str);
1760 ret = -1;
1761 goto out;
1762 }
1763
1764 free(str);
1765 }
1766 }
1767 icmap_iter_finalize(member_iter);
1768
1769 totem_config->interfaces[linknumber].member_count = member_count;
1770
1771 }
1772
1773out:
1774 icmap_iter_finalize(iter);
1775 free(cluster_name);
1776
1777 return (ret);
1778}
1779
1781 struct totem_config *totem_config,
1782 const char **error_string,
1783 uint64_t *warnings)
1784{
1785 int res = 0;
1786 char *str, *ring0_addr_str;
1787 char tmp_key[ICMAP_KEYNAME_MAXLEN];
1788 uint16_t u16;
1789 int i;
1790 int local_node_pos;
1791 uint32_t u32;
1792
1793 *warnings = 0;
1794
1795 memset (totem_config, 0, sizeof (struct totem_config));
1796 totem_config->interfaces = malloc (sizeof (struct totem_interface) * INTERFACE_MAX);
1797 if (totem_config->interfaces == 0) {
1798 *error_string = "Out of memory trying to allocate ethernet interface storage area";
1799 return -1;
1800 }
1801
1803 if (icmap_get_string("totem.transport", &str) == CS_OK) {
1804 if (strcmp (str, "udpu") == 0) {
1806 } else if (strcmp (str, "udp") == 0) {
1808 } else if (strcmp (str, "knet") == 0) {
1810 } else {
1811 *error_string = "Invalid transport type. Should be udpu, udp or knet";
1812 free(str);
1813 return -1;
1814 }
1815
1816 free(str);
1817 }
1818
1819 memset (totem_config->interfaces, 0,
1820 sizeof (struct totem_interface) * INTERFACE_MAX);
1821
1822 strcpy (totem_config->link_mode, "passive");
1823
1824 icmap_get_uint32("totem.version", (uint32_t *)&totem_config->version);
1825
1826 /* initial crypto load */
1827 if (totem_get_crypto(totem_config, icmap_get_global_map(), error_string) != 0) {
1828 return -1;
1829 }
1830 if (totem_config_keyread(totem_config, icmap_get_global_map(), error_string) != 0) {
1831 return -1;
1832 }
1835
1836 if (icmap_get_string("totem.link_mode", &str) == CS_OK) {
1837 if (strlen(str) >= TOTEM_LINK_MODE_BYTES) {
1838 *error_string = "totem.link_mode is too long";
1839 free(str);
1840
1841 return -1;
1842 }
1843 strcpy (totem_config->link_mode, str);
1844 free(str);
1845 }
1846
1847 if (icmap_get_uint32("totem.nodeid", &u32) == CS_OK) {
1849 }
1850
1852 if (icmap_get_string("totem.clear_node_high_bit", &str) == CS_OK) {
1853 if (strcmp (str, "yes") == 0) {
1855 }
1856 free(str);
1857 }
1858
1859 icmap_get_uint32("totem.threads", &totem_config->threads);
1860
1861 icmap_get_uint32("totem.netmtu", &totem_config->net_mtu);
1862
1863 totem_config->ip_version = totem_config_get_ip_version(totem_config);
1864
1865 if (icmap_get_string("totem.interface.0.bindnetaddr", &str) != CS_OK) {
1866 /*
1867 * We were not able to find ring 0 bindnet addr. Try to use nodelist informations
1868 */
1869 config_convert_nodelist_to_interface(icmap_get_global_map(), totem_config);
1870 } else {
1871 if (icmap_get_string("nodelist.node.0.ring0_addr", &ring0_addr_str) == CS_OK) {
1872 /*
1873 * Both bindnetaddr and ring0_addr are set.
1874 * Log warning information, and use nodelist instead
1875 */
1877
1878 config_convert_nodelist_to_interface(icmap_get_global_map(), totem_config);
1879
1880 free(ring0_addr_str);
1881 }
1882
1883 free(str);
1884 }
1885
1886 /*
1887 * Broadcast option is global but set in interface section,
1888 * so reset before processing interfaces.
1889 */
1891
1892 res = get_interface_params(totem_config, icmap_get_global_map(), error_string, warnings, 0);
1893 if (res < 0) {
1894 return res;
1895 }
1896
1897 /*
1898 * Use broadcast is global, so if set, make sure to fill mcast addr correctly
1899 * broadcast is only supported for UDP so just do interface 0;
1900 */
1903 "255.255.255.255", TOTEM_IP_VERSION_4);
1904 }
1905
1906
1907 /*
1908 * Store automatically generated items back to icmap only for UDP
1909 */
1911 for (i = 0; i < INTERFACE_MAX; i++) {
1913 continue;
1914 }
1915 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastaddr", i);
1916 if (icmap_get_string(tmp_key, &str) == CS_OK) {
1917 free(str);
1918 } else {
1919 str = (char *)totemip_print(&totem_config->interfaces[i].mcast_addr);
1920 icmap_set_string(tmp_key, str);
1921 }
1922
1923 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastport", i);
1924 if (icmap_get_uint16(tmp_key, &u16) != CS_OK) {
1926 }
1927 }
1928 }
1929
1930 /*
1931 * Check existence of nodelist
1932 */
1933 if ((icmap_get_string("nodelist.node.0.name", &str) == CS_OK) ||
1934 (icmap_get_string("nodelist.node.0.ring0_addr", &str) == CS_OK)) {
1935 free(str);
1936 /*
1937 * find local node
1938 */
1939 local_node_pos = find_local_node(icmap_get_global_map(), 1);
1940 if (local_node_pos != -1) {
1941
1942 assert(totem_config->node_id == 0);
1943
1944 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", local_node_pos);
1945 (void)icmap_get_uint32(tmp_key, &totem_config->node_id);
1946
1947
1949 *error_string = "Knet requires an explicit nodeid for the local node";
1950 return -1;
1951 }
1952
1955
1956 snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", local_node_pos);
1957 icmap_get_string(tmp_key, &str);
1958
1959 totem_config->node_id = generate_nodeid(totem_config, str);
1960 if (totem_config->node_id == -1) {
1961 *error_string = "An IPV6 network requires that a node ID be specified";
1962
1963 free(str);
1964 return (-1);
1965 }
1966
1968
1969 free(str);
1970 }
1971
1972 /* Users must not change this */
1973 icmap_set_ro_access("nodelist.local_node_pos", 0, 1);
1974 }
1975
1976 if (put_nodelist_members_to_config(totem_config, icmap_get_global_map(), 0, error_string)) {
1977 return -1;
1978 }
1979 }
1980
1981 /*
1982 * Get things that might change in the future (and can depend on totem_config->interfaces);
1983 */
1985
1986 calc_knet_ping_timers(totem_config);
1987
1988 /* This is now done in the totemknet interface callback */
1989 /* configure_totem_links(totem_config, icmap_get_global_map()); */
1990
1991 add_totem_config_notification(totem_config);
1992
1993 return 0;
1994}
1995
1996
1998 struct totem_config *totem_config,
1999 const char **error_string)
2000{
2001 static char local_error_reason[512];
2002 char parse_error[512];
2003 const char *error_reason = local_error_reason;
2004 int i;
2005 uint32_t u32;
2006 int num_configured = 0;
2007 unsigned int interface_max = INTERFACE_MAX;
2008
2009 for (i = 0; i < INTERFACE_MAX; i++) {
2011 num_configured++;
2012 }
2013 }
2014 if (num_configured == 0) {
2015 error_reason = "No interfaces defined";
2016 goto parse_error;
2017 }
2018
2019 /* Check we found a local node name */
2020 if (icmap_get_uint32("nodelist.local_node_pos", &u32) != CS_OK) {
2021 error_reason = "No valid name found for local host";
2022 goto parse_error;
2023 }
2024
2025 for (i = 0; i < INTERFACE_MAX; i++) {
2026 /*
2027 * Some error checking of parsed data to make sure its valid
2028 */
2029
2030 struct totem_ip_address null_addr;
2031
2033 continue;
2034 }
2035
2036 memset (&null_addr, 0, sizeof (struct totem_ip_address));
2037
2039 memcmp (&totem_config->interfaces[i].mcast_addr, &null_addr,
2040 sizeof (struct totem_ip_address)) == 0) {
2041 snprintf (local_error_reason, sizeof(local_error_reason),
2042 "No multicast address specified for interface %u", i);
2043 goto parse_error;
2044 }
2045
2046 if (totem_config->interfaces[i].ip_port == 0) {
2047 snprintf (local_error_reason, sizeof(local_error_reason),
2048 "No multicast port specified for interface %u", i);
2049 goto parse_error;
2050 }
2051
2052 if (totem_config->interfaces[i].ttl > 255) {
2053 snprintf (local_error_reason, sizeof(local_error_reason),
2054 "Invalid TTL (should be 0..255) for interface %u", i);
2055 goto parse_error;
2056 }
2058 totem_config->interfaces[i].ttl != 1) {
2059 snprintf (local_error_reason, sizeof(local_error_reason),
2060 "Can only set ttl on multicast transport types for interface %u", i);
2061 goto parse_error;
2062 }
2064 snprintf (local_error_reason, sizeof(local_error_reason),
2065 "Invalid link priority (should be 0..255) for interface %u", i);
2066 goto parse_error;
2067 }
2070 snprintf (local_error_reason, sizeof(local_error_reason),
2071 "Can only set link priority on knet transport type for interface %u", i);
2072 goto parse_error;
2073 }
2074
2075 if (totem_config->interfaces[i].mcast_addr.family == AF_INET6 &&
2076 totem_config->node_id == 0) {
2077 snprintf (local_error_reason, sizeof(local_error_reason),
2078 "An IPV6 network requires that a node ID be specified for interface %u", i);
2079 goto parse_error;
2080 }
2081
2084 snprintf (local_error_reason, sizeof(local_error_reason),
2085 "Multicast address family does not match bind address family for interface %u", i);
2086 goto parse_error;
2087 }
2088
2090 snprintf (local_error_reason, sizeof(local_error_reason),
2091 "mcastaddr is not a correct multicast address for interface %u", i);
2092 goto parse_error;
2093 }
2094 }
2095 }
2096
2097 if (totem_config->version != 2) {
2098 error_reason = "This totem parser can only parse version 2 configurations.";
2099 goto parse_error;
2100 }
2101
2103 return (-1);
2104 }
2105
2106 if (check_for_duplicate_nodeids(totem_config, error_string) == -1) {
2107 return (-1);
2108 }
2109
2110 /*
2111 * KNET Link values validation
2112 */
2113 if (strcmp (totem_config->link_mode, "active") &&
2114 strcmp (totem_config->link_mode, "rr") &&
2115 strcmp (totem_config->link_mode, "passive")) {
2116 snprintf (local_error_reason, sizeof(local_error_reason),
2117 "The Knet link mode \"%s\" specified is invalid. It must be active, passive or rr.\n", totem_config->link_mode);
2118 goto parse_error;
2119 }
2120
2121 /* Only Knet does multiple interfaces */
2123 interface_max = 1;
2124 }
2125
2126 if (interface_max < num_configured) {
2127 snprintf (parse_error, sizeof(parse_error),
2128 "%d is too many configured interfaces for non-Knet transport.",
2129 num_configured);
2130 error_reason = parse_error;
2131 goto parse_error;
2132 }
2133
2134 /* Only knet allows crypto */
2136 if ((strcmp(totem_config->crypto_cipher_type, "none") != 0) ||
2137 (strcmp(totem_config->crypto_hash_type, "none") != 0)) {
2138
2139 snprintf (parse_error, sizeof(parse_error),
2140 "crypto_cipher & crypto_hash are only valid for the Knet transport.");
2141 error_reason = parse_error;
2142 goto parse_error;
2143 }
2144 }
2145
2146 if (totem_config->net_mtu == 0) {
2148 totem_config->net_mtu = KNET_MAX_PACKET_SIZE;
2149 }
2150 else {
2152 }
2153 }
2154
2155 return 0;
2156
2157parse_error:
2158 snprintf (error_string_response, sizeof(error_string_response),
2159 "parse error in config: %s\n", error_reason);
2160 *error_string = error_string_response;
2161 return (-1);
2162
2163}
2164
2165static int read_keyfile (
2166 const char *key_location,
2167 struct totem_config *totem_config,
2168 const char **error_string)
2169{
2170 int fd;
2171 int res;
2172 int saved_errno;
2173 char error_str[100];
2174 const char *error_ptr;
2175
2176 fd = open (key_location, O_RDONLY);
2177 if (fd == -1) {
2178 error_ptr = qb_strerror_r(errno, error_str, sizeof(error_str));
2179 snprintf (error_string_response, sizeof(error_string_response),
2180 "Could not open %s: %s\n",
2181 key_location, error_ptr);
2182 goto parse_error;
2183 }
2184
2186 saved_errno = errno;
2187 close (fd);
2188
2189 if (res == -1) {
2190 error_ptr = qb_strerror_r (saved_errno, error_str, sizeof(error_str));
2191 snprintf (error_string_response, sizeof(error_string_response),
2192 "Could not read %s: %s\n",
2193 key_location, error_ptr);
2194 goto parse_error;
2195 }
2196
2197 if (res < TOTEM_PRIVATE_KEY_LEN_MIN) {
2198 snprintf (error_string_response, sizeof(error_string_response),
2199 "Could only read %d bits of minimum %u bits from %s.\n",
2200 res * 8, TOTEM_PRIVATE_KEY_LEN_MIN * 8, key_location);
2201 goto parse_error;
2202 }
2203
2205
2206 return 0;
2207
2208parse_error:
2209 *error_string = error_string_response;
2210 return (-1);
2211}
2212
2214 struct totem_config *totem_config,
2215 icmap_map_t map,
2216 const char **error_string)
2217{
2218 int got_key = 0;
2219 char *key_location = NULL;
2220 int res;
2221 size_t key_len;
2222 char old_key[TOTEM_PRIVATE_KEY_LEN_MAX];
2223 size_t old_key_len;
2224
2225 /* Take a copy so we can see if it has changed */
2226 memcpy(old_key, totem_config->private_key, sizeof(totem_config->private_key));
2227 old_key_len = totem_config->private_key_len;
2228
2229 memset (totem_config->private_key, 0, sizeof(totem_config->private_key));
2231
2232 if (strcmp(totem_config->crypto_cipher_type, "none") == 0 &&
2233 strcmp(totem_config->crypto_hash_type, "none") == 0) {
2234 return (0);
2235 }
2236
2237 /* cmap may store the location of the key file */
2238 if (icmap_get_string_r(map, "totem.keyfile", &key_location) == CS_OK) {
2239 res = read_keyfile(key_location, totem_config, error_string);
2240 free(key_location);
2241 if (res) {
2242 goto key_error;
2243 }
2244 got_key = 1;
2245 } else { /* Or the key itself may be in the cmap */
2246 if (icmap_get_r(map, "totem.key", NULL, &key_len, NULL) == CS_OK) {
2247 if (key_len > sizeof(totem_config->private_key)) {
2248 sprintf(error_string_response, "key is too long");
2249 goto key_error;
2250 }
2251 if (key_len < TOTEM_PRIVATE_KEY_LEN_MIN) {
2252 sprintf(error_string_response, "key is too short");
2253 goto key_error;
2254 }
2255 if (icmap_get_r(map, "totem.key", totem_config->private_key, &key_len, NULL) == CS_OK) {
2256 totem_config->private_key_len = key_len;
2257 got_key = 1;
2258 } else {
2259 sprintf(error_string_response, "can't load private key");
2260 goto key_error;
2261 }
2262 }
2263 }
2264
2265 /* In desperation we read the default filename */
2266 if (!got_key) {
2267 res = read_keyfile(COROSYSCONFDIR "/authkey", totem_config, error_string);
2268 if (res)
2269 goto key_error;
2270 }
2271
2272 if (old_key_len != totem_config->private_key_len ||
2273 memcmp(old_key, totem_config->private_key, sizeof(totem_config->private_key))) {
2275 }
2276
2277 return (0);
2278
2279key_error:
2280 *error_string = error_string_response;
2281 return (-1);
2282
2283}
2284
2285int totem_reread_crypto_config(struct totem_config *totem_config, icmap_map_t map, const char **error_string)
2286{
2287 if (totem_get_crypto(totem_config, map, error_string) != 0) {
2288 return -1;
2289 }
2290 if (totem_config_keyread(totem_config, map, error_string) != 0) {
2291 return -1;
2292 }
2293 return 0;
2294}
2295
2296static void debug_dump_totem_config(const struct totem_config *totem_config)
2297{
2298
2299 log_printf(LOGSYS_LEVEL_DEBUG, "Token Timeout (%d ms) retransmit timeout (%d ms)",
2302 uint32_t token_warning_ms = totem_config->token_warning * totem_config->token_timeout / 100;
2303 log_printf(LOGSYS_LEVEL_DEBUG, "Token warning every %d ms (%d%% of Token Timeout)",
2304 token_warning_ms, totem_config->token_warning);
2305 if (token_warning_ms < totem_config->token_retransmit_timeout)
2307 "The token warning interval (%d ms) is less than the token retransmit timeout (%d ms) "
2308 "which can lead to spurious token warnings. Consider increasing the token_warning parameter.",
2309 token_warning_ms, totem_config->token_retransmit_timeout);
2310
2311 } else
2312 log_printf(LOGSYS_LEVEL_DEBUG, "Token warnings disabled");
2313 log_printf(LOGSYS_LEVEL_DEBUG, "token hold (%d ms) retransmits before loss (%d retrans)",
2315 log_printf(LOGSYS_LEVEL_DEBUG, "join (%d ms) send_join (%d ms) consensus (%d ms) merge (%d ms)",
2318 log_printf(LOGSYS_LEVEL_DEBUG, "downcheck (%d ms) fail to recv const (%d msgs)",
2321 "seqno unchanged const (%d rotations) Maximum network MTU %d",
2324 "window size per rotation (%d messages) maximum messages per rotation (%d messages)",
2326 log_printf(LOGSYS_LEVEL_DEBUG, "missed count const (%d messages)", totem_config->miss_count_const);
2327 log_printf(LOGSYS_LEVEL_DEBUG, "heartbeat_failures_allowed (%d)",
2329 log_printf(LOGSYS_LEVEL_DEBUG, "max_network_delay (%d ms)", totem_config->max_network_delay);
2330}
2331
2332
2333static void totem_change_notify(
2334 int32_t event,
2335 const char *key_name,
2336 struct icmap_notify_value new_val,
2337 struct icmap_notify_value old_val,
2338 void *user_data)
2339{
2340 struct totem_config *totem_config = (struct totem_config *)user_data;
2341 uint32_t *param;
2342 uint8_t reloading;
2343 const char *deleted_key = NULL;
2344 const char *error_string;
2345
2346 /*
2347 * If a full reload is in progress then don't do anything until it's done and
2348 * can reconfigure it all atomically
2349 */
2350 if (icmap_get_uint8("config.reload_in_progress", &reloading) == CS_OK && reloading)
2351 return;
2352
2353 param = totem_get_param_by_name((struct totem_config *)user_data, key_name);
2354 /*
2355 * Process change only if changed key is found in totem_config (-> param is not NULL)
2356 * or for special key token_coefficient. token_coefficient key is not stored in
2357 * totem_config, but it is used for computation of token timeout.
2358 */
2359 if (!param && strcmp(key_name, "totem.token_coefficient") != 0)
2360 return;
2361
2362 /*
2363 * Values other than UINT32 are not supported, or needed (yet)
2364 */
2365 switch (event) {
2366 case ICMAP_TRACK_DELETE:
2367 deleted_key = key_name;
2368 break;
2369 case ICMAP_TRACK_ADD:
2370 case ICMAP_TRACK_MODIFY:
2371 deleted_key = NULL;
2372 break;
2373 default:
2374 break;
2375 }
2376
2378 log_printf(LOGSYS_LEVEL_DEBUG, "Totem related config key changed. Dumping actual totem config.");
2379 debug_dump_totem_config(totem_config);
2381 log_printf (LOGSYS_LEVEL_ERROR, "%s", error_string);
2382 /*
2383 * TODO: Consider corosync exit and/or load defaults for volatile
2384 * values. For now, log error seems to be enough
2385 */
2386 }
2387}
2388
2389
2391 struct totem_config *totem_config,
2392 icmap_map_t map,
2393 const char **error_string)
2394{
2395 uint64_t warnings = 0LL;
2396
2397 get_interface_params(totem_config, map, error_string, &warnings, 1);
2398 if (put_nodelist_members_to_config (totem_config, map, 1, error_string)) {
2399 return -1;
2400 }
2401
2402 calc_knet_ping_timers(totem_config);
2403
2404 log_printf(LOGSYS_LEVEL_DEBUG, "Configuration reloaded. Dumping actual totem config.");
2405 debug_dump_totem_config(totem_config);
2406
2407 /* Reinstate the local_node_pos */
2408 (void)find_local_node(map, 0);
2409
2410 return 0;
2411}
2412
2414 struct totem_config *totem_config,
2415 icmap_map_t map)
2416{
2417 struct totem_interface *new_interfaces = NULL;
2418
2419 new_interfaces = malloc (sizeof (struct totem_interface) * INTERFACE_MAX);
2420 assert(new_interfaces != NULL);
2421 memcpy(new_interfaces, totem_config->interfaces, sizeof (struct totem_interface) * INTERFACE_MAX);
2422
2423 /* Set link parameters including local_ip */
2424 configure_totem_links(totem_config, map);
2425
2426 /* Add & remove nodes */
2427 compute_and_set_totempg_interfaces(totem_config->orig_interfaces, new_interfaces);
2428
2429 /* Does basic global params (like compression) */
2431
2432 free(new_interfaces);
2433}
2434
2435static void add_totem_config_notification(struct totem_config *totem_config)
2436{
2438
2439 icmap_track_add("totem.",
2441 totem_change_notify,
2443 &icmap_track);
2444}
#define COROSYSCONFDIR
Definition: config.h:8
#define INTERFACE_MAX
Definition: coroapi.h:88
unsigned int nodeid
Definition: coroapi.h:0
unsigned char addr[TOTEMIP_ADDRLEN]
Definition: coroapi.h:2
#define PROCESSOR_COUNT_MAX
Definition: coroapi.h:96
#define CS_PRI_NODE_ID
Definition: corotypes.h:59
@ CS_OK
Definition: corotypes.h:99
uint8_t param
uint32_t value
icmap_iter_t icmap_iter_init_r(const icmap_map_t map, const char *prefix)
icmap_iter_init_r
Definition: icmap.c:1084
cs_error_t icmap_get_uint8(const char *key_name, uint8_t *u8)
Definition: icmap.c:868
#define ICMAP_TRACK_MODIFY
Definition: icmap.h:78
cs_error_t icmap_get_string_r(const icmap_map_t map, const char *key_name, char **str)
Definition: icmap.c:735
cs_error_t icmap_get_uint16(const char *key_name, uint16_t *u16)
Definition: icmap.c:880
cs_error_t icmap_get_uint32(const char *key_name, uint32_t *u32)
Definition: icmap.c:892
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:1225
cs_error_t icmap_set_uint32_r(const icmap_map_t map, const char *key_name, uint32_t value)
Definition: icmap.c:527
cs_error_t icmap_get_uint8_r(const icmap_map_t map, const char *key_name, uint8_t *u8)
Definition: icmap.c:802
#define ICMAP_TRACK_DELETE
Definition: icmap.h:77
cs_error_t icmap_set_uint16(const char *key_name, uint16_t value)
Definition: icmap.c:585
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:1159
cs_error_t icmap_set_string(const char *key_name, const char *value)
Definition: icmap.c:627
#define ICMAP_TRACK_PREFIX
Whole prefix is tracked, instead of key only (so "totem." tracking means that "totem....
Definition: icmap.h:85
icmap_iter_t icmap_iter_init(const char *prefix)
Initialize iterator with given prefix.
Definition: icmap.c:1089
cs_error_t icmap_get_uint16_r(const icmap_map_t map, const char *key_name, uint16_t *u16)
Definition: icmap.c:814
cs_error_t icmap_set_int32_r(const icmap_map_t map, const char *key_name, int32_t value)
Definition: icmap.c:521
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:1095
icmap_map_t icmap_get_global_map(void)
Return global icmap.
Definition: icmap.c:264
qb_map_iter_t * icmap_iter_t
Itterator type.
Definition: icmap.h:123
cs_error_t icmap_set_string_r(const icmap_map_t map, const char *key_name, const char *value)
Definition: icmap.c:557
void icmap_iter_finalize(icmap_iter_t iter)
Finalize iterator.
Definition: icmap.c:1116
cs_error_t icmap_get_int32_r(const icmap_map_t map, const char *key_name, int32_t *i32)
Definition: icmap.c:820
cs_error_t icmap_get_r(const icmap_map_t map, const char *key_name, void *value, size_t *value_len, icmap_value_types_t *type)
Same as icmap_get but it's reentrant and operates on given icmap_map.
Definition: icmap.c:692
#define ICMAP_KEYNAME_MAXLEN
Maximum length of key in icmap.
Definition: icmap.h:48
cs_error_t icmap_get_uint32_r(const icmap_map_t map, const char *key_name, uint32_t *u32)
Definition: icmap.c:826
cs_error_t icmap_set_uint32(const char *key_name, uint32_t value)
Definition: icmap.c:597
#define ICMAP_TRACK_ADD
Definition: icmap.h:76
cs_error_t icmap_get_string(const char *key_name, char **str)
Shortcut for icmap_get for string type.
Definition: icmap.c:856
#define LOGSYS_LEVEL_ERROR
Definition: logsys.h:72
#define log_printf(level, format, args...)
Definition: logsys.h:323
#define LOGSYS_LEVEL_INFO
Definition: logsys.h:75
#define LOGSYS_LEVEL_DEBUG
Definition: logsys.h:76
void * user_data
Definition: sam.c:127
Structure passed as new_value and old_value in change callback.
Definition: icmap.h:91
char crypto_model[CONFIG_STRING_LEN_MAX]
Definition: totem.h:223
unsigned int max_messages
Definition: totem.h:219
unsigned int heartbeat_failures_allowed
Definition: totem.h:213
unsigned int token_timeout
Definition: totem.h:181
unsigned int private_key_len
Definition: totem.h:176
unsigned int node_id
Definition: totem.h:167
unsigned int broadcast_use
Definition: totem.h:221
uint32_t knet_compression_threshold
Definition: totem.h:235
unsigned int window_size
Definition: totem.h:217
unsigned int downcheck_timeout
Definition: totem.h:199
unsigned int miss_count_const
Definition: totem.h:241
totem_transport_t transport_number
Definition: totem.h:239
struct totem_interface * interfaces
Definition: totem.h:165
int crypto_changed
Definition: totem.h:231
unsigned int cancel_token_hold_on_retransmit
Definition: totem.h:247
unsigned int fail_to_recv_const
Definition: totem.h:201
unsigned int clear_node_high_bit
Definition: totem.h:168
unsigned int merge_timeout
Definition: totem.h:197
struct totem_interface * orig_interfaces
Definition: totem.h:166
int knet_compression_level
Definition: totem.h:237
unsigned int net_mtu
Definition: totem.h:209
int version
Definition: totem.h:160
char knet_compression_model[CONFIG_STRING_LEN_MAX]
Definition: totem.h:233
unsigned int block_unlisted_ips
Definition: totem.h:245
unsigned int token_retransmits_before_loss_const
Definition: totem.h:189
unsigned char private_key[TOTEM_PRIVATE_KEY_LEN_MAX]
Definition: totem.h:174
int crypto_index
Definition: totem.h:229
unsigned int max_network_delay
Definition: totem.h:215
unsigned int seqno_unchanged_const
Definition: totem.h:203
unsigned int consensus_timeout
Definition: totem.h:195
unsigned int knet_pmtud_interval
Definition: totem.h:169
char crypto_cipher_type[CONFIG_STRING_LEN_MAX]
Definition: totem.h:225
unsigned int threads
Definition: totem.h:211
unsigned int send_join_timeout
Definition: totem.h:193
char link_mode[TOTEM_LINK_MODE_BYTES]
Definition: totem.h:205
enum totem_ip_version_enum ip_version
Definition: totem.h:243
unsigned int token_retransmit_timeout
Definition: totem.h:185
char crypto_hash_type[CONFIG_STRING_LEN_MAX]
Definition: totem.h:227
unsigned int token_warning
Definition: totem.h:183
unsigned int join_timeout
Definition: totem.h:191
unsigned int token_hold_timeout
Definition: totem.h:187
struct totem_ip_address local_ip
Definition: totem.h:86
int knet_ping_timeout
Definition: totem.h:93
int knet_link_priority
Definition: totem.h:91
uint16_t ip_port
Definition: totem.h:87
int knet_ping_interval
Definition: totem.h:92
uint8_t configured
Definition: totem.h:89
int knet_ping_precision
Definition: totem.h:94
int knet_pong_count
Definition: totem.h:95
int knet_transport
Definition: totem.h:96
int member_count
Definition: totem.h:90
struct totem_ip_address bindnet
Definition: totem.h:83
struct totem_ip_address member_list[PROCESSOR_COUNT_MAX]
Definition: totem.h:97
uint16_t ttl
Definition: totem.h:88
struct totem_ip_address mcast_addr
Definition: totem.h:85
The totem_ip_address struct.
Definition: coroapi.h:111
unsigned int nodeid
Definition: coroapi.h:112
unsigned short family
Definition: coroapi.h:113
#define swab32(x)
The swab32 macro.
Definition: swab.h:51
@ TOTEM_LINK_MODE_BYTES
Definition: totem.h:140
@ TOTEM_PRIVATE_KEY_LEN_MAX
Definition: totem.h:137
@ TOTEM_PRIVATE_KEY_LEN_MIN
Definition: totem.h:136
#define CONFIG_STRING_LEN_MAX
Definition: totem.h:54
@ TOTEM_TRANSPORT_UDPU
Definition: totem.h:144
@ TOTEM_TRANSPORT_KNET
Definition: totem.h:145
@ TOTEM_TRANSPORT_UDP
Definition: totem.h:143
#define MINIMUM_TIMEOUT
Definition: totemconfig.c:77
#define TOKEN_RETRANSMITS_BEFORE_LOSS_CONST
Definition: totemconfig.c:68
#define JOIN_TIMEOUT
Definition: totemconfig.c:72
#define DEFAULT_PORT
Definition: totemconfig.c:96
#define MAX_MESSAGES
Definition: totemconfig.c:81
#define MAX_NETWORK_DELAY
Definition: totemconfig.c:79
#define KNET_PONG_COUNT
Definition: totemconfig.c:92
#define KNET_PMTUD_INTERVAL
Definition: totemconfig.c:93
#define MINIMUM_TIMEOUT_HOLD
Definition: totemconfig.c:78
#define TOKEN_COEFFICIENT
Definition: totemconfig.c:71
#define SEQNO_UNCHANGED_CONST
Definition: totemconfig.c:76
void totem_volatile_config_read(struct totem_config *totem_config, icmap_map_t temp_map, const char *deleted_key)
Definition: totemconfig.c:308
void totemconfig_commit_new_params(struct totem_config *totem_config, icmap_map_t map)
Definition: totemconfig.c:2413
int totem_config_read(struct totem_config *totem_config, const char **error_string, uint64_t *warnings)
Definition: totemconfig.c:1780
#define MISS_COUNT_CONST
Definition: totemconfig.c:82
int totem_volatile_config_validate(struct totem_config *totem_config, icmap_map_t temp_map, const char **error_string)
Definition: totemconfig.c:376
#define TOKEN_WARNING
Definition: totemconfig.c:70
int totem_reread_crypto_config(struct totem_config *totem_config, icmap_map_t map, const char **error_string)
Definition: totemconfig.c:2285
#define MERGE_TIMEOUT
Definition: totemconfig.c:73
#define KNET_PING_PRECISION
Definition: totemconfig.c:91
int totem_config_validate(struct totem_config *totem_config, const char **error_string)
Definition: totemconfig.c:1997
#define BLOCK_UNLISTED_IPS
Definition: totemconfig.c:83
#define KNET_DEFAULT_TRANSPORT
Definition: totemconfig.c:94
#define UDP_NETMTU
Definition: totemconfig.c:86
#define CANCEL_TOKEN_HOLD_ON_RETRANSMIT
Definition: totemconfig.c:84
#define FAIL_TO_RECV_CONST
Definition: totemconfig.c:75
#define WINDOW_SIZE
Definition: totemconfig.c:80
#define DOWNCHECK_TIMEOUT
Definition: totemconfig.c:74
#define TOKEN_TIMEOUT
Definition: totemconfig.c:69
int totemconfig_configure_new_params(struct totem_config *totem_config, icmap_map_t map, const char **error_string)
Definition: totemconfig.c:2390
int totem_config_keyread(struct totem_config *totem_config, icmap_map_t map, const char **error_string)
Definition: totemconfig.c:2213
#define TOTEM_CONFIG_BINDNETADDR_NODELIST_SET
Definition: totemconfig.h:48
#define TOTEM_CONFIG_WARNING_TOTEM_NODEID_SET
Definition: totemconfig.h:47
#define TOTEM_CONFIG_WARNING_MEMBERS_IGNORED
Definition: totemconfig.h:45
#define TOTEM_CONFIG_WARNING_MEMBERS_DEPRECATED
Definition: totemconfig.h:46
int totemip_parse(struct totem_ip_address *totemip, const char *addr, enum totem_ip_version_enum ip_version)
Definition: totemip.c:306
const char * totemip_print(const struct totem_ip_address *addr)
Definition: totemip.c:256
totem_ip_version_enum
Definition: totemip.h:70
@ TOTEM_IP_VERSION_6_4
Definition: totemip.h:74
@ TOTEM_IP_VERSION_4
Definition: totemip.h:71
@ TOTEM_IP_VERSION_6
Definition: totemip.h:72
@ TOTEM_IP_VERSION_4_6
Definition: totemip.h:73
int totemip_is_mcast(struct totem_ip_address *addr)
Definition: totemip.c:134
int totempg_iface_set(struct totem_ip_address *interface_addr, unsigned short ip_port, unsigned int iface_no)
Definition: totempg.c:1434
int totempg_reconfigure(void)
Definition: totempg.c:1566
int totempg_member_remove(const struct totem_ip_address *member, int ring_no)
Definition: totempg.c:1559
int totempg_member_add(const struct totem_ip_address *member, int ring_no)
Definition: totempg.c:1552