1 /*
2 * This file is part of RTRlib.
3 *
4 * This file is subject to the terms and conditions of the MIT license.
5 * See the file LICENSE in the top level directory for more details.
6 *
7 * Website: http://rtrlib.realmv6.org/
8 */
9
10 #include <stdlib.h>
11 #include <pthread.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <arpa/inet.h>
15
16 #include "rtrlib/lib/alloc_utils_private.h"
17 #include "rtrlib/lib/log_private.h"
18 #include "rtrlib/pfx/pfx_private.h"
19 #include "rtrlib/rtr/rtr_private.h"
20 #include "rtrlib/rtr_mgr_private.h"
21 #include "rtrlib/rtrlib_export_private.h"
22 #include "rtrlib/spki/hashtable/ht-spkitable_private.h"
23 #include "rtrlib/transport/transport_private.h"
24
25 #define MGR_DBG(fmt, ...) lrtr_dbg("RTR_MGR: " fmt, ## __VA_ARGS__)
26 #define MGR_DBG1(a) lrtr_dbg("RTR_MGR: " a)
27
28 static const char * const mgr_str_status[] = {
29 [RTR_MGR_CLOSED] = "RTR_MGR_CLOSED",
30 [RTR_MGR_CONNECTING] = "RTR_MGR_CONNECTING",
31 [RTR_MGR_ESTABLISHED] = "RTR_MGR_ESTABLISHED",
32 [RTR_MGR_ERROR] = "RTR_MGR_ERROR",
33 };
34
35 static int rtr_mgr_config_cmp(const void *a, const void *b);
36 static int rtr_mgr_config_cmp_tommy(const void *a, const void *b);
37 static bool rtr_mgr_config_status_is_synced(const struct rtr_mgr_group *group);
38 static void rtr_mgr_cb(const struct rtr_socket *sock,
39 const enum rtr_socket_state state,
40 void *data_config, void *data_group);
41
set_status(const struct rtr_mgr_config * conf,struct rtr_mgr_group * group,enum rtr_mgr_status mgr_status,const struct rtr_socket * rtr_sock)42 static void set_status(const struct rtr_mgr_config *conf,
43 struct rtr_mgr_group *group,
44 enum rtr_mgr_status mgr_status,
45 const struct rtr_socket *rtr_sock)
46 {
47 MGR_DBG("Group(%u) status changed to: %s", group->preference,
48 rtr_mgr_status_to_str(mgr_status));
49
50 group->status = mgr_status;
51 if (conf->status_fp)
52 conf->status_fp(group, mgr_status,
53 rtr_sock, conf->status_fp_data);
54 }
55
rtr_mgr_start_sockets(struct rtr_mgr_group * group)56 static int rtr_mgr_start_sockets(struct rtr_mgr_group *group)
57 {
58 for (unsigned int i = 0; i < group->sockets_len; i++) {
59 if (rtr_start(group->sockets[i]) != 0) {
60 MGR_DBG1("rtr_mgr: Error starting rtr_socket pthread");
61 return RTR_ERROR;
62 }
63 }
64 group->status = RTR_MGR_CONNECTING;
65 return RTR_SUCCESS;
66 }
67
rtr_mgr_init_sockets(struct rtr_mgr_group * group,struct rtr_mgr_config * config,const unsigned int refresh_interval,const unsigned int expire_interval,const unsigned int retry_interval,enum rtr_interval_mode iv_mode)68 static int rtr_mgr_init_sockets(struct rtr_mgr_group *group,
69 struct rtr_mgr_config *config,
70 const unsigned int refresh_interval,
71 const unsigned int expire_interval,
72 const unsigned int retry_interval,
73 enum rtr_interval_mode iv_mode)
74 {
75 for (unsigned int i = 0; i < group->sockets_len; i++) {
76 enum rtr_rtvals err_code = rtr_init(group->sockets[i], NULL,
77 config->pfx_table,
78 config->spki_table,
79 refresh_interval,
80 expire_interval,
81 retry_interval, iv_mode,
82 rtr_mgr_cb, config, group);
83 if (err_code)
84 return err_code;
85 }
86 return RTR_SUCCESS;
87 }
88
rtr_mgr_config_status_is_synced(const struct rtr_mgr_group * group)89 bool rtr_mgr_config_status_is_synced(const struct rtr_mgr_group *group)
90 {
91 for (unsigned int i = 0; i < group->sockets_len; i++) {
92 enum rtr_socket_state state = group->sockets[i]->state;
93
94 if ((group->sockets[i]->last_update == 0) ||
95 ((state != RTR_ESTABLISHED) && (state != RTR_RESET) &&
96 (state != RTR_SYNC)))
97 return false;
98 }
99 return true;
100 }
101
rtr_mgr_close_less_preferable_groups(const struct rtr_socket * sock,struct rtr_mgr_config * config,struct rtr_mgr_group * group)102 static void rtr_mgr_close_less_preferable_groups(const struct rtr_socket *sock,
103 struct rtr_mgr_config *config,
104 struct rtr_mgr_group *group)
105 {
106 pthread_mutex_lock(&config->mutex);
107 tommy_node *node = tommy_list_head(&config->groups->list);
108
109 while (node) {
110 struct rtr_mgr_group_node *group_node = node->data;
111 struct rtr_mgr_group *current_group = group_node->group;
112
113 if ((current_group->status != RTR_MGR_CLOSED) &&
114 (current_group != group) &&
115 (current_group->preference > group->preference)) {
116 for (unsigned int j = 0;
117 j < current_group->sockets_len; j++) {
118 rtr_stop(current_group->sockets[j]);
119 }
120 set_status(config, current_group, RTR_MGR_CLOSED, sock);
121 }
122 node = node->next;
123 }
124 pthread_mutex_unlock(&config->mutex);
125 }
126
get_best_inactive_rtr_mgr_group(struct rtr_mgr_config * config,struct rtr_mgr_group * group)127 static struct rtr_mgr_group *get_best_inactive_rtr_mgr_group(
128 struct rtr_mgr_config *config,
129 struct rtr_mgr_group *group)
130 {
131 pthread_mutex_lock(&config->mutex);
132 tommy_node *node = tommy_list_head(&config->groups->list);
133
134 while (node) {
135 struct rtr_mgr_group_node *group_node = node->data;
136 struct rtr_mgr_group *current_group = group_node->group;
137 if ((current_group != group) &&
138 (current_group->status == RTR_MGR_CLOSED)) {
139 pthread_mutex_unlock(&config->mutex);
140 return current_group;
141 }
142 node = node->next;
143 }
144 pthread_mutex_unlock(&config->mutex);
145 return NULL;
146 }
147
is_some_rtr_mgr_group_established(struct rtr_mgr_config * config)148 static bool is_some_rtr_mgr_group_established(struct rtr_mgr_config *config)
149 {
150 pthread_mutex_lock(&config->mutex);
151 tommy_node *node = tommy_list_head(&config->groups->list);
152
153 while (node) {
154 struct rtr_mgr_group_node *group_node = node->data;
155
156 if (group_node->group->status == RTR_MGR_ESTABLISHED) {
157 pthread_mutex_unlock(&config->mutex);
158 return true;
159 }
160 node = node->next;
161 }
162 pthread_mutex_unlock(&config->mutex);
163 return false;
164 }
165
_rtr_mgr_cb_state_shutdown(const struct rtr_socket * sock,struct rtr_mgr_config * config,struct rtr_mgr_group * group)166 static inline void _rtr_mgr_cb_state_shutdown(const struct rtr_socket *sock,
167 struct rtr_mgr_config *config,
168 struct rtr_mgr_group *group)
169 {
170 bool all_down = true;
171
172 for (unsigned int i = 0; i < group->sockets_len; i++) {
173 if (group->sockets[i]->state != RTR_SHUTDOWN) {
174 all_down = false;
175 break;
176 }
177 }
178 if (all_down)
179 set_status(config, group,
180 RTR_MGR_CLOSED, sock);
181 else
182 set_status(config, group,
183 group->status, sock);
184 }
185
_rtr_mgr_cb_state_established(const struct rtr_socket * sock,struct rtr_mgr_config * config,struct rtr_mgr_group * group)186 static inline void _rtr_mgr_cb_state_established(const struct rtr_socket *sock,
187 struct rtr_mgr_config *config,
188 struct rtr_mgr_group *group)
189 {
190 /* socket established a connection to the rtr_server */
191 if (group->status == RTR_MGR_CONNECTING) {
192 /*
193 * if previous state was CONNECTING, check if all
194 * other sockets in the group also have a established
195 * connection, if yes change group state to ESTABLISHED
196 */
197 if (rtr_mgr_config_status_is_synced(group)) {
198 set_status(config, group,
199 RTR_MGR_ESTABLISHED, sock);
200 rtr_mgr_close_less_preferable_groups(sock, config,
201 group);
202 } else {
203 set_status(config, group,
204 RTR_MGR_CONNECTING, sock);
205 }
206 } else if (group->status == RTR_MGR_ERROR) {
207 /* if previous state was ERROR, only change state to
208 * ESTABLISHED if all other more preferable socket
209 * groups are also in ERROR or SHUTDOWN state
210 */
211 bool all_error = true;
212
213 pthread_mutex_lock(&config->mutex);
214 tommy_node *node = tommy_list_head(&config->groups->list);
215
216 while (node) {
217 struct rtr_mgr_group_node *group_node = node->data;
218 struct rtr_mgr_group *current_group = group_node->group;
219
220 if ((current_group != group) &&
221 (current_group->status != RTR_MGR_ERROR) &&
222 (current_group->status != RTR_MGR_CLOSED) &&
223 (current_group->preference < group->preference)) {
224 all_error = false;
225 }
226 node = node->next;
227 }
228 pthread_mutex_unlock(&config->mutex);
229
230 if (all_error && rtr_mgr_config_status_is_synced(group)) {
231 set_status(config, group, RTR_MGR_ESTABLISHED, sock);
232 rtr_mgr_close_less_preferable_groups(sock, config,
233 group);
234 } else {
235 set_status(config, group, RTR_MGR_ERROR, sock);
236 }
237 }
238 }
239
_rtr_mgr_cb_state_connecting(const struct rtr_socket * sock,struct rtr_mgr_config * config,struct rtr_mgr_group * group)240 static inline void _rtr_mgr_cb_state_connecting(const struct rtr_socket *sock,
241 struct rtr_mgr_config *config,
242 struct rtr_mgr_group *group)
243 {
244 if (group->status == RTR_MGR_ERROR)
245 set_status(config, group,
246 RTR_MGR_ERROR, sock);
247 else
248 set_status(config, group,
249 RTR_MGR_CONNECTING, sock);
250 }
251
_rtr_mgr_cb_state_error(const struct rtr_socket * sock,struct rtr_mgr_config * config,struct rtr_mgr_group * group)252 static inline void _rtr_mgr_cb_state_error(const struct rtr_socket *sock,
253 struct rtr_mgr_config *config,
254 struct rtr_mgr_group *group)
255 {
256 set_status(config, group, RTR_MGR_ERROR, sock);
257
258 if (!is_some_rtr_mgr_group_established(config)) {
259 struct rtr_mgr_group *next_group =
260 get_best_inactive_rtr_mgr_group(config, group);
261
262 if (next_group)
263 rtr_mgr_start_sockets(next_group);
264 else
265 MGR_DBG1("No other inactive groups found");
266 }
267 }
268
rtr_mgr_cb(const struct rtr_socket * sock,const enum rtr_socket_state state,void * data_config,void * data_group)269 static void rtr_mgr_cb(const struct rtr_socket *sock,
270 const enum rtr_socket_state state,
271 void *data_config, void *data_group)
272 {
273 if (state == RTR_SHUTDOWN)
274 MGR_DBG1("Received RTR_SHUTDOWN callback");
275
276 struct rtr_mgr_config *config = data_config;
277 struct rtr_mgr_group *group = data_group;
278
279 if (!group) {
280 MGR_DBG1("ERROR: Socket has no group");
281 return;
282 }
283
284 switch (state) {
285 case RTR_SHUTDOWN:
286 _rtr_mgr_cb_state_shutdown(sock, config, group);
287 break;
288 case RTR_ESTABLISHED:
289 _rtr_mgr_cb_state_established(sock, config, group);
290 break;
291 case RTR_CONNECTING:
292 _rtr_mgr_cb_state_connecting(sock, config, group);
293 break;
294 case RTR_ERROR_FATAL:
295 case RTR_ERROR_TRANSPORT:
296 case RTR_ERROR_NO_DATA_AVAIL:
297 _rtr_mgr_cb_state_error(sock, config, group);
298 break;
299 default:
300 set_status(config, group, group->status, sock);
301 }
302 }
303
rtr_mgr_config_cmp(const void * a,const void * b)304 int rtr_mgr_config_cmp(const void *a, const void *b)
305 {
306 const struct rtr_mgr_group *ar = a;
307 const struct rtr_mgr_group *br = b;
308
309 if (ar->preference > br->preference)
310 return 1;
311 else if (ar->preference < br->preference)
312 return -1;
313 return 0;
314 }
315
rtr_mgr_config_cmp_tommy(const void * a,const void * b)316 int rtr_mgr_config_cmp_tommy(const void *a, const void *b)
317 {
318 const struct rtr_mgr_group_node *ar = a;
319 const struct rtr_mgr_group_node *br = b;
320
321 return rtr_mgr_config_cmp(ar->group, br->group);
322 }
323
rtr_mgr_init(struct rtr_mgr_config ** config_out,struct rtr_mgr_group groups[],const unsigned int groups_len,const unsigned int refresh_interval,const unsigned int expire_interval,const unsigned int retry_interval,const pfx_update_fp update_fp,const spki_update_fp spki_update_fp,const rtr_mgr_status_fp status_fp,void * status_fp_data)324 RTRLIB_EXPORT int rtr_mgr_init(struct rtr_mgr_config **config_out,
325 struct rtr_mgr_group groups[],
326 const unsigned int groups_len,
327 const unsigned int refresh_interval,
328 const unsigned int expire_interval,
329 const unsigned int retry_interval,
330 const pfx_update_fp update_fp,
331 const spki_update_fp spki_update_fp,
332 const rtr_mgr_status_fp status_fp,
333 void *status_fp_data)
334 {
335 enum rtr_rtvals err_code = RTR_ERROR;
336 enum rtr_interval_mode iv_mode = RTR_INTERVAL_MODE_DEFAULT_MIN_MAX;
337 struct pfx_table *pfxt = NULL;
338 struct spki_table *spki_table = NULL;
339 struct rtr_mgr_config *config = NULL;
340 struct rtr_mgr_group *cg = NULL;
341 struct rtr_mgr_group_node *group_node;
342 uint8_t last_preference = UINT8_MAX;
343
344 *config_out = NULL;
345
346 if (groups_len == 0) {
347 MGR_DBG1("Error Empty rtr_mgr_group array");
348 return RTR_ERROR;
349 }
350
351 *config_out = config = lrtr_malloc(sizeof(*config));
352 if (!config)
353 return RTR_ERROR;
354
355 config->len = groups_len;
356
357 if (pthread_mutex_init(&config->mutex, NULL) != 0) {
358 MGR_DBG1("Mutex initialization failed");
359 goto err;
360 }
361 /* sort array in asc order, so we can check for dupl. pref */
362 qsort(groups, groups_len,
363 sizeof(struct rtr_mgr_group), &rtr_mgr_config_cmp);
364
365 /* Check that the groups have unique pref and at least one socket */
366 for (unsigned int i = 0; i < config->len; i++) {
367 struct rtr_mgr_group cg = groups[i];
368
369 if ((i > 0) && (cg.preference == last_preference)) {
370 MGR_DBG1("Error Same preference for 2 socket groups!");
371 goto err;
372 }
373 if (cg.sockets_len == 0) {
374 MGR_DBG1("Error Empty sockets array in socket group!");
375 goto err;
376 }
377 }
378
379 /* Init data structures that we need to pass to the sockets */
380 pfxt = lrtr_malloc(sizeof(*pfxt));
381 if (!pfxt)
382 goto err;
383 pfx_table_init(pfxt, update_fp);
384
385 spki_table = lrtr_malloc(sizeof(*spki_table));
386 if (!spki_table)
387 goto err;
388 spki_table_init(spki_table, spki_update_fp);
389
390 config->pfx_table = pfxt;
391 config->spki_table = spki_table;
392
393 /* Copy the groups from the array into linked list config->groups */
394 config->len = groups_len;
395 config->groups = lrtr_malloc(sizeof(*config->groups));
396 if (!config->groups)
397 goto err;
398 config->groups->list = NULL;
399
400 for (unsigned int i = 0; i < groups_len; i++) {
401 cg = lrtr_malloc(sizeof(struct rtr_mgr_group));
402 if (!cg)
403 goto err;
404
405 memcpy(cg, &groups[i], sizeof(struct rtr_mgr_group));
406
407 cg->status = RTR_MGR_CLOSED;
408 err_code = rtr_mgr_init_sockets(cg, config, refresh_interval,
409 expire_interval,
410 retry_interval,
411 iv_mode);
412 if (err_code)
413 goto err;
414
415 group_node = lrtr_malloc(sizeof(struct rtr_mgr_group_node));
416 if (!group_node)
417 goto err;
418
419 group_node->group = cg;
420 tommy_list_insert_tail(&config->groups->list, &group_node->node,
421 group_node);
422 }
423 /* Our linked list should be sorted already, since the groups array was
424 * sorted. However, for safety reasons we sort again.
425 */
426 tommy_list_sort(&config->groups->list, &rtr_mgr_config_cmp_tommy);
427
428 config->status_fp_data = status_fp_data;
429 config->status_fp = status_fp;
430 return RTR_SUCCESS;
431
432 err:
433 if (spki_table)
434 spki_table_free(spki_table);
435 if (pfxt)
436 pfx_table_free(pfxt);
437 lrtr_free(pfxt);
438 lrtr_free(spki_table);
439
440 lrtr_free(cg);
441
442 lrtr_free(config->groups);
443 lrtr_free(config);
444 config = NULL;
445 *config_out = NULL;
446
447 return err_code;
448 }
449
rtr_mgr_get_first_group(struct rtr_mgr_config * config)450 RTRLIB_EXPORT struct rtr_mgr_group *rtr_mgr_get_first_group(
451 struct rtr_mgr_config *config)
452 {
453 tommy_node *head = tommy_list_head(&config->groups->list);
454 struct rtr_mgr_group_node *group_node = head->data;
455
456 return group_node->group;
457 }
458
rtr_mgr_start(struct rtr_mgr_config * config)459 RTRLIB_EXPORT int rtr_mgr_start(struct rtr_mgr_config *config)
460 {
461 MGR_DBG1("rtr_mgr_start()");
462 struct rtr_mgr_group *best_group = rtr_mgr_get_first_group(config);
463
464 return rtr_mgr_start_sockets(best_group);
465 }
466
rtr_mgr_conf_in_sync(struct rtr_mgr_config * config)467 RTRLIB_EXPORT bool rtr_mgr_conf_in_sync(struct rtr_mgr_config *config)
468 {
469 pthread_mutex_lock(&config->mutex);
470 tommy_node *node = tommy_list_head(&config->groups->list);
471
472 while (node) {
473 bool all_sync = true;
474 struct rtr_mgr_group_node *group_node = node->data;
475
476 for (unsigned int j = 0; all_sync &&
477 (j < group_node->group->sockets_len); j++) {
478 if (group_node->group->sockets[j]->last_update == 0)
479 all_sync = false;
480 }
481 if (all_sync) {
482 pthread_mutex_unlock(&config->mutex);
483 return true;
484 }
485 node = node->next;
486 }
487 pthread_mutex_unlock(&config->mutex);
488 return false;
489 }
490
rtr_mgr_free(struct rtr_mgr_config * config)491 RTRLIB_EXPORT void rtr_mgr_free(struct rtr_mgr_config *config)
492 {
493 MGR_DBG1("rtr_mgr_free()");
494 pthread_mutex_lock(&config->mutex);
495
496 pfx_table_free(config->pfx_table);
497 spki_table_free(config->spki_table);
498 lrtr_free(config->spki_table);
499 lrtr_free(config->pfx_table);
500
501 /* Free linked list */
502 tommy_node *head = tommy_list_head(&config->groups->list);
503
504 while (head) {
505 tommy_node *tmp = head;
506 struct rtr_mgr_group_node *group_node = tmp->data;
507
508 head = head->next;
509 for (unsigned int j = 0; j < group_node->group->sockets_len;
510 j++) {
511 tr_free(group_node->group->sockets[j]->tr_socket);
512 }
513
514 lrtr_free(group_node->group);
515 lrtr_free(group_node);
516 }
517
518 lrtr_free(config->groups);
519
520 pthread_mutex_unlock(&config->mutex);
521 pthread_mutex_destroy(&config->mutex);
522 lrtr_free(config);
523 }
524
525 /* cppcheck-suppress unusedFunction */
rtr_mgr_validate(struct rtr_mgr_config * config,const uint32_t asn,const struct lrtr_ip_addr * prefix,const uint8_t mask_len,enum pfxv_state * result)526 RTRLIB_EXPORT inline int rtr_mgr_validate(struct rtr_mgr_config *config,
527 const uint32_t asn,
528 const struct lrtr_ip_addr *prefix,
529 const uint8_t mask_len,
530 enum pfxv_state *result)
531 {
532 return pfx_table_validate(config->pfx_table, asn, prefix, mask_len,
533 result);
534 }
535
536 /* cppcheck-suppress unusedFunction */
rtr_mgr_get_spki(struct rtr_mgr_config * config,const uint32_t asn,uint8_t * ski,struct spki_record ** result,unsigned int * result_count)537 RTRLIB_EXPORT inline int rtr_mgr_get_spki(struct rtr_mgr_config *config,
538 const uint32_t asn,
539 uint8_t *ski,
540 struct spki_record **result,
541 unsigned int *result_count)
542 {
543 return spki_table_get_all(config->spki_table,
544 asn, ski, result, result_count);
545 }
546
rtr_mgr_stop(struct rtr_mgr_config * config)547 RTRLIB_EXPORT void rtr_mgr_stop(struct rtr_mgr_config *config)
548 {
549 pthread_mutex_lock(&config->mutex);
550 tommy_node *node = tommy_list_head(&config->groups->list);
551
552 MGR_DBG1("rtr_mgr_stop()");
553 while (node) {
554 struct rtr_mgr_group_node *group_node = node->data;
555
556 for (unsigned int j = 0; j < group_node->group->sockets_len;
557 j++) {
558 rtr_stop(group_node->group->sockets[j]);
559 }
560 node = node->next;
561 }
562 pthread_mutex_unlock(&config->mutex);
563 }
564
565 /* cppcheck-suppress unusedFunction */
rtr_mgr_add_group(struct rtr_mgr_config * config,const struct rtr_mgr_group * group)566 RTRLIB_EXPORT int rtr_mgr_add_group(
567 struct rtr_mgr_config *config,
568 const struct rtr_mgr_group *group)
569 {
570 unsigned int refresh_iv = 3600;
571 unsigned int retry_iv = 600;
572 unsigned int expire_iv = 7200;
573 enum rtr_interval_mode iv_mode = RTR_INTERVAL_MODE_DEFAULT_MIN_MAX;
574 enum rtr_rtvals err_code = RTR_ERROR;
575 struct rtr_mgr_group_node *new_group_node = NULL;
576 struct rtr_mgr_group *new_group = NULL;
577 struct rtr_mgr_group_node *gnode;
578
579 pthread_mutex_lock(&config->mutex);
580
581 tommy_node *node = tommy_list_head(&config->groups->list);
582
583 while (node) {
584 gnode = node->data;
585 if (gnode->group->preference == group->preference) {
586 MGR_DBG1("Group with preference value already exists!");
587 err_code = RTR_INVALID_PARAM;
588 goto err;
589 }
590
591 //TODO This is not pretty. It wants to get the same intervals
592 //that are being used by other groups. Maybe intervals should
593 //be store globally/per-group/per-socket?
594 if (gnode->group->sockets[0]->refresh_interval)
595 refresh_iv = gnode->group->sockets[0]->refresh_interval;
596 if (gnode->group->sockets[0]->retry_interval)
597 retry_iv = gnode->group->sockets[0]->retry_interval;
598 if (gnode->group->sockets[0]->expire_interval)
599 expire_iv = gnode->group->sockets[0]->expire_interval;
600 node = node->next;
601 }
602 new_group = lrtr_malloc(sizeof(struct rtr_mgr_group));
603
604 if (!new_group)
605 goto err;
606
607 memcpy(new_group, group, sizeof(struct rtr_mgr_group));
608 new_group->status = RTR_MGR_CLOSED;
609
610 err_code = rtr_mgr_init_sockets(new_group, config, refresh_iv,
611 expire_iv, retry_iv, iv_mode);
612 if (err_code)
613 goto err;
614
615 new_group_node = lrtr_malloc(sizeof(struct rtr_mgr_group_node));
616 if (!new_group_node)
617 goto err;
618
619 new_group_node->group = new_group;
620 tommy_list_insert_tail(&config->groups->list, &new_group_node->node,
621 new_group_node);
622 config->len++;
623
624 MGR_DBG("Group with preference %d successfully added!",
625 new_group->preference);
626
627 tommy_list_sort(&config->groups->list, &rtr_mgr_config_cmp_tommy);
628
629 struct rtr_mgr_group *best_group = rtr_mgr_get_first_group(config);
630
631 if (best_group->status == RTR_MGR_CLOSED)
632 rtr_mgr_start_sockets(best_group);
633
634 pthread_mutex_unlock(&config->mutex);
635 return RTR_SUCCESS;
636
637 err:
638 pthread_mutex_unlock(&config->mutex);
639
640 if (new_group)
641 lrtr_free(new_group);
642
643 return err_code;
644 }
645
646 /* cppcheck-suppress unusedFunction */
rtr_mgr_remove_group(struct rtr_mgr_config * config,unsigned int preference)647 RTRLIB_EXPORT int rtr_mgr_remove_group(
648 struct rtr_mgr_config *config,
649 unsigned int preference)
650 {
651 pthread_mutex_lock(&config->mutex);
652 tommy_node *remove_node = NULL;
653 tommy_node *node = tommy_list_head(&config->groups->list);
654 struct rtr_mgr_group_node *group_node;
655 struct rtr_mgr_group *remove_group;
656
657 if (config->len == 1) {
658 MGR_DBG1("Cannot remove last remaining group!");
659 pthread_mutex_unlock(&config->mutex);
660 return RTR_ERROR;
661 }
662
663 // Find the node of the group we want to remove
664 while (node && !remove_node) {
665 group_node = node->data;
666 if (group_node->group->preference == preference)
667 remove_node = node;
668 node = node->next;
669 }
670
671 if (!remove_node) {
672 MGR_DBG1("The group that should be removed does not exist!");
673 pthread_mutex_unlock(&config->mutex);
674 return RTR_ERROR;
675 }
676
677 group_node = remove_node->data;
678 remove_group = group_node->group;
679 tommy_list_remove_existing(&config->groups->list, remove_node);
680 config->len--;
681 MGR_DBG("Group with preference %d successfully removed!", preference);
682 //tommy_list_sort(&config->groups->list, &rtr_mgr_config_cmp);
683 pthread_mutex_unlock(&config->mutex);
684
685 //If group isn't closed, make it so!
686 if (remove_group->status != RTR_MGR_CLOSED) {
687 for (unsigned int j = 0; j < remove_group->sockets_len; j++) {
688 rtr_stop(remove_group->sockets[j]);
689 tr_free(remove_group->sockets[j]->tr_socket);
690 }
691 set_status(config, remove_group, RTR_MGR_CLOSED, NULL);
692 }
693
694 struct rtr_mgr_group *best_group = rtr_mgr_get_first_group(config);
695
696 if (best_group->status == RTR_MGR_CLOSED)
697 rtr_mgr_start_sockets(best_group);
698
699 lrtr_free(group_node->group);
700 lrtr_free(group_node);
701 return RTR_SUCCESS;
702 }
703
704 // TODO: write test for this function.
705 /* cppcheck-suppress unusedFunction */
rtr_mgr_for_each_group(struct rtr_mgr_config * config,void (fp)(const struct rtr_mgr_group * group,void * data),void * data)706 RTRLIB_EXPORT int rtr_mgr_for_each_group(
707 struct rtr_mgr_config *config,
708 void (fp)(const struct rtr_mgr_group *group, void *data),
709 void *data)
710 {
711 tommy_node *node = tommy_list_head(&config->groups->list);
712
713 while (node) {
714 struct rtr_mgr_group_node *group_node = node->data;
715
716 fp(group_node->group, data);
717 node = node->next;
718 }
719
720 return RTR_SUCCESS;
721 }
722
rtr_mgr_status_to_str(enum rtr_mgr_status status)723 RTRLIB_EXPORT const char *rtr_mgr_status_to_str(enum rtr_mgr_status status)
724 {
725 return mgr_str_status[status];
726 }
727
728 /* cppcheck-suppress unusedFunction */
rtr_mgr_for_each_ipv4_record(struct rtr_mgr_config * config,void (fp)(const struct pfx_record *,void * data),void * data)729 RTRLIB_EXPORT inline void rtr_mgr_for_each_ipv4_record(
730 struct rtr_mgr_config *config,
731 void (fp)(
732 const struct pfx_record *,
733 void *data),
734 void *data)
735 {
736 pfx_table_for_each_ipv4_record(config->pfx_table,
737 fp, data);
738 }
739
740 /* cppcheck-suppress unusedFunction */
rtr_mgr_for_each_ipv6_record(struct rtr_mgr_config * config,void (fp)(const struct pfx_record *,void * data),void * data)741 RTRLIB_EXPORT inline void rtr_mgr_for_each_ipv6_record(
742 struct rtr_mgr_config *config,
743 void (fp)(
744 const struct pfx_record *,
745 void *data),
746 void *data)
747 {
748 pfx_table_for_each_ipv6_record(config->pfx_table,
749 fp, data);
750 }
751