frr/bgpd/bgp_lcommunity.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

675 lines
16 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/* BGP Large Communities Attribute
*
* Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
*/
#include <zebra.h>
#include "hash.h"
#include "memory.h"
#include "prefix.h"
#include "command.h"
#include "filter.h"
#include "jhash.h"
#include "stream.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_lcommunity.h"
#include "bgpd/bgp_community_alias.h"
#include "bgpd/bgp_aspath.h"
/* Hash of community attribute. */
static struct hash *lcomhash;
/* Allocate a new lcommunities. */
static struct lcommunity *lcommunity_new(void)
{
return XCALLOC(MTYPE_LCOMMUNITY, sizeof(struct lcommunity));
}
/* Allocate lcommunities. */
void lcommunity_free(struct lcommunity **lcom)
{
if (!(*lcom))
return;
XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str);
if ((*lcom)->json)
json_object_free((*lcom)->json);
XFREE(MTYPE_LCOMMUNITY, *lcom);
}
static void lcommunity_hash_free(struct lcommunity *lcom)
{
lcommunity_free(&lcom);
}
/* Add a new Large Communities value to Large Communities
Attribute structure. When the value is already exists in the
structure, we don't add the value. Newly added value is sorted by
numerical order. When the value is added to the structure return 1
else return 0. */
static bool lcommunity_add_val(struct lcommunity *lcom,
struct lcommunity_val *lval)
{
uint8_t *p;
int ret;
int c;
/* When this is fist value, just add it. */
if (lcom->val == NULL) {
lcom->size++;
lcom->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
memcpy(lcom->val, lval->val, LCOMMUNITY_SIZE);
return true;
}
/* If the value already exists in the structure return 0. */
c = 0;
for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) {
ret = memcmp(p, lval->val, LCOMMUNITY_SIZE);
if (ret == 0)
return false;
if (ret > 0)
break;
}
/* Add the value to the structure with numerical sorting. */
lcom->size++;
lcom->val =
XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length(lcom));
memmove(lcom->val + (c + 1) * LCOMMUNITY_SIZE,
lcom->val + c * LCOMMUNITY_SIZE,
(lcom->size - 1 - c) * LCOMMUNITY_SIZE);
memcpy(lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
return true;
}
/* This function takes pointer to Large Communites structure then
create a new Large Communities structure by uniq and sort each
Large Communities value. */
struct lcommunity *lcommunity_uniq_sort(struct lcommunity *lcom)
{
int i;
struct lcommunity *new;
struct lcommunity_val *lval;
if (!lcom)
return NULL;
new = lcommunity_new();
for (i = 0; i < lcom->size; i++) {
lval = (struct lcommunity_val *)(lcom->val
+ (i * LCOMMUNITY_SIZE));
lcommunity_add_val(new, lval);
}
return new;
}
/* Parse Large Communites Attribute in BGP packet. */
struct lcommunity *lcommunity_parse(uint8_t *pnt, unsigned short length)
{
struct lcommunity tmp;
struct lcommunity *new;
/* Length check. */
if (length % LCOMMUNITY_SIZE)
return NULL;
/* Prepare tmporary structure for making a new Large Communities
Attribute. */
tmp.size = length / LCOMMUNITY_SIZE;
tmp.val = pnt;
/* Create a new Large Communities Attribute by uniq and sort each
Large Communities value */
new = lcommunity_uniq_sort(&tmp);
return lcommunity_intern(new);
}
/* Duplicate the Large Communities Attribute structure. */
struct lcommunity *lcommunity_dup(struct lcommunity *lcom)
{
struct lcommunity *new;
new = lcommunity_new();
new->size = lcom->size;
if (new->size) {
new->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
memcpy(new->val, lcom->val, lcom_length(lcom));
} else
new->val = NULL;
return new;
}
/* Merge two Large Communities Attribute structure. */
struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
struct lcommunity *lcom2)
{
lcom1->val = XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom1->val,
lcom_length(lcom1) + lcom_length(lcom2));
memcpy(lcom1->val + lcom_length(lcom1), lcom2->val, lcom_length(lcom2));
lcom1->size += lcom2->size;
return lcom1;
}
static void set_lcommunity_string(struct lcommunity *lcom, bool make_json,
bool translate_alias)
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
{
int i;
int len;
char *str_buf;
const uint8_t *pnt;
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
uint32_t global, local1, local2;
json_object *json_lcommunity_list = NULL;
json_object *json_string = NULL;
/* 3 32-bit integers, 2 colons, and a space */
#define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1)
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
if (!lcom)
return;
if (make_json) {
lcom->json = json_object_new_object();
json_lcommunity_list = json_object_new_array();
}
if (lcom->size == 0) {
str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, 1);
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
if (make_json) {
json_object_string_add(lcom->json, "string", "");
json_object_object_add(lcom->json, "list",
json_lcommunity_list);
}
lcom->str = str_buf;
return;
}
/* 1 space + lcom->size lcom strings + null terminator */
bgpd: Fix lcom->str string length to correctly cover aliases If you have a very large number of large communities whose string length happened to be greater than BUFSIZ FRR's bgpd would crash. This is because bgpd would write beyond the end of the string. Originally the code auto-calculated the string size appropriately but commit ed0e57e3f079352714c3a3a8a5b0dddf4aadfe1d modified the string length to be a hard coded BUFSIZ. When a route-map like this is added: route-map LARGE-OUT permit 10 set large-community 4635:0:0 4635:1:906 4635:1:2906 4635:1:4515 4635:1:4594 4635:1:4641 4635:1:4760 4635:1:7979 4635:1:9253 4635:1:9293 4635:1:9304 4635:1:9908 4635:1:13335 4635:1:16265 4635:1:17924 4635:1:18013 4635:1:20940 4635:1:22822 4635:1:24429 4635:1:24482 4635:1:32590 4635:1:32934 4635:1:36692 4635:1:38008 4635:1:38819 4635:1:41378 4635:1:45753 4635:1:46489 4635:1:49544 4635:1:51847 4635:1:54574 4635:1:54994 4635:1:55720 4635:1:56059 4635:1:57724 4635:1:65021 4635:1:134823 4635:1:136907 4635:1:146961 24115:0:24115 24115:1:906 24115:1:2906 24115:1:4515 24115:1:4594 24115:1:4641 24115:1:4760 24115:1:7979 24115:1:9253 24115:1:9293 24115:1:9304 24115:1:9908 24115:1:13335 24115:1:16265 24115:1:17924 24115:1:18013 24115:1:20940 24115:1:22822 24115:1:24429 24115:1:24482 24115:1:32590 24115:1:32934 24115:1:36692 24115:1:38008 24115:1:38819 24115:1:41378 24115:1:45753 24115:1:46489 24115:1:49544 24115:1:51847 24115:1:54574 24115:1:54994 24115:1:55720 24115:1:56059 24115:1:57724 24115:1:65021 24115:1:134823 24115:1:136907 24115:1:100000 24115:1:100001 24115:1:100002 exit BGP would have issues and crash. Modify the code to correctly determine the string length of the communities and to also double check if the string has an alias and ensure that the string is still sufficiently large enough. If not auto size it again. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-04-20 22:27:20 +02:00
size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2;
str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz);
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
bgpd: Fix lcom->str string length to correctly cover aliases If you have a very large number of large communities whose string length happened to be greater than BUFSIZ FRR's bgpd would crash. This is because bgpd would write beyond the end of the string. Originally the code auto-calculated the string size appropriately but commit ed0e57e3f079352714c3a3a8a5b0dddf4aadfe1d modified the string length to be a hard coded BUFSIZ. When a route-map like this is added: route-map LARGE-OUT permit 10 set large-community 4635:0:0 4635:1:906 4635:1:2906 4635:1:4515 4635:1:4594 4635:1:4641 4635:1:4760 4635:1:7979 4635:1:9253 4635:1:9293 4635:1:9304 4635:1:9908 4635:1:13335 4635:1:16265 4635:1:17924 4635:1:18013 4635:1:20940 4635:1:22822 4635:1:24429 4635:1:24482 4635:1:32590 4635:1:32934 4635:1:36692 4635:1:38008 4635:1:38819 4635:1:41378 4635:1:45753 4635:1:46489 4635:1:49544 4635:1:51847 4635:1:54574 4635:1:54994 4635:1:55720 4635:1:56059 4635:1:57724 4635:1:65021 4635:1:134823 4635:1:136907 4635:1:146961 24115:0:24115 24115:1:906 24115:1:2906 24115:1:4515 24115:1:4594 24115:1:4641 24115:1:4760 24115:1:7979 24115:1:9253 24115:1:9293 24115:1:9304 24115:1:9908 24115:1:13335 24115:1:16265 24115:1:17924 24115:1:18013 24115:1:20940 24115:1:22822 24115:1:24429 24115:1:24482 24115:1:32590 24115:1:32934 24115:1:36692 24115:1:38008 24115:1:38819 24115:1:41378 24115:1:45753 24115:1:46489 24115:1:49544 24115:1:51847 24115:1:54574 24115:1:54994 24115:1:55720 24115:1:56059 24115:1:57724 24115:1:65021 24115:1:134823 24115:1:136907 24115:1:100000 24115:1:100001 24115:1:100002 exit BGP would have issues and crash. Modify the code to correctly determine the string length of the communities and to also double check if the string has an alias and ensure that the string is still sufficiently large enough. If not auto size it again. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-04-20 22:27:20 +02:00
len = 0;
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
for (i = 0; i < lcom->size; i++) {
if (i > 0)
bgpd: Fix lcom->str string length to correctly cover aliases If you have a very large number of large communities whose string length happened to be greater than BUFSIZ FRR's bgpd would crash. This is because bgpd would write beyond the end of the string. Originally the code auto-calculated the string size appropriately but commit ed0e57e3f079352714c3a3a8a5b0dddf4aadfe1d modified the string length to be a hard coded BUFSIZ. When a route-map like this is added: route-map LARGE-OUT permit 10 set large-community 4635:0:0 4635:1:906 4635:1:2906 4635:1:4515 4635:1:4594 4635:1:4641 4635:1:4760 4635:1:7979 4635:1:9253 4635:1:9293 4635:1:9304 4635:1:9908 4635:1:13335 4635:1:16265 4635:1:17924 4635:1:18013 4635:1:20940 4635:1:22822 4635:1:24429 4635:1:24482 4635:1:32590 4635:1:32934 4635:1:36692 4635:1:38008 4635:1:38819 4635:1:41378 4635:1:45753 4635:1:46489 4635:1:49544 4635:1:51847 4635:1:54574 4635:1:54994 4635:1:55720 4635:1:56059 4635:1:57724 4635:1:65021 4635:1:134823 4635:1:136907 4635:1:146961 24115:0:24115 24115:1:906 24115:1:2906 24115:1:4515 24115:1:4594 24115:1:4641 24115:1:4760 24115:1:7979 24115:1:9253 24115:1:9293 24115:1:9304 24115:1:9908 24115:1:13335 24115:1:16265 24115:1:17924 24115:1:18013 24115:1:20940 24115:1:22822 24115:1:24429 24115:1:24482 24115:1:32590 24115:1:32934 24115:1:36692 24115:1:38008 24115:1:38819 24115:1:41378 24115:1:45753 24115:1:46489 24115:1:49544 24115:1:51847 24115:1:54574 24115:1:54994 24115:1:55720 24115:1:56059 24115:1:57724 24115:1:65021 24115:1:134823 24115:1:136907 24115:1:100000 24115:1:100001 24115:1:100002 exit BGP would have issues and crash. Modify the code to correctly determine the string length of the communities and to also double check if the string has an alias and ensure that the string is still sufficiently large enough. If not auto size it again. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-04-20 22:27:20 +02:00
len = strlcat(str_buf, " ", str_buf_sz);
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
pnt = lcom->val + (i * LCOMMUNITY_SIZE);
pnt = ptr_get_be32(pnt, &global);
pnt = ptr_get_be32(pnt, &local1);
pnt = ptr_get_be32(pnt, &local2);
(void)pnt;
char lcsb[LCOMMUNITY_STRLEN + 1];
snprintf(lcsb, sizeof(lcsb), "%u:%u:%u", global, local1,
local2);
bgpd: Fix lcom->str string length to correctly cover aliases If you have a very large number of large communities whose string length happened to be greater than BUFSIZ FRR's bgpd would crash. This is because bgpd would write beyond the end of the string. Originally the code auto-calculated the string size appropriately but commit ed0e57e3f079352714c3a3a8a5b0dddf4aadfe1d modified the string length to be a hard coded BUFSIZ. When a route-map like this is added: route-map LARGE-OUT permit 10 set large-community 4635:0:0 4635:1:906 4635:1:2906 4635:1:4515 4635:1:4594 4635:1:4641 4635:1:4760 4635:1:7979 4635:1:9253 4635:1:9293 4635:1:9304 4635:1:9908 4635:1:13335 4635:1:16265 4635:1:17924 4635:1:18013 4635:1:20940 4635:1:22822 4635:1:24429 4635:1:24482 4635:1:32590 4635:1:32934 4635:1:36692 4635:1:38008 4635:1:38819 4635:1:41378 4635:1:45753 4635:1:46489 4635:1:49544 4635:1:51847 4635:1:54574 4635:1:54994 4635:1:55720 4635:1:56059 4635:1:57724 4635:1:65021 4635:1:134823 4635:1:136907 4635:1:146961 24115:0:24115 24115:1:906 24115:1:2906 24115:1:4515 24115:1:4594 24115:1:4641 24115:1:4760 24115:1:7979 24115:1:9253 24115:1:9293 24115:1:9304 24115:1:9908 24115:1:13335 24115:1:16265 24115:1:17924 24115:1:18013 24115:1:20940 24115:1:22822 24115:1:24429 24115:1:24482 24115:1:32590 24115:1:32934 24115:1:36692 24115:1:38008 24115:1:38819 24115:1:41378 24115:1:45753 24115:1:46489 24115:1:49544 24115:1:51847 24115:1:54574 24115:1:54994 24115:1:55720 24115:1:56059 24115:1:57724 24115:1:65021 24115:1:134823 24115:1:136907 24115:1:100000 24115:1:100001 24115:1:100002 exit BGP would have issues and crash. Modify the code to correctly determine the string length of the communities and to also double check if the string has an alias and ensure that the string is still sufficiently large enough. If not auto size it again. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-04-20 22:27:20 +02:00
/*
* Aliases can cause havoc, if the alias length is greater
* than the LCOMMUNITY_STRLEN for a particular item
* then we need to realloc the memory associated
* with the string so that it can fit
*/
const char *com2alias =
translate_alias ? bgp_community2alias(lcsb) : lcsb;
bgpd: Fix lcom->str string length to correctly cover aliases If you have a very large number of large communities whose string length happened to be greater than BUFSIZ FRR's bgpd would crash. This is because bgpd would write beyond the end of the string. Originally the code auto-calculated the string size appropriately but commit ed0e57e3f079352714c3a3a8a5b0dddf4aadfe1d modified the string length to be a hard coded BUFSIZ. When a route-map like this is added: route-map LARGE-OUT permit 10 set large-community 4635:0:0 4635:1:906 4635:1:2906 4635:1:4515 4635:1:4594 4635:1:4641 4635:1:4760 4635:1:7979 4635:1:9253 4635:1:9293 4635:1:9304 4635:1:9908 4635:1:13335 4635:1:16265 4635:1:17924 4635:1:18013 4635:1:20940 4635:1:22822 4635:1:24429 4635:1:24482 4635:1:32590 4635:1:32934 4635:1:36692 4635:1:38008 4635:1:38819 4635:1:41378 4635:1:45753 4635:1:46489 4635:1:49544 4635:1:51847 4635:1:54574 4635:1:54994 4635:1:55720 4635:1:56059 4635:1:57724 4635:1:65021 4635:1:134823 4635:1:136907 4635:1:146961 24115:0:24115 24115:1:906 24115:1:2906 24115:1:4515 24115:1:4594 24115:1:4641 24115:1:4760 24115:1:7979 24115:1:9253 24115:1:9293 24115:1:9304 24115:1:9908 24115:1:13335 24115:1:16265 24115:1:17924 24115:1:18013 24115:1:20940 24115:1:22822 24115:1:24429 24115:1:24482 24115:1:32590 24115:1:32934 24115:1:36692 24115:1:38008 24115:1:38819 24115:1:41378 24115:1:45753 24115:1:46489 24115:1:49544 24115:1:51847 24115:1:54574 24115:1:54994 24115:1:55720 24115:1:56059 24115:1:57724 24115:1:65021 24115:1:134823 24115:1:136907 24115:1:100000 24115:1:100001 24115:1:100002 exit BGP would have issues and crash. Modify the code to correctly determine the string length of the communities and to also double check if the string has an alias and ensure that the string is still sufficiently large enough. If not auto size it again. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-04-20 22:27:20 +02:00
size_t individual_len = strlen(com2alias);
if (individual_len + len > str_buf_sz) {
str_buf_sz = individual_len + len + 1;
str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf,
str_buf_sz);
}
len = strlcat(str_buf, com2alias, str_buf_sz);
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
if (make_json) {
json_string = json_object_new_string(com2alias);
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
json_object_array_add(json_lcommunity_list,
json_string);
}
}
if (make_json) {
json_object_string_add(lcom->json, "string", str_buf);
json_object_object_add(lcom->json, "list",
json_lcommunity_list);
}
lcom->str = str_buf;
}
/* Intern Large Communities Attribute. */
struct lcommunity *lcommunity_intern(struct lcommunity *lcom)
{
struct lcommunity *find;
assert(lcom->refcnt == 0);
find = (struct lcommunity *)hash_get(lcomhash, lcom, hash_alloc_intern);
if (find != lcom)
lcommunity_free(&lcom);
find->refcnt++;
if (!find->str)
set_lcommunity_string(find, false, true);
return find;
}
/* Unintern Large Communities Attribute. */
void lcommunity_unintern(struct lcommunity **lcom)
{
struct lcommunity *ret;
if (!*lcom)
return;
if ((*lcom)->refcnt)
(*lcom)->refcnt--;
/* Pull off from hash. */
if ((*lcom)->refcnt == 0) {
/* Large community must be in the hash. */
ret = (struct lcommunity *)hash_release(lcomhash, *lcom);
assert(ret != NULL);
lcommunity_free(lcom);
}
}
/* Return string representation of lcommunities attribute. */
char *lcommunity_str(struct lcommunity *lcom, bool make_json,
bool translate_alias)
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
{
if (!lcom)
return NULL;
if (make_json && !lcom->json && lcom->str)
XFREE(MTYPE_LCOMMUNITY_STR, lcom->str);
if (!lcom->str)
set_lcommunity_string(lcom, make_json, translate_alias);
bgpd: Improve JSON support for large communities The current implementation of building JSON output is greatly different for large communities compared to standard communities. This is mainly noticeable by the missing 'list' attribute, which usually offers an array of all communities present on a BGP route. This commit adds the missing functionality of properly returning a 'list' attribute in JSON output and also tries a similar approach like the standard communities are using to implement this feature. Additionally, the 'format' specifier has been completely removed from large communities string/JSON rendering, as the official RFC8092 specifies that there is only one canonical representation: > The canonical representation of BGP Large Communities is three > separate unsigned integers in decimal notation in the following > order: Global Administrator, Local Data 1, Local Data 2. Numbers > MUST NOT contain leading zeros; a zero value MUST be represented with > a single zero. Each number is separated from the next by a single > colon. For example: 64496:4294967295:2, 64496:0:0. As the 'format' specifier has not been used/checked and only one canonical representation exists per today, there was no reason to keep the 'format' parameter in the function signature. Last but not least, the struct attribute 'community_entry.config' is no longer being used for large communities and instead 'lcommunity_str' is being called to maintain a similar approach to standard communities. As a side effect, this also fixed a memory leak inside 'community_entry_free' which did not free the allocated memory for the 'config' attribute when dealing with a large community. Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
2018-05-13 02:29:40 +02:00
return lcom->str;
}
/* Utility function to make hash key. */
unsigned int lcommunity_hash_make(const void *arg)
{
const struct lcommunity *lcom = arg;
int size = lcom_length(lcom);
return jhash(lcom->val, size, 0xab125423);
}
/* Compare two Large Communities Attribute structure. */
bool lcommunity_cmp(const void *arg1, const void *arg2)
{
const struct lcommunity *lcom1 = arg1;
const struct lcommunity *lcom2 = arg2;
if (lcom1 == NULL && lcom2 == NULL)
return true;
if (lcom1 == NULL || lcom2 == NULL)
return false;
return (lcom1->size == lcom2->size
&& memcmp(lcom1->val, lcom2->val, lcom_length(lcom1)) == 0);
}
/* Return communities hash. */
struct hash *lcommunity_hash(void)
{
return lcomhash;
}
/* Initialize Large Comminities related hash. */
void lcommunity_init(void)
{
lcomhash = hash_create(lcommunity_hash_make, lcommunity_cmp,
"BGP lcommunity hash");
}
void lcommunity_finish(void)
{
hash_clean_and_free(&lcomhash, (void (*)(void *))lcommunity_hash_free);
}
/* Get next Large Communities token from the string.
* Assumes str is space-delimeted and describes 0 or more
* valid large communities
*/
static const char *lcommunity_gettoken(const char *str,
struct lcommunity_val *lval)
{
const char *p = str;
/* Skip white space. */
while (isspace((unsigned char)*p)) {
p++;
str++;
}
/* Check the end of the line. */
if (*p == '\0')
return NULL;
/* Community value. */
int separator = 0;
int digit = 0;
uint32_t globaladmin = 0;
uint32_t localdata1 = 0;
uint32_t localdata2 = 0;
while (*p && *p != ' ') {
/* large community valid chars */
assert(isdigit((unsigned char)*p) || *p == ':');
if (*p == ':') {
separator++;
digit = 0;
if (separator == 1) {
globaladmin = localdata2;
} else {
localdata1 = localdata2;
}
localdata2 = 0;
} else {
digit = 1;
/* left shift the accumulated value and add current
* digit
*/
localdata2 *= 10;
localdata2 += (*p - '0');
}
p++;
}
/* Assert str was a valid large community */
assert(separator == 2 && digit == 1);
/*
* Copy the large comm.
*/
lval->val[0] = (globaladmin >> 24) & 0xff;
lval->val[1] = (globaladmin >> 16) & 0xff;
lval->val[2] = (globaladmin >> 8) & 0xff;
lval->val[3] = globaladmin & 0xff;
lval->val[4] = (localdata1 >> 24) & 0xff;
lval->val[5] = (localdata1 >> 16) & 0xff;
lval->val[6] = (localdata1 >> 8) & 0xff;
lval->val[7] = localdata1 & 0xff;
lval->val[8] = (localdata2 >> 24) & 0xff;
lval->val[9] = (localdata2 >> 16) & 0xff;
lval->val[10] = (localdata2 >> 8) & 0xff;
lval->val[11] = localdata2 & 0xff;
return p;
}
/*
Convert string to large community attribute.
When type is already known, please specify both str and type.
When string includes keyword for each large community value.
Please specify keyword_included as non-zero value.
*/
struct lcommunity *lcommunity_str2com(const char *str)
{
struct lcommunity *lcom = NULL;
struct lcommunity_val lval;
if (!lcommunity_list_valid(str, LARGE_COMMUNITY_LIST_STANDARD))
return NULL;
do {
str = lcommunity_gettoken(str, &lval);
if (lcom == NULL)
lcom = lcommunity_new();
lcommunity_add_val(lcom, &lval);
} while (str);
return lcom;
}
bool lcommunity_include(struct lcommunity *lcom, uint8_t *ptr)
{
int i;
uint8_t *lcom_ptr;
for (i = 0; i < lcom->size; i++) {
lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
if (memcmp(ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
return true;
}
return false;
}
bool lcommunity_match(const struct lcommunity *lcom1,
const struct lcommunity *lcom2)
{
int i = 0;
int j = 0;
if (lcom1 == NULL && lcom2 == NULL)
return true;
if (lcom1 == NULL || lcom2 == NULL)
return false;
if (lcom1->size < lcom2->size)
return false;
/* Every community on com2 needs to be on com1 for this to match */
while (i < lcom1->size && j < lcom2->size) {
if (memcmp(lcom1->val + (i * LCOMMUNITY_SIZE),
lcom2->val + (j * LCOMMUNITY_SIZE), LCOMMUNITY_SIZE)
== 0)
j++;
i++;
}
if (j == lcom2->size)
return true;
else
return false;
}
/* Delete one lcommunity. */
void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr)
{
int i = 0;
int c = 0;
if (!lcom->val)
return;
while (i < lcom->size) {
if (memcmp(lcom->val + i * LCOMMUNITY_SIZE, ptr,
LCOMMUNITY_SIZE)
== 0) {
c = lcom->size - i - 1;
if (c > 0)
memmove(lcom->val + i * LCOMMUNITY_SIZE,
lcom->val + (i + 1) * LCOMMUNITY_SIZE,
c * LCOMMUNITY_SIZE);
lcom->size--;
if (lcom->size > 0)
lcom->val =
XREALLOC(MTYPE_LCOMMUNITY_VAL,
lcom->val, lcom_length(lcom));
else {
XFREE(MTYPE_LCOMMUNITY_VAL, lcom->val);
}
return;
}
i++;
}
}
static struct lcommunity *bgp_aggr_lcommunity_lookup(
struct bgp_aggregate *aggregate,
struct lcommunity *lcommunity)
{
return hash_lookup(aggregate->lcommunity_hash, lcommunity);
}
static void *bgp_aggr_lcommunty_hash_alloc(void *p)
{
struct lcommunity *ref = (struct lcommunity *)p;
struct lcommunity *lcommunity = NULL;
lcommunity = lcommunity_dup(ref);
return lcommunity;
}
static void bgp_aggr_lcommunity_prepare(struct hash_bucket *hb, void *arg)
{
struct lcommunity *hb_lcommunity = hb->data;
struct lcommunity **aggr_lcommunity = arg;
if (*aggr_lcommunity)
*aggr_lcommunity = lcommunity_merge(*aggr_lcommunity,
hb_lcommunity);
else
*aggr_lcommunity = lcommunity_dup(hb_lcommunity);
}
void bgp_aggr_lcommunity_remove(void *arg)
{
struct lcommunity *lcommunity = arg;
lcommunity_free(&lcommunity);
}
void bgp_compute_aggregate_lcommunity(struct bgp_aggregate *aggregate,
struct lcommunity *lcommunity)
{
bgp_compute_aggregate_lcommunity_hash(aggregate, lcommunity);
bgp_compute_aggregate_lcommunity_val(aggregate);
}
void bgp_compute_aggregate_lcommunity_hash(struct bgp_aggregate *aggregate,
struct lcommunity *lcommunity)
{
struct lcommunity *aggr_lcommunity = NULL;
if ((aggregate == NULL) || (lcommunity == NULL))
return;
/* Create hash if not already created.
*/
if (aggregate->lcommunity_hash == NULL)
aggregate->lcommunity_hash = hash_create(
lcommunity_hash_make, lcommunity_cmp,
"BGP Aggregator lcommunity hash");
aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
if (aggr_lcommunity == NULL) {
/* Insert lcommunity into hash.
*/
aggr_lcommunity = hash_get(aggregate->lcommunity_hash,
lcommunity,
bgp_aggr_lcommunty_hash_alloc);
}
/* Increment reference counter.
*/
aggr_lcommunity->refcnt++;
}
void bgp_compute_aggregate_lcommunity_val(struct bgp_aggregate *aggregate)
{
struct lcommunity *lcommerge = NULL;
if (aggregate == NULL)
return;
/* Re-compute aggregate's lcommunity.
*/
if (aggregate->lcommunity)
lcommunity_free(&aggregate->lcommunity);
if (aggregate->lcommunity_hash &&
aggregate->lcommunity_hash->count) {
hash_iterate(aggregate->lcommunity_hash,
bgp_aggr_lcommunity_prepare,
&aggregate->lcommunity);
lcommerge = aggregate->lcommunity;
aggregate->lcommunity = lcommunity_uniq_sort(lcommerge);
if (lcommerge)
lcommunity_free(&lcommerge);
}
}
void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate *aggregate,
struct lcommunity *lcommunity)
{
struct lcommunity *aggr_lcommunity = NULL;
struct lcommunity *ret_lcomm = NULL;
if ((!aggregate)
|| (!aggregate->lcommunity_hash)
|| (!lcommunity))
return;
/* Look-up the lcommunity in the hash.
*/
aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
if (aggr_lcommunity) {
aggr_lcommunity->refcnt--;
if (aggr_lcommunity->refcnt == 0) {
ret_lcomm = hash_release(aggregate->lcommunity_hash,
aggr_lcommunity);
lcommunity_free(&ret_lcomm);
bgp_compute_aggregate_lcommunity_val(aggregate);
}
}
}
void bgp_remove_lcomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
struct lcommunity *lcommunity)
{
struct lcommunity *aggr_lcommunity = NULL;
struct lcommunity *ret_lcomm = NULL;
if ((!aggregate)
|| (!aggregate->lcommunity_hash)
|| (!lcommunity))
return;
/* Look-up the lcommunity in the hash.
*/
aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
if (aggr_lcommunity) {
aggr_lcommunity->refcnt--;
if (aggr_lcommunity->refcnt == 0) {
ret_lcomm = hash_release(aggregate->lcommunity_hash,
aggr_lcommunity);
lcommunity_free(&ret_lcomm);
}
}
}