lib, bgp: add initial support for asdot format

AS number can be defined as an unsigned long number, or
two uint16 values separated by a period (.). The possible
valus are:
- usual 32 bit values : [1;2^32 -1]
- <1.65535>.<0.65535> for dot notation
- <0.65535>.<0.65535> for dot+ notation.

The 0.0 value is forbidden when configuring BGP instances
or peer configurations.

A new ASN type is added for parsing in the vty.
The following commands use that new identifier:
- router bgp ..
- bgp confederation ..
- neighbor <> remote-as <>
- neighbor <> local-as <>
- clear ip bgp <>
- route-map / set as-path <>

An asn library is available in lib/ and provides some
services:
- convert an as string into an as number.
- parse an as path list string and extract a number.
- convert an as number into a string.

Also, the bgp tests forge an as_zero_path, and to do that,
an API to relax the possibility to have a 0 as value is
specifically called from the tests.

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
Philippe Guibert 2022-11-02 18:17:21 +01:00
parent 9eb1199710
commit 8079a4138d
20 changed files with 397 additions and 105 deletions

View file

@ -1940,6 +1940,8 @@ static const char *aspath_gettoken(const char *buf, enum as_token *token,
unsigned long *asno)
{
const char *p = buf;
as_t asval;
bool found = false;
/* Skip separators (space for sequences, ',' for sets). */
while (isspace((unsigned char)*p) || *p == ',')
@ -1976,26 +1978,13 @@ static const char *aspath_gettoken(const char *buf, enum as_token *token,
return p;
}
/* Check actual AS value. */
if (isdigit((unsigned char)*p)) {
as_t asval;
*token = as_token_asval;
asval = (*p - '0');
p++;
while (isdigit((unsigned char)*p)) {
asval *= 10;
asval += (*p - '0');
p++;
}
asval = 0;
p = asn_str2asn_parse(p, &asval, &found);
if (found) {
*asno = asval;
return p;
}
/* There is no match then return unknown token. */
*token = as_token_unknown;
p++;
*token = as_token_asval;
} else
*token = as_token_unknown;
return p;
}

View file

