From 9eb11997104e3ba5e436220f758f7881c0c0556d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 15 Nov 2022 16:42:40 +0100 Subject: [PATCH 01/22] bgpd: store the bgp as identifier in the configured as-notation This is a preliminary work to handle various ways to configure a BGP Autonomous System. When creating a BGP instance, the user may want to define the AS number as a dotted value, instead of using an integer value. To handle both cases, an as_pretty char attribute will store the as number as it has been given to the vtysh command: router bgp Whenever the as integer of the BGP instance was dumped, the as_pretty original format is used. The json output reuses the integer value to keep backward compatibility with old displays. Signed-off-by: Philippe Guibert --- bgpd/bgp_evpn.c | 9 +++++---- bgpd/bgp_vty.c | 34 ++++++++++++++++++---------------- bgpd/bgp_vty.h | 2 +- bgpd/bgpd.c | 20 ++++++++++++-------- bgpd/bgpd.h | 4 +++- tests/bgpd/test_capability.c | 3 ++- tests/bgpd/test_mp_attr.c | 3 ++- tests/bgpd/test_packet.c | 3 ++- 8 files changed, 45 insertions(+), 33 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index d8acbcd19a..6ccd64dba4 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -6252,13 +6252,14 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, ret = bgp_get_vty(&bgp_vrf, &as, vrf_id_to_name(vrf_id), vrf_id == VRF_DEFAULT - ? BGP_INSTANCE_TYPE_DEFAULT - : BGP_INSTANCE_TYPE_VRF); + ? BGP_INSTANCE_TYPE_DEFAULT + : BGP_INSTANCE_TYPE_VRF, + NULL); switch (ret) { case BGP_ERR_AS_MISMATCH: flog_err(EC_BGP_EVPN_AS_MISMATCH, - "BGP instance is already running; AS is %u", - as); + "BGP instance is already running; AS is %s", + bgp_vrf->as_pretty); return -1; case BGP_ERR_INSTANCE_MISMATCH: flog_err(EC_BGP_EVPN_INSTANCE_MISMATCH, diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index daa435bb2d..6023d9811d 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -594,9 +594,9 @@ static const char *get_bgp_default_af_flag(afi_t afi, safi_t safi) } int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, - enum bgp_instance_type inst_type) + enum bgp_instance_type inst_type, const char *as_pretty) { - int ret = bgp_get(bgp, as, name, inst_type); + int ret = bgp_get(bgp, as, name, inst_type, as_pretty); if (ret == BGP_CREATED) { bgp_timers_set(*bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME, @@ -1531,17 +1531,19 @@ DEFUN_NOSH (router_bgp, if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) is_new_bgp = (bgp_lookup(as, name) == NULL); - ret = bgp_get_vty(&bgp, &as, name, inst_type); + ret = bgp_get_vty(&bgp, &as, name, inst_type, + argv[idx_asn]->arg); switch (ret) { case BGP_ERR_AS_MISMATCH: - vty_out(vty, "BGP is already running; AS is %u\n", as); + vty_out(vty, "BGP is already running; AS is %s\n", + bgp->as_pretty); return CMD_WARNING_CONFIG_FAILED; case BGP_ERR_INSTANCE_MISMATCH: vty_out(vty, "BGP instance name and AS number mismatch\n"); vty_out(vty, - "BGP instance is already running; AS is %u\n", - as); + "BGP instance is already running; AS is %s\n", + bgp->as_pretty); return CMD_WARNING_CONFIG_FAILED; } @@ -9486,7 +9488,7 @@ DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd, /* Auto-create assuming the same AS */ ret = bgp_get_vty(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT); + BGP_INSTANCE_TYPE_DEFAULT, NULL); if (ret) { vty_out(vty, @@ -9598,7 +9600,7 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, if (!bgp_default) { /* Auto-create assuming the same AS */ ret = bgp_get_vty(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT); + BGP_INSTANCE_TYPE_DEFAULT, NULL); if (ret) { vty_out(vty, @@ -9613,7 +9615,7 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, vrf_bgp = bgp_default; else /* Auto-create assuming the same AS */ - ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type); + ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type, NULL); if (ret) { vty_out(vty, @@ -10293,8 +10295,8 @@ DEFUN (show_bgp_views, /* Skip VRFs. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) continue; - vty_out(vty, "\t%s (AS%u)\n", bgp->name ? bgp->name : "(null)", - bgp->as); + vty_out(vty, "\t%s (AS%s)\n", bgp->name ? bgp->name : "(null)", + bgp->as_pretty); } return CMD_SUCCESS; @@ -11085,8 +11087,8 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, : bgp->name); } else { vty_out(vty, - "BGP router identifier %pI4, local AS number %u vrf-id %d", - &bgp->router_id, bgp->as, + "BGP router identifier %pI4, local AS number %s vrf-id %d", + &bgp->router_id, bgp->as_pretty, bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id); @@ -15917,8 +15919,8 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group, json_object_int_add(json_peer_group, "remoteAs", group->bgp->as); else - vty_out(vty, "\nBGP peer-group %s, remote AS %u\n", - group->name, group->bgp->as); + vty_out(vty, "\nBGP peer-group %s, remote AS %s\n", + group->name, group->bgp->as_pretty); } else { if (!json) vty_out(vty, "\nBGP peer-group %s\n", group->name); @@ -17944,7 +17946,7 @@ int bgp_config_write(struct vty *vty) continue; /* Router bgp ASN */ - vty_out(vty, "router bgp %u", bgp->as); + vty_out(vty, "router bgp %s", bgp->as_pretty); if (bgp->name) vty_out(vty, " %s %s", diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 019789dff8..8ed7c965e4 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -150,7 +150,7 @@ extern void bgp_vty_init(void); extern void community_alias_vty(void); extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, - enum bgp_instance_type inst_type); + enum bgp_instance_type inst_type, const char *as_pretty); extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 39010e76f9..9fca975212 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3189,23 +3189,27 @@ static void bgp_vrf_string_name_delete(void *data) /* BGP instance creation by `router bgp' commands. */ static struct bgp *bgp_create(as_t *as, const char *name, - enum bgp_instance_type inst_type) + enum bgp_instance_type inst_type, + const char *as_pretty) { struct bgp *bgp; afi_t afi; safi_t safi; bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp)); - + bgp->as = *as; + if (as_pretty) + bgp->as_pretty = XSTRDUP(MTYPE_BGP, as_pretty); if (BGP_DEBUG(zebra, ZEBRA)) { if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) - zlog_debug("Creating Default VRF, AS %u", *as); + zlog_debug("Creating Default VRF, AS %s", + bgp->as_pretty); else - zlog_debug("Creating %s %s, AS %u", + zlog_debug("Creating %s %s, AS %s", (inst_type == BGP_INSTANCE_TYPE_VRF) ? "VRF" : "VIEW", - name, *as); + name, bgp->as_pretty); } /* Default the EVPN VRF to the default one */ @@ -3282,7 +3286,6 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->condition_check_period = DEFAULT_CONDITIONAL_ROUTES_POLL_TIME; bgp_addpath_init_bgp_data(&bgp->tx_addpath); bgp->fast_convergence = false; - bgp->as = *as; bgp->llgr_stale_time = BGP_DEFAULT_LLGR_STALE_TIME; #ifdef ENABLE_BGP_VNC @@ -3519,7 +3522,7 @@ int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, const char *name, /* Called from VTY commands. */ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, - enum bgp_instance_type inst_type) + enum bgp_instance_type inst_type, const char *as_pretty) { struct bgp *bgp; struct vrf *vrf = NULL; @@ -3529,7 +3532,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, if (ret || *bgp_val) return ret; - bgp = bgp_create(as, name, inst_type); + bgp = bgp_create(as, name, inst_type, as_pretty); /* * view instances will never work inside of a vrf @@ -3925,6 +3928,7 @@ void bgp_free(struct bgp *bgp) ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]); } + XFREE(MTYPE_BGP, bgp->as_pretty); XFREE(MTYPE_BGP, bgp->name); XFREE(MTYPE_BGP, bgp->name_pretty); XFREE(MTYPE_BGP, bgp->snmp_stats); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 61119ab6e0..a0627af751 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -343,6 +343,7 @@ struct bgp_srv6_function { struct bgp { /* AS number of this BGP instance. */ as_t as; + char *as_pretty; /* Name of this BGP instance. */ char *name; @@ -2161,7 +2162,8 @@ extern void bgp_option_norib_set_runtime(void); /* unset the bgp no-rib option during runtime and reset all peers */ extern void bgp_option_norib_unset_runtime(void); -extern int bgp_get(struct bgp **, as_t *, const char *, enum bgp_instance_type); +extern int bgp_get(struct bgp **bgp, as_t *as, const char *name, + enum bgp_instance_type kind, const char *as_pretty); extern void bgp_instance_up(struct bgp *); extern void bgp_instance_down(struct bgp *); extern int bgp_delete(struct bgp *); diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index 51792825da..5a63758d06 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -952,7 +952,8 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, + NULL) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index c5ce5d3cd2..3f6401a2a3 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -1094,7 +1094,8 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, + NULL) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c index 2ce8b561bb..cf91f5570f 100644 --- a/tests/bgpd/test_packet.c +++ b/tests/bgpd/test_packet.c @@ -63,7 +63,8 @@ int main(int argc, char *argv[]) vrf_init(NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, + NULL) < 0) return -1; peer = peer_create_accept(bgp); From 8079a4138d61500117ebbffb250ceba0a894f9c0 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 2 Nov 2022 18:17:21 +0100 Subject: [PATCH 02/22] 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 --- bgpd/bgp_aspath.c | 27 +++---- bgpd/bgp_routemap.c | 56 +++++++++++---- bgpd/bgp_vty.c | 136 +++++++++++++++++++++++------------ bgpd/bgpd.c | 3 + bgpd/bgpd.h | 2 +- doc/developer/cli.rst | 45 ++++++------ lib/asn.c | 150 +++++++++++++++++++++++++++++++++++++++ lib/asn.h | 48 +++++++++++++ lib/command.c | 1 + lib/command.h | 3 +- lib/command_graph.c | 2 + lib/command_graph.h | 1 + lib/command_lex.l | 2 + lib/command_match.c | 6 +- lib/command_parse.y | 6 ++ lib/command_py.c | 1 + lib/subdir.am | 2 + python/clidef.py | 7 ++ tests/bgpd/test_aspath.c | 2 + vtysh/vtysh.c | 2 +- 20 files changed, 397 insertions(+), 105 deletions(-) create mode 100644 lib/asn.c create mode 100644 lib/asn.h 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") From e55b08839914a3b94f361ee909ee63d265d07845 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 3 Nov 2022 21:17:57 +0100 Subject: [PATCH 03/22] bgpd: add as-notation keyword to 'router bgp' vty command A new keyword permits changing the BGP as-notation output: - [no] router bgp <> [vrf BLABLA] [as-notation []] At the BGP instance creation, the output will inherit the way the BGP instance is declared. For instance, the 'router bgp 1.1' command will configure the output in the dot format. However, if the client wants to choose an alternate output, he will have to add the extra command: 'router bgp 1.1 as-notation dot+'. Also, if the user wants to have plain format, even if the BGP instance is declared in dot format, the keyword can also be used for that. The as-notation output is only taken into account at the BGP instance creation. In the case where VPN instances are used, a separate instance may be dynamically created. In that case, the real as-notation format will be taken into acccount at the first configuration. Linking the as-notation format with the BGP instance makes sense, as the operators want to keep consistency of what they configure. One technical reason why to link the as-notation output with the BGP instance creation is that the as-path segment lists stored in the BGP updates use a string representation to handle aspath operations (by using regexp for instance). Changing on the fly the output needs to regenerate this string representation to the correct format. Linking the configuration to the BGP instance creation avoids refreshing the BGP updates. A similar mechanism is put in place in junos too. Signed-off-by: Philippe Guibert --- bgpd/bgp_evpn.c | 2 +- bgpd/bgp_vty.c | 89 +++++++++++++++++++++++++++--------- bgpd/bgp_vty.h | 3 +- bgpd/bgpd.c | 14 ++++-- bgpd/bgpd.h | 6 ++- doc/user/bgp.rst | 25 +++++++++- lib/asn.c | 43 +++++++++++++---- lib/asn.h | 10 ++++ tests/bgpd/test_capability.c | 4 +- tests/bgpd/test_mp_attr.c | 4 +- tests/bgpd/test_packet.c | 4 +- vtysh/vtysh.c | 8 +++- 12 files changed, 166 insertions(+), 46 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 6ccd64dba4..323256816b 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -6254,7 +6254,7 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, vrf_id == VRF_DEFAULT ? BGP_INSTANCE_TYPE_DEFAULT : BGP_INSTANCE_TYPE_VRF, - NULL); + NULL, ASNOTATION_UNDEFINED); switch (ret) { case BGP_ERR_AS_MISMATCH: flog_err(EC_BGP_EVPN_AS_MISMATCH, diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 8af3f65f45..a1fda80ad9 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -595,9 +595,10 @@ static const char *get_bgp_default_af_flag(afi_t afi, safi_t safi) } int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, - enum bgp_instance_type inst_type, const char *as_pretty) + enum bgp_instance_type inst_type, const char *as_pretty, + enum asnotation_mode asnotation) { - int ret = bgp_get(bgp, as, name, inst_type, as_pretty); + int ret = bgp_get(bgp, as, name, inst_type, as_pretty, asnotation); if (ret == BGP_CREATED) { bgp_timers_set(*bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME, @@ -1481,16 +1482,23 @@ DEFUN (no_auto_summary, /* "router bgp" commands. */ DEFUN_NOSH (router_bgp, router_bgp_cmd, - "router bgp [ASNUM$instasn [ VIEWVRFNAME]]", + "router bgp [ASNUM$instasn [ VIEWVRFNAME] [as-notation ]]", ROUTER_STR BGP_STR AS_STR - BGP_INSTANCE_HELP_STR) + BGP_INSTANCE_HELP_STR + "Force the AS notation output\n" + "use 'AA.BB' format for AS 4 byte values\n" + "use 'AA.BB' format for all AS values\n" + "use plain format for all AS values\n") { int idx_asn = 2; int idx_view_vrf = 3; int idx_vrf = 4; int is_new_bgp = 0; + int idx_asnotation = 3; + int idx_asnotation_kind = 4; + enum asnotation_mode asnotation = ASNOTATION_UNDEFINED; int ret; as_t as; struct bgp *bgp; @@ -1526,23 +1534,40 @@ DEFUN_NOSH (router_bgp, BGP_PRIVATE_AS_MAX, BGP_AS4_MAX, as); inst_type = BGP_INSTANCE_TYPE_DEFAULT; - if (argc > 3) { - name = argv[idx_vrf]->arg; - if (!strcmp(argv[idx_view_vrf]->text, "vrf")) { - if (strmatch(name, VRF_DEFAULT_NAME)) - name = NULL; - else - inst_type = BGP_INSTANCE_TYPE_VRF; - } else if (!strcmp(argv[idx_view_vrf]->text, "view")) - inst_type = BGP_INSTANCE_TYPE_VIEW; + if (argv_find(argv, argc, "VIEWVRFNAME", &idx_vrf)) { + idx_view_vrf = idx_vrf - 1; + if (argv[idx_view_vrf]->text) { + name = argv[idx_vrf]->arg; + + if (!strcmp(argv[idx_view_vrf]->text, "vrf")) { + if (strmatch(name, VRF_DEFAULT_NAME)) + name = NULL; + else + inst_type = + BGP_INSTANCE_TYPE_VRF; + } else if (!strcmp(argv[idx_view_vrf]->text, + "view")) + inst_type = BGP_INSTANCE_TYPE_VIEW; + } + } + if (argv_find(argv, argc, "as-notation", &idx_asnotation)) { + idx_asnotation_kind = idx_asnotation + 1; + if (strmatch(argv[idx_asnotation_kind]->text, "dot+")) + asnotation = ASNOTATION_DOTPLUS; + else if (strmatch(argv[idx_asnotation_kind]->text, + "dot")) + asnotation = ASNOTATION_DOT; + else if (strmatch(argv[idx_asnotation_kind]->text, + "plain")) + asnotation = ASNOTATION_PLAIN; } if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) is_new_bgp = (bgp_lookup(as, name) == NULL); ret = bgp_get_vty(&bgp, &as, name, inst_type, - argv[idx_asn]->arg); + argv[idx_asn]->arg, asnotation); switch (ret) { case BGP_ERR_AS_MISMATCH: vty_out(vty, "BGP is already running; AS is %s\n", @@ -1569,6 +1594,19 @@ DEFUN_NOSH (router_bgp, bgp_vpn_leak_export(bgp); /* Pending: handle when user tries to change a view to vrf n vv. */ + /* for pre-existing bgp instance, + * - update as_pretty + * - update asnotation if explicitly mentioned + */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) { + XFREE(MTYPE_BGP, bgp->as_pretty); + bgp->as_pretty = XSTRDUP(MTYPE_BGP, argv[idx_asn]->arg); + if (!CHECK_FLAG(bgp->config, BGP_CONFIG_ASNOTATION) && + asnotation != ASNOTATION_UNDEFINED) { + SET_FLAG(bgp->config, BGP_CONFIG_ASNOTATION); + bgp->asnotation = asnotation; + } + } } /* unset the auto created flag as the user config is now present */ @@ -1581,12 +1619,16 @@ DEFUN_NOSH (router_bgp, /* "no router bgp" commands. */ DEFUN (no_router_bgp, no_router_bgp_cmd, - "no router bgp [ASNUM$instasn [ VIEWVRFNAME]]", + "no router bgp [ASNUM$instasn [ VIEWVRFNAME] [as-notation ]]", NO_STR ROUTER_STR BGP_STR AS_STR - BGP_INSTANCE_HELP_STR) + BGP_INSTANCE_HELP_STR + "Force the AS notation output\n" + "use 'AA.BB' format for AS 4 byte values\n" + "use 'AA.BB' format for all AS values\n" + "use plain format for all AS values\n") { int idx_asn = 3; int idx_vrf = 5; @@ -1851,7 +1893,6 @@ DEFPY (bgp_suppress_fib_pending, return CMD_SUCCESS; } - /* BGP Cluster ID. */ DEFUN (bgp_cluster_id, bgp_cluster_id_cmd, @@ -9529,7 +9570,8 @@ DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd, /* Auto-create assuming the same AS */ ret = bgp_get_vty(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT, NULL); + BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_UNDEFINED); if (ret) { vty_out(vty, @@ -9641,7 +9683,8 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, if (!bgp_default) { /* Auto-create assuming the same AS */ ret = bgp_get_vty(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT, NULL); + BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_UNDEFINED); if (ret) { vty_out(vty, @@ -9656,8 +9699,8 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, vrf_bgp = bgp_default; else /* Auto-create assuming the same AS */ - ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type, NULL); - + ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type, + NULL, ASNOTATION_UNDEFINED); if (ret) { vty_out(vty, "VRF %s is not configured as a bgp instance\n", @@ -17996,6 +18039,10 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " %s %s", (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) ? "view" : "vrf", bgp->name); + if (CHECK_FLAG(bgp->config, BGP_CONFIG_ASNOTATION)) + vty_out(vty, " as-notation %s", + asn_mode2str(bgp->asnotation)); + vty_out(vty, "\n"); /* BGP fast-external-failover. */ diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 8ed7c965e4..d75eed2dda 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -150,7 +150,8 @@ extern void bgp_vty_init(void); extern void community_alias_vty(void); extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, - enum bgp_instance_type inst_type, const char *as_pretty); + enum bgp_instance_type inst_type, const char *as_pretty, + enum asnotation_mode asnotation); extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 32f169e342..565cbccee4 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3190,7 +3190,8 @@ static void bgp_vrf_string_name_delete(void *data) /* BGP instance creation by `router bgp' commands. */ static struct bgp *bgp_create(as_t *as, const char *name, enum bgp_instance_type inst_type, - const char *as_pretty) + const char *as_pretty, + enum asnotation_mode asnotation) { struct bgp *bgp; afi_t afi; @@ -3203,6 +3204,12 @@ static struct bgp *bgp_create(as_t *as, const char *name, else bgp->as_pretty = XSTRDUP(MTYPE_BGP, asn_asn2asplain(*as)); + if (asnotation != ASNOTATION_UNDEFINED) { + bgp->asnotation = asnotation; + SET_FLAG(bgp->config, BGP_CONFIG_ASNOTATION); + } else + asn_str2asn_notation(bgp->as_pretty, NULL, &bgp->asnotation); + if (BGP_DEBUG(zebra, ZEBRA)) { if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) zlog_debug("Creating Default VRF, AS %s", @@ -3525,7 +3532,8 @@ int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, const char *name, /* Called from VTY commands. */ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, - enum bgp_instance_type inst_type, const char *as_pretty) + enum bgp_instance_type inst_type, const char *as_pretty, + enum asnotation_mode asnotation) { struct bgp *bgp; struct vrf *vrf = NULL; @@ -3535,7 +3543,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, if (ret || *bgp_val) return ret; - bgp = bgp_create(as, name, inst_type, as_pretty); + bgp = bgp_create(as, name, inst_type, as_pretty, asnotation); /* * view instances will never work inside of a vrf diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 4efeb0e9ef..634c4cf1d1 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -399,6 +399,7 @@ struct bgp { uint16_t config; #define BGP_CONFIG_CLUSTER_ID (1 << 0) #define BGP_CONFIG_CONFEDERATION (1 << 1) +#define BGP_CONFIG_ASNOTATION (1 << 2) /* BGP router identifier. */ struct in_addr router_id; @@ -798,6 +799,8 @@ struct bgp { bool allow_martian; + enum asnotation_mode asnotation; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(bgp); @@ -2163,7 +2166,8 @@ extern void bgp_option_norib_set_runtime(void); extern void bgp_option_norib_unset_runtime(void); extern int bgp_get(struct bgp **bgp, as_t *as, const char *name, - enum bgp_instance_type kind, const char *as_pretty); + enum bgp_instance_type kind, const char *as_pretty, + enum asnotation_mode asnotation); extern void bgp_instance_up(struct bgp *); extern void bgp_instance_down(struct bgp *); extern int bgp_delete(struct bgp *); diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index b17442f641..4e887eecf0 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -254,8 +254,9 @@ ASN and Router ID ----------------- First of all you must configure BGP router with the :clicmd:`router bgp ASN` -command. The AS number is an identifier for the autonomous system. The BGP -protocol uses the AS number for detecting whether the BGP connection is +command. The AS number is an identifier for the autonomous system. The AS +identifier can either be a number or two numbers separated by a period. The +BGP protocol uses the AS identifier for detecting whether the BGP connection is internal or external. .. clicmd:: router bgp ASN @@ -4289,6 +4290,26 @@ Segment-Routing IPv6 vpn_policy[AFI_IP].tovpn_sid: none vpn_policy[AFI_IP6].tovpn_sid: 2001:db8:1:1::200 +AS-notation support +------------------- + +By default, the ASN value output follows how the BGP ASN instance is +expressed in the configuration. Three as-notation outputs are available: + +- plain output: both AS4B and AS2B use a single number. + ` router bgp 65536`. + +- dot output: AS4B values are using two numbers separated by a period. + `router bgp 1.1` means that the AS number is 65536. + +- dot+ output: AS2B and AS4B values are using two numbers separated by a + period. `router bgp 0.5` means that the AS number is 5. + +The below option permits forcing the as-notation output: + +.. clicmd:: router bgp ASN as-notation dot|dot+|plain + + The chosen as-notation format will override the BGP ASN output. .. _bgp-route-reflector: diff --git a/lib/asn.c b/lib/asn.c index 7a786866cb..502854ec95 100644 --- a/lib/asn.c +++ b/lib/asn.c @@ -23,18 +23,28 @@ static bool relax_as_zero; +static const struct message asnotation_mode_msg[] = { + {ASNOTATION_PLAIN, "plain"}, + {ASNOTATION_DOT, "dot"}, + {ASNOTATION_DOTPLUS, "dot+"}, + {ASNOTATION_UNDEFINED, "undefined"}, + {0} +}; + /* 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) + const char **next, bool *partial, + enum asnotation_mode *mode) { uint32_t high = 0, low = 0; uint64_t temp_val; const char *p = asstring; bool ret = false; uint32_t digit; + enum asnotation_mode val = ASNOTATION_PLAIN; if (!asstring) goto end; @@ -82,12 +92,13 @@ static bool asn_str2asn_internal(const char *asstring, as_t *asn, *partial = true; goto end; } - if (!asn) { - ret = true; - goto end; - } - *asn = (high << 16) + low; + if (asn) + *asn = (high << 16) + low; ret = true; + if (high == 0) + val = ASNOTATION_DOTPLUS; + else + val = ASNOTATION_DOT; goto end; } /* AS 0 is forbidden */ @@ -102,12 +113,14 @@ static bool asn_str2asn_internal(const char *asstring, as_t *asn, end: if (next) *next = p; + if (mode) + *mode = val; return ret; } bool asn_str2asn(const char *asstring, as_t *asn) { - return asn_str2asn_internal(asstring, asn, NULL, NULL); + return asn_str2asn_internal(asstring, asn, NULL, NULL, NULL); } const char *asn_asn2asplain(as_t asn) @@ -124,7 +137,7 @@ const char *asn_str2asn_parse(const char *asstring, as_t *asn, bool *found_ptr) const char **next = &p; bool found; - found = asn_str2asn_internal(asstring, asn, next, NULL); + found = asn_str2asn_internal(asstring, asn, next, NULL, NULL); if (found_ptr) *found_ptr = found; return *next; @@ -139,7 +152,7 @@ enum match_type asn_str2asn_match(const char *str) { bool found, partial = false; - found = asn_str2asn_internal(str, NULL, NULL, &partial); + found = asn_str2asn_internal(str, NULL, NULL, &partial, NULL); if (found && !partial) return exact_match; @@ -148,3 +161,15 @@ enum match_type asn_str2asn_match(const char *str) return no_match; } + +bool asn_str2asn_notation(const char *asstring, as_t *asn, + enum asnotation_mode *asnotation) +{ + return asn_str2asn_internal(asstring, asn, NULL, NULL, asnotation); +} + +const char *asn_mode2str(enum asnotation_mode asnotation) +{ + return lookup_msg(asnotation_mode_msg, asnotation, + "Unrecognized AS notation mode"); +} diff --git a/lib/asn.h b/lib/asn.h index baaaf4d807..2884aafd54 100644 --- a/lib/asn.h +++ b/lib/asn.h @@ -31,6 +31,13 @@ extern "C" { #define ASN_STRING_MAX_SIZE 12 +enum asnotation_mode { + ASNOTATION_PLAIN = 0, + ASNOTATION_DOT, + ASNOTATION_DOTPLUS, + ASNOTATION_UNDEFINED, +}; + typedef uint32_t as_t; extern bool asn_str2asn(const char *asstring, as_t *asn); @@ -38,6 +45,9 @@ 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); +extern bool asn_str2asn_notation(const char *asstring, as_t *asn, + enum asnotation_mode *asnotation); +extern const char *asn_mode2str(enum asnotation_mode asnotation); /* for test */ extern void asn_relax_as_zero(bool relax); diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index 5a63758d06..6257d2517a 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -952,8 +952,8 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, - NULL) < 0) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_PLAIN) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index 3f6401a2a3..0b2c182e60 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -1094,8 +1094,8 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, - NULL) < 0) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_PLAIN) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c index cf91f5570f..e1263ed375 100644 --- a/tests/bgpd/test_packet.c +++ b/tests/bgpd/test_packet.c @@ -63,8 +63,8 @@ int main(int argc, char *argv[]) vrf_init(NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, - NULL) < 0) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL, + ASNOTATION_PLAIN) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 8a46dfc108..b29e189511 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1688,10 +1688,14 @@ DEFUNSH(VTYSH_ZEBRA, srv6_locator, srv6_locator_cmd, #ifdef HAVE_BGPD DEFUNSH(VTYSH_BGPD, router_bgp, router_bgp_cmd, - "router bgp [ASNUM [ VIEWVRFNAME]]", + "router bgp [ASNUM [ VIEWVRFNAME] [as-notation ]]", ROUTER_STR BGP_STR AS_STR "BGP view\nBGP VRF\n" - "View/VRF name\n") + "View/VRF name\n" + "Force the AS notation output\n" + "use 'AA.BB' format for AS 4 byte values\n" + "use 'AA.BB' format for all AS values\n" + "use plain format for all AS values\n") { vty->node = BGP_NODE; return CMD_SUCCESS; From 17571c4ae7fa294e031b3e614a4fa62f834d0b3d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 14 Nov 2022 15:56:40 +0100 Subject: [PATCH 04/22] bgpd: aspath list format binds on as-notation format Each BGP prefix may have an as-path list attached. A forged string is stored in the BGP attribute and shows the as-path list output. Before this commit, the as-path list output was expressed as a list of AS values in plain format. Now, if a given BGP instance uses a specific asnotation, then the output is changed: new output: router bgp 1.1 asnotation dot ! address-family ipv4 unicast network 10.200.0.0/24 route-map rmap network 10.201.0.0/24 route-map rmap redistribute connected route-map rmap exit-address-family exit ! route-map rmap permit 1 set as-path prepend 1.1 5433.55 264564564 exit ubuntu2004# do show bgp ipv4 BGP table version is 2, local router ID is 10.0.2.15, vrf id 0 Default local pref 100, local AS 1.1 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 4.4.4.4/32 0.0.0.0 0 32768 1.1 5433.55 4036.61268 ? *> 10.0.2.0/24 0.0.0.0 0 32768 1.1 5433.55 4036.61268 ? 10.200.0.0/24 0.0.0.0 0 32768 1.1 5433.55 4036.61268 i 10.201.0.0/24 0.0.0.0 0 32768 1.1 5433.55 4036.61268 i The changes include: - the aspath structure has a new field: asnotation type The ashash list will differentiate 2 aspaths using a different asnotation. - 3 new printf extensions display the as number in the wished format: pASP, pASD, pASE for plain, dot, or dot+ format (extended). Signed-off-by: Philippe Guibert --- bgpd/bgp_aspath.c | 54 +++++++++++++++++++++------------ bgpd/bgp_aspath.h | 12 ++++++-- bgpd/bgp_attr.c | 21 ++++++++----- bgpd/bgp_btoa.c | 3 +- bgpd/bgp_route.c | 7 +++-- bgpd/bgp_routemap.c | 2 +- bgpd/bgp_script.c | 3 +- bgpd/bgpd.c | 7 +++++ bgpd/bgpd.h | 1 + lib/asn.c | 64 +++++++++++++++++++++++++++++++++++++++ lib/asn.h | 15 +++++++++ tests/bgpd/test_aspath.c | 6 ++-- tests/lib/test_printfrr.c | 9 ++++++ 13 files changed, 166 insertions(+), 38 deletions(-) diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 9f74aa76d4..fdfe494e99 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -302,9 +302,13 @@ static struct assegment *assegment_normalise(struct assegment *head) return head; } -static struct aspath *aspath_new(void) +static struct aspath *aspath_new(enum asnotation_mode asnotation) { - return XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath)); + struct aspath *as; + + as = XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath)); + as->asnotation = asnotation; + return as; } /* Free AS path structure. */ @@ -552,8 +556,10 @@ static void aspath_make_str_count(struct aspath *as, bool make_json) * * This was changed to 10 after the well-known BGP assertion, which * had hit some parts of the Internet in May of 2009. + * plain format : '4294967295 ' : 10 + 1 + * astod format : '65535.65535 ': 11 + 1 */ -#define ASN_STR_LEN (10 + 1) +#define ASN_STR_LEN (11 + 1) str_size = MAX(assegment_count_asns(seg, 0) * ASN_STR_LEN + 2 + 1, ASPATH_STR_DEFAULT_LEN); str_buf = XMALLOC(MTYPE_AS_STR, str_size); @@ -584,7 +590,7 @@ static void aspath_make_str_count(struct aspath *as, bool make_json) /* We might need to increase str_buf, particularly if path has * differing segments types, our initial guesstimate above will - * have been wrong. Need 10 chars for ASN, a separator each and + * have been wrong. Need 11 chars for ASN, a separator each and * potentially two segment delimiters, plus a space between each * segment and trailing zero. * @@ -610,12 +616,11 @@ static void aspath_make_str_count(struct aspath *as, bool make_json) /* write out the ASNs, with their separators, bar the last one*/ for (i = 0; i < seg->length; i++) { if (make_json) - json_object_array_add( - jseg_list, - json_object_new_int64(seg->as[i])); - - len += snprintf(str_buf + len, str_size - len, "%u", - seg->as[i]); + asn_asn2json_array(jseg_list, seg->as[i], + as->asnotation); + len += snprintfrr(str_buf + len, str_size - len, + ASN_FORMAT(as->asnotation), + &seg->as[i]); if (i < (seg->length - 1)) len += snprintf(str_buf + len, str_size - len, @@ -706,6 +711,7 @@ struct aspath *aspath_dup(struct aspath *aspath) new->str = XMALLOC(MTYPE_AS_STR, buflen); new->str_len = aspath->str_len; + new->asnotation = aspath->asnotation; /* copy the string data */ if (aspath->str_len > 0) @@ -733,6 +739,7 @@ static void *aspath_hash_alloc(void *arg) new->str = aspath->str; new->str_len = aspath->str_len; new->json = aspath->json; + new->asnotation = aspath->asnotation; return new; } @@ -840,7 +847,8 @@ static int assegments_parse(struct stream *s, size_t length, On error NULL is returned. */ -struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit) +struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit, + enum asnotation_mode asnotation) { struct aspath as; struct aspath *find; @@ -855,6 +863,7 @@ struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit) return NULL; memset(&as, 0, sizeof(as)); + as.asnotation = asnotation; if (assegments_parse(s, length, &as.segments, use32bit) < 0) return NULL; @@ -1072,7 +1081,7 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2) seg = assegment_append_asns(seg, seg1->as, match); if (!aspath) { - aspath = aspath_new(); + aspath = aspath_new(as1->asnotation); aspath->segments = seg; } else prevseg->next = seg; @@ -1092,7 +1101,7 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2) } if (!aspath) - aspath = aspath_new(); + aspath = aspath_new(as1->asnotation); /* Make as-set using rest of all information. */ from = match; @@ -1536,7 +1545,7 @@ struct aspath *aspath_filter_exclude(struct aspath *source, struct assegment *srcseg, *exclseg, *lastseg; struct aspath *newpath; - newpath = aspath_new(); + newpath = aspath_new(source->asnotation); lastseg = NULL; for (srcseg = source->segments; srcseg; srcseg = srcseg->next) { @@ -1766,7 +1775,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath, newseg = assegment_append_asns(newseg, seg->as, cpasns); if (!newpath) { - newpath = aspath_new(); + newpath = aspath_new(aspath->asnotation); newpath->segments = newseg; } else prevseg->next = newseg; @@ -1895,16 +1904,16 @@ static void aspath_segment_add(struct aspath *as, int type) as->segments = new; } -struct aspath *aspath_empty(void) +struct aspath *aspath_empty(enum asnotation_mode asnotation) { - return aspath_parse(NULL, 0, 1); /* 32Bit ;-) */ + return aspath_parse(NULL, 0, 1, asnotation); /* 32Bit ;-) */ } struct aspath *aspath_empty_get(void) { struct aspath *aspath; - aspath = aspath_new(); + aspath = aspath_new(bgp_get_asnotation(NULL)); aspath_make_str_count(aspath, false); return aspath; } @@ -1988,7 +1997,8 @@ static const char *aspath_gettoken(const char *buf, enum as_token *token, return p; } -struct aspath *aspath_str2aspath(const char *str) +struct aspath *aspath_str2aspath(const char *str, + enum asnotation_mode asnotation) { enum as_token token = as_token_unknown; unsigned short as_type; @@ -1996,7 +2006,7 @@ struct aspath *aspath_str2aspath(const char *str) struct aspath *aspath; int needtype; - aspath = aspath_new(); + aspath = aspath_new(asnotation); /* We start default type as AS_SEQUENCE. */ as_type = AS_SEQUENCE; @@ -2070,6 +2080,10 @@ bool aspath_cmp(const void *arg1, const void *arg2) const struct assegment *seg1 = ((const struct aspath *)arg1)->segments; const struct assegment *seg2 = ((const struct aspath *)arg2)->segments; + if (((const struct aspath *)arg1)->asnotation != + ((const struct aspath *)arg2)->asnotation) + return false; + while (seg1 || seg2) { int i; if ((!seg1 && seg2) || (seg1 && !seg2)) diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index a814d73ddd..e0cadef5e0 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -72,6 +72,9 @@ struct aspath { and AS path regular expression match. */ char *str; unsigned short str_len; + + /* AS notation used by string expression of AS path */ + enum asnotation_mode asnotation; }; #define ASPATH_STR_DEFAULT_LEN 32 @@ -80,7 +83,9 @@ struct aspath { extern void aspath_init(void); extern void aspath_finish(void); extern struct aspath *aspath_parse(struct stream *s, size_t length, - int use32bit); + int use32bit, + enum asnotation_mode asnotation); + extern struct aspath *aspath_dup(struct aspath *aspath); extern struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2); extern struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2); @@ -96,9 +101,10 @@ extern bool aspath_cmp_left(const struct aspath *aspath1, extern bool aspath_cmp_left_confed(const struct aspath *as1, const struct aspath *as2); extern struct aspath *aspath_delete_confed_seq(struct aspath *aspath); -extern struct aspath *aspath_empty(void); +extern struct aspath *aspath_empty(enum asnotation_mode asnotation); extern struct aspath *aspath_empty_get(void); -extern struct aspath *aspath_str2aspath(const char *str); +extern struct aspath *aspath_str2aspath(const char *str, + enum asnotation_mode asnotation); extern void aspath_str_update(struct aspath *as, bool make_json); extern void aspath_free(struct aspath *aspath); extern struct aspath *aspath_intern(struct aspath *aspath); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 392b558805..eb1b208d9b 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1069,7 +1069,7 @@ struct attr *bgp_attr_default_set(struct attr *attr, struct bgp *bgp, attr->origin = origin; attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGIN); - attr->aspath = aspath_empty(); + attr->aspath = aspath_empty(bgp->asnotation); attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH); attr->weight = BGP_ATTR_DEFAULT_WEIGHT; attr->tag = 0; @@ -1107,7 +1107,7 @@ struct attr *bgp_attr_aggregate_intern( if (aspath) attr.aspath = aspath_intern(aspath); else - attr.aspath = aspath_empty(); + attr.aspath = aspath_empty(bgp->asnotation); attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH); /* Next hop attribute. */ @@ -1605,15 +1605,19 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; struct peer *const peer = args->peer; const bgp_size_t length = args->length; + enum asnotation_mode asnotation; + asnotation = bgp_get_asnotation( + args->peer && args->peer->bgp ? args->peer->bgp : NULL); /* * peer with AS4 => will get 4Byte ASnums * otherwise, will get 16 Bit */ - attr->aspath = aspath_parse( - peer->curr, length, - CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) - && CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV)); + attr->aspath = + aspath_parse(peer->curr, length, + CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) && + CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV), + asnotation); /* In case of IBGP, length will be zero. */ if (!attr->aspath) { @@ -1705,8 +1709,11 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args, struct peer *const peer = args->peer; struct attr *const attr = args->attr; const bgp_size_t length = args->length; + enum asnotation_mode asnotation; - *as4_path = aspath_parse(peer->curr, length, 1); + asnotation = bgp_get_asnotation(peer->bgp); + + *as4_path = aspath_parse(peer->curr, length, 1, asnotation); /* In case of IBGP, length will be zero. */ if (!*as4_path) { diff --git a/bgpd/bgp_btoa.c b/bgpd/bgp_btoa.c index aa14d99f18..af944593b6 100644 --- a/bgpd/bgp_btoa.c +++ b/bgpd/bgp_btoa.c @@ -101,7 +101,8 @@ static void attr_parse(struct stream *s, uint16_t len) case BGP_ATTR_AS_PATH: { struct aspath *aspath; - aspath = aspath_parse(s, length, 1); + aspath = aspath_parse(s, length, 1, + bgp_get_asnotation(NULL)); printf("ASPATH: %s\n", aspath->str); aspath_free(aspath); } break; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 23e6195d34..32de9e3024 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -7246,7 +7246,7 @@ static bool aggr_suppress_map_test(struct bgp *bgp, return false; /* Call route map matching and return result. */ - attr.aspath = aspath_empty(); + attr.aspath = aspath_empty(bgp->asnotation); rmap_path.peer = bgp->peer_self; rmap_path.attr = &attr; @@ -7340,9 +7340,12 @@ static bool bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin, struct lcommunity *lcomm) { static struct aspath *ae = NULL; + enum asnotation_mode asnotation; + + asnotation = bgp_get_asnotation(NULL); if (!ae) - ae = aspath_empty(); + ae = aspath_empty(asnotation); if (!pi) return false; diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index c9da71c6de..87e532efbe 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -228,7 +228,7 @@ static void *route_aspath_compile(const char *arg) { struct aspath *aspath; - aspath = aspath_str2aspath(arg); + aspath = aspath_str2aspath(arg, bgp_get_asnotation(NULL)); if (!aspath) return NULL; return aspath; diff --git a/bgpd/bgp_script.c b/bgpd/bgp_script.c index bf3e612bfd..a6004fc724 100644 --- a/bgpd/bgp_script.c +++ b/bgpd/bgp_script.c @@ -163,7 +163,8 @@ void lua_decode_attr(lua_State *L, int idx, struct attr *attr) attr->nh_ifindex = lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, idx, "aspath"); - attr->aspath = aspath_str2aspath(lua_tostring(L, -1)); + attr->aspath = aspath_str2aspath(lua_tostring(L, -1), + bgp_get_asnotation(NULL)); lua_pop(L, 1); lua_getfield(L, idx, "localpref"); attr->local_pref = lua_tointeger(L, -1); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 565cbccee4..6b8c17da8b 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2060,6 +2060,13 @@ const char *bgp_get_name_by_role(uint8_t role) return "unknown"; } +enum asnotation_mode bgp_get_asnotation(struct bgp *bgp) +{ + if (!bgp) + return ASNOTATION_PLAIN; + return bgp->asnotation; +} + static void peer_group2peer_config_copy_af(struct peer_group *group, struct peer *peer, afi_t afi, safi_t safi) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 634c4cf1d1..aa53e7c30d 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -2350,6 +2350,7 @@ extern void peer_tx_shutdown_message_unset(struct peer *); extern void bgp_route_map_update_timer(struct thread *thread); extern const char *bgp_get_name_by_role(uint8_t role); +extern enum asnotation_mode bgp_get_asnotation(struct bgp *bgp); extern void bgp_route_map_terminate(void); diff --git a/lib/asn.c b/lib/asn.c index 502854ec95..4c37cca1f6 100644 --- a/lib/asn.c +++ b/lib/asn.c @@ -118,6 +118,15 @@ static bool asn_str2asn_internal(const char *asstring, as_t *asn, return ret; } +static void asn_asn2asdot(as_t asn, char *asstring, size_t len) +{ + uint16_t low, high; + + high = (asn >> 16) & 0xffff; + low = asn & 0xffff; + snprintf(asstring, len, "%hu.%hu", high, low); +} + bool asn_str2asn(const char *asstring, as_t *asn) { return asn_str2asn_internal(asstring, asn, NULL, NULL, NULL); @@ -173,3 +182,58 @@ const char *asn_mode2str(enum asnotation_mode asnotation) return lookup_msg(asnotation_mode_msg, asnotation, "Unrecognized AS notation mode"); } + +void asn_asn2json_array(json_object *jseg_list, as_t asn, + enum asnotation_mode asnotation) +{ + static char as_str[ASN_STRING_MAX_SIZE]; + + if ((asnotation == ASNOTATION_PLAIN) || + ((asnotation == ASNOTATION_DOT) && asn < UINT16_MAX)) + json_object_array_add(jseg_list, + json_object_new_int64(asn)); + else { + asn_asn2asdot(asn, as_str, sizeof(as_str)); + json_array_string_add(jseg_list, as_str); + } +} + +static ssize_t printfrr_asnotation(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr, + enum asnotation_mode asnotation) +{ + /* for alignemnt up to 33 chars - %33pASD for instance - */ + char as_str[ASN_STRING_MAX_SIZE*3]; + const as_t *asn; + + if (!ptr) + return bputs(buf, "(null)"); + asn = ptr; + if ((asnotation == ASNOTATION_PLAIN) || + ((asnotation == ASNOTATION_DOT) && *asn < UINT16_MAX)) + snprintf(as_str, sizeof(as_str), "%u", *asn); + else + asn_asn2asdot(*asn, as_str, sizeof(as_str)); + return bputs(buf, as_str); +} + +printfrr_ext_autoreg_p("ASP", printfrr_asplain); +static ssize_t printfrr_asplain(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_asnotation(buf, ea, ptr, ASNOTATION_PLAIN); +} + +printfrr_ext_autoreg_p("ASD", printfrr_asdot); +static ssize_t printfrr_asdot(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_asnotation(buf, ea, ptr, ASNOTATION_DOT); +} + +printfrr_ext_autoreg_p("ASE", printfrr_asdotplus); +static ssize_t printfrr_asdotplus(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_asnotation(buf, ea, ptr, ASNOTATION_DOTPLUS); +} diff --git a/lib/asn.h b/lib/asn.h index 2884aafd54..902516ef7f 100644 --- a/lib/asn.h +++ b/lib/asn.h @@ -24,6 +24,7 @@ #include "zebra.h" #include "command_match.h" +#include "json.h" #ifdef __cplusplus extern "C" { @@ -48,6 +49,20 @@ extern enum match_type asn_str2asn_match(const char *str); extern bool asn_str2asn_notation(const char *asstring, as_t *asn, enum asnotation_mode *asnotation); extern const char *asn_mode2str(enum asnotation_mode asnotation); +void asn_asn2json_array(json_object *jseg_list, as_t asn, + enum asnotation_mode asnotation); +/* display AS in appropriate format */ +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pASP" (as_t *) +#pragma FRR printfrr_ext "%pASD" (as_t *) +#pragma FRR printfrr_ext "%pASE" (as_t *) +#endif + +#define ASN_FORMAT(mode) \ + ((mode == ASNOTATION_DOT) ? "%pASD" : \ + ((mode == ASNOTATION_DOTPLUS) ? "%pASE" : \ + "%pASP")) + /* for test */ extern void asn_relax_as_zero(bool relax); diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c index 0f6d5b023d..8c173ea669 100644 --- a/tests/bgpd/test_aspath.c +++ b/tests/bgpd/test_aspath.c @@ -880,7 +880,7 @@ static struct aspath *make_aspath(const uint8_t *data, size_t len, int use32bit) s = stream_new(len); stream_put(s, data, len); } - as = aspath_parse(s, len, use32bit); + as = aspath_parse(s, len, use32bit, ASNOTATION_PLAIN); if (s) stream_free(s); @@ -925,7 +925,7 @@ static int validate(struct aspath *as, const struct test_spec *sp) as4 = make_aspath(STREAM_DATA(s), bytes4, 1); asn_relax_as_zero(true); - asstr = aspath_str2aspath(sp->shouldbe); + asstr = aspath_str2aspath(sp->shouldbe, ASNOTATION_PLAIN); asn_relax_as_zero(false); asconfeddel = aspath_delete_confed_seq(aspath_dup(asinout)); @@ -1103,7 +1103,7 @@ static void empty_prepend_test(struct test_segment *t) printf("empty prepend %s: %s\n", t->name, t->desc); asp1 = make_aspath(t->asdata, t->len, 0); - asp2 = aspath_empty(); + asp2 = aspath_empty(ASNOTATION_PLAIN); ascratch = aspath_dup(asp2); aspath_unintern(&asp2); diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c index 59d08ae82b..f9755512a5 100644 --- a/tests/lib/test_printfrr.c +++ b/tests/lib/test_printfrr.c @@ -25,6 +25,7 @@ #include "lib/memory.h" #include "lib/prefix.h" #include "lib/nexthop.h" +#include "lib/asn.h" static int errors; @@ -158,6 +159,7 @@ int main(int argc, char **argv) struct in_addr ip; char *p; char buf[256]; + as_t asn; printcmp("%d %u %d %u", 123, 123, -456, -456); printcmp("%lld %llu %lld %llu", 123LL, 123LL, -456LL, -456LL); @@ -405,6 +407,13 @@ int main(int argc, char **argv) printchk("-00:09", "%pTSIm", &ts); printchk("--:--", "%pTVImx", &tv); printchk("--:--", "%pTTImx", &tt); + /* ASN checks */ + asn = 65536; + printchk("1.0", "%pASD", &asn); + asn = 65400; + printchk("65400", "%pASP", &asn); + printchk("0.65400", "%pASE", &asn); + printchk("65400", "%pASD", &asn); return !!errors; } From e84c7c12f224f27ae430f2929a9fd121b133c2b3 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 18 Nov 2022 13:49:53 +0100 Subject: [PATCH 05/22] bgpd: modify bgp as number output A json AS number API is created in order to output a given AS number. In order to keep backward compatibility, if the as-notation uses a number, then the json is encoded as an integer, otherwise the encoding will be a string. For what is not relevant to running-configuration, the as-notation mode is the one used for the BGP instance. Also, the vty completion gets the configured 'as_pretty' string value, when an user wants to get the available BGP instances. Signed-off-by: Philippe Guibert --- bgpd/bgp_evpn_vty.c | 6 +++--- bgpd/bgp_route.c | 20 ++++++++++++++++---- bgpd/bgp_vty.c | 7 ++++--- bgpd/bgpd.c | 4 ++-- lib/asn.c | 14 ++++++++++++++ lib/asn.h | 2 ++ 6 files changed, 41 insertions(+), 12 deletions(-) diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 6b63c6e3aa..067ad525c2 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -1345,9 +1345,9 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, json, "defaultLocPrf", bgp->default_local_pref); - json_object_int_add( - json, "localAS", - bgp->as); + asn_asn2json(json, "localAS", + bgp->as, + bgp->asnotation); } else { if (option == SHOW_DISPLAY_TAGS) vty_out(vty, diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 32de9e3024..9d9422b362 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -11190,17 +11190,26 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, vty_out(vty, "{\n"); *json_header_depth = 2; } - vty_out(vty, " \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64 ",\n \"routerId\": \"%pI4\",\n \"defaultLocPrf\": %u,\n" - " \"localAS\": %u,\n \"routes\": { ", + " \"localAS\": ", bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id, bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT ? VRF_DEFAULT_NAME : bgp->name, table->version, &bgp->router_id, - bgp->default_local_pref, bgp->as); + bgp->default_local_pref); + if ((bgp->asnotation == ASNOTATION_PLAIN) || + ((bgp->asnotation == ASNOTATION_DOT) && + (bgp->as < UINT16_MAX))) + vty_out(vty, "%u", bgp->as); + else { + vty_out(vty, "\""); + vty_out(vty, ASN_FORMAT(bgp->asnotation), &bgp->as); + vty_out(vty, "\""); + } + vty_out(vty, ",\n \"routes\": { "); if (rd) { vty_out(vty, " \"routeDistinguishers\" : {"); ++*json_header_depth; @@ -11474,7 +11483,10 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, vty_out(vty, "\n"); vty_out(vty, "Default local pref %u, ", bgp->default_local_pref); - vty_out(vty, "local AS %u\n", bgp->as); + vty_out(vty, "local AS "); + vty_out(vty, ASN_FORMAT(bgp->asnotation), + &bgp->as); + vty_out(vty, "\n"); if (!detail_routes) { vty_out(vty, BGP_SHOW_SCODE_HEADER); vty_out(vty, BGP_SHOW_NCODE_HEADER); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index a1fda80ad9..29fc22655e 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -11161,7 +11161,8 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_string_addf(json, "routerId", "%pI4", &bgp->router_id); - json_object_int_add(json, "as", bgp->as); + asn_asn2json(json, "as", bgp->as, + bgp->asnotation); json_object_int_add(json, "vrfId", vrf_id_ui); json_object_string_add( json, "vrfName", @@ -16003,8 +16004,8 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group, group->name, conf->as); } else if (conf->as_type == AS_INTERNAL) { if (json) - json_object_int_add(json_peer_group, "remoteAs", - group->bgp->as); + asn_asn2json(json, "remoteAs", group->bgp->as, + group->bgp->asnotation); else vty_out(vty, "\nBGP peer-group %s, remote AS %s\n", group->name, group->bgp->as_pretty); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 6b8c17da8b..5da156ba65 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -8030,7 +8030,7 @@ static void bgp_instasn_autocomplete(vector comps, struct cmd_token *token) { struct listnode *next, *next2; struct bgp *bgp, *bgp2; - char buf[11]; + char buf[ASN_STRING_MAX_SIZE]; for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) { /* deduplicate */ @@ -8043,7 +8043,7 @@ static void bgp_instasn_autocomplete(vector comps, struct cmd_token *token) if (bgp2 != bgp) continue; - snprintf(buf, sizeof(buf), "%u", bgp->as); + snprintf(buf, sizeof(buf), "%s", bgp->as_pretty); vector_set(comps, XSTRDUP(MTYPE_COMPLETION, buf)); } } diff --git a/lib/asn.c b/lib/asn.c index 4c37cca1f6..27bff1884d 100644 --- a/lib/asn.c +++ b/lib/asn.c @@ -183,6 +183,20 @@ const char *asn_mode2str(enum asnotation_mode asnotation) "Unrecognized AS notation mode"); } +void asn_asn2json(json_object *json, const char *attr, + as_t asn, enum asnotation_mode asnotation) +{ + static char as_str[ASN_STRING_MAX_SIZE]; + + if ((asnotation == ASNOTATION_PLAIN) || + ((asnotation == ASNOTATION_DOT) && asn < UINT16_MAX)) + json_object_int_add(json, attr, asn); + else { + asn_asn2asdot(asn, as_str, sizeof(as_str)); + json_object_string_add(json, attr, as_str); + } +} + void asn_asn2json_array(json_object *jseg_list, as_t asn, enum asnotation_mode asnotation) { diff --git a/lib/asn.h b/lib/asn.h index 902516ef7f..54208bc75c 100644 --- a/lib/asn.h +++ b/lib/asn.h @@ -51,6 +51,8 @@ extern bool asn_str2asn_notation(const char *asstring, as_t *asn, extern const char *asn_mode2str(enum asnotation_mode asnotation); void asn_asn2json_array(json_object *jseg_list, as_t asn, enum asnotation_mode asnotation); +void asn_asn2json(json_object *jseg_list, const char *attr, + as_t asn, enum asnotation_mode asnotation); /* display AS in appropriate format */ #ifdef _FRR_ATTRIBUTE_PRINTFRR #pragma FRR printfrr_ext "%pASP" (as_t *) From de76ed8a0e4c26b21267556a41bbb056791c1c5c Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 18 Nov 2022 16:02:55 +0100 Subject: [PATCH 06/22] bgpd: store the neighbor as identifier as a string This identifier is used to display the peer configuration in the running-config, like it has been configured. The following commands are using a specific string attribute: - neighbor .. remote-as ASN - neighbor .. local-as ASN Signed-off-by: Philippe Guibert --- bgpd/bgp_network.c | 2 +- bgpd/bgp_vty.c | 34 ++++++++++++++++++---------------- bgpd/bgpd.c | 45 ++++++++++++++++++++++++++++++++++----------- bgpd/bgpd.h | 21 ++++++++++++++------- 4 files changed, 67 insertions(+), 35 deletions(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 7186a50711..cc832bd5fe 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -553,7 +553,7 @@ static void bgp_accept(struct thread *thread) peer1->host); peer = peer_create(&su, peer1->conf_if, peer1->bgp, peer1->local_as, - peer1->as, peer1->as_type, NULL, false); + peer1->as, peer1->as_type, NULL, false, NULL); peer_xfer_config(peer, peer1); bgp_peer_gr_flags_update(peer); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 29fc22655e..6a01791444 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -4586,11 +4586,12 @@ static int peer_remote_as_vty(struct vty *vty, const char *peer_str, /* Check if existing interface peer */ peer = peer_lookup_by_conf_if(bgp, peer_str); - ret = peer_remote_as(bgp, NULL, peer_str, &as, as_type); + ret = peer_remote_as(bgp, NULL, peer_str, &as, as_type, as_str); /* if not interface peer, check peer-group settings */ if (ret < 0 && !peer) { - ret = peer_group_remote_as(bgp, peer_str, &as, as_type); + ret = peer_group_remote_as(bgp, peer_str, &as, as_type, + as_str); if (ret < 0) { vty_out(vty, "%% Create the peer-group or interface first\n"); @@ -4604,7 +4605,7 @@ static int peer_remote_as_vty(struct vty *vty, const char *peer_str, "%% Can not configure the local system as neighbor\n"); return CMD_WARNING_CONFIG_FAILED; } - ret = peer_remote_as(bgp, &su, NULL, &as, as_type); + ret = peer_remote_as(bgp, &su, NULL, &as, as_type, as_str); } return bgp_vty_return(vty, ret); @@ -4763,10 +4764,11 @@ static int peer_conf_interface_get(struct vty *vty, const char *conf_if, peer = peer_lookup_by_conf_if(bgp, conf_if); if (peer) { if (as_str) - ret = peer_remote_as(bgp, NULL, conf_if, &as, as_type); + ret = peer_remote_as(bgp, NULL, conf_if, &as, as_type, + as_str); } else { peer = peer_create(NULL, conf_if, bgp, bgp->as, as, as_type, - NULL, true); + NULL, true, as_str); if (!peer) { vty_out(vty, "%% BGP failed to create peer\n"); @@ -5071,7 +5073,7 @@ DEFUN (no_neighbor_interface_peer_group_remote_as, /* look up for neighbor by interface name config. */ peer = peer_lookup_by_conf_if(bgp, argv[idx_word]->arg); if (peer) { - peer_as_change(peer, 0, AS_UNSPECIFIED); + peer_as_change(peer, 0, AS_UNSPECIFIED, NULL); return CMD_SUCCESS; } @@ -5109,7 +5111,7 @@ DEFUN (neighbor_local_as, return CMD_WARNING_CONFIG_FAILED; } - ret = peer_local_as_set(peer, as, 0, 0); + ret = peer_local_as_set(peer, as, 0, 0, argv[idx_number]->arg); return bgp_vty_return(vty, ret); } @@ -5138,7 +5140,7 @@ DEFUN (neighbor_local_as_no_prepend, return CMD_WARNING_CONFIG_FAILED; } - ret = peer_local_as_set(peer, as, 1, 0); + ret = peer_local_as_set(peer, as, 1, 0, argv[idx_number]->arg); return bgp_vty_return(vty, ret); } @@ -5168,7 +5170,7 @@ DEFUN (neighbor_local_as_no_prepend_replace_as, return CMD_WARNING_CONFIG_FAILED; } - ret = peer_local_as_set(peer, as, 1, 1); + ret = peer_local_as_set(peer, as, 1, 1, argv[idx_number]->arg); return bgp_vty_return(vty, ret); } @@ -17302,7 +17304,7 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, vty_out(vty, " peer-group %s", peer->group->name); if_pg_printed = true; } else if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " remote-as %u", peer->as); + vty_out(vty, " remote-as %s", peer->as_pretty); if_ras_printed = true; } else if (peer->as_type == AS_INTERNAL) { vty_out(vty, " remote-as internal"); @@ -17322,8 +17324,8 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) { if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " neighbor %s remote-as %u\n", - addr, peer->as); + vty_out(vty, " neighbor %s remote-as %s\n", + addr, peer->as_pretty); } else if (peer->as_type == AS_INTERNAL) { vty_out(vty, " neighbor %s remote-as internal\n", @@ -17351,8 +17353,8 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, if (!if_ras_printed) { if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " neighbor %s remote-as %u\n", - addr, peer->as); + vty_out(vty, " neighbor %s remote-as %s\n", + addr, peer->as_pretty); } else if (peer->as_type == AS_INTERNAL) { vty_out(vty, " neighbor %s remote-as internal\n", @@ -17367,8 +17369,8 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, /* local-as */ if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) { - vty_out(vty, " neighbor %s local-as %u", addr, - peer->change_local_as); + vty_out(vty, " neighbor %s local-as %s", addr, + peer->change_local_as_pretty); if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND)) vty_out(vty, " no-prepend"); if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS)) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 5da156ba65..97e55a9fb1 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1185,6 +1185,11 @@ static void peer_free(struct peer *peer) FOREACH_AFI_SAFI (afi, safi) bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE); + if (peer->change_local_as_pretty) + XFREE(MTYPE_BGP, peer->change_local_as_pretty); + if (peer->as_pretty) + XFREE(MTYPE_BGP, peer->as_pretty); + bgp_unlock(peer->bgp); memset(peer, 0, sizeof(struct peer)); @@ -1766,7 +1771,7 @@ void bgp_recalculate_all_bestpaths(struct bgp *bgp) struct peer *peer_create(union sockunion *su, const char *conf_if, struct bgp *bgp, as_t local_as, as_t remote_as, int as_type, struct peer_group *group, - bool config_node) + bool config_node, const char *as_str) { int active; struct peer *peer; @@ -1791,6 +1796,9 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, } peer->local_as = local_as; peer->as = remote_as; + /* internal and external values do not use as_pretty */ + if (as_str && asn_str2asn(as_str, NULL)) + peer->as_pretty = XSTRDUP(MTYPE_BGP, as_str); peer->as_type = as_type; peer->local_id = bgp->router_id; peer->v_holdtime = bgp->default_holdtime; @@ -1894,7 +1902,8 @@ bool bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi) } /* Change peer's AS number. */ -void peer_as_change(struct peer *peer, as_t as, int as_specified) +void peer_as_change(struct peer *peer, as_t as, int as_specified, + const char *as_str) { enum bgp_peer_sort origtype, newtype; @@ -1909,6 +1918,12 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) } origtype = peer_sort_lookup(peer); peer->as = as; + if (as_specified == AS_SPECIFIED && as_str) { + if (peer->as_pretty) + XFREE(MTYPE_BGP, peer->as_pretty); + peer->as_pretty = XSTRDUP(MTYPE_BGP, as_str); + } else if (peer->as_type == AS_UNSPECIFIED && peer->as_pretty) + XFREE(MTYPE_BGP, peer->as_pretty); peer->as_type = as_specified; if (bgp_config_check(peer->bgp, BGP_CONFIG_CONFEDERATION) @@ -1966,7 +1981,7 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) /* If peer does not exist, create new one. If peer already exists, set AS number to the peer. */ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if, - as_t *as, int as_type) + as_t *as, int as_type, const char *as_str) { struct peer *peer; as_t local_as; @@ -2020,7 +2035,7 @@ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if, /* Existing peer's AS number change. */ if (((peer->as_type == AS_SPECIFIED) && peer->as != *as) || (peer->as_type != as_type)) - peer_as_change(peer, *as, as_type); + peer_as_change(peer, *as, as_type, as_str); } else { if (conf_if) return BGP_ERR_NO_INTERFACE_CONFIG; @@ -2035,7 +2050,7 @@ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if, local_as = bgp->as; peer_create(su, conf_if, bgp, local_as, *as, as_type, NULL, - true); + true, as_str); } return 0; @@ -2816,7 +2831,7 @@ static void peer_group2peer_config_copy(struct peer_group *group, /* Peer group's remote AS configuration. */ int peer_group_remote_as(struct bgp *bgp, const char *group_name, as_t *as, - int as_type) + int as_type, const char *as_str) { struct peer_group *group; struct peer *peer; @@ -2832,12 +2847,12 @@ int peer_group_remote_as(struct bgp *bgp, const char *group_name, as_t *as, /* When we setup peer-group AS number all peer group member's AS number must be updated to same number. */ - peer_as_change(group->conf, *as, as_type); + peer_as_change(group->conf, *as, as_type, as_str); for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { if (((peer->as_type == AS_SPECIFIED) && peer->as != *as) || (peer->as_type != as_type)) - peer_as_change(peer, *as, as_type); + peer_as_change(peer, *as, as_type, as_str); } return 0; @@ -3144,7 +3159,7 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, } peer = peer_create(su, NULL, bgp, bgp->as, group->conf->as, - group->conf->as_type, group, true); + group->conf->as_type, group, true, NULL); peer = peer_lock(peer); /* group->peer list reference */ listnode_add(group->peer, peer); @@ -4051,7 +4066,7 @@ struct peer *peer_create_bind_dynamic_neighbor(struct bgp *bgp, /* Create peer first; we've already checked group config is valid. */ peer = peer_create(su, NULL, bgp, bgp->as, group->conf->as, - group->conf->as_type, group, true); + group->conf->as_type, group, true, NULL); if (!peer) return NULL; @@ -6234,7 +6249,7 @@ int peer_allowas_in_unset(struct peer *peer, afi_t afi, safi_t safi) } int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, - bool replace_as) + bool replace_as, const char *as_str) { bool old_no_prepend, old_replace_as; struct bgp *bgp = peer->bgp; @@ -6259,6 +6274,9 @@ int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, && old_replace_as == replace_as) return 0; peer->change_local_as = as; + if (as_str) + peer->change_local_as_pretty = XSTRDUP(MTYPE_BGP, as_str); + (void)peer_sort(peer); /* Check if handling a regular peer. */ @@ -6292,6 +6310,9 @@ int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, COND_FLAG(member->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS, replace_as); member->change_local_as = as; + if (as_str) + member->change_local_as_pretty = + XSTRDUP(MTYPE_BGP, as_str); } return 0; @@ -6317,6 +6338,7 @@ int peer_local_as_unset(struct peer *peer) peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND); peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS); peer->change_local_as = 0; + XFREE(MTYPE_BGP, peer->change_local_as_pretty); } /* Check if handling a regular peer. */ @@ -6347,6 +6369,7 @@ int peer_local_as_unset(struct peer *peer) UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); member->change_local_as = 0; + XFREE(MTYPE_BGP, member->change_local_as_pretty); /* Send notification or stop peer depending on state. */ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->status)) { diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index aa53e7c30d..995c92d225 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1124,6 +1124,8 @@ struct peer { /* Peer's remote AS number. */ int as_type; as_t as; + /* for vty as format */ + char *as_pretty; /* Peer's local AS number. */ as_t local_as; @@ -1132,6 +1134,8 @@ struct peer { /* Peer's Change local AS number. */ as_t change_local_as; + /* for vty as format */ + char *change_local_as_pretty; /* Remote router ID. */ struct in_addr remote_id; @@ -2138,7 +2142,7 @@ extern void bgp_recalculate_all_bestpaths(struct bgp *bgp); extern struct peer *peer_create(union sockunion *su, const char *conf_if, struct bgp *bgp, as_t local_as, as_t remote_as, int as_type, struct peer_group *group, - bool config_node); + bool config_node, const char *as_str); extern struct peer *peer_create_accept(struct bgp *); extern void peer_xfer_config(struct peer *dst, struct peer *src); extern char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json, @@ -2208,10 +2212,13 @@ extern void bgp_listen_limit_unset(struct bgp *bgp); extern bool bgp_update_delay_active(struct bgp *); extern bool bgp_update_delay_configured(struct bgp *); extern bool bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi); -extern void peer_as_change(struct peer *, as_t, int); -extern int peer_remote_as(struct bgp *, union sockunion *, const char *, as_t *, - int); -extern int peer_group_remote_as(struct bgp *, const char *, as_t *, int); +extern void peer_as_change(struct peer *peer, as_t as, int as_type, + const char *as_str); +extern int peer_remote_as(struct bgp *bgp, union sockunion *su, + const char *conf_if, as_t *as, int as_type, + const char *as_str); +extern int peer_group_remote_as(struct bgp *bgp, const char *peer_str, as_t *as, + int as_type, const char *as_str); extern int peer_delete(struct peer *peer); extern void peer_notify_unconfig(struct peer *peer); extern int peer_group_delete(struct peer_group *); @@ -2290,8 +2297,8 @@ extern int peer_distribute_unset(struct peer *, afi_t, safi_t, int); extern int peer_allowas_in_set(struct peer *, afi_t, safi_t, int, int); extern int peer_allowas_in_unset(struct peer *, afi_t, safi_t); -extern int peer_local_as_set(struct peer *, as_t, bool no_prepend, - bool replace_as); +extern int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, + bool replace_as, const char *as_str); extern int peer_local_as_unset(struct peer *); extern int peer_prefix_list_set(struct peer *, afi_t, safi_t, int, From 7e14d0fab23955f345ecef9893238845b4be30fe Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 18 Nov 2022 16:56:09 +0100 Subject: [PATCH 07/22] bgpd: store the confederation as identifier as a string The confederation peers as and the confederation identifier as are stored as a string to preserve the output in the running configuration. Signed-off-by: Philippe Guibert --- bgpd/bgp_vty.c | 11 ++++++----- bgpd/bgpd.c | 40 +++++++++++++++++++++++++++------------- bgpd/bgpd.h | 14 +++++++++++--- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 6a01791444..87d5d27385 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -2003,7 +2003,7 @@ DEFUN (bgp_confederation_identifier, return CMD_WARNING_CONFIG_FAILED; } - bgp_confederation_id_set(bgp, as); + bgp_confederation_id_set(bgp, as, argv[idx_number]->arg); return CMD_SUCCESS; } @@ -2043,7 +2043,7 @@ DEFUN (bgp_confederation_peers, continue; } - bgp_confederation_peers_add(bgp, as); + bgp_confederation_peers_add(bgp, as, argv[i]->arg); } return CMD_SUCCESS; } @@ -18163,8 +18163,8 @@ int bgp_config_write(struct vty *vty) /* Confederation identifier*/ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) - vty_out(vty, " bgp confederation identifier %u\n", - bgp->confed_id); + vty_out(vty, " bgp confederation identifier %s\n", + bgp->confed_id_pretty); /* Confederation peer */ if (bgp->confed_peers_cnt > 0) { @@ -18173,7 +18173,8 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " bgp confederation peers"); for (i = 0; i < bgp->confed_peers_cnt; i++) - vty_out(vty, " %u", bgp->confed_peers[i]); + vty_out(vty, " %s", + bgp->confed_peers[i].as_pretty); vty_out(vty, "\n"); } diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 97e55a9fb1..69557507d0 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -568,7 +568,7 @@ void bgp_tcp_keepalive_unset(struct bgp *bgp) } /* BGP confederation configuration. */ -void bgp_confederation_id_set(struct bgp *bgp, as_t as) +void bgp_confederation_id_set(struct bgp *bgp, as_t as, const char *as_str) { struct peer *peer; struct listnode *node, *nnode; @@ -580,6 +580,9 @@ void bgp_confederation_id_set(struct bgp *bgp, as_t as) /* Remember - were we doing confederation before? */ already_confed = bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION); bgp->confed_id = as; + if (bgp->confed_id_pretty) + XFREE(MTYPE_BGP, bgp->confed_id_pretty); + bgp->confed_id_pretty = XSTRDUP(MTYPE_BGP, as_str); bgp_config_set(bgp, BGP_CONFIG_CONFEDERATION); /* If we were doing confederation already, this is just an external @@ -632,6 +635,7 @@ void bgp_confederation_id_unset(struct bgp *bgp) struct listnode *node, *nnode; bgp->confed_id = 0; + XFREE(MTYPE_BGP, bgp->confed_id_pretty); bgp_config_unset(bgp, BGP_CONFIG_CONFEDERATION); for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { @@ -659,14 +663,14 @@ bool bgp_confederation_peers_check(struct bgp *bgp, as_t as) return false; for (i = 0; i < bgp->confed_peers_cnt; i++) - if (bgp->confed_peers[i] == as) + if (bgp->confed_peers[i].as == as) return true; return false; } /* Add an AS to the confederation set. */ -void bgp_confederation_peers_add(struct bgp *bgp, as_t as) +void bgp_confederation_peers_add(struct bgp *bgp, as_t as, const char *as_str) { struct peer *peer; struct listnode *node, *nnode; @@ -677,11 +681,13 @@ void bgp_confederation_peers_add(struct bgp *bgp, as_t as) if (bgp_confederation_peers_check(bgp, as)) return; - bgp->confed_peers = - XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers, - (bgp->confed_peers_cnt + 1) * sizeof(as_t)); + bgp->confed_peers = XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers, + (bgp->confed_peers_cnt + 1) * + sizeof(struct as_confed)); - bgp->confed_peers[bgp->confed_peers_cnt] = as; + bgp->confed_peers[bgp->confed_peers_cnt].as = as; + bgp->confed_peers[bgp->confed_peers_cnt].as_pretty = + XSTRDUP(MTYPE_BGP, as_str); bgp->confed_peers_cnt++; if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION)) { @@ -718,9 +724,15 @@ void bgp_confederation_peers_remove(struct bgp *bgp, as_t as) return; for (i = 0; i < bgp->confed_peers_cnt; i++) - if (bgp->confed_peers[i] == as) - for (j = i + 1; j < bgp->confed_peers_cnt; j++) - bgp->confed_peers[j - 1] = bgp->confed_peers[j]; + if (bgp->confed_peers[i].as == as) { + XFREE(MTYPE_BGP, bgp->confed_peers[i].as_pretty); + for (j = i + 1; j < bgp->confed_peers_cnt; j++) { + bgp->confed_peers[j - 1].as = + bgp->confed_peers[j].as; + bgp->confed_peers[j - 1].as_pretty = + bgp->confed_peers[j].as_pretty; + } + } bgp->confed_peers_cnt--; @@ -729,9 +741,9 @@ void bgp_confederation_peers_remove(struct bgp *bgp, as_t as) XFREE(MTYPE_BGP_CONFED_LIST, bgp->confed_peers); bgp->confed_peers = NULL; } else - bgp->confed_peers = - XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers, - bgp->confed_peers_cnt * sizeof(as_t)); + bgp->confed_peers = XREALLOC( + MTYPE_BGP_CONFED_LIST, bgp->confed_peers, + bgp->confed_peers_cnt * sizeof(struct as_confed)); /* Now reset any peer who's remote AS has just been removed from the CONFED */ @@ -3961,6 +3973,8 @@ void bgp_free(struct bgp *bgp) ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]); } + bgp_confederation_id_unset(bgp); + XFREE(MTYPE_BGP, bgp->as_pretty); XFREE(MTYPE_BGP, bgp->name); XFREE(MTYPE_BGP, bgp->name_pretty); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 995c92d225..57685390c8 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -339,6 +339,11 @@ struct bgp_srv6_function { char locator_name[SRV6_LOCNAME_SIZE]; }; +struct as_confed { + as_t as; + char *as_pretty; +}; + /* BGP instance structure. */ struct bgp { /* AS number of this BGP instance. */ @@ -411,7 +416,8 @@ struct bgp { /* BGP confederation information. */ as_t confed_id; - as_t *confed_peers; + char *confed_id_pretty; + struct as_confed *confed_peers; int confed_peers_cnt; struct thread @@ -2187,11 +2193,13 @@ extern void bgp_suppress_fib_pending_set(struct bgp *bgp, bool set); extern void bgp_cluster_id_set(struct bgp *bgp, struct in_addr *cluster_id); extern void bgp_cluster_id_unset(struct bgp *bgp); -extern void bgp_confederation_id_set(struct bgp *bgp, as_t as); +extern void bgp_confederation_id_set(struct bgp *bgp, as_t as, + const char *as_str); extern void bgp_confederation_id_unset(struct bgp *bgp); extern bool bgp_confederation_peers_check(struct bgp *, as_t); -extern void bgp_confederation_peers_add(struct bgp *bgp, as_t as); +extern void bgp_confederation_peers_add(struct bgp *bgp, as_t as, + const char *as_str); extern void bgp_confederation_peers_remove(struct bgp *bgp, as_t as); extern void bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime, From 44a4d55ed803c92a92c53491b57e26b354e8cbfc Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 18 Nov 2022 16:58:14 +0100 Subject: [PATCH 08/22] bgpd: the neighbor as are displayed with as-notation mode The as-notation mode of the BGP instance will impact the way the neighbor AS information is dumped in the show commands. Signed-off-by: Philippe Guibert --- bgpd/bgp_vty.c | 82 ++++++++++++++++++++++++++++++-------------------- lib/asn.h | 4 +++ 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 87d5d27385..36b1707a1e 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -11409,12 +11409,13 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_string_add(json_peer, "domainname", peer->domainname); - json_object_int_add(json_peer, "remoteAs", peer->as); - json_object_int_add( - json_peer, "localAs", - peer->change_local_as - ? peer->change_local_as - : peer->local_as); + asn_asn2json(json_peer, "remoteAs", peer->as, + bgp->asnotation); + asn_asn2json(json_peer, "localAs", + peer->change_local_as + ? peer->change_local_as + : peer->local_as, + bgp->asnotation); json_object_int_add(json_peer, "version", 4); json_object_int_add(json_peer, "msgRcvd", PEER_TOTAL_RX(peer)); @@ -11594,14 +11595,19 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, &peer->ibuf->count, memory_order_relaxed); - if (show_wide) + vty_out(vty, "4 "); + vty_out(vty, ASN_FORMAT_SPACE(bgp->asnotation), + &peer->as); + if (show_wide) { vty_out(vty, - "4 %10u %10u %9u %9u %8" PRIu64 - " %4zu %4zu %8s", - peer->as, + ASN_FORMAT_SPACE( + bgp->asnotation), peer->change_local_as - ? peer->change_local_as - : peer->local_as, + ? &peer->change_local_as + : &peer->local_as); + vty_out(vty, + " %9u %9u %8" PRIu64 + " %4zu %4zu %8s", PEER_TOTAL_RX(peer), PEER_TOTAL_TX(peer), peer->version[afi][safi], @@ -11610,10 +11616,11 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, timebuf, BGP_UPTIME_LEN, 0, NULL)); - else - vty_out(vty, "4 %10u %9u %9u %8" PRIu64 - " %4zu %4zu %8s", - peer->as, PEER_TOTAL_RX(peer), + } else { + vty_out(vty, + " %9u %9u %8" PRIu64 + " %4zu %4zu %8s", + PEER_TOTAL_RX(peer), PEER_TOTAL_TX(peer), peer->version[afi][safi], inq_count, outq_count, @@ -11621,7 +11628,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, timebuf, BGP_UPTIME_LEN, 0, NULL)); - + } if (peer_established(peer)) { if (peer->afc_recv[afi][safi]) { if (CHECK_FLAG( @@ -13107,13 +13114,14 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object_string_addf(json_neigh, "bgpNeighborAddr", "%pSU", &p->su); - json_object_int_add(json_neigh, "remoteAs", p->as); + asn_asn2json(json_neigh, "remoteAs", p->as, bgp->asnotation); if (p->change_local_as) - json_object_int_add(json_neigh, "localAs", - p->change_local_as); + asn_asn2json(json_neigh, "localAs", p->change_local_as, + bgp->asnotation); else - json_object_int_add(json_neigh, "localAs", p->local_as); + asn_asn2json(json_neigh, "localAs", p->local_as, + bgp->asnotation); if (CHECK_FLAG(p->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)) json_object_boolean_true_add(json_neigh, @@ -13123,13 +13131,19 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object_boolean_true_add(json_neigh, "localAsReplaceAs"); } else { - if ((p->as_type == AS_SPECIFIED) || (p->as_type == AS_EXTERNAL) - || (p->as_type == AS_INTERNAL)) - vty_out(vty, "remote AS %u, ", p->as); - else + if ((p->as_type == AS_SPECIFIED) || + (p->as_type == AS_EXTERNAL) || + (p->as_type == AS_INTERNAL)) { + vty_out(vty, "remote AS "); + vty_out(vty, ASN_FORMAT(bgp->asnotation), &p->as); + vty_out(vty, ", "); + } else vty_out(vty, "remote AS Unspecified, "); - vty_out(vty, "local AS %u%s%s, ", - p->change_local_as ? p->change_local_as : p->local_as, + vty_out(vty, "local AS "); + vty_out(vty, ASN_FORMAT(bgp->asnotation), + p->change_local_as ? &p->change_local_as + : &p->local_as); + vty_out(vty, "%s%s, ", CHECK_FLAG(p->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ? " no-prepend" : "", @@ -15999,11 +16013,15 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group, if (conf->as_type == AS_SPECIFIED || conf->as_type == AS_EXTERNAL) { if (json) - json_object_int_add(json_peer_group, "remoteAs", - conf->as); - else - vty_out(vty, "\nBGP peer-group %s, remote AS %u\n", - group->name, conf->as); + asn_asn2json(json_peer_group, "remoteAs", conf->as, + bgp_get_asnotation(conf->bgp)); + else { + vty_out(vty, "\nBGP peer-group %s, remote AS ", + group->name); + vty_out(vty, ASN_FORMAT(bgp_get_asnotation(conf->bgp)), + &conf->as); + vty_out(vty, "\n"); + } } else if (conf->as_type == AS_INTERNAL) { if (json) asn_asn2json(json, "remoteAs", group->bgp->as, diff --git a/lib/asn.h b/lib/asn.h index 54208bc75c..2617798d9d 100644 --- a/lib/asn.h +++ b/lib/asn.h @@ -64,6 +64,10 @@ void asn_asn2json(json_object *jseg_list, const char *attr, ((mode == ASNOTATION_DOT) ? "%pASD" : \ ((mode == ASNOTATION_DOTPLUS) ? "%pASE" : \ "%pASP")) +#define ASN_FORMAT_SPACE(mode) \ + ((mode == ASNOTATION_DOT) ? "%10pASD" : \ + ((mode == ASNOTATION_DOTPLUS) ? "%10pASE" : \ + "%10pASP")) /* for test */ extern void asn_relax_as_zero(bool relax); From 4a8cd6ad7f3a9b1eabfbc1dd42b8f1b8ba9d3b97 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 22 Nov 2022 09:57:10 +0100 Subject: [PATCH 09/22] bgpd: support for as notation format for route distinguisher RD may be built based on an AS number. Like for the AS, the RD may use the AS notation. The two below examples can illustrate: RD 1.1:20 stands for an AS4B:NN RD with AS4B=65536 in dot format. RD 0.1:20 stands for an AS2B:NNNN RD with AS2B=0.1 in dot+ format. This commit adds the asnotation mode to prefix_rd2str() API so as to pick up the relevant display. Two new printfrr extensions are available to display the RD with the two above display methods. - The pRDD extension stands for dot asnotation format - The pRDE extension stands for dot+ asnotation format. - The pRD extension has been renamed to pRDP extension The code is changed each time '%pRD' printf extension is called. Possibly, the asnotation may change the output, then a macro defines the asnotation mode to use. A side effect of forging the mode to use is that the string could not be concatenated with other strings in vty_out and snprintfrr. Those functions have been called multiple times. When zlog_debug needs to display the RD with some other string, the prefix_rd2str() old API is used instead of the printf extension. Some code has been kept untouched: - code related to running-config. Actually, wherever an RD is displayed, its configured name should be dumped. - bgp rfapi code - bgp evpn multihoming code (partially done), since the logic is missing to get the asnotation of 'struct bgp_evpn_es'. Signed-off-by: Philippe Guibert --- bgpd/bgp_debug.c | 11 +++++-- bgpd/bgp_evpn_mh.c | 25 +++++++++----- bgpd/bgp_evpn_vty.c | 54 +++++++++++++++++++++--------- bgpd/bgp_mplsvpn.c | 20 +++++++----- bgpd/bgp_mplsvpn_snmp.c | 6 ++-- bgpd/bgp_nexthop.c | 17 ++++++---- bgpd/bgp_nht.c | 16 ++++++--- bgpd/bgp_rd.c | 67 ++++++++++++++++++++++++-------------- bgpd/bgp_rd.h | 16 ++++++++- bgpd/bgp_route.c | 44 +++++++++++++++---------- bgpd/bgp_vty.c | 4 ++- bgpd/rfapi/bgp_rfapi_cfg.c | 6 ++-- bgpd/rfapi/rfapi.c | 6 ++-- bgpd/rfapi/rfapi_import.c | 12 ++++--- bgpd/rfapi/rfapi_rib.c | 6 ++-- bgpd/rfapi/rfapi_vty.c | 2 +- lib/asn.c | 17 +++++++--- lib/asn.h | 2 ++ lib/prefix.h | 5 ++- 19 files changed, 223 insertions(+), 113 deletions(-) diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index f01c3e4f35..91156b9ed0 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -2651,6 +2651,7 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, char tag_buf[30]; char overlay_index_buf[INET6_ADDRSTRLEN + 14]; const struct prefix_evpn *evp; + int len = 0; /* ' with addpath ID ' 17 * max strlen of uint32 + 10 @@ -2704,11 +2705,15 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, } } - if (prd) - snprintfrr(str, size, "RD %pRD %pFX%s%s%s %s %s", prd, pu.p, + if (prd) { + len += snprintfrr(str + len, size - len, "RD "); + len += snprintfrr(str + len, size - len, + BGP_RD_AS_FORMAT(bgp_get_asnotation(NULL)), + prd); + snprintfrr(str + len, size - len, " %pFX%s%s%s %s %s", pu.p, overlay_index_buf, tag_buf, pathid_buf, afi2str(afi), safi2str(safi)); - else if (safi == SAFI_FLOWSPEC) { + } else if (safi == SAFI_FLOWSPEC) { char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX]; const struct prefix_fs *fs = pu.fs; diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 7ad0816631..dd40a072f9 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -2406,7 +2406,8 @@ static void bgp_evpn_es_json_frag_fill(json_object *json_frags, for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) { json_frag = json_object_new_object(); - json_object_string_addf(json_frag, "rd", "%pRD", &es_frag->prd); + json_object_string_addf(json_frag, "rd", "%pRDP", + &es_frag->prd); json_object_int_add(json_frag, "eviCount", listcount(es_frag->es_evi_frag_list)); @@ -2421,7 +2422,7 @@ static void bgp_evpn_es_frag_show_detail(struct vty *vty, struct bgp_evpn_es_frag *es_frag; for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) { - vty_out(vty, " %pRD EVIs: %d\n", &es_frag->prd, + vty_out(vty, " %pRDP EVIs: %d\n", &es_frag->prd, listcount(es_frag->es_evi_frag_list)); } } @@ -2535,7 +2536,7 @@ static void bgp_evpn_es_show_entry(struct vty *vty, json_object_string_add(json, "esi", es->esi_str); if (es->es_base_frag) - json_object_string_addf(json, "rd", "%pRD", + json_object_string_addf(json, "rd", "%pRDP", &es->es_base_frag->prd); if (es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) { @@ -2573,7 +2574,7 @@ static void bgp_evpn_es_show_entry(struct vty *vty, bgp_evpn_es_vteps_str(vtep_str, es, sizeof(vtep_str)); - vty_out(vty, "%-30s %-5s %-21pRD %-8d %s\n", es->esi_str, + vty_out(vty, "%-30s %-5s %-21pRDP %-8d %s\n", es->esi_str, type_str, &es->es_base_frag->prd, listcount(es->es_evi_list), vtep_str); } @@ -2650,7 +2651,7 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, vty_out(vty, "ESI: %s\n", es->esi_str); vty_out(vty, " Type: %s\n", type_str); - vty_out(vty, " RD: %pRD\n", &es->es_base_frag->prd); + vty_out(vty, " RD: %pRDP\n", &es->es_base_frag->prd); vty_out(vty, " Originator-IP: %pI4\n", &es->originator_ip); if (es->flags & BGP_EVPNES_LOCAL) vty_out(vty, " Local ES DF preference: %u\n", @@ -4009,13 +4010,18 @@ static void bgp_evpn_es_evi_show_entry(struct vty *vty, static void bgp_evpn_es_evi_show_entry_detail(struct vty *vty, struct bgp_evpn_es_evi *es_evi, json_object *json) { + enum asnotation_mode mode; + + mode = bgp_get_asnotation(es_evi->vpn->bgp_vrf); + if (json) { json_object *json_flags; /* Add the "brief" info first */ bgp_evpn_es_evi_show_entry(vty, es_evi, json); if (es_evi->es_frag) - json_object_string_addf(json, "esFragmentRd", "%pRD", + json_object_string_addf(json, "esFragmentRd", + BGP_RD_AS_FORMAT(mode), &es_evi->es_frag->prd); if (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) { json_flags = json_object_new_array(); @@ -4039,9 +4045,12 @@ static void bgp_evpn_es_evi_show_entry_detail(struct vty *vty, vty_out(vty, "VNI: %d ESI: %s\n", es_evi->vpn->vni, es_evi->es->esi_str); vty_out(vty, " Type: %s\n", type_str); - if (es_evi->es_frag) - vty_out(vty, " ES fragment RD: %pRD\n", + if (es_evi->es_frag) { + vty_out(vty, " ES fragment RD: "); + vty_out(vty, BGP_RD_AS_FORMAT(mode), &es_evi->es_frag->prd); + vty_out(vty, "\n"); + } vty_out(vty, " Inconsistencies: %s\n", (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) ? "es-vtep-mismatch":"-"); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 067ad525c2..e8a52cb430 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -387,7 +387,9 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, json_object_int_add(json, "vni", bgp_vrf->l3vni); json_object_string_add(json, "type", "L3"); json_object_string_add(json, "inKernel", "True"); - json_object_string_addf(json, "rd", "%pRD", &bgp_vrf->vrf_prd); + json_object_string_addf(json, "rd", + BGP_RD_AS_FORMAT(bgp_vrf->asnotation), + &bgp_vrf->vrf_prd); json_object_string_addf(json, "originatorIp", "%pI4", &bgp_vrf->originator_ip); json_object_string_add(json, "advertiseGatewayMacip", "n/a"); @@ -411,7 +413,10 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, vty_out(vty, " Type: %s\n", "L3"); vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(bgp_vrf->vrf_id)); - vty_out(vty, " RD: %pRD\n", &bgp_vrf->vrf_prd); + vty_out(vty, " RD: "); + vty_out(vty, BGP_RD_AS_FORMAT(bgp_vrf->asnotation), + &bgp_vrf->vrf_prd); + vty_out(vty, "\n"); vty_out(vty, " Originator IP: %pI4\n", &bgp_vrf->originator_ip); vty_out(vty, " Advertise-gw-macip : %s\n", "n/a"); @@ -474,8 +479,10 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json_object *json_import_rtl = NULL; json_object *json_export_rtl = NULL; struct bgp *bgp_evpn; + enum asnotation_mode asnotation; bgp_evpn = bgp_get_evpn(); + asnotation = bgp_get_asnotation(bgp_evpn); if (json) { json_import_rtl = json_object_new_array(); @@ -484,7 +491,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json_object_string_add(json, "type", "L2"); json_object_string_add(json, "inKernel", is_vni_live(vpn) ? "True" : "False"); - json_object_string_addf(json, "rd", "%pRD", &vpn->prd); + json_object_string_addf( + json, "rd", BGP_RD_AS_FORMAT(asnotation), &vpn->prd); json_object_string_addf(json, "originatorIp", "%pI4", &vpn->originator_ip); json_object_string_addf(json, "mcastGroup", "%pI4", @@ -525,7 +533,9 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) vty_out(vty, " Type: %s\n", "L2"); vty_out(vty, " Tenant-Vrf: %s\n", vrf_id_to_name(vpn->tenant_vrf_id)); - vty_out(vty, " RD: %pRD\n", &vpn->prd); + vty_out(vty, " RD: "); + vty_out(vty, BGP_RD_AS_FORMAT(asnotation), &vpn->prd); + vty_out(vty, "\n"); vty_out(vty, " Originator IP: %pI4\n", &vpn->originator_ip); vty_out(vty, " Mcast group: %pI4\n", &vpn->mcast_grp); if (!vpn->advertise_gw_macip && @@ -1004,7 +1014,9 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, json_object_string_add(json_vni, "inKernel", "True"); json_object_string_addf(json_vni, "originatorIp", "%pI4", &bgp->originator_ip); - json_object_string_addf(json_vni, "rd", "%pRD", &bgp->vrf_prd); + json_object_string_addf(json_vni, "rd", + BGP_RD_AS_FORMAT(bgp->asnotation), + &bgp->vrf_prd); json_object_string_add(json_vni, "advertiseGatewayMacip", "n/a"); json_object_string_add(json_vni, "advertiseSviMacIp", "n/a"); @@ -1020,7 +1032,8 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, json_vni, "rmac", prefix_mac2str(&bgp->rmac, buf2, sizeof(buf2))); } else { - vty_out(vty, "%-1s %-10u %-4s %-21pRD", buf1, bgp->l3vni, "L3", + vty_out(vty, "%-1s %-10u %-4s ", buf1, bgp->l3vni, "L3"); + vty_out(vty, BGP_RD_AS_FORMAT_SPACE(bgp->asnotation), &bgp->vrf_prd); } @@ -1104,11 +1117,13 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) struct listnode *node, *nnode; struct ecommunity *ecom; struct bgp *bgp_evpn; + enum asnotation_mode asnotation; vty = args[0]; json = args[1]; bgp_evpn = bgp_get_evpn(); + asnotation = bgp_get_asnotation(bgp_evpn); if (json) { json_vni = json_object_new_object(); @@ -1125,7 +1140,9 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) json_object_string_add(json_vni, "type", "L2"); json_object_string_add(json_vni, "inKernel", is_vni_live(vpn) ? "True" : "False"); - json_object_string_addf(json_vni, "rd", "%pRD", &vpn->prd); + json_object_string_addf(json_vni, "rd", + BGP_RD_AS_FORMAT(asnotation), + &vpn->prd); json_object_string_addf(json_vni, "originatorIp", "%pI4", &vpn->originator_ip); json_object_string_addf(json_vni, "mcastGroup", "%pI4", @@ -1155,8 +1172,8 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) json_object_string_add(json_vni, "advertiseSviMacIp", "Disabled"); } else { - vty_out(vty, "%-1s %-10u %-4s %-21pRD", buf1, vpn->vni, "L2", - &vpn->prd); + vty_out(vty, "%-1s %-10u %-4s ", buf1, vpn->vni, "L2"); + vty_out(vty, BGP_RD_AS_FORMAT_SPACE(asnotation), &vpn->prd); } for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) { @@ -2791,7 +2808,8 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, if (json) { json_rd = json_object_new_object(); - json_object_string_addf(json_rd, "rd", "%pRD", prd); + json_object_string_addf(json_rd, "rd", + BGP_RD_AS_FORMAT(bgp->asnotation), prd); } bgp_dest_unlock_node(rd_dest); @@ -2874,7 +2892,9 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, if (json) { if (add_rd_to_json) - json_object_object_addf(json, json_rd, "%pRD", prd); + json_object_object_addf( + json, json_rd, + BGP_RD_AS_FORMAT(bgp->asnotation), prd); else { json_object_free(json_rd); json_rd = NULL; @@ -2929,7 +2949,7 @@ static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp, continue; prefix_rd2str((struct prefix_rd *)rd_destp, rd_str, - sizeof(rd_str)); + sizeof(rd_str), bgp->asnotation); /* Construct an RT-2 from the user-supplied mac(ip), * then search the l2vpn evpn table for it. @@ -3056,7 +3076,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, tbl_ver = table->version; prefix_rd2str((struct prefix_rd *)rd_destp, rd_str, - sizeof(rd_str)); + sizeof(rd_str), bgp->asnotation); if (json) json_rd = json_object_new_object(); @@ -6401,7 +6421,9 @@ DEFUN (show_bgp_vrf_l3vni_info, for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, l3rt)) vty_out(vty, "%s ", ecommunity_str(l3rt->ecom)); vty_out(vty, "\n"); - vty_out(vty, " RD: %pRD\n", &bgp->vrf_prd); + vty_out(vty, " RD: "); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), &bgp->vrf_prd); + vty_out(vty, "\n"); } else { json_object_string_add(json, "vrf", name); json_object_string_addf(json, "local-ip", "%pI4", @@ -6437,7 +6459,9 @@ DEFUN (show_bgp_vrf_l3vni_info, json_object_new_string( ecommunity_str(l3rt->ecom))); json_object_object_add(json, "import-rts", json_import_rts); - json_object_string_addf(json, "rd", "%pRD", &bgp->vrf_prd); + json_object_string_addf(json, "rd", + BGP_RD_AS_FORMAT(bgp->asnotation), + &bgp->vrf_prd); } if (uj) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 33e7d971ad..a892fe261c 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1868,7 +1868,7 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ int origin_local = 0; struct bgp *src_vrf; struct interface *ifp; - + char rd_buf[RD_ADDRSTRLEN]; int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); if (!vpn_leak_from_vpn_active(to_bgp, afi, &debugmsg)) { @@ -1907,6 +1907,9 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ return false; } + if (debug) + prefix_rd2str(prd, rd_buf, sizeof(rd_buf), to_bgp->asnotation); + /* A route MUST NOT ever be accepted back into its source VRF, even if * it carries one or more RTs that match that VRF. */ @@ -1915,15 +1918,14 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ ECOMMUNITY_SIZE) == 0) { if (debug) zlog_debug( - "%s: skipping import, match RD (%pRD) of src VRF (%s) and the prefix (%pFX)", - __func__, prd, to_bgp->name_pretty, p); - + "%s: skipping import, match RD (%s) of src VRF (%s) and the prefix (%pFX)", + __func__, rd_buf, to_bgp->name_pretty, p); return false; } if (debug) - zlog_debug("%s: updating RD %pRD, %pFX to %s", __func__, prd, p, - to_bgp->name_pretty); + zlog_debug("%s: updating RD %s, %pFX to %s", __func__, rd_buf, + p, to_bgp->name_pretty); /* shallow copy */ static_attr = *path_vpn->attr; @@ -2418,7 +2420,7 @@ void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw, &bgp->vrf_prd_auto); bgp->vpn_policy[afi].tovpn_rd = bgp->vrf_prd_auto; prefix_rd2str(&bgp->vpn_policy[afi].tovpn_rd, buf, - sizeof(buf)); + sizeof(buf), bgp->asnotation); /* free up pre-existing memory if any and allocate * the ecommunity attribute with new RD/RT @@ -2553,8 +2555,8 @@ void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, from_bgp->vpn_policy[afi].tovpn_rd = from_bgp->vrf_prd_auto; SET_FLAG(from_bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET); - prefix_rd2str(&from_bgp->vpn_policy[afi].tovpn_rd, - buf, sizeof(buf)); + prefix_rd2str(&from_bgp->vpn_policy[afi].tovpn_rd, buf, + sizeof(buf), from_bgp->asnotation); from_bgp->vpn_policy[afi].rtlist[edir] = ecommunity_str2com(buf, ECOMMUNITY_ROUTE_TARGET, 0); SET_FLAG(from_bgp->af_flags[afi][safi], diff --git a/bgpd/bgp_mplsvpn_snmp.c b/bgpd/bgp_mplsvpn_snmp.c index ea147e55b9..7407d44a25 100644 --- a/bgpd/bgp_mplsvpn_snmp.c +++ b/bgpd/bgp_mplsvpn_snmp.c @@ -957,11 +957,13 @@ static uint8_t *mplsL3vpnVrfTable(struct variable *v, oid name[], if (CHECK_FLAG(l3vpn_bgp->vpn_policy[AFI_IP].flags, BGP_VPN_POLICY_TOVPN_RD_SET)) prefix_rd2str(&l3vpn_bgp->vpn_policy[AFI_IP].tovpn_rd, - rd_buf, sizeof(rd_buf)); + rd_buf, sizeof(rd_buf), + bgp_get_asnotation(l3vpn_bgp)); else if (CHECK_FLAG(l3vpn_bgp->vpn_policy[AFI_IP6].flags, BGP_VPN_POLICY_TOVPN_RD_SET)) prefix_rd2str(&l3vpn_bgp->vpn_policy[AFI_IP6].tovpn_rd, - rd_buf, sizeof(rd_buf)); + rd_buf, sizeof(rd_buf), + bgp_get_asnotation(l3vpn_bgp)); *var_len = strnlen(rd_buf, RD_ADDRSTRLEN); return (uint8_t *)rd_buf; diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 77e26037c0..f666c3a5e2 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -817,6 +817,7 @@ static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, safi = table->safi; bgp_path = table->bgp; + if (json) { json_path = json_object_new_object(); json_object_string_add(json_path, "afi", afi2str(afi)); @@ -826,7 +827,8 @@ static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, dest); if (dest->pdest) json_object_string_addf( - json_path, "rd", "%pRD", + json_path, "rd", + BGP_RD_AS_FORMAT(bgp->asnotation), (struct prefix_rd *)bgp_dest_get_prefix( dest->pdest)); json_object_string_add( @@ -836,13 +838,14 @@ static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp, json_object_array_add(paths, json_path); continue; } - if (dest->pdest) - vty_out(vty, " %d/%d %pBD RD %pRD %s flags 0x%x\n", - afi, safi, dest, + if (dest->pdest) { + vty_out(vty, " %d/%d %pBD RD ", afi, safi, dest); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), (struct prefix_rd *)bgp_dest_get_prefix( - dest->pdest), - bgp_path->name_pretty, path->flags); - else + dest->pdest)); + vty_out(vty, " %s flags 0x%x\n", bgp_path->name_pretty, + path->flags); + } else vty_out(vty, " %d/%d %pBD %s flags 0x%x\n", afi, safi, dest, bgp_path->name_pretty, path->flags); } diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index cf8ff524e9..595aa46355 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -1205,14 +1205,20 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) } if (BGP_DEBUG(nht, NHT)) { - if (dest->pdest) - zlog_debug( - "... eval path %d/%d %pBD RD %pRD %s flags 0x%x", - afi, safi, dest, + + if (dest->pdest) { + char rd_buf[RD_ADDRSTRLEN]; + + prefix_rd2str( (struct prefix_rd *)bgp_dest_get_prefix( dest->pdest), + rd_buf, sizeof(rd_buf), + bgp_get_asnotation(bnc->bgp)); + zlog_debug( + "... eval path %d/%d %pBD RD %s %s flags 0x%x", + afi, safi, dest, rd_buf, bgp_path->name_pretty, path->flags); - else + } else zlog_debug( "... eval path %d/%d %pBD %s flags 0x%x", afi, safi, dest, bgp_path->name_pretty, diff --git a/bgpd/bgp_rd.c b/bgpd/bgp_rd.c index b4bcbdb804..f2ecd3de5f 100644 --- a/bgpd/bgp_rd.c +++ b/bgpd/bgp_rd.c @@ -101,10 +101,10 @@ int str2prefix_rd(const char *str, struct prefix_rd *prd) { int ret = 0; char *p; - char *p2; struct stream *s = NULL; char *half = NULL; struct in_addr addr; + as_t as_val; prd->family = AF_UNSPEC; prd->prefixlen = 64; @@ -116,22 +116,15 @@ int str2prefix_rd(const char *str, struct prefix_rd *prd) if (!all_digit(p + 1)) goto out; + /* case AS dot format is used */ s = stream_new(RD_BYTES); half = XMALLOC(MTYPE_TMP, (p - str) + 1); memcpy(half, str, (p - str)); half[p - str] = '\0'; - - p2 = strchr(str, '.'); - - if (!p2) { - unsigned long as_val; - - if (!all_digit(half)) - goto out; - - as_val = atol(half); - if (as_val > 0xffff) { + /* if it is an AS format or an IP */ + if (asn_str2asn(half, &as_val)) { + if (as_val > UINT16_MAX) { stream_putw(s, RD_TYPE_AS4); stream_putl(s, as_val); stream_putw(s, atol(p + 1)); @@ -140,14 +133,12 @@ int str2prefix_rd(const char *str, struct prefix_rd *prd) stream_putw(s, as_val); stream_putl(s, atol(p + 1)); } - } else { - if (!inet_aton(half, &addr)) - goto out; - + } else if (inet_aton(half, &addr)) { stream_putw(s, RD_TYPE_IP); stream_put_in_addr(s, &addr); stream_putw(s, atol(p + 1)); - } + } else + goto out; memcpy(prd->val, s->data, 8); ret = 1; @@ -158,12 +149,14 @@ out: return ret; } -char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size) +char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size, + enum asnotation_mode asnotation) { const uint8_t *pnt; uint16_t type; struct rd_as rd_as; struct rd_ip rd_ip; + int len = 0; assert(size >= RD_ADDRSTRLEN); @@ -173,11 +166,15 @@ char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size) if (type == RD_TYPE_AS) { decode_rd_as(pnt + 2, &rd_as); - snprintf(buf, size, "%u:%u", rd_as.as, rd_as.val); + len += snprintfrr(buf + len, size - len, ASN_FORMAT(asnotation), + &rd_as.as); + snprintfrr(buf + len, size - len, ":%u", rd_as.val); return buf; } else if (type == RD_TYPE_AS4) { decode_rd_as4(pnt + 2, &rd_as); - snprintf(buf, size, "%u:%u", rd_as.as, rd_as.val); + len += snprintfrr(buf + len, size - len, ASN_FORMAT(asnotation), + &rd_as.as); + snprintfrr(buf + len, size - len, ":%u", rd_as.val); return buf; } else if (type == RD_TYPE_IP) { decode_rd_ip(pnt + 2, &rd_ip); @@ -212,16 +209,38 @@ void form_auto_rd(struct in_addr router_id, (void)str2prefix_rd(buf, prd); } -printfrr_ext_autoreg_p("RD", printfrr_prd); -static ssize_t printfrr_prd(struct fbuf *buf, struct printfrr_eargs *ea, - const void *ptr) +static ssize_t printfrr_prd_asnotation(struct fbuf *buf, + struct printfrr_eargs *ea, + const void *ptr, + enum asnotation_mode asnotation) { char rd_buf[RD_ADDRSTRLEN]; if (!ptr) return bputs(buf, "(null)"); - prefix_rd2str(ptr, rd_buf, sizeof(rd_buf)); + prefix_rd2str(ptr, rd_buf, sizeof(rd_buf), asnotation); return bputs(buf, rd_buf); } + +printfrr_ext_autoreg_p("RDP", printfrr_prd); +static ssize_t printfrr_prd(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_prd_asnotation(buf, ea, ptr, ASNOTATION_PLAIN); +} + +printfrr_ext_autoreg_p("RDD", printfrr_prd_dot); +static ssize_t printfrr_prd_dot(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_prd_asnotation(buf, ea, ptr, ASNOTATION_DOT); +} + +printfrr_ext_autoreg_p("RDE", printfrr_prd_dotplus); +static ssize_t printfrr_prd_dotplus(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + return printfrr_prd_asnotation(buf, ea, ptr, ASNOTATION_DOTPLUS); +} diff --git a/bgpd/bgp_rd.h b/bgpd/bgp_rd.h index 2aee44c721..aa9bc0b872 100644 --- a/bgpd/bgp_rd.h +++ b/bgpd/bgp_rd.h @@ -23,6 +23,9 @@ #ifndef _QUAGGA_BGP_RD_H #define _QUAGGA_BGP_RD_H +#include "asn.h" +#include "prefix.h" + /* RD types */ #define RD_TYPE_AS 0 #define RD_TYPE_IP 1 @@ -35,6 +38,16 @@ #define RD_ADDRSTRLEN 28 #define RD_BYTES 8 +#define BGP_RD_AS_FORMAT(mode) \ + ((mode == ASNOTATION_DOT) \ + ? "%pRDD" \ + : ((mode == ASNOTATION_DOTPLUS) ? "%pRDE" : "%pRDP")) + +#define BGP_RD_AS_FORMAT_SPACE(mode) \ + ((mode == ASNOTATION_DOT) \ + ? "%-21pRDD" \ + : ((mode == ASNOTATION_DOTPLUS) ? "%-21pRDE" : "%-21pRDP")) + struct rd_as { uint16_t type; as_t as; @@ -67,7 +80,8 @@ extern void decode_rd_vnc_eth(const uint8_t *pnt, #endif extern int str2prefix_rd(const char *, struct prefix_rd *); -extern char *prefix_rd2str(const struct prefix_rd *, char *, size_t); +extern char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size, + enum asnotation_mode asnotation); extern void form_auto_rd(struct in_addr router_id, uint16_t rd_id, struct prefix_rd *prd); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 9d9422b362..c46b8b4c2b 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -10260,10 +10260,11 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, if (dest && dest->pdest) { pdest = dest->pdest; if (is_pi_family_evpn(parent_ri)) { - vty_out(vty, - " Imported from %pRD:%pFX, VNI %s", + vty_out(vty, " Imported from "); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), (struct prefix_rd *)bgp_dest_get_prefix( - pdest), + pdest)); + vty_out(vty, ":%pFX, VNI %s", (struct prefix_evpn *) bgp_dest_get_prefix(dest), tag_buf); @@ -10276,12 +10277,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, : "inactive"); vty_out(vty, "\n"); - } else - vty_out(vty, " Imported from %pRD:%pFX\n", + } else { + vty_out(vty, " Imported from "); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), (struct prefix_rd *)bgp_dest_get_prefix( - pdest), + pdest)); + vty_out(vty, ":%pFX\n", (struct prefix_evpn *) bgp_dest_get_prefix(dest)); + } } } @@ -11682,7 +11686,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, char rd[RD_ADDRSTRLEN]; memcpy(&prd, dest_p, sizeof(struct prefix_rd)); - prefix_rd2str(&prd, rd, sizeof(rd)); + prefix_rd2str(&prd, rd, sizeof(rd), bgp->asnotation); bgp_show_table(vty, bgp, safi, itable, type, output_arg, rd, next == NULL, &output_cum, &total_cum, &json_header_depth, @@ -11832,13 +11836,16 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, if (safi == SAFI_EVPN) { if (!json) { vty_out(vty, "BGP routing table entry for %s%s%pFX\n", - prd ? prefix_rd2str(prd, buf1, sizeof(buf1)) + prd ? prefix_rd2str(prd, buf1, sizeof(buf1), + bgp->asnotation) : "", prd ? ":" : "", (struct prefix_evpn *)p); } else { - json_object_string_add(json, "rd", - prd ? prefix_rd2str(prd, buf1, sizeof(buf1)) : - ""); + json_object_string_add( + json, "rd", + prd ? prefix_rd2str(prd, buf1, sizeof(buf1), + bgp->asnotation) + : ""); bgp_evpn_route2json((struct prefix_evpn *)p, json); } } else { @@ -11848,7 +11855,8 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, "\n", ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) ? prefix_rd2str(prd, buf1, - sizeof(buf1)) + sizeof(buf1), + bgp->asnotation) : ""), safi == SAFI_MPLS_VPN ? ":" : "", p, dest->version); @@ -12057,8 +12065,9 @@ static void bgp_show_path_info(const struct prefix_rd *pfx_rd, json_object_object_add(json_header, "paths", json_paths); if (pfx_rd) - json_object_object_addf(json, json_header, "%pRD", - pfx_rd); + json_object_object_addf( + json, json_header, + BGP_RD_AS_FORMAT(bgp->asnotation), pfx_rd); } } @@ -14467,7 +14476,8 @@ CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs") prd = (const struct prefix_rd *)bgp_dest_get_prefix( dest); - prefix_rd2str(prd, rd_str, sizeof(rd_str)); + prefix_rd2str(prd, rd_str, sizeof(rd_str), + bgp->asnotation); show_adj_route( vty, peer, table, afi, safi, type, rmap_name, @@ -15620,7 +15630,7 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, /* "network" configuration display. */ label = decode_label(&bgp_static->label); - vty_out(vty, " network %pFX rd %pRD", p, prd); + vty_out(vty, " network %pFX rd %pRDP", p, prd); if (safi == SAFI_MPLS_VPN) vty_out(vty, " label %u", label); @@ -15698,7 +15708,7 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, &bgp_static->gatewayIp.u.prefix, buf2, sizeof(buf2)); vty_out(vty, - " network %s rd %pRD ethtag %u label %u esi %s gwip %s routermac %s\n", + " network %s rd %pRDP ethtag %u label %u esi %s gwip %s routermac %s\n", buf, prd, p->u.prefix_evpn.prefix_addr.eth_tag, decode_label(&bgp_static->label), esi_buf, buf2, macrouter); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 36b1707a1e..0ea636764a 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -15647,8 +15647,10 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, node, vname)) vty_out(vty, " %s\n", vname); - vty_out(vty, "RD: %pRD\n", + vty_out(vty, "RD: "); + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), &bgp->vpn_policy[afi].tovpn_rd); + vty_out(vty, "\n"); dir = BGP_VPN_POLICY_DIR_TOVPN; if (bgp->vpn_policy[afi].rtlist[dir]) { diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index b65d90e1b3..547893bfec 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -3937,7 +3937,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) value); } else - vty_out(vty, " rd %pRD\n", &rfg->rd); + vty_out(vty, " rd %pRDP\n", &rfg->rd); } if (rfg->rt_import_list && rfg->rt_export_list @@ -4157,7 +4157,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) value); } else - vty_out(vty, " rd %pRD\n", + vty_out(vty, " rd %pRDP\n", &hc->default_rd); } if (hc->default_response_lifetime @@ -4237,7 +4237,7 @@ int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) value); } else - vty_out(vty, " rd %pRD\n", + vty_out(vty, " rd %pRDP\n", &rfg->rd); } if (rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME) { diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 80d0b3e269..9209b34653 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -376,7 +376,7 @@ void del_vnc_route(struct rfapi_descriptor *rfd, bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); vnc_zlog_debug_verbose( - "%s: peer=%p, prefix=%pFX, prd=%pRD afi=%d, safi=%d bn=%p, bn->info=%p", + "%s: peer=%p, prefix=%pFX, prd=%pRDP afi=%d, safi=%d bn=%p, bn->info=%p", __func__, peer, p, prd, afi, safi, bn, (bn ? bgp_dest_get_bgp_path_info(bn) : NULL)); @@ -1066,7 +1066,7 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ bgp_process(bgp, bn, afi, safi); vnc_zlog_debug_any( - "%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%pRD)", + "%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%pRDP)", __func__, safi2str(safi), buf, bn, prd); done: @@ -3725,7 +3725,7 @@ int rfapi_set_autord_from_vn(struct prefix_rd *rd, struct rfapi_ip_addr *vn) memcpy(rd->val + 2, &vn->addr.v6.s6_addr32[3], 4); /* low order 4 bytes */ } - vnc_zlog_debug_verbose("%s: auto-RD is set to %pRD", __func__, rd); + vnc_zlog_debug_verbose("%s: auto-RD is set to %pRDP", __func__, rd); return 0; } diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index 5db40be362..7024797cf9 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -2097,7 +2097,7 @@ static void rfapiItBiIndexAdd(struct agg_node *rn, /* Import table VPN node */ assert(bpi); assert(bpi->extra); - vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRD", __func__, bpi, + vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRDP", __func__, bpi, bpi->peer, &bpi->extra->vnc.import.rd); sl = RFAPI_RDINDEX_W_ALLOC(rn); @@ -2135,7 +2135,9 @@ static void rfapiItBiIndexDump(struct agg_node *rn) char buf[RD_ADDRSTRLEN]; char buf_aux_pfx[PREFIX_STRLEN]; - prefix_rd2str(&k->extra->vnc.import.rd, buf, sizeof(buf)); + prefix_rd2str( + &k->extra->vnc.import.rd, buf, sizeof(buf), + bgp_get_asnotation(k->peer ? k->peer->bgp : NULL)); if (k->extra->vnc.import.aux_prefix.family) { prefix2str(&k->extra->vnc.import.aux_prefix, buf_aux_pfx, sizeof(buf_aux_pfx)); @@ -2173,7 +2175,7 @@ static struct bgp_path_info *rfapiItBiIndexSearch( strlcpy(buf_aux_pfx, "(nil)", sizeof(buf_aux_pfx)); vnc_zlog_debug_verbose( - "%s want prd=%pRD, peer=%p, aux_prefix=%s", __func__, + "%s want prd=%pRDP, peer=%p, aux_prefix=%s", __func__, prd, peer, buf_aux_pfx); rfapiItBiIndexDump(rn); } @@ -2189,7 +2191,7 @@ static struct bgp_path_info *rfapiItBiIndexSearch( bpi_result = bpi_result->next) { #ifdef DEBUG_BI_SEARCH vnc_zlog_debug_verbose( - "%s: bpi has prd=%pRD, peer=%p", __func__, + "%s: bpi has prd=%pRDP, peer=%p", __func__, &bpi_result->extra->vnc.import.rd, bpi_result->peer); #endif @@ -2253,7 +2255,7 @@ static void rfapiItBiIndexDel(struct agg_node *rn, /* Import table VPN node */ struct skiplist *sl; int rc; - vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRD", __func__, bpi, + vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRDP", __func__, bpi, bpi->peer, &bpi->extra->vnc.import.rd); sl = RFAPI_RDINDEX(rn); diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index 50a10c3b1d..b4600d5203 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -1127,7 +1127,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, skiplist_insert(slRibPt, &ori->rk, ori); vnc_zlog_debug_verbose( - "%s: nomatch lPendCost item %p in slRibPt, added (rd=%pRD)", + "%s: nomatch lPendCost item %p in slRibPt, added (rd=%pRDP)", __func__, ri, &ori->rk.rd); } @@ -1369,7 +1369,7 @@ callback: ri->last_sent_time = monotime(NULL); #if DEBUG_RIB_SL_RD vnc_zlog_debug_verbose( - "%s: move route to recently deleted list, rd=%pRD", + "%s: move route to recently deleted list, rd=%pRDP", __func__, &ri->rk.rd); #endif @@ -2265,7 +2265,7 @@ static int print_rib_sl(int (*fp)(void *, const char *, ...), struct vty *vty, } #endif - fp(out, " %c %-20s %-15s %-15s %-4u %-8s %-8s %pRD\n", + fp(out, " %c %-20s %-15s %-15s %-4u %-8s %-8s %pRDP\n", deleted ? 'r' : ' ', *printedprefix ? "" : str_pfx, str_vn, str_un, ri->cost, str_lifetime, str_age, &ri->rk.rd); diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index e04938fde3..dbbef2a077 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -1602,7 +1602,7 @@ void rfapiPrintDescriptor(struct vty *vty, struct rfapi_descriptor *rfd) vty_out(vty, " "); rfapiPrintRfapiIpAddr(vty, &rfd->vn_addr); vty_out(vty, " %p %p ", rfd->response_cb, rfd->cookie); - vty_out(vty, "%pRD", &rfd->rd); + vty_out(vty, "%pRDP", &rfd->rd); vty_out(vty, " %d", rfd->response_lifetime); vty_out(vty, " %s", (rfd->rfg ? rfd->rfg->name : "")); vty_out(vty, "%s", HVTYNL); diff --git a/lib/asn.c b/lib/asn.c index 27bff1884d..c64666375d 100644 --- a/lib/asn.c +++ b/lib/asn.c @@ -212,6 +212,17 @@ void asn_asn2json_array(json_object *jseg_list, as_t asn, } } +char *asn_asn2string(const as_t *asn, char *buf, size_t len, + enum asnotation_mode asnotation) +{ + if ((asnotation == ASNOTATION_PLAIN) || + ((asnotation == ASNOTATION_DOT) && *asn < UINT16_MAX)) + snprintf(buf, len, "%u", *asn); + else + asn_asn2asdot(*asn, buf, len); + return buf; +} + static ssize_t printfrr_asnotation(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr, enum asnotation_mode asnotation) @@ -223,11 +234,7 @@ static ssize_t printfrr_asnotation(struct fbuf *buf, struct printfrr_eargs *ea, if (!ptr) return bputs(buf, "(null)"); asn = ptr; - if ((asnotation == ASNOTATION_PLAIN) || - ((asnotation == ASNOTATION_DOT) && *asn < UINT16_MAX)) - snprintf(as_str, sizeof(as_str), "%u", *asn); - else - asn_asn2asdot(*asn, as_str, sizeof(as_str)); + asn_asn2string(asn, as_str, sizeof(as_str), asnotation); return bputs(buf, as_str); } diff --git a/lib/asn.h b/lib/asn.h index 2617798d9d..81a42c658d 100644 --- a/lib/asn.h +++ b/lib/asn.h @@ -53,6 +53,8 @@ void asn_asn2json_array(json_object *jseg_list, as_t asn, enum asnotation_mode asnotation); void asn_asn2json(json_object *jseg_list, const char *attr, as_t asn, enum asnotation_mode asnotation); +extern char *asn_asn2string(const as_t *as, char *buf, size_t len, + enum asnotation_mode asnotation); /* display AS in appropriate format */ #ifdef _FRR_ATTRIBUTE_PRINTFRR #pragma FRR printfrr_ext "%pASP" (as_t *) diff --git a/lib/prefix.h b/lib/prefix.h index 7de8d7903e..be049ad8a7 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -659,7 +659,10 @@ static inline bool ipv4_mcast_ssm(const struct in_addr *addr) #pragma FRR printfrr_ext "%pFX" (struct prefix_eth *) #pragma FRR printfrr_ext "%pFX" (struct prefix_evpn *) #pragma FRR printfrr_ext "%pFX" (struct prefix_fs *) -#pragma FRR printfrr_ext "%pRD" (struct prefix_rd *) +#pragma FRR printfrr_ext "%pRDP" (struct prefix_rd *) +/* RD with AS4B with dot and dot+ format */ +#pragma FRR printfrr_ext "%pRDD" (struct prefix_rd *) +#pragma FRR printfrr_ext "%pRDE" (struct prefix_rd *) #pragma FRR printfrr_ext "%pPSG4" (struct prefix_sg *) #endif From fa566a94af4c5b70fa917c5ca0c1db6808f676cc Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 22 Nov 2022 11:20:51 +0100 Subject: [PATCH 10/22] bgpd: store the route-distinguisher from config as a string The route-distinguisher string can be expressed in different ways when the AS number is part of the RD. And the configured string value has to be kept intact. The following vty commands store the string value internally: - router bgp / address-family ipv4 unicast / rd vpn export <> - router bgp / address-family l2vpn evpn / rd <> - router bgp / address-family l2vpn evpn / vni <> / rd <> The vty commands where RD is configured in the below places is not considered: - router bgp / rfapi related commands - router bgp / address-family xxx xxx / network .. rd <> Signed-off-by: Philippe Guibert --- bgpd/bgp_evpn.c | 7 +++++++ bgpd/bgp_evpn_private.h | 1 + bgpd/bgp_evpn_vty.c | 18 +++++++++++------- bgpd/bgp_route.c | 2 ++ bgpd/bgp_vty.c | 14 ++++++++------ bgpd/bgpd.c | 2 ++ bgpd/bgpd.h | 2 ++ bgpd/rfapi/bgp_rfapi_cfg.c | 3 +++ bgpd/rfapi/rfapi_vty.c | 1 + 9 files changed, 37 insertions(+), 13 deletions(-) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 323256816b..5bbc32d304 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -5937,6 +5937,8 @@ void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn) vpn->prd.prefixlen = 64; snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, vpn->rd_id); (void)str2prefix_rd(buf, &vpn->prd); + if (vpn->prd_pretty) + XFREE(MTYPE_BGP, vpn->prd_pretty); UNSET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD); } @@ -6041,6 +6043,8 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn) bf_release_index(bm->rd_idspace, vpn->rd_id); hash_release(bgp->vni_svi_hash, vpn); hash_release(bgp->vnihash, vpn); + if (vpn->prd_pretty) + XFREE(MTYPE_BGP, vpn->prd_pretty); QOBJ_UNREG(vpn); XFREE(MTYPE_BGP_EVPN, vpn); } @@ -6675,6 +6679,9 @@ void bgp_evpn_cleanup(struct bgp *bgp) list_delete(&bgp->vrf_import_rtl); list_delete(&bgp->vrf_export_rtl); list_delete(&bgp->l2vnis); + + if (bgp->vrf_prd_pretty) + XFREE(MTYPE_BGP, bgp->vrf_prd_pretty); } /* diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index 3f18e4e9f1..30a3ca670b 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -92,6 +92,7 @@ struct bgpevpn { /* RD for this VNI. */ struct prefix_rd prd; + char *prd_pretty; /* Route type 3 field */ struct in_addr originator_ip; diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index e8a52cb430..e1f735f2b6 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -2233,7 +2233,8 @@ static void evpn_unconfigure_export_rt(struct bgp *bgp, struct bgpevpn *vpn, /* * Configure RD for VRF */ -static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, struct prefix_rd *rd) +static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, struct prefix_rd *rd, + const char *rd_pretty) { /* If we have already advertise type-5 routes with a diffrent RD, we * have to delete and withdraw them firs @@ -2242,6 +2243,7 @@ static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, struct prefix_rd *rd) /* update RD */ memcpy(&bgp_vrf->vrf_prd, rd, sizeof(struct prefix_rd)); + bgp_vrf->vrf_prd_pretty = XSTRDUP(MTYPE_BGP, rd_pretty); SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD); /* We have a new RD for VRF. @@ -2263,7 +2265,8 @@ static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf) /* fall back to default RD */ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf); UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD); - + if (bgp_vrf->vrf_prd_pretty) + XFREE(MTYPE_BGP, bgp_vrf->vrf_prd_pretty); /* We have a new RD for VRF. * Advertise all type-5 routes again with the new RD */ @@ -2274,7 +2277,7 @@ static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf) * Configure RD for a VNI (vty handler) */ static void evpn_configure_rd(struct bgp *bgp, struct bgpevpn *vpn, - struct prefix_rd *rd) + struct prefix_rd *rd, const char *rd_pretty) { /* If the VNI is "live", we need to delete and withdraw this VNI's * local routes with the prior RD first. Then, after updating RD, @@ -2285,6 +2288,7 @@ static void evpn_configure_rd(struct bgp *bgp, struct bgpevpn *vpn, /* update RD */ memcpy(&vpn->prd, rd, sizeof(struct prefix_rd)); + vpn->prd_pretty = XSTRDUP(MTYPE_BGP, rd_pretty); SET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD); if (is_vni_live(vpn)) @@ -3493,7 +3497,7 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn) if (is_vni_configured(vpn)) { vty_out(vty, " vni %u\n", vpn->vni); if (is_rd_configured(vpn)) - vty_out(vty, " rd %pRD\n", &vpn->prd); + vty_out(vty, " rd %s\n", vpn->prd_pretty); if (is_import_rt_configured(vpn)) { for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, @@ -6144,7 +6148,7 @@ DEFUN (bgp_evpn_vrf_rd, return CMD_SUCCESS; /* Configure or update the RD. */ - evpn_configure_vrf_rd(bgp_vrf, &prd); + evpn_configure_vrf_rd(bgp_vrf, &prd, argv[1]->arg); return CMD_SUCCESS; } @@ -6236,7 +6240,7 @@ DEFUN (bgp_evpn_vni_rd, return CMD_SUCCESS; /* Configure or update the RD. */ - evpn_configure_rd(bgp, vpn, &prd); + evpn_configure_rd(bgp, vpn, &prd, argv[1]->arg); return CMD_SUCCESS; } @@ -7288,7 +7292,7 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, } } if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_RD_CFGD)) - vty_out(vty, " rd %pRD\n", &bgp->vrf_prd); + vty_out(vty, " rd %s\n", bgp->vrf_prd_pretty); /* import route-target */ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) { diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index c46b8b4c2b..8f16f6cae8 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -15630,6 +15630,7 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, /* "network" configuration display. */ label = decode_label(&bgp_static->label); + /* TODO: save RD format */ vty_out(vty, " network %pFX rd %pRDP", p, prd); if (safi == SAFI_MPLS_VPN) vty_out(vty, " label %u", label); @@ -15707,6 +15708,7 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, inet_ntop(bgp_static->gatewayIp.family, &bgp_static->gatewayIp.u.prefix, buf2, sizeof(buf2)); + /* TODO: save RD format */ vty_out(vty, " network %s rd %pRDP ethtag %u label %u esi %s gwip %s routermac %s\n", buf, prd, p->u.prefix_evpn.prefix_addr.eth_tag, diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 0ea636764a..0f140baad5 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -9044,10 +9044,13 @@ DEFPY (af_rd_vpn_export, bgp_get_default(), bgp); if (yes) { + bgp->vpn_policy[afi].tovpn_rd_pretty = + XSTRDUP(MTYPE_BGP, rd_str); bgp->vpn_policy[afi].tovpn_rd = prd; SET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET); } else { + XFREE(MTYPE_BGP, bgp->vpn_policy[afi].tovpn_rd_pretty); UNSET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET); } @@ -15576,10 +15579,9 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name, json_object_new_string(vname)); json_object_object_add(json, "exportToVrfs", json_export_vrfs); - json_object_string_addf(json, "routeDistinguisher", - "%pRD", - &bgp->vpn_policy[afi].tovpn_rd); - + json_object_string_addf( + json, "routeDistinguisher", "%s", + bgp->vpn_policy[afi].tovpn_rd_pretty); dir = BGP_VPN_POLICY_DIR_TOVPN; if (bgp->vpn_policy[afi].rtlist[dir]) { ecom_str = ecommunity_ecom2str( @@ -17153,8 +17155,8 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, } if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET)) - vty_out(vty, "%*srd vpn export %pRD\n", indent, "", - &bgp->vpn_policy[afi].tovpn_rd); + vty_out(vty, "%*srd vpn export %s\n", indent, "", + bgp->vpn_policy[afi].tovpn_rd_pretty); if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) { diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 69557507d0..3d61f508d7 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3971,6 +3971,8 @@ void bgp_free(struct bgp *bgp) dir = BGP_VPN_POLICY_DIR_TOVPN; if (bgp->vpn_policy[afi].rtlist[dir]) ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]); + if (bgp->vpn_policy[afi].tovpn_rd_pretty) + XFREE(MTYPE_BGP, bgp->vpn_policy[afi].tovpn_rd_pretty); } bgp_confederation_id_unset(bgp); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 57685390c8..910cb42746 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -218,6 +218,7 @@ struct vpn_policy { /* should be mpls_label_t? */ uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */ uint32_t tovpn_zebra_vrf_label_last_sent; + char *tovpn_rd_pretty; struct prefix_rd tovpn_rd; struct prefix tovpn_nexthop; /* unset => set to 0 */ uint32_t flags; @@ -751,6 +752,7 @@ struct bgp { /* RD for this VRF */ struct prefix_rd vrf_prd; + char *vrf_prd_pretty; /* import rt list for the vrf instance */ struct list *vrf_import_rtl; diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 547893bfec..c1f64393c4 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -423,6 +423,7 @@ DEFUN (vnc_defaults_rd, } else { + /* TODO: save RD format */ ret = str2prefix_rd(argv[1]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed rd\n"); @@ -2887,6 +2888,7 @@ DEFUN (vnc_nve_group_rd, } else { + /* TODO: save RD format */ ret = str2prefix_rd(argv[1]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed rd\n"); @@ -3359,6 +3361,7 @@ DEFUN (vnc_vrf_policy_rd, } else { + /* TODO: save RD format */ ret = str2prefix_rd(argv[1]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed rd\n"); diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index dbbef2a077..c0a0ec2366 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -4722,6 +4722,7 @@ static int vnc_add_vrf_prefix(struct vty *vty, const char *arg_vrf, if (arg_rd) { opt = &optary[cur_opt++]; opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD; + /* TODO: save RD format */ if (!str2prefix_rd(arg_rd, &opt->v.internal_rd)) { vty_out(vty, "Malformed RD \"%s\"\n", arg_rd); return CMD_WARNING_CONFIG_FAILED; From 8ede0c8015dc96f52faa852e66e037179b48e208 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 23 Nov 2022 15:46:34 +0100 Subject: [PATCH 11/22] topotests: add test with aspath list encoded as asdot+ format Signed-off-by: Philippe Guibert --- .../__init__.py | 0 .../r1/bgpd.conf | 8 ++ .../r1/zebra.conf | 9 ++ .../r2/bgpd.conf | 5 + .../r2/zebra.conf | 6 + .../r3/bgpd.conf | 8 ++ .../r3/zebra.conf | 9 ++ .../r4/bgpd.conf | 5 + .../r4/zebra.conf | 6 + ...est_bgp_local_as_dotplus_private_remove.py | 114 ++++++++++++++++++ 10 files changed, 170 insertions(+) create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf create mode 100644 tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py b/tests/topotests/bgp_local_as_dotplus_private_remove/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf new file mode 100644 index 0000000000..1846df24f3 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 0.65000 as-notation dot+ + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 0.1000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 local-as 0.500 + address-family ipv4 unicast + neighbor 192.168.255.2 remove-private-AS + redistribute connected diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf new file mode 100644 index 0000000000..0a283c06d5 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf new file mode 100644 index 0000000000..c9adfa4671 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 0.1000 as-notation dot+ + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 0.500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf new file mode 100644 index 0000000000..9a831270b4 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 3000 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 1000 + neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 local-as 500 + address-family ipv4 unicast + neighbor 192.168.255.2 remove-private-AS + redistribute connected diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf new file mode 100644 index 0000000000..39499a198d --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r3/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r3-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf new file mode 100644 index 0000000000..c9adfa4671 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 0.1000 as-notation dot+ + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 0.500 + neighbor 192.168.255.1 timers 3 10 +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf new file mode 100644 index 0000000000..b85911504e --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/r4/zebra.conf @@ -0,0 +1,6 @@ +! +interface r4-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py b/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py new file mode 100644 index 0000000000..efecad3eb2 --- /dev/null +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +# +# bgp_local_as_private_remove.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +bgp_local_as_private_remove.py: +Test if primary AS number is not removed in cases when `local-as` +used together with `remove-private-AS`. +""" + +import os +import sys +import json +import time +import pytest + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_remove_private_as(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + while True: + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json") + ) + if output["192.168.255.1"]["bgpState"] == "Established": + time.sleep(1) + return True + + def _bgp_as_path(router): + output = json.loads( + tgen.gears[router].vtysh_cmd("show ip bgp 172.16.255.254/32 json") + ) + if output["prefix"] == "172.16.255.254/32": + return output["paths"][0]["aspath"]["segments"][0]["list"] + + if _bgp_converge("r2"): + assert len(_bgp_as_path("r2")) == 1 + assert '0.65000' not in _bgp_as_path("r2") + + if _bgp_converge("r4"): + assert len(_bgp_as_path("r4")) == 2 + assert '0.3000' in _bgp_as_path("r4") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) From bebda8a143c4747418ab26a51c1c6be0ae33e38a Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 24 Nov 2022 14:02:31 +0100 Subject: [PATCH 12/22] yang: route-distinguisher typedef for route-maps reworked Some route-distinguisher combinations were not possible under route-maps: route-map rmap permit 1 match evpn rd 65540:44 match evpn rd 1.2.3.4:44 match evpn rd 2000000:44 Do not use the ietf definition for route-distinguisher by overriding a new definition in bgp-route-map.yang itself. When the BGP northbound API will be done, this route-distinguisher definition will have to be used too. Fixes: ("48cb7ea99d10") bgpd: North-bound implementation for bgp rmaps Signed-off-by: Philippe Guibert --- yang/frr-bgp-route-map.yang | 86 ++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index ad2a142fef..b3013b4f3f 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -356,6 +356,90 @@ module frr-bgp-route-map { } } + typedef route-distinguisher { + type string { + pattern + '(0:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(429496729[0-5]|' + + '42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|429[0-3][0-9]{6}|' + + '42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0))|' + + '(1:((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|' + + '1[0-9]{2}|2[0-4][0-9]|25[0-5])):(6553[0-5]|' + + '655[0-2][0-9]|' + + '65[0-4][0-9]{2}|6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(2:(429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '(6(:[a-fA-F0-9]{2}){6})|' + + '(([3-57-9a-fA-F]|[1-9a-fA-F][0-9a-fA-F]{1,3}):' + + '[0-9a-fA-F]{1,12})|' + + '((6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(429496729[0-5]|' + + '42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|429496[0-6][0-9]{3}|' + + '42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|429[0-3][0-9]{6}|' + + '42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0)|' + + '((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|' + + '1[0-9]{2}|2[0-4][0-9]|25[0-5])):(6553[0-5]|' + + '655[0-2][0-9]|' + + '65[0-4][0-9]{2}|6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '((429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)'; + } + + description + "A Route Distinguisher is an 8-octet value used to + distinguish routes from different BGP VPNs (RFC 4364). + A Route Distinguisher will have the same format as a + Route Target as per RFC 4360 and will consist of + two or three fields: a 2-octet Type field, an administrator + field, and, optionally, an assigned number field. + According to the data formats for types 0, 1, 2, and 6 as + defined in RFC 4360, RFC 5668, and RFC 7432, the encoding + pattern is defined as: + 0:2-octet-asn:4-octet-number + 1:4-octet-ipv4addr:2-octet-number + 2:4-octet-asn:2-octet-number + 6:6-octet-mac-address + Additionally, a generic pattern is defined for future + route discriminator types: + 2-octet-other-hex-number:6-octet-hex-number + Some valid examples are 0:100:100, 1:1.1.1.1:100, + 2:1234567890:203, and 6:26:00:08:92:78:00. + The following route distinguisher with two fields are also + accepted : 10000:44 1.2.3.4:44."; + reference + "RFC 4360: BGP Extended Communities Attribute. + RFC 4364: BGP/MPLS IP Virtual Private Networks (VPNs). + RFC 5668: 4-Octet AS Specific BGP Extended Community. + RFC 7432: BGP MPLS-Based Ethernet VPN."; + } + typedef extcommunity-lb-type { type enumeration { enum "explicit-bandwidth" { @@ -598,7 +682,7 @@ module frr-bgp-route-map { description "Match eVPN route-distinguisher"; leaf route-distinguisher { - type rt-types:route-distinguisher; + type route-distinguisher; } } From ad45cb7c040fc7b079751f9fdc77f0ae924d6d7e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 24 Nov 2022 15:44:53 +0100 Subject: [PATCH 13/22] yang: route-distinguisher typedef support asdot notation Some route-distinguisher notation is not supported today. route-map rmap permit 1 match evpn rd 1.1:1 match evpn rd 0.65000:1 ! Add support for this. Signed-off-by: Philippe Guibert --- yang/frr-bgp-route-map.yang | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index b3013b4f3f..8e288194ec 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -409,7 +409,19 @@ module frr-bgp-route-map { + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):' + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + '6[0-4][0-9]{3}|' - + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)'; + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|' + + '((6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0).' + + '(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|' + + '6[0-4][0-9]{3}|' + + '[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):' + + '(429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[01][0-9]{2}|' + + '429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|' + + '4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|' + + '[1-3][0-9]{9}|[1-9][0-9]{0,8}|0))'; } description From 6f167d2363949dbbfac767b2c4bebf7b870febed Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 24 Nov 2022 16:13:08 +0100 Subject: [PATCH 14/22] bgpd: support for route-distinguisher format with 3 fields The ietf proposes to define a RD with a 3 field separated by the ':' character. The last 2 fields stands for the usual fields, namely AS4B:NN, AS2B,NNNN, IP:NN. The first field stands for the kind of route distinguisher used. Today, except with the route-map, no other RD configuration supports this mode. Handle the support for this in FRR. Link: https://github.com/FRRouting/frr/blob/master/yang/ietf/ietf-routing-types.yang#L258 Signed-off-by: Philippe Guibert --- bgpd/bgp_rd.c | 35 +++++++++++++++++++++++++++++------ bgpd/bgp_rd.h | 1 + 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/bgpd/bgp_rd.c b/bgpd/bgp_rd.c index f2ecd3de5f..e406d9ca6d 100644 --- a/bgpd/bgp_rd.c +++ b/bgpd/bgp_rd.c @@ -99,8 +99,8 @@ void decode_rd_vnc_eth(const uint8_t *pnt, struct rd_vnc_eth *rd_vnc_eth) int str2prefix_rd(const char *str, struct prefix_rd *prd) { - int ret = 0; - char *p; + int ret = 0, type = RD_TYPE_UNDEFINED; + char *p, *p2; struct stream *s = NULL; char *half = NULL; struct in_addr addr; @@ -113,30 +113,53 @@ int str2prefix_rd(const char *str, struct prefix_rd *prd) if (!p) goto out; + /* a second ':' is accepted */ + p2 = strchr(p + 1, ':'); + if (p2) { + /* type is in first part */ + half = XMALLOC(MTYPE_TMP, (p - str) + 1); + memcpy(half, str, (p - str)); + half[p - str] = '\0'; + type = atoi(half); + if (type != RD_TYPE_AS && type != RD_TYPE_IP && + type != RD_TYPE_AS4) + goto out; + XFREE(MTYPE_TMP, half); + half = XMALLOC(MTYPE_TMP, (p2 - p)); + memcpy(half, p + 1, (p2 - p - 1)); + half[p2 - p - 1] = '\0'; + p = p2 + 1; + } else { + half = XMALLOC(MTYPE_TMP, (p - str) + 1); + memcpy(half, str, (p - str)); + half[p - str] = '\0'; + } if (!all_digit(p + 1)) goto out; - /* case AS dot format is used */ s = stream_new(RD_BYTES); - half = XMALLOC(MTYPE_TMP, (p - str) + 1); - memcpy(half, str, (p - str)); - half[p - str] = '\0'; /* if it is an AS format or an IP */ if (asn_str2asn(half, &as_val)) { if (as_val > UINT16_MAX) { stream_putw(s, RD_TYPE_AS4); stream_putl(s, as_val); stream_putw(s, atol(p + 1)); + if (type != RD_TYPE_UNDEFINED && type != RD_TYPE_AS4) + goto out; } else { stream_putw(s, RD_TYPE_AS); stream_putw(s, as_val); stream_putl(s, atol(p + 1)); + if (type != RD_TYPE_UNDEFINED && type != RD_TYPE_AS) + goto out; } } else if (inet_aton(half, &addr)) { stream_putw(s, RD_TYPE_IP); stream_put_in_addr(s, &addr); stream_putw(s, atol(p + 1)); + if (type != RD_TYPE_UNDEFINED && type != RD_TYPE_IP) + goto out; } else goto out; memcpy(prd->val, s->data, 8); diff --git a/bgpd/bgp_rd.h b/bgpd/bgp_rd.h index aa9bc0b872..073eb7e114 100644 --- a/bgpd/bgp_rd.h +++ b/bgpd/bgp_rd.h @@ -27,6 +27,7 @@ #include "prefix.h" /* RD types */ +#define RD_TYPE_UNDEFINED (-1) #define RD_TYPE_AS 0 #define RD_TYPE_IP 1 #define RD_TYPE_AS4 2 From 3fbcdc6af669d35a2137124884fc29836ae06b63 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 24 Nov 2022 17:32:10 +0100 Subject: [PATCH 15/22] topotests: add bgp_local_asn_dot test This test performs AS handling operations on BGP instances, and does some checks by using the asdot notation. AS4B values are used for configuration. Signed-off-by: Philippe Guibert --- .../bgp_local_asn_dot_agg.json | 147 + .../bgp_local_asn_dot_ecmp.json | 317 ++ .../bgp_local_asn_dot_topo1.json | 132 + .../test_bgp_local_asn_dot_agg.py | 420 ++ .../test_bgp_local_asn_dot_ecmp.py | 524 +++ .../test_bgp_local_asn_dot_topo1.py | 3655 +++++++++++++++++ 6 files changed, 5195 insertions(+) create mode 100644 tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json create mode 100644 tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json create mode 100644 tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json create mode 100644 tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py create mode 100644 tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py create mode 100644 tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json new file mode 100644 index 0000000000..b481932449 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_agg.json @@ -0,0 +1,147 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r1": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.1.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::1:0/128", + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.200", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.2.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::2:0/128", + "next_hop":"Null0" + }] + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json new file mode 100644 index 0000000000..afacab4946 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_ecmp.json @@ -0,0 +1,317 @@ +{ + "address_types": [ + "ipv4", + "ipv6" + ], + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 24, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:DB8:F::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + } + + ], + "static_routes":[ + { + "network":"10.0.0.1/32", + "next_hop":"Null0" + }, + { + "network":"10::1/128", + "next_hop":"Null0" + } + ] + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r4-link8": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {}, + "r3-link5": {}, + "r3-link6": {}, + "r3-link7": {}, + "r3-link8": {} + } + } + } + } + } + } + } + ] + }, + "r4": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link3": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link4": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link5": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link6": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link7": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link8": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": [{ + "local_as": "1.400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {}, + "r4-link8": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {}, + "r4-link5": {}, + "r4-link6": {}, + "r4-link7": {}, + "r4-link8": {} + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json new file mode 100644 index 0000000000..02aacf791a --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/bgp_local_asn_dot_topo1.json @@ -0,0 +1,132 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"} + ], + "neighbor": { + "r2": {"dest_link": {"r1": {}}} + } + } + } + } + }, + "static_routes":[ + { + "network":"10.1.1.0/32", + "next_hop":"Null0" + }, + { + "network":"10:1::1:0/128", + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "1.400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py new file mode 100644 index 0000000000..cb8fa1e9f9 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_agg.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify the BGP Local AS functionality by aggregating routes in between eBGP Peers. +""" + +import os +import sys +import time +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +ADDR_TYPES = check_address_types() +NETWORK_1_1 = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NETWORK_1_2 = {"ipv4": "10.1.2.0/32", "ipv6": "10:1::2:0/128"} +AGGREGATE_NW = {"ipv4": "10.1.0.0/16", "ipv6": "10:1::/96"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_dot_agg.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +#################################################################################################################### +# +# Testcases +# +#################################################################################################################### + + +def test_verify_bgp_local_as_agg_in_EBGP_p0(request): + """ + Verify the BGP Local AS functionality by aggregating routes in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Done in base config: Advertise prefix 10.1.1.0/24 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/120 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK_1_1[addr_type]}]} + } + + input_static_verify_r2 = { + "r2": {"static_routes": [{"network": NETWORK_1_2[addr_type]}]} + } + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", input_static_verify_r2) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure aggregate-address to summarise all the advertised routes.") + for addr_type in ADDR_TYPES: + route_aggregate = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "aggregate_address": [ + { + "network": AGGREGATE_NW[addr_type], + "summary": True, + "as_set": True, + } + ] + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, route_aggregate) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that we see a summarised route on advertising router R3 " + "and receiving router R4 for both AFIs" + ) + + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + input_static_r1 = { + "r1": {"static_routes": [{"network": [NETWORK_1_1[addr_type]]}]} + } + + input_static_r2 = { + "r2": {"static_routes": [{"network": [NETWORK_1_2[addr_type]]}]} + } + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_agg_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1", "r2"], [input_static_r1, input_static_r2]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 1.110 {1.100,1.110,1.200} by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "{1.100,1.110,1.200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "{1.100,1.200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 {1.100,1.200}" + for addr_type in ADDR_TYPES: + input_static_agg_r1 = { + "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]} + } + result = verify_bgp_rib( + tgen, addr_type, dut, input_static_agg_r1, aspath=aspath + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py new file mode 100644 index 0000000000..6937a61c33 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_ecmp.py @@ -0,0 +1,524 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# +# +########################################################################################################################################## +# +# Testcases +# +########################################################################################################################################### +########################################################################################################################################### +# +# 1.10.1.7. Verify the BGP Local AS functionality with ECMP on 8 links by adding no-prepend and replace-as command in between eBGP Peers. +# +################################################################################################################################################# + +import os +import sys +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status, + create_static_routes, + verify_fib_routes, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + verify_bgp_rib, + create_router_bgp, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +BGP_CONVERGENCE = False +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_dot_ecmp.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +########################################################################################################################################## +# +# Testcases +# +########################################################################################################################################### + + +def test_verify_bgp_local_as_in_ecmp_EBGP_p0(request): + """ + Verify the BGP Local AS functionality with ECMP on 8 links by + adding no-prepend and replace-as command in between eBGP Peers. + """ + + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + dut = "r1" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": {"local_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = {"local_asn": {"local_as": "1.110"}} + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + dest_link = {} + for link_no in range(1, 9): + link = "r4-link" + str(link_no) + dest_link[link] = {"local_asn": {"remote_as": "1.110"}} + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r3": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = {"local_asn": {"local_as": "1.110"}} + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + dest_link = {} + for link_no in range(1, 9): + link = "r3-link" + str(link_no) + dest_link[link] = { + "local_asn": {"local_as": "1.110", "no_prepend": True, "replace_as": True} + } + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": {"r4": {"dest_link": dest_link}} + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py new file mode 100644 index 0000000000..e9234f5172 --- /dev/null +++ b/tests/topotests/bgp_local_asn_dot/test_bgp_local_asn_dot_topo1.py @@ -0,0 +1,3655 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +########################################################################################################## +# +# Functionality Testcases +# +########################################################################################################## +""" +1. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between eBGP Peers. +2. Verify the BGP Local AS functionality by configuring 4 Byte AS at R3 and 2 Byte AS at R2 & R4 in between eBGP Peers. +3. Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers. +4. Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers. +4. Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers. +5. Verify the BGP Local AS functionality with allowas-in in between iBGP Peers. +6. Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors. +7. Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and + further restarting clear BGP * and shutdown BGP neighbor. +8. Verify the BGP Local AS functionality with different AS configurations. +9. Verify the BGP Local AS functionality with R3& R4 with different AS configurations. +""" + +import os +import sys +import time +import pytest +from copy import deepcopy + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + get_frr_ipv6_linklocal, + check_address_types, + check_router_status, + create_static_routes, + verify_fib_routes, + create_route_maps, + kill_router_daemons, + start_router_daemons, + shutdown_bringup_interface, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + clear_bgp_and_verify, + verify_bgp_rib, + modify_as_number, + create_router_bgp, + verify_bgp_advertised_routes_from_neighbor, + verify_graceful_restart, + verify_r_bit, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK = {"ipv4": "10.1.1.0/32", "ipv6": "10:1::1:0/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +NEXT_HOP_IP_GR = {"ipv4": "10.0.0.5", "ipv6": "fd00:0:0:1::2/64"} +NEXT_HOP_IP_1 = {"ipv4": "10.0.0.101", "ipv6": "fd00::1"} +NEXT_HOP_IP_2 = {"ipv4": "10.0.0.102", "ipv6": "fd00::2"} + +BGP_CONVERGENCE = False +PREFERRED_NEXT_HOP = "link_local" +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_local_asn_dot_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +########################################################################################################## +# +# Local APIs +# +########################################################################################################## + + +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): + """ + This function groups the repetitive function calls into one function. + """ + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = clear_bgp_and_verify(tgen, topo, dut) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + return True + + +def next_hop_per_address_family( + tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP +): + """ + This function returns link_local or global next_hop per address-family + """ + intferface = topo["routers"][peer]["links"]["{}".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +########################################################################################################## +# +# Testcases +# +########################################################################################################## + + +def test_verify_bgp_local_as_in_EBGP_p0(request): + """ + Verify the BGP Local AS functionality by adding no-prepend and + replace-as command in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_4B_AS_mid_4B_AS_p0(request): + """ + Verify the BGP Local AS functionality by configuring 4 Byte AS + at R3 and 4 Byte AS at R2 & R4 in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "183.2926" + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: { + "local_asn": { + "remote_as": "183.2926" + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-183.2926 is got added in the AS list 183.2926 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "183.2926 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "183.2926", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "183.2926", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "183.2926 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_GR_EBGP_p0(request): + """ + Verify that BGP Local AS functionality by performing graceful restart in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP_GR[addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + { + "network": NETWORK[addr_type], + "next_hop": NEXT_HOP_IP_GR[addr_type], + } + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + """ + GR Steps : Helper BGP router R2, mark and unmark IPV4 routes + as stale as the restarting router R3 come up within the restart time + """ + # Create route-map to prefer global next-hop + input_dict = { + "r2": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + "r3": { + "route_maps": { + "rmap_global": [ + {"action": "permit", "set": {"ipv6": {"nexthop": "prefer-global"}}} + ] + } + }, + } + result = create_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure neighbor for route map + input_dict_neigh_rm = { + "r2": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + } + } + } + } + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_neigh_rm) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + # Configure graceful-restart + input_dict = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "graceful-restart-helper": True, + "local_asn": {"remote_as": "1.110"}, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "graceful-restart-helper": True, + "local_asn": {"remote_as": "1.110"}, + } + } + } + } + } + }, + } + } + }, + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"graceful-restart": True}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3": {"graceful-restart": True}}} + } + } + }, + } + } + }, + } + + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r3", peer="r2") + for addr_type in ADDR_TYPES: + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + dut = "r2" + peer = "r3" + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + input_topo = {key: topo["routers"][key] for key in ["r3"]} + result = verify_bgp_rib(tgen, addr_type, dut, input_topo) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, "bgp") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("[Phase 2] : R3 goes for reload ") + + kill_router_daemons(tgen, "r3", ["bgpd"]) + + logger.info( + "[Phase 3] : R3 is still down, restart time 120 sec." + " So time verify the routes are present in BGP RIB" + " and ZEBRA" + ) + + for addr_type in ADDR_TYPES: + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + logger.info("[Phase 5] : R3 is about to come up now ") + start_router_daemons(tgen, "r3", ["bgpd"]) + + logger.info("[Phase 5] : R3 is UP Now ! ") + + for addr_type in ADDR_TYPES: + result = verify_bgp_convergence(tgen, topo) + assert ( + result is True + ), "BGP Convergence after BGPd restart" " :Failed \n Error:{}".format(result) + + # Verifying GR stats + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r3") + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying BGP RIB routes + next_hop = next_hop_per_address_family( + tgen, dut, peer, addr_type, NEXT_HOP_IP_2, preferred_next_hop="global" + ) + result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + # Verifying RIB routes + protocol = "bgp" + result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_aspath_p0(request): + """ + Verify the BGP Local AS functionality by adding another AS & by same AS with AS-Prepend command in between eBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-map on R3 to prepend AS 2 times.") + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r3": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "1.1000 1.1000", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure route map in out direction on R4") + # Configure neighbor for route map + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "ASP_{}".format( + addr_type + ), + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-1.300 is got replaced with 1.200 in the AS list 1.110 1.1000 1.1000 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r4" + aspath = "1.110 1.1000 1.1000 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_iBGP_p0(request): + """ + Verify the BGP Local AS functionality by adding no-prepend and replace-as command in between iBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Modify AS Number for R3") + input_dict_modify_as_number = {"r3": {"bgp": {"local_as": "1.200"}}} + result = modify_as_number(tgen, topo, input_dict_modify_as_number) + + step("Base config is done as part of JSON") + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.400", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "next_hop_self": True, + "local_asn": { + "remote_as": "1.200", + }, + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_allow_as_in_iBGP_p0(request): + """ + Verify the BGP Local AS functionality with allowas-in in between iBGP Peers. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Modidy AS Number for R4") + input_dict_modify_as_number = {"r4": {"bgp": {"local_as": "1.100"}}} + result = modify_as_number(tgen, topo, input_dict_modify_as_number) + + step("Base config is done as part of JSON") + dut = "r1" + for addr_type in ADDR_TYPES: + # Enable static routes + input_dict_static_route = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_dict_static_route_redist = { + "r1": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + ] + } + } + result = create_router_bgp(tgen, topo, input_dict_static_route_redist) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify IPv4 and IPv6 static routes received on R1") + result = verify_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + result = verify_fib_routes(tgen, addr_type, "r1", input_dict_static_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure allow-as at R4") + for addr_type in ADDR_TYPES: + allow_as_config_r4 = { + "r4": { + "bgp": [ + { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "allowas-in": { + "number_occurences": 1 + } + } + } + } + } + } + } + } + } + ] + } + } + + step( + "Configuring allow-as for {} address-family on router R4 ".format(addr_type) + ) + result = create_router_bgp(tgen, topo, allow_as_config_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + # now modify the as in r4 and reconfig bgp in r3 with new remote as. + topo1 = deepcopy(topo) + topo1["routers"]["r4"]["bgp"]["local_as"] = "1.100" + + delete_bgp = {"r3": {"bgp": {"delete": True}}} + result = create_router_bgp(tgen, topo1, delete_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + build_config_from_json(tgen, topo1, save_bkup=False) + + step("Configure local-as at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R2 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r2_to_r3 = { + "r2": { + "bgp": [ + { + "local_as": "1.200", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r2_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure remote-as at R4 towards R3.") + for addr_type in ADDR_TYPES: + input_dict_r4_to_r3 = { + "r4": { + "bgp": [ + { + "local_as": "1.100", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r4": { + "local_asn": {"remote_as": "1.110"} + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_r4_to_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify IPv4 and IPv6 static routes received on R3 & R4") + for addr_type in ADDR_TYPES: + static_routes_input = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + for dut in ["r3", "r4"]: + result = verify_fib_routes(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following " + " commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R2.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4.") + for addr_type in ADDR_TYPES: + input_dict_no_prep_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r2": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R2") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r2 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r2) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4") + for addr_type in ADDR_TYPES: + input_dict_no_prep_rep_as_r3_to_r4 = { + "r3": { + "bgp": [ + { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + ] + } + } + result = create_router_bgp(tgen, topo1, input_dict_no_prep_rep_as_r3_to_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo1) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_port_reset_p0(request): + """ + Verify that BGP Local AS functionality by performing shut/ noshut on the interfaces in between BGP neighbors. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step("Api call to modfiy BGP timers at R3") + for addr_type in ADDR_TYPES: + input_dict_r3_timers = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3_timers) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify advertised routes at R3 towards R4") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for count in range(1, 1): + step("Iteration {}".format(count)) + step("Shut down connecting interface between R3<<>>R4 on R3.") + + intf1 = topo["routers"]["r3"]["links"]["r4"]["interface"] + + interfaces = [intf1] + for intf in interfaces: + shutdown_bringup_interface(tgen, "r3", intf, False) + + step( + "On R3, all BGP peering in respective vrf instances go down" + " when the interface is shut" + ) + + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: BGP will not be converged \n " + "Error {}".format(tc_name, result) + ) + + step("BGP neighborship is verified after restart of r3") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_negative2_p0(request): + """ + Verify the BGP Local AS functionality with different AS configurations. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step("Verify advertised routes to R4 at R3") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that AS-1.110 is not prepended in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + step("Verify that AS-1.300 is replaced with AS-1.110 at R3 router.") + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # configure negative scenarios + step("Configure local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.300"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + step("Configure another local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.110", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_negative3_p0(request): + """ + Verify the BGP Local AS functionality with R3& R4 with different AS configurations. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Configure basic BGP Peerings between R1,R2,R3 and R4") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Perform Negative scenarios + step("Configure another local-as at R3 towards R4.") + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.300"}} + } + } + } + } + } + }, + } + } + } + if "bgp" in topo["routers"]["r3"].keys(): + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is not True, ( + "Testcase {} :Failed \n " + "Expected Behaviour: Cannot have local-as same as BGP AS number \n " + "Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_local_as_in_EBGP_restart_daemons_p0(request): + """ + Verify that BGP Local AS functionality by restarting BGP,Zebra and FRR services and + further restarting clear BGP * and shutdown BGP neighbor. + """ + tgen = get_topogen() + global BGP_CONVERGENCE + if BGP_CONVERGENCE != True: + pytest.skip("skipped because of BGP Convergence failure") + # test case name + tc_name = request.node.name + write_test_header(tc_name) + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + step("Base config is done as part of JSON") + step("Configure local-as at R3 towards R4.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": {"local_asn": {"local_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + for dut, asn, neighbor in zip(["r2", "r4"], ["1.200", "1.400"], ["r3", "r3"]): + input_dict_r2_r4 = { + dut: { + "bgp": { + "local_as": asn, + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + dut: {"local_asn": {"remote_as": "1.110"}} + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r2_r4) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + # configure static routes + step("Done in base config: Advertise prefix 10.1.1.0/32 from Router-1(AS-1.100).") + step( + "Done in base config: Advertise an ipv6 prefix 10:1::1:0/128 from Router-1(AS-1.100)." + ) + step("Verify that Static routes are redistributed in BGP process") + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + # Enable static routes + input_static_r1 = { + "r1": { + "static_routes": [ + {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]} + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("configure redistribute static in Router BGP in R1") + input_static_redist_r1 = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that Static routes are redistributed in BGP process") + for addr_type in ADDR_TYPES: + input_static_verify_r1 = { + "r1": {"static_routes": [{"network": NETWORK[addr_type]}]} + } + + result = verify_rib(tgen, addr_type, "r1", input_static_verify_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r3", "r4"]: + result = verify_rib(tgen, addr_type, dut, input_static_r1) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + for dut, input_routes in zip(["r1"], [input_static_r1]): + result = verify_rib(tgen, addr_type, dut, input_routes) + assert result is True, "Testcase {}: Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Kill BGPd daemon on R3.") + kill_router_daemons(tgen, "r3", ["bgpd"]) + + step("Bring up BGPd daemon on R3.") + start_router_daemons(tgen, "r3", ["bgpd"]) + + step( + "Verify that AS-1.110 is got added in the AS list 1.110 1.200 1.100 by following" + "commands at R3 router." + ) + dut = "r3" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify advertised routes at R3 towards R4") + expected_routes = { + "ipv4": [ + {"network": "10.1.1.0/32", "nexthop": ""}, + ], + "ipv6": [ + {"network": "10:1::1:0/128", "nexthop": ""}, + ], + } + result = verify_bgp_advertised_routes_from_neighbor( + tgen, topo, dut="r3", peer="r4", expected_routes=expected_routes + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure local-as with no-prepend at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verify that AS-1.110 is not prepended in the AS list 1.200 1.100 by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Kill BGPd daemon on R3.") + kill_router_daemons(tgen, "r3", ["bgpd"]) + + step("Bring up BGPd daemon on R3.") + start_router_daemons(tgen, "r3", ["bgpd"]) + + step( + "Verify that AS-1.110 is not prepended in the AS list 1.200 1.100 by following " + "commands at R3 router." + ) + dut = "r3" + aspath = "1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure local-as with no-prepend and replace-as at R3 towards R4 & R2.") + for addr_type in ADDR_TYPES: + for neighbor in ["r2", "r4"]: + input_dict_r3 = { + "r3": { + "bgp": { + "local_as": "1.300", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + neighbor: { + "dest_link": { + "r3": { + "local_asn": { + "local_as": "1.110", + "no_prepend": True, + "replace_as": True, + } + } + } + } + } + } + } + }, + } + } + } + result = create_router_bgp(tgen, topo, input_dict_r3) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("BGP neighborship is verified by following commands in R3 routers") + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "BGP convergence :Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + step( + "Verified that AS-1.300 is got replaced with original AS-1.110 at R4 by following commands" + ) + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verified that AS-1.300 is got replaced with original AS-1.110 at R4 by following commands" + ) + dut = "r4" + aspath = "1.110 1.200 1.100" + for addr_type in ADDR_TYPES: + input_static_r1 = {"r1": {"static_routes": [{"network": NETWORK[addr_type]}]}} + result = verify_bgp_rib(tgen, addr_type, dut, input_static_r1, aspath=aspath) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) From cd304769accd62ae6b244b7f1ab74e313ecaab2d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 25 Nov 2022 10:07:28 +0100 Subject: [PATCH 16/22] tests: add unit tests to bgp aspath to check as dot format 4 aspath tests are added, and expect the output string format tobe in dot+ format. Signed-off-by: Philippe Guibert --- tests/bgpd/test_aspath.c | 194 ++++++++++++++++++++++++++++++++------- 1 file changed, 161 insertions(+), 33 deletions(-) diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c index 8c173ea669..7e90e5c070 100644 --- a/tests/bgpd/test_aspath.c +++ b/tests/bgpd/test_aspath.c @@ -70,6 +70,7 @@ static struct test_segment { const uint8_t asdata[1024]; int len; struct test_spec sp; + enum asnotation_mode asnotation; } test_segments[] = { { /* 0 */ @@ -79,6 +80,7 @@ static struct test_segment { 10, {"8466 3 52737 4096", "8466 3 52737 4096", 4, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, }, { /* 1 */ @@ -87,8 +89,16 @@ static struct test_segment { {0x2, 0x1, 0x22, 0x12, 0x2, 0x1, 0x00, 0x04}, 8, { - "8722 4", "8722 4", 2, 0, NOT_ALL_PRIVATE, 4, 5, 8722, + "8722 4", + "8722 4", + 2, + 0, + NOT_ALL_PRIVATE, + 4, + 5, + 8722, }, + 0, }, { /* 2 */ @@ -99,6 +109,7 @@ static struct test_segment { 14, {"8466 3 52737 4096 8722 4", "8466 3 52737 4096 8722 4", 6, 0, NOT_ALL_PRIVATE, 3, 5, 8466}, + 0, }, { /* 3 */ @@ -108,6 +119,7 @@ static struct test_segment { 10, {"8482 51457 {5204}", "8482 51457 {5204}", 3, 0, NOT_ALL_PRIVATE, 5204, 51456, 8482}, + 0, }, { /* 4 */ @@ -119,6 +131,7 @@ static struct test_segment { {"8467 59649 {4196,48658} {17322,30745}", "8467 59649 {4196,48658} {17322,30745}", 4, 0, NOT_ALL_PRIVATE, 48658, 1, 8467}, + 0, }, { /* 5 */ @@ -131,6 +144,7 @@ static struct test_segment { {"6435 59408 21665 {2457,4369,61697} 1842 41590 51793", "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", 7, 0, NOT_ALL_PRIVATE, 51793, 1, 6435}, + 0, }, { /* 6 */ @@ -139,6 +153,7 @@ static struct test_segment { {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15}, 8, {"(123 456 789)", "", 0, 3, NOT_ALL_PRIVATE, 789, 1, NULL_ASN}, + 0, }, { /* 7 */ @@ -149,6 +164,7 @@ static struct test_segment { 14, {"(123 456 789) (111 222)", "", 0, 5, NOT_ALL_PRIVATE, 111, 1, NULL_ASN}, + 0, }, { /* 8 */ @@ -157,6 +173,7 @@ static struct test_segment { {0x4, 0x3, 0x01, 0xc8, 0x00, 0x7b, 0x03, 0x15}, 8, {"[123,456,789]", "", 0, 1, NOT_ALL_PRIVATE, 123, 1, NULL_ASN}, + 0, }, { /* 9 */ @@ -168,6 +185,7 @@ static struct test_segment { 24, {"(123 456 789) [111,222] 8722 {4196,48658}", "8722 {4196,48658}", 2, 4, NOT_ALL_PRIVATE, 123, 1, NULL_ASN}, + 0, }, { /* 10 */ @@ -178,6 +196,7 @@ static struct test_segment { 14, {"8466 2 52737 4096 8722 4", "8466 2 52737 4096 8722 4", 6, 0, NOT_ALL_PRIVATE, 4096, 1, 8466}, + 0, }, { /* 11 */ @@ -189,6 +208,7 @@ static struct test_segment { {"8466 2 52737 4096 8722 4 8722", "8466 2 52737 4096 8722 4 8722", 7, 0, NOT_ALL_PRIVATE, 4096, 1, 8466}, + 0, }, { /* 12 */ @@ -198,6 +218,7 @@ static struct test_segment { 10, {"8466 64512 52737 65535", "8466 64512 52737 65535", 4, 0, NOT_ALL_PRIVATE, 65535, 4, 8466}, + 0, }, { /* 13 */ @@ -207,6 +228,7 @@ static struct test_segment { 10, {"65534 64512 64513 65535", "65534 64512 64513 65535", 4, 0, ALL_PRIVATE, 65534, 4, 65534}, + 0, }, { /* 14 */ @@ -275,6 +297,7 @@ static struct test_segment { "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285", 250, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, }, { /* 15 */ @@ -285,6 +308,7 @@ static struct test_segment { 12, {"8466 3 52737 4096 3456", "8466 3 52737 4096 3456", 5, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, }, { /* 16 */ @@ -293,6 +317,7 @@ static struct test_segment { {}, 0, {"", "", 0, 0, 0, 0, 0, 0}, + 0, }, { /* 17 */ @@ -308,6 +333,7 @@ static struct test_segment { "8466 3 52737 4096 3456 {7099,8153}", "8466 3 52737 4096 3456 {7099,8153}", 6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, }, { /* 18 */ @@ -320,6 +346,7 @@ static struct test_segment { {"6435 59408 21665 {23456} 23456 23456 23456", "6435 59408 21665 {23456} 23456 23456 23456", 7, 0, NOT_ALL_PRIVATE, 23456, 1, 6435}, + 0, }, { /* 19 */ @@ -331,6 +358,7 @@ static struct test_segment { {"{2457,4369,61697} 1842 41591 51793", "{2457,4369,61697} 1842 41591 51793", 4, 0, NOT_ALL_PRIVATE, 51793, 1, 2457}, + 0, }, { /* 20 */ @@ -344,44 +372,88 @@ static struct test_segment { {"(123 456 789) [124,456,788] 6435 59408 21665 {23456} 23456 23456 23456", "6435 59408 21665 {23456} 23456 23456 23456", 7, 4, NOT_ALL_PRIVATE, 23456, 1, 6435}, + 0, }, { /* 21 */ "reconcile_start_trans", "seq(23456,23456,23456) seq(6435,59408,21665)", { - 0x2, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0, 0x2, 0x3, - 0x19, 0x23, 0xe8, 0x10, 0x54, 0xa1, + 0x2, + 0x3, + 0x5b, + 0xa0, + 0x5b, + 0xa0, + 0x5b, + 0xa0, + 0x2, + 0x3, + 0x19, + 0x23, + 0xe8, + 0x10, + 0x54, + 0xa1, }, 16, {"23456 23456 23456 6435 59408 21665", "23456 23456 23456 6435 59408 21665", 6, 0, NOT_ALL_PRIVATE, 21665, 1, 23456}, + 0, }, { /* 22 */ "reconcile_start_trans4", "seq(1842,41591,51793) seq(6435,59408,21665)", { - 0x2, 0x3, 0x07, 0x32, 0xa2, 0x77, 0xca, 0x51, 0x2, 0x3, - 0x19, 0x23, 0xe8, 0x10, 0x54, 0xa1, + 0x2, + 0x3, + 0x07, + 0x32, + 0xa2, + 0x77, + 0xca, + 0x51, + 0x2, + 0x3, + 0x19, + 0x23, + 0xe8, + 0x10, + 0x54, + 0xa1, }, 16, {"1842 41591 51793 6435 59408 21665", "1842 41591 51793 6435 59408 21665", 6, 0, NOT_ALL_PRIVATE, 41591, 1, 1842}, + 0, }, { /* 23 */ "reconcile_start_trans_error", "seq(23456,23456,23456) seq(6435,59408)", { - 0x2, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0, 0x2, 0x2, - 0x19, 0x23, 0xe8, 0x10, + 0x2, + 0x3, + 0x5b, + 0xa0, + 0x5b, + 0xa0, + 0x5b, + 0xa0, + 0x2, + 0x2, + 0x19, + 0x23, + 0xe8, + 0x10, }, 14, {"23456 23456 23456 6435 59408", "23456 23456 23456 6435 59408", 5, 0, NOT_ALL_PRIVATE, 59408, 1, 23456}, + 0, }, { /* 24 */ @@ -397,6 +469,7 @@ static struct test_segment { "8466 3 52737 4096 3456 {7099,8153}", "8466 3 52737 4096 3456 {7099,8153}", 6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, }, { /* 25 */ @@ -406,6 +479,7 @@ static struct test_segment { 0x80}, 12, {NULL, NULL, 0, 0, 0, 0, 0, 0}, + 0, }, { /* 26 */ @@ -415,6 +489,7 @@ static struct test_segment { 0x00, 0x0d, 0x80}, 14, {NULL, NULL, 0, 0, 0, 0, 0, 0}, + 0, }, { /* 27 */ @@ -423,20 +498,66 @@ static struct test_segment { {0x8, 0x2, 0x10, 0x00, 0x0d, 0x80}, 14, {NULL, NULL, 0, 0, 0, 0, 0, 0}, + 0, }, { /* 28 */ "BGP_AS_ZERO", "seq(8466,3,52737,0,4096)", - {0x2, 0x5, - 0x21, 0x12, - 0x00, 0x03, - 0xce, 0x01, - 0x00, 0x00, - 0x10, 0x00}, + {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x00, 0x00, 0x10, + 0x00}, 12, {"8466 3 52737 0 4096", "8466 3 52737 0 4096", 5, 0, NOT_ALL_PRIVATE, 4096, 4, 8466}, + 0, + }, + { + /* 29 */ + "seq3_asdot+", + "seq(0.8466,0.3,0.52737,0.4096,0.8722,0.4)", + {0x2, 0x6, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x22, + 0x12, 0x00, 0x04}, + 14, + {"0.8466 0.3 0.52737 0.4096 0.8722 0.4", + "0.8466 0.3 0.52737 0.4096 0.8722 0.4", 6, 0, NOT_ALL_PRIVATE, + 3, 5, 8466}, + ASNOTATION_DOTPLUS, + }, + { + /* 30 */ + "confmulti_asdot+", + "confseq(0.123,0.456,0.789) confset(0.222,0.111) seq(0.8722) set(0.4196,0.48658)", + {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15, + 0x4, 0x2, 0x00, 0xde, 0x00, 0x6f, 0x2, 0x1, + 0x22, 0x12, 0x1, 0x2, 0x10, 0x64, 0xbe, 0x12}, + 24, + {"(0.123 0.456 0.789) [0.111,0.222] 0.8722 {0.4196,0.48658}", + "0.8722 {0.4196,0.48658}", 2, 4, NOT_ALL_PRIVATE, 123, 1, + NULL_ASN}, + ASNOTATION_DOTPLUS, + }, + { + /* 31 */ + "someprivate asdot+", + "seq(0.8466,0.64512,0.52737,0.65535)", + {0x2, 0x4, 0x21, 0x12, 0xfc, 0x00, 0xce, 0x01, 0xff, 0xff}, + 10, + {"0.8466 0.64512 0.52737 0.65535", + "0.8466 0.64512 0.52737 0.65535", 4, 0, NOT_ALL_PRIVATE, 65535, + 4, 8466}, + ASNOTATION_DOTPLUS, + }, + { + /* 32 */ + "BGP_AS_ZERO asdot+", + "seq(0.8466,0.3,0.52737,0.0,0.4096)", + {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x00, 0x00, 0x10, + 0x00}, + 12, + {"0.8466 0.3 0.52737 0.0 0.4096", + "0.8466 0.3 0.52737 0.0 0.4096", 5, 0, NOT_ALL_PRIVATE, 4096, + 4, 8466}, + ASNOTATION_DOTPLUS, }, {NULL, NULL, {0}, 0, {NULL, 0, 0}}}; @@ -871,16 +992,16 @@ struct compare_tests { }; /* make an aspath from a data stream */ -static struct aspath *make_aspath(const uint8_t *data, size_t len, int use32bit) +static struct aspath *make_aspath(const uint8_t *data, size_t len, int use32bit, + enum asnotation_mode asnotation) { struct stream *s = NULL; struct aspath *as; - if (len) { s = stream_new(len); stream_put(s, data, len); } - as = aspath_parse(s, len, use32bit, ASNOTATION_PLAIN); + as = aspath_parse(s, len, use32bit, asnotation); if (s) stream_free(s); @@ -916,16 +1037,15 @@ static int validate(struct aspath *as, const struct test_spec *sp) } out = aspath_snmp_pathseg(as, &bytes); - asinout = make_aspath(out, bytes, 0); - + asinout = make_aspath(out, bytes, 0, as->asnotation); /* Excercise AS4 parsing a bit, with a dogfood test */ if (!s) s = stream_new(BGP_MAX_PACKET_SIZE); bytes4 = aspath_put(s, as, 1); - as4 = make_aspath(STREAM_DATA(s), bytes4, 1); + as4 = make_aspath(STREAM_DATA(s), bytes4, 1, as->asnotation); asn_relax_as_zero(true); - asstr = aspath_str2aspath(sp->shouldbe, ASNOTATION_PLAIN); + asstr = aspath_str2aspath(sp->shouldbe, as->asnotation); asn_relax_as_zero(false); asconfeddel = aspath_delete_confed_seq(aspath_dup(asinout)); @@ -1053,7 +1173,7 @@ static void parse_test(struct test_segment *t) printf("%s: %s\n", t->name, t->desc); - asp = make_aspath(t->asdata, t->len, 0); + asp = make_aspath(t->asdata, t->len, 0, t->asnotation); printf("aspath: %s\nvalidating...:\n", aspath_print(asp)); @@ -1075,8 +1195,10 @@ static void prepend_test(struct tests *t) printf("prepend %s: %s\n", t->test1->name, t->test1->desc); printf("to %s: %s\n", t->test2->name, t->test2->desc); - asp1 = make_aspath(t->test1->asdata, t->test1->len, 0); - asp2 = make_aspath(t->test2->asdata, t->test2->len, 0); + asp1 = make_aspath(t->test1->asdata, t->test1->len, 0, + ASNOTATION_PLAIN); + asp2 = make_aspath(t->test2->asdata, t->test2->len, 0, + ASNOTATION_PLAIN); ascratch = aspath_dup(asp2); aspath_unintern(&asp2); @@ -1102,8 +1224,8 @@ static void empty_prepend_test(struct test_segment *t) printf("empty prepend %s: %s\n", t->name, t->desc); - asp1 = make_aspath(t->asdata, t->len, 0); - asp2 = aspath_empty(ASNOTATION_PLAIN); + asp1 = make_aspath(t->asdata, t->len, 0, t->asnotation); + asp2 = aspath_empty(t->asnotation); ascratch = aspath_dup(asp2); aspath_unintern(&asp2); @@ -1130,8 +1252,10 @@ static void as4_reconcile_test(struct tests *t) printf("reconciling %s:\n %s\n", t->test1->name, t->test1->desc); printf("with %s:\n %s\n", t->test2->name, t->test2->desc); - asp1 = make_aspath(t->test1->asdata, t->test1->len, 0); - asp2 = make_aspath(t->test2->asdata, t->test2->len, 0); + asp1 = make_aspath(t->test1->asdata, t->test1->len, 0, + ASNOTATION_PLAIN); + asp2 = make_aspath(t->test2->asdata, t->test2->len, 0, + ASNOTATION_PLAIN); ascratch = aspath_reconcile_as4(asp1, asp2); @@ -1155,8 +1279,10 @@ static void aggregate_test(struct tests *t) printf("aggregate %s: %s\n", t->test1->name, t->test1->desc); printf("with %s: %s\n", t->test2->name, t->test2->desc); - asp1 = make_aspath(t->test1->asdata, t->test1->len, 0); - asp2 = make_aspath(t->test2->asdata, t->test2->len, 0); + asp1 = make_aspath(t->test1->asdata, t->test1->len, 0, + ASNOTATION_PLAIN); + asp2 = make_aspath(t->test2->asdata, t->test2->len, 0, + ASNOTATION_PLAIN); ascratch = aspath_aggregate(asp1, asp2); @@ -1188,8 +1314,8 @@ static void cmp_test(void) printf("left cmp %s: %s\n", t1->name, t1->desc); printf("and %s: %s\n", t2->name, t2->desc); - asp1 = make_aspath(t1->asdata, t1->len, 0); - asp2 = make_aspath(t2->asdata, t2->len, 0); + asp1 = make_aspath(t1->asdata, t1->len, 0, ASNOTATION_PLAIN); + asp2 = make_aspath(t2->asdata, t2->len, 0, ASNOTATION_PLAIN); if (aspath_cmp_left(asp1, asp2) != left_compare[i].shouldbe_cmp || aspath_cmp_left(asp2, asp1) @@ -1227,7 +1353,9 @@ static int handle_attr_test(struct aspath_tests *t) struct aspath *asp; size_t datalen; - asp = make_aspath(t->segment->asdata, t->segment->len, 0); + asp = make_aspath(t->segment->asdata, t->segment->len, 0, + t->segment->asnotation); + bgp.asnotation = t->segment->asnotation; peer.curr = stream_new(BGP_MAX_PACKET_SIZE); peer.obuf = stream_fifo_new(); @@ -1303,8 +1431,8 @@ int main(void) parse_test(&test_segments[i]); empty_prepend_test(&test_segments[i++]); } - i = 0; + while (prepend_tests[i].test1) { printf("prepend test %u\n", i); prepend_test(&prepend_tests[i++]); From c1aa9e7f905e2ee033e972cda60e5497f3062884 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 25 Nov 2022 12:24:15 +0100 Subject: [PATCH 17/22] bgpd: handle network rd parameter in network configuration The bgp network command creates static routes with an optional route-distinguisher parameter for VPN and EVPN address families. Store the rd parameter in those static routes. This will be used by the 'show running-config' later. Signed-off-by: Philippe Guibert --- bgpd/bgp_route.c | 19 +++++++++---------- bgpd/bgp_route.h | 1 + 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 8f16f6cae8..5686f21f87 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -6081,6 +6081,8 @@ static void bgp_static_free(struct bgp_static *bgp_static) XFREE(MTYPE_ROUTE_MAP_NAME, bgp_static->rmap.name); route_map_counter_decrement(bgp_static->rmap.map); + if (bgp_static->prd_pretty) + XFREE(MTYPE_BGP, bgp_static->prd_pretty); XFREE(MTYPE_ATTR, bgp_static->eth_s_id); XFREE(MTYPE_BGP_STATIC, bgp_static); } @@ -6984,6 +6986,8 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, bgp_static->label = label; bgp_static->prd = prd; + if (rd_str) + bgp_static->prd_pretty = XSTRDUP(MTYPE_BGP, rd_str); if (rmap_str) { XFREE(MTYPE_ROUTE_MAP_NAME, bgp_static->rmap.name); route_map_counter_decrement(bgp_static->rmap.map); @@ -15606,7 +15610,6 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, struct bgp_dest *dest; struct bgp_table *table; const struct prefix *p; - const struct prefix_rd *prd; struct bgp_static *bgp_static; mpls_label_t label; @@ -15624,14 +15627,12 @@ static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp, continue; p = bgp_dest_get_prefix(dest); - prd = (const struct prefix_rd *)bgp_dest_get_prefix( - pdest); /* "network" configuration display. */ label = decode_label(&bgp_static->label); - /* TODO: save RD format */ - vty_out(vty, " network %pFX rd %pRDP", p, prd); + vty_out(vty, " network %pFX rd %s", p, + bgp_static->prd_pretty); if (safi == SAFI_MPLS_VPN) vty_out(vty, " label %u", label); @@ -15654,7 +15655,6 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, struct bgp_dest *dest; struct bgp_table *table; const struct prefix *p; - const struct prefix_rd *prd; struct bgp_static *bgp_static; char buf[PREFIX_STRLEN * 2]; char buf2[SU_ADDRSTRLEN]; @@ -15682,7 +15682,6 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, esi_to_str(bgp_static->eth_s_id, esi_buf, sizeof(esi_buf)); p = bgp_dest_get_prefix(dest); - prd = (struct prefix_rd *)bgp_dest_get_prefix(pdest); /* "network" configuration display. */ if (p->u.prefix_evpn.route_type == 5) { @@ -15708,10 +15707,10 @@ static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp, inet_ntop(bgp_static->gatewayIp.family, &bgp_static->gatewayIp.u.prefix, buf2, sizeof(buf2)); - /* TODO: save RD format */ vty_out(vty, - " network %s rd %pRDP ethtag %u label %u esi %s gwip %s routermac %s\n", - buf, prd, p->u.prefix_evpn.prefix_addr.eth_tag, + " network %s rd %s ethtag %u label %u esi %s gwip %s routermac %s\n", + buf, bgp_static->prd_pretty, + p->u.prefix_evpn.prefix_addr.eth_tag, decode_label(&bgp_static->label), esi_buf, buf2, macrouter); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 452282926f..441f84e115 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -370,6 +370,7 @@ struct bgp_static { /* Route Distinguisher */ struct prefix_rd prd; + char *prd_pretty; /* MPLS label. */ mpls_label_t label; From 6ccfd1030b42771cfb182e9e6ad53c5368d4b10e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 25 Nov 2022 17:51:33 +0100 Subject: [PATCH 18/22] bgpd: add as notation support for bgp rpki service Rpki service uses AS number: - some show commands use the as number as paramter. use asnotation support. - the as number of entries is displayed based on the asnotation mode of the chose bgp instance. Signed-off-by: Philippe Guibert --- bgpd/bgp_rpki.c | 53 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 73c6fe0c47..50bbfc1465 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -96,6 +96,7 @@ struct rpki_for_each_record_arg { unsigned int *prefix_amount; as_t as; json_object *json; + enum asnotation_mode asnotation; }; static int start(void); @@ -120,7 +121,7 @@ static void rpki_delete_all_cache_nodes(void); static int add_tcp_cache(const char *host, const char *port, const uint8_t preference, const char *bindaddr); static void print_record(const struct pfx_record *record, struct vty *vty, - json_object *json); + json_object *json, enum asnotation_mode asnotation); static bool is_synchronized(void); static bool is_running(void); static bool is_stopping(void); @@ -285,7 +286,7 @@ static void rpki_delete_all_cache_nodes(void) } static void print_record(const struct pfx_record *record, struct vty *vty, - json_object *json) + json_object *json, enum asnotation_mode asnotation) { char ip[INET6_ADDRSTRLEN]; json_object *json_record = NULL; @@ -293,8 +294,10 @@ static void print_record(const struct pfx_record *record, struct vty *vty, lrtr_ip_addr_to_str(&record->prefix, ip, sizeof(ip)); if (!json) { - vty_out(vty, "%-40s %3u - %3u %10u\n", ip, record->min_len, - record->max_len, record->asn); + vty_out(vty, "%-40s %3u - %3u ", ip, record->min_len, + record->max_len); + vty_out(vty, ASN_FORMAT(asnotation), &record->asn); + vty_out(vty, "\n"); } else { json_record = json_object_new_object(); json_object_string_add(json_record, "prefix", ip); @@ -302,7 +305,7 @@ static void print_record(const struct pfx_record *record, struct vty *vty, record->min_len); json_object_int_add(json_record, "prefixLenMax", record->max_len); - json_object_int_add(json_record, "asn", record->asn); + asn_asn2json(json_record, "asn", record->asn, asnotation); json_object_array_add(json, json_record); } } @@ -314,7 +317,7 @@ static void print_record_by_asn(const struct pfx_record *record, void *data) if (record->asn == arg->as) { (*arg->prefix_amount)++; - print_record(record, vty, arg->json); + print_record(record, vty, arg->json, arg->asnotation); } } @@ -325,7 +328,7 @@ static void print_record_cb(const struct pfx_record *record, void *data) (*arg->prefix_amount)++; - print_record(record, vty, arg->json); + print_record(record, vty, arg->json, arg->asnotation); } static struct rtr_mgr_group *get_groups(void) @@ -743,6 +746,7 @@ static void print_prefix_table_by_asn(struct vty *vty, as_t as, arg.vty = vty; arg.as = as; arg.json = NULL; + arg.asnotation = bgp_get_asnotation(bgp_lookup_by_vrf_id(VRF_DEFAULT)); if (!group) { if (!json) @@ -795,6 +799,7 @@ static void print_prefix_table(struct vty *vty, json_object *json) arg.vty = vty; arg.json = NULL; + arg.asnotation = bgp_get_asnotation(bgp_lookup_by_vrf_id(VRF_DEFAULT)); if (!group) { if (!json) @@ -1352,7 +1357,7 @@ DEFPY (show_rpki_prefix_table, DEFPY (show_rpki_as_number, show_rpki_as_number_cmd, - "show rpki as-number (1-4294967295)$by_asn [json$uj]", + "show rpki as-number ASNUM$by_asn [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Lookup by ASN in prefix table\n" @@ -1360,23 +1365,28 @@ DEFPY (show_rpki_as_number, JSON_STR) { struct json_object *json = NULL; + as_t as; if (!is_synchronized()) { if (!uj) vty_out(vty, "No Connection to RPKI cache server.\n"); return CMD_WARNING; } - + if (!asn_str2asn(by_asn, &as)) { + if (!uj) + vty_out(vty, "Invalid AS value: %s.\n", by_asn); + return CMD_WARNING; + } if (uj) json = json_object_new_object(); - print_prefix_table_by_asn(vty, by_asn, json); + print_prefix_table_by_asn(vty, as, json); return CMD_SUCCESS; } DEFPY (show_rpki_prefix, show_rpki_prefix_cmd, - "show rpki prefix [(1-4294967295)$asn] [json$uj]", + "show rpki prefix [ASNUM$asn] [json$uj]", SHOW_STR RPKI_OUTPUT_STRING "Lookup IP prefix and optionally ASN in prefix table\n" @@ -1387,6 +1397,8 @@ DEFPY (show_rpki_prefix, { json_object *json = NULL; json_object *json_records = NULL; + as_t as; + enum asnotation_mode asnotation; if (!is_synchronized()) { if (!uj) @@ -1407,13 +1419,19 @@ DEFPY (show_rpki_prefix, return CMD_WARNING; } + if (asn && !asn_str2asn(asn, &as)) { + if (!uj) + vty_out(vty, "Invalid AS value: %s.\n", asn); + return CMD_WARNING; + } + struct pfx_record *matches = NULL; unsigned int match_count = 0; enum pfxv_state result; if (pfx_table_validate_r(rtr_config->pfx_table, &matches, &match_count, - asn, &addr, prefix->prefixlen, &result) - != PFX_SUCCESS) { + as, &addr, prefix->prefixlen, + &result) != PFX_SUCCESS) { if (!json) vty_out(vty, "Prefix lookup failed\n"); return CMD_WARNING; @@ -1430,13 +1448,14 @@ DEFPY (show_rpki_prefix, json_object_object_add(json, "prefixes", json_records); } + asnotation = bgp_get_asnotation(bgp_lookup_by_vrf_id(VRF_DEFAULT)); for (size_t i = 0; i < match_count; ++i) { const struct pfx_record *record = &matches[i]; - if (record->max_len >= prefix->prefixlen - && ((asn != 0 && (uint32_t)asn == record->asn) - || asn == 0)) { - print_record(&matches[i], vty, json_records); + if (record->max_len >= prefix->prefixlen && + ((as != 0 && (uint32_t)as == record->asn) || as == 0)) { + print_record(&matches[i], vty, json_records, + asnotation); } } From 8650fef03e736b4209a8acb6cb3cf022be0aabb8 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 28 Nov 2022 10:13:07 +0100 Subject: [PATCH 19/22] topotests: add bgp_asdot_regex test This test ensures that the regex used to filter as paths has to be expressed in the asnotation of the BGP instance where prefixes are received. 2 aspaths have been forged, both for AS 65540, but only the former is expressed in asdot. If the local BGP instance is expressed in asdot format, then only the former ASPATH will match properly the incoming update. Reversely, when the local BGP instance is expressed in plain format, then only the latter ASPATH will match properly the incoming update. Signed-off-by: Philippe Guibert --- tests/topotests/bgp_asdot_regex/__init__.py | 0 tests/topotests/bgp_asdot_regex/r1/bgpd.conf | 27 ++++ .../bgp_asdot_regex/r1/show_bgp_ipv4.json | 80 ++++++++++++ tests/topotests/bgp_asdot_regex/r1/zebra.conf | 6 + tests/topotests/bgp_asdot_regex/r2/bgpd.conf | 26 ++++ .../bgp_asdot_regex/r2/show_bgp_ipv4.json | 80 ++++++++++++ tests/topotests/bgp_asdot_regex/r2/zebra.conf | 6 + .../bgp_asdot_regex/test_bgp_asdot_regex.py | 122 ++++++++++++++++++ 8 files changed, 347 insertions(+) create mode 100644 tests/topotests/bgp_asdot_regex/__init__.py create mode 100644 tests/topotests/bgp_asdot_regex/r1/bgpd.conf create mode 100644 tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json create mode 100644 tests/topotests/bgp_asdot_regex/r1/zebra.conf create mode 100644 tests/topotests/bgp_asdot_regex/r2/bgpd.conf create mode 100644 tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json create mode 100644 tests/topotests/bgp_asdot_regex/r2/zebra.conf create mode 100644 tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py diff --git a/tests/topotests/bgp_asdot_regex/__init__.py b/tests/topotests/bgp_asdot_regex/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_asdot_regex/r1/bgpd.conf b/tests/topotests/bgp_asdot_regex/r1/bgpd.conf new file mode 100644 index 0000000000..4dd95dd8b7 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r1/bgpd.conf @@ -0,0 +1,27 @@ +router bgp 1.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.255.2 remote-as 1.2 + address-family ipv4 unicast + network 172.31.1.0/24 route-map rmapout + network 172.31.2.0/24 route-map rmapout + neighbor 192.168.255.2 route-map rmapin in + neighbor 192.168.255.2 activate + exit-address-family +exit +bgp as-path access-list only1_4 permit _1.4_ +bgp as-path access-list only65540 permit _65540_ +access-list 172313 permit 172.31.3.0/24 +access-list 172314 permit 172.31.4.0/24 +route-map rmapout permit 1 + set as-path prepend 1.4 +exit +route-map rmapin permit 1 + match ip address 172313 + match as-path only1_4 +exit +route-map rmapin permit 2 + match ip address 172314 + match as-path only65540 +exit + diff --git a/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json b/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json new file mode 100644 index 0000000000..e3703bf953 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r1/show_bgp_ipv4.json @@ -0,0 +1,80 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 3, + "routerId": "192.168.255.1", + "defaultLocPrf": 100, + "localAS": "1.1", + "routes": { "172.31.1.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.1.0", + "prefixLen":24, + "network":"172.31.1.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"1.4", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.2.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.2.0", + "prefixLen":24, + "network":"172.31.2.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"1.4", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.3.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.3.0", + "prefixLen":24, + "network":"172.31.3.0/24", + "metric":0, + "weight":0, + "peerId":"192.168.255.2", + "path":"1.2 1.4", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.255.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] + } } diff --git a/tests/topotests/bgp_asdot_regex/r1/zebra.conf b/tests/topotests/bgp_asdot_regex/r1/zebra.conf new file mode 100644 index 0000000000..6e9b0b4a7e --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_asdot_regex/r2/bgpd.conf b/tests/topotests/bgp_asdot_regex/r2/bgpd.conf new file mode 100644 index 0000000000..216dbd19ef --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r2/bgpd.conf @@ -0,0 +1,26 @@ +router bgp 65538 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.255.1 remote-as 65537 + address-family ipv4 unicast + network 172.31.3.0/24 route-map rmapout + network 172.31.4.0/24 route-map rmapout + neighbor 192.168.255.1 route-map rmapin in + neighbor 192.168.255.1 activate + exit-address-family +exit +bgp as-path access-list only65540 permit _65540_ +bgp as-path access-list only1_4 permit _1.4_ +access-list 172311 permit 172.31.1.0/24 +access-list 172312 permit 172.31.2.0/24 +route-map rmapout permit 1 + set as-path prepend 65540 +exit +route-map rmapin permit 1 + match ip address 172311 + match as-path only65540 +exit +route-map rmapin permit 2 + match ip address 172312 + match as-path only1_4 +exit diff --git a/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json b/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json new file mode 100644 index 0000000000..1af4ff7e3d --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r2/show_bgp_ipv4.json @@ -0,0 +1,80 @@ +{ + "vrfId": 0, + "vrfName": "default", + "tableVersion": 3, + "routerId": "192.168.255.2", + "defaultLocPrf": 100, + "localAS": 65538, + "routes": { "172.31.1.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.1.0", + "prefixLen":24, + "network":"172.31.1.0/24", + "metric":0, + "weight":0, + "peerId":"192.168.255.1", + "path":"65537 65540", + "origin":"IGP", + "nexthops":[ + { + "ip":"192.168.255.1", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.3.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.3.0", + "prefixLen":24, + "network":"172.31.3.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"65540", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] +,"172.31.4.0/24": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"172.31.4.0", + "prefixLen":24, + "network":"172.31.4.0/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"65540", + "origin":"IGP", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } +] + } } diff --git a/tests/topotests/bgp_asdot_regex/r2/zebra.conf b/tests/topotests/bgp_asdot_regex/r2/zebra.conf new file mode 100644 index 0000000000..6c14de583b --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py b/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py new file mode 100644 index 0000000000..5d5f1659e9 --- /dev/null +++ b/tests/topotests/bgp_asdot_regex/test_bgp_asdot_regex.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python + +# +# test_bgp_asdot_regex.py +# Part of Topotests +# +# Copyright 2022 6WIND S.A. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_asdot_regex.py: + +Test how regex applies when asnotation to forge bgp config is based on dot or not. +""" + +import os +import sys +import json +import pytest +from functools import partial + +# add after imports, before defining classes or functions: +pytestmark = [pytest.mark.bgpd] + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_asdot_regex(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + "192.168.255.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + logger.info("Check if neighbor sessions are up in {}".format(router1.name)) + test_func = partial(_bgp_converge, router1) + success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5) + assert result is None, 'Failed to see BGP convergence in "{}"'.format(router1.name) + + logger.info("BGP neighbor session is up in {}".format(router1.name)) + + logger.info("waiting for bgp peers exchanging UPDATES") + + for router in tgen.routers().values(): + ref_file = "{}/{}/show_bgp_ipv4.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show bgp ipv4 unicast json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=2.5) + assertmsg = "{}: BGP UPDATE exchange failure".format(router.name) + assert res is None, assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) From f7b60a3a1fd91846e8f4096073d8e57cb4cf3d8f Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 28 Nov 2022 10:31:58 +0100 Subject: [PATCH 20/22] bgpd: fix dereference of null pointer in 'bgp_evpn_es_evi_show_entry' The bgp_evpn_es_evi_show_entry() function tries to access the vni attribute, while the vpn structure may be null. Signed-off-by: Philippe Guibert --- bgpd/bgp_evpn_mh.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index dd40a072f9..29a4058ded 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -3966,7 +3966,8 @@ static void bgp_evpn_es_evi_show_entry(struct vty *vty, json_object *json_types; json_object_string_add(json, "esi", es_evi->es->esi_str); - json_object_int_add(json, "vni", es_evi->vpn->vni); + if (es_evi->vpn) + json_object_int_add(json, "vni", es_evi->vpn->vni); if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | BGP_EVPNES_EVI_REMOTE)) { From 629d84f512c017077f3b4e16b2a00492ed1762ff Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 3 Feb 2023 09:41:41 +0100 Subject: [PATCH 21/22] bgpd: fix dereference of null pointer in bgp_attr_aspath The peer pointer theorically have a NULL bgp pointer. This triggers a SA issue. So let us fix it. Signed-off-by: Philippe Guibert --- bgpd/bgp_attr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index eb1b208d9b..e79c7a4d38 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1633,7 +1633,8 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args) * such messages, conformant BGP speakers SHOULD use the "Treat-as- * withdraw" error handling behavior as per [RFC7606]. */ - if (peer->bgp->reject_as_sets && aspath_check_as_sets(attr->aspath)) { + if (peer->bgp && peer->bgp->reject_as_sets && + aspath_check_as_sets(attr->aspath)) { flog_err(EC_BGP_ATTR_MAL_AS_PATH, "AS_SET and AS_CONFED_SET are deprecated from %pBP", peer); From 616e9f0d9f0458788adf4b30707016c8db383b02 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 10 Feb 2023 18:12:48 +0100 Subject: [PATCH 22/22] bgpd: handle case where passed RD is null The function vpn_leak_to_vrf_update_onevrf() has the RD parameter set to NULL. Test the RD value before displaying it in the called function. Signed-off-by: Philippe Guibert --- bgpd/bgp_mplsvpn.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index a892fe261c..4aa37b8598 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1907,7 +1907,8 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ return false; } - if (debug) + rd_buf[0] = '\0'; + if (debug && prd) prefix_rd2str(prd, rd_buf, sizeof(rd_buf), to_bgp->asnotation); /* A route MUST NOT ever be accepted back into its source VRF, even if