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