bgpd: fix ecommunity parsing for AS4

The parser for extended communities was incorrectly disallowing an
operator from configuring "Route Origin" extended communities
(e.g. RD/RT/SoO) with a 4-byte value matching BGP_AS4_MAX (UINT32_MAX)
and allowed the user to overflow UINT32_MAX. This updates the parser to
read the value as a uint64_t so that we can do proper checks on the
upper bounds (> BGP_AS4_MAX || errno).

before:
```
TORC11(config-router-af)# neighbor uplink-1 soo 4294967296:65
TORC11(config-router-af)# do sh run | include soo
  neighbor uplink-1 soo 0:65
TORC11(config-router-af)# neighbor uplink-1 soo 4294967295:65
% Malformed SoO extended community
TORC11(config-router-af)#
```

after:
```
TORC11(config-router-af)# neighbor uplink-1 soo 4294967296:65
% Malformed SoO extended community
TORC11(config-router-af)# neighbor uplink-1 soo 4294967295:65
TORC11(config-router-af)# do sh run | include soo
  neighbor uplink-1 soo 4294967295:65
TORC11(config-router-af)#
```

Signed-off-by: Trey Aspelund <taspelund@nvidia.com>
This commit is contained in:
Trey Aspelund 2023-03-31 17:46:21 -04:00
parent bdf62ec61b
commit b571d79d64

View File

@ -503,6 +503,8 @@ static const char *ecommunity_gettoken(const char *str,
uint8_t ecomm_type; uint8_t ecomm_type;
char buf[INET_ADDRSTRLEN + 1]; char buf[INET_ADDRSTRLEN + 1];
struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
uint64_t tmp_as = 0;
/* Skip white space. */ /* Skip white space. */
while (isspace((unsigned char)*p)) { while (isspace((unsigned char)*p)) {
p++; p++;
@ -581,9 +583,18 @@ static const char *ecommunity_gettoken(const char *str,
goto error; goto error;
endptr++; endptr++;
as = strtoul(endptr, &endptr, 10); errno = 0;
if (*endptr != '\0' || as == BGP_AS4_MAX) tmp_as = strtoul(endptr, &endptr, 10);
/* 'unsigned long' is a uint64 on 64-bit
* systems, and uint32 on 32-bit systems. So for
* 64-bit we can just directly check the value
* against BGP_AS4_MAX/UINT32_MAX, and for
* 32-bit we can check for errno (set to ERANGE
* upon overflow).
*/
if (*endptr != '\0' || tmp_as == BGP_AS4_MAX || errno)
goto error; goto error;
as = (as_t)tmp_as;
memcpy(buf, p, (limit - p)); memcpy(buf, p, (limit - p));
buf[limit - p] = '\0'; buf[limit - p] = '\0';
@ -625,9 +636,19 @@ static const char *ecommunity_gettoken(const char *str,
goto error; goto error;
} else { } else {
/* ASN */ /* ASN */
as = strtoul(buf, &endptr, 10); errno = 0;
if (*endptr != '\0' || as == BGP_AS4_MAX) tmp_as = strtoul(buf, &endptr, 10);
/* 'unsigned long' is a uint64 on 64-bit
* systems, and uint32 on 32-bit systems. So for
* 64-bit we can just directly check the value
* against BGP_AS4_MAX/UINT32_MAX, and for
* 32-bit we can check for errno (set to ERANGE
* upon overflow).
*/
if (*endptr != '\0' || tmp_as > BGP_AS4_MAX ||
errno)
goto error; goto error;
as = (as_t)tmp_as;
} }
} else if (*p == '.') { } else if (*p == '.') {
if (separator) if (separator)