@ -5608,15 +5608,16 @@ DEFUN_YANG (no_set_label_index,
DEFUN_YANG (set_aspath_prepend_asn,
set_aspath_prepend_asn_cmd,
"set as-path prepend (1-4294967295)...",
"set as-path prepend ASNUM...",
SET_STR
"Transform BGP AS_PATH attribute\n"
"Prepend to the as-path\n"
"AS number\n")
AS_STR)
{
int idx_asn = 3;
int ret;
char *str;
struct aspath *aspath;
str = argv_concat(argv, argc, idx_asn);
@ -5624,6 +5625,12 @@ DEFUN_YANG (set_aspath_prepend_asn,
"./set-action[action='frr-bgp-route-map:as-path-prepend']";
char xpath_value[XPATH_MAXLEN];
aspath = route_aspath_compile(str);
if (!aspath) {
vty_out(vty, "%% Invalid AS path value %s\n", str);
return CMD_WARNING_CONFIG_FAILED;
}
route_aspath_free(aspath);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
snprintf(xpath_value, sizeof(xpath_value),
"%s/rmap-set-action/frr-bgp-route-map:prepend-as-path", xpath);
@ -5658,16 +5665,22 @@ DEFUN_YANG (set_aspath_prepend_lastas,
DEFPY_YANG (set_aspath_replace_asn,
set_aspath_replace_asn_cmd,
"set as-path replace <any|(1-4294967295)>$replace",
"set as-path replace <any|ASNUM>$replace",
SET_STR
"Transform BGP AS_PATH attribute\n"
"Replace AS number to local AS number\n"
"Replace any AS number to local AS number\n"
"Replace a specific AS number to local AS number\n")
"Replace a specific AS number in plain or dotted format to local AS number\n")
{
const char *xpath =
"./set-action[action='frr-bgp-route-map:as-path-replace']";
char xpath_value[XPATH_MAXLEN];
as_t as_value;
if (!strmatch(replace, "any") && !asn_str2asn(replace, &as_value)) {
vty_out(vty, "%% Invalid AS value %s\n", replace);
return CMD_WARNING_CONFIG_FAILED;
}
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
snprintf(xpath_value, sizeof(xpath_value),
@ -5678,13 +5691,13 @@ DEFPY_YANG (set_aspath_replace_asn,
DEFPY_YANG (no_set_aspath_replace_asn,
no_set_aspath_replace_asn_cmd,
"no set as-path replace [<any|(1-4294967295)>]",
"no set as-path replace [<any|ASNUM>]",
NO_STR
SET_STR
"Transform BGP AS_PATH attribute\n"
"Replace AS number to local AS number\n"
"Replace any AS number to local AS number\n"
"Replace a specific AS number to local AS number\n")
"Replace a specific AS number in plain or dotted format to local AS number\n")
{
const char *xpath =
"./set-action[action='frr-bgp-route-map:as-path-replace']";
@ -5695,12 +5708,12 @@ DEFPY_YANG (no_set_aspath_replace_asn,
DEFUN_YANG (no_set_aspath_prepend,
no_set_aspath_prepend_cmd,
"no set as-path prepend [(1-4294967295)]",
"no set as-path prepend [ASNUM]",
NO_STR
SET_STR
"Transform BGP AS_PATH attribute\n"
"Prepend to the as-path\n"
"AS number\n")
AS_STR)
{
const char *xpath =
"./set-action[action='frr-bgp-route-map:as-path-prepend']";
@ -5728,15 +5741,16 @@ DEFUN_YANG (no_set_aspath_prepend_lastas,
DEFUN_YANG (set_aspath_exclude,
set_aspath_exclude_cmd,
"set as-path exclude (1-4294967295)...",
"set as-path exclude ASNUM...",
SET_STR
"Transform BGP AS-path attribute\n"
"Exclude from the as-path\n"
"AS number\n")
AS_STR)
{
int idx_asn = 3;
int ret;
char *str;
struct aspath *aspath;
str = argv_concat(argv, argc, idx_asn);
@ -5744,6 +5758,12 @@ DEFUN_YANG (set_aspath_exclude,
"./set-action[action='frr-bgp-route-map:as-path-exclude']";
char xpath_value[XPATH_MAXLEN];
aspath = route_aspath_compile(str);
if (!aspath) {
vty_out(vty, "%% Invalid AS path value %s\n", str);
return CMD_WARNING_CONFIG_FAILED;
}
route_aspath_free(aspath);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
snprintf(xpath_value, sizeof(xpath_value),
"%s/rmap-set-action/frr-bgp-route-map:exclude-as-path", xpath);
@ -5755,7 +5775,7 @@ DEFUN_YANG (set_aspath_exclude,
DEFUN_YANG (no_set_aspath_exclude,
no_set_aspath_exclude_cmd,
"no set as-path exclude (1-4294967295)...",
"no set as-path exclude ASNUM...",
NO_STR
SET_STR
"Transform BGP AS_PATH attribute\n"
@ -6441,11 +6461,11 @@ DEFPY_YANG (no_set_aigp_metric,
DEFUN_YANG (set_aggregator_as,
set_aggregator_as_cmd,
"set aggregator as (1-4294967295) A.B.C.D",
"set aggregator as ASNUM A.B.C.D",
SET_STR
"BGP aggregator attribute\n"
"AS number of aggregator\n"
"AS number\n"
AS_STR
"IP address of aggregator\n")
{
int idx_number = 3;
@ -6454,6 +6474,12 @@ DEFUN_YANG (set_aggregator_as,
char xpath_addr[XPATH_MAXLEN];
const char *xpath =
"./set-action[action='frr-bgp-route-map:aggregator']";
as_t as_value;
if (!asn_str2asn(argv[idx_number]->arg, &as_value)) {
vty_out(vty, "%% Invalid AS value %s\n", argv[idx_number]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
@ -6476,12 +6502,12 @@ DEFUN_YANG (set_aggregator_as,
DEFUN_YANG (no_set_aggregator_as,
no_set_aggregator_as_cmd,
"no set aggregator as [(1-4294967295) A.B.C.D]",
"no set aggregator as [ASNUM A.B.C.D]",
NO_STR
SET_STR
"BGP aggregator attribute\n"
"AS number of aggregator\n"
"AS number\n"
AS_STR
"IP address of aggregator\n")
{
const char *xpath =

View file

@ -39,6 +39,7 @@
#include "queue.h"
#include "filter.h"
#include "frrstr.h"
#include "asn.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_attr_evpn.h"
@ -1237,7 +1238,12 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
/* Clear all neighbors belonging to a specific AS. */
if (sort == clear_as) {
as_t as = strtoul(arg, NULL, 10);
as_t as;
if (!asn_str2asn(arg, &as)) {
vty_out(vty, "%% BGP: No such AS %s\n", arg);
return CMD_WARNING;
}
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
if (peer->as != as)
@ -1475,7 +1481,7 @@ DEFUN (no_auto_summary,
/* "router bgp" commands. */
DEFUN_NOSH (router_bgp,
router_bgp_cmd,
"router bgp [(1-4294967295)$instasn [<view|vrf> VIEWVRFNAME]]",
"router bgp [ASNUM$instasn [<view|vrf> VIEWVRFNAME]]",
ROUTER_STR
BGP_STR
AS_STR
@ -1509,7 +1515,11 @@ DEFUN_NOSH (router_bgp,
// "router bgp X"
else {
as = strtoul(argv[idx_asn]->arg, NULL, 10);
if (!asn_str2asn(argv[idx_asn]->arg, &as)) {
vty_out(vty, "%% BGP: No such AS %s\n",
argv[idx_asn]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
if (as == BGP_PRIVATE_AS_MAX || as == BGP_AS4_MAX)
vty_out(vty, "Reserved AS used (%u|%u); AS is %u\n",
@ -1571,7 +1581,7 @@ DEFUN_NOSH (router_bgp,
/* "no router bgp" commands. */
DEFUN (no_router_bgp,
no_router_bgp_cmd,
"no router bgp [(1-4294967295)$instasn [<view|vrf> VIEWVRFNAME]]",
"no router bgp [ASNUM$instasn [<view|vrf> VIEWVRFNAME]]",
NO_STR
ROUTER_STR
BGP_STR
@ -1605,8 +1615,11 @@ DEFUN (no_router_bgp,
return CMD_WARNING_CONFIG_FAILED;
}
} else {
as = strtoul(argv[idx_asn]->arg, NULL, 10);
if (!asn_str2asn(argv[idx_asn]->arg, &as)) {
vty_out(vty, "%% BGP: No such AS %s\n",
argv[idx_asn]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
if (argc > 4) {
name = argv[idx_vrf]->arg;
if (strmatch(argv[idx_vrf - 1]->text, "vrf")
@ -1934,17 +1947,20 @@ DEFPY (no_bgp_send_extra_data,
DEFUN (bgp_confederation_identifier,
bgp_confederation_identifier_cmd,
"bgp confederation identifier (1-4294967295)",
"bgp confederation identifier ASNUM",
BGP_STR
"AS confederation parameters\n"
"AS number\n"
AS_STR
"Set routing domain confederation AS\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
int idx_number = 3;
as_t as;
as = strtoul(argv[idx_number]->arg, NULL, 10);
if (!asn_str2asn(argv[idx_number]->arg, &as)) {
vty_out(vty, "%% BGP: No such AS %s\n", argv[idx_number]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
bgp_confederation_id_set(bgp, as);
@ -1953,11 +1969,11 @@ DEFUN (bgp_confederation_identifier,
DEFUN (no_bgp_confederation_identifier,
no_bgp_confederation_identifier_cmd,
"no bgp confederation identifier [(1-4294967295)]",
"no bgp confederation identifier [ASNUM]",
NO_STR
BGP_STR
"AS confederation parameters\n"
"AS number\n"
AS_STR
"Set routing domain confederation AS\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
@ -1968,7 +1984,7 @@ DEFUN (no_bgp_confederation_identifier,
DEFUN (bgp_confederation_peers,
bgp_confederation_peers_cmd,
"bgp confederation peers (1-4294967295)...",
"bgp confederation peers ASNUM...",
BGP_STR
"AS confederation parameters\n"
"Peer ASs in BGP confederation\n"
@ -1980,7 +1996,12 @@ DEFUN (bgp_confederation_peers,
int i;
for (i = idx_asn; i < argc; i++) {
as = strtoul(argv[i]->arg, NULL, 10);
if (!asn_str2asn(argv[i]->arg, &as)) {
vty_out(vty, "%% Invalid confed peer AS value: %s\n",
argv[i]->arg);
continue;
}
bgp_confederation_peers_add(bgp, as);
}
return CMD_SUCCESS;
@ -1988,7 +2009,7 @@ DEFUN (bgp_confederation_peers,
DEFUN (no_bgp_confederation_peers,
no_bgp_confederation_peers_cmd,
"no bgp confederation peers (1-4294967295)...",
"no bgp confederation peers ASNUM...",
NO_STR
BGP_STR
"AS confederation parameters\n"
@ -2001,8 +2022,11 @@ DEFUN (no_bgp_confederation_peers,
int i;
for (i = idx_asn; i < argc; i++) {
as = strtoul(argv[i]->arg, NULL, 10);
if (!asn_str2asn(argv[i]->arg, &as)) {
vty_out(vty, "%% Invalid confed peer AS value: %s\n",
argv[i]->arg);
continue;
}
bgp_confederation_peers_remove(bgp, as);
}
return CMD_SUCCESS;
@ -4506,11 +4530,13 @@ static int peer_remote_as_vty(struct vty *vty, const char *peer_str,
} else if (as_str[0] == 'e') {
as = 0;
as_type = AS_EXTERNAL;
} else {
/* Get AS number. */
as = strtoul(as_str, NULL, 10);
}
} else if (!asn_str2asn(as_str, &as))
as_type = AS_UNSPECIFIED;
if (as_type == AS_UNSPECIFIED) {
vty_out(vty, "%% Invalid peer AS: %s\n", as_str);
return CMD_WARNING_CONFIG_FAILED;
}
/* If peer is peer group or interface peer, call proper function. */
ret = str2sockunion(peer_str, &su);
if (ret < 0) {
@ -4608,7 +4634,7 @@ ALIAS(no_bgp_shutdown, no_bgp_shutdown_msg_cmd,
DEFUN (neighbor_remote_as,
neighbor_remote_as_cmd,
"neighbor <A.B.C.D|X:X::X:X|WORD> remote-as <(1-4294967295)|internal|external>",
"neighbor <A.B.C.D|X:X::X:X|WORD> remote-as <ASNUM|internal|external>",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Specify a BGP neighbor\n"
@ -4688,8 +4714,8 @@ static int peer_conf_interface_get(struct vty *vty, const char *conf_if,
as_type = AS_EXTERNAL;
} else {
/* Get AS number. */
as = strtoul(as_str, NULL, 10);
as_type = AS_SPECIFIED;
if (asn_str2asn(as_str, &as))
as_type = AS_SPECIFIED;
}
}
@ -4801,7 +4827,7 @@ DEFUN (neighbor_interface_config_v6only,
DEFUN (neighbor_interface_config_remote_as,
neighbor_interface_config_remote_as_cmd,
"neighbor WORD interface remote-as <(1-4294967295)|internal|external>",
"neighbor WORD interface remote-as <ASNUM|internal|external>",
NEIGHBOR_STR
"Interface name or neighbor tag\n"
"Enable BGP on interface\n"
@ -4818,7 +4844,7 @@ DEFUN (neighbor_interface_config_remote_as,
DEFUN (neighbor_interface_v6only_config_remote_as,
neighbor_interface_v6only_config_remote_as_cmd,
"neighbor WORD interface v6only remote-as <(1-4294967295)|internal|external>",
"neighbor WORD interface v6only remote-as <ASNUM|internal|external>",
NEIGHBOR_STR
"Interface name or neighbor tag\n"
"Enable BGP with v6 link-local only\n"
@ -4987,7 +5013,7 @@ DEFUN (no_neighbor_peer_group,
DEFUN (no_neighbor_interface_peer_group_remote_as,
no_neighbor_interface_peer_group_remote_as_cmd,
"no neighbor WORD remote-as <(1-4294967295)|internal|external>",
"no neighbor WORD remote-as <ASNUM|internal|external>",
NO_STR
NEIGHBOR_STR
"Interface name or neighbor tag\n"
@ -5020,11 +5046,11 @@ DEFUN (no_neighbor_interface_peer_group_remote_as,
DEFUN (neighbor_local_as,
neighbor_local_as_cmd,
"neighbor <A.B.C.D|X:X::X:X|WORD> local-as (1-4294967295)",
"neighbor <A.B.C.D|X:X::X:X|WORD> local-as ASNUM",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Specify a local-as number\n"
"AS number used as local AS\n")
"AS number expressed in dotted or plain format used as local AS\n")
{
int idx_peer = 1;
int idx_number = 3;
@ -5036,18 +5062,23 @@ DEFUN (neighbor_local_as,
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
as = strtoul(argv[idx_number]->arg, NULL, 10);
if (!asn_str2asn(argv[idx_number]->arg, &as)) {
vty_out(vty, "%% Invalid neighbor local-as value: %s\n",
argv[idx_number]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
ret = peer_local_as_set(peer, as, 0, 0);
return bgp_vty_return(vty, ret);
}
DEFUN (neighbor_local_as_no_prepend,
neighbor_local_as_no_prepend_cmd,
"neighbor <A.B.C.D|X:X::X:X|WORD> local-as (1-4294967295) no-prepend",
"neighbor <A.B.C.D|X:X::X:X|WORD> local-as ASNUM no-prepend",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Specify a local-as number\n"
"AS number used as local AS\n"
"AS number expressed in dotted or plain format used as local AS\n"
"Do not prepend local-as to updates from ebgp peers\n")
{
int idx_peer = 1;
@ -5060,18 +5091,23 @@ DEFUN (neighbor_local_as_no_prepend,
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
as = strtoul(argv[idx_number]->arg, NULL, 10);
if (!asn_str2asn(argv[idx_number]->arg, &as)) {
vty_out(vty, "%% Invalid neighbor local-as value: %s\n",
argv[idx_number]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
ret = peer_local_as_set(peer, as, 1, 0);
return bgp_vty_return(vty, ret);
}
DEFUN (neighbor_local_as_no_prepend_replace_as,
neighbor_local_as_no_prepend_replace_as_cmd,
"neighbor <A.B.C.D|X:X::X:X|WORD> local-as (1-4294967295) no-prepend replace-as",
"neighbor <A.B.C.D|X:X::X:X|WORD> local-as ASNUM no-prepend replace-as",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Specify a local-as number\n"
"AS number used as local AS\n"
"AS number expressed in dotted or plain format used as local AS\n"
"Do not prepend local-as to updates from ebgp peers\n"
"Do not prepend local-as to updates from ibgp peers\n")
{
@ -5085,19 +5121,24 @@ DEFUN (neighbor_local_as_no_prepend_replace_as,
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
as = strtoul(argv[idx_number]->arg, NULL, 10);
if (!asn_str2asn(argv[idx_number]->arg, &as)) {
vty_out(vty, "%% Invalid neighbor local-as value: %s\n",
argv[idx_number]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
ret = peer_local_as_set(peer, as, 1, 1);
return bgp_vty_return(vty, ret);
}
DEFUN (no_neighbor_local_as,
no_neighbor_local_as_cmd,
"no neighbor <A.B.C.D|X:X::X:X|WORD> local-as [(1-4294967295) [no-prepend [replace-as]]]",
"no neighbor <A.B.C.D|X:X::X:X|WORD> local-as [ASNUM [no-prepend [replace-as]]]",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Specify a local-as number\n"
"AS number used as local AS\n"
"AS number expressed in dotted or plain format used as local AS\n"
"Do not prepend local-as to updates from ebgp peers\n"
"Do not prepend local-as to updates from ibgp peers\n")
{
@ -10084,7 +10125,7 @@ static int bgp_clear_prefix(struct vty *vty, const char *view_name,
/* one clear bgp command to rule them all */
DEFUN (clear_ip_bgp_all,
clear_ip_bgp_all_cmd,
"clear [ip] bgp [<view|vrf> VIEWVRFNAME] [<ipv4|ipv6|l2vpn> [<unicast|multicast|vpn|labeled-unicast|flowspec|evpn>]] <*|A.B.C.D$neighbor|X:X::X:X$neighbor|WORD$neighbor|(1-4294967295)|external|peer-group PGNAME> [<soft [<in|out>]|in [prefix-filter]|out|message-stats>]",
"clear [ip] bgp [<view|vrf> VIEWVRFNAME] [<ipv4|ipv6|l2vpn> [<unicast|multicast|vpn|labeled-unicast|flowspec|evpn>]] <*|A.B.C.D$neighbor|X:X::X:X$neighbor|WORD$neighbor|ASNUM|external|peer-group PGNAME> [<soft [<in|out>]|in [prefix-filter]|out|message-stats>]",
CLEAR_STR
IP_STR
BGP_STR
@ -10097,7 +10138,7 @@ DEFUN (clear_ip_bgp_all,
"BGP IPv4 neighbor to clear\n"
"BGP IPv6 neighbor to clear\n"
"BGP neighbor on interface to clear\n"
"Clear peers with the AS number\n"
"Clear peers with the AS number in plain or dotted format\n"
"Clear all external peers\n"
"Clear all members of peer-group\n"
"BGP peer-group name\n"
@ -10138,7 +10179,7 @@ DEFUN (clear_ip_bgp_all,
if (argv_find_and_parse_afi(argv, argc, &idx, &afi))
argv_find_and_parse_safi(argv, argc, &idx, &safi);
/* <*|A.B.C.D|X:X::X:X|WORD|(1-4294967295)|external|peer-group PGNAME> */
/* <*|A.B.C.D|X:X::X:X|WORD|ASNUM|external|peer-group PGNAME> */
if (argv_find(argv, argc, "*", &idx)) {
clr_sort = clear_all;
} else if (argv_find(argv, argc, "A.B.C.D", &idx)) {
@ -10157,7 +10198,7 @@ DEFUN (clear_ip_bgp_all,
} else if (argv_find(argv, argc, "WORD", &idx)) {
clr_sort = clear_peer;
clr_arg = argv[idx]->arg;
} else if (argv_find(argv, argc, "(1-4294967295)", &idx)) {
} else if (argv_find(argv, argc, "ASNUM", &idx)) {
clr_sort = clear_as;
clr_arg = argv[idx]->arg;
} else if (argv_find(argv, argc, "external", &idx)) {
@ -11827,7 +11868,7 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd,
"show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR
" [" BGP_SAFI_WITH_LABEL_CMD_STR
"]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [terse] [wide] [json$uj]",
"]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <ASNUM|internal|external>>] [terse] [wide] [json$uj]",
SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
BGP_SAFI_WITH_LABEL_HELP_STR
"Display the entries for all address families\n"
@ -11838,8 +11879,7 @@ DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd,
"Neighbor to display information about\n"
"Neighbor to display information about\n"
"Neighbor on BGP configured interface\n"
"Show only the specified remote AS sessions\n"
"AS number\n"
"Show only the specified remote AS sessions\n" AS_STR
"Internal (iBGP) AS sessions\n"
"External (eBGP) AS sessions\n"
"Shorten the information on BGP instances\n"
@ -11881,8 +11921,12 @@ DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd,
as_type = AS_INTERNAL;
else if (argv[idx + 1]->arg[0] == 'e')
as_type = AS_EXTERNAL;
else
as = (as_t)atoi(argv[idx + 1]->arg);
else if (!asn_str2asn(argv[idx + 1]->arg, &as)) {
vty_out(vty,
"%% Invalid neighbor remote-as value: %s\n",
argv[idx + 1]->arg);
return CMD_SUCCESS;
}
}
if (argv_find(argv, argc, "terse", &idx))

View file

@ -3200,6 +3200,9 @@ static struct bgp *bgp_create(as_t *as, const char *name,
bgp->as = *as;
if (as_pretty)
bgp->as_pretty = XSTRDUP(MTYPE_BGP, as_pretty);
else
bgp->as_pretty = XSTRDUP(MTYPE_BGP, asn_asn2asplain(*as));
if (BGP_DEBUG(zebra, ZEBRA)) {
if (inst_type == BGP_INSTANCE_TYPE_DEFAULT)
zlog_debug("Creating Default VRF, AS %s",

View file

@ -31,6 +31,7 @@
#include "vty.h"
#include "srv6.h"
#include "iana_afi.h"
#include "asn.h"
/* For union sockunion. */
#include "queue.h"
@ -77,7 +78,6 @@ enum zebra_gr_mode {
};
/* Typedef BGP specific types. */
typedef uint32_t as_t;
typedef uint16_t as16_t; /* we may still encounter 16 Bit asnums */
typedef uint16_t bgp_size_t;

View file

@ -151,6 +151,7 @@ by the parser.
: RANGE
: MAC
: MAC_PREFIX
: ASNUM
selector: "<" `selector_seq_seq` ">" `varname_token`
: "{" `selector_seq_seq` "}" `varname_token`
: "[" `selector_seq_seq` "]" `varname_token`
@ -176,27 +177,29 @@ parser, but this is merely a dumb copy job.
Here is a brief summary of the various token types along with examples.
+-----------------+-------------------+-------------------------------------------------------------+
| Token type | Syntax | Description |
+=================+===================+=============================================================+
| ``WORD`` | ``show ip bgp`` | Matches itself. In the given example every token is a WORD. |
+-----------------+-------------------+-------------------------------------------------------------+
| ``IPV4`` | ``A.B.C.D`` | Matches an IPv4 address. |
+-----------------+-------------------+-------------------------------------------------------------+
| ``IPV6`` | ``X:X::X:X`` | Matches an IPv6 address. |
+-----------------+-------------------+-------------------------------------------------------------+
| ``IPV4_PREFIX`` | ``A.B.C.D/M`` | Matches an IPv4 prefix in CIDR notation. |
+-----------------+-------------------+-------------------------------------------------------------+
| ``IPV6_PREFIX`` | ``X:X::X:X/M`` | Matches an IPv6 prefix in CIDR notation. |
+-----------------+-------------------+-------------------------------------------------------------+
| ``MAC`` | ``X:X:X:X:X:X`` | Matches a 48-bit mac address. |
+-----------------+-------------------+-------------------------------------------------------------+
| ``MAC_PREFIX`` | ``X:X:X:X:X:X/M`` | Matches a 48-bit mac address with a mask. |
+-----------------+-------------------+-------------------------------------------------------------+
| ``VARIABLE`` | ``FOOBAR`` | Matches anything. |
+-----------------+-------------------+-------------------------------------------------------------+
| ``RANGE`` | ``(X-Y)`` | Matches numbers in the range X..Y inclusive. |
+-----------------+-------------------+-------------------------------------------------------------+
+-----------------+-------------------------+-------------------------------------------------------+
| Token type | Syntax | Description |
+=================+=========================+=======================================================+
| ``WORD`` | ``show ip bgp`` | Matches itself. In the example every token is a WORD. |
+-----------------+-------------------------+-------------------------------------------------------+
| ``IPV4`` | ``A.B.C.D`` | Matches an IPv4 address. |
+-----------------+-------------------------+-------------------------------------------------------+
| ``IPV6`` | ``X:X::X:X`` | Matches an IPv6 address. |
+-----------------+-------------------------+-------------------------------------------------------+
| ``IPV4_PREFIX`` | ``A.B.C.D/M`` | Matches an IPv4 prefix in CIDR notation. |
+-----------------+-------------------------+-------------------------------------------------------+
| ``IPV6_PREFIX`` | ``X:X::X:X/M`` | Matches an IPv6 prefix in CIDR notation. |
+-----------------+-------------------------+-------------------------------------------------------+
| ``MAC`` | ``X:X:X:X:X:X`` | Matches a 48-bit mac address. |
+-----------------+-------------------------+-------------------------------------------------------+
| ``MAC_PREFIX`` | ``X:X:X:X:X:X/M`` | Matches a 48-bit mac address with a mask. |
+-----------------+-------------------------+-------------------------------------------------------+
| ``VARIABLE`` | ``FOOBAR`` | Matches anything. |
+-----------------+-------------------------+-------------------------------------------------------+
| ``RANGE`` | ``(X-Y)`` | Matches numbers in the range X..Y inclusive. |
+-----------------+-------------------------+-------------------------------------------------------+
| ``ASNUM`` | ``<A.B|(1-4294967295>`` | Matches an AS in plain or dot format. |
+-----------------+-------------------------+-------------------------------------------------------+
When presented with user input, the parser will search over all defined
commands in the current context to find a match. It is aware of the various

150
lib/asn.c Normal file
View file

@ -0,0 +1,150 @@
/*
* ASN functions
*
* Copyright 2022 6WIND
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
#include "log.h"
#include "asn.h"
static bool relax_as_zero;
/* converts a string into an Autonomous system number
* "1.1" => 65536
* "65500" => 65500
*/
static bool asn_str2asn_internal(const char *asstring, as_t *asn,
const char **next, bool *partial)
{
uint32_t high = 0, low = 0;
uint64_t temp_val;
const char *p = asstring;
bool ret = false;
uint32_t digit;
if (!asstring)
goto end;
if (!isdigit((unsigned char)*p))
goto end;
temp_val = 0;
while (isdigit((unsigned char)*p)) {
digit = (*p) - '0';
temp_val *= 10;
temp_val += digit;
if (temp_val > UINT32_MAX)
/* overflow */
goto end;
p++;
}
high = (uint32_t)temp_val;
if (*p == '.') { /* dot format */
p++;
temp_val = 0;
if (*p == '\0' && partial) {
*partial = true;
goto end;
}
while (isdigit((unsigned char)*p)) {
digit = (*p) - '0';
temp_val *= 10;
temp_val += digit;
if (temp_val > UINT16_MAX)
/* overflow */
goto end;
p++;
}
low = (uint32_t)temp_val;
if (!next && *p != '\0' && !isdigit((unsigned char)*p))
goto end;
/* AS <AS4B>.<AS4B> is forbidden */
if (high > UINT16_MAX)
goto end;
/* AS 0.0 is authorised for some case only */
if (!relax_as_zero && high == 0 && low == 0) {
if (partial)
*partial = true;
goto end;
}
if (!asn) {
ret = true;
goto end;
}
*asn = (high << 16) + low;
ret = true;
goto end;
}
/* AS 0 is forbidden */
if (!relax_as_zero && high == 0)
goto end;
if (!asn) {
ret = true;
goto end;
}
*asn = high;
ret = true;
end:
if (next)
*next = p;
return ret;
}
bool asn_str2asn(const char *asstring, as_t *asn)
{
return asn_str2asn_internal(asstring, asn, NULL, NULL);
}
const char *asn_asn2asplain(as_t asn)
{
static char buf[ASN_STRING_MAX_SIZE];
snprintf(buf, sizeof(buf), "%u", asn);
return buf;
}
const char *asn_str2asn_parse(const char *asstring, as_t *asn, bool *found_ptr)
{
const char *p = NULL;
const char **next = &p;
bool found;
found = asn_str2asn_internal(asstring, asn, next, NULL);
if (found_ptr)
*found_ptr = found;
return *next;
}
void asn_relax_as_zero(bool relax)
{
relax_as_zero = relax;
}
enum match_type asn_str2asn_match(const char *str)
{
bool found, partial = false;
found = asn_str2asn_internal(str, NULL, NULL, &partial);
if (found && !partial)
return exact_match;
if (partial)
return partly_match;
return no_match;
}

48
lib/asn.h Normal file
View file

@ -0,0 +1,48 @@
/*
* AS number structure
* Copyright 2022 6WIND
*
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2, or (at your
* option) any later version.
*
* GNU Zebra is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _FRR_ASN_H
#define _FRR_ASN_H
#include "zebra.h"
#include "command_match.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ASN_STRING_MAX_SIZE 12
typedef uint32_t as_t;
extern bool asn_str2asn(const char *asstring, as_t *asn);
extern const char *asn_asn2asplain(as_t asn);
extern const char *asn_str2asn_parse(const char *asstring, as_t *asn,
bool *found_ptr);
extern enum match_type asn_str2asn_match(const char *str);
/* for test */
extern void asn_relax_as_zero(bool relax);
#ifdef __cplusplus
}
#endif
#endif /* _FRR_ASN_H */

View file

@ -71,6 +71,7 @@ const struct message tokennames[] = {
item(IPV6_PREFIX_TKN),
item(MAC_TKN),
item(MAC_PREFIX_TKN),
item(ASNUM_TKN),
item(FORK_TKN),
item(JOIN_TKN),
item(START_TKN),

View file

@ -403,7 +403,8 @@ struct cmd_node {
#define DEBUG_STR "Debugging functions\n"
#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n"
#define ROUTER_STR "Enable a routing process\n"
#define AS_STR "AS number\n"
#define AS_STR \
"AS number in plain <1-4294967295> or dotted <0-65535>.<0-65535> format\n"
#define MAC_STR "MAC address\n"
#define MBGP_STR "MBGP information\n"
#define MATCH_STR "Match values from routing table\n"

View file

@ -279,6 +279,7 @@ static bool cmd_nodes_equal(struct graph_node *ga, struct graph_node *gb)
case END_TKN:
case NEG_ONLY_TKN:
case WORD_TKN:
case ASNUM_TKN:
return true;
}
@ -548,6 +549,7 @@ void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf)
case MAC_PREFIX_TKN:
case END_TKN:
case VARIABLE_TKN:
case ASNUM_TKN:
color = "#ffffff";
break;
}

View file

@ -58,6 +58,7 @@ enum cmd_token_type {
IPV6_PREFIX_TKN, // IPV6 network prefixes
MAC_TKN, // Ethernet address
MAC_PREFIX_TKN, // Ethernet address w/ CIDR mask
ASNUM_TKN, // AS dot format
/* plumbing types */
FORK_TKN, // marks subgraph beginning

View file

@ -54,6 +54,7 @@ VARIABLE [A-Z][-_A-Z:0-9]+
WORD (\-|\+)?[a-zA-Z0-9\*][-+_a-zA-Z0-9\*]*
NUMBER (\-|\+)?[0-9]{1,20}
RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\)
ASNUM ASNUM
/* yytext shall be a pointer */
%pointer
@ -73,6 +74,7 @@ RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\)
%}
[ \t]+ LOC_STEP /* ignore whitespace */;
{ASNUM} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return ASNUM;}
{IPV4} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV4;}
{IPV4_PREFIX} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV4_PREFIX;}
{IPV6} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV6;}

View file

@ -25,6 +25,7 @@
#include "command_match.h"
#include "memory.h"
#include "asn.h"
DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack");
@ -556,6 +557,7 @@ static enum match_type min_match_level(enum cmd_token_type type)
case END_TKN:
case NEG_ONLY_TKN:
case VARIABLE_TKN:
case ASNUM_TKN:
return exact_match;
}
@ -579,6 +581,7 @@ static int score_precedence(enum cmd_token_type type)
case IPV6_PREFIX_TKN:
case MAC_TKN:
case MAC_PREFIX_TKN:
case ASNUM_TKN:
case RANGE_TKN:
return 2;
case WORD_TKN:
@ -713,6 +716,8 @@ static enum match_type match_token(struct cmd_token *token, char *input_token)
return match_mac(input_token, false);
case MAC_PREFIX_TKN:
return match_mac(input_token, true);
case ASNUM_TKN:
return asn_str2asn_match(input_token);
case END_TKN:
case FORK_TKN:
case JOIN_TKN:
@ -855,7 +860,6 @@ static enum match_type match_ipv4_prefix(const char *str)
return exact_match;
}
#define IPV6_ADDR_STR "0123456789abcdefABCDEF:."
#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./"
#define STATE_START 1

View file

@ -104,6 +104,7 @@
%token <string> RANGE
%token <string> MAC
%token <string> MAC_PREFIX
%token <string> ASNUM
/* special syntax, value is irrelevant */
%token <string> EXCL_BRACKET
@ -293,6 +294,11 @@ placeholder_token_real:
$$ = new_token_node (ctx, MAC_PREFIX_TKN, $1, doc_next(ctx));
XFREE (MTYPE_LEX, $1);
}
| ASNUM
{
$$ = new_token_node (ctx, ASNUM_TKN, $1, doc_next(ctx));
XFREE (MTYPE_LEX, $1);
}
placeholder_token:
placeholder_token_real varname_token

View file

@ -214,6 +214,7 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph,
item(IPV6_PREFIX_TKN); // IPV6 network prefixes
item(MAC_TKN); // MAC address
item(MAC_PREFIX_TKN); // MAC address with mask
item(ASNUM_TKN); // ASNUM
/* plumbing types */
item(FORK_TKN);

View file

@ -9,6 +9,7 @@ lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(UST
lib_libfrr_la_SOURCES = \
lib/agg_table.c \
lib/atomlist.c \
lib/asn.c \
lib/base64.c \
lib/bfd.c \
lib/buffer.c \
@ -160,6 +161,7 @@ clippy_scan += \
pkginclude_HEADERS += \
lib/agg_table.h \
lib/asn.h \
lib/atomlist.h \
lib/base64.h \
lib/bfd.h \

View file

@ -64,6 +64,12 @@ _fail = (_end == argv[_i]->arg) || (*_end != '\\0');"""
)
class AsDotHandler(RenderHandler):
argtype = "as_t"
decl = Template("as_t $varname = 0;")
code = Template("_fail = !asn_str2asn(argv[_i]->arg, &$varname);")
# A.B.C.D/M (prefix_ipv4) and
# X:X::X:X/M (prefix_ipv6) are "compatible" and can merge into a
# struct prefix:
@ -165,6 +171,7 @@ handlers = {
"IPV6_PREFIX_TKN": Prefix6Handler,
"MAC_TKN": PrefixEthHandler,
"MAC_PREFIX_TKN": PrefixEthHandler,
"ASNUM_TKN": AsDotHandler,
}
# core template invoked for each occurence of DEFPY.

View file

@ -924,7 +924,9 @@ static int validate(struct aspath *as, const struct test_spec *sp)
bytes4 = aspath_put(s, as, 1);
as4 = make_aspath(STREAM_DATA(s), bytes4, 1);
asn_relax_as_zero(true);
asstr = aspath_str2aspath(sp->shouldbe);
asn_relax_as_zero(false);
asconfeddel = aspath_delete_confed_seq(aspath_dup(asinout));

View file

@ -1688,7 +1688,7 @@ DEFUNSH(VTYSH_ZEBRA, srv6_locator, srv6_locator_cmd,
#ifdef HAVE_BGPD
DEFUNSH(VTYSH_BGPD, router_bgp, router_bgp_cmd,
"router bgp [(1-4294967295) [<view|vrf> VIEWVRFNAME]]",
"router bgp [ASNUM [<view|vrf> VIEWVRFNAME]]",
ROUTER_STR BGP_STR AS_STR
"BGP view\nBGP VRF\n"
"View/VRF name\n")