diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 2ae693cd30..9f74aa76d4 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -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; } diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index d00bdd2571..c9da71c6de 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -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 $replace", + "set as-path replace $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 []", + "no set as-path replace []", 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 = diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 6023d9811d..8af3f65f45 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -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 [ VIEWVRFNAME]]", + "router bgp [ASNUM$instasn [ 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 [ VIEWVRFNAME]]", + "no router bgp [ASNUM$instasn [ 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 remote-as <(1-4294967295)|internal|external>", + "neighbor remote-as ", 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 ", 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 ", 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 ", 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 local-as (1-4294967295)", + "neighbor 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 local-as (1-4294967295) no-prepend", + "neighbor 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 local-as (1-4294967295) no-prepend replace-as", + "neighbor 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 local-as [(1-4294967295) [no-prepend [replace-as]]]", + "no neighbor 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 [ VIEWVRFNAME] [ []] <*|A.B.C.D$neighbor|X:X::X:X$neighbor|WORD$neighbor|(1-4294967295)|external|peer-group PGNAME> []|in [prefix-filter]|out|message-stats>]", + "clear [ip] bgp [ VIEWVRFNAME] [ []] <*|A.B.C.D$neighbor|X:X::X:X$neighbor|WORD$neighbor|ASNUM|external|peer-group PGNAME> []|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 [ VIEWVRFNAME] [" BGP_AFI_CMD_STR " [" BGP_SAFI_WITH_LABEL_CMD_STR - "]] [all$all] summary [established|failed] [|remote-as <(1-4294967295)|internal|external>>] [terse] [wide] [json$uj]", + "]] [all$all] summary [established|failed] [|remote-as >] [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)) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 9fca975212..32f169e342 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -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", diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index a0627af751..4efeb0e9ef 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -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; diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst index 5d1dda06df..2a08531bd7 100644 --- a/doc/developer/cli.rst +++ b/doc/developer/cli.rst @@ -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`` | ```` | 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 diff --git a/lib/asn.c b/lib/asn.c new file mode 100644 index 0000000000..7a786866cb --- /dev/null +++ b/lib/asn.c @@ -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 +#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 . 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; +} diff --git a/lib/asn.h b/lib/asn.h new file mode 100644 index 0000000000..baaaf4d807 --- /dev/null +++ b/lib/asn.h @@ -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 */ diff --git a/lib/command.c b/lib/command.c index 4e8194bbc6..3cab6a1e7a 100644 --- a/lib/command.c +++ b/lib/command.c @@ -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), diff --git a/lib/command.h b/lib/command.h index 8f5d96053c..0ee45cee55 100644 --- a/lib/command.h +++ b/lib/command.h @@ -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" diff --git a/lib/command_graph.c b/lib/command_graph.c index 766d7e9371..ee03ff85d4 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -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; } diff --git a/lib/command_graph.h b/lib/command_graph.h index b8c7a9c72c..2ead2798e0 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -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 diff --git a/lib/command_lex.l b/lib/command_lex.l index ec366ce7e1..60661b986d 100644 --- a/lib/command_lex.l +++ b/lib/command_lex.l @@ -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;} diff --git a/lib/command_match.c b/lib/command_match.c index 6c1d05d926..3318e2c7e6 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -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 diff --git a/lib/command_parse.y b/lib/command_parse.y index 35c119691b..6de14d621f 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -104,6 +104,7 @@ %token RANGE %token MAC %token MAC_PREFIX +%token ASNUM /* special syntax, value is irrelevant */ %token 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 diff --git a/lib/command_py.c b/lib/command_py.c index cce9542e30..a560453e6a 100644 --- a/lib/command_py.c +++ b/lib/command_py.c @@ -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); diff --git a/lib/subdir.am b/lib/subdir.am index 18e9825a7a..9a00cd01b1 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -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 \ diff --git a/python/clidef.py b/python/clidef.py index 101c9a5ae3..cd399e93b5 100644 --- a/python/clidef.py +++ b/python/clidef.py @@ -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. diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c index ef1fcf5cec..0f6d5b023d 100644 --- a/tests/bgpd/test_aspath.c +++ b/tests/bgpd/test_aspath.c @@ -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)); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index acc984ced7..8a46dfc108 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -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) [ VIEWVRFNAME]]", + "router bgp [ASNUM [ VIEWVRFNAME]]", ROUTER_STR BGP_STR AS_STR "BGP view\nBGP VRF\n" "View/VRF name\n")