1 /* Community attribute related functions.
2  * Copyright (C) 1998, 2001 Kunihiro Ishiguro
3  *
4  * This file is part of GNU Zebra.
5  *
6  * GNU Zebra is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2, or (at your option) any
9  * later version.
10  *
11  * GNU Zebra is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; see the file COPYING; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <zebra.h>
22 
23 #include "command.h"
24 #include "hash.h"
25 #include "memory.h"
26 #include "jhash.h"
27 #include "frrstr.h"
28 
29 #include "bgpd/bgp_memory.h"
30 #include "bgpd/bgp_community.h"
31 
32 /* Hash of community attribute. */
33 static struct hash *comhash;
34 
35 /* Allocate a new communities value.  */
community_new(void)36 static struct community *community_new(void)
37 {
38 	return XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
39 }
40 
41 /* Free communities value.  */
community_free(struct community ** com)42 void community_free(struct community **com)
43 {
44 	if (!(*com))
45 		return;
46 
47 	XFREE(MTYPE_COMMUNITY_VAL, (*com)->val);
48 	XFREE(MTYPE_COMMUNITY_STR, (*com)->str);
49 
50 	if ((*com)->json) {
51 		json_object_free((*com)->json);
52 		(*com)->json = NULL;
53 	}
54 
55 	XFREE(MTYPE_COMMUNITY, (*com));
56 }
57 
58 /* Add one community value to the community. */
community_add_val(struct community * com,uint32_t val)59 static void community_add_val(struct community *com, uint32_t val)
60 {
61 	com->size++;
62 	if (com->val)
63 		com->val = XREALLOC(MTYPE_COMMUNITY_VAL, com->val,
64 				    com_length(com));
65 	else
66 		com->val = XMALLOC(MTYPE_COMMUNITY_VAL, com_length(com));
67 
68 	val = htonl(val);
69 	memcpy(com_lastval(com), &val, sizeof(uint32_t));
70 }
71 
72 /* Delete one community. */
community_del_val(struct community * com,uint32_t * val)73 void community_del_val(struct community *com, uint32_t *val)
74 {
75 	int i = 0;
76 	int c = 0;
77 
78 	if (!com->val)
79 		return;
80 
81 	while (i < com->size) {
82 		if (memcmp(com->val + i, val, sizeof(uint32_t)) == 0) {
83 			c = com->size - i - 1;
84 
85 			if (c > 0)
86 				memmove(com->val + i, com->val + (i + 1),
87 					c * sizeof(*val));
88 
89 			com->size--;
90 
91 			if (com->size > 0)
92 				com->val = XREALLOC(MTYPE_COMMUNITY_VAL,
93 						    com->val, com_length(com));
94 			else {
95 				XFREE(MTYPE_COMMUNITY_VAL, com->val);
96 			}
97 			return;
98 		}
99 		i++;
100 	}
101 }
102 
103 /* Delete all communities listed in com2 from com1 */
community_delete(struct community * com1,struct community * com2)104 struct community *community_delete(struct community *com1,
105 				   struct community *com2)
106 {
107 	int i = 0;
108 
109 	while (i < com2->size) {
110 		community_del_val(com1, com2->val + i);
111 		i++;
112 	}
113 
114 	return com1;
115 }
116 
117 /* Callback function from qsort(). */
community_compare(const void * a1,const void * a2)118 static int community_compare(const void *a1, const void *a2)
119 {
120 	uint32_t v1;
121 	uint32_t v2;
122 
123 	memcpy(&v1, a1, sizeof(uint32_t));
124 	memcpy(&v2, a2, sizeof(uint32_t));
125 	v1 = ntohl(v1);
126 	v2 = ntohl(v2);
127 
128 	if (v1 < v2)
129 		return -1;
130 	if (v1 > v2)
131 		return 1;
132 	return 0;
133 }
134 
community_include(struct community * com,uint32_t val)135 bool community_include(struct community *com, uint32_t val)
136 {
137 	int i;
138 
139 	val = htonl(val);
140 
141 	for (i = 0; i < com->size; i++)
142 		if (memcmp(&val, com_nthval(com, i), sizeof(uint32_t)) == 0)
143 			return true;
144 	return false;
145 }
146 
community_val_get(struct community * com,int i)147 uint32_t community_val_get(struct community *com, int i)
148 {
149 	uint8_t *p;
150 	uint32_t val;
151 
152 	p = (uint8_t *)com->val;
153 	p += (i * COMMUNITY_SIZE);
154 
155 	memcpy(&val, p, sizeof(uint32_t));
156 
157 	return ntohl(val);
158 }
159 
160 /* Sort and uniq given community. */
community_uniq_sort(struct community * com)161 struct community *community_uniq_sort(struct community *com)
162 {
163 	int i;
164 	struct community *new;
165 	uint32_t val;
166 
167 	if (!com)
168 		return NULL;
169 
170 	new = community_new();
171 	new->json = NULL;
172 
173 	for (i = 0; i < com->size; i++) {
174 		val = community_val_get(com, i);
175 
176 		if (!community_include(new, val))
177 			community_add_val(new, val);
178 	}
179 
180 	qsort(new->val, new->size, sizeof(uint32_t), community_compare);
181 
182 	return new;
183 }
184 
185 /* Convert communities attribute to string.
186 
187    For Well-known communities value, below keyword is used.
188 
189    0x0             "internet"
190    0xFFFF0000      "graceful-shutdown"
191    0xFFFF0001      "accept-own"
192    0xFFFF0002      "route-filter-translated-v4"
193    0xFFFF0003      "route-filter-v4"
194    0xFFFF0004      "route-filter-translated-v6"
195    0xFFFF0005      "route-filter-v6"
196    0xFFFF0006      "llgr-stale"
197    0xFFFF0007      "no-llgr"
198    0xFFFF0008      "accept-own-nexthop"
199    0xFFFF029A      "blackhole"
200    0xFFFFFF01      "no-export"
201    0xFFFFFF02      "no-advertise"
202    0xFFFFFF03      "local-AS"
203    0xFFFFFF04      "no-peer"
204 
205    For other values, "AS:VAL" format is used.  */
set_community_string(struct community * com,bool make_json)206 static void set_community_string(struct community *com, bool make_json)
207 {
208 	int i;
209 	char *str;
210 	int len;
211 	int first;
212 	uint32_t comval;
213 	uint16_t as;
214 	uint16_t val;
215 	json_object *json_community_list = NULL;
216 	json_object *json_string = NULL;
217 
218 	if (!com)
219 		return;
220 
221 	if (make_json) {
222 		com->json = json_object_new_object();
223 		json_community_list = json_object_new_array();
224 	}
225 
226 	/* When communities attribute is empty.  */
227 	if (com->size == 0) {
228 		str = XMALLOC(MTYPE_COMMUNITY_STR, 1);
229 		str[0] = '\0';
230 
231 		if (make_json) {
232 			json_object_string_add(com->json, "string", "");
233 			json_object_object_add(com->json, "list",
234 					       json_community_list);
235 		}
236 		com->str = str;
237 		return;
238 	}
239 
240 	/* Memory allocation is time consuming work.  So we calculate
241 	   required string length first.  */
242 	len = 0;
243 
244 	for (i = 0; i < com->size; i++) {
245 		memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
246 		comval = ntohl(comval);
247 
248 		switch (comval) {
249 		case COMMUNITY_INTERNET:
250 			len += strlen(" internet");
251 			break;
252 		case COMMUNITY_GSHUT:
253 			len += strlen(" graceful-shutdown");
254 			break;
255 		case COMMUNITY_ACCEPT_OWN:
256 			len += strlen(" accept-own");
257 			break;
258 		case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
259 			len += strlen(" route-filter-translated-v4");
260 			break;
261 		case COMMUNITY_ROUTE_FILTER_v4:
262 			len += strlen(" route-filter-v4");
263 			break;
264 		case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
265 			len += strlen(" route-filter-translated-v6");
266 			break;
267 		case COMMUNITY_ROUTE_FILTER_v6:
268 			len += strlen(" route-filter-v6");
269 			break;
270 		case COMMUNITY_LLGR_STALE:
271 			len += strlen(" llgr-stale");
272 			break;
273 		case COMMUNITY_NO_LLGR:
274 			len += strlen(" no-llgr");
275 			break;
276 		case COMMUNITY_ACCEPT_OWN_NEXTHOP:
277 			len += strlen(" accept-own-nexthop");
278 			break;
279 		case COMMUNITY_BLACKHOLE:
280 			len += strlen(" blackhole");
281 			break;
282 		case COMMUNITY_NO_EXPORT:
283 			len += strlen(" no-export");
284 			break;
285 		case COMMUNITY_NO_ADVERTISE:
286 			len += strlen(" no-advertise");
287 			break;
288 		case COMMUNITY_LOCAL_AS:
289 			len += strlen(" local-AS");
290 			break;
291 		case COMMUNITY_NO_PEER:
292 			len += strlen(" no-peer");
293 			break;
294 		default:
295 			len += strlen(" 65536:65535");
296 			break;
297 		}
298 	}
299 
300 	/* Allocate memory.  */
301 	str = XCALLOC(MTYPE_COMMUNITY_STR, len);
302 	first = 1;
303 
304 	/* Fill in string.  */
305 	for (i = 0; i < com->size; i++) {
306 		memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
307 		comval = ntohl(comval);
308 
309 		if (first)
310 			first = 0;
311 		else
312 			strlcat(str, " ", len);
313 
314 		switch (comval) {
315 		case COMMUNITY_INTERNET:
316 			strlcat(str, "internet", len);
317 			if (make_json) {
318 				json_string =
319 					json_object_new_string("internet");
320 				json_object_array_add(json_community_list,
321 						      json_string);
322 			}
323 			break;
324 		case COMMUNITY_GSHUT:
325 			strlcat(str, "graceful-shutdown", len);
326 			if (make_json) {
327 				json_string = json_object_new_string(
328 					"gracefulShutdown");
329 				json_object_array_add(json_community_list,
330 						      json_string);
331 			}
332 			break;
333 		case COMMUNITY_ACCEPT_OWN:
334 			strlcat(str, "accept-own", len);
335 			if (make_json) {
336 				json_string = json_object_new_string(
337 					"acceptown");
338 				json_object_array_add(json_community_list,
339 						      json_string);
340 			}
341 			break;
342 		case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
343 			strlcat(str, "route-filter-translated-v4", len);
344 			if (make_json) {
345 				json_string = json_object_new_string(
346 					"routeFilterTranslatedV4");
347 				json_object_array_add(json_community_list,
348 						      json_string);
349 			}
350 			break;
351 		case COMMUNITY_ROUTE_FILTER_v4:
352 			strlcat(str, "route-filter-v4", len);
353 			if (make_json) {
354 				json_string = json_object_new_string(
355 					"routeFilterV4");
356 				json_object_array_add(json_community_list,
357 						      json_string);
358 			}
359 			break;
360 		case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
361 			strlcat(str, "route-filter-translated-v6", len);
362 			if (make_json) {
363 				json_string = json_object_new_string(
364 					"routeFilterTranslatedV6");
365 				json_object_array_add(json_community_list,
366 						      json_string);
367 			}
368 			break;
369 		case COMMUNITY_ROUTE_FILTER_v6:
370 			strlcat(str, "route-filter-v6", len);
371 			if (make_json) {
372 				json_string = json_object_new_string(
373 					"routeFilterV6");
374 				json_object_array_add(json_community_list,
375 						      json_string);
376 			}
377 			break;
378 		case COMMUNITY_LLGR_STALE:
379 			strlcat(str, "llgr-stale", len);
380 			if (make_json) {
381 				json_string = json_object_new_string(
382 					"llgrStale");
383 				json_object_array_add(json_community_list,
384 						      json_string);
385 			}
386 			break;
387 		case COMMUNITY_NO_LLGR:
388 			strlcat(str, "no-llgr", len);
389 			if (make_json) {
390 				json_string = json_object_new_string(
391 					"noLlgr");
392 				json_object_array_add(json_community_list,
393 						      json_string);
394 			}
395 			break;
396 		case COMMUNITY_ACCEPT_OWN_NEXTHOP:
397 			strlcat(str, "accept-own-nexthop", len);
398 			if (make_json) {
399 				json_string = json_object_new_string(
400 					"acceptownnexthop");
401 				json_object_array_add(json_community_list,
402 						      json_string);
403 			}
404 			break;
405 		case COMMUNITY_BLACKHOLE:
406 			strlcat(str, "blackhole", len);
407 			if (make_json) {
408 				json_string = json_object_new_string(
409 					"blackhole");
410 				json_object_array_add(json_community_list,
411 						      json_string);
412 			}
413 			break;
414 		case COMMUNITY_NO_EXPORT:
415 			strlcat(str, "no-export", len);
416 			if (make_json) {
417 				json_string =
418 					json_object_new_string("noExport");
419 				json_object_array_add(json_community_list,
420 						      json_string);
421 			}
422 			break;
423 		case COMMUNITY_NO_ADVERTISE:
424 			strlcat(str, "no-advertise", len);
425 			if (make_json) {
426 				json_string =
427 					json_object_new_string("noAdvertise");
428 				json_object_array_add(json_community_list,
429 						      json_string);
430 			}
431 			break;
432 		case COMMUNITY_LOCAL_AS:
433 			strlcat(str, "local-AS", len);
434 			if (make_json) {
435 				json_string = json_object_new_string("localAs");
436 				json_object_array_add(json_community_list,
437 						      json_string);
438 			}
439 			break;
440 		case COMMUNITY_NO_PEER:
441 			strlcat(str, "no-peer", len);
442 			if (make_json) {
443 				json_string = json_object_new_string("noPeer");
444 				json_object_array_add(json_community_list,
445 						      json_string);
446 			}
447 			break;
448 		default:
449 			as = (comval >> 16) & 0xFFFF;
450 			val = comval & 0xFFFF;
451 			char buf[32];
452 			snprintf(buf, sizeof(buf), "%u:%d", as, val);
453 			strlcat(str, buf, len);
454 			if (make_json) {
455 				json_string = json_object_new_string(buf);
456 				json_object_array_add(json_community_list,
457 						      json_string);
458 			}
459 			break;
460 		}
461 	}
462 
463 	if (make_json) {
464 		json_object_string_add(com->json, "string", str);
465 		json_object_object_add(com->json, "list", json_community_list);
466 	}
467 	com->str = str;
468 }
469 
470 /* Intern communities attribute.  */
community_intern(struct community * com)471 struct community *community_intern(struct community *com)
472 {
473 	struct community *find;
474 
475 	/* Assert this community structure is not interned. */
476 	assert(com->refcnt == 0);
477 
478 	/* Lookup community hash. */
479 	find = (struct community *)hash_get(comhash, com, hash_alloc_intern);
480 
481 	/* Arguemnt com is allocated temporary.  So when it is not used in
482 	   hash, it should be freed.  */
483 	if (find != com)
484 		community_free(&com);
485 
486 	/* Increment refrence counter.  */
487 	find->refcnt++;
488 
489 	/* Make string.  */
490 	if (!find->str)
491 		set_community_string(find, false);
492 
493 	return find;
494 }
495 
496 /* Free community attribute. */
community_unintern(struct community ** com)497 void community_unintern(struct community **com)
498 {
499 	struct community *ret;
500 
501 	if ((*com)->refcnt)
502 		(*com)->refcnt--;
503 
504 	/* Pull off from hash.  */
505 	if ((*com)->refcnt == 0) {
506 		/* Community value com must exist in hash. */
507 		ret = (struct community *)hash_release(comhash, *com);
508 		assert(ret != NULL);
509 
510 		community_free(com);
511 	}
512 }
513 
514 /* Create new community attribute. */
community_parse(uint32_t * pnt,unsigned short length)515 struct community *community_parse(uint32_t *pnt, unsigned short length)
516 {
517 	struct community tmp;
518 	struct community *new;
519 
520 	/* If length is malformed return NULL. */
521 	if (length % COMMUNITY_SIZE)
522 		return NULL;
523 
524 	/* Make temporary community for hash look up. */
525 	tmp.size = length / COMMUNITY_SIZE;
526 	tmp.val = pnt;
527 
528 	new = community_uniq_sort(&tmp);
529 
530 	return community_intern(new);
531 }
532 
community_dup(struct community * com)533 struct community *community_dup(struct community *com)
534 {
535 	struct community *new;
536 
537 	new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
538 	new->size = com->size;
539 	if (new->size) {
540 		new->val = XMALLOC(MTYPE_COMMUNITY_VAL,
541 				   com->size * COMMUNITY_SIZE);
542 		memcpy(new->val, com->val, com->size * COMMUNITY_SIZE);
543 	} else
544 		new->val = NULL;
545 	return new;
546 }
547 
548 /* Retrun string representation of communities attribute. */
community_str(struct community * com,bool make_json)549 char *community_str(struct community *com, bool make_json)
550 {
551 	if (!com)
552 		return NULL;
553 
554 	if (make_json && !com->json && com->str)
555 		XFREE(MTYPE_COMMUNITY_STR, com->str);
556 
557 	if (!com->str)
558 		set_community_string(com, make_json);
559 	return com->str;
560 }
561 
562 /* Make hash value of community attribute. This function is used by
563    hash package.*/
community_hash_make(const struct community * com)564 unsigned int community_hash_make(const struct community *com)
565 {
566 	uint32_t *pnt = com->val;
567 
568 	return jhash2(pnt, com->size, 0x43ea96c1);
569 }
570 
community_match(const struct community * com1,const struct community * com2)571 bool community_match(const struct community *com1, const struct community *com2)
572 {
573 	int i = 0;
574 	int j = 0;
575 
576 	if (com1 == NULL && com2 == NULL)
577 		return true;
578 
579 	if (com1 == NULL || com2 == NULL)
580 		return false;
581 
582 	if (com1->size < com2->size)
583 		return false;
584 
585 	/* Every community on com2 needs to be on com1 for this to match */
586 	while (i < com1->size && j < com2->size) {
587 		if (memcmp(com1->val + i, com2->val + j, sizeof(uint32_t)) == 0)
588 			j++;
589 		i++;
590 	}
591 
592 	if (j == com2->size)
593 		return true;
594 	else
595 		return false;
596 }
597 
598 /* If two aspath have same value then return 1 else return 0. This
599    function is used by hash package. */
community_cmp(const struct community * com1,const struct community * com2)600 bool community_cmp(const struct community *com1, const struct community *com2)
601 {
602 	if (com1 == NULL && com2 == NULL)
603 		return true;
604 	if (com1 == NULL || com2 == NULL)
605 		return false;
606 
607 	if (com1->size == com2->size)
608 		if (memcmp(com1->val, com2->val, com1->size * COMMUNITY_SIZE)
609 		    == 0)
610 			return true;
611 	return false;
612 }
613 
614 /* Add com2 to the end of com1. */
community_merge(struct community * com1,struct community * com2)615 struct community *community_merge(struct community *com1,
616 				  struct community *com2)
617 {
618 	if (com1->val)
619 		com1->val =
620 			XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
621 				 (com1->size + com2->size) * COMMUNITY_SIZE);
622 	else
623 		com1->val = XMALLOC(MTYPE_COMMUNITY_VAL,
624 				    (com1->size + com2->size) * COMMUNITY_SIZE);
625 
626 	memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE);
627 	com1->size += com2->size;
628 
629 	return com1;
630 }
631 
632 /* Community token enum. */
633 enum community_token {
634 	community_token_val,
635 	community_token_gshut,
636 	community_token_accept_own,
637 	community_token_route_filter_translated_v4,
638 	community_token_route_filter_v4,
639 	community_token_route_filter_translated_v6,
640 	community_token_route_filter_v6,
641 	community_token_llgr_stale,
642 	community_token_no_llgr,
643 	community_token_accept_own_nexthop,
644 	community_token_blackhole,
645 	community_token_no_export,
646 	community_token_no_advertise,
647 	community_token_local_as,
648 	community_token_no_peer,
649 	community_token_unknown
650 };
651 
652 /* Helper to check if a given community is valid */
community_valid(const char * community)653 static bool community_valid(const char *community)
654 {
655 	int octets = 0;
656 	char **splits;
657 	int num;
658 	int invalid = 0;
659 
660 	frrstr_split(community, ":", &splits, &num);
661 
662 	for (int i = 0; i < num; i++) {
663 		if (strtoul(splits[i], NULL, 10) > UINT16_MAX)
664 			invalid++;
665 
666 		if (strlen(splits[i]) == 0)
667 			invalid++;
668 
669 		octets++;
670 		XFREE(MTYPE_TMP, splits[i]);
671 	}
672 	XFREE(MTYPE_TMP, splits);
673 
674 	return (octets < 2 || invalid) ? false : true;
675 }
676 
677 /* Get next community token from string. */
678 static const char *
community_gettoken(const char * buf,enum community_token * token,uint32_t * val)679 community_gettoken(const char *buf, enum community_token *token, uint32_t *val)
680 {
681 	const char *p = buf;
682 
683 	/* Skip white space. */
684 	while (isspace((unsigned char)*p))
685 		p++;
686 
687 	/* Check the end of the line. */
688 	if (*p == '\0')
689 		return NULL;
690 
691 	/* Well known community string check. */
692 	if (isalpha((unsigned char)*p)) {
693 		if (strncmp(p, "internet", strlen("internet")) == 0) {
694 			*val = COMMUNITY_INTERNET;
695 			*token = community_token_no_export;
696 			p += strlen("internet");
697 			return p;
698 		}
699 		if (strncmp(p, "graceful-shutdown", strlen("graceful-shutdown"))
700 		    == 0) {
701 			*val = COMMUNITY_GSHUT;
702 			*token = community_token_gshut;
703 			p += strlen("graceful-shutdown");
704 			return p;
705 		}
706 		if (strncmp(p, "accept-own-nexthop",
707 			    strlen("accept-own-nexthop"))
708 		    == 0) {
709 			*val = COMMUNITY_ACCEPT_OWN_NEXTHOP;
710 			*token = community_token_accept_own_nexthop;
711 			p += strlen("accept-own-nexthop");
712 			return p;
713 		}
714 		if (strncmp(p, "accept-own", strlen("accept-own"))
715 		    == 0) {
716 			*val = COMMUNITY_ACCEPT_OWN;
717 			*token = community_token_accept_own;
718 			p += strlen("accept-own");
719 			return p;
720 		}
721 		if (strncmp(p, "route-filter-translated-v4",
722 			strlen("route-filter-translated-v4"))
723 		    == 0) {
724 			*val = COMMUNITY_ROUTE_FILTER_TRANSLATED_v4;
725 			*token = community_token_route_filter_translated_v4;
726 			p += strlen("route-filter-translated-v4");
727 			return p;
728 		}
729 		if (strncmp(p, "route-filter-v4", strlen("route-filter-v4"))
730 		    == 0) {
731 			*val = COMMUNITY_ROUTE_FILTER_v4;
732 			*token = community_token_route_filter_v4;
733 			p += strlen("route-filter-v4");
734 			return p;
735 		}
736 		if (strncmp(p, "route-filter-translated-v6",
737 			strlen("route-filter-translated-v6"))
738 		    == 0) {
739 			*val = COMMUNITY_ROUTE_FILTER_TRANSLATED_v6;
740 			*token = community_token_route_filter_translated_v6;
741 			p += strlen("route-filter-translated-v6");
742 			return p;
743 		}
744 		if (strncmp(p, "route-filter-v6", strlen("route-filter-v6"))
745 		    == 0) {
746 			*val = COMMUNITY_ROUTE_FILTER_v6;
747 			*token = community_token_route_filter_v6;
748 			p += strlen("route-filter-v6");
749 			return p;
750 		}
751 		if (strncmp(p, "llgr-stale", strlen("llgr-stale"))
752 		    == 0) {
753 			*val = COMMUNITY_LLGR_STALE;
754 			*token = community_token_llgr_stale;
755 			p += strlen("llgr-stale");
756 			return p;
757 		}
758 		if (strncmp(p, "no-llgr", strlen("no-llgr"))
759 		    == 0) {
760 			*val = COMMUNITY_NO_LLGR;
761 			*token = community_token_no_llgr;
762 			p += strlen("no-llgr");
763 			return p;
764 		}
765 		if (strncmp(p, "blackhole", strlen("blackhole"))
766 		    == 0) {
767 			*val = COMMUNITY_BLACKHOLE;
768 			*token = community_token_blackhole;
769 			p += strlen("blackhole");
770 			return p;
771 		}
772 		if (strncmp(p, "no-export", strlen("no-export")) == 0) {
773 			*val = COMMUNITY_NO_EXPORT;
774 			*token = community_token_no_export;
775 			p += strlen("no-export");
776 			return p;
777 		}
778 		if (strncmp(p, "no-advertise", strlen("no-advertise")) == 0) {
779 			*val = COMMUNITY_NO_ADVERTISE;
780 			*token = community_token_no_advertise;
781 			p += strlen("no-advertise");
782 			return p;
783 		}
784 		if (strncmp(p, "local-AS", strlen("local-AS")) == 0) {
785 			*val = COMMUNITY_LOCAL_AS;
786 			*token = community_token_local_as;
787 			p += strlen("local-AS");
788 			return p;
789 		}
790 		if (strncmp(p, "no-peer", strlen("no-peer")) == 0) {
791 			*val = COMMUNITY_NO_PEER;
792 			*token = community_token_no_peer;
793 			p += strlen("no-peer");
794 			return p;
795 		}
796 
797 		/* Unknown string. */
798 		*token = community_token_unknown;
799 		return NULL;
800 	}
801 
802 	/* Community value. */
803 	if (isdigit((unsigned char)*p)) {
804 		int separator = 0;
805 		int digit = 0;
806 		uint32_t community_low = 0;
807 		uint32_t community_high = 0;
808 
809 		if (!community_valid(p)) {
810 			*token = community_token_unknown;
811 			return NULL;
812 		}
813 
814 		while (isdigit((unsigned char)*p) || *p == ':') {
815 			if (*p == ':') {
816 				if (separator) {
817 					*token = community_token_unknown;
818 					return NULL;
819 				} else {
820 					separator = 1;
821 					digit = 0;
822 
823 					if (community_low > UINT16_MAX) {
824 						*token =
825 							community_token_unknown;
826 						return NULL;
827 					}
828 
829 					community_high = community_low << 16;
830 					community_low = 0;
831 				}
832 			} else {
833 				digit = 1;
834 				community_low *= 10;
835 				community_low += (*p - '0');
836 			}
837 			p++;
838 		}
839 		if (!digit) {
840 			*token = community_token_unknown;
841 			return NULL;
842 		}
843 
844 		*val = community_high + community_low;
845 		*token = community_token_val;
846 		return p;
847 	}
848 	*token = community_token_unknown;
849 	return NULL;
850 }
851 
852 /* convert string to community structure */
community_str2com(const char * str)853 struct community *community_str2com(const char *str)
854 {
855 	struct community *com = NULL;
856 	struct community *com_sort = NULL;
857 	uint32_t val = 0;
858 	enum community_token token = community_token_unknown;
859 
860 	do {
861 		str = community_gettoken(str, &token, &val);
862 
863 		switch (token) {
864 		case community_token_val:
865 		case community_token_gshut:
866 		case community_token_accept_own:
867 		case community_token_route_filter_translated_v4:
868 		case community_token_route_filter_v4:
869 		case community_token_route_filter_translated_v6:
870 		case community_token_route_filter_v6:
871 		case community_token_llgr_stale:
872 		case community_token_no_llgr:
873 		case community_token_accept_own_nexthop:
874 		case community_token_blackhole:
875 		case community_token_no_export:
876 		case community_token_no_advertise:
877 		case community_token_local_as:
878 		case community_token_no_peer:
879 			if (com == NULL) {
880 				com = community_new();
881 				com->json = NULL;
882 			}
883 			community_add_val(com, val);
884 			break;
885 		case community_token_unknown:
886 			if (com)
887 				community_free(&com);
888 			return NULL;
889 		}
890 	} while (str);
891 
892 	com_sort = community_uniq_sort(com);
893 	community_free(&com);
894 
895 	return com_sort;
896 }
897 
898 /* Return communities hash entry count.  */
community_count(void)899 unsigned long community_count(void)
900 {
901 	return comhash->count;
902 }
903 
904 /* Return communities hash.  */
community_hash(void)905 struct hash *community_hash(void)
906 {
907 	return comhash;
908 }
909 
910 /* Initialize comminity related hash. */
community_init(void)911 void community_init(void)
912 {
913 	comhash =
914 		hash_create((unsigned int (*)(const void *))community_hash_make,
915 			    (bool (*)(const void *, const void *))community_cmp,
916 			    "BGP Community Hash");
917 }
918 
community_finish(void)919 void community_finish(void)
920 {
921 	hash_free(comhash);
922 	comhash = NULL;
923 }
924 
bgp_aggr_community_lookup(struct bgp_aggregate * aggregate,struct community * community)925 static struct community *bgp_aggr_community_lookup(
926 						struct bgp_aggregate *aggregate,
927 						struct community *community)
928 {
929 	return hash_lookup(aggregate->community_hash, community);
930 }
931 
bgp_aggr_communty_hash_alloc(void * p)932 static void *bgp_aggr_communty_hash_alloc(void *p)
933 {
934 	struct community *ref = (struct community *)p;
935 	struct community *community = NULL;
936 
937 	community = community_dup(ref);
938 	return community;
939 }
940 
bgp_aggr_community_prepare(struct hash_bucket * hb,void * arg)941 static void bgp_aggr_community_prepare(struct hash_bucket *hb, void *arg)
942 {
943 	struct community *hb_community = hb->data;
944 	struct community **aggr_community = arg;
945 
946 	if (*aggr_community)
947 		*aggr_community = community_merge(*aggr_community,
948 						  hb_community);
949 	else
950 		*aggr_community = community_dup(hb_community);
951 }
952 
bgp_aggr_community_remove(void * arg)953 void bgp_aggr_community_remove(void *arg)
954 {
955 	struct community *community = arg;
956 
957 	community_free(&community);
958 }
959 
bgp_compute_aggregate_community(struct bgp_aggregate * aggregate,struct community * community)960 void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
961 				     struct community *community)
962 {
963 	bgp_compute_aggregate_community_hash(aggregate, community);
964 	bgp_compute_aggregate_community_val(aggregate);
965 }
966 
967 
bgp_compute_aggregate_community_hash(struct bgp_aggregate * aggregate,struct community * community)968 void bgp_compute_aggregate_community_hash(struct bgp_aggregate *aggregate,
969 					  struct community *community)
970 {
971 	struct community *aggr_community = NULL;
972 
973 	if ((aggregate == NULL) || (community == NULL))
974 		return;
975 
976 	/* Create hash if not already created.
977 	 */
978 	if (aggregate->community_hash == NULL)
979 		aggregate->community_hash = hash_create(
980 			(unsigned int (*)(const void *))community_hash_make,
981 			(bool (*)(const void *, const void *))community_cmp,
982 			"BGP Aggregator community hash");
983 
984 	aggr_community = bgp_aggr_community_lookup(aggregate, community);
985 	if (aggr_community == NULL) {
986 		/* Insert community into hash.
987 		 */
988 		aggr_community = hash_get(aggregate->community_hash, community,
989 					  bgp_aggr_communty_hash_alloc);
990 	}
991 
992 	/* Increment reference counter.
993 	 */
994 	aggr_community->refcnt++;
995 }
996 
bgp_compute_aggregate_community_val(struct bgp_aggregate * aggregate)997 void bgp_compute_aggregate_community_val(struct bgp_aggregate *aggregate)
998 {
999 	struct community *commerge = NULL;
1000 
1001 	if (aggregate == NULL)
1002 		return;
1003 
1004 	/* Re-compute aggregate's community.
1005 	 */
1006 	if (aggregate->community)
1007 		community_free(&aggregate->community);
1008 	if (aggregate->community_hash &&
1009 	    aggregate->community_hash->count) {
1010 		hash_iterate(aggregate->community_hash,
1011 			     bgp_aggr_community_prepare,
1012 			     &aggregate->community);
1013 		commerge = aggregate->community;
1014 		aggregate->community = community_uniq_sort(commerge);
1015 		if (commerge)
1016 			community_free(&commerge);
1017 	}
1018 }
1019 
1020 
1021 
bgp_remove_community_from_aggregate(struct bgp_aggregate * aggregate,struct community * community)1022 void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate,
1023 					 struct community *community)
1024 {
1025 	struct community *aggr_community = NULL;
1026 	struct community *ret_comm = NULL;
1027 
1028 	if ((!aggregate)
1029 	    || (!aggregate->community_hash)
1030 	    || (!community))
1031 		return;
1032 
1033 	/* Look-up the community in the hash.
1034 	 */
1035 	aggr_community = bgp_aggr_community_lookup(aggregate, community);
1036 	if (aggr_community) {
1037 		aggr_community->refcnt--;
1038 
1039 		if (aggr_community->refcnt == 0) {
1040 			ret_comm = hash_release(aggregate->community_hash,
1041 						aggr_community);
1042 			community_free(&ret_comm);
1043 
1044 			bgp_compute_aggregate_community_val(aggregate);
1045 		}
1046 	}
1047 }
1048 
bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate * aggregate,struct community * community)1049 void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1050 		struct community *community)
1051 {
1052 
1053 	struct community *aggr_community = NULL;
1054 	struct community *ret_comm = NULL;
1055 
1056 	if ((!aggregate)
1057 	    || (!aggregate->community_hash)
1058 	    || (!community))
1059 		return;
1060 
1061 	/* Look-up the community in the hash.
1062 	 */
1063 	aggr_community = bgp_aggr_community_lookup(aggregate, community);
1064 	if (aggr_community) {
1065 		aggr_community->refcnt--;
1066 
1067 		if (aggr_community->refcnt == 0) {
1068 			ret_comm = hash_release(aggregate->community_hash,
1069 						aggr_community);
1070 			community_free(&ret_comm);
1071 		}
1072 	}
1073 }
1074