summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/conflicts.yml21
-rw-r--r--bgpd/bgp_evpn.c529
-rw-r--r--bgpd/bgp_evpn_private.h22
-rw-r--r--bgpd/bgp_evpn_vty.c425
-rw-r--r--bgpd/bgp_mplsvpn.c6
-rw-r--r--bgpd/bgp_route.h20
-rw-r--r--bgpd/bgp_routemap.c18
-rw-r--r--bgpd/bgp_vty.c2
-rw-r--r--bgpd/bgpd.h6
-rw-r--r--configure.ac1
-rw-r--r--doc/user/bgp.rst14
-rw-r--r--doc/user/isisd.rst4
-rw-r--r--isisd/isis_cli.c41
-rw-r--r--isisd/isis_lsp.c31
-rw-r--r--isisd/isis_lsp.h1
-rw-r--r--isisd/isis_nb.c13
-rw-r--r--isisd/isis_nb.h6
-rw-r--r--isisd/isis_nb_config.c24
-rw-r--r--isisd/isisd.c113
-rw-r--r--isisd/isisd.h10
-rw-r--r--ldpd/ldp_vty_cmds.c2
-rw-r--r--lib/command.c20
-rw-r--r--lib/command.h86
-rw-r--r--lib/command_graph.c5
-rw-r--r--lib/command_graph.h9
-rw-r--r--lib/command_match.c3
-rw-r--r--lib/command_py.c10
-rw-r--r--lib/grammar_sandbox.c3
-rw-r--r--lib/log_vty.c4
-rw-r--r--lib/prefix.c57
-rw-r--r--lib/prefix.h1
-rw-r--r--lib/routemap.c19
-rw-r--r--ospfd/ospf_interface.c11
-rw-r--r--pimd/pim6_mld.c4
-rw-r--r--pimd/pim_cmd.c2
-rw-r--r--python/clippy/__init__.py24
-rw-r--r--python/makefile.py3
-rw-r--r--python/xrelfo.py29
-rw-r--r--snapcraft/snapcraft.yaml.in2
-rw-r--r--tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json2
-rw-r--r--tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json2
-rw-r--r--tests/topotests/bgp_suppress_fib/r2/bgpd.conf2
-rw-r--r--tests/topotests/bgp_suppress_fib/r2/zebra.conf3
-rw-r--r--tests/topotests/bgp_suppress_fib/r3/v4_route3.json23
-rw-r--r--tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py14
-rw-r--r--tests/topotests/isis_topo1/test_isis_topo1.py313
-rwxr-xr-xtools/frr.in2
-rwxr-xr-xtools/frrcommon.sh.in2
-rw-r--r--tools/permutations.c3
-rw-r--r--vrrpd/vrrp_vty.c8
-rw-r--r--vtysh/vtysh.c4
-rw-r--r--yang/frr-isisd.yang22
-rw-r--r--zebra/zebra_fpm.c28
53 files changed, 1577 insertions, 452 deletions
diff --git a/.github/workflows/conflicts.yml b/.github/workflows/conflicts.yml
new file mode 100644
index 0000000000..18a8c0d15d
--- /dev/null
+++ b/.github/workflows/conflicts.yml
@@ -0,0 +1,21 @@
+name: Add a conflict label is PR needs to rebase
+
+on:
+ push:
+ pull_request_target:
+ types: [synchronize]
+
+jobs:
+ conflicts:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+ steps:
+ - name: Check if PRs need a rebase (have some conflicts)
+ uses: eps1lon/actions-label-merge-conflict@releases/2.x
+ with:
+ dirtyLabel: "conflicts"
+ removeOnDirtyLabel: "no_conflicts"
+ repoToken: "${{ secrets.GITHUB_TOKEN }}"
+ commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index 58f5e9a226..0a97a8b7bc 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -62,6 +62,7 @@
DEFINE_QOBJ_TYPE(bgpevpn);
DEFINE_QOBJ_TYPE(bgp_evpn_es);
+DEFINE_MTYPE_STATIC(BGPD, VRF_ROUTE_TARGET, "L3 Route Target");
/*
* Static function declarations
@@ -350,6 +351,15 @@ int bgp_evpn_route_target_cmp(struct ecommunity *ecom1,
return strcmp(ecom1->str, ecom2->str);
}
+/*
+ * Compare L3 Route Targets.
+ */
+static int evpn_vrf_route_target_cmp(struct vrf_route_target *rt1,
+ struct vrf_route_target *rt2)
+{
+ return bgp_evpn_route_target_cmp(rt1->ecom, rt2->ecom);
+}
+
void bgp_evpn_xxport_delete_ecomm(void *val)
{
struct ecommunity *ecomm = val;
@@ -357,11 +367,37 @@ void bgp_evpn_xxport_delete_ecomm(void *val)
}
/*
+ * Delete l3 Route Target.
+ */
+static void evpn_vrf_rt_del(void *val)
+{
+ struct vrf_route_target *l3rt = val;
+
+ ecommunity_free(&l3rt->ecom);
+
+ XFREE(MTYPE_VRF_ROUTE_TARGET, l3rt);
+}
+
+/*
+ * Allocate a new l3 Route Target.
+ */
+static struct vrf_route_target *evpn_vrf_rt_new(struct ecommunity *ecom)
+{
+ struct vrf_route_target *l3rt;
+
+ l3rt = XCALLOC(MTYPE_VRF_ROUTE_TARGET, sizeof(struct vrf_route_target));
+
+ l3rt->ecom = ecom;
+
+ return l3rt;
+}
+
+/*
* Mask off global-admin field of specified extended community (RT),
* just retain the local-admin field.
*/
static inline void mask_ecom_global_admin(struct ecommunity_val *dst,
- struct ecommunity_val *src)
+ const struct ecommunity_val *src)
{
uint8_t type;
@@ -377,33 +413,55 @@ static inline void mask_ecom_global_admin(struct ecommunity_val *dst,
}
/*
- * Map one RT to specified VRF.
- * bgp_vrf = BGP vrf instance
+ * Converts the RT to Ecommunity Value and adjusts masking based
+ * on flags set for RT.
*/
-static void map_vrf_to_rt(struct bgp *bgp_vrf, struct ecommunity_val *eval)
+static void vrf_rt2ecom_val(struct ecommunity_val *to_eval,
+ const struct vrf_route_target *l3rt, int iter)
{
- struct vrf_irt_node *irt = NULL;
- struct ecommunity_val eval_tmp;
+ const struct ecommunity_val *eval;
- /* If using "automatic" RT,
+ eval = (const struct ecommunity_val *)(l3rt->ecom->val +
+ (iter * ECOMMUNITY_SIZE));
+ /* If using "automatic" or "wildcard *" RT,
* we only care about the local-admin sub-field.
* This is to facilitate using L3VNI(VRF-VNI)
- * as the RT for EBGP peering too.
+ * as the RT for EBGP peering too and simplify
+ * configurations by allowing any ASN via '*'.
*/
- memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
- if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
- mask_ecom_global_admin(&eval_tmp, eval);
+ memcpy(to_eval, eval, ECOMMUNITY_SIZE);
- irt = lookup_vrf_import_rt(&eval_tmp);
- if (irt && is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
- /* Already mapped. */
- return;
+ if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO) ||
+ CHECK_FLAG(l3rt->flags, BGP_VRF_RT_WILD))
+ mask_ecom_global_admin(to_eval, eval);
+}
- if (!irt)
- irt = vrf_import_rt_new(&eval_tmp);
+/*
+ * Map one RT to specified VRF.
+ * bgp_vrf = BGP vrf instance
+ */
+static void map_vrf_to_rt(struct bgp *bgp_vrf, struct vrf_route_target *l3rt)
+{
+ uint32_t i = 0;
+
+ for (i = 0; i < l3rt->ecom->size; i++) {
+ struct vrf_irt_node *irt = NULL;
+ struct ecommunity_val eval_tmp;
+
+ /* Adjust masking for value */
+ vrf_rt2ecom_val(&eval_tmp, l3rt, i);
+
+ irt = lookup_vrf_import_rt(&eval_tmp);
- /* Add VRF to the list for this RT. */
- listnode_add(irt->vrfs, bgp_vrf);
+ if (irt && is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+ return; /* Already mapped. */
+
+ if (!irt)
+ irt = vrf_import_rt_new(&eval_tmp);
+
+ /* Add VRF to the list for this RT. */
+ listnode_add(irt->vrfs, bgp_vrf);
+ }
}
/*
@@ -411,12 +469,28 @@ static void map_vrf_to_rt(struct bgp *bgp_vrf, struct ecommunity_val *eval)
* VRFs for this RT, then the RT hash is deleted.
* bgp_vrf: BGP VRF specific instance
*/
-static void unmap_vrf_from_rt(struct bgp *bgp_vrf, struct vrf_irt_node *irt)
+static void unmap_vrf_from_rt(struct bgp *bgp_vrf,
+ struct vrf_route_target *l3rt)
{
- /* Delete VRF from list for this RT. */
- listnode_delete(irt->vrfs, bgp_vrf);
- if (!listnode_head(irt->vrfs)) {
- vrf_import_rt_free(irt);
+ uint32_t i;
+
+ for (i = 0; i < l3rt->ecom->size; i++) {
+ struct vrf_irt_node *irt;
+ struct ecommunity_val eval_tmp;
+
+ /* Adjust masking for value */
+ vrf_rt2ecom_val(&eval_tmp, l3rt, i);
+
+ irt = lookup_vrf_import_rt(&eval_tmp);
+
+ if (!irt)
+ return; /* Not mapped */
+
+ /* Delete VRF from list for this RT. */
+ listnode_delete(irt->vrfs, bgp_vrf);
+
+ if (!listnode_head(irt->vrfs))
+ vrf_import_rt_free(irt);
}
}
@@ -505,10 +579,14 @@ static void bgp_evpn_get_rmac_nexthop(struct bgpevpn *vpn,
* VNIs but the same across routers (in the same AS) for a particular
* VNI.
*/
-static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl)
+static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl,
+ bool is_l3)
{
struct ecommunity_val eval;
- struct ecommunity *ecomadd, *ecom;
+ struct ecommunity *ecomadd;
+ struct ecommunity *ecom;
+ struct vrf_route_target *l3rt;
+ struct vrf_route_target *newrt;
bool ecom_found = false;
struct listnode *node;
@@ -518,15 +596,30 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl)
ecomadd = ecommunity_new();
ecommunity_add_val(ecomadd, &eval, false, false);
- for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom))
- if (ecommunity_cmp(ecomadd, ecom)) {
- ecom_found = true;
- break;
- }
- if (!ecom_found)
- listnode_add_sort(rtl, ecomadd);
- else
+ if (is_l3) {
+ for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt))
+ if (ecommunity_cmp(ecomadd, l3rt->ecom)) {
+ ecom_found = true;
+ break;
+ }
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom))
+ if (ecommunity_cmp(ecomadd, ecom)) {
+ ecom_found = true;
+ break;
+ }
+ }
+
+ if (!ecom_found) {
+ if (is_l3) {
+ newrt = evpn_vrf_rt_new(ecomadd);
+ /* Label it as autoderived */
+ SET_FLAG(newrt->flags, BGP_VRF_RT_AUTO);
+ listnode_add_sort(rtl, newrt);
+ } else
+ listnode_add_sort(rtl, ecomadd);
+ } else
ecommunity_free(&ecomadd);
}
@@ -724,8 +817,9 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
struct ecommunity_val eval_rmac;
bgp_encap_types tnl_type;
struct listnode *node, *nnode;
- struct ecommunity *ecom;
+ struct vrf_route_target *l3rt;
struct ecommunity *old_ecom;
+ struct ecommunity *ecom;
struct list *vrf_export_rtl = NULL;
/* Encap */
@@ -749,10 +843,10 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
/* Add the export RTs for L3VNI/VRF */
vrf_export_rtl = bgp_vrf->vrf_export_rtl;
- for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom))
+ for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, l3rt))
bgp_attr_set_ecommunity(
- attr,
- ecommunity_merge(bgp_attr_get_ecommunity(attr), ecom));
+ attr, ecommunity_merge(bgp_attr_get_ecommunity(attr),
+ l3rt->ecom));
/* add the router mac extended community */
if (!is_zero_mac(&attr->rmac)) {
@@ -789,6 +883,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
bgp_encap_types tnl_type;
struct listnode *node, *nnode;
struct ecommunity *ecom;
+ struct vrf_route_target *l3rt;
uint32_t seqnum;
struct list *vrf_export_rtl = NULL;
@@ -817,12 +912,12 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);
if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode,
- ecom))
+ l3rt))
bgp_attr_set_ecommunity(
attr,
ecommunity_merge(
bgp_attr_get_ecommunity(attr),
- ecom));
+ l3rt->ecom));
}
}
@@ -4263,13 +4358,14 @@ static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf)
{
struct bgp *bgp_evpn = NULL;
- form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
- UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+ form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl, true);
/* Map RT to VRF */
bgp_evpn = bgp_get_evpn();
+
if (!bgp_evpn)
return;
+
bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
}
@@ -4278,7 +4374,8 @@ static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf)
*/
static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf)
{
- evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
+ evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl,
+ true);
}
/*
@@ -4286,8 +4383,7 @@ static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf)
*/
static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf)
{
- UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
- form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
+ form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl, true);
}
/*
@@ -4295,7 +4391,8 @@ static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf)
*/
static void evpn_auto_rt_export_delete_for_vrf(struct bgp *bgp_vrf)
{
- evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
+ evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl,
+ true);
}
static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf)
@@ -4538,36 +4635,65 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi,
}
}
-void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl)
+static void rt_list_remove_node(struct list *rt_list,
+ struct ecommunity *ecomdel, bool is_l3)
{
- struct listnode *node, *nnode, *node_to_del;
- struct ecommunity *ecom, *ecom_auto;
+ struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
+ struct vrf_route_target *l3rt = NULL;
+ struct ecommunity *ecom = NULL;
+
+ if (is_l3) {
+ for (ALL_LIST_ELEMENTS(rt_list, node, nnode, l3rt)) {
+ if (ecommunity_match(l3rt->ecom, ecomdel)) {
+ evpn_vrf_rt_del(l3rt);
+ node_to_del = node;
+ break;
+ }
+ }
+ } else {
+ for (ALL_LIST_ELEMENTS(rt_list, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomdel)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+ }
+
+
+ if (node_to_del)
+ list_delete_node(rt_list, node_to_del);
+}
+
+void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl,
+ bool is_l3)
+{
+ struct ecommunity *ecom_auto;
struct ecommunity_val eval;
if (bgp->advertise_autort_rfc8365)
vni |= EVPN_AUTORT_VXLAN;
+
encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
ecom_auto = ecommunity_new();
ecommunity_add_val(ecom_auto, &eval, false, false);
- node_to_del = NULL;
- for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) {
- if (ecommunity_match(ecom, ecom_auto)) {
- ecommunity_free(&ecom);
- node_to_del = node;
- break;
- }
- }
-
- if (node_to_del)
- list_delete_node(rtl, node_to_del);
+ rt_list_remove_node(rtl, ecom_auto, is_l3);
ecommunity_free(&ecom_auto);
}
-void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
- struct ecommunity *ecomadd)
+static void evpn_vrf_rt_routes_map(struct bgp *bgp_vrf)
+{
+ /* map VRFs to its RTs and install routes matching this new RT */
+ if (is_l3vni_live(bgp_vrf)) {
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+ install_routes_for_vrf(bgp_vrf);
+ }
+}
+
+static void evpn_vrf_rt_routes_unmap(struct bgp *bgp_vrf)
{
/* uninstall routes from vrf */
if (is_l3vni_live(bgp_vrf))
@@ -4575,112 +4701,189 @@ void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
/* Cleanup the RT to VRF mapping */
bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+}
- /* Remove auto generated RT */
- evpn_auto_rt_import_delete_for_vrf(bgp_vrf);
+static bool rt_list_has_cfgd_rt(struct list *rt_list)
+{
+ struct listnode *node = NULL, *nnode = NULL;
+ struct vrf_route_target *l3rt = NULL;
+
+ for (ALL_LIST_ELEMENTS(rt_list, node, nnode, l3rt)) {
+ if (!CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO))
+ return true;
+ }
+
+ return false;
+}
+
+static void unconfigure_import_rt_for_vrf_fini(struct bgp *bgp_vrf)
+{
+ if (!bgp_vrf->vrf_import_rtl)
+ return; /* this should never fail */
+
+ if (!is_l3vni_live(bgp_vrf))
+ return; /* Nothing to do if no vni */
+
+ /* fall back to auto-generated RT if this was the last RT */
+ if (list_isempty(bgp_vrf->vrf_import_rtl))
+ evpn_auto_rt_import_add_for_vrf(bgp_vrf);
+}
+
+static void unconfigure_export_rt_for_vrf_fini(struct bgp *bgp_vrf)
+{
+
+ if (!bgp_vrf->vrf_export_rtl)
+ return; /* this should never fail */
+
+ if (!is_l3vni_live(bgp_vrf))
+ return; /* Nothing to do if no vni */
+
+ /* fall back to auto-generated RT if this was the last RT */
+ if (list_isempty(bgp_vrf->vrf_export_rtl))
+ evpn_auto_rt_export_add_for_vrf(bgp_vrf);
+
+ bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+}
+
+void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd,
+ bool is_wildcard)
+{
+ struct vrf_route_target *newrt;
+
+ newrt = evpn_vrf_rt_new(ecomadd);
+
+ if (is_wildcard)
+ SET_FLAG(newrt->flags, BGP_VRF_RT_WILD);
+
+ evpn_vrf_rt_routes_unmap(bgp_vrf);
+
+ /* Remove auto generated RT if not configured */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))
+ evpn_auto_rt_import_delete_for_vrf(bgp_vrf);
/* Add the newly configured RT to RT list */
- listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd);
+ listnode_add_sort(bgp_vrf->vrf_import_rtl, newrt);
+
SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
- /* map VRF to its RTs and install routes matching the new RTs */
- if (is_l3vni_live(bgp_vrf)) {
- bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
- install_routes_for_vrf(bgp_vrf);
- }
+ evpn_vrf_rt_routes_map(bgp_vrf);
+}
+
+void bgp_evpn_configure_import_auto_rt_for_vrf(struct bgp *bgp_vrf)
+{
+ if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))
+ return; /* Already configured */
+
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD);
+
+ if (!is_l3vni_live(bgp_vrf))
+ return; /* Wait for VNI before adding rts */
+
+ evpn_vrf_rt_routes_unmap(bgp_vrf);
+
+ evpn_auto_rt_import_add_for_vrf(bgp_vrf);
+
+ evpn_vrf_rt_routes_map(bgp_vrf);
}
void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
struct ecommunity *ecomdel)
{
- struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
- struct ecommunity *ecom = NULL;
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+ return; /* Already un-configured */
- /* uninstall routes from vrf */
- if (is_l3vni_live(bgp_vrf))
- uninstall_routes_for_vrf(bgp_vrf);
+ evpn_vrf_rt_routes_unmap(bgp_vrf);
- /* Cleanup the RT to VRF mapping */
- bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+ /* Remove rt */
+ rt_list_remove_node(bgp_vrf->vrf_import_rtl, ecomdel, true);
- /* remove the RT from the RT list */
- for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
- if (ecommunity_match(ecom, ecomdel)) {
- ecommunity_free(&ecom);
- node_to_del = node;
- break;
- }
- }
+ if (!rt_list_has_cfgd_rt(bgp_vrf->vrf_import_rtl))
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
- if (node_to_del)
- list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del);
+ unconfigure_import_rt_for_vrf_fini(bgp_vrf);
- assert(bgp_vrf->vrf_import_rtl);
- /* fallback to auto import rt, if this was the last RT */
- if (bgp_vrf->vrf_import_rtl && list_isempty(bgp_vrf->vrf_import_rtl)) {
- UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
- if (is_l3vni_live(bgp_vrf))
- evpn_auto_rt_import_add_for_vrf(bgp_vrf);
- }
+ evpn_vrf_rt_routes_map(bgp_vrf);
+}
- /* map VRFs to its RTs and install routes matching this new RT */
- if (is_l3vni_live(bgp_vrf)) {
- bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
- install_routes_for_vrf(bgp_vrf);
- }
+void bgp_evpn_unconfigure_import_auto_rt_for_vrf(struct bgp *bgp_vrf)
+{
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))
+ return; /* Already un-configured */
+
+ evpn_vrf_rt_routes_unmap(bgp_vrf);
+
+ /* remove auto-generated RT */
+ evpn_auto_rt_import_delete_for_vrf(bgp_vrf);
+
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD);
+
+ unconfigure_import_rt_for_vrf_fini(bgp_vrf);
+
+ evpn_vrf_rt_routes_map(bgp_vrf);
}
void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
struct ecommunity *ecomadd)
{
- /* remove auto-generated RT */
- evpn_auto_rt_export_delete_for_vrf(bgp_vrf);
+ struct vrf_route_target *newrt;
+
+ newrt = evpn_vrf_rt_new(ecomadd);
+
+ /* Remove auto generated RT if not configured */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
+ evpn_auto_rt_export_delete_for_vrf(bgp_vrf);
/* Add the new RT to the RT list */
- listnode_add_sort(bgp_vrf->vrf_export_rtl, ecomadd);
+ listnode_add_sort(bgp_vrf->vrf_export_rtl, newrt);
+
SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
if (is_l3vni_live(bgp_vrf))
bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
}
+void bgp_evpn_configure_export_auto_rt_for_vrf(struct bgp *bgp_vrf)
+{
+ if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
+ return; /* Already configured */
+
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD);
+
+ if (!is_l3vni_live(bgp_vrf))
+ return; /* Wait for VNI before adding rts */
+
+ evpn_auto_rt_export_add_for_vrf(bgp_vrf);
+
+ bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+}
+
void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
struct ecommunity *ecomdel)
{
- struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
- struct ecommunity *ecom = NULL;
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD))
+ return; /* Already un-configured */
- /* Remove the RT from the RT list */
- for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) {
- if (ecommunity_match(ecom, ecomdel)) {
- ecommunity_free(&ecom);
- node_to_del = node;
- break;
- }
- }
+ /* Remove rt */
+ rt_list_remove_node(bgp_vrf->vrf_export_rtl, ecomdel, true);
- if (node_to_del)
- list_delete_node(bgp_vrf->vrf_export_rtl, node_to_del);
+ if (!rt_list_has_cfgd_rt(bgp_vrf->vrf_export_rtl))
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
- /*
- * Temporary assert to make SA happy.
- * The ALL_LIST_ELEMENTS macro above has a NULL check
- * which means that SA is going to complain about
- * the list_isempty call, which doesn't NULL check.
- * So until we get this situation cleaned up, here
- * we are.
- */
- assert(bgp_vrf->vrf_export_rtl);
+ unconfigure_export_rt_for_vrf_fini(bgp_vrf);
+}
- /* fall back to auto-generated RT if this was the last RT */
- if (list_isempty(bgp_vrf->vrf_export_rtl)) {
- UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
- if (is_l3vni_live(bgp_vrf))
- evpn_auto_rt_export_add_for_vrf(bgp_vrf);
- }
+void bgp_evpn_unconfigure_export_auto_rt_for_vrf(struct bgp *bgp_vrf)
+{
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
+ return; /* Already un-configured */
- if (is_l3vni_live(bgp_vrf))
- bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+ /* remove auto-generated RT */
+ evpn_auto_rt_export_delete_for_vrf(bgp_vrf);
+
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD);
+
+ unconfigure_export_rt_for_vrf_fini(bgp_vrf);
}
/*
@@ -5109,19 +5312,11 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
*/
void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)
{
- uint32_t i = 0;
- struct ecommunity_val *eval = NULL;
- struct listnode *node = NULL, *nnode = NULL;
- struct ecommunity *ecom = NULL;
+ struct listnode *node, *nnode;
+ struct vrf_route_target *l3rt;
- for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
- for (i = 0; i < ecom->size; i++) {
- eval = (struct ecommunity_val *)(ecom->val
- + (i
- * ECOMMUNITY_SIZE));
- map_vrf_to_rt(bgp_vrf, eval);
- }
- }
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt))
+ map_vrf_to_rt(bgp_vrf, l3rt);
}
/*
@@ -5129,37 +5324,13 @@ void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)
*/
void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf)
{
- uint32_t i;
- struct ecommunity_val *eval;
struct listnode *node, *nnode;
- struct ecommunity *ecom;
-
- for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
- for (i = 0; i < ecom->size; i++) {
- struct vrf_irt_node *irt;
- struct ecommunity_val eval_tmp;
-
- eval = (struct ecommunity_val *)(ecom->val
- + (i
- * ECOMMUNITY_SIZE));
- /* If using "automatic" RT, we only care about the
- * local-admin sub-field.
- * This is to facilitate using VNI as the RT for EBGP
- * peering too.
- */
- memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
- if (!CHECK_FLAG(bgp_vrf->vrf_flags,
- BGP_VRF_IMPORT_RT_CFGD))
- mask_ecom_global_admin(&eval_tmp, eval);
+ struct vrf_route_target *l3rt;
- irt = lookup_vrf_import_rt(&eval_tmp);
- if (irt)
- unmap_vrf_from_rt(bgp_vrf, irt);
- }
- }
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt))
+ unmap_vrf_from_rt(bgp_vrf, l3rt);
}
-
/*
* Map the RTs (configured or automatically derived) of a VNI to the VNI.
* The mapping will be used during route processing.
@@ -5221,7 +5392,7 @@ void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn)
*/
void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
{
- form_auto_rt(bgp, vpn->vni, vpn->import_rtl);
+ form_auto_rt(bgp, vpn->vni, vpn->import_rtl, false);
UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
/* Map RT to VNI */
@@ -5233,7 +5404,7 @@ void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
*/
void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn)
{
- form_auto_rt(bgp, vpn->vni, vpn->export_rtl);
+ form_auto_rt(bgp, vpn->vni, vpn->export_rtl, false);
UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
}
@@ -5643,12 +5814,14 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id,
}
/* Map auto derive or configured RTs */
- if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD) ||
+ CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))
evpn_auto_rt_import_add_for_vrf(bgp_vrf);
else
bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
- if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD))
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD) ||
+ CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
evpn_auto_rt_export_add_for_vrf(bgp_vrf);
/* auto derive RD */
@@ -6021,12 +6194,12 @@ void bgp_evpn_init(struct bgp *bgp)
"BGP VRF Import RT Hash");
bgp->vrf_import_rtl = list_new();
bgp->vrf_import_rtl->cmp =
- (int (*)(void *, void *))bgp_evpn_route_target_cmp;
- bgp->vrf_import_rtl->del = bgp_evpn_xxport_delete_ecomm;
+ (int (*)(void *, void *))evpn_vrf_route_target_cmp;
+ bgp->vrf_import_rtl->del = evpn_vrf_rt_del;
bgp->vrf_export_rtl = list_new();
bgp->vrf_export_rtl->cmp =
- (int (*)(void *, void *))bgp_evpn_route_target_cmp;
- bgp->vrf_export_rtl->del = bgp_evpn_xxport_delete_ecomm;
+ (int (*)(void *, void *))evpn_vrf_route_target_cmp;
+ bgp->vrf_export_rtl->del = evpn_vrf_rt_del;
bgp->l2vnis = list_new();
bgp->l2vnis->cmp = vni_list_cmp;
/* By default Duplicate Address Dection is enabled.
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index 64fdc29704..fdbffa95dd 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -194,6 +194,18 @@ struct evpn_remote_ip {
struct list *macip_path_list;
};
+/*
+ * Wrapper struct for l3 RT's
+ */
+struct vrf_route_target {
+ /* flags based on config to determine how RTs are handled */
+ uint8_t flags;
+#define BGP_VRF_RT_AUTO (1 << 0)
+#define BGP_VRF_RT_WILD (1 << 1)
+
+ struct ecommunity *ecom;
+};
+
static inline int is_vrf_rd_configured(struct bgp *bgp_vrf)
{
return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD));
@@ -590,15 +602,21 @@ extern struct zclient *zclient;
extern void bgp_evpn_install_uninstall_default_route(struct bgp *bgp_vrf,
afi_t afi, safi_t safi,
bool add);
-extern void evpn_rt_delete_auto(struct bgp *, vni_t, struct list *);
+extern void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl,
+ bool is_l3);
extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
struct ecommunity *ecomadd);
+extern void bgp_evpn_configure_export_auto_rt_for_vrf(struct bgp *bgp_vrf);
extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
struct ecommunity *ecomdel);
+extern void bgp_evpn_unconfigure_export_auto_rt_for_vrf(struct bgp *bgp_vrf);
extern void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
- struct ecommunity *ecomadd);
+ struct ecommunity *ecomadd,
+ bool is_wildcard);
+extern void bgp_evpn_configure_import_auto_rt_for_vrf(struct bgp *bgp_vrf);
extern void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
struct ecommunity *ecomdel);
+extern void bgp_evpn_unconfigure_import_auto_rt_for_vrf(struct bgp *bgp_vrf);
extern int bgp_evpn_handle_export_rt_change(struct bgp *bgp,
struct bgpevpn *vpn);
extern void bgp_evpn_handle_autort_change(struct bgp *bgp);
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 5ad5cf8bff..f920a783b3 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -373,7 +373,7 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf,
char buf1[INET6_ADDRSTRLEN];
char *ecom_str;
struct listnode *node, *nnode;
- struct ecommunity *ecom;
+ struct vrf_route_target *l3rt;
json_object *json_import_rtl = NULL;
json_object *json_export_rtl = NULL;
char buf2[ETHER_ADDR_STRLEN];
@@ -431,8 +431,8 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf,
if (!json)
vty_out(vty, " Import Route Target:\n");
- for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
- ecom_str = ecommunity_ecom2str(ecom,
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, l3rt)) {
+ ecom_str = ecommunity_ecom2str(l3rt->ecom,
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
if (json)
@@ -449,8 +449,8 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf,
else
vty_out(vty, " Export Route Target:\n");
- for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) {
- ecom_str = ecommunity_ecom2str(ecom,
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, l3rt)) {
+ ecom_str = ecommunity_ecom2str(l3rt->ecom,
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
if (json)
@@ -913,7 +913,7 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp,
char rt_buf[25];
char *ecom_str;
struct listnode *node, *nnode;
- struct ecommunity *ecom;
+ struct vrf_route_target *l3rt;
if (!bgp->l3vni)
return;
@@ -954,8 +954,8 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp,
&bgp->vrf_prd);
}
- for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, ecom)) {
- ecom_str = ecommunity_ecom2str(ecom,
+ for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, l3rt)) {
+ ecom_str = ecommunity_ecom2str(l3rt->ecom,
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
if (json) {
@@ -982,8 +982,8 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp,
if (json)
json_object_object_add(json_vni, "importRTs", json_import_rtl);
- for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, ecom)) {
- ecom_str = ecommunity_ecom2str(ecom,
+ for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, l3rt)) {
+ ecom_str = ecommunity_ecom2str(l3rt->ecom,
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
if (json) {
@@ -1984,12 +1984,12 @@ DEFUN(no_evpnrt5_network,
static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
- evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl);
+ evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl, false);
}
static void evpn_export_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
- evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl);
+ evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl, false);
}
/*
@@ -3980,11 +3980,13 @@ DEFUN (bgp_evpn_advertise_type5,
DEFUN (no_bgp_evpn_advertise_type5,
no_bgp_evpn_advertise_type5_cmd,
- "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR,
+ "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]",
NO_STR
"Advertise prefix routes\n"
BGP_AFI_HELP_STR
- BGP_SAFI_HELP_STR)
+ BGP_SAFI_HELP_STR
+ "route-map for filtering specific routes\n"
+ "Name of the route map\n")
{
struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
int idx_afi = 0;
@@ -5654,18 +5656,35 @@ DEFUN (no_bgp_evpn_vni_rd_without_val,
* Loop over all extended-communities in the route-target list rtl and
* return 1 if we find ecomtarget
*/
-static int bgp_evpn_rt_matches_existing(struct list *rtl,
- struct ecommunity *ecomtarget)
+static bool bgp_evpn_rt_matches_existing(struct list *rtl,
+ struct ecommunity *ecomtarget)
{
- struct listnode *node, *nnode;
+ struct listnode *node;
struct ecommunity *ecom;
- for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) {
+ for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom)) {
if (ecommunity_match(ecom, ecomtarget))
- return 1;
+ return true;
}
- return 0;
+ return false;
+}
+
+/*
+ * L3 RT version of above.
+ */
+static bool bgp_evpn_vrf_rt_matches_existing(struct list *rtl,
+ struct ecommunity *ecomtarget)
+{
+ struct listnode *node;
+ struct vrf_route_target *l3rt;
+
+ for (ALL_LIST_ELEMENTS_RO(rtl, node, l3rt)) {
+ if (ecommunity_match(l3rt->ecom, ecomtarget))
+ return true;
+ }
+
+ return false;
}
/* display L3VNI related info for a VRF instance */
@@ -5685,7 +5704,7 @@ DEFUN (show_bgp_vrf_l3vni_info,
struct bgp *bgp = NULL;
struct listnode *node = NULL;
struct bgpevpn *vpn = NULL;
- struct ecommunity *ecom = NULL;
+ struct vrf_route_target *l3rt;
json_object *json = NULL;
json_object *json_vnis = NULL;
json_object *json_export_rts = NULL;
@@ -5735,13 +5754,13 @@ DEFUN (show_bgp_vrf_l3vni_info,
vty_out(vty, "\n");
vty_out(vty, " Export-RTs:\n");
vty_out(vty, " ");
- for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom))
- vty_out(vty, "%s ", ecommunity_str(ecom));
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, l3rt))
+ vty_out(vty, "%s ", ecommunity_str(l3rt->ecom));
vty_out(vty, "\n");
vty_out(vty, " Import-RTs:\n");
vty_out(vty, " ");
- for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom))
- vty_out(vty, "%s ", ecommunity_str(ecom));
+ 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);
} else {
@@ -5765,17 +5784,19 @@ DEFUN (show_bgp_vrf_l3vni_info,
json_object_object_add(json, "l2vnis", json_vnis);
/* export rts */
- for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom))
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, l3rt))
json_object_array_add(
json_export_rts,
- json_object_new_string(ecommunity_str(ecom)));
+ json_object_new_string(
+ ecommunity_str(l3rt->ecom)));
json_object_object_add(json, "export-rts", json_export_rts);
/* import rts */
- for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom))
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, l3rt))
json_object_array_add(
json_import_rts,
- json_object_new_string(ecommunity_str(ecom)));
+ 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);
}
@@ -5785,22 +5806,133 @@ DEFUN (show_bgp_vrf_l3vni_info,
return CMD_SUCCESS;
}
+static int add_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import,
+ bool is_wildcard)
+{
+ /* Do nothing if we already have this route-target */
+ if (is_import) {
+ if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_import_rtl,
+ ecom))
+ bgp_evpn_configure_import_rt_for_vrf(bgp, ecom,
+ is_wildcard);
+ else
+ return -1;
+ } else {
+ if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_export_rtl,
+ ecom))
+ bgp_evpn_configure_export_rt_for_vrf(bgp, ecom);
+ else
+ return -1;
+ }
+
+ return 0;
+}
+
+static int del_rt(struct bgp *bgp, struct ecommunity *ecom, bool is_import)
+{
+ /* Verify we already have this route-target */
+ if (is_import) {
+ if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_import_rtl,
+ ecom))
+ return -1;
+
+ bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecom);
+ } else {
+ if (!bgp_evpn_vrf_rt_matches_existing(bgp->vrf_export_rtl,
+ ecom))
+ return -1;
+
+ bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecom);
+ }
+
+ return 0;
+}
+
+static int parse_rtlist(struct bgp *bgp, struct vty *vty, int argc,
+ struct cmd_token **argv, int rt_idx, bool is_add,
+ bool is_import)
+{
+ int ret = CMD_SUCCESS;
+ bool is_wildcard = false;
+ struct ecommunity *ecom = NULL;
+
+ for (int i = rt_idx; i < argc; i++) {
+ is_wildcard = false;
+
+ /*
+ * Special handling for wildcard '*' here.
+ *
+ * Let's just convert it to 0 here so we dont have to modify
+ * the ecommunity parser.
+ */
+ if ((argv[i]->arg)[0] == '*') {
+ if (!is_import) {
+ vty_out(vty,
+ "%% Wildcard '*' only applicable for import\n");
+ ret = CMD_WARNING;
+ continue;
+ }
+
+ (argv[i]->arg)[0] = '0';
+ is_wildcard = true;
+ }
+
+ ecom = ecommunity_str2com(argv[i]->arg, ECOMMUNITY_ROUTE_TARGET,
+ 0);
+
+ /* Put it back as was */
+ if (is_wildcard)
+ (argv[i]->arg)[0] = '*';
+
+ if (!ecom) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ ret = CMD_WARNING;
+ continue;
+ }
+
+ ecommunity_str(ecom);
+
+ if (is_add) {
+ if (add_rt(bgp, ecom, is_import, is_wildcard) != 0) {
+ vty_out(vty,
+ "%% RT specified already configured for this VRF: %s\n",
+ argv[i]->arg);
+ ecommunity_free(&ecom);
+ ret = CMD_WARNING;
+ }
+
+ } else {
+ if (del_rt(bgp, ecom, is_import) != 0) {
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VRF: %s\n",
+ argv[i]->arg);
+ ret = CMD_WARNING;
+ }
+
+ ecommunity_free(&ecom);
+ }
+ }
+
+ return ret;
+}
+
/* import/export rt for l3vni-vrf */
DEFUN (bgp_evpn_vrf_rt,
bgp_evpn_vrf_rt_cmd,
- "route-target <both|import|export> RT",
+ "route-target <both|import|export> RTLIST...",
"Route Target\n"
"import and export\n"
"import\n"
"export\n"
- "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|*:OPQR|*:MN)\n")
{
+ int ret = CMD_SUCCESS;
+ int tmp_ret = CMD_SUCCESS;
int rt_type;
struct bgp *bgp = VTY_GET_CONTEXT(bgp);
- struct ecommunity *ecomadd = NULL;
if (!bgp)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
if (!strcmp(argv[1]->arg, "import"))
rt_type = RT_TYPE_IMPORT;
@@ -5810,49 +5942,82 @@ DEFUN (bgp_evpn_vrf_rt,
rt_type = RT_TYPE_BOTH;
else {
vty_out(vty, "%% Invalid Route Target type\n");
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
}
- ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
- if (!ecomadd) {
- vty_out(vty, "%% Malformed Route Target list\n");
- return CMD_WARNING;
+ if (strmatch(argv[2]->arg, "auto")) {
+ vty_out(vty, "%% `auto` cannot be configured via list\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
- ecommunity_str(ecomadd);
/* Add/update the import route-target */
- if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) {
- /* Do nothing if we already have this import route-target */
- if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, ecomadd))
- bgp_evpn_configure_import_rt_for_vrf(bgp, ecomadd);
- }
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
+ tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, true);
- /* Add/update the export route-target */
- if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) {
- /* Do nothing if we already have this export route-target */
- if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, ecomadd))
- bgp_evpn_configure_export_rt_for_vrf(bgp, ecomadd);
+ if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS)
+ ret = tmp_ret;
+
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT)
+ tmp_ret = parse_rtlist(bgp, vty, argc, argv, 2, true, false);
+
+ if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS)
+ ret = tmp_ret;
+
+ return ret;
+}
+
+DEFPY (bgp_evpn_vrf_rt_auto,
+ bgp_evpn_vrf_rt_auto_cmd,
+ "route-target <both|import|export>$type auto",
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ "Automatically derive route target\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ int rt_type;
+
+ if (!bgp)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (strmatch(type, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (strmatch(type, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (strmatch(type, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
+ bgp_evpn_configure_import_auto_rt_for_vrf(bgp);
+
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT)
+ bgp_evpn_configure_export_auto_rt_for_vrf(bgp);
+
return CMD_SUCCESS;
}
DEFUN (no_bgp_evpn_vrf_rt,
no_bgp_evpn_vrf_rt_cmd,
- "no route-target <both|import|export> RT",
+ "no route-target <both|import|export> RTLIST...",
NO_STR
"Route Target\n"
"import and export\n"
"import\n"
"export\n"
- EVPN_ASN_IP_HELP_STR)
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
{
struct bgp *bgp = VTY_GET_CONTEXT(bgp);
- int rt_type, found_ecomdel;
- struct ecommunity *ecomdel = NULL;
+ int ret = CMD_SUCCESS;
+ int tmp_ret = CMD_SUCCESS;
+ int rt_type;
if (!bgp)
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
if (!strcmp(argv[2]->arg, "import"))
rt_type = RT_TYPE_IMPORT;
@@ -5862,79 +6027,104 @@ DEFUN (no_bgp_evpn_vrf_rt,
rt_type = RT_TYPE_BOTH;
else {
vty_out(vty, "%% Invalid Route Target type\n");
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!strcmp(argv[3]->arg, "auto")) {
+ vty_out(vty, "%% `auto` cannot be unconfigured via list\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
if (rt_type == RT_TYPE_IMPORT) {
if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
vty_out(vty,
"%% Import RT is not configured for this VRF\n");
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
}
} else if (rt_type == RT_TYPE_EXPORT) {
if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
vty_out(vty,
"%% Export RT is not configured for this VRF\n");
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
}
} else if (rt_type == RT_TYPE_BOTH) {
if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)
&& !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
vty_out(vty,
"%% Import/Export RT is not configured for this VRF\n");
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
}
}
- ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
- if (!ecomdel) {
- vty_out(vty, "%% Malformed Route Target list\n");
- return CMD_WARNING;
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
+ tmp_ret = parse_rtlist(bgp, vty, argc, argv, 3, false, true);
+
+ if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS)
+ ret = tmp_ret;
+
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT)
+ tmp_ret = parse_rtlist(bgp, vty, argc, argv, 3, false, false);
+
+ if (ret == CMD_SUCCESS && tmp_ret != CMD_SUCCESS)
+ ret = tmp_ret;
+
+ return ret;
+}
+
+DEFPY (no_bgp_evpn_vrf_rt_auto,
+ no_bgp_evpn_vrf_rt_auto_cmd,
+ "no route-target <both|import|export>$type auto",
+ NO_STR
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ "Automatically derive route target\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ int rt_type;
+
+ if (!bgp)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (strmatch(type, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (strmatch(type, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (strmatch(type, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
- ecommunity_str(ecomdel);
if (rt_type == RT_TYPE_IMPORT) {
- if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
- ecomdel)) {
- ecommunity_free(&ecomdel);
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD)) {
vty_out(vty,
- "%% RT specified does not match configuration for this VRF\n");
- return CMD_WARNING;
+ "%% Import AUTO RT is not configured for this VRF\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
- bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel);
} else if (rt_type == RT_TYPE_EXPORT) {
- if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
- ecomdel)) {
- ecommunity_free(&ecomdel);
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) {
vty_out(vty,
- "%% RT specified does not match configuration for this VRF\n");
- return CMD_WARNING;
+ "%% Export AUTO RT is not configured for this VRF\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
- bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel);
} else if (rt_type == RT_TYPE_BOTH) {
- found_ecomdel = 0;
-
- if (bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
- ecomdel)) {
- bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel);
- found_ecomdel = 1;
- }
-
- if (bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
- ecomdel)) {
- bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel);
- found_ecomdel = 1;
- }
-
- if (!found_ecomdel) {
- ecommunity_free(&ecomdel);
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD) &&
+ !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD)) {
vty_out(vty,
- "%% RT specified does not match configuration for this VRF\n");
- return CMD_WARNING;
+ "%% Import/Export AUTO RT is not configured for this VRF\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
}
- ecommunity_free(&ecomdel);
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT)
+ bgp_evpn_unconfigure_import_auto_rt_for_vrf(bgp);
+
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT)
+ bgp_evpn_unconfigure_export_auto_rt_for_vrf(bgp);
+
return CMD_SUCCESS;
}
@@ -6421,31 +6611,62 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
char *ecom_str;
struct listnode *node, *nnode;
- struct ecommunity *ecom;
+ struct vrf_route_target *l3rt;
for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode,
- ecom)) {
+ l3rt)) {
+
+ if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO))
+ continue;
+
ecom_str = ecommunity_ecom2str(
- ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
- vty_out(vty, " route-target import %s\n", ecom_str);
+ l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_WILD)) {
+ char *vni_str = NULL;
+
+ vni_str = strchr(ecom_str, ':') + 1;
+
+ if (!vni_str)
+ continue; /* This should never happen */
+
+ vty_out(vty, " route-target import *:%s\n",
+ vni_str);
+
+ } else
+ vty_out(vty, " route-target import %s\n",
+ ecom_str);
+
XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
}
}
+ /* import route-target auto */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_AUTO_RT_CFGD))
+ vty_out(vty, " route-target import auto\n");
+
/* export route-target */
if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
char *ecom_str;
struct listnode *node, *nnode;
- struct ecommunity *ecom;
+ struct vrf_route_target *l3rt;
for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode,
- ecom)) {
+ l3rt)) {
+
+ if (CHECK_FLAG(l3rt->flags, BGP_VRF_RT_AUTO))
+ continue;
+
ecom_str = ecommunity_ecom2str(
- ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ l3rt->ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
vty_out(vty, " route-target export %s\n", ecom_str);
XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
}
}
+
+ /* export route-target auto */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_AUTO_RT_CFGD))
+ vty_out(vty, " route-target export auto\n");
}
void bgp_ethernetvpn_init(void)
@@ -6547,6 +6768,8 @@ void bgp_ethernetvpn_init(void)
install_element(BGP_NODE, &no_bgp_evpn_vrf_rd_without_val_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_auto_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_auto_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_rt_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_ead_es_rt_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_frag_evi_limit_cmd);
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 66eef1aa52..f1ffa7b840 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -656,8 +656,10 @@ void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi)
tovpn_sid_locator, tovpn_sid);
if (tovpn_sid_transpose_label == 0) {
- zlog_debug("%s: not allocated new sid for vrf %s: afi %s",
- __func__, bgp_vrf->name_pretty, afi2str(afi));
+ if (debug)
+ zlog_debug(
+ "%s: not allocated new sid for vrf %s: afi %s",
+ __func__, bgp_vrf->name_pretty, afi2str(afi));
srv6_locator_chunk_free(tovpn_sid_locator);
XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid);
return;
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 22d28ecd00..7c6e60bd92 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -609,19 +609,35 @@ static inline bool bgp_check_advertise(struct bgp *bgp, struct bgp_dest *dest)
*/
static inline bool bgp_check_withdrawal(struct bgp *bgp, struct bgp_dest *dest)
{
- struct bgp_path_info *pi;
+ struct bgp_path_info *pi, *selected = NULL;
if (!BGP_SUPPRESS_FIB_ENABLED(bgp))
return false;
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
- if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+ selected = pi;
continue;
+ }
if (pi->sub_type != BGP_ROUTE_NORMAL)
return true;
}
+ /*
+ * pi is selected and bgp is dealing with a static route
+ * ( ie a network statement of some sort ). FIB installed
+ * is irrelevant
+ *
+ * I am not sure what the above for loop is wanted in this
+ * manner at this point. But I do know that if I have
+ * a static route that is selected and it's the one
+ * being checked for should I withdrawal we do not
+ * want to withdraw the route on installation :)
+ */
+ if (selected && selected->sub_type == BGP_ROUTE_STATIC)
+ return false;
+
if (CHECK_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED))
return false;
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index ded47028a6..233fd55ef0 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -643,6 +643,20 @@ route_match_prefix_list_flowspec(afi_t afi, struct prefix_list *plist,
}
static enum route_map_cmd_result_t
+route_match_prefix_list_evpn(afi_t afi, struct prefix_list *plist,
+ const struct prefix *p)
+{
+ /* Convert to match a general plist */
+ struct prefix new;
+
+ if (evpn_prefix2prefix(p, &new))
+ return RMAP_NOMATCH;
+
+ return (prefix_list_apply(plist, &new) == PREFIX_DENY ? RMAP_NOMATCH
+ : RMAP_MATCH);
+}
+
+static enum route_map_cmd_result_t
route_match_address_prefix_list(void *rule, afi_t afi,
const struct prefix *prefix, void *object)
{
@@ -655,6 +669,10 @@ route_match_address_prefix_list(void *rule, afi_t afi,
if (prefix->family == AF_FLOWSPEC)
return route_match_prefix_list_flowspec(afi, plist,
prefix);
+
+ else if (prefix->family == AF_EVPN)
+ return route_match_prefix_list_evpn(afi, plist, prefix);
+
return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH
: RMAP_MATCH);
}
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index b85f3707b6..e574ae7805 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -5816,7 +5816,7 @@ ALIAS_HIDDEN(neighbor_remove_private_as_all,
"neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS all",
NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Remove private ASNs in outbound updates\n"
- "Apply to all AS numbers")
+ "Apply to all AS numbers\n")
DEFUN (neighbor_remove_private_as_replace_as,
neighbor_remove_private_as_replace_as_cmd,
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 44e225b043..64d87f946c 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -755,8 +755,10 @@ struct bgp {
#define BGP_VRF_AUTO (1 << 0)
#define BGP_VRF_IMPORT_RT_CFGD (1 << 1)
#define BGP_VRF_EXPORT_RT_CFGD (1 << 2)
-#define BGP_VRF_RD_CFGD (1 << 3)
-#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 4)
+#define BGP_VRF_IMPORT_AUTO_RT_CFGD (1 << 3) /* retain auto when cfgd */
+#define BGP_VRF_EXPORT_AUTO_RT_CFGD (1 << 4) /* retain auto when cfgd */
+#define BGP_VRF_RD_CFGD (1 << 5)
+#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 6)
/* unique ID for auto derivation of RD for this vrf */
uint16_t vrf_rd_id;
diff --git a/configure.ac b/configure.ac
index 4e1080045e..4cbdfe0fcc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2631,6 +2631,7 @@ AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra ap
AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket])
AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information])
AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information])
+AC_DEFINE_UNQUOTED([ISISD_RESTART], ["$frr_statedir%s/isid-restart.json"], [isisd restart information])
AC_DEFINE_UNQUOTED([OSPF6_AUTH_SEQ_NUM_FILE], ["$frr_statedir/ospf6d-at-seq-no.dat"], [ospf6d AT Sequence number information])
AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory])
AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory])
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 8a16c57e6c..e32a84f80c 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -2944,6 +2944,20 @@ sysctl configurations:
For more information, see ``man 7 arp``.
+.. _bgp-evpn-l3-route-targets:
+
+EVPN L3 Route-Targets
+^^^^^^^^^^^^^^^^^^^^^
+
+.. clicmd:: route-target <import|export|both> <RTLIST|auto>
+
+Modify the route-target set for EVPN advertised type-2/type-5 routes.
+RTLIST is a list of any of matching
+``(A.B.C.D:MN|EF:OPQR|GHJK:MN|*:OPQR|*:MN)`` where ``*`` indicates wildcard
+matching for the AS number. It will be set to match any AS number. This is
+useful in datacenter deployments with Downstream VNI. ``auto`` is used to
+retain the autoconfigure that is default behavior for L3 RTs.
+
.. _bgp-evpn-advertise-pip:
EVPN advertise-PIP
diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst
index 9ccb5ba4b5..2b114ad127 100644
--- a/doc/user/isisd.rst
+++ b/doc/user/isisd.rst
@@ -83,6 +83,10 @@ writing, *isisd* does not support multiple ISIS processes.
Set overload bit to avoid any transit traffic.
+.. clicmd:: set-overload-bit on-startup (0-86400)
+
+ Set overload bit on startup for the specified duration, in seconds. Reference: :rfc:`3277`
+
.. clicmd:: purge-originator
Enable or disable :rfc:`6232` purge originator identification.
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index a673cb8c1e..9db867e2c0 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -404,7 +404,7 @@ DEFPY_YANG(set_overload_bit, set_overload_bit_cmd, "[no] set-overload-bit",
"Reset overload bit to accept transit traffic\n"
"Set overload bit to avoid any transit traffic\n")
{
- nb_cli_enqueue_change(vty, "./overload", NB_OP_MODIFY,
+ nb_cli_enqueue_change(vty, "./overload/enabled", NB_OP_MODIFY,
no ? "false" : "true");
return nb_cli_apply_changes(vty, NULL);
@@ -419,6 +419,42 @@ void cli_show_isis_overload(struct vty *vty, const struct lyd_node *dnode,
}
/*
+ * XPath: /frr-isisd:isis/instance/overload/on-startup
+ */
+DEFPY_YANG(set_overload_bit_on_startup, set_overload_bit_on_startup_cmd,
+ "set-overload-bit on-startup (0-86400)$val",
+ "Set overload bit to avoid any transit traffic\n"
+ "Set overload bit on startup\n"
+ "Set overload time in seconds\n")
+{
+ nb_cli_enqueue_change(vty, "./overload/on-startup", NB_OP_MODIFY,
+ val_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_set_overload_bit_on_startup, no_set_overload_bit_on_startup_cmd,
+ "no set-overload-bit on-startup [(0-86400)$val]",
+ NO_STR
+ "Reset overload bit to accept transit traffic\n"
+ "Set overload bit on startup\n"
+ "Set overload time in seconds\n")
+{
+ nb_cli_enqueue_change(vty, "./overload/on-startup", NB_OP_DESTROY,
+ NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_overload_on_startup(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " set-overload-bit on-startup %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
* XPath: /frr-isisd:isis/instance/attach-send
*/
DEFPY_YANG(attached_bit_send, attached_bit_send_cmd, "[no] attached-bit send",
@@ -3107,6 +3143,9 @@ void isis_cli_init(void)
install_element(ISIS_NODE, &dynamic_hostname_cmd);
install_element(ISIS_NODE, &set_overload_bit_cmd);
+ install_element(ISIS_NODE, &set_overload_bit_on_startup_cmd);
+ install_element(ISIS_NODE, &no_set_overload_bit_on_startup_cmd);
+
install_element(ISIS_NODE, &attached_bit_send_cmd);
install_element(ISIS_NODE, &attached_bit_receive_ignore_cmd);
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 5387f37039..63b4edb1e1 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -68,6 +68,8 @@ static void lsp_l2_refresh_pseudo(struct thread *thread);
static void lsp_destroy(struct isis_lsp *lsp);
+static bool device_startup;
+
int lsp_id_cmp(uint8_t *id1, uint8_t *id2)
{
return memcmp(id1, id2, ISIS_SYS_ID_LEN + 2);
@@ -437,6 +439,21 @@ bool isis_level2_adj_up(struct isis_area *area)
return false;
}
+/*
+ * Unset the overload bit after the timer expires
+ */
+void set_overload_on_start_timer(struct thread *thread)
+{
+ struct isis_area *area = THREAD_ARG(thread);
+ assert(area);
+
+ area->t_overload_on_startup_timer = NULL;
+
+ /* Check if set-overload-bit is not currently configured */
+ if (!area->overload_configured)
+ isis_area_overload_bit_set(area, false);
+}
+
static void isis_reset_attach_bit(struct isis_adjacency *adj)
{
struct isis_area *area = adj->circuit->area;
@@ -1355,6 +1372,7 @@ int lsp_generate(struct isis_area *area, int level)
uint32_t seq_num = 0;
uint8_t lspid[ISIS_SYS_ID_LEN + 2];
uint16_t rem_lifetime, refresh_time;
+ uint32_t overload_time;
if ((area == NULL) || (area->is_type & level) != level)
return ISIS_ERROR;
@@ -1363,6 +1381,18 @@ int lsp_generate(struct isis_area *area, int level)
memcpy(&lspid, area->isis->sysid, ISIS_SYS_ID_LEN);
+ /* Check if device should be overloaded on startup */
+ if (device_startup) {
+ overload_time = isis_restart_read_overload_time(area);
+ if (overload_time > 0) {
+ isis_area_overload_bit_set(area, true);
+ thread_add_timer(master, set_overload_on_start_timer,
+ area, overload_time,
+ &area->t_overload_on_startup_timer);
+ }
+ device_startup = false;
+ }
+
/* only builds the lsp if the area shares the level */
oldlsp = lsp_search(&area->lspdb[level - 1], lspid);
if (oldlsp) {
@@ -2373,6 +2403,7 @@ int isis_lsp_iterate_is_reach(struct isis_lsp *lsp, uint16_t mtid,
void lsp_init(void)
{
+ device_startup = true;
hook_register(isis_adj_state_change_hook,
lsp_handle_adj_state_change);
}
diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h
index b13b2a35e6..d7762324d9 100644
--- a/isisd/isis_lsp.h
+++ b/isisd/isis_lsp.h
@@ -66,6 +66,7 @@ DECLARE_RBTREE_UNIQ(lspdb, struct isis_lsp, dbe, lspdb_compare);
void lsp_db_init(struct lspdb_head *head);
void lsp_db_fini(struct lspdb_head *head);
void lsp_tick(struct thread *thread);
+void set_overload_on_start_timer(struct thread *thread);
int lsp_generate(struct isis_area *area, int level);
#define lsp_regenerate_schedule(area, level, all_pseudo) \
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index a2ba33d078..4f4e6dc730 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -81,11 +81,18 @@ const struct frr_yang_module_info frr_isisd_info = {
},
},
{
- .xpath = "/frr-isisd:isis/instance/overload",
+ .xpath = "/frr-isisd:isis/instance/overload/enabled",
.cbs = {
.cli_show = cli_show_isis_overload,
- .modify = isis_instance_overload_modify,
- },
+ .modify = isis_instance_overload_enabled_modify,
+ }
+ },
+ {
+ .xpath = "/frr-isisd:isis/instance/overload/on-startup",
+ .cbs = {
+ .cli_show = cli_show_isis_overload_on_startup,
+ .modify = isis_instance_overload_on_startup_modify,
+ }
},
{
.xpath = "/frr-isisd:isis/instance/metric-style",
diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h
index 00ca8be3b0..a9f2eaea95 100644
--- a/isisd/isis_nb.h
+++ b/isisd/isis_nb.h
@@ -37,7 +37,8 @@ int isis_instance_dynamic_hostname_modify(struct nb_cb_modify_args *args);
int isis_instance_attached_send_modify(struct nb_cb_modify_args *args);
int isis_instance_attached_receive_modify(struct nb_cb_modify_args *args);
int isis_instance_attached_modify(struct nb_cb_modify_args *args);
-int isis_instance_overload_modify(struct nb_cb_modify_args *args);
+int isis_instance_overload_enabled_modify(struct nb_cb_modify_args *args);
+int isis_instance_overload_on_startup_modify(struct nb_cb_modify_args *args);
int isis_instance_metric_style_modify(struct nb_cb_modify_args *args);
int isis_instance_purge_originator_modify(struct nb_cb_modify_args *args);
int isis_instance_lsp_mtu_modify(struct nb_cb_modify_args *args);
@@ -442,6 +443,9 @@ void cli_show_isis_attached_receive(struct vty *vty,
bool show_defaults);
void cli_show_isis_overload(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
+void cli_show_isis_overload_on_startup(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_isis_metric_style(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
void cli_show_isis_area_pwd(struct vty *vty, const struct lyd_node *dnode,
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index e0decf48f2..1b7663fcfd 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -336,9 +336,9 @@ int isis_instance_attached_modify(struct nb_cb_modify_args *args)
}
/*
- * XPath: /frr-isisd:isis/instance/overload
+ * XPath: /frr-isisd:isis/instance/overload/enabled
*/
-int isis_instance_overload_modify(struct nb_cb_modify_args *args)
+int isis_instance_overload_enabled_modify(struct nb_cb_modify_args *args)
{
struct isis_area *area;
bool overload;
@@ -348,12 +348,32 @@ int isis_instance_overload_modify(struct nb_cb_modify_args *args)
area = nb_running_get_entry(args->dnode, NULL, true);
overload = yang_dnode_get_bool(args->dnode, NULL);
+ area->overload_configured = overload;
+
isis_area_overload_bit_set(area, overload);
return NB_OK;
}
/*
+ * XPath: /frr-isisd:isis/instance/overload/on-startup
+ */
+int isis_instance_overload_on_startup_modify(struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ uint32_t overload_time;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ overload_time = yang_dnode_get_uint32(args->dnode, NULL);
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ isis_area_overload_on_startup_set(area, overload_time);
+
+ return NB_OK;
+}
+
+/*
* XPath: /frr-isisd:isis/instance/metric-style
*/
int isis_instance_metric_style_modify(struct nb_cb_modify_args *args)
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 0ff31df0f8..efea1e5d5e 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -3198,9 +3198,15 @@ void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit)
if (new_overload_bit != area->overload_bit) {
area->overload_bit = new_overload_bit;
-
- if (new_overload_bit)
+ if (new_overload_bit) {
area->overload_counter++;
+ } else {
+ /* Cancel overload on startup timer if it's running */
+ if (area->t_overload_on_startup_timer) {
+ THREAD_OFF(area->t_overload_on_startup_timer);
+ area->t_overload_on_startup_timer = NULL;
+ }
+ }
#ifndef FABRICD
hook_call(isis_hook_db_overload, area);
@@ -3213,6 +3219,109 @@ void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit)
#endif /* ifndef FABRICD */
}
+void isis_area_overload_on_startup_set(struct isis_area *area,
+ uint32_t startup_time)
+{
+ if (area->overload_on_startup_time != startup_time) {
+ area->overload_on_startup_time = startup_time;
+ isis_restart_write_overload_time(area, startup_time);
+ }
+}
+
+/*
+ * Returns the path of the file (non-volatile memory) that contains restart
+ * information.
+ */
+char *isis_restart_filepath()
+{
+ static char filepath[MAXPATHLEN];
+ snprintf(filepath, sizeof(filepath), ISISD_RESTART, "");
+ return filepath;
+}
+
+/*
+ * Record in non-volatile memory the overload on startup time.
+ */
+void isis_restart_write_overload_time(struct isis_area *isis_area,
+ uint32_t overload_time)
+{
+ char *filepath;
+ const char *area_name;
+ json_object *json;
+ json_object *json_areas;
+ json_object *json_area;
+
+ filepath = isis_restart_filepath();
+ area_name = isis_area->area_tag;
+
+ json = json_object_from_file(filepath);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "areas", &json_areas);
+ if (!json_areas) {
+ json_areas = json_object_new_object();
+ json_object_object_add(json, "areas", json_areas);
+ }
+
+ json_object_object_get_ex(json_areas, area_name, &json_area);
+ if (!json_area) {
+ json_area = json_object_new_object();
+ json_object_object_add(json_areas, area_name, json_area);
+ }
+
+ json_object_int_add(json_area, "overload_time",
+ isis_area->overload_on_startup_time);
+ json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/*
+ * Fetch from non-volatile memory the overload on startup time.
+ */
+uint32_t isis_restart_read_overload_time(struct isis_area *isis_area)
+{
+ char *filepath;
+ const char *area_name;
+ json_object *json;
+ json_object *json_areas;
+ json_object *json_area;
+ json_object *json_overload_time;
+ uint32_t overload_time = 0;
+
+ filepath = isis_restart_filepath();
+ area_name = isis_area->area_tag;
+
+ json = json_object_from_file(filepath);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "areas", &json_areas);
+ if (!json_areas) {
+ json_areas = json_object_new_object();
+ json_object_object_add(json, "areas", json_areas);
+ }
+
+ json_object_object_get_ex(json_areas, area_name, &json_area);
+ if (!json_area) {
+ json_area = json_object_new_object();
+ json_object_object_add(json_areas, area_name, json_area);
+ }
+
+ json_object_object_get_ex(json_area, "overload_time",
+ &json_overload_time);
+ if (json_overload_time) {
+ overload_time = json_object_get_int(json_overload_time);
+ }
+
+ json_object_object_del(json_areas, area_name);
+
+ json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+
+ return overload_time;
+}
+
void isis_area_attached_bit_send_set(struct isis_area *area, bool attached_bit)
{
diff --git a/isisd/isisd.h b/isisd/isisd.h
index 4951e5809b..a9c1d60439 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -142,6 +142,7 @@ struct isis_area {
struct flags flags;
struct thread *t_tick; /* LSP walker */
struct thread *t_lsp_refresh[ISIS_LEVELS];
+ struct thread *t_overload_on_startup_timer;
struct timeval last_lsp_refresh_event[ISIS_LEVELS];
struct thread *t_rlfa_rib_update;
/* t_lsp_refresh is used in two ways:
@@ -180,7 +181,9 @@ struct isis_area {
char is_type; /* level-1 level-1-2 or level-2-only */
/* are we overloaded? */
char overload_bit;
+ bool overload_configured;
uint32_t overload_counter;
+ uint32_t overload_on_startup_time;
/* L1/L2 router identifier for inter-area traffic */
char attached_bit_send;
char attached_bit_rcv_ignore;
@@ -290,6 +293,8 @@ void isis_area_invalidate_routes(struct isis_area *area, int levels);
void isis_area_verify_routes(struct isis_area *area);
void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit);
+void isis_area_overload_on_startup_set(struct isis_area *area,
+ uint32_t startup_time);
void isis_area_attached_bit_send_set(struct isis_area *area, bool attached_bit);
void isis_area_attached_bit_receive_set(struct isis_area *area,
bool attached_bit);
@@ -315,7 +320,10 @@ void show_isis_database_lspdb_json(struct json_object *json,
void show_isis_database_lspdb_vty(struct vty *vty, struct isis_area *area,
int level, struct lspdb_head *lspdb,
const char *argv, int ui_level);
-
+char *isis_restart_filepath(void);
+void isis_restart_write_overload_time(struct isis_area *isis_area,
+ uint32_t overload_time);
+uint32_t isis_restart_read_overload_time(struct isis_area *isis_area);
/* YANG paths */
#define ISIS_INSTANCE "/frr-isisd:isis/instance"
#define ISIS_SR "/frr-isisd:isis/instance/segment-routing"
diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c
index 3795cdaf38..3d11d3137a 100644
--- a/ldpd/ldp_vty_cmds.c
+++ b/ldpd/ldp_vty_cmds.c
@@ -246,7 +246,7 @@ DEFPY (ldp_allow_broken_lsps,
"[no] install allow-broken-lsps",
NO_STR
"install lsps\n"
- "if no remote-label install with imp-null")
+ "if no remote-label install with imp-null\n")
{
return (ldp_vty_allow_broken_lsp(vty, no));
}
diff --git a/lib/command.c b/lib/command.c
index 7e171cb309..1fae32a04a 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -265,8 +265,7 @@ void install_node(struct cmd_node *node)
node->cmdgraph = graph_new();
node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
// add start node
- struct cmd_token *token =
- cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+ struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
graph_new_node(node->cmdgraph, token,
(void (*)(void *)) & cmd_token_del);
@@ -326,7 +325,7 @@ void _install_element(enum node_type ntype, const struct cmd_element *cmd)
if (cnode->graph_built || !defer_cli_tree) {
struct graph *graph = graph_new();
struct cmd_token *token =
- cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+ cmd_token_new(START_TKN, 0, NULL, NULL);
graph_new_node(graph, token,
(void (*)(void *)) & cmd_token_del);
@@ -349,8 +348,7 @@ static void cmd_finalize_iter(struct hash_bucket *hb, void *arg)
struct cmd_node *cnode = arg;
const struct cmd_element *cmd = hb->data;
struct graph *graph = graph_new();
- struct cmd_token *token =
- cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+ struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
@@ -405,7 +403,7 @@ void uninstall_element(enum node_type ntype, const struct cmd_element *cmd)
if (cnode->graph_built) {
struct graph *graph = graph_new();
struct cmd_token *token =
- cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+ cmd_token_new(START_TKN, 0, NULL, NULL);
graph_new_node(graph, token,
(void (*)(void *)) & cmd_token_del);
@@ -991,7 +989,7 @@ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter,
* Perform pending commit (if any) before executing
* non-YANG command.
*/
- if (matched_element->attr != CMD_ATTR_YANG)
+ if (!(matched_element->attr & CMD_ATTR_YANG))
(void)nb_cli_pending_commit_check(vty);
}
@@ -1472,8 +1470,7 @@ static void permute(struct graph_node *start, struct vty *vty)
for (unsigned int i = 0; i < vector_active(start->to); i++) {
struct graph_node *gn = vector_slot(start->to, i);
struct cmd_token *tok = gn->data;
- if (tok->attr == CMD_ATTR_HIDDEN
- || tok->attr == CMD_ATTR_DEPRECATED)
+ if (tok->attr & CMD_ATTR_HIDDEN)
continue;
else if (tok->type == END_TKN || gn == start) {
vty_out(vty, " ");
@@ -1562,9 +1559,8 @@ int cmd_list_cmds(struct vty *vty, int do_permute)
const struct cmd_element *element = NULL;
for (unsigned int i = 0; i < vector_active(node->cmd_vector);
i++)
- if ((element = vector_slot(node->cmd_vector, i))
- && element->attr != CMD_ATTR_DEPRECATED
- && element->attr != CMD_ATTR_HIDDEN) {
+ if ((element = vector_slot(node->cmd_vector, i)) &&
+ !(element->attr & CMD_ATTR_HIDDEN)) {
vty_out(vty, " ");
print_cmd(vty, element->string);
}
diff --git a/lib/command.h b/lib/command.h
index 42b4406212..f4168dedd7 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -280,17 +280,18 @@ struct cmd_node {
int argc __attribute__((unused)), \
struct cmd_token *argv[] __attribute__((unused)))
-#define DEFPY(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
- funcdecl_##funcname
-
-#define DEFPY_NOSH(funcname, cmdname, cmdstr, helpstr) \
- DEFPY(funcname, cmdname, cmdstr, helpstr)
+/* DEFPY variants */
#define DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
funcdecl_##funcname
+#define DEFPY(funcname, cmdname, cmdstr, helpstr) \
+ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, 0)
+
+#define DEFPY_NOSH(funcname, cmdname, cmdstr, helpstr) \
+ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_NOSH)
+
#define DEFPY_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
@@ -298,18 +299,19 @@ struct cmd_node {
DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
#define DEFPY_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \
- DEFPY_YANG(funcname, cmdname, cmdstr, helpstr)
+ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, \
+ CMD_ATTR_YANG | CMD_ATTR_NOSH)
-#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
- DEFUN_CMD_FUNC_TEXT(funcname)
+/* DEFUN variants */
#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_FUNC_DECL(funcname) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
DEFUN_CMD_FUNC_TEXT(funcname)
+#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, 0)
+
#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
@@ -318,73 +320,55 @@ struct cmd_node {
/* DEFUN_NOSH for commands that vtysh should ignore */
#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
- DEFUN(funcname, cmdname, cmdstr, helpstr)
+ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_NOSH)
#define DEFUN_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_YANG(funcname, cmdname, cmdstr, helpstr)
+ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, \
+ CMD_ATTR_YANG | CMD_ATTR_NOSH)
/* DEFSH for vtysh. */
+#define DEFSH_ATTR(daemon, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, attr, daemon)
+
#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon)
+ DEFSH_ATTR(daemon, cmdname, cmdstr, helpstr, 0)
#define DEFSH_HIDDEN(daemon, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, \
- daemon)
-
-#define DEFSH_YANG(daemon, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_YANG, daemon)
+ DEFSH_ATTR(daemon, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
/* DEFUN + DEFSH */
-#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
- DEFUN_CMD_FUNC_TEXT(funcname)
-
-/* DEFUN + DEFSH with attributes */
#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_FUNC_DECL(funcname) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
DEFUN_CMD_FUNC_TEXT(funcname)
+#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, 0)
+
#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, \
CMD_ATTR_HIDDEN)
-#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, \
- CMD_ATTR_DEPRECATED)
-
-#define DEFUNSH_YANG(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
-
/* ALIAS macro which define existing command's alias. */
-#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
-
#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
+#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
+ ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, 0)
+
#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, \
- 0)
+ ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+/* note: DEPRECATED implies HIDDEN, and other than that there is currently no
+ * difference. It's purely for expressing intent in the source code - a
+ * DEPRECATED command is supposed to go away, a HIDDEN one is likely to stay.
+ */
#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, \
- CMD_ATTR_DEPRECATED, 0)
+ ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, \
+ CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)
#define ALIAS_YANG(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG, 0)
-
-#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
-
-#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, \
- daemon)
-
-#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, \
- CMD_ATTR_DEPRECATED, daemon)
+ ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
#endif /* VTYSH_EXTRACT_PL */
diff --git a/lib/command_graph.c b/lib/command_graph.c
index 09d802e796..e940685250 100644
--- a/lib/command_graph.c
+++ b/lib/command_graph.c
@@ -494,9 +494,10 @@ void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf)
snprintf(nbuf, sizeof(nbuf), "<b>%s</b>",
lookup_msg(tokennames, tok->type, NULL));
buffer_putstr(buf, nbuf);
- if (tok->attr == CMD_ATTR_DEPRECATED)
+ if (tok->attr & CMD_ATTR_DEPRECATED)
buffer_putstr(buf, " (d)");
- else if (tok->attr == CMD_ATTR_HIDDEN)
+ /* DEPRECATED implies HIDDEN, don't print both */
+ else if (tok->attr & CMD_ATTR_HIDDEN)
buffer_putstr(buf, " (h)");
if (tok->text) {
if (tok->type == WORD_TKN)
diff --git a/lib/command_graph.h b/lib/command_graph.h
index ed4da6aa4c..b8c7a9c72c 100644
--- a/lib/command_graph.h
+++ b/lib/command_graph.h
@@ -73,10 +73,11 @@ enum cmd_token_type {
#define IS_VARYING_TOKEN(x) ((x) >= VARIABLE_TKN && (x) < FORK_TKN)
/* Command attributes */
-enum { CMD_ATTR_NORMAL,
- CMD_ATTR_DEPRECATED,
- CMD_ATTR_HIDDEN,
- CMD_ATTR_YANG,
+enum {
+ CMD_ATTR_YANG = (1 << 0),
+ CMD_ATTR_HIDDEN = (1 << 1),
+ CMD_ATTR_DEPRECATED = (1 << 2),
+ CMD_ATTR_NOSH = (1 << 3),
};
enum varname_src {
diff --git a/lib/command_match.c b/lib/command_match.c
index f221e0a02c..ce2dbc9528 100644
--- a/lib/command_match.c
+++ b/lib/command_match.c
@@ -395,8 +395,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline,
for (ALL_LIST_ELEMENTS_RO(current, node, gstack)) {
struct cmd_token *token = gstack[0]->data;
- if (token->attr == CMD_ATTR_HIDDEN
- || token->attr == CMD_ATTR_DEPRECATED)
+ if (token->attr & CMD_ATTR_HIDDEN)
continue;
enum match_type minmatch = min_match_level(token->type);
diff --git a/lib/command_py.c b/lib/command_py.c
index 6301eec5e8..cce9542e30 100644
--- a/lib/command_py.c
+++ b/lib/command_py.c
@@ -226,8 +226,8 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph,
wrap->type = "???";
}
- wrap->deprecated = (tok->attr == CMD_ATTR_DEPRECATED);
- wrap->hidden = (tok->attr == CMD_ATTR_HIDDEN);
+ wrap->deprecated = !!(tok->attr & CMD_ATTR_DEPRECATED);
+ wrap->hidden = !!(tok->attr & CMD_ATTR_HIDDEN);
wrap->text = tok->text;
wrap->desc = tok->desc;
wrap->varname = tok->varname;
@@ -353,6 +353,12 @@ PyMODINIT_FUNC command_py_init(void)
if (!pymod)
initret(NULL);
+ if (PyModule_AddIntMacro(pymod, CMD_ATTR_YANG)
+ || PyModule_AddIntMacro(pymod, CMD_ATTR_HIDDEN)
+ || PyModule_AddIntMacro(pymod, CMD_ATTR_DEPRECATED)
+ || PyModule_AddIntMacro(pymod, CMD_ATTR_NOSH))
+ initret(NULL);
+
Py_INCREF(&typeobj_graph_node);
PyModule_AddObject(pymod, "GraphNode", (PyObject *)&typeobj_graph_node);
Py_INCREF(&typeobj_graph);
diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c
index f9778c5d4c..8fa47c053b 100644
--- a/lib/grammar_sandbox.c
+++ b/lib/grammar_sandbox.c
@@ -76,8 +76,7 @@ DEFUN (grammar_test,
// parse the command and install it into the command graph
struct graph *graph = graph_new();
- struct cmd_token *token =
- cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
+ struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL);
graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
cmd_graph_parse(graph, cmd);
diff --git a/lib/log_vty.c b/lib/log_vty.c
index 81280f302f..c9268734c4 100644
--- a/lib/log_vty.c
+++ b/lib/log_vty.c
@@ -761,8 +761,8 @@ DEFPY (log_immediate_mode,
log_immediate_mode_cmd,
"[no] log immediate-mode",
NO_STR
- "Logging control"
- "Output immediately, without buffering")
+ "Logging control\n"
+ "Output immediately, without buffering\n")
{
zlog_set_immediate(!no);
return CMD_SUCCESS;
diff --git a/lib/prefix.c b/lib/prefix.c
index e64b10bf24..4642f14d35 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -1404,6 +1404,63 @@ bool ipv4_unicast_valid(const struct in_addr *addr)
return true;
}
+static int ipaddr2prefix(const struct ipaddr *ip, uint16_t prefixlen,
+ struct prefix *p)
+{
+ switch (ip->ipa_type) {
+ case (IPADDR_V4):
+ p->family = AF_INET;
+ p->u.prefix4 = ip->ipaddr_v4;
+ p->prefixlen = prefixlen;
+ break;
+ case (IPADDR_V6):
+ p->family = AF_INET6;
+ p->u.prefix6 = ip->ipaddr_v6;
+ p->prefixlen = prefixlen;
+ break;
+ case (IPADDR_NONE):
+ p->family = AF_UNSPEC;
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Convert type-2 and type-5 evpn route prefixes into the more
+ * general ipv4/ipv6 prefix types so we can match prefix lists
+ * and such.
+ */
+int evpn_prefix2prefix(const struct prefix *evpn, struct prefix *to)
+{
+ const struct evpn_addr *addr;
+
+ if (evpn->family != AF_EVPN)
+ return -1;
+
+ addr = &evpn->u.prefix_evpn;
+
+ switch (addr->route_type) {
+ case BGP_EVPN_MAC_IP_ROUTE:
+ if (IS_IPADDR_V4(&addr->macip_addr.ip))
+ ipaddr2prefix(&addr->macip_addr.ip, 32, to);
+ else if (IS_IPADDR_V6(&addr->macip_addr.ip))
+ ipaddr2prefix(&addr->macip_addr.ip, 128, to);
+ else
+ return -1; /* mac only? */
+
+ break;
+ case BGP_EVPN_IP_PREFIX_ROUTE:
+ ipaddr2prefix(&addr->prefix_addr.ip,
+ addr->prefix_addr.ip_prefix_length, to);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
printfrr_ext_autoreg_p("EA", printfrr_ea);
static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
diff --git a/lib/prefix.h b/lib/prefix.h
index b904311539..c67656cfd1 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -510,6 +510,7 @@ extern char *esi_to_str(const esi_t *esi, char *buf, int size);
extern char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len);
extern void prefix_evpn_hexdump(const struct prefix_evpn *p);
extern bool ipv4_unicast_valid(const struct in_addr *addr);
+extern int evpn_prefix2prefix(const struct prefix *evpn, struct prefix *to);
static inline int ipv6_martian(const struct in6_addr *addr)
{
diff --git a/lib/routemap.c b/lib/routemap.c
index 3a92799991..dcaecd8085 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -1815,7 +1815,24 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix,
struct route_map_index *index = NULL, *best_index = NULL;
struct route_map_index *head_index = NULL;
struct route_table *table = NULL;
- unsigned char family = prefix->family;
+ struct prefix conv;
+ unsigned char family;
+
+ /*
+ * Handling for matching evpn_routes in the prefix table.
+ *
+ * We convert type2/5 prefix to ipv4/6 prefix to do longest
+ * prefix matching on.
+ */
+ if (prefix->family == AF_EVPN) {
+ if (evpn_prefix2prefix(prefix, &conv) != 0)
+ return NULL;
+
+ prefix = &conv;
+ }
+
+
+ family = prefix->family;
if (family == AF_INET)
table = map->ipv4_prefix_table;
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index 646d318362..a0b14e73ee 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -461,13 +461,13 @@ struct ospf_interface *ospf_if_lookup_recv_if(struct ospf *ospf,
{
struct route_node *rn;
struct prefix_ipv4 addr;
- struct ospf_interface *oi, *match;
+ struct ospf_interface *oi, *match, *unnumbered_match;
addr.family = AF_INET;
addr.prefix = src;
addr.prefixlen = IPV4_MAX_BITLEN;
- match = NULL;
+ match = unnumbered_match = NULL;
for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
oi = rn->info;
@@ -482,7 +482,7 @@ struct ospf_interface *ospf_if_lookup_recv_if(struct ospf *ospf,
continue;
if (CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED))
- match = oi;
+ unnumbered_match = oi;
else if (prefix_match(CONNECTED_PREFIX(oi->connected),
(struct prefix *)&addr)) {
if ((match == NULL) || (match->address->prefixlen
@@ -491,7 +491,10 @@ struct ospf_interface *ospf_if_lookup_recv_if(struct ospf *ospf,
}
}
- return match;
+ if (match)
+ return match;
+
+ return unnumbered_match;
}
void ospf_interface_fifo_flush(struct ospf_interface *oi)
diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c
index 23042ef14e..c34c782969 100644
--- a/pimd/pim6_mld.c
+++ b/pimd/pim6_mld.c
@@ -2997,9 +2997,9 @@ DEFPY(gm_debug_show,
"debug show mld interface IFNAME",
DEBUG_STR
SHOW_STR
- "MLD"
+ MLD_STR
INTERFACE_STR
- "interface name")
+ "interface name\n")
{
struct interface *ifp;
struct pim_interface *pim_ifp;
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 1ac22f38a3..ab0689a156 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -5438,7 +5438,7 @@ DEFPY(no_ip_msdp_mesh_group,
IP_STR
CFG_MSDP_STR
"Delete MSDP mesh-group\n"
- "Mesh group name")
+ "Mesh group name\n")
{
const char *vrfname;
char xpath_value[XPATH_MAXLEN];
diff --git a/python/clippy/__init__.py b/python/clippy/__init__.py
index 344a1c91ee..281e2bb3c6 100644
--- a/python/clippy/__init__.py
+++ b/python/clippy/__init__.py
@@ -17,8 +17,23 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os, stat
+
+try:
+ from enum import IntFlag as _IntFlag
+except ImportError:
+ # python <3.6
+ from enum import IntEnum as _IntFlag # type: ignore
+
import _clippy
-from _clippy import parse, Graph, GraphNode
+from _clippy import (
+ parse,
+ Graph,
+ GraphNode,
+ CMD_ATTR_YANG,
+ CMD_ATTR_HIDDEN,
+ CMD_ATTR_DEPRECATED,
+ CMD_ATTR_NOSH,
+)
frr_top_src = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -78,3 +93,10 @@ def wrdiff(filename, buf, reffiles=[]):
with open(newname, "w") as out:
out.write(buf)
os.rename(newname, filename)
+
+
+class CmdAttr(_IntFlag):
+ YANG = CMD_ATTR_YANG
+ HIDDEN = CMD_ATTR_HIDDEN
+ DEPRECATED = CMD_ATTR_DEPRECATED
+ NOSH = CMD_ATTR_NOSH
diff --git a/python/makefile.py b/python/makefile.py
index afc993b5b9..bd897b7508 100644
--- a/python/makefile.py
+++ b/python/makefile.py
@@ -160,6 +160,9 @@ for clippy_file in clippy_scan:
# combine daemon .xref files into frr.xref
out_lines.append("")
+xref_targets = [
+ target for target in xref_targets if target not in ["tools/ssd", "vtysh/vtysh"]
+]
out_lines.append(
"xrefs = %s" % (" ".join(["%s.xref" % target for target in xref_targets]))
)
diff --git a/python/xrelfo.py b/python/xrelfo.py
index 17262da8d9..09455ea9b4 100644
--- a/python/xrelfo.py
+++ b/python/xrelfo.py
@@ -21,12 +21,21 @@ import os
import struct
import re
import traceback
-import json
+
+json_dump_args = {}
+
+try:
+ import ujson as json
+
+ json_dump_args["escape_forward_slashes"] = False
+except ImportError:
+ import json
+
import argparse
from clippy.uidhash import uidhash
from clippy.elf import *
-from clippy import frr_top_src
+from clippy import frr_top_src, CmdAttr
from tiabwarfo import FieldApplicator
try:
@@ -196,8 +205,6 @@ Xref.containers[XREFT_LOGMSG] = XrefLogmsg
class CmdElement(ELFDissectStruct, XrelfoJson):
struct = 'cmd_element'
- cmd_attrs = { 0: None, 1: 'deprecated', 2: 'hidden'}
-
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -207,10 +214,14 @@ class CmdElement(ELFDissectStruct, XrelfoJson):
jsobj.update({
'string': self.string,
'doc': self.doc,
- 'attr': self.cmd_attrs.get(self.attr, self.attr),
})
- if jsobj['attr'] is None:
- del jsobj['attr']
+ if self.attr:
+ jsobj['attr'] = attr = self.attr
+ for attrname in CmdAttr.__members__:
+ val = CmdAttr[attrname]
+ if attr & val:
+ jsobj.setdefault('attrs', []).append(attrname.lower())
+ attr &= ~val
jsobj['defun'] = dict([(i, getattr(self.xref, i)) for i in ['file', 'line', 'func']])
@@ -416,12 +427,12 @@ def _main(args):
if args.output:
with open(args.output + '.tmp', 'w') as fd:
- json.dump(out, fd, indent=2, sort_keys=True)
+ json.dump(out, fd, indent=2, sort_keys=True, **json_dump_args)
os.rename(args.output + '.tmp', args.output)
if args.out_by_file:
with open(args.out_by_file + '.tmp', 'w') as fd:
- json.dump(outbyfile, fd, indent=2, sort_keys=True)
+ json.dump(outbyfile, fd, indent=2, sort_keys=True, **json_dump_args)
os.rename(args.out_by_file + '.tmp', args.out_by_file)
if __name__ == '__main__':
diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in
index 9729be7b92..f634b59c75 100644
--- a/snapcraft/snapcraft.yaml.in
+++ b/snapcraft/snapcraft.yaml.in
@@ -272,7 +272,7 @@ parts:
- zlib1g
prime:
- lib/librtr.so*
- - usr/lib/x86_64-linux-gnu/libssh.so*
+ - usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libssh.so*
source: https://github.com/rtrlib/rtrlib.git
source-type: git
source-tag: v0.8.0
diff --git a/tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json b/tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json
index bc4d0f4796..1a5ede276d 100644
--- a/tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json
+++ b/tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json
@@ -32,7 +32,7 @@
],
"peer":{
"peerId":"10.0.0.2",
- "routerId":"10.0.0.9",
+ "routerId":"60.0.0.1",
"type":"external"
}
}
diff --git a/tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json b/tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json
index 16561ce837..4a35abfd6f 100644
--- a/tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json
+++ b/tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json
@@ -61,7 +61,7 @@
],
"peer":{
"peerId":"0.0.0.0",
- "routerId":"10.0.0.9"
+ "routerId":"60.0.0.1"
}
}
]
diff --git a/tests/topotests/bgp_suppress_fib/r2/bgpd.conf b/tests/topotests/bgp_suppress_fib/r2/bgpd.conf
index ebef2012a8..010e86aad7 100644
--- a/tests/topotests/bgp_suppress_fib/r2/bgpd.conf
+++ b/tests/topotests/bgp_suppress_fib/r2/bgpd.conf
@@ -7,3 +7,5 @@ router bgp 2
bgp suppress-fib-pending
neighbor 10.0.0.1 remote-as 1
neighbor 10.0.0.10 remote-as 3
+ address-family ipv4 uni
+ network 60.0.0.0/24 \ No newline at end of file
diff --git a/tests/topotests/bgp_suppress_fib/r2/zebra.conf b/tests/topotests/bgp_suppress_fib/r2/zebra.conf
index 443fffc703..6e8bce0450 100644
--- a/tests/topotests/bgp_suppress_fib/r2/zebra.conf
+++ b/tests/topotests/bgp_suppress_fib/r2/zebra.conf
@@ -1,4 +1,7 @@
!
+interface lo
+ ip address 60.0.0.1/24
+!
interface r2-eth0
ip address 10.0.0.2/30
!
diff --git a/tests/topotests/bgp_suppress_fib/r3/v4_route3.json b/tests/topotests/bgp_suppress_fib/r3/v4_route3.json
new file mode 100644
index 0000000000..ab8c3aa5e5
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r3/v4_route3.json
@@ -0,0 +1,23 @@
+{
+ "60.0.0.0/24":[
+ {
+ "prefix":"60.0.0.0/24",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.0.9",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
index 2c87d9d7b0..96a294cae3 100644
--- a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
+++ b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
@@ -84,8 +84,6 @@ def test_bgp_route():
r3 = tgen.gears["r3"]
- sleep(5)
-
json_file = "{}/r3/v4_route.json".format(CWD)
expected = json.loads(open(json_file).read())
@@ -95,7 +93,7 @@ def test_bgp_route():
"show ip route 40.0.0.0 json",
expected,
)
- _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
assertmsg = '"r3" JSON output mismatches'
assert result is None, assertmsg
@@ -112,6 +110,16 @@ def test_bgp_route():
assertmsg = '"r3" JSON output mismatches'
assert result is None, assertmsg
+ json_file = "{}/r3/v4_route3.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ r3,
+ "show ip route 10.0.0.3 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5)
def test_bgp_better_admin_won():
"A better Admin distance protocol may come along and knock us out"
diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py
index 014722387f..8b6c3772b8 100644
--- a/tests/topotests/isis_topo1/test_isis_topo1.py
+++ b/tests/topotests/isis_topo1/test_isis_topo1.py
@@ -25,7 +25,7 @@
"""
test_isis_topo1.py: Test ISIS topology.
"""
-
+import datetime
import functools
import json
import os
@@ -38,6 +38,11 @@ sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
from lib import topotest
+from lib.common_config import (
+ retry,
+ stop_router,
+ start_router,
+)
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
@@ -248,10 +253,12 @@ def test_isis_summary_json():
for rname, router in tgen.routers().items():
logger.info("Checking router %s", rname)
json_output = tgen.gears[rname].vtysh_cmd("show isis summary json", isjson=True)
- assertmsg = "Test isis summary json failed in '{}' data '{}'".format(rname, json_output)
- assert json_output['vrf'] == "default", assertmsg
- assert json_output['areas'][0]['area'] == "1", assertmsg
- assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg
+ assertmsg = "Test isis summary json failed in '{}' data '{}'".format(
+ rname, json_output
+ )
+ assert json_output["vrf"] == "default", assertmsg
+ assert json_output["areas"][0]["area"] == "1", assertmsg
+ assert json_output["areas"][0]["levels"][0]["id"] != "3", assertmsg
def test_isis_interface_json():
@@ -265,15 +272,29 @@ def test_isis_interface_json():
logger.info("Checking 'show isis interface json'")
for rname, router in tgen.routers().items():
logger.info("Checking router %s", rname)
- json_output = tgen.gears[rname].vtysh_cmd("show isis interface json", isjson=True)
- assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output)
- assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg
+ json_output = tgen.gears[rname].vtysh_cmd(
+ "show isis interface json", isjson=True
+ )
+ assertmsg = "Test isis interface json failed in '{}' data '{}'".format(
+ rname, json_output
+ )
+ assert (
+ json_output["areas"][0]["circuits"][0]["interface"]["name"]
+ == rname + "-eth0"
+ ), assertmsg
for rname, router in tgen.routers().items():
logger.info("Checking router %s", rname)
- json_output = tgen.gears[rname].vtysh_cmd("show isis interface detail json", isjson=True)
- assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output)
- assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg
+ json_output = tgen.gears[rname].vtysh_cmd(
+ "show isis interface detail json", isjson=True
+ )
+ assertmsg = "Test isis interface json failed in '{}' data '{}'".format(
+ rname, json_output
+ )
+ assert (
+ json_output["areas"][0]["circuits"][0]["interface"]["name"]
+ == rname + "-eth0"
+ ), assertmsg
def test_isis_neighbor_json():
@@ -284,19 +305,32 @@ def test_isis_neighbor_json():
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
- #tgen.mininet_cli()
+ # tgen.mininet_cli()
logger.info("Checking 'show isis neighbor json'")
for rname, router in tgen.routers().items():
logger.info("Checking router %s", rname)
- json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor json", isjson=True)
- assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output)
- assert json_output['areas'][0]['circuits'][0]['interface'] == rname+"-eth0", assertmsg
+ json_output = tgen.gears[rname].vtysh_cmd(
+ "show isis neighbor json", isjson=True
+ )
+ assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(
+ rname, json_output
+ )
+ assert (
+ json_output["areas"][0]["circuits"][0]["interface"] == rname + "-eth0"
+ ), assertmsg
for rname, router in tgen.routers().items():
logger.info("Checking router %s", rname)
- json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor detail json", isjson=True)
- assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output)
- assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg
+ json_output = tgen.gears[rname].vtysh_cmd(
+ "show isis neighbor detail json", isjson=True
+ )
+ assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(
+ rname, json_output
+ )
+ assert (
+ json_output["areas"][0]["circuits"][0]["interface"]["name"]
+ == rname + "-eth0"
+ ), assertmsg
def test_isis_database_json():
@@ -307,21 +341,246 @@ def test_isis_database_json():
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
- #tgen.mininet_cli()
+ # tgen.mininet_cli()
logger.info("Checking 'show isis database json'")
for rname, router in tgen.routers().items():
logger.info("Checking router %s", rname)
- json_output = tgen.gears[rname].vtysh_cmd("show isis database json", isjson=True)
- assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output)
- assert json_output['areas'][0]['area']['name'] == "1", assertmsg
- assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg
+ json_output = tgen.gears[rname].vtysh_cmd(
+ "show isis database json", isjson=True
+ )
+ assertmsg = "Test isis database json failed in '{}' data '{}'".format(
+ rname, json_output
+ )
+ assert json_output["areas"][0]["area"]["name"] == "1", assertmsg
+ assert json_output["areas"][0]["levels"][0]["id"] != "3", assertmsg
for rname, router in tgen.routers().items():
logger.info("Checking router %s", rname)
- json_output = tgen.gears[rname].vtysh_cmd("show isis database detail json", isjson=True)
- assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output)
- assert json_output['areas'][0]['area']['name'] == "1", assertmsg
- assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg
+ json_output = tgen.gears[rname].vtysh_cmd(
+ "show isis database detail json", isjson=True
+ )
+ assertmsg = "Test isis database json failed in '{}' data '{}'".format(
+ rname, json_output
+ )
+ assert json_output["areas"][0]["area"]["name"] == "1", assertmsg
+ assert json_output["areas"][0]["levels"][0]["id"] != "3", assertmsg
+
+
+def test_isis_overload_on_startup():
+ "Check that overload on startup behaves as expected"
+
+ tgen = get_topogen()
+ net = get_topogen().net
+ overload_time = 120
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing overload on startup behavior")
+
+ # Configure set-overload-bit on-startup on r3
+ r3 = tgen.gears["r3"]
+ r3.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ set-overload-bit on-startup {overload_time}
+ """
+ )
+ # Restart r3
+ logger.info("Stop router")
+ stop_router(tgen, "r3")
+ logger.info("Start router")
+
+ tstamp_before_start_router = datetime.datetime.now()
+ start_router(tgen, "r3")
+ tstamp_after_start_router = datetime.datetime.now()
+ startup_router_time = (
+ tstamp_after_start_router - tstamp_before_start_router
+ ).total_seconds()
+
+ # Check that the overload bit is set in r3's LSP
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/1")
+ check_lsp_overload_bit("r1", "r3.00-00", "0/0/1")
+
+ # Attempt to unset overload bit while timer is still running
+ r3.vtysh_cmd(
+ """
+ configure
+ router isis 1
+ no set-overload-bit on-startup
+ no set-overload-bit
+ """
+ )
+
+ # Check overload bit is still set
+ check_lsp_overload_bit("r1", "r3.00-00", "0/0/1")
+
+ # Check that overload bit is unset after timer completes
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/0")
+ tstamp_after_bit_unset = datetime.datetime.now()
+ check_lsp_overload_bit("r1", "r3.00-00", "0/0/0")
+
+ # Collect time overloaded
+ time_overloaded = (
+ tstamp_after_bit_unset - tstamp_after_start_router
+ ).total_seconds()
+ logger.info(f"Time Overloaded: {time_overloaded}")
+
+ # Use time it took to startup router as lower bound
+ logger.info(
+ f"Assert that overload time falls in range: {overload_time - startup_router_time} < {time_overloaded} <= {overload_time}"
+ )
+ result = overload_time - startup_router_time < time_overloaded <= overload_time
+ assert result
+
+
+def test_isis_overload_on_startup_cancel_timer():
+ "Check that overload on startup timer is cancelled when overload bit is set/unset"
+
+ tgen = get_topogen()
+ net = get_topogen().net
+ overload_time = 90
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Testing overload on startup behavior with set overload bit: cancel timer"
+ )
+
+ # Configure set-overload-bit on-startup on r3
+ r3 = tgen.gears["r3"]
+ r3.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ set-overload-bit on-startup {overload_time}
+ set-overload-bit
+ """
+ )
+ # Restart r3
+ logger.info("Stop router")
+ stop_router(tgen, "r3")
+ logger.info("Start router")
+ start_router(tgen, "r3")
+
+ # Check that the overload bit is set in r3's LSP
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/1")
+
+ # Check that overload timer is running
+ check_overload_timer("r3", True)
+
+ # Unset overload bit while timer is running
+ r3.vtysh_cmd(
+ """
+ configure
+ router isis 1
+ no set-overload-bit
+ """
+ )
+
+ # Check that overload timer is cancelled
+ check_overload_timer("r3", False)
+
+ # Check overload bit is unset
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/0")
+
+
+def test_isis_overload_on_startup_override_timer():
+ "Check that overload bit remains set after overload timer expires if overload bit is configured"
+
+ tgen = get_topogen()
+ net = get_topogen().net
+ overload_time = 60
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Testing overload on startup behavior with set overload bit: override timer"
+ )
+
+ # Configure set-overload-bit on-startup on r3
+ r3 = tgen.gears["r3"]
+ r3.vtysh_cmd(
+ f"""
+ configure
+ router isis 1
+ set-overload-bit on-startup {overload_time}
+ set-overload-bit
+ """
+ )
+ # Restart r3
+ logger.info("Stop router")
+ stop_router(tgen, "r3")
+ logger.info("Start router")
+ start_router(tgen, "r3")
+
+ # Check that the overload bit is set in r3's LSP
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/1")
+
+ # Check that overload timer is running
+ check_overload_timer("r3", True)
+
+ # Check that overload timer expired
+ check_overload_timer("r3", False)
+
+ # Check overload bit is still set
+ check_lsp_overload_bit("r3", "r3.00-00", "0/0/1")
+
+
+@retry(retry_timeout=200)
+def _check_lsp_overload_bit(router, overloaded_router_lsp, att_p_ol_expected):
+ "Verfiy overload bit in router's LSP"
+
+ tgen = get_topogen()
+ router = tgen.gears[router]
+ logger.info(f"check_overload_bit {router}")
+ isis_database_output = router.vtysh_cmd(
+ "show isis database {} json".format(overloaded_router_lsp)
+ )
+
+ database_json = json.loads(isis_database_output)
+ att_p_ol = database_json["areas"][0]["levels"][1]["att-p-ol"]
+ if att_p_ol == att_p_ol_expected:
+ return True
+ return "{} peer with expected att_p_ol {} got {} ".format(
+ router.name, att_p_ol_expected, att_p_ol
+ )
+
+
+def check_lsp_overload_bit(router, overloaded_router_lsp, att_p_ol_expected):
+ "Verfiy overload bit in router's LSP"
+
+ assertmsg = _check_lsp_overload_bit(
+ router, overloaded_router_lsp, att_p_ol_expected
+ )
+ assert assertmsg is True, assertmsg
+
+
+@retry(retry_timeout=200)
+def _check_overload_timer(router, timer_expected):
+ "Verfiy overload bit in router's LSP"
+
+ tgen = get_topogen()
+ router = tgen.gears[router]
+ thread_output = router.vtysh_cmd("show thread timers")
+
+ timer_running = "set_overload_on_start_timer" in thread_output
+ if timer_running == timer_expected:
+ return True
+ return "Expected timer running status: {}".format(timer_expected)
+
+
+def check_overload_timer(router, timer_expected):
+ "Verfiy overload bit in router's LSP"
+
+ assertmsg = _check_overload_timer(router, timer_expected)
+ assert assertmsg is True, assertmsg
def test_memory_leak():
diff --git a/tools/frr.in b/tools/frr.in
index f0c665fdee..e9f1122834 100755
--- a/tools/frr.in
+++ b/tools/frr.in
@@ -524,7 +524,7 @@ convert_daemon_prios
if [ ! -d $V_PATH ]; then
echo "Creating $V_PATH"
- install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" -d /proc "$V_PATH"
+ install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" -d "$V_PATH"
chmod gu+x "${V_PATH}"
fi
diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in
index 469b9c5d8c..61f1abb378 100755
--- a/tools/frrcommon.sh.in
+++ b/tools/frrcommon.sh.in
@@ -161,7 +161,7 @@ daemon_start() {
[ "$MAX_FDS" != "" ] && ulimit -n "$MAX_FDS" > /dev/null 2> /dev/null
daemon_prep "$daemon" "$inst" || return 1
if test ! -d "$V_PATH"; then
- install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" -d /proc "$V_PATH"
+ install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" -d "$V_PATH"
chmod gu+x "${V_PATH}"
fi
diff --git a/tools/permutations.c b/tools/permutations.c
index b280cc15b1..a0b041f2f2 100644
--- a/tools/permutations.c
+++ b/tools/permutations.c
@@ -80,8 +80,7 @@ void permute(struct graph_node *start)
for (unsigned int i = 0; i < vector_active(start->to); i++) {
struct graph_node *gn = vector_slot(start->to, i);
struct cmd_token *tok = gn->data;
- if (tok->attr == CMD_ATTR_HIDDEN
- || tok->attr == CMD_ATTR_DEPRECATED)
+ if (tok->attr & CMD_ATTR_HIDDEN)
continue;
else if (tok->type == END_TKN || gn == start) {
fprintf(stdout, " ");
diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c
index d2f25f2d40..aea7d9abc7 100644
--- a/vrrpd/vrrp_vty.c
+++ b/vrrpd/vrrp_vty.c
@@ -121,7 +121,7 @@ DEFPY_YANG(vrrp_priority,
VRRP_STR
VRRP_VRID_STR
VRRP_PRIORITY_STR
- "Priority value")
+ "Priority value\n")
{
nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, priority_str);
@@ -138,7 +138,7 @@ DEFPY_YANG(no_vrrp_priority,
VRRP_STR
VRRP_VRID_STR
VRRP_PRIORITY_STR
- "Priority value")
+ "Priority value\n")
{
nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, NULL);
@@ -162,7 +162,7 @@ DEFPY_YANG(vrrp_advertisement_interval,
vrrp_advertisement_interval_cmd,
"vrrp (1-255)$vrid advertisement-interval (10-40950)",
VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
- "Advertisement interval in milliseconds; must be multiple of 10")
+ "Advertisement interval in milliseconds; must be multiple of 10\n")
{
char val[20];
@@ -183,7 +183,7 @@ DEFPY_YANG(no_vrrp_advertisement_interval,
no_vrrp_advertisement_interval_cmd,
"no vrrp (1-255)$vrid advertisement-interval [(10-40950)]",
NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
- "Advertisement interval in milliseconds; must be multiple of 10")
+ "Advertisement interval in milliseconds; must be multiple of 10\n")
{
nb_cli_enqueue_change(vty, "./advertisement-interval", NB_OP_MODIFY,
NULL);
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index f39ecf0709..48274d7170 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -2951,7 +2951,7 @@ DEFUN_HIDDEN (show_config_running,
show_config_running_cmd,
"show configuration running\
[<json|xml> [translate WORD]]\
- [with-defaults]" DAEMONS_LIST,
+ [with-defaults] " DAEMONS_LIST,
SHOW_STR
"Configuration information\n"
"Running configuration\n"
@@ -2972,7 +2972,7 @@ DEFUN (show_yang_operational_data,
format <json|xml>\
|translate WORD\
|with-config\
- }]" DAEMONS_LIST,
+ }] " DAEMONS_LIST,
SHOW_STR
"YANG information\n"
"Show YANG operational data\n"
diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang
index 0812c86fac..380fce3859 100644
--- a/yang/frr-isisd.yang
+++ b/yang/frr-isisd.yang
@@ -1066,11 +1066,25 @@ module frr-isisd {
"If true, identify as L1/L2 router for inter-area traffic.";
}
- leaf overload {
- type boolean;
- default "false";
+ container overload {
description
- "If true, avoid any transit traffic.";
+ "Overload bit configuration.";
+ leaf enabled {
+ type boolean;
+ default "false";
+ description
+ "If true, avoid any transit traffic.";
+ }
+
+ leaf on-startup {
+ type uint32 {
+ range "0..86400";
+ }
+ units "seconds";
+ default "0";
+ description
+ "The duration the overload bit should be set on startup.";
+ }
}
leaf metric-style {
diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c
index 1b2753377b..c74c692ee1 100644
--- a/zebra/zebra_fpm.c
+++ b/zebra/zebra_fpm.c
@@ -1841,12 +1841,15 @@ DEFUN (clear_zebra_fpm_stats,
/*
* update fpm connection information
*/
-DEFUN ( fpm_remote_ip,
+DEFUN (fpm_remote_ip,
fpm_remote_ip_cmd,
- "fpm connection ip A.B.C.D port (1-65535)",
- "fpm connection remote ip and port\n"
- "Remote fpm server ip A.B.C.D\n"
- "Enter ip ")
+ "fpm connection ip A.B.C.D port (1-65535)",
+ "Forwarding Path Manager\n"
+ "Configure FPM connection\n"
+ "Connect to IPv4 address\n"
+ "Connect to IPv4 address\n"
+ "TCP port number\n"
+ "TCP port number\n")
{
in_addr_t fpm_server;
@@ -1867,13 +1870,16 @@ DEFUN ( fpm_remote_ip,
return CMD_SUCCESS;
}
-DEFUN ( no_fpm_remote_ip,
+DEFUN (no_fpm_remote_ip,
no_fpm_remote_ip_cmd,
- "no fpm connection ip A.B.C.D port (1-65535)",
- "fpm connection remote ip and port\n"
- "Connection\n"
- "Remote fpm server ip A.B.C.D\n"
- "Enter ip ")
+ "no fpm connection ip A.B.C.D port (1-65535)",
+ NO_STR
+ "Forwarding Path Manager\n"
+ "Remove configured FPM connection\n"
+ "Connect to IPv4 address\n"
+ "Connect to IPv4 address\n"
+ "TCP port number\n"
+ "TCP port number\n")
{
if (zfpm_g->fpm_server != inet_addr(argv[4]->arg)
|| zfpm_g->fpm_port != atoi(argv[6]->arg))