summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/behind-base.yml28
-rw-r--r--bgpd/bgp_attr.c4
-rw-r--r--bgpd/bgp_ecommunity.c16
-rw-r--r--bgpd/bgp_mac.c4
-rw-r--r--bgpd/bgp_route.c42
-rw-r--r--bgpd/bgp_routemap.c79
-rw-r--r--bgpd/bgp_routemap_nb.c7
-rw-r--r--bgpd/bgp_routemap_nb.h4
-rw-r--r--bgpd/bgp_routemap_nb_config.c54
-rw-r--r--bgpd/bgp_vty.c13
-rw-r--r--bgpd/bgpd.c1
-rw-r--r--bgpd/bgpd.h1
-rw-r--r--doc/developer/building-frr-for-ubuntu2204.rst171
-rw-r--r--doc/developer/topotests.rst85
-rw-r--r--doc/developer/workflow.rst3
-rw-r--r--doc/user/bgp.rst123
-rw-r--r--doc/user/ospf6d.rst17
-rw-r--r--doc/user/ospfd.rst17
-rw-r--r--doc/user/ripd.rst5
-rw-r--r--doc/user/routemap.rst2
-rw-r--r--doc/user/static.rst4
-rw-r--r--ldpd/interface.c18
-rw-r--r--ldpd/labelmapping.c236
-rw-r--r--ldpd/lde_lib.c89
-rw-r--r--ldpd/ldpe.c59
-rw-r--r--ldpd/neighbor.c36
-rw-r--r--ldpd/packet.c69
-rw-r--r--lib/command.c8
-rw-r--r--lib/libfrr.c44
-rw-r--r--lib/libospf.h1
-rw-r--r--lib/nexthop.c9
-rw-r--r--lib/nexthop.h3
-rw-r--r--lib/routemap.h2
-rw-r--r--lib/routemap_cli.c7
-rw-r--r--lib/vty.c18
-rw-r--r--lib/vty.h1
-rw-r--r--mgmtd/mgmt.h12
-rw-r--r--mgmtd/mgmt_ds.h3
-rw-r--r--mgmtd/mgmt_fe_adapter.c4
-rw-r--r--mgmtd/mgmt_history.c54
-rw-r--r--mgmtd/mgmt_history.h38
-rw-r--r--ospf6d/ospf6_gr.c196
-rw-r--r--ospf6d/ospf6_gr.h6
-rw-r--r--ospf6d/ospf6_interface.c74
-rw-r--r--ospf6d/ospf6_interface.h9
-rw-r--r--ospf6d/ospf6_message.c4
-rw-r--r--ospf6d/ospf6_spf.c1
-rw-r--r--ospf6d/ospf6_top.c17
-rw-r--r--ospf6d/ospf6_top.h2
-rw-r--r--ospf6d/ospf6_zebra.c16
-rw-r--r--ospf6d/subdir.am1
-rw-r--r--ospfd/ospf_abr.c6
-rw-r--r--ospfd/ospf_apiserver.c26
-rw-r--r--ospfd/ospf_ase.c1
-rw-r--r--ospfd/ospf_gr.c211
-rw-r--r--ospfd/ospf_gr.h7
-rw-r--r--ospfd/ospf_interface.c7
-rw-r--r--ospfd/ospf_interface.h11
-rw-r--r--ospfd/ospf_ism.c6
-rw-r--r--ospfd/ospf_lsa.c3
-rw-r--r--ospfd/ospf_opaque.c16
-rw-r--r--ospfd/ospf_packet.c7
-rw-r--r--ospfd/ospf_packet.h3
-rw-r--r--ospfd/ospf_ti_lfa.c1
-rw-r--r--ospfd/ospf_vty.c93
-rw-r--r--ospfd/ospf_zebra.c16
-rw-r--r--ospfd/ospfd.c12
-rw-r--r--ospfd/ospfd.h2
-rw-r--r--pimd/pim6_mld.c15
-rw-r--r--pimd/pim_iface.c6
-rw-r--r--ripd/rip_bfd.c2
-rw-r--r--ripd/rip_bfd.h2
-rw-r--r--ripd/rip_cli.c40
-rw-r--r--ripd/rip_interface.c8
-rw-r--r--ripd/rip_nb_config.c10
-rw-r--r--ripd/rip_zebra.c11
-rw-r--r--ripd/ripd.c65
-rw-r--r--ripd/ripd.h4
-rw-r--r--tests/lib/subdir.am6
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref2
-rw-r--r--tests/topotests/all_protocol_startup/test_all_protocol_startup.py6
-rw-r--r--tests/topotests/bgp_aigp/r2/bgpd.conf3
-rw-r--r--tests/topotests/bgp_aigp/r3/bgpd.conf3
-rw-r--r--tests/topotests/bgp_aigp/r4/bgpd.conf5
-rw-r--r--tests/topotests/bgp_aigp/r5/bgpd.conf5
-rw-r--r--tests/topotests/bgp_aigp/r6/bgpd.conf2
-rw-r--r--tests/topotests/bgp_aigp/r7/bgpd.conf2
-rw-r--r--tests/topotests/bgp_aigp/test_bgp_aigp.py42
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth1.py4
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth2.py4
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth3.py4
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth4.py4
-rwxr-xr-xtests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json35
-rw-r--r--tests/topotests/bgp_features/test_bgp_features.py29
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/__init__.py0
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf32
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf10
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf10
-rw-r--r--tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py115
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json15
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf3
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf5
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf4
-rw-r--r--tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py20
-rwxr-xr-xtests/topotests/conftest.py58
-rw-r--r--tests/topotests/evpn_pim_1/spine/bgp.summ.json2
-rw-r--r--tests/topotests/lib/bgp.py11
-rw-r--r--tests/topotests/lib/bgprib.py1
-rw-r--r--tests/topotests/lib/common_config.py9
-rw-r--r--tests/topotests/lib/lutil.py18
-rw-r--r--tests/topotests/lib/micronet_compat.py2
-rw-r--r--tests/topotests/lib/ospf.py19
-rw-r--r--tests/topotests/lib/pim.py15
-rwxr-xr-xtests/topotests/lib/test/test_json.py1
-rw-r--r--tests/topotests/lib/topojson.py6
-rw-r--r--tests/topotests/lib/topotest.py52
-rw-r--r--tests/topotests/munet/base.py10
-rwxr-xr-xtests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py138
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py14
-rwxr-xr-xtests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py138
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/frr.conf6
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json2
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json2
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json2
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json2
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json2
-rw-r--r--tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json2
-rw-r--r--tests/topotests/ospf_metric_propagation/r2/frr.conf6
-rw-r--r--tests/topotests/ospf_metric_propagation/r3/frr.conf6
-rw-r--r--tests/topotests/ospf_metric_propagation/r4/frr.conf6
-rw-r--r--tests/topotests/ospf_metric_propagation/ra/frr.conf6
-rw-r--r--tests/topotests/ospf_metric_propagation/rb/frr.conf6
-rw-r--r--tests/topotests/ospf_metric_propagation/rc/frr.conf4
-rw-r--r--tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py41
-rw-r--r--tests/topotests/ospfapi/test_ospf_clientapi.py195
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py2
-rw-r--r--tests/topotests/pytest.ini2
-rw-r--r--tests/topotests/rip_allow_ecmp/r4/frr.conf13
-rw-r--r--tests/topotests/rip_allow_ecmp/r5/frr.conf13
-rw-r--r--tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py34
-rw-r--r--yang/example/ripd.json2
-rw-r--r--yang/example/ripd.xml2
-rw-r--r--yang/frr-bgp-route-map.yang17
-rw-r--r--yang/frr-ripd.yang4
-rw-r--r--zebra/redistribute.c4
-rw-r--r--zebra/zebra_dplane.c65
-rw-r--r--zebra/zebra_nhg.c87
-rw-r--r--zebra/zebra_nhg.h1
-rw-r--r--zebra/zebra_rib.c37
149 files changed, 2821 insertions, 922 deletions
diff --git a/.github/workflows/behind-base.yml b/.github/workflows/behind-base.yml
new file mode 100644
index 000000000..16b643477
--- /dev/null
+++ b/.github/workflows/behind-base.yml
@@ -0,0 +1,28 @@
+name: Add rebase label if the branch is > 50 commits behind
+
+on:
+ pull_request_target:
+ types: [synchronize, opened, reopened, labeled, unlabeled]
+
+jobs:
+ behind:
+ if: github.repository == 'frrouting/frr'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ fetch-depth: 0
+ - name: Set custom variables
+ id: vars
+ run: |
+ echo "behind_by=$(git log --oneline origin/${{ github.base_ref }} ^${{ github.event.pull_request.head.sha }} | wc -l)" >> $GITHUB_OUTPUT
+ - name: Add rebase label if needed
+ if: ${{ steps.vars.outputs.behind_by > 50 }}
+ uses: actions-ecosystem/action-add-labels@v1
+ with:
+ labels: rebase
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 5b06bc391..195298c81 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -1467,7 +1467,7 @@ const uint8_t attr_flags_values[] = {
[BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_IPV6_EXT_COMMUNITIES] =
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
- [BGP_ATTR_AIGP] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_AIGP] = BGP_ATTR_FLAG_OPTIONAL,
};
static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
@@ -4740,7 +4740,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
*/
uint8_t attr_len = BGP_AIGP_TLV_METRIC_LEN;
- stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
stream_putc(s, BGP_ATTR_AIGP);
stream_putc(s, attr_len);
stream_put_bgp_aigp_tlv_metric(s, bpi);
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 7bf260901..a55593013 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -445,7 +445,8 @@ bool ecommunity_node_target_match(struct ecommunity *ecom,
return match;
}
-static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr)
+static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr,
+ int format)
{
/*
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -458,7 +459,11 @@ static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr)
IPV4_ADDR_COPY(&node_id, (struct in_addr *)ptr);
- snprintfrr(buf, bufsz, "NT:%pI4", &node_id);
+
+ snprintfrr(buf, bufsz, "%s%pI4%s",
+ format == ECOMMUNITY_FORMAT_COMMUNITY_LIST ? "nt " : "NT:",
+ &node_id,
+ format == ECOMMUNITY_FORMAT_COMMUNITY_LIST ? ":0" : "");
(void)ptr; /* consume value */
}
@@ -1059,7 +1064,8 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
} else if (sub_type == ECOMMUNITY_NODE_TARGET &&
type == ECOMMUNITY_ENCODE_IP) {
ecommunity_node_target_str(
- encbuf, sizeof(encbuf), pnt);
+ encbuf, sizeof(encbuf), pnt,
+ format);
} else
unk_ecom = 1;
} else {
@@ -1272,8 +1278,8 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
} else if (type == ECOMMUNITY_ENCODE_IP_NON_TRANS) {
sub_type = *pnt++;
if (sub_type == ECOMMUNITY_NODE_TARGET)
- ecommunity_node_target_str(encbuf,
- sizeof(encbuf), pnt);
+ ecommunity_node_target_str(
+ encbuf, sizeof(encbuf), pnt, format);
else
unk_ecom = 1;
} else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) {
diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c
index 980f35130..6272bdb88 100644
--- a/bgpd/bgp_mac.c
+++ b/bgpd/bgp_mac.c
@@ -360,7 +360,7 @@ bool bgp_mac_exist(const struct ethaddr *mac)
return true;
}
-/* This API checks EVPN type-2 prefix and comapares
+/* This API checks EVPN type-2 prefix and compares
* mac against any of local assigned (SVIs) MAC
* address.
*/
@@ -375,8 +375,6 @@ bool bgp_mac_entry_exists(const struct prefix *p)
return false;
return bgp_mac_exist(&p->u.prefix_evpn.macip_addr.mac);
-
- return true;
}
static void bgp_mac_show_mac_entry(struct hash_bucket *bucket, void *arg)
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index f5ead66f2..4e4dce84f 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -14947,35 +14947,42 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer,
RPKI_NOT_BEING_USED);
}
-DEFUN (show_ip_bgp_flowspec_routes_detailed,
- show_ip_bgp_flowspec_routes_detailed_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" flowspec] detail [json]",
- SHOW_STR
- IP_STR
- BGP_STR
- BGP_INSTANCE_HELP_STR
- BGP_AFI_HELP_STR
- "SAFI Flowspec\n"
- "Detailed information on flowspec entries\n"
- JSON_STR)
+/*
+ * Used for "detailed" output for cmds like show bgp <afi> <safi> (or)
+ * show bgp <vrf> (or) show bgp <vrf> <afi> <safi>
+ */
+DEFPY(show_ip_bgp_vrf_afi_safi_routes_detailed,
+ show_ip_bgp_vrf_afi_safi_routes_detailed_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME$vrf_name] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] detail [json$uj]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Detailed information\n"
+ JSON_STR)
{
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
struct bgp *bgp = NULL;
int idx = 0;
- bool uj = use_json(argc, argv);
uint16_t show_flags = BGP_SHOW_OPT_ROUTES_DETAIL;
- if (uj) {
- argc--;
+ if (uj)
SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
- }
bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
&bgp, uj);
if (!idx)
return CMD_WARNING;
+ /* 'vrf all' case to iterate all vrfs & show output per vrf instance */
+ if (vrf_name && strmatch(vrf_name, "all")) {
+ bgp_show_all_instances_routes_vty(vty, afi, safi, show_flags);
+ return CMD_SUCCESS;
+ }
+ /* All other cases except vrf all */
return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL,
show_flags, RPKI_NOT_BEING_USED);
}
@@ -16091,8 +16098,9 @@ void bgp_route_init(void)
install_element(VIEW_NODE, &show_ip_bgp_large_community_list_cmd);
install_element(VIEW_NODE, &show_ip_bgp_large_community_cmd);
- /* show bgp ipv4 flowspec detailed */
- install_element(VIEW_NODE, &show_ip_bgp_flowspec_routes_detailed_cmd);
+ /* show bgp vrf <afi> <safi> detailed */
+ install_element(VIEW_NODE,
+ &show_ip_bgp_vrf_afi_safi_routes_detailed_cmd);
install_element(VIEW_NODE, &show_bgp_listeners_cmd);
install_element(VIEW_NODE, &show_bgp_peerhash_cmd);
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 10fc3ecda..7db110be9 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -872,6 +872,46 @@ static const struct route_map_rule_cmd
route_match_ip_next_hop_type_free
};
+/* `match source-protocol` */
+static enum route_map_cmd_result_t
+route_match_source_protocol(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct bgp_path_info *path = object;
+ int *protocol = rule;
+
+ if (!path)
+ return RMAP_NOMATCH;
+
+ if (path->type == *protocol)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_source_protocol_compile(const char *arg)
+{
+ int *protocol;
+
+ protocol = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*protocol));
+ *protocol = proto_name2num(arg);
+
+ return protocol;
+}
+
+static void route_match_source_protocol_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_source_protocol_cmd = {
+ "source-protocol",
+ route_match_source_protocol,
+ route_match_source_protocol_compile,
+ route_match_source_protocol_free
+};
+
+
/* `match ip route-source prefix-list PREFIX_LIST' */
static enum route_map_cmd_result_t
@@ -7177,6 +7217,42 @@ DEFPY_YANG (match_rpki_extcommunity,
return nb_cli_apply_changes(vty, NULL);
}
+DEFPY_YANG (match_source_protocol,
+ match_source_protocol_cmd,
+ "match source-protocol " FRR_REDIST_STR_ZEBRA "$proto",
+ MATCH_STR
+ "Match protocol via which the route was learnt\n"
+ FRR_REDIST_HELP_STR_ZEBRA)
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:source-protocol']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:source-protocol",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, proto);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (no_match_source_protocol,
+ no_match_source_protocol_cmd,
+ "no match source-protocol [" FRR_REDIST_STR_ZEBRA "]",
+ NO_STR
+ MATCH_STR
+ "Match protocol via which the route was learnt\n"
+ FRR_REDIST_HELP_STR_ZEBRA)
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:source-protocol']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
/* Initialization of route map. */
void bgp_route_map_init(void)
{
@@ -7252,6 +7328,7 @@ void bgp_route_map_init(void)
route_map_install_match(&route_match_ip_address_prefix_list_cmd);
route_map_install_match(&route_match_ip_next_hop_prefix_list_cmd);
route_map_install_match(&route_match_ip_next_hop_type_cmd);
+ route_map_install_match(&route_match_source_protocol_cmd);
route_map_install_match(&route_match_ip_route_source_prefix_list_cmd);
route_map_install_match(&route_match_aspath_cmd);
route_map_install_match(&route_match_community_cmd);
@@ -7441,6 +7518,8 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &set_ipv6_nexthop_peer_cmd);
install_element(RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd);
install_element(RMAP_NODE, &match_rpki_extcommunity_cmd);
+ install_element(RMAP_NODE, &match_source_protocol_cmd);
+ install_element(RMAP_NODE, &no_match_source_protocol_cmd);
#ifdef HAVE_SCRIPTING
install_element(RMAP_NODE, &match_script_cmd);
#endif
diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c
index 6e8439cc2..282ebe911 100644
--- a/bgpd/bgp_routemap_nb.c
+++ b/bgpd/bgp_routemap_nb.c
@@ -74,6 +74,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_destroy,
}
},
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-protocol",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy,
+ }
+ },
{
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv4-address",
.cbs = {
diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h
index bcd1e837e..7066fdb41 100644
--- a/bgpd/bgp_routemap_nb.h
+++ b/bgpd/bgp_routemap_nb.h
@@ -30,6 +30,10 @@ int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_m
struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_destroy(
struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy(
+ struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_probability_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_modify(struct nb_cb_modify_args *args);
diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c
index 938a5ec31..02564b000 100644
--- a/bgpd/bgp_routemap_nb_config.c
+++ b/bgpd/bgp_routemap_nb_config.c
@@ -369,6 +369,60 @@ lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy(
/*
* XPath:
+ * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-protocol
+ */
+int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ enum rmap_compile_rets ret;
+ const char *proto;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ proto = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "source-protocol";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "source-protocol",
+ proto, RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
* /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:rpki-extcommunity
*/
int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_modify(
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 1be44adde..7ef9db9f0 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -11620,8 +11620,11 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
&peer->ibuf->count,
memory_order_relaxed);
- json_object_int_add(json_peer, "tableVersion",
- peer->version[afi][safi]);
+ json_object_int_add(
+ json_peer, "tableVersion",
+ (paf && PAF_SUBGRP(paf))
+ ? paf->subgroup->version
+ : 0);
json_object_int_add(json_peer, "outq",
outq_count);
json_object_int_add(json_peer, "inq",
@@ -11799,8 +11802,10 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
" %9u %9u %8" PRIu64 " %4zu %4zu %8s",
PEER_TOTAL_RX(peer),
PEER_TOTAL_TX(peer),
- peer->version[afi][safi], inq_count,
- outq_count,
+ (paf && PAF_SUBGRP(paf))
+ ? paf->subgroup->version
+ : 0,
+ inq_count, outq_count,
peer_uptime(peer->uptime, timebuf,
BGP_UPTIME_LEN, 0, NULL));
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 42ad8a563..99c6a46e7 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1543,6 +1543,7 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
peer_dst->ifname =
XSTRDUP(MTYPE_BGP_PEER_IFNAME, peer_src->ifname);
}
+ peer_dst->ttl = peer_src->ttl;
}
static int bgp_peer_conf_if_to_su_update_v4(struct peer *peer,
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 68b32b594..9cb1d5108 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1119,7 +1119,6 @@ struct peer {
/* BGP peer group. */
struct peer_group *group;
- uint64_t version[AFI_MAX][SAFI_MAX];
/* BGP peer_af structures, per configured AF on this peer */
struct peer_af *peer_af_array[BGP_AF_MAX];
diff --git a/doc/developer/building-frr-for-ubuntu2204.rst b/doc/developer/building-frr-for-ubuntu2204.rst
new file mode 100644
index 000000000..6b941b367
--- /dev/null
+++ b/doc/developer/building-frr-for-ubuntu2204.rst
@@ -0,0 +1,171 @@
+Ubuntu 22.04 LTS
+================
+
+This document describes installation from source. If you want to build a
+``deb``, see :ref:`packaging-debian`.
+
+Installing Dependencies
+-----------------------
+
+.. code-block:: console
+
+ sudo apt update
+ sudo apt-get install \
+ git autoconf automake libtool make libreadline-dev texinfo \
+ pkg-config libpam0g-dev libjson-c-dev bison flex \
+ libc-ares-dev python3-dev python3-sphinx \
+ install-info build-essential libsnmp-dev perl \
+ libcap-dev python2 libelf-dev libunwind-dev \
+ libyang2 libyang2-dev
+
+.. include:: building-libunwind-note.rst
+
+Note that Ubuntu >= 20 no longer installs python 2.x, so it must be
+installed explicitly. Ensure that your system has a symlink named
+``/usr/bin/python`` pointing at ``/usr/bin/python3``.
+
+.. code-block:: shell
+
+ sudo ln -s /usr/bin/python3 /usr/bin/python
+ python --version
+
+In addition, ``pip`` for python2 must be installed if you wish to run
+the FRR topotests. That version of ``pip`` is not available from the
+ubuntu apt repositories; in order to install it:
+
+.. code-block:: shell
+
+ curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
+ sudo python2 ./get-pip.py
+
+ # And verify the installation
+ pip2 --version
+
+
+Protobuf
+^^^^^^^^
+This is optional
+
+.. code-block:: console
+
+ sudo apt-get install protobuf-c-compiler libprotobuf-c-dev
+
+ZeroMQ
+^^^^^^
+This is optional
+
+.. code-block:: console
+
+ sudo apt-get install libzmq5 libzmq3-dev
+
+Building & Installing FRR
+-------------------------
+
+Add FRR user and groups
+^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+ sudo groupadd -r -g 92 frr
+ sudo groupadd -r -g 85 frrvty
+ sudo adduser --system --ingroup frr --home /var/run/frr/ \
+ --gecos "FRR suite" --shell /sbin/nologin frr
+ sudo usermod -a -G frrvty frr
+
+Compile
+^^^^^^^
+
+.. include:: include-compile.rst
+
+Install FRR configuration files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+ sudo install -m 775 -o frr -g frr -d /var/log/frr
+ sudo install -m 775 -o frr -g frrvty -d /etc/frr
+ sudo install -m 640 -o frr -g frrvty tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf
+ sudo install -m 640 -o frr -g frr tools/etc/frr/frr.conf /etc/frr/frr.conf
+ sudo install -m 640 -o frr -g frr tools/etc/frr/daemons.conf /etc/frr/daemons.conf
+ sudo install -m 640 -o frr -g frr tools/etc/frr/daemons /etc/frr/daemons
+
+Tweak sysctls
+^^^^^^^^^^^^^
+
+Some sysctls need to be changed in order to enable IPv4/IPv6 forwarding and
+MPLS (if supported by your platform). If your platform does not support MPLS,
+skip the MPLS related configuration in this section.
+
+Edit :file:`/etc/sysctl.conf` and uncomment the following values (ignore the
+other settings):
+
+::
+
+ # Uncomment the next line to enable packet forwarding for IPv4
+ net.ipv4.ip_forward=1
+
+ # Uncomment the next line to enable packet forwarding for IPv6
+ # Enabling this option disables Stateless Address Autoconfiguration
+ # based on Router Advertisements for this host
+ net.ipv6.conf.all.forwarding=1
+
+Reboot or use ``sysctl -p`` to apply the same config to the running system.
+
+Add MPLS kernel modules
+"""""""""""""""""""""""
+
+Ubuntu 20.04 ships with kernel 5.4; MPLS modules are present by default. To
+enable, add the following lines to :file:`/etc/modules-load.d/modules.conf`:
+
+::
+
+ # Load MPLS Kernel Modules
+ mpls_router
+ mpls_iptunnel
+
+
+And load the kernel modules on the running system:
+
+.. code-block:: console
+
+ sudo modprobe mpls-router mpls-iptunnel
+
+If the above command returns an error, you may need to install the appropriate
+or latest linux-modules-extra-<kernel-version>-generic package. For example
+``apt-get install linux-modules-extra-`uname -r`-generic``
+
+Enable MPLS Forwarding
+""""""""""""""""""""""
+
+Edit :file:`/etc/sysctl.conf` and the following lines. Make sure to add a line
+equal to :file:`net.mpls.conf.eth0.input` for each interface used with MPLS.
+
+::
+
+ # Enable MPLS Label processing on all interfaces
+ net.mpls.conf.eth0.input=1
+ net.mpls.conf.eth1.input=1
+ net.mpls.conf.eth2.input=1
+ net.mpls.platform_labels=100000
+
+Install service files
+^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: console
+
+ sudo install -m 644 tools/frr.service /etc/systemd/system/frr.service
+ sudo systemctl enable frr
+
+Enable daemons
+^^^^^^^^^^^^^^
+
+Open :file:`/etc/frr/daemons` with your text editor of choice. Look for the
+section with ``watchfrr_enable=...`` and ``zebra=...`` etc. Enable the daemons
+as required by changing the value to ``yes``.
+
+Start FRR
+^^^^^^^^^
+
+.. code-block:: shell
+
+ systemctl start frr
diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst
index 13936e18e..f44cf9df9 100644
--- a/doc/developer/topotests.rst
+++ b/doc/developer/topotests.rst
@@ -17,15 +17,23 @@ Tested with Ubuntu 20.04,Ubuntu 18.04, and Debian 11.
Instructions are the same for all setups (i.e. ExaBGP is only used for
BGP tests).
+Tshark is only required if you enable any packet captures on test runs.
+
+Valgrind is only required if you enable valgrind on test runs.
+
Installing Topotest Requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code:: shell
- apt-get install gdb
- apt-get install iproute2
- apt-get install net-tools
- apt-get install python3-pip
+ apt-get install \
+ gdb \
+ iproute2 \
+ net-tools \
+ python3-pip \
+ iputils-ping \
+ tshark \
+ valgrind
python3 -m pip install wheel
python3 -m pip install 'pytest>=6.2.4'
python3 -m pip install 'pytest-xdist>=2.3.0'
@@ -119,6 +127,7 @@ If you prefer to manually build FRR, then use the following suggested config:
--sysconfdir=/etc/frr \
--enable-vtysh \
--enable-pimd \
+ --enable-pim6d \
--enable-sharpd \
--enable-multipath=64 \
--enable-user=frr \
@@ -311,32 +320,6 @@ Test will set exit code which can be used with ``git bisect``.
For the simulated topology, see the description in the python file.
-StdErr log from daemos after exit
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-To enable the reporting of any messages seen on StdErr after the daemons exit,
-the following env variable can be set::
-
- export TOPOTESTS_CHECK_STDERR=Yes
-
-(The value doesn't matter at this time. The check is whether the env
-variable exists or not.) There is no pass/fail on this reporting; the
-Output will be reported to the console.
-
-Collect Memory Leak Information
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-FRR processes can report unfreed memory allocations upon exit. To
-enable the reporting of memory leaks, define an environment variable
-``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.::
-
- export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
-
-This will enable the check and output to console and the writing of
-the information to files with the given prefix (followed by testname),
-ie :file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case
-of a memory leak.
-
Running Topotests with AddressSanitizer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -562,6 +545,48 @@ Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router
--gdb-breakpoints=nb_config_diff \
all-protocol-startup
+Reporting Memleaks with FRR Memory Statistics
+"""""""""""""""""""""""""""""""""""""""""""""
+
+FRR reports all allocated FRR memory objects on exit to standard error.
+Topotest can be run to report such output as errors in order to check for
+memleaks in FRR memory allocations. Specifying the CLI argument
+``--memleaks`` will enable reporting FRR-based memory allocations at exit as errors.
+
+.. code:: shell
+
+ sudo -E pytest --memleaks all-protocol-startup
+
+
+StdErr log from daemos after exit
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When running with ``--memleaks``, to enable the reporting of other,
+non-memory related, messages seen on StdErr after the daemons exit,
+the following env variable can be set::
+
+ export TOPOTESTS_CHECK_STDERR=Yes
+
+(The value doesn't matter at this time. The check is whether the env
+variable exists or not.) There is no pass/fail on this reporting; the
+Output will be reported to the console.
+
+Collect Memory Leak Information
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When running with ``--memleaks``, FRR processes report unfreed memory
+allocations upon exit. To enable also reporting of memory leaks to a specific
+location, define an environment variable ``TOPOTESTS_CHECK_MEMLEAK`` with the
+file prefix, i.e.:
+
+ export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
+
+For tests that support the TOPOTESTS_CHECK_MEMLEAK environment variable, this
+will enable output to the information to files with the given prefix (followed
+by testname), e.g.,:
+file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case
+of a memory leak.
+
Detecting Memleaks with Valgrind
""""""""""""""""""""""""""""""""
diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst
index f11fff5de..28cf5d0ab 100644
--- a/doc/developer/workflow.rst
+++ b/doc/developer/workflow.rst
@@ -537,7 +537,8 @@ Programming Languages, Tools and Libraries
==========================================
The core of FRR is written in C (gcc or clang supported) and makes
-use of GNU compiler extensions. A few non-essential scripts are
+use of GNU compiler extensions. Additionally, the CLI generation
+tool, `clippy`, requires Python. A few other non-essential scripts are
implemented in Perl and Python. FRR requires the following tools
to build distribution packages: automake, autoconf, texinfo, libtool and
gawk and various libraries (i.e. libpam and libjson-c).
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 3910caf7f..f045ca239 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -2032,6 +2032,13 @@ Capability Negotiation
Disabled by default.
+.. clicmd:: neighbor PEER aigp
+
+ Send and receive AIGP attribute for this neighbor. This is valid only for
+ eBGP neighbors.
+
+ Disabled by default. iBGP neighbors have this option enabled implicitly.
+
.. _bgp-as-path-access-lists:
AS Path Access Lists
@@ -4116,6 +4123,122 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`.
If ``afi`` is specified, with ``all`` option, routes will be displayed for
each SAFI in the selected AFI.
+.. clicmd:: show [ip] bgp [<view|vrf> VIEWVRFNAME] [afi] [safi] detail [json]
+
+ Display the detailed version of all routes from the specified bgp vrf table
+ for a given afi + safi.
+
+ If no vrf is specified, then it is assumed as a default vrf and routes
+ are displayed from default vrf table.
+
+ If ``all`` option is specified as vrf name, then all bgp vrf tables routes
+ from a given afi+safi are displayed in the detailed output of routes.
+
+ If ``json`` option is specified, detailed output is displayed in JSON format.
+
+ Following are sample output for few examples of how to use this command.
+
+.. code-block:: frr
+
+ torm-23# sh bgp ipv4 unicast detail (OR) sh bgp vrf default ipv4 unicast detail
+
+ !--- Output suppressed.
+
+ BGP routing table entry for 172.16.16.1/32
+ Paths: (1 available, best #1, table default)
+ Not advertised to any peer
+ Local, (Received from a RR-client)
+ 172.16.16.1 (metric 20) from torm-22(172.16.16.1) (192.168.0.10)
+ Origin IGP, metric 0, localpref 100, valid, internal
+ Last update: Fri May 8 12:54:05 2023
+ BGP routing table entry for 172.16.16.2/32
+ Paths: (1 available, best #1, table default)
+ Not advertised to any peer
+ Local
+ 0.0.0.0 from 0.0.0.0 (172.16.16.2)
+ Origin incomplete, metric 0, weight 32768, valid, sourced, bestpath-from-AS Local, best (First path received)
+ Last update: Wed May 8 12:54:41 2023
+
+ Displayed 2 routes and 2 total paths
+
+.. code-block:: frr
+
+ torm-23# sh bgp vrf all detail
+
+ Instance default:
+
+ !--- Output suppressed.
+
+ BGP routing table entry for 172.16.16.1/32
+ Paths: (1 available, best #1, table default)
+ Not advertised to any peer
+ Local, (Received from a RR-client)
+ 172.16.16.1 (metric 20) from torm-22(172.16.16.1) (192.168.0.10)
+ Origin IGP, metric 0, localpref 100, valid, internal
+ Last update: Fri May 8 12:44:05 2023
+ BGP routing table entry for 172.16.16.2/32
+ Paths: (1 available, best #1, table default)
+ Not advertised to any peer
+ Local
+ 0.0.0.0 from 0.0.0.0 (172.16.16.2)
+ Origin incomplete, metric 0, weight 32768, valid, sourced, bestpath-from-AS Local, best (First path received)
+ Last update: Wed May 8 12:45:01 2023
+
+ Displayed 2 routes and 2 total paths
+
+ Instance vrf3:
+
+ !--- Output suppressed.
+
+ BGP routing table entry for 192.168.0.2/32
+ Paths: (1 available, best #1, vrf vrf3)
+ Not advertised to any peer
+ Imported from 172.16.16.1:12:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.0.2], VNI 1008/4003
+ Local
+ 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self
+ Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received)
+ Extended Community: RT:65000:1008 ET:8 Rmac:00:02:00:00:00:58
+ Last update: Fri May 8 02:41:55 2023
+ BGP routing table entry for 192.168.1.2/32
+ Paths: (1 available, best #1, vrf vrf3)
+ Not advertised to any peer
+ Imported from 172.16.16.1:13:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.1.2], VNI 1009/4003
+ Local
+ 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self
+ Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received)
+ Extended Community: RT:65000:1009 ET:8 Rmac:00:02:00:00:00:58
+ Last update: Fri May 8 02:41:55 2023
+
+ Displayed 2 routes and 2 total paths
+
+
+.. code-block:: frr
+
+ torm-23# sh bgp vrf vrf3 ipv4 unicast detail
+
+ !--- Output suppressed.
+
+ BGP routing table entry for 192.168.0.2/32
+ Paths: (1 available, best #1, vrf vrf3)
+ Not advertised to any peer
+ Imported from 172.16.16.1:12:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.0.2], VNI 1008/4003
+ Local
+ 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self
+ Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received)
+ Extended Community: RT:65000:1008 ET:8 Rmac:00:02:00:00:00:58
+ Last update: Fri May 8 02:23:35 2023
+ BGP routing table entry for 192.168.1.2/32
+ Paths: (1 available, best #1, vrf vrf3)
+ Not advertised to any peer
+ Imported from 172.16.16.1:13:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.1.2], VNI 1009/4003
+ Local
+ 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self
+ Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received)
+ Extended Community: RT:65000:1009 ET:8 Rmac:00:02:00:00:00:58
+ Last update: Fri May 8 02:23:55 2023
+
+ Displayed 2 routes and 2 total paths
+
.. _bgp-display-routes-by-community:
Displaying Routes by Community Attribute
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index 8dacb9c9d..2f4c956ff 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -287,6 +287,19 @@ OSPF6 interface
Sets interface's Router Dead Interval. Default value is 40.
+.. clicmd:: ipv6 ospf6 graceful-restart hello-delay HELLODELAYINTERVAL
+
+ Set the length of time during which Grace-LSAs are sent at 1-second intervals
+ while coming back up after an unplanned outage. During this time, no hello
+ packets are sent.
+
+ A higher hello delay will increase the chance that all neighbors are notified
+ about the ongoing graceful restart before receiving a hello packet (which is
+ crucial for the graceful restart to succeed). The hello delay shouldn't be set
+ too high, however, otherwise the adjacencies might time out. As a best practice,
+ it's recommended to set the hello delay and hello interval with the same values.
+ The default value is 10 seconds.
+
.. clicmd:: ipv6 ospf6 retransmit-interval RETRANSMITINTERVAL
Sets interface's Rxmt Interval. Default value is 5.
@@ -343,6 +356,10 @@ Graceful Restart
To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf"
EXEC-level command needs to be issued before restarting the ospf6d daemon.
+ When Graceful Restart is enabled and the ospf6d daemon crashes or is killed
+ abruptely (e.g. SIGKILL), it will attempt an unplanned Graceful Restart once
+ it restarts.
+
.. clicmd:: graceful-restart helper enable [A.B.C.D]
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 517183260..effad0fd0 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -635,6 +635,19 @@ Interfaces
:clicmd:`ip ospf dead-interval minimal hello-multiplier (2-20)` is also
specified for the interface.
+.. clicmd:: ip ospf graceful-restart hello-delay (1-1800)
+
+ Set the length of time during which Grace-LSAs are sent at 1-second intervals
+ while coming back up after an unplanned outage. During this time, no hello
+ packets are sent.
+
+ A higher hello delay will increase the chance that all neighbors are notified
+ about the ongoing graceful restart before receiving a hello packet (which is
+ crucial for the graceful restart to succeed). The hello delay shouldn't be set
+ too high, however, otherwise the adjacencies might time out. As a best practice,
+ it's recommended to set the hello delay and hello interval with the same values.
+ The default value is 10 seconds.
+
.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn])
When configuring a point-to-point network on an interface and the interface
@@ -770,6 +783,10 @@ Graceful Restart
To perform a graceful shutdown, the "graceful-restart prepare ip ospf"
EXEC-level command needs to be issued before restarting the ospfd daemon.
+ When Graceful Restart is enabled and the ospfd daemon crashes or is killed
+ abruptely (e.g. SIGKILL), it will attempt an unplanned Graceful Restart once
+ it restarts.
+
.. clicmd:: graceful-restart helper enable [A.B.C.D]
diff --git a/doc/user/ripd.rst b/doc/user/ripd.rst
index 67323e61f..f9c772430 100644
--- a/doc/user/ripd.rst
+++ b/doc/user/ripd.rst
@@ -159,6 +159,11 @@ RIP Configuration
If `poisoned-reverse` is also set, the router sends the poisoned routes
with highest metric back to the sending router.
+.. clicmd:: allow-ecmp [1-MULTIPATH_NUM]
+
+ Control how many ECMP paths RIP can inject for the same prefix. If specified
+ without a number, a maximum is taken (compiled with ``--enable-multipath``).
+
.. _rip-version-control:
RIP Version Control
diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst
index b7f533656..bd19ae88e 100644
--- a/doc/user/routemap.rst
+++ b/doc/user/routemap.rst
@@ -212,7 +212,7 @@ Route Map Match Command
.. clicmd:: match source-protocol PROTOCOL_NAME
- This is a ZEBRA specific match command. Matches the
+ This is a ZEBRA and BGP specific match command. Matches the
originating protocol specified.
.. clicmd:: match source-instance NUMBER
diff --git a/doc/user/static.rst b/doc/user/static.rst
index 05847ba39..6d8aca97b 100644
--- a/doc/user/static.rst
+++ b/doc/user/static.rst
@@ -90,7 +90,7 @@ a static prefix and gateway, with several possible forms.
Multiple nexthop static route
=============================
-To create multiple nexthops to the same NETWORK, just reenter the same
+To create multiple nexthops to the same NETWORK (also known as a multipath route), just reenter the same
network statement with different nexthop information.
.. code-block:: frr
@@ -122,7 +122,7 @@ nexthops, if the platform supports this.
ip route 10.0.0.0/8 null0 255
-This will install a multihop route via the specified next-hops if they are
+This will install a multipath route via the specified next-hops if they are
reachable, as well as a high-distance blackhole route, which can be useful to
prevent traffic destined for a prefix to match less-specific routes (e.g.
default) should the specified gateways not be reachable. E.g.:
diff --git a/ldpd/interface.c b/ldpd/interface.c
index ad5d853b6..f0e70cbac 100644
--- a/ldpd/interface.c
+++ b/ldpd/interface.c
@@ -138,14 +138,13 @@ void
if_update_info(struct iface *iface, struct kif *kif)
{
/* get type */
- if (kif->flags & IFF_POINTOPOINT)
+ if (CHECK_FLAG(kif->flags, IFF_POINTOPOINT))
iface->type = IF_TYPE_POINTOPOINT;
- if (kif->flags & IFF_BROADCAST &&
- kif->flags & IFF_MULTICAST)
+ if (CHECK_FLAG(kif->flags, IFF_BROADCAST) &&
+ CHECK_FLAG(kif->flags, IFF_MULTICAST))
iface->type = IF_TYPE_BROADCAST;
- if (ldpd_process == PROC_LDP_ENGINE && iface->operative &&
- !kif->operative)
+ if (ldpd_process == PROC_LDP_ENGINE && iface->operative && !kif->operative)
ldp_sync_fsm(iface, LDP_SYNC_EVT_IFACE_SHUTDOWN);
/* get index and flags */
@@ -276,8 +275,7 @@ if_start(struct iface *iface, int af)
struct iface_af *ia;
struct timeval now;
- log_debug("%s: %s address-family %s", __func__, iface->name,
- af_name(af));
+ log_debug("%s: %s address-family %s", __func__, iface->name, af_name(af));
ia = iface_af_get(iface, af);
@@ -560,8 +558,7 @@ if_join_ipv4_group(struct iface *iface, struct in_addr *addr)
{
struct in_addr if_addr;
- log_debug("%s: interface %s addr %pI4", __func__, iface->name,
- addr);
+ log_debug("%s: interface %s addr %pI4", __func__, iface->name, addr);
if_addr.s_addr = if_get_ipv4_addr(iface);
@@ -579,8 +576,7 @@ if_leave_ipv4_group(struct iface *iface, struct in_addr *addr)
{
struct in_addr if_addr;
- log_debug("%s: interface %s addr %pI4", __func__, iface->name,
- addr);
+ log_debug("%s: interface %s addr %pI4", __func__, iface->name, addr);
if_addr.s_addr = if_get_ipv4_addr(iface);
diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c
index dcaf1cc10..3c5a5d999 100644
--- a/ldpd/labelmapping.c
+++ b/ldpd/labelmapping.c
@@ -47,12 +47,11 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
while ((me = TAILQ_FIRST(mh)) != NULL) {
/* generate pdu */
if (first) {
- if ((buf = ibuf_open(nbr->max_pdu_len +
- LDP_HDR_DEAD_LEN)) == NULL)
+ if ((buf = ibuf_open(nbr->max_pdu_len + LDP_HDR_DEAD_LEN)) == NULL)
fatal(__func__);
/* real size will be set up later */
- err |= gen_ldp_hdr(buf, 0);
+ SET_FLAG(err, gen_ldp_hdr(buf, 0));
size = LDP_HDR_SIZE;
first = 0;
@@ -63,9 +62,9 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
msg_size += len_fec_tlv(&me->map);
if (me->map.label != NO_LABEL)
msg_size += LABEL_TLV_SIZE;
- if (me->map.flags & F_MAP_REQ_ID)
+ if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID))
msg_size += REQID_TLV_SIZE;
- if (me->map.flags & F_MAP_STATUS)
+ if (CHECK_FLAG(me->map.flags, F_MAP_STATUS))
msg_size += STATUS_SIZE;
/* maximum pdu length exceeded, we need a new ldp pdu */
@@ -78,17 +77,17 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
size += msg_size;
/* append message and tlvs */
- err |= gen_msg_hdr(buf, type, msg_size);
- err |= gen_fec_tlv(buf, &me->map);
+ SET_FLAG(err, gen_msg_hdr(buf, type, msg_size));
+ SET_FLAG(err, gen_fec_tlv(buf, &me->map));
if (me->map.label != NO_LABEL)
- err |= gen_label_tlv(buf, me->map.label);
- if (me->map.flags & F_MAP_REQ_ID)
- err |= gen_reqid_tlv(buf, me->map.requestid);
- if (me->map.flags & F_MAP_PW_STATUS)
- err |= gen_pw_status_tlv(buf, me->map.pw_status);
- if (me->map.flags & F_MAP_STATUS)
- err |= gen_status_tlv(buf, me->map.st.status_code,
- me->map.st.msg_id, me->map.st.msg_type);
+ SET_FLAG(err, gen_label_tlv(buf, me->map.label));
+ if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID))
+ SET_FLAG(err, gen_reqid_tlv(buf, me->map.requestid));
+ if (CHECK_FLAG(me->map.flags, F_MAP_PW_STATUS))
+ SET_FLAG(err, gen_pw_status_tlv(buf, me->map.pw_status));
+ if (CHECK_FLAG(me->map.flags, F_MAP_STATUS))
+ SET_FLAG(err, gen_status_tlv(buf, me->map.st.status_code,
+ me->map.st.msg_id, me->map.st.msg_type));
if (err) {
ibuf_free(buf);
mapping_list_clr(mh);
@@ -172,15 +171,13 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
memset(&map, 0, sizeof(map));
map.msg_id = msg.id;
- if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, feclen,
- &map)) == -1)
+ if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, feclen, &map)) == -1)
goto err;
if (map.type == MAP_TYPE_PWID &&
- !(map.flags & F_MAP_PW_ID) &&
+ !CHECK_FLAG(map.flags, F_MAP_PW_ID) &&
type != MSG_TYPE_LABELWITHDRAW &&
type != MSG_TYPE_LABELRELEASE) {
- send_notification(nbr->tcp, S_MISS_MSG, msg.id,
- msg.type);
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
goto err;
}
@@ -193,8 +190,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
case MSG_TYPE_LABELMAPPING:
case MSG_TYPE_LABELREQUEST:
case MSG_TYPE_LABELABORTREQ:
- session_shutdown(nbr, S_UNKNOWN_FEC, msg.id,
- msg.type);
+ session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, msg.type);
goto err;
default:
break;
@@ -211,8 +207,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
switch (type) {
case MSG_TYPE_LABELMAPPING:
case MSG_TYPE_LABELABORTREQ:
- session_shutdown(nbr, S_UNKNOWN_FEC, msg.id,
- msg.type);
+ session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, msg.type);
goto err;
default:
break;
@@ -223,8 +218,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
* LDP supports the use of multiple FEC Elements per
* FEC for the Label Mapping message only.
*/
- if (type != MSG_TYPE_LABELMAPPING &&
- tlen != feclen) {
+ if (type != MSG_TYPE_LABELMAPPING && tlen != feclen) {
session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
goto err;
}
@@ -262,10 +256,10 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
* For Label Mapping messages the Label TLV is mandatory and
* should appear right after the FEC TLV.
*/
- if (current_tlv == 1 && type == MSG_TYPE_LABELMAPPING &&
- !(tlv_type & TLV_TYPE_GENERICLABEL)) {
- send_notification(nbr->tcp, S_MISS_MSG, msg.id,
- msg.type);
+ if (current_tlv == 1 &&
+ type == MSG_TYPE_LABELMAPPING &&
+ !CHECK_FLAG(tlv_type, TLV_TYPE_GENERICLABEL)) {
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
goto err;
}
@@ -275,12 +269,11 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
case MSG_TYPE_LABELMAPPING:
case MSG_TYPE_LABELREQUEST:
if (tlv_len != REQID_TLV_LEN) {
- session_shutdown(nbr, S_BAD_TLV_LEN,
- msg.id, msg.type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
goto err;
}
- flags |= F_MAP_REQ_ID;
+ SET_FLAG(flags, F_MAP_REQ_ID);
memcpy(&reqbuf, buf, sizeof(reqbuf));
reqid = ntohl(reqbuf);
break;
@@ -299,8 +292,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
case MSG_TYPE_LABELWITHDRAW:
case MSG_TYPE_LABELRELEASE:
if (tlv_len != LABEL_TLV_LEN) {
- session_shutdown(nbr, S_BAD_TLV_LEN,
- msg.id, msg.type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
goto err;
}
@@ -312,8 +304,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
label != MPLS_LABEL_IPV4_EXPLICIT_NULL &&
label != MPLS_LABEL_IPV6_EXPLICIT_NULL &&
label != MPLS_LABEL_IMPLICIT_NULL)) {
- session_shutdown(nbr, S_BAD_TLV_VAL,
- msg.id, msg.type);
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
goto err;
}
break;
@@ -329,8 +320,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
case MSG_TYPE_LABELWITHDRAW:
case MSG_TYPE_LABELRELEASE:
/* unsupported */
- session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
- msg.type);
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
goto err;
break;
default:
@@ -340,8 +330,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
break;
case TLV_TYPE_STATUS:
if (tlv_len != STATUS_TLV_LEN) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
- msg.type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
goto err;
}
/* ignore */
@@ -350,12 +339,11 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
switch (type) {
case MSG_TYPE_LABELMAPPING:
if (tlv_len != PW_STATUS_TLV_LEN) {
- session_shutdown(nbr, S_BAD_TLV_LEN,
- msg.id, msg.type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
goto err;
}
- flags |= F_MAP_PW_STATUS;
+ SET_FLAG(flags, F_MAP_PW_STATUS);
memcpy(&statusbuf, buf, sizeof(statusbuf));
pw_status = ntohl(statusbuf);
break;
@@ -365,7 +353,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
}
break;
default:
- if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+ if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG))
send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
msg.id, msg.type, tlv_type, tlv_len, buf);
/* ignore unknown tlv */
@@ -380,14 +368,13 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
while ((me = TAILQ_FIRST(&mh)) != NULL) {
int imsg_type = IMSG_NONE;
- me->map.flags |= flags;
+ SET_FLAG(me->map.flags, flags);
switch (me->map.type) {
case MAP_TYPE_PREFIX:
switch (me->map.fec.prefix.af) {
case AF_INET:
if (label == MPLS_LABEL_IPV6_EXPLICIT_NULL) {
- session_shutdown(nbr, S_BAD_TLV_VAL,
- msg.id, msg.type);
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
goto err;
}
if (!nbr->v4_enabled)
@@ -395,8 +382,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
break;
case AF_INET6:
if (label == MPLS_LABEL_IPV4_EXPLICIT_NULL) {
- session_shutdown(nbr, S_BAD_TLV_VAL,
- msg.id, msg.type);
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
goto err;
}
if (!nbr->v6_enabled)
@@ -408,18 +394,17 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
break;
case MAP_TYPE_PWID:
if (label <= MPLS_LABEL_RESERVED_MAX) {
- session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
- msg.type);
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
goto err;
}
- if (me->map.flags & F_MAP_PW_STATUS)
+ if (CHECK_FLAG(me->map.flags, F_MAP_PW_STATUS))
me->map.pw_status = pw_status;
break;
default:
break;
}
me->map.label = label;
- if (me->map.flags & F_MAP_REQ_ID)
+ if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID))
me->map.requestid = reqid;
log_msg_mapping(0, type, nbr, &me->map);
@@ -513,11 +498,11 @@ len_fec_tlv(struct map *map)
break;
case MAP_TYPE_PWID:
len += FEC_PWID_ELM_MIN_LEN;
- if (map->flags & F_MAP_PW_ID)
+ if (CHECK_FLAG(map->flags, F_MAP_PW_ID))
len += PW_STATUS_TLV_LEN;
- if (map->flags & F_MAP_PW_IFMTU)
+ if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU))
len += FEC_SUBTLV_IFMTU_SIZE;
- if (map->flags & F_MAP_PW_STATUS)
+ if (CHECK_FLAG(map->flags, F_MAP_PW_STATUS))
len += PW_STATUS_TLV_SIZE;
break;
case MAP_TYPE_TYPED_WCARD:
@@ -552,15 +537,15 @@ gen_fec_tlv(struct ibuf *buf, struct map *map)
switch (map->type) {
case MAP_TYPE_WILDCARD:
ft.length = htons(sizeof(uint8_t));
- err |= ibuf_add(buf, &ft, sizeof(ft));
- err |= ibuf_add(buf, &map->type, sizeof(map->type));
+ SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft)));
+ SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(map->type)));
break;
case MAP_TYPE_PREFIX:
len = PREFIX_SIZE(map->fec.prefix.prefixlen);
ft.length = htons(sizeof(map->type) + sizeof(family) +
sizeof(map->fec.prefix.prefixlen) + len);
- err |= ibuf_add(buf, &ft, sizeof(ft));
- err |= ibuf_add(buf, &map->type, sizeof(map->type));
+ SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft)));
+ SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(map->type)));
switch (map->fec.prefix.af) {
case AF_INET:
family = htons(AF_IPV4);
@@ -572,45 +557,45 @@ gen_fec_tlv(struct ibuf *buf, struct map *map)
fatalx("gen_fec_tlv: unknown af");
break;
}
- err |= ibuf_add(buf, &family, sizeof(family));
- err |= ibuf_add(buf, &map->fec.prefix.prefixlen,
- sizeof(map->fec.prefix.prefixlen));
+ SET_FLAG(err, ibuf_add(buf, &family, sizeof(family)));
+ SET_FLAG(err, ibuf_add(buf, &map->fec.prefix.prefixlen,
+ sizeof(map->fec.prefix.prefixlen)));
if (len)
- err |= ibuf_add(buf, &map->fec.prefix.prefix, len);
+ SET_FLAG(err, ibuf_add(buf, &map->fec.prefix.prefix, len));
break;
case MAP_TYPE_PWID:
- if (map->flags & F_MAP_PW_ID)
+ if (CHECK_FLAG(map->flags, F_MAP_PW_ID))
pw_len += FEC_PWID_SIZE;
- if (map->flags & F_MAP_PW_IFMTU)
+ if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU))
pw_len += FEC_SUBTLV_IFMTU_SIZE;
len = FEC_PWID_ELM_MIN_LEN + pw_len;
ft.length = htons(len);
- err |= ibuf_add(buf, &ft, sizeof(ft));
+ SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft)));
- err |= ibuf_add(buf, &map->type, sizeof(uint8_t));
+ SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(uint8_t)));
pw_type = map->fec.pwid.type;
- if (map->flags & F_MAP_PW_CWORD)
- pw_type |= CONTROL_WORD_FLAG;
+ if (CHECK_FLAG(map->flags, F_MAP_PW_CWORD))
+ SET_FLAG(pw_type, CONTROL_WORD_FLAG);
pw_type = htons(pw_type);
- err |= ibuf_add(buf, &pw_type, sizeof(uint16_t));
- err |= ibuf_add(buf, &pw_len, sizeof(uint8_t));
+ SET_FLAG(err, ibuf_add(buf, &pw_type, sizeof(uint16_t)));
+ SET_FLAG(err, ibuf_add(buf, &pw_len, sizeof(uint8_t)));
group_id = htonl(map->fec.pwid.group_id);
- err |= ibuf_add(buf, &group_id, sizeof(uint32_t));
- if (map->flags & F_MAP_PW_ID) {
+ SET_FLAG(err, ibuf_add(buf, &group_id, sizeof(uint32_t)));
+ if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) {
pwid = htonl(map->fec.pwid.pwid);
- err |= ibuf_add(buf, &pwid, sizeof(uint32_t));
+ SET_FLAG(err, ibuf_add(buf, &pwid, sizeof(uint32_t)));
}
- if (map->flags & F_MAP_PW_IFMTU) {
+ if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) {
struct subtlv stlv;
stlv.type = SUBTLV_IFMTU;
stlv.length = FEC_SUBTLV_IFMTU_SIZE;
- err |= ibuf_add(buf, &stlv, sizeof(uint16_t));
+ SET_FLAG(err, ibuf_add(buf, &stlv, sizeof(uint16_t)));
ifmtu = htons(map->fec.pwid.ifmtu);
- err |= ibuf_add(buf, &ifmtu, sizeof(uint16_t));
+ SET_FLAG(err, ibuf_add(buf, &ifmtu, sizeof(uint16_t)));
}
break;
case MAP_TYPE_TYPED_WCARD:
@@ -624,14 +609,14 @@ gen_fec_tlv(struct ibuf *buf, struct map *map)
fatalx("gen_fec_tlv: unexpected fec type");
}
ft.length = htons(len);
- err |= ibuf_add(buf, &ft, sizeof(ft));
- err |= ibuf_add(buf, &map->type, sizeof(uint8_t));
- err |= ibuf_add(buf, &map->fec.twcard.type, sizeof(uint8_t));
+ SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft)));
+ SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(uint8_t)));
+ SET_FLAG(err, ibuf_add(buf, &map->fec.twcard.type, sizeof(uint8_t)));
switch (map->fec.twcard.type) {
case MAP_TYPE_PREFIX:
twcard_len = sizeof(uint16_t);
- err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t));
+ SET_FLAG(err, ibuf_add(buf, &twcard_len, sizeof(uint8_t)));
switch (map->fec.twcard.u.prefix_af) {
case AF_INET:
@@ -645,13 +630,13 @@ gen_fec_tlv(struct ibuf *buf, struct map *map)
break;
}
- err |= ibuf_add(buf, &family, sizeof(uint16_t));
+ SET_FLAG(err, ibuf_add(buf, &family, sizeof(uint16_t)));
break;
case MAP_TYPE_PWID:
twcard_len = sizeof(uint16_t);
- err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t));
+ SET_FLAG(err, ibuf_add(buf, &twcard_len, sizeof(uint8_t)));
pw_type = htons(map->fec.twcard.u.pw_type);
- err |= ibuf_add(buf, &pw_type, sizeof(uint16_t));
+ SET_FLAG(err, ibuf_add(buf, &pw_type, sizeof(uint16_t)));
break;
default:
fatalx("gen_fec_tlv: unexpected fec type");
@@ -679,21 +664,18 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
if (len == FEC_ELM_WCARD_LEN)
return (off);
else {
- session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type);
return (-1);
}
break;
case MAP_TYPE_PREFIX:
if (len < FEC_ELM_PREFIX_MIN_LEN) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
/* Address Family */
- memcpy(&map->fec.prefix.af, buf + off,
- sizeof(map->fec.prefix.af));
+ memcpy(&map->fec.prefix.af, buf + off, sizeof(map->fec.prefix.af));
off += sizeof(map->fec.prefix.af);
map->fec.prefix.af = ntohs(map->fec.prefix.af);
switch (map->fec.prefix.af) {
@@ -704,8 +686,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
map->fec.prefix.af = AF_INET6;
break;
default:
- send_notification(nbr->tcp, S_UNSUP_ADDR, msg->id,
- msg->type);
+ send_notification(nbr->tcp, S_UNSUP_ADDR, msg->id, msg->type);
return (-1);
}
@@ -716,19 +697,16 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
&& map->fec.prefix.prefixlen > IPV4_MAX_BITLEN)
|| (map->fec.prefix.af == AF_IPV6
&& map->fec.prefix.prefixlen > IPV6_MAX_BITLEN)) {
- session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type);
return (-1);
}
if (len < off + PREFIX_SIZE(map->fec.prefix.prefixlen)) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
/* Prefix */
- memset(&map->fec.prefix.prefix, 0,
- sizeof(map->fec.prefix.prefix));
+ memset(&map->fec.prefix.prefix, 0, sizeof(map->fec.prefix.prefix));
memcpy(&map->fec.prefix.prefix, buf + off,
PREFIX_SIZE(map->fec.prefix.prefixlen));
@@ -739,17 +717,16 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
return (off + PREFIX_SIZE(map->fec.prefix.prefixlen));
case MAP_TYPE_PWID:
if (len < FEC_PWID_ELM_MIN_LEN) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
/* PW type */
memcpy(&map->fec.pwid.type, buf + off, sizeof(uint16_t));
map->fec.pwid.type = ntohs(map->fec.pwid.type);
- if (map->fec.pwid.type & CONTROL_WORD_FLAG) {
- map->flags |= F_MAP_PW_CWORD;
- map->fec.pwid.type &= ~CONTROL_WORD_FLAG;
+ if (CHECK_FLAG(map->fec.pwid.type, CONTROL_WORD_FLAG)) {
+ SET_FLAG(map->flags, F_MAP_PW_CWORD);
+ UNSET_FLAG(map->fec.pwid.type, CONTROL_WORD_FLAG);
}
off += sizeof(uint16_t);
@@ -758,8 +735,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
off += sizeof(uint8_t);
if (len != FEC_PWID_ELM_MIN_LEN + pw_len) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
@@ -773,14 +749,13 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
return (off);
if (pw_len < sizeof(uint32_t)) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
memcpy(&map->fec.pwid.pwid, buf + off, sizeof(uint32_t));
map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid);
- map->flags |= F_MAP_PW_ID;
+ SET_FLAG(map->flags, F_MAP_PW_ID);
off += sizeof(uint32_t);
pw_len -= sizeof(uint32_t);
@@ -789,29 +764,26 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
struct subtlv stlv;
if (pw_len < sizeof(stlv)) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
memcpy(&stlv, buf + off, sizeof(stlv));
if (stlv.length > pw_len) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
switch (stlv.type) {
case SUBTLV_IFMTU:
if (stlv.length != FEC_SUBTLV_IFMTU_SIZE) {
- session_shutdown(nbr, S_BAD_TLV_LEN,
- msg->id, msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
memcpy(&map->fec.pwid.ifmtu, buf + off +
SUBTLV_HDR_SIZE, sizeof(uint16_t));
map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu);
- map->flags |= F_MAP_PW_IFMTU;
+ SET_FLAG(map->flags, F_MAP_PW_IFMTU);
break;
default:
/* ignore */
@@ -824,8 +796,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
return (off);
case MAP_TYPE_TYPED_WCARD:
if (len < FEC_ELM_TWCARD_MIN_LEN) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
@@ -834,23 +805,19 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
memcpy(&twcard_len, buf + off, sizeof(uint8_t));
off += sizeof(uint8_t);
if (len != FEC_ELM_TWCARD_MIN_LEN + twcard_len) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
switch (map->fec.twcard.type) {
case MAP_TYPE_PREFIX:
if (twcard_len != sizeof(uint16_t)) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
- memcpy(&map->fec.twcard.u.prefix_af, buf + off,
- sizeof(uint16_t));
- map->fec.twcard.u.prefix_af =
- ntohs(map->fec.twcard.u.prefix_af);
+ memcpy(&map->fec.twcard.u.prefix_af, buf + off, sizeof(uint16_t));
+ map->fec.twcard.u.prefix_af = ntohs(map->fec.twcard.u.prefix_af);
off += sizeof(uint16_t);
switch (map->fec.twcard.u.prefix_af) {
@@ -861,29 +828,24 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
map->fec.twcard.u.prefix_af = AF_INET6;
break;
default:
- session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type);
return (-1);
}
break;
case MAP_TYPE_PWID:
if (twcard_len != sizeof(uint16_t)) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
+ session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
return (-1);
}
- memcpy(&map->fec.twcard.u.pw_type, buf + off,
- sizeof(uint16_t));
- map->fec.twcard.u.pw_type =
- ntohs(map->fec.twcard.u.pw_type);
+ memcpy(&map->fec.twcard.u.pw_type, buf + off, sizeof(uint16_t));
+ map->fec.twcard.u.pw_type = ntohs(map->fec.twcard.u.pw_type);
/* ignore the reserved bit as per RFC 6667 */
map->fec.twcard.u.pw_type &= ~PW_TWCARD_RESERVED_BIT;
off += sizeof(uint16_t);
break;
default:
- send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id,
- msg->type);
+ send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, msg->type);
return (-1);
}
diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c
index 470580ff5..04bff9015 100644
--- a/ldpd/lde_lib.c
+++ b/ldpd/lde_lib.c
@@ -17,17 +17,16 @@
#include "mpls.h"
static __inline int fec_compare(const struct fec *, const struct fec *);
-static int lde_nbr_is_nexthop(struct fec_node *,
- struct lde_nbr *);
-static void fec_free(void *);
-static struct fec_node *fec_add(struct fec *fec);
-static struct fec_nh *fec_nh_add(struct fec_node *, int, union ldpd_addr *,
+static int lde_nbr_is_nexthop(struct fec_node *, struct lde_nbr *);
+static void fec_free(void *);
+static struct fec_node *fec_add(struct fec *fec);
+static struct fec_nh *fec_nh_add(struct fec_node *, int, union ldpd_addr *,
ifindex_t, uint8_t, unsigned short);
-static void fec_nh_del(struct fec_nh *);
+static void fec_nh_del(struct fec_nh *);
RB_GENERATE(fec_tree, fec, entry, fec_compare)
-struct fec_tree ft = RB_INITIALIZER(&ft);
+struct fec_tree ft = RB_INITIALIZER(&ft);
struct event *gc_timer;
/* FEC tree functions */
@@ -47,11 +46,9 @@ fec_compare(const struct fec *a, const struct fec *b)
switch (a->type) {
case FEC_TYPE_IPV4:
- if (ntohl(a->u.ipv4.prefix.s_addr) <
- ntohl(b->u.ipv4.prefix.s_addr))
+ if (ntohl(a->u.ipv4.prefix.s_addr) < ntohl(b->u.ipv4.prefix.s_addr))
return (-1);
- if (ntohl(a->u.ipv4.prefix.s_addr) >
- ntohl(b->u.ipv4.prefix.s_addr))
+ if (ntohl(a->u.ipv4.prefix.s_addr) > ntohl(b->u.ipv4.prefix.s_addr))
return (1);
if (a->u.ipv4.prefixlen < b->u.ipv4.prefixlen)
return (-1);
@@ -79,11 +76,9 @@ fec_compare(const struct fec *a, const struct fec *b)
return (-1);
if (a->u.pwid.pwid > b->u.pwid.pwid)
return (1);
- if (ntohl(a->u.pwid.lsr_id.s_addr) <
- ntohl(b->u.pwid.lsr_id.s_addr))
+ if (ntohl(a->u.pwid.lsr_id.s_addr) < ntohl(b->u.pwid.lsr_id.s_addr))
return (-1);
- if (ntohl(a->u.pwid.lsr_id.s_addr) >
- ntohl(b->u.pwid.lsr_id.s_addr))
+ if (ntohl(a->u.pwid.lsr_id.s_addr) > ntohl(b->u.pwid.lsr_id.s_addr))
return (1);
return (0);
}
@@ -261,8 +256,7 @@ fec_add(struct fec *fec)
fn->pw_remote_status = PW_FORWARDING;
if (fec_insert(&ft, &fn->fec))
- log_warnx("failed to add %s to ft tree",
- log_fec(&fn->fec));
+ log_warnx("failed to add %s to ft tree", log_fec(&fn->fec));
return (fn);
}
@@ -338,14 +332,14 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop,
* installing in kernel and sending to peer
*/
iface = if_lookup(ldeconf, ifindex);
- if ((ldeconf->flags & F_LDPD_ORDERED_CONTROL) &&
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL) &&
!connected && iface != NULL && fec->type != FEC_TYPE_PWID)
- fnh->flags |= F_FEC_NH_DEFER;
+ SET_FLAG(fnh->flags, F_FEC_NH_DEFER);
}
- fnh->flags |= F_FEC_NH_NEW;
+ SET_FLAG(fnh->flags, F_FEC_NH_NEW);
if (connected)
- fnh->flags |= F_FEC_NH_CONNECTED;
+ SET_FLAG(fnh->flags, F_FEC_NH_CONNECTED);
}
void
@@ -388,22 +382,22 @@ lde_kernel_update(struct fec *fec)
return;
LIST_FOREACH_SAFE(fnh, &fn->nexthops, entry, safe) {
- if (fnh->flags & F_FEC_NH_NEW) {
- fnh->flags &= ~F_FEC_NH_NEW;
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_NEW)) {
+ UNSET_FLAG(fnh->flags, F_FEC_NH_NEW);
/*
* if LDP configured on interface or a static route
* clear flag else treat fec as a connected route
*/
- if (ldeconf->flags & F_LDPD_ENABLED) {
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ENABLED)) {
iface = if_lookup(ldeconf,fnh->ifindex);
- if (fnh->flags & F_FEC_NH_CONNECTED ||
+ if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED) ||
iface ||
fnh->route_type == ZEBRA_ROUTE_STATIC)
- fnh->flags &=~F_FEC_NH_NO_LDP;
+ UNSET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
else
- fnh->flags |= F_FEC_NH_NO_LDP;
+ SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
} else
- fnh->flags |= F_FEC_NH_NO_LDP;
+ SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP);
} else {
lde_send_delete_klabel(fn, fnh);
fec_nh_del(fnh);
@@ -510,7 +504,7 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping)
/* RFC 4447 control word and status tlv negotiation */
if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map)) {
- if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS)
+ if (rcvd_label_mapping && CHECK_FLAG(map->flags, F_MAP_PW_STATUS))
fn->pw_remote_status = map->pw_status;
return;
@@ -534,8 +528,7 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping)
* the possibility of multipath.
*/
LIST_FOREACH(fnh, &fn->nexthops, entry) {
- if (lde_address_find(ln, fnh->af,
- &fnh->nexthop) == NULL)
+ if (lde_address_find(ln, fnh->af, &fnh->nexthop) == NULL)
continue;
lde_send_delete_klabel(fn, fnh);
@@ -561,9 +554,9 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping)
* NH so clear flag and send labelmap msg to
* peer
*/
- if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) {
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) {
send_map = true;
- fnh->flags &= ~F_FEC_NH_DEFER;
+ UNSET_FLAG(fnh->flags, F_FEC_NH_DEFER);
}
fnh->remote_label = map->label;
if (fn->local_label != NO_LABEL)
@@ -575,9 +568,9 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping)
continue;
pw->remote_group = map->fec.pwid.group_id;
- if (map->flags & F_MAP_PW_IFMTU)
+ if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU))
pw->remote_mtu = map->fec.pwid.ifmtu;
- if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS) {
+ if (rcvd_label_mapping && CHECK_FLAG(map->flags, F_MAP_PW_STATUS)) {
pw->remote_status = map->pw_status;
fn->pw_remote_status = map->pw_status;
}
@@ -726,7 +719,7 @@ lde_check_release(struct map *map, struct lde_nbr *ln)
/* wildcard label release */
if (map->type == MAP_TYPE_WILDCARD ||
map->type == MAP_TYPE_TYPED_WCARD ||
- (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) {
+ (map->type == MAP_TYPE_PWID && !CHECK_FLAG(map->flags, F_MAP_PW_ID))) {
lde_check_release_wcard(map, ln);
return;
}
@@ -818,7 +811,7 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln)
/* wildcard label withdraw */
if (map->type == MAP_TYPE_WILDCARD ||
map->type == MAP_TYPE_TYPED_WCARD ||
- (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) {
+ (map->type == MAP_TYPE_PWID && !CHECK_FLAG(map->flags, F_MAP_PW_ID))) {
lde_check_withdraw_wcard(map, ln);
return;
}
@@ -868,15 +861,14 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln)
return;
/* Ordered Control: additional withdraw steps */
- if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) {
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) {
/* LWd.8: for each neighbor other that src of withdraw msg */
RB_FOREACH(lnbr, nbr_tree, &lde_nbrs) {
if (ln->peerid == lnbr->peerid)
continue;
/* LWd.9: check if previously sent a label mapping */
- me = (struct lde_map *)fec_find(&lnbr->sent_map,
- &fn->fec);
+ me = (struct lde_map *)fec_find(&lnbr->sent_map, &fn->fec);
/*
* LWd.10: does label sent to peer "map" to withdraw
@@ -915,8 +907,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
switch (f->type) {
case FEC_TYPE_IPV4:
case FEC_TYPE_IPV6:
- if (!lde_address_find(ln, fnh->af,
- &fnh->nexthop))
+ if (!lde_address_find(ln, fnh->af, &fnh->nexthop))
continue;
break;
case FEC_TYPE_PWID:
@@ -929,8 +920,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
default:
break;
}
- if (map->label != NO_LABEL && map->label !=
- fnh->remote_label)
+ if (map->label != NO_LABEL && map->label != fnh->remote_label)
continue;
lde_send_delete_klabel(fn, fnh);
@@ -941,8 +931,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL);
/* LWd.3: check previously received label mapping */
- if (me && (map->label == NO_LABEL ||
- map->label == me->map.label))
+ if (me && (map->label == NO_LABEL || map->label == me->map.label))
/*
* LWd.4: remove record of previously received
* label mapping
@@ -953,7 +942,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
continue;
/* Ordered Control: additional withdraw steps */
- if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) {
+ if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) {
/*
* LWd.8: for each neighbor other that src of
* withdraw msg
@@ -965,16 +954,14 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)
/* LWd.9: check if previously sent a label
* mapping
*/
- me = (struct lde_map *)fec_find(
- &lnbr->sent_map, &fn->fec);
+ me = (struct lde_map *)fec_find(&lnbr->sent_map, &fn->fec);
/*
* LWd.10: does label sent to peer "map" to
* withdraw label
*/
if (me && lde_nbr_is_nexthop(fn, lnbr))
/* LWd.11: send label withdraw */
- lde_send_labelwithdraw(lnbr, fn, NULL,
- NULL);
+ lde_send_labelwithdraw(lnbr, fn, NULL, NULL);
}
}
}
diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c
index d3193b16d..e66b9e92d 100644
--- a/ldpd/ldpe.c
+++ b/ldpd/ldpe.c
@@ -257,8 +257,7 @@ ldpe_imsg_compose_lde(int type, uint32_t peerid, pid_t pid, void *data,
{
if (iev_lde->ibuf.fd == -1)
return (0);
- return (imsg_compose_event(iev_lde, type, peerid, pid, -1,
- data, datalen));
+ return (imsg_compose_event(iev_lde, type, peerid, pid, -1, data, datalen));
}
/* ARGSUSED */
@@ -309,8 +308,7 @@ static void ldpe_dispatch_main(struct event *thread)
switch (imsg.hdr.type) {
case IMSG_IFSTATUS:
- if (imsg.hdr.len != IMSG_HEADER_SIZE +
- sizeof(struct kif))
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kif))
fatalx("IFSTATUS imsg with wrong len");
kif = imsg.data;
@@ -336,15 +334,13 @@ static void ldpe_dispatch_main(struct event *thread)
}
break;
case IMSG_NEWADDR:
- if (imsg.hdr.len != IMSG_HEADER_SIZE +
- sizeof(struct kaddr))
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kaddr))
fatalx("NEWADDR imsg with wrong len");
if_addr_add(imsg.data);
break;
case IMSG_DELADDR:
- if (imsg.hdr.len != IMSG_HEADER_SIZE +
- sizeof(struct kaddr))
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kaddr))
fatalx("DELADDR imsg with wrong len");
if_addr_del(imsg.data);
@@ -369,8 +365,7 @@ static void ldpe_dispatch_main(struct event *thread)
iev_lde->ev_write = NULL;
break;
case IMSG_INIT:
- if (imsg.hdr.len != IMSG_HEADER_SIZE +
- sizeof(struct ldpd_init))
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct ldpd_init))
fatalx("INIT imsg with wrong len");
memcpy(&init, imsg.data, sizeof(init));
@@ -398,14 +393,11 @@ static void ldpe_dispatch_main(struct event *thread)
disc_socket = -1;
edisc_socket = -1;
session_socket = -1;
- if ((ldp_af_conf_get(leconf, af))->flags &
- F_LDPD_AF_ENABLED)
- ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS,
- af, NULL, 0);
+ if (CHECK_FLAG((ldp_af_conf_get(leconf, af))->flags, F_LDPD_AF_ENABLED))
+ ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS, af, NULL, 0);
break;
case IMSG_SOCKET_NET:
- if (imsg.hdr.len != IMSG_HEADER_SIZE +
- sizeof(enum socket_type))
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(enum socket_type))
fatalx("SOCKET_NET imsg with wrong len");
socket_type = imsg.data;
@@ -434,15 +426,13 @@ static void ldpe_dispatch_main(struct event *thread)
break;
}
- ldpe_setup_sockets(af, disc_socket, edisc_socket,
- session_socket);
+ ldpe_setup_sockets(af, disc_socket, edisc_socket, session_socket);
if_update_all(af);
tnbr_update_all(af);
RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) {
if (nbr->af != af)
continue;
- nbr->laddr = (ldp_af_conf_get(leconf,
- af))->trans_addr;
+ nbr->laddr = (ldp_af_conf_get(leconf, af))->trans_addr;
#ifdef __OpenBSD__
nbrp = nbr_params_find(leconf, nbr->id);
if (nbrp) {
@@ -456,8 +446,7 @@ static void ldpe_dispatch_main(struct event *thread)
}
break;
case IMSG_RTRID_UPDATE:
- memcpy(&global.rtr_id, imsg.data,
- sizeof(global.rtr_id));
+ memcpy(&global.rtr_id, imsg.data, sizeof(global.rtr_id));
if (leconf->rtr_id.s_addr == INADDR_ANY) {
ldpe_reset_nbrs(AF_UNSPEC);
}
@@ -465,8 +454,7 @@ static void ldpe_dispatch_main(struct event *thread)
tnbr_update_all(AF_UNSPEC);
break;
case IMSG_RECONF_CONF:
- if ((nconf = malloc(sizeof(struct ldpd_conf))) ==
- NULL)
+ if ((nconf = malloc(sizeof(struct ldpd_conf))) == NULL)
fatal(NULL);
memcpy(nconf, imsg.data, sizeof(struct ldpd_conf));
@@ -546,16 +534,13 @@ static void ldpe_dispatch_main(struct event *thread)
memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug));
break;
case IMSG_FILTER_UPDATE:
- if (imsg.hdr.len != IMSG_HEADER_SIZE +
- sizeof(struct ldp_access)) {
+ if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct ldp_access)) {
log_warnx("%s: wrong imsg len", __func__);
break;
}
laccess = imsg.data;
- ldpe_check_filter_af(AF_INET, &leconf->ipv4,
- laccess->name);
- ldpe_check_filter_af(AF_INET6, &leconf->ipv6,
- laccess->name);
+ ldpe_check_filter_af(AF_INET, &leconf->ipv4, laccess->name);
+ ldpe_check_filter_af(AF_INET6, &leconf->ipv6, laccess->name);
break;
case IMSG_LDP_SYNC_IF_STATE_REQUEST:
if (imsg.hdr.len != IMSG_HEADER_SIZE +
@@ -605,8 +590,7 @@ static void ldpe_dispatch_main(struct event *thread)
}
break;
default:
- log_debug("%s: error handling imsg %d",
- __func__, imsg.hdr.type);
+ log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type);
break;
}
imsg_free(&imsg);
@@ -650,8 +634,7 @@ static void ldpe_dispatch_lde(struct event *thread)
case IMSG_RELEASE_ADD:
case IMSG_REQUEST_ADD:
case IMSG_WITHDRAW_ADD:
- if (imsg.hdr.len - IMSG_HEADER_SIZE !=
- sizeof(struct map))
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct map))
fatalx("invalid size of map request");
map = imsg.data;
@@ -706,8 +689,7 @@ static void ldpe_dispatch_lde(struct event *thread)
}
break;
case IMSG_NOTIFICATION_SEND:
- if (imsg.hdr.len - IMSG_HEADER_SIZE !=
- sizeof(struct notify_msg))
+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct notify_msg))
fatalx("invalid size of OE request");
nm = imsg.data;
@@ -741,8 +723,7 @@ static void ldpe_dispatch_lde(struct event *thread)
session_shutdown(nbr,S_SHUTDOWN,0,0);
break;
default:
- log_debug("%s: error handling imsg %d",
- __func__, imsg.hdr.type);
+ log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type);
break;
}
imsg_free(&imsg);
@@ -860,7 +841,7 @@ ldpe_remove_dynamic_tnbrs(int af)
if (tnbr->af != af)
continue;
- tnbr->flags &= ~F_TNBR_DYNAMIC;
+ UNSET_FLAG(tnbr->flags, F_TNBR_DYNAMIC);
tnbr_check(leconf, tnbr);
}
}
diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c
index 6f9177fe8..5209c55bb 100644
--- a/ldpd/neighbor.c
+++ b/ldpd/neighbor.c
@@ -19,10 +19,8 @@ DEFINE_HOOK(ldp_nbr_state_change, (struct nbr * nbr, int old_state),
(nbr, old_state));
static __inline int nbr_id_compare(const struct nbr *, const struct nbr *);
-static __inline int nbr_addr_compare(const struct nbr *,
- const struct nbr *);
-static __inline int nbr_pid_compare(const struct nbr *,
- const struct nbr *);
+static __inline int nbr_addr_compare(const struct nbr *, const struct nbr *);
+static __inline int nbr_pid_compare(const struct nbr *, const struct nbr *);
static void nbr_update_peerid(struct nbr *);
static void nbr_ktimer(struct event *thread);
static void nbr_start_ktimer(struct nbr *);
@@ -127,7 +125,7 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event)
old_state = nbr->state;
for (i = 0; nbr_fsm_tbl[i].state != -1; i++)
- if ((nbr_fsm_tbl[i].state & old_state) &&
+ if (CHECK_FLAG(nbr_fsm_tbl[i].state, old_state) &&
(nbr_fsm_tbl[i].event == event)) {
new_state = nbr_fsm_tbl[i].new_state;
break;
@@ -196,8 +194,7 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event)
send_keepalive(nbr);
break;
case NBR_ACT_CLOSE_SESSION:
- ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0,
- NULL, 0);
+ ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0);
session_close(nbr);
break;
case NBR_ACT_NOTHING:
@@ -606,8 +603,7 @@ nbr_establish_connection(struct nbr *nbr)
return (-1);
}
#else
- sock_set_md5sig(nbr->fd, nbr->af, &nbr->raddr,
- nbrp->auth.md5key);
+ sock_set_md5sig(nbr->fd, nbr->af, &nbr->raddr, nbrp->auth.md5key);
#endif
}
@@ -646,8 +642,7 @@ nbr_establish_connection(struct nbr *nbr)
send_hello(adj->source.type, adj->source.link.ia,
adj->source.target);
- if (connect(nbr->fd, &remote_su.sa, sockaddr_len(&remote_su.sa))
- == -1) {
+ if (connect(nbr->fd, &remote_su.sa, sockaddr_len(&remote_su.sa)) == -1) {
if (errno == EINPROGRESS) {
event_add_write(master, nbr_connect_cb, nbr, nbr->fd,
&nbr->ev_connect);
@@ -674,14 +669,14 @@ nbr_gtsm_enabled(struct nbr *nbr, struct nbr_params *nbrp)
* statically (e.g., via configuration) and/or dynamically override the
* default behavior and enable/disable GTSM on a per-peer basis".
*/
- if (nbrp && (nbrp->flags & F_NBRP_GTSM))
+ if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_GTSM))
return (nbrp->gtsm_enabled);
- if ((ldp_af_conf_get(leconf, nbr->af))->flags & F_LDPD_AF_NO_GTSM)
+ if (CHECK_FLAG((ldp_af_conf_get(leconf, nbr->af))->flags, F_LDPD_AF_NO_GTSM))
return (0);
/* By default, GTSM support has to be negotiated for LDPv4 */
- if (nbr->af == AF_INET && !(nbr->flags & F_NBR_GTSM_NEGOTIATED))
+ if (nbr->af == AF_INET && !CHECK_FLAG(nbr->flags, F_NBR_GTSM_NEGOTIATED))
return (0);
return (1);
@@ -692,7 +687,7 @@ nbr_gtsm_setup(int fd, int af, struct nbr_params *nbrp)
{
int ttl = 255;
- if (nbrp && (nbrp->flags & F_NBRP_GTSM_HOPS))
+ if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_GTSM_HOPS))
ttl = 256 - nbrp->gtsm_hops;
switch (af) {
@@ -740,8 +735,7 @@ nbr_gtsm_check(int fd, struct nbr *nbr, struct nbr_params *nbrp)
}
if (nbr_gtsm_setup(fd, nbr->af, nbrp) == -1) {
- log_warnx("%s: error enabling GTSM for lsr-id %pI4", __func__,
- &nbr->id);
+ log_warnx("%s: error enabling GTSM for lsr-id %pI4", __func__, &nbr->id);
return (-1);
}
@@ -772,8 +766,7 @@ nbr_act_session_operational(struct nbr *nbr)
static void
nbr_send_labelmappings(struct nbr *nbr)
{
- ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0,
- NULL, 0);
+ ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0, NULL, 0);
}
static __inline int
@@ -810,7 +803,7 @@ nbr_get_keepalive(int af, struct in_addr lsr_id)
struct nbr_params *nbrp;
nbrp = nbr_params_find(leconf, lsr_id);
- if (nbrp && (nbrp->flags & F_NBRP_KEEPALIVE))
+ if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_KEEPALIVE))
return (nbrp->keepalive);
return ((ldp_af_conf_get(leconf, af))->keepalive);
@@ -834,8 +827,7 @@ nbr_to_ctl(struct nbr *nbr)
nctl.stats = nbr->stats;
nctl.flags = nbr->flags;
nctl.max_pdu_len = nbr->max_pdu_len;
- nctl.hold_time_remaining =
- event_timer_remain_second(nbr->keepalive_timer);
+ nctl.hold_time_remaining = event_timer_remain_second(nbr->keepalive_timer);
gettimeofday(&now, NULL);
if (nbr->state == NBR_STA_OPER) {
diff --git a/ldpd/packet.c b/ldpd/packet.c
index 71eeb1adb..d2d9305e7 100644
--- a/ldpd/packet.c
+++ b/ldpd/packet.c
@@ -15,13 +15,12 @@
#include "sockopt.h"
-static struct iface *disc_find_iface(unsigned int, int,
- union ldpd_addr *);
+static struct iface *disc_find_iface(unsigned int, int, union ldpd_addr *);
static void session_read(struct event *thread);
static void session_write(struct event *thread);
-static ssize_t session_get_pdu(struct ibuf_read *, char **);
-static void tcp_close(struct tcp_conn *);
-static struct pending_conn *pending_conn_new(int, int, union ldpd_addr *);
+static ssize_t session_get_pdu(struct ibuf_read *, char **);
+static void tcp_close(struct tcp_conn *);
+static struct pending_conn *pending_conn_new(int, int, union ldpd_addr *);
static void pending_conn_timeout(struct event *thread);
int
@@ -144,8 +143,7 @@ void disc_recv_packet(struct event *thread)
if ((r = recvmsg(fd, &m, 0)) == -1) {
if (errno != EAGAIN && errno != EINTR)
- log_debug("%s: read error: %s", __func__,
- strerror(errno));
+ log_debug("%s: read error: %s", __func__, strerror(errno));
return;
}
@@ -154,10 +152,10 @@ void disc_recv_packet(struct event *thread)
multicast = (m.msg_flags & MSG_MCAST) ? 1 : 0;
#else
multicast = 0;
- for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL;
- cmsg = CMSG_NXTHDR(&m, cmsg)) {
+ for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL; cmsg = CMSG_NXTHDR(&m, cmsg)) {
#if defined(HAVE_IP_PKTINFO)
- if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP &&
+ if (af == AF_INET &&
+ cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo *pktinfo;
@@ -167,7 +165,8 @@ void disc_recv_packet(struct event *thread)
break;
}
#elif defined(HAVE_IP_RECVDSTADDR)
- if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP &&
+ if (af == AF_INET &&
+ cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_RECVDSTADDR) {
struct in_addr *addr;
@@ -179,7 +178,8 @@ void disc_recv_packet(struct event *thread)
#else
#error "Unsupported socket API"
#endif
- if (af == AF_INET6 && cmsg->cmsg_level == IPPROTO_IPV6 &&
+ if (af == AF_INET6 &&
+ cmsg->cmsg_level == IPPROTO_IPV6 &&
cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo *pktinfo;
@@ -191,8 +191,7 @@ void disc_recv_packet(struct event *thread)
}
#endif /* MSG_MCAST */
if (bad_addr(af, &src)) {
- log_debug("%s: invalid source address: %s", __func__,
- log_addr(af, &src));
+ log_debug("%s: invalid source address: %s", __func__, log_addr(af, &src));
return;
}
ifindex = getsockopt_ifindex(af, &m);
@@ -207,8 +206,7 @@ void disc_recv_packet(struct event *thread)
/* check packet size */
len = (uint16_t)r;
if (len < (LDP_HDR_SIZE + LDP_MSG_SIZE) || len > LDP_MAX_LEN) {
- log_debug("%s: bad packet size, source %s", __func__,
- log_addr(af, &src));
+ log_debug("%s: bad packet size, source %s", __func__, log_addr(af, &src));
return;
}
@@ -309,10 +307,8 @@ void session_accept(struct event *thread)
*/
if (errno == ENFILE || errno == EMFILE) {
accept_pause();
- } else if (errno != EWOULDBLOCK && errno != EINTR &&
- errno != ECONNABORTED)
- log_debug("%s: accept error: %s", __func__,
- strerror(errno));
+ } else if (errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED)
+ log_debug("%s: accept error: %s", __func__, strerror(errno));
return;
}
sock_set_nonblock(newfd);
@@ -445,15 +441,13 @@ static void session_read(struct event *thread)
max_pdu_len = nbr->max_pdu_len;
else
max_pdu_len = LDP_MAX_LEN;
- if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE) ||
- pdu_len > max_pdu_len) {
+ if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE) || pdu_len > max_pdu_len) {
session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0);
free(buf);
return;
}
pdu_len -= LDP_HDR_PDU_LEN;
- if (ldp_hdr->lsr_id != nbr->id.s_addr ||
- ldp_hdr->lspace_id != 0) {
+ if (ldp_hdr->lsr_id != nbr->id.s_addr || ldp_hdr->lspace_id != 0) {
session_shutdown(nbr, S_BAD_LDP_ID, 0, 0);
free(buf);
return;
@@ -469,10 +463,8 @@ static void session_read(struct event *thread)
msg = (struct ldp_msg *)pdu;
type = ntohs(msg->type);
msg_len = ntohs(msg->length);
- if (msg_len < LDP_MSG_LEN ||
- (msg_len + LDP_MSG_DEAD_LEN) > pdu_len) {
- session_shutdown(nbr, S_BAD_MSG_LEN, msg->id,
- msg->type);
+ if (msg_len < LDP_MSG_LEN || (msg_len + LDP_MSG_DEAD_LEN) > pdu_len) {
+ session_shutdown(nbr, S_BAD_MSG_LEN, msg->id, msg->type);
free(buf);
return;
}
@@ -484,8 +476,7 @@ static void session_read(struct event *thread)
case MSG_TYPE_INIT:
if ((nbr->state != NBR_STA_INITIAL) &&
(nbr->state != NBR_STA_OPENSENT)) {
- session_shutdown(nbr, S_SHUTDOWN,
- msg->id, msg->type);
+ session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type);
free(buf);
return;
}
@@ -493,8 +484,7 @@ static void session_read(struct event *thread)
case MSG_TYPE_KEEPALIVE:
if ((nbr->state == NBR_STA_INITIAL) ||
(nbr->state == NBR_STA_OPENSENT)) {
- session_shutdown(nbr, S_SHUTDOWN,
- msg->id, msg->type);
+ session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type);
free(buf);
return;
}
@@ -503,8 +493,7 @@ static void session_read(struct event *thread)
break;
default:
if (nbr->state != NBR_STA_OPER) {
- session_shutdown(nbr, S_SHUTDOWN,
- msg->id, msg->type);
+ session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type);
free(buf);
return;
}
@@ -534,16 +523,14 @@ static void session_read(struct event *thread)
case MSG_TYPE_LABELWITHDRAW:
case MSG_TYPE_LABELRELEASE:
case MSG_TYPE_LABELABORTREQ:
- ret = recv_labelmessage(nbr, pdu, msg_size,
- type);
+ ret = recv_labelmessage(nbr, pdu, msg_size, type);
break;
default:
log_debug("%s: unknown LDP message from nbr %pI4",
__func__, &nbr->id);
if (!(ntohs(msg->type) & UNKNOWN_FLAG)) {
nbr->stats.unknown_msg++;
- send_notification(nbr->tcp,
- S_UNKNOWN_MSG, msg->id, msg->type);
+ send_notification(nbr->tcp, S_UNKNOWN_MSG, msg->id, msg->type);
}
/* ignore the message */
ret = 0;
@@ -664,8 +651,7 @@ session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id,
void
session_close(struct nbr *nbr)
{
- log_debug("%s: closing session with lsr-id %pI4", __func__,
- &nbr->id);
+ log_debug("%s: closing session with lsr-id %pI4", __func__, &nbr->id);
ldp_sync_fsm_nbr_event(nbr, LDP_SYNC_EVT_SESSION_CLOSE);
@@ -788,8 +774,7 @@ pending_conn_find(int af, union ldpd_addr *addr)
struct pending_conn *pconn;
TAILQ_FOREACH(pconn, &global.pending_conns, entry)
- if (af == pconn->af &&
- ldp_addrcmp(af, addr, &pconn->addr) == 0)
+ if (af == pconn->af && ldp_addrcmp(af, addr, &pconn->addr) == 0)
return (pconn);
return (NULL);
diff --git a/lib/command.c b/lib/command.c
index 27cd3a04b..7a7ce3f5d 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -1303,6 +1303,14 @@ int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
++(*line_num);
+ if (vty_log_commands) {
+ int len = strlen(vty->buf);
+
+ /* now log the command */
+ zlog_notice("config-from-file# %.*s", len ? len - 1 : 0,
+ vty->buf);
+ }
+
ret = command_config_read_one_line(vty, NULL, *line_num, 0);
if (ret != CMD_SUCCESS && ret != CMD_WARNING
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 07e2eafec..e89005726 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -130,6 +130,7 @@ static const struct optspec os_always = {
" --limit-fds Limit number of fds supported\n",
lo_always};
+static bool logging_to_stdout = false; /* set when --log stdout specified */
static const struct option lo_cfg[] = {
{"config_file", required_argument, NULL, 'f'},
@@ -738,6 +739,11 @@ struct event_loop *frr_init(void)
while ((log_arg = log_args_pop(di->early_logging))) {
command_setup_early_logging(log_arg->target,
di->early_loglevel);
+ /* this is a bit of a hack,
+ but need to notice when
+ the target is stdout */
+ if (strcmp(log_arg->target, "stdout") == 0)
+ logging_to_stdout = true;
XFREE(MTYPE_TMP, log_arg);
}
@@ -1088,9 +1094,15 @@ static void frr_terminal_close(int isexit)
"%s: failed to open /dev/null: %s", __func__,
safe_strerror(errno));
} else {
- dup2(nullfd, 0);
- dup2(nullfd, 1);
- dup2(nullfd, 2);
+ int fd;
+ /*
+ * only redirect stdin, stdout, stderr to null when a tty also
+ * don't redirect when stdout is set with --log stdout
+ */
+ for (fd = 2; fd >= 0; fd--)
+ if (isatty(fd) &&
+ (fd != STDOUT_FILENO || !logging_to_stdout))
+ dup2(nullfd, fd);
close(nullfd);
}
}
@@ -1168,9 +1180,16 @@ void frr_run(struct event_loop *master)
"%s: failed to open /dev/null: %s",
__func__, safe_strerror(errno));
} else {
- dup2(nullfd, 0);
- dup2(nullfd, 1);
- dup2(nullfd, 2);
+ int fd;
+ /*
+ * only redirect stdin, stdout, stderr to null when a
+ * tty also don't redirect when stdout is set with --log
+ * stdout
+ */
+ for (fd = 2; fd >= 0; fd--)
+ if (isatty(fd) &&
+ (fd != STDOUT_FILENO || !logging_to_stdout))
+ dup2(nullfd, fd);
close(nullfd);
}
@@ -1194,7 +1213,7 @@ void frr_fini(void)
{
FILE *fp;
char filename[128];
- int have_leftovers;
+ int have_leftovers = 0;
hook_call(frr_fini);
@@ -1220,16 +1239,15 @@ void frr_fini(void)
/* frrmod_init -> nothing needed / hooks */
rcu_shutdown();
- if (!debug_memstats_at_exit)
- return;
-
- have_leftovers = log_memstats(stderr, di->name);
+ /* also log memstats to stderr when stderr goes to a file*/
+ if (debug_memstats_at_exit || !isatty(STDERR_FILENO))
+ have_leftovers = log_memstats(stderr, di->name);
/* in case we decide at runtime that we want exit-memstats for
- * a daemon, but it has no stderr because it's daemonized
+ * a daemon
* (only do this if we actually have something to print though)
*/
- if (!have_leftovers)
+ if (!debug_memstats_at_exit || !have_leftovers)
return;
snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu",
diff --git a/lib/libospf.h b/lib/libospf.h
index 3262534de..676b563ff 100644
--- a/lib/libospf.h
+++ b/lib/libospf.h
@@ -55,6 +55,7 @@ extern "C" {
#define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT 40
#define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL 1
#define OSPF_HELLO_INTERVAL_DEFAULT 10
+#define OSPF_HELLO_DELAY_DEFAULT 10
#define OSPF_ROUTER_PRIORITY_DEFAULT 1
#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5
#define OSPF_TRANSMIT_DELAY_DEFAULT 1
diff --git a/lib/nexthop.c b/lib/nexthop.c
index b04c95c05..dcbb76b68 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -1076,3 +1076,12 @@ static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea,
}
return -1;
}
+
+bool nexthop_is_ifindex_type(const struct nexthop *nh)
+{
+ if (nh->type == NEXTHOP_TYPE_IFINDEX ||
+ nh->type == NEXTHOP_TYPE_IPV4_IFINDEX ||
+ nh->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+ return true;
+ return false;
+}
diff --git a/lib/nexthop.h b/lib/nexthop.h
index 1d95a3eee..43dd71e11 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -234,6 +234,9 @@ extern struct nexthop *nexthop_dup(const struct nexthop *nexthop,
extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
struct nexthop *rparent);
+/* Check nexthop of IFINDEX type */
+extern bool nexthop_is_ifindex_type(const struct nexthop *nh);
+
/*
* Parse one or more backup index values, as comma-separated numbers,
* into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS
diff --git a/lib/routemap.h b/lib/routemap.h
index 9b2e18b4a..7277744dc 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -259,6 +259,8 @@ DECLARE_QOBJ_TYPE(route_map);
(strmatch(C, "frr-zebra-route-map:ipv4-next-hop-prefix-length"))
#define IS_MATCH_SRC_PROTO(C) \
(strmatch(C, "frr-zebra-route-map:source-protocol"))
+#define IS_MATCH_BGP_SRC_PROTO(C) \
+ (strmatch(C, "frr-bgp-route-map:source-protocol"))
#define IS_MATCH_SRC_INSTANCE(C) \
(strmatch(C, "frr-zebra-route-map:source-instance"))
/* BGP route-map match conditions */
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index 419086c4c..0ccc78e83 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -599,11 +599,14 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length"));
- } else if (IS_MATCH_SRC_PROTO(condition)) {
+ } else if (IS_MATCH_SRC_PROTO(condition) ||
+ IS_MATCH_BGP_SRC_PROTO(condition)) {
vty_out(vty, " match source-protocol %s\n",
yang_dnode_get_string(
dnode,
- "./rmap-match-condition/frr-zebra-route-map:source-protocol"));
+ IS_MATCH_SRC_PROTO(condition)
+ ? "./rmap-match-condition/frr-zebra-route-map:source-protocol"
+ : "./rmap-match-condition/frr-bgp-route-map:source-protocol"));
} else if (IS_MATCH_SRC_INSTANCE(condition)) {
vty_out(vty, " match source-instance %s\n",
yang_dnode_get_string(
diff --git a/lib/vty.c b/lib/vty.c
index c6134fe07..d6a0dba20 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -125,8 +125,8 @@ static int no_password_check = 0;
/* Integrated configuration file path */
static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
-static bool do_log_commands;
-static bool do_log_commands_perm;
+bool vty_log_commands;
+static bool vty_log_commands_perm;
void vty_mgmt_resume_response(struct vty *vty, bool success)
{
@@ -508,7 +508,7 @@ static int vty_command(struct vty *vty, char *buf)
/*
* Log non empty command lines
*/
- if (do_log_commands &&
+ if (vty_log_commands &&
strncmp(buf, "echo PING", strlen("echo PING")) != 0)
cp = buf;
if (cp != NULL) {
@@ -3160,15 +3160,15 @@ DEFPY (log_commands,
"Log all commands\n")
{
if (no) {
- if (do_log_commands_perm) {
+ if (vty_log_commands_perm) {
vty_out(vty,
"Daemon started with permanent logging turned on for commands, ignoring\n");
return CMD_WARNING;
}
- do_log_commands = false;
+ vty_log_commands = false;
} else
- do_log_commands = true;
+ vty_log_commands = true;
return CMD_SUCCESS;
}
@@ -3196,7 +3196,7 @@ static int vty_config_write(struct vty *vty)
vty_endframe(vty, "exit\n");
- if (do_log_commands)
+ if (vty_log_commands)
vty_out(vty, "log commands\n");
vty_out(vty, "!\n");
@@ -3677,8 +3677,8 @@ void vty_init(struct event_loop *master_thread, bool do_command_logging)
install_element(CONFIG_NODE, &log_commands_cmd);
if (do_command_logging) {
- do_log_commands = true;
- do_log_commands_perm = true;
+ vty_log_commands = true;
+ vty_log_commands_perm = true;
}
install_element(ENABLE_NODE, &terminal_monitor_cmd);
diff --git a/lib/vty.h b/lib/vty.h
index 5114238f6..560748d91 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -335,6 +335,7 @@ struct vty_arg {
#endif
extern struct nb_config *vty_mgmt_candidate_config;
+extern bool vty_log_commands;
/* Prototypes. */
extern void vty_init(struct event_loop *m, bool do_command_logging);
diff --git a/mgmtd/mgmt.h b/mgmtd/mgmt.h
index 83353e817..603296bb3 100644
--- a/mgmtd/mgmt.h
+++ b/mgmtd/mgmt.h
@@ -108,16 +108,4 @@ extern void mgmt_master_init(struct event_loop *master, const int buffer_size);
extern void mgmt_init(void);
extern void mgmt_vty_init(void);
-static inline char *mgmt_realtime_to_string(struct timeval *tv, char *buf,
- size_t sz)
-{
- struct tm tm;
- size_t n;
-
- localtime_r((const time_t *)&tv->tv_sec, &tm);
- n = strftime(buf, sz, "%Y-%m-%dT%H:%M:%S", &tm);
- snprintf(&buf[n], sz - n, ",%06u000", (unsigned int)tv->tv_usec);
- return buf;
-}
-
#endif /* _FRR_MGMTD_H */
diff --git a/mgmtd/mgmt_ds.h b/mgmtd/mgmt_ds.h
index 8d01f3d5b..2a32eb641 100644
--- a/mgmtd/mgmt_ds.h
+++ b/mgmtd/mgmt_ds.h
@@ -28,12 +28,9 @@
for ((id) = MGMTD_DS_NONE; (id) < MGMTD_DS_MAX_ID; (id)++)
#define MGMTD_MAX_COMMIT_LIST 10
-#define MGMTD_MD5_HASH_LEN 16
-#define MGMTD_MD5_HASH_STR_HEX_LEN 33
#define MGMTD_COMMIT_FILE_PATH DAEMON_DB_DIR "/commit-%s.json"
#define MGMTD_COMMIT_INDEX_FILE_NAME DAEMON_DB_DIR "/commit-index.dat"
-#define MGMTD_COMMIT_TIME_STR_LEN 100
extern struct nb_config *running_config;
diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c
index abf1f954d..262741b66 100644
--- a/mgmtd/mgmt_fe_adapter.c
+++ b/mgmtd/mgmt_fe_adapter.c
@@ -1714,7 +1714,7 @@ static void
mgmt_fe_adapter_cmt_stats_write(struct vty *vty,
struct mgmt_fe_client_adapter *adapter)
{
- char buf[100] = {0};
+ char buf[MGMT_LONG_TIME_MAX_LEN];
if (!mm->perf_stats_en)
return;
@@ -1795,7 +1795,7 @@ static void
mgmt_fe_adapter_setcfg_stats_write(struct vty *vty,
struct mgmt_fe_client_adapter *adapter)
{
- char buf[100] = {0};
+ char buf[MGMT_LONG_TIME_MAX_LEN];
if (!mm->perf_stats_en)
return;
diff --git a/mgmtd/mgmt_history.c b/mgmtd/mgmt_history.c
index 2251c49f1..a49718a49 100644
--- a/mgmtd/mgmt_history.c
+++ b/mgmtd/mgmt_history.c
@@ -18,8 +18,8 @@
struct mgmt_cmt_info_t {
struct mgmt_cmt_infos_item cmts;
- char cmtid_str[MGMTD_MD5_HASH_STR_HEX_LEN];
- char time_str[MGMTD_COMMIT_TIME_STR_LEN];
+ char cmtid_str[MGMT_SHORT_TIME_MAX_LEN];
+ char time_str[MGMT_LONG_TIME_MAX_LEN];
char cmt_json_file[PATH_MAX];
};
@@ -54,36 +54,30 @@ static void mgmt_history_remove_file(char *name)
zlog_err("Old commit info deletion failed");
}
-static void mgmt_history_hash(const char *input_str, char *hash)
+static struct mgmt_cmt_info_t *mgmt_history_new_cmt_info(void)
{
- int i;
- unsigned char digest[MGMTD_MD5_HASH_LEN];
- MD5_CTX ctx;
-
- memset(&ctx, 0, sizeof(ctx));
- MD5Init(&ctx);
- MD5Update(&ctx, input_str, strlen(input_str));
- MD5Final(digest, &ctx);
-
- for (i = 0; i < MGMTD_MD5_HASH_LEN; i++)
- snprintf(&hash[i * 2], MGMTD_MD5_HASH_STR_HEX_LEN, "%02x",
- (unsigned int)digest[i]);
+ struct mgmt_cmt_info_t *new;
+ struct timespec tv;
+ struct tm tm;
+
+ new = XCALLOC(MTYPE_MGMTD_CMT_INFO, sizeof(struct mgmt_cmt_info_t));
+
+ clock_gettime(CLOCK_REALTIME, &tv);
+ localtime_r(&tv.tv_sec, &tm);
+
+ mgmt_time_to_string(&tv, true, new->time_str, sizeof(new->time_str));
+ mgmt_time_to_string(&tv, false, new->cmtid_str, sizeof(new->cmtid_str));
+ snprintf(new->cmt_json_file, sizeof(new->cmt_json_file),
+ MGMTD_COMMIT_FILE_PATH, new->cmtid_str);
+
+ return new;
}
static struct mgmt_cmt_info_t *mgmt_history_create_cmt_rec(void)
{
- struct mgmt_cmt_info_t *new;
+ struct mgmt_cmt_info_t *new = mgmt_history_new_cmt_info();
struct mgmt_cmt_info_t *cmt_info;
struct mgmt_cmt_info_t *last_cmt_info = NULL;
- struct timeval cmt_recd_tv;
-
- new = XCALLOC(MTYPE_MGMTD_CMT_INFO, sizeof(struct mgmt_cmt_info_t));
- gettimeofday(&cmt_recd_tv, NULL);
- mgmt_realtime_to_string(&cmt_recd_tv, new->time_str,
- sizeof(new->time_str));
- mgmt_history_hash(new->time_str, new->cmtid_str);
- snprintf(new->cmt_json_file, sizeof(new->cmt_json_file) - 1,
- MGMTD_COMMIT_FILE_PATH, new->cmtid_str);
if (mgmt_cmt_infos_count(&mm->cmts) == MGMTD_MAX_COMMIT_LIST) {
FOREACH_CMT_REC (mm, cmt_info)
@@ -106,8 +100,7 @@ mgmt_history_find_cmt_record(const char *cmtid_str)
struct mgmt_cmt_info_t *cmt_info;
FOREACH_CMT_REC (mm, cmt_info) {
- if (strncmp(cmt_info->cmtid_str, cmtid_str,
- MGMTD_MD5_HASH_STR_HEX_LEN) == 0)
+ if (strcmp(cmt_info->cmtid_str, cmtid_str) == 0)
return cmt_info;
}
@@ -282,8 +275,7 @@ int mgmt_history_rollback_by_id(struct vty *vty, const char *cmtid_str)
}
FOREACH_CMT_REC (mm, cmt_info) {
- if (strncmp(cmt_info->cmtid_str, cmtid_str,
- MGMTD_MD5_HASH_STR_HEX_LEN) == 0) {
+ if (strcmp(cmt_info->cmtid_str, cmtid_str) == 0) {
ret = mgmt_history_rollback_to_cmt(vty, cmt_info,
false);
return ret;
@@ -349,9 +341,9 @@ void show_mgmt_cmt_history(struct vty *vty)
int slno = 0;
vty_out(vty, "Last 10 commit history:\n");
- vty_out(vty, " Sl.No\tCommit-ID(HEX)\t\t\t Commit-Record-Time\n");
+ vty_out(vty, "Slot Commit-ID Commit-Record-Time\n");
FOREACH_CMT_REC (mm, cmt_info) {
- vty_out(vty, " %d\t%s %s\n", slno, cmt_info->cmtid_str,
+ vty_out(vty, "%4d %23s %s\n", slno, cmt_info->cmtid_str,
cmt_info->time_str);
slno++;
}
diff --git a/mgmtd/mgmt_history.h b/mgmtd/mgmt_history.h
index 5f96cf90e..d3f795895 100644
--- a/mgmtd/mgmt_history.h
+++ b/mgmtd/mgmt_history.h
@@ -54,4 +54,42 @@ extern void mgmt_history_new_record(struct mgmt_ds_ctx *ds_ctx);
extern void mgmt_history_destroy(void);
extern void mgmt_history_init(void);
+/*
+ * 012345678901234567890123456789
+ * 2023-12-31T12:12:12,012345678
+ * 20231231121212012345678
+ */
+#define MGMT_LONG_TIME_FMT "%Y-%m-%dT%H:%M:%S"
+#define MGMT_LONG_TIME_MAX_LEN 30
+#define MGMT_SHORT_TIME_FMT "%Y%m%d%H%M%S"
+#define MGMT_SHORT_TIME_MAX_LEN 24
+
+static inline const char *
+mgmt_time_to_string(struct timespec *tv, bool long_fmt, char *buffer, size_t sz)
+{
+ struct tm tm;
+ size_t n;
+
+ localtime_r(&tv->tv_sec, &tm);
+
+ if (long_fmt) {
+ n = strftime(buffer, sz, MGMT_LONG_TIME_FMT, &tm);
+ snprintf(&buffer[n], sz - n, ",%09lu", tv->tv_nsec);
+ } else {
+ n = strftime(buffer, sz, MGMT_SHORT_TIME_FMT, &tm);
+ snprintf(&buffer[n], sz - n, "%09lu", tv->tv_nsec);
+ }
+
+ return buffer;
+}
+
+static inline const char *mgmt_realtime_to_string(struct timeval *tv, char *buf,
+ size_t sz)
+{
+ struct timespec ts = {.tv_sec = tv->tv_sec,
+ .tv_nsec = tv->tv_usec * 1000};
+
+ return mgmt_time_to_string(&ts, true, buf, sz);
+}
+
#endif /* _FRR_MGMTD_HISTORY_H_ */
diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c
index 976eb529d..3d5d4d259 100644
--- a/ospf6d/ospf6_gr.c
+++ b/ospf6d/ospf6_gr.c
@@ -14,6 +14,7 @@
#include "log.h"
#include "hook.h"
#include "printfrr.h"
+#include "lib_errors.h"
#include "ospf6d/ospf6_lsa.h"
#include "ospf6d/ospf6_lsdb.h"
@@ -25,21 +26,25 @@
#include "ospf6d/ospf6_zebra.h"
#include "ospf6d/ospf6_message.h"
#include "ospf6d/ospf6_neighbor.h"
+#include "ospf6d/ospf6_network.h"
#include "ospf6d/ospf6_flood.h"
#include "ospf6d/ospf6_intra.h"
#include "ospf6d/ospf6_spf.h"
#include "ospf6d/ospf6_gr.h"
#include "ospf6d/ospf6_gr_clippy.c"
-static void ospf6_gr_nvm_delete(struct ospf6 *ospf6);
+static void ospf6_gr_grace_period_expired(struct event *thread);
/* Originate and install Grace-LSA for a given interface. */
-static int ospf6_gr_lsa_originate(struct ospf6_interface *oi)
+static int ospf6_gr_lsa_originate(struct ospf6_interface *oi,
+ enum ospf6_gr_restart_reason reason)
{
- struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info;
+ struct ospf6 *ospf6 = oi->area->ospf6;
+ struct ospf6_gr_info *gr_info = &ospf6->gr_info;
struct ospf6_lsa_header *lsa_header;
struct ospf6_grace_lsa *grace_lsa;
struct ospf6_lsa *lsa;
+ uint16_t lsa_length;
char buffer[OSPF6_MAX_LSASIZE];
if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
@@ -61,29 +66,59 @@ static int ospf6_gr_lsa_originate(struct ospf6_interface *oi)
/* Put restart reason. */
grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE);
grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
- if (gr_info->restart_support)
- grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART;
- else
- grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART;
+ grace_lsa->tlv_reason.reason = reason;
/* Fill LSA Header */
+ lsa_length = sizeof(*lsa_header) + sizeof(*grace_lsa);
lsa_header->age = 0;
lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA);
lsa_header->id = htonl(oi->interface->ifindex);
- lsa_header->adv_router = oi->area->ospf6->router_id;
+ lsa_header->adv_router = ospf6->router_id;
lsa_header->seqnum =
ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
lsa_header->adv_router, oi->lsdb);
- lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa));
+ lsa_header->length = htons(lsa_length);
/* LSA checksum */
ospf6_lsa_checksum(lsa_header);
- /* create LSA */
- lsa = ospf6_lsa_create(lsa_header);
-
- /* Originate */
- ospf6_lsa_originate_interface(lsa, oi);
+ if (reason == OSPF6_GR_UNKNOWN_RESTART) {
+ struct ospf6_header *oh;
+ uint32_t *uv32;
+ int n;
+ uint16_t length = OSPF6_HEADER_SIZE + 4 + lsa_length;
+ struct iovec iovector[2] = {};
+
+ /* Reserve space for OSPFv3 header. */
+ memmove(&buffer[OSPF6_HEADER_SIZE + 4], buffer, lsa_length);
+
+ /* Fill in the OSPFv3 header. */
+ oh = (struct ospf6_header *)buffer;
+ oh->version = OSPFV3_VERSION;
+ oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
+ oh->router_id = oi->area->ospf6->router_id;
+ oh->area_id = oi->area->area_id;
+ oh->instance_id = oi->instance_id;
+ oh->reserved = 0;
+ oh->length = htons(length);
+
+ /* Fill LSA header. */
+ uv32 = (uint32_t *)&buffer[sizeof(*oh)];
+ *uv32 = htonl(1);
+
+ /* Send packet. */
+ iovector[0].iov_base = lsa_header;
+ iovector[0].iov_len = length;
+ n = ospf6_sendmsg(oi->linklocal_addr, &allspfrouters6,
+ oi->interface->ifindex, iovector, ospf6->fd);
+ if (n != length)
+ flog_err(EC_LIB_DEVELOPMENT,
+ "%s: could not send entire message", __func__);
+ } else {
+ /* Create and install LSA. */
+ lsa = ospf6_lsa_create(lsa_header);
+ ospf6_lsa_originate_interface(lsa, oi);
+ }
return 0;
}
@@ -134,11 +169,10 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
ospf6->gr_info.restart_in_progress = false;
ospf6->gr_info.finishing_restart = true;
+ XFREE(MTYPE_TMP, ospf6->gr_info.exit_reason);
+ ospf6->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason);
EVENT_OFF(ospf6->gr_info.t_grace_period);
- /* Record in non-volatile memory that the restart is complete. */
- ospf6_gr_nvm_delete(ospf6);
-
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) {
struct ospf6_interface *oi;
@@ -156,6 +190,14 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(area);
for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) {
+ /* Disable hello delay. */
+ if (oi->gr.hello_delay.t_grace_send) {
+ oi->gr.hello_delay.elapsed_seconds = 0;
+ EVENT_OFF(oi->gr.hello_delay.t_grace_send);
+ event_add_event(master, ospf6_hello_send, oi, 0,
+ &oi->thread_send_hello);
+ }
+
/* Reoriginate Link-LSA. */
if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
OSPF6_LINK_LSA_EXECUTE(oi);
@@ -195,6 +237,26 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
ospf6_gr_flush_grace_lsas(ospf6);
}
+/* Enter the Graceful Restart mode. */
+void ospf6_gr_restart_enter(struct ospf6 *ospf6,
+ enum ospf6_gr_restart_reason reason, int timestamp)
+{
+ unsigned long remaining_time;
+
+ ospf6->gr_info.restart_in_progress = true;
+ ospf6->gr_info.reason = reason;
+
+ /* Schedule grace period timeout. */
+ remaining_time = timestamp - time(NULL);
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: remaining time until grace period expires: %lu(s)",
+ remaining_time);
+
+ event_add_timer(master, ospf6_gr_grace_period_expired, ospf6,
+ remaining_time, &ospf6->gr_info.t_grace_period);
+}
+
#define RTR_LSA_MISSING 0
#define RTR_LSA_ADJ_FOUND 1
#define RTR_LSA_ADJ_NOT_FOUND 2
@@ -466,11 +528,26 @@ static void ospf6_gr_grace_period_expired(struct event *thread)
ospf6_gr_restart_exit(ospf6, "grace period has expired");
}
+/* Send extra Grace-LSA out the interface (unplanned outages only). */
+void ospf6_gr_iface_send_grace_lsa(struct event *thread)
+{
+ struct ospf6_interface *oi = EVENT_ARG(thread);
+
+ ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason);
+
+ if (++oi->gr.hello_delay.elapsed_seconds < oi->gr.hello_delay.interval)
+ event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1,
+ &oi->gr.hello_delay.t_grace_send);
+ else
+ event_add_event(master, ospf6_hello_send, oi, 0,
+ &oi->thread_send_hello);
+}
+
/*
* Record in non-volatile memory that the given OSPF instance is attempting to
* perform a graceful restart.
*/
-static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
+static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare)
{
const char *inst_name;
json_object *json;
@@ -496,16 +573,18 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
json_instance);
}
+ json_object_int_add(json_instance, "gracePeriod",
+ ospf6->gr_info.grace_period);
+
/*
* Record not only the grace period, but also a UNIX timestamp
* corresponding to the end of that period. That way, once ospf6d is
* restarted, it will be possible to take into account the time that
* passed while ospf6d wasn't running.
*/
- json_object_int_add(json_instance, "gracePeriod",
- ospf6->gr_info.grace_period);
- json_object_int_add(json_instance, "timestamp",
- time(NULL) + ospf6->gr_info.grace_period);
+ if (prepare)
+ json_object_int_add(json_instance, "timestamp",
+ time(NULL) + ospf6->gr_info.grace_period);
json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
JSON_C_TO_STRING_PRETTY);
@@ -516,7 +595,7 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
* Delete GR status information about the given OSPF instance from non-volatile
* memory.
*/
-static void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
+void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
{
const char *inst_name;
json_object *json;
@@ -552,6 +631,7 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
json_object *json_instances;
json_object *json_instance;
json_object *json_timestamp;
+ json_object *json_grace_period;
time_t timestamp = 0;
inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
@@ -573,29 +653,33 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
json_instance);
}
+ json_object_object_get_ex(json_instance, "gracePeriod",
+ &json_grace_period);
json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
if (json_timestamp) {
time_t now;
- unsigned long remaining_time;
- /* Check if the grace period has already expired. */
+ /* Planned GR: check if the grace period has already expired. */
now = time(NULL);
timestamp = json_object_get_int(json_timestamp);
if (now > timestamp) {
ospf6_gr_restart_exit(
ospf6, "grace period has expired already");
- } else {
- /* Schedule grace period timeout. */
- ospf6->gr_info.restart_in_progress = true;
- remaining_time = timestamp - time(NULL);
- if (IS_DEBUG_OSPF6_GR)
- zlog_debug(
- "GR: remaining time until grace period expires: %lu(s)",
- remaining_time);
- event_add_timer(master, ospf6_gr_grace_period_expired,
- ospf6, remaining_time,
- &ospf6->gr_info.t_grace_period);
- }
+ } else
+ ospf6_gr_restart_enter(ospf6, OSPF6_GR_SW_RESTART,
+ timestamp);
+ } else if (json_grace_period) {
+ uint32_t grace_period;
+
+ /*
+ * Unplanned GR: the Grace-LSAs will be sent later as soon as
+ * the interfaces are operational.
+ */
+ grace_period = json_object_get_int(json_grace_period);
+ ospf6->gr_info.grace_period = grace_period;
+ ospf6_gr_restart_enter(ospf6, OSPF6_GR_UNKNOWN_RESTART,
+ time(NULL) +
+ ospf6->gr_info.grace_period);
}
json_object_object_del(json_instances, inst_name);
@@ -605,6 +689,24 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
json_object_free(json);
}
+void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi)
+{
+ /*
+ * Can't check OSPF interface state as the OSPF instance might not be
+ * enabled yet.
+ */
+ if (!if_is_operative(oi->interface) || if_is_loopback(oi->interface))
+ return;
+
+ /* Send Grace-LSA. */
+ ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason);
+
+ /* Start GR hello-delay interval. */
+ oi->gr.hello_delay.elapsed_seconds = 0;
+ event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1,
+ &oi->gr.hello_delay.t_grace_send);
+}
+
/* Prepare to start a Graceful Restart. */
static void ospf6_gr_prepare(void)
{
@@ -625,25 +727,17 @@ static void ospf6_gr_prepare(void)
ospf6->gr_info.grace_period,
ospf6_vrf_id_to_name(ospf6->vrf_id));
- /* Freeze OSPF routes in the RIB. */
- if (ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period)) {
- zlog_warn(
- "%s: failed to activate graceful restart: not connected to zebra",
- __func__);
- continue;
- }
-
/* Send a Grace-LSA to all neighbors. */
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
if (oi->state < OSPF6_INTERFACE_POINTTOPOINT)
continue;
- ospf6_gr_lsa_originate(oi);
+ ospf6_gr_lsa_originate(oi, OSPF6_GR_SW_RESTART);
}
}
/* Record end of the grace period in non-volatile memory. */
- ospf6_gr_nvm_update(ospf6);
+ ospf6_gr_nvm_update(ospf6, true);
/*
* Mark that a Graceful Restart preparation is in progress, to
@@ -714,6 +808,12 @@ DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd,
ospf6->gr_info.restart_support = true;
ospf6->gr_info.grace_period = grace_period;
+ /* Freeze OSPF routes in the RIB. */
+ (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period);
+
+ /* Record that GR is enabled in non-volatile memory. */
+ ospf6_gr_nvm_update(ospf6, false);
+
return CMD_SUCCESS;
}
@@ -736,6 +836,8 @@ DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd,
ospf6->gr_info.restart_support = false;
ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL;
+ ospf6_gr_nvm_delete(ospf6);
+ ospf6_zebra_gr_disable(ospf6);
return CMD_SUCCESS;
}
diff --git a/ospf6d/ospf6_gr.h b/ospf6d/ospf6_gr.h
index 2c7e8b341..e6566a609 100644
--- a/ospf6d/ospf6_gr.h
+++ b/ospf6d/ospf6_gr.h
@@ -155,9 +155,15 @@ extern int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6);
extern int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6);
extern int config_write_ospf6_debug_gr_helper(struct vty *vty);
+extern void ospf6_gr_iface_send_grace_lsa(struct event *thread);
+extern void ospf6_gr_restart_enter(struct ospf6 *ospf6,
+ enum ospf6_gr_restart_reason reason,
+ int timestamp);
extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf,
struct ospf6_area *area);
extern void ospf6_gr_nvm_read(struct ospf6 *ospf);
+extern void ospf6_gr_nvm_delete(struct ospf6 *ospf6);
+extern void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi);
extern void ospf6_gr_init(void);
#endif /* OSPF6_GR_H */
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index e7148d66b..ea059c4be 100644
--- a/ospf6d/ospf6_interface.c
+++ b/ospf6d/ospf6_interface.c
@@ -35,6 +35,7 @@
#include "ospf6_proto.h"
#include "lib/keychain.h"
#include "ospf6_auth_trailer.h"
+#include "ospf6d/ospf6_interface_clippy.c"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_IF, "OSPF6 interface");
DEFINE_MTYPE(OSPF6D, OSPF6_AUTH_KEYCHAIN, "OSPF6 auth keychain");
@@ -202,6 +203,7 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp)
oi->priority = OSPF6_INTERFACE_PRIORITY;
oi->hello_interval = OSPF_HELLO_INTERVAL_DEFAULT;
+ oi->gr.hello_delay.interval = OSPF_HELLO_DELAY_DEFAULT;
oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT;
oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT;
oi->type = ospf6_default_iftype(ifp);
@@ -324,6 +326,9 @@ void ospf6_interface_disable(struct ospf6_interface *oi)
EVENT_OFF(oi->thread_intra_prefix_lsa);
EVENT_OFF(oi->thread_as_extern_lsa);
EVENT_OFF(oi->thread_wait_timer);
+
+ oi->gr.hello_delay.elapsed_seconds = 0;
+ EVENT_OFF(oi->gr.hello_delay.t_grace_send);
}
static struct in6_addr *
@@ -772,6 +777,17 @@ void interface_up(struct event *thread)
return;
}
+ /*
+ * RFC 3623 - Section 5 ("Unplanned Outages"):
+ * "The grace-LSAs are encapsulated in Link State Update Packets
+ * and sent out to all interfaces, even though the restarted
+ * router has no adjacencies and no knowledge of previous
+ * adjacencies".
+ */
+ if (oi->area->ospf6->gr_info.restart_in_progress &&
+ oi->area->ospf6->gr_info.reason == OSPF6_GR_UNKNOWN_RESTART)
+ ospf6_gr_unplanned_start_interface(oi);
+
#ifdef __FreeBSD__
/*
* There's a delay in FreeBSD between issuing a command to leave a
@@ -1180,6 +1196,9 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
json_arr, json_object_new_string(lsa->name));
json_object_object_add(json_obj, "pendingLsaLsAck", json_arr);
+ if (oi->gr.hello_delay.interval != 0)
+ json_object_int_add(json_obj, "grHelloDelaySecs",
+ oi->gr.hello_delay.interval);
} else {
timerclear(&res);
if (event_is_scheduled(oi->thread_send_lsupdate))
@@ -1205,6 +1224,10 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp,
: "off"));
for (ALL_LSDB(oi->lsack_list, lsa, lsanext))
vty_out(vty, " %s\n", lsa->name);
+
+ if (oi->gr.hello_delay.interval != 0)
+ vty_out(vty, " Graceful Restart hello delay: %us\n",
+ oi->gr.hello_delay.interval);
}
/* BFD specific. */
@@ -2157,6 +2180,50 @@ ALIAS (ipv6_ospf6_deadinterval,
"Interval time after which a neighbor is declared down\n"
SECONDS_STR)
+DEFPY(ipv6_ospf6_gr_hdelay, ipv6_ospf6_gr_hdelay_cmd,
+ "ipv6 ospf6 graceful-restart hello-delay (1-1800)",
+ IP6_STR
+ OSPF6_STR
+ "Graceful Restart parameters\n"
+ "Delay the sending of the first hello packets.\n"
+ "Delay in seconds\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct ospf6_interface *oi;
+
+ oi = ifp->info;
+ if (oi == NULL)
+ oi = ospf6_interface_create(ifp);
+
+ /* Note: new or updated value won't affect ongoing graceful restart. */
+ oi->gr.hello_delay.interval = hello_delay;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_ipv6_ospf6_gr_hdelay, no_ipv6_ospf6_gr_hdelay_cmd,
+ "no ipv6 ospf6 graceful-restart hello-delay [(1-1800)]",
+ NO_STR
+ IP6_STR
+ OSPF6_STR
+ "Graceful Restart parameters\n"
+ "Delay the sending of the first hello packets.\n"
+ "Delay in seconds\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct ospf6_interface *oi;
+
+ oi = ifp->info;
+ if (oi == NULL)
+ oi = ospf6_interface_create(ifp);
+
+ oi->gr.hello_delay.interval = OSPF_HELLO_DELAY_DEFAULT;
+ oi->gr.hello_delay.elapsed_seconds = 0;
+ EVENT_OFF(oi->gr.hello_delay.t_grace_send);
+
+ return CMD_SUCCESS;
+}
+
/* interface variable set command */
DEFUN (ipv6_ospf6_transmitdelay,
ipv6_ospf6_transmitdelay_cmd,
@@ -2624,6 +2691,11 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf)
else if (oi->type_cfg && oi->type == OSPF_IFTYPE_BROADCAST)
vty_out(vty, " ipv6 ospf6 network broadcast\n");
+ if (oi->gr.hello_delay.interval != OSPF_HELLO_DELAY_DEFAULT)
+ vty_out(vty,
+ " ipv6 ospf6 graceful-restart hello-delay %u\n",
+ oi->gr.hello_delay.interval);
+
ospf6_bfd_write_config(vty, oi);
ospf6_auth_write_config(vty, &oi->at_data);
@@ -2722,12 +2794,14 @@ void ospf6_interface_init(void)
install_element(INTERFACE_NODE, &ipv6_ospf6_deadinterval_cmd);
install_element(INTERFACE_NODE, &ipv6_ospf6_hellointerval_cmd);
+ install_element(INTERFACE_NODE, &ipv6_ospf6_gr_hdelay_cmd);
install_element(INTERFACE_NODE, &ipv6_ospf6_priority_cmd);
install_element(INTERFACE_NODE, &ipv6_ospf6_retransmitinterval_cmd);
install_element(INTERFACE_NODE, &ipv6_ospf6_transmitdelay_cmd);
install_element(INTERFACE_NODE, &ipv6_ospf6_instance_cmd);
install_element(INTERFACE_NODE, &no_ipv6_ospf6_deadinterval_cmd);
install_element(INTERFACE_NODE, &no_ipv6_ospf6_hellointerval_cmd);
+ install_element(INTERFACE_NODE, &no_ipv6_ospf6_gr_hdelay_cmd);
install_element(INTERFACE_NODE, &no_ipv6_ospf6_priority_cmd);
install_element(INTERFACE_NODE, &no_ipv6_ospf6_retransmitinterval_cmd);
install_element(INTERFACE_NODE, &no_ipv6_ospf6_transmitdelay_cmd);
diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h
index ae0744b25..5942df0ab 100644
--- a/ospf6d/ospf6_interface.h
+++ b/ospf6d/ospf6_interface.h
@@ -74,6 +74,15 @@ struct ospf6_interface {
uint16_t dead_interval;
uint32_t rxmt_interval;
+ /* Graceful-Restart data. */
+ struct {
+ struct {
+ uint16_t interval;
+ uint16_t elapsed_seconds;
+ struct event *t_grace_send;
+ } hello_delay;
+ } gr;
+
uint32_t state_change;
/* Cost */
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
index 14b02dac7..032988a91 100644
--- a/ospf6d/ospf6_message.c
+++ b/ospf6d/ospf6_message.c
@@ -2244,6 +2244,10 @@ void ospf6_hello_send(struct event *thread)
oi = (struct ospf6_interface *)EVENT_ARG(thread);
+ /* Check if the GR hello-delay is active. */
+ if (oi->gr.hello_delay.t_grace_send)
+ return;
+
if (oi->state <= OSPF6_INTERFACE_DOWN) {
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND_HDR))
zlog_debug("Unable to send Hello on down interface %s",
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index 0990b1430..e39ae504a 100644
--- a/ospf6d/ospf6_spf.c
+++ b/ospf6d/ospf6_spf.c
@@ -1262,6 +1262,7 @@ static void ospf6_ase_calculate_timer(struct event *t)
* no longer valid.
*/
ospf6_zebra_gr_disable(ospf6);
+ ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period);
ospf6->gr_info.finishing_restart = false;
}
}
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index c2aa3abee..01c962194 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -455,6 +455,13 @@ struct ospf6 *ospf6_instance_create(const char *name)
if (ospf6->router_id == 0)
ospf6_router_id_update(ospf6, true);
ospf6_add(ospf6);
+
+ /*
+ * Read from non-volatile memory whether this instance is performing a
+ * graceful restart or not.
+ */
+ ospf6_gr_nvm_read(ospf6);
+
if (ospf6->vrf_id != VRF_UNKNOWN) {
vrf = vrf_lookup_by_id(ospf6->vrf_id);
FOR_ALL_INTERFACES (vrf, ifp) {
@@ -465,12 +472,6 @@ struct ospf6 *ospf6_instance_create(const char *name)
if (ospf6->fd < 0)
return ospf6;
- /*
- * Read from non-volatile memory whether this instance is performing a
- * graceful restart or not.
- */
- ospf6_gr_nvm_read(ospf6);
-
event_add_read(master, ospf6_receive, ospf6, ospf6->fd,
&ospf6->t_ospf6_receive);
@@ -490,6 +491,7 @@ void ospf6_delete(struct ospf6 *o)
ospf6_gr_helper_deinit(o);
if (!o->gr_info.prepare_in_progress)
ospf6_flush_self_originated_lsas_now(o);
+ XFREE(MTYPE_TMP, o->gr_info.exit_reason);
ospf6_disable(o);
ospf6_del(o);
@@ -693,6 +695,9 @@ DEFUN(no_router_ospf6, no_router_ospf6_cmd, "no router ospf6 [vrf NAME]",
if (ospf6 == NULL)
vty_out(vty, "OSPFv3 is not configured\n");
else {
+ if (ospf6->gr_info.restart_support)
+ ospf6_gr_nvm_delete(ospf6);
+
ospf6_delete(ospf6);
ospf6 = NULL;
}
diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h
index 8fdd29112..a38dad8fc 100644
--- a/ospf6d/ospf6_top.h
+++ b/ospf6d/ospf6_top.h
@@ -51,6 +51,8 @@ struct ospf6_gr_info {
bool prepare_in_progress;
bool finishing_restart;
uint32_t grace_period;
+ int reason;
+ char *exit_reason;
struct event *t_grace_period;
};
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index 6b3d4955d..0f631c4d0 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -239,12 +239,18 @@ static int ospf6_zebra_gr_update(struct ospf6 *ospf6, int command,
int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time)
{
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug("Zebra enable GR [stale time %u]", stale_time);
+
return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES,
stale_time);
}
int ospf6_zebra_gr_disable(struct ospf6 *ospf6)
{
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug("Zebra disable GR");
+
return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0);
}
@@ -735,10 +741,20 @@ uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or,
static void ospf6_zebra_connected(struct zclient *zclient)
{
+ struct ospf6 *ospf6;
+ struct listnode *node;
+
/* Send the client registration */
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
zclient_send_reg_requests(zclient, VRF_DEFAULT);
+
+ /* Activate graceful restart if configured. */
+ for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) {
+ if (!ospf6->gr_info.restart_support)
+ continue;
+ (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period);
+ }
}
static zclient_handler *const ospf6_handlers[] = {
diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am
index 3dff03956..c34db3012 100644
--- a/ospf6d/subdir.am
+++ b/ospf6d/subdir.am
@@ -78,6 +78,7 @@ clippy_scan += \
ospf6d/ospf6_top.c \
ospf6d/ospf6_area.c \
ospf6d/ospf6_asbr.c \
+ ospf6d/ospf6_interface.c \
ospf6d/ospf6_lsa.c \
ospf6d/ospf6_gr_helper.c \
ospf6d/ospf6_gr.c \
diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c
index ded520889..28d526870 100644
--- a/ospfd/ospf_abr.c
+++ b/ospfd/ospf_abr.c
@@ -66,9 +66,11 @@ static void ospf_area_range_add(struct ospf_area *area,
apply_mask_ipv4(&p);
rn = route_node_get(ranges, (struct prefix *)&p);
- if (rn->info)
+ if (rn->info) {
route_unlock_node(rn);
- else
+ ospf_area_range_free(rn->info);
+ rn->info = range;
+ } else
rn->info = range;
}
diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c
index 1aacc341a..34e59c545 100644
--- a/ospfd/ospf_apiserver.c
+++ b/ospfd/ospf_apiserver.c
@@ -1764,6 +1764,12 @@ int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old)
* session. Dump it, but increment past it's seqnum.
*/
assert(!ospf_opaque_is_owned(old));
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug(
+ "LSA[Type%d:%pI4]: OSPF API Server Originate LSA Old Seq: 0x%x Age: %d",
+ old->data->type, &old->data->id,
+ ntohl(old->data->ls_seqnum),
+ ntohl(old->data->ls_age));
if (IS_LSA_MAX_SEQ(old)) {
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
"%s: old LSA at maxseq", __func__);
@@ -1772,6 +1778,11 @@ int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old)
lsa->data->ls_seqnum = lsa_seqnum_increment(old);
ospf_discard_from_db(ospf, old->lsdb, old);
}
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug(
+ "LSA[Type%d:%pI4]: OSPF API Server Originate LSA New Seq: 0x%x Age: %d",
+ lsa->data->type, &lsa->data->id,
+ ntohl(lsa->data->ls_seqnum), ntohl(lsa->data->ls_age));
/* Install this LSA into LSDB. */
if (ospf_lsa_install(ospf, lsa->oi, lsa) == NULL) {
@@ -1846,6 +1857,11 @@ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa)
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
assert(ospf);
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+ zlog_debug("LSA[Type%d:%pI4]: OSPF API Server LSA Refresher",
+ lsa->data->type, &lsa->data->id);
+ }
+
apiserv = lookup_apiserver_by_lsa(lsa);
if (!apiserv) {
zlog_warn("%s: LSA[%s]: No apiserver?", __func__,
@@ -1855,14 +1871,14 @@ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa)
goto out;
}
- if (IS_LSA_MAXAGE(lsa)) {
- ospf_opaque_lsa_flush_schedule(lsa);
- goto out;
- }
-
/* Check if updated version of LSA instance has already prepared. */
new = ospf_lsdb_lookup(&apiserv->reserve, lsa);
if (!new) {
+ if (IS_LSA_MAXAGE(lsa)) {
+ ospf_opaque_lsa_flush_schedule(lsa);
+ goto out;
+ }
+
/* This is a periodic refresh, driven by core OSPF mechanism. */
new = ospf_apiserver_opaque_lsa_new(lsa->area, lsa->oi,
lsa->data);
diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c
index cc2110d43..610b5fc08 100644
--- a/ospfd/ospf_ase.c
+++ b/ospfd/ospf_ase.c
@@ -615,6 +615,7 @@ static void ospf_ase_calculate_timer(struct event *t)
*/
if (ospf->gr_info.finishing_restart) {
ospf_zebra_gr_disable(ospf);
+ ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period);
ospf->gr_info.finishing_restart = false;
}
}
diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c
index f60f0e863..6999b3f62 100644
--- a/ospfd/ospf_gr.c
+++ b/ospfd/ospf_gr.c
@@ -33,7 +33,7 @@
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_gr_clippy.c"
-static void ospf_gr_nvm_delete(struct ospf *ospf);
+static void ospf_gr_grace_period_expired(struct event *thread);
/* Lookup self-originated Grace-LSA in the LSDB. */
static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf,
@@ -53,7 +53,9 @@ static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf,
/* Fill in fields of the Grace-LSA that is being originated. */
static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
- struct ospf_interface *oi, struct stream *s)
+ struct ospf_interface *oi,
+ enum ospf_gr_restart_reason reason,
+ struct stream *s)
{
struct grace_tlv_graceperiod tlv_period = {};
struct grace_tlv_restart_reason tlv_reason = {};
@@ -68,10 +70,7 @@ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
/* Put restart reason. */
tlv_reason.header.type = htons(RESTART_REASON_TYPE);
tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
- if (gr_info->restart_support)
- tlv_reason.reason = OSPF_GR_SW_RESTART;
- else
- tlv_reason.reason = OSPF_GR_UNKNOWN_RESTART;
+ tlv_reason.reason = reason;
stream_put(s, &tlv_reason, sizeof(tlv_reason));
/* Put IP address. */
@@ -85,7 +84,8 @@ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
}
/* Generate Grace-LSA for a given interface. */
-static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
+static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi,
+ enum ospf_gr_restart_reason reason)
{
struct stream *s;
struct lsa_header *lsah;
@@ -112,7 +112,7 @@ static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
lsa_header_set(s, options, lsa_type, lsa_id, oi->ospf->router_id);
/* Set opaque-LSA body fields. */
- ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, s);
+ ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, reason, s);
/* Set length. */
length = stream_get_endp(s);
@@ -135,15 +135,20 @@ static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
}
/* Originate and install Grace-LSA for a given interface. */
-static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage)
+static void ospf_gr_lsa_originate(struct ospf_interface *oi,
+ enum ospf_gr_restart_reason reason,
+ bool maxage)
{
struct ospf_lsa *lsa, *old;
- if (ospf_interface_neighbor_count(oi) == 0)
+ /* Skip originating a Grace-LSA when not necessary. */
+ if (!if_is_operative(oi->ifp) || if_is_loopback(oi->ifp) ||
+ (reason != OSPF_GR_UNKNOWN_RESTART &&
+ ospf_interface_neighbor_count(oi) == 0))
return;
/* Create new Grace-LSA. */
- lsa = ospf_gr_lsa_new(oi);
+ lsa = ospf_gr_lsa_new(oi, reason);
if (!lsa) {
zlog_warn("%s: ospf_gr_lsa_new() failed", __func__);
return;
@@ -157,18 +162,36 @@ static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage)
if (old)
lsa->data->ls_seqnum = lsa_seqnum_increment(old);
- /* Install this LSA into LSDB. */
- if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) {
- zlog_warn("%s: ospf_lsa_install() failed", __func__);
- ospf_lsa_unlock(&lsa);
- return;
+ if (!maxage && reason == OSPF_GR_UNKNOWN_RESTART) {
+ struct list *update;
+ struct in_addr addr;
+
+ /*
+ * When performing an unplanned restart, send a handcrafted
+ * Grace-LSA since the interface isn't fully initialized yet.
+ */
+ ospf_lsa_checksum(lsa->data);
+ ospf_lsa_lock(lsa);
+ update = list_new();
+ listnode_add(update, lsa);
+ addr.s_addr = htonl(OSPF_ALLSPFROUTERS);
+ ospf_ls_upd_queue_send(oi, update, addr, true);
+ list_delete(&update);
+ ospf_lsa_discard(lsa);
+ } else {
+ /* Install this LSA into LSDB. */
+ if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) {
+ zlog_warn("%s: ospf_lsa_install() failed", __func__);
+ ospf_lsa_unlock(&lsa);
+ return;
+ }
+
+ /* Flood the LSA through out the interface */
+ ospf_flood_through_interface(oi, NULL, lsa);
}
/* Update new LSA origination count. */
oi->ospf->lsa_originate_count++;
-
- /* Flood the LSA through out the interface */
- ospf_flood_through_interface(oi, NULL, lsa);
}
/* Flush all self-originated Grace-LSAs. */
@@ -181,13 +204,14 @@ static void ospf_gr_flush_grace_lsas(struct ospf *ospf)
struct ospf_interface *oi;
struct listnode *inode;
- if (IS_DEBUG_OSPF_GR)
- zlog_debug(
- "GR: flushing self-originated Grace-LSAs [area %pI4]",
- &area->area_id);
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: flushing self-originated Grace-LSA [area %pI4] [interface %s]",
+ &area->area_id, oi->ifp->name);
- for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi))
- ospf_gr_lsa_originate(oi, true);
+ ospf_gr_lsa_originate(oi, ospf->gr_info.reason, true);
+ }
}
}
@@ -203,9 +227,6 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason)
ospf->gr_info.restart_in_progress = false;
EVENT_OFF(ospf->gr_info.t_grace_period);
- /* Record in non-volatile memory that the restart is complete. */
- ospf_gr_nvm_delete(ospf);
-
for (ALL_LIST_ELEMENTS_RO(ospf->areas, onode, area)) {
struct ospf_interface *oi;
@@ -216,13 +237,22 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason)
*/
ospf_router_lsa_update_area(area);
- /*
- * 2) The router should reoriginate network-LSAs on all segments
- * where it is the Designated Router.
- */
- for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi))
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi)) {
+ /* Disable hello delay. */
+ if (oi->gr.hello_delay.t_grace_send) {
+ oi->gr.hello_delay.elapsed_seconds = 0;
+ EVENT_OFF(oi->gr.hello_delay.t_grace_send);
+ OSPF_ISM_TIMER_MSEC_ON(oi->t_hello,
+ ospf_hello_timer, 1);
+ }
+
+ /*
+ * 2) The router should reoriginate network-LSAs on all
+ * segments where it is the Designated Router.
+ */
if (oi->state == ISM_DR)
ospf_network_lsa_update(oi);
+ }
}
/*
@@ -242,12 +272,34 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason)
* should be removed.
*/
ospf->gr_info.finishing_restart = true;
+ XFREE(MTYPE_TMP, ospf->gr_info.exit_reason);
+ ospf->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason);
ospf_spf_calculate_schedule(ospf, SPF_FLAG_GR_FINISH);
/* 6) Any grace-LSAs that the router originated should be flushed. */
ospf_gr_flush_grace_lsas(ospf);
}
+/* Enter the Graceful Restart mode. */
+void ospf_gr_restart_enter(struct ospf *ospf,
+ enum ospf_gr_restart_reason reason, int timestamp)
+{
+ unsigned long remaining_time;
+
+ ospf->gr_info.restart_in_progress = true;
+ ospf->gr_info.reason = reason;
+
+ /* Schedule grace period timeout. */
+ remaining_time = timestamp - time(NULL);
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: remaining time until grace period expires: %lu(s)",
+ remaining_time);
+
+ event_add_timer(master, ospf_gr_grace_period_expired, ospf,
+ remaining_time, &ospf->gr_info.t_grace_period);
+}
+
/* Check if a Router-LSA contains a given link. */
static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa,
struct in_addr *id)
@@ -518,11 +570,26 @@ static char *ospf_gr_nvm_filepath(struct ospf *ospf)
return filepath;
}
+/* Send extra Grace-LSA out the interface (unplanned outages only). */
+void ospf_gr_iface_send_grace_lsa(struct event *thread)
+{
+ struct ospf_interface *oi = EVENT_ARG(thread);
+ struct ospf_if_params *params = IF_DEF_PARAMS(oi->ifp);
+
+ ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false);
+
+ if (++oi->gr.hello_delay.elapsed_seconds < params->v_gr_hello_delay)
+ event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1,
+ &oi->gr.hello_delay.t_grace_send);
+ else
+ OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1);
+}
+
/*
* Record in non-volatile memory that the given OSPF instance is attempting to
* perform a graceful restart.
*/
-static void ospf_gr_nvm_update(struct ospf *ospf)
+static void ospf_gr_nvm_update(struct ospf *ospf, bool prepare)
{
char *filepath;
const char *inst_name;
@@ -550,16 +617,18 @@ static void ospf_gr_nvm_update(struct ospf *ospf)
json_instance);
}
+ json_object_int_add(json_instance, "gracePeriod",
+ ospf->gr_info.grace_period);
+
/*
* Record not only the grace period, but also a UNIX timestamp
* corresponding to the end of that period. That way, once ospfd is
* restarted, it will be possible to take into account the time that
* passed while ospfd wasn't running.
*/
- json_object_int_add(json_instance, "gracePeriod",
- ospf->gr_info.grace_period);
- json_object_int_add(json_instance, "timestamp",
- time(NULL) + ospf->gr_info.grace_period);
+ if (prepare)
+ json_object_int_add(json_instance, "timestamp",
+ time(NULL) + ospf->gr_info.grace_period);
json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY);
json_object_free(json);
@@ -569,7 +638,7 @@ static void ospf_gr_nvm_update(struct ospf *ospf)
* Delete GR status information about the given OSPF instance from non-volatile
* memory.
*/
-static void ospf_gr_nvm_delete(struct ospf *ospf)
+void ospf_gr_nvm_delete(struct ospf *ospf)
{
char *filepath;
const char *inst_name;
@@ -607,6 +676,7 @@ void ospf_gr_nvm_read(struct ospf *ospf)
json_object *json_instances;
json_object *json_instance;
json_object *json_timestamp;
+ json_object *json_grace_period;
time_t timestamp = 0;
filepath = ospf_gr_nvm_filepath(ospf);
@@ -629,29 +699,33 @@ void ospf_gr_nvm_read(struct ospf *ospf)
json_instance);
}
+ json_object_object_get_ex(json_instance, "gracePeriod",
+ &json_grace_period);
json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
+
if (json_timestamp) {
time_t now;
- unsigned long remaining_time;
- /* Check if the grace period has already expired. */
+ /* Planned GR: check if the grace period has already expired. */
now = time(NULL);
timestamp = json_object_get_int(json_timestamp);
if (now > timestamp) {
ospf_gr_restart_exit(
ospf, "grace period has expired already");
- } else {
- /* Schedule grace period timeout. */
- ospf->gr_info.restart_in_progress = true;
- remaining_time = timestamp - time(NULL);
- if (IS_DEBUG_OSPF_GR)
- zlog_debug(
- "GR: remaining time until grace period expires: %lu(s)",
- remaining_time);
- event_add_timer(master, ospf_gr_grace_period_expired,
- ospf, remaining_time,
- &ospf->gr_info.t_grace_period);
- }
+ } else
+ ospf_gr_restart_enter(ospf, OSPF_GR_SW_RESTART,
+ timestamp);
+ } else if (json_grace_period) {
+ uint32_t grace_period;
+
+ /*
+ * Unplanned GR: the Grace-LSAs will be sent later as soon as
+ * the interfaces are operational.
+ */
+ grace_period = json_object_get_int(json_grace_period);
+ ospf->gr_info.grace_period = grace_period;
+ ospf_gr_restart_enter(ospf, OSPF_GR_UNKNOWN_RESTART,
+ time(NULL) + ospf->gr_info.grace_period);
}
json_object_object_del(json_instances, inst_name);
@@ -660,6 +734,17 @@ void ospf_gr_nvm_read(struct ospf *ospf)
json_object_free(json);
}
+void ospf_gr_unplanned_start_interface(struct ospf_interface *oi)
+{
+ /* Send Grace-LSA. */
+ ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false);
+
+ /* Start GR hello-delay interval. */
+ oi->gr.hello_delay.elapsed_seconds = 0;
+ event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1,
+ &oi->gr.hello_delay.t_grace_send);
+}
+
/* Prepare to start a Graceful Restart. */
static void ospf_gr_prepare(void)
{
@@ -687,20 +772,12 @@ static void ospf_gr_prepare(void)
continue;
}
- /* Freeze OSPF routes in the RIB. */
- if (ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period)) {
- zlog_warn(
- "%s: failed to activate graceful restart: not connected to zebra",
- __func__);
- continue;
- }
-
/* Send a Grace-LSA to all neighbors. */
for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi))
- ospf_gr_lsa_originate(oi, false);
+ ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART, false);
/* Record end of the grace period in non-volatile memory. */
- ospf_gr_nvm_update(ospf);
+ ospf_gr_nvm_update(ospf, true);
/*
* Mark that a Graceful Restart preparation is in progress, to
@@ -749,6 +826,12 @@ DEFPY(graceful_restart, graceful_restart_cmd,
ospf->gr_info.restart_support = true;
ospf->gr_info.grace_period = grace_period;
+ /* Freeze OSPF routes in the RIB. */
+ (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period);
+
+ /* Record that GR is enabled in non-volatile memory. */
+ ospf_gr_nvm_update(ospf, false);
+
return CMD_SUCCESS;
}
@@ -771,6 +854,8 @@ DEFPY(no_graceful_restart, no_graceful_restart_cmd,
ospf->gr_info.restart_support = false;
ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL;
+ ospf_gr_nvm_delete(ospf);
+ ospf_zebra_gr_disable(ospf);
return CMD_SUCCESS;
}
diff --git a/ospfd/ospf_gr.h b/ospfd/ospf_gr.h
index 9760bb172..750d77381 100644
--- a/ospfd/ospf_gr.h
+++ b/ospfd/ospf_gr.h
@@ -166,11 +166,16 @@ extern void ospf_gr_helper_supported_gracetime_set(struct ospf *ospf,
uint32_t interval);
extern void ospf_gr_helper_set_supported_planned_only_restart(struct ospf *ospf,
bool planned_only);
-
+extern void ospf_gr_iface_send_grace_lsa(struct event *thread);
+extern void ospf_gr_restart_enter(struct ospf *ospf,
+ enum ospf_gr_restart_reason reason,
+ int timestamp);
extern void ospf_gr_check_lsdb_consistency(struct ospf *ospf,
struct ospf_area *area);
extern void ospf_gr_check_adjs(struct ospf *ospf);
extern void ospf_gr_nvm_read(struct ospf *ospf);
+extern void ospf_gr_nvm_delete(struct ospf *ospf);
+extern void ospf_gr_unplanned_start_interface(struct ospf_interface *oi);
extern void ospf_gr_init(void);
#endif /* _ZEBRA_OSPF_GR_H */
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index 5742ece1f..8da982aed 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -101,6 +101,9 @@ int ospf_if_get_output_cost(struct ospf_interface *oi)
cost = 1;
else if (cost > 65535)
cost = 65535;
+
+ if (if_is_loopback(oi->ifp))
+ cost = 0;
}
return cost;
@@ -527,6 +530,7 @@ static struct ospf_if_params *ospf_new_if_params(void)
UNSET_IF_PARAM(oip, passive_interface);
UNSET_IF_PARAM(oip, v_hello);
UNSET_IF_PARAM(oip, fast_hello);
+ UNSET_IF_PARAM(oip, v_gr_hello_delay);
UNSET_IF_PARAM(oip, v_wait);
UNSET_IF_PARAM(oip, priority);
UNSET_IF_PARAM(oip, type);
@@ -676,6 +680,9 @@ int ospf_if_new_hook(struct interface *ifp)
SET_IF_PARAM(IF_DEF_PARAMS(ifp), fast_hello);
IF_DEF_PARAMS(ifp)->fast_hello = OSPF_FAST_HELLO_DEFAULT;
+ SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_gr_hello_delay);
+ IF_DEF_PARAMS(ifp)->v_gr_hello_delay = OSPF_HELLO_DELAY_DEFAULT;
+
SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_wait);
IF_DEF_PARAMS(ifp)->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT;
diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h
index 649df437a..24768b9ab 100644
--- a/ospfd/ospf_interface.h
+++ b/ospfd/ospf_interface.h
@@ -72,6 +72,9 @@ struct ospf_if_params {
DECLARE_IF_PARAM(uint32_t, v_wait); /* Router Dead Interval */
bool is_v_wait_set; /* Check for Dead Interval set */
+ /* GR Hello Delay Interval */
+ DECLARE_IF_PARAM(uint16_t, v_gr_hello_delay);
+
/* MTU mismatch check (see RFC2328, chap 10.6) */
DECLARE_IF_PARAM(uint8_t, mtu_ignore);
@@ -214,6 +217,14 @@ struct ospf_interface {
/* List of configured NBMA neighbor. */
struct list *nbr_nbma;
+ /* Graceful-Restart data. */
+ struct {
+ struct {
+ uint16_t elapsed_seconds;
+ struct event *t_grace_send;
+ } hello_delay;
+ } gr;
+
/* self-originated LSAs. */
struct ospf_lsa *network_lsa_self; /* network-LSA. */
struct list *opaque_lsa_self; /* Type-9 Opaque-LSAs */
diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c
index 9f795ea91..2516fa75d 100644
--- a/ospfd/ospf_ism.c
+++ b/ospfd/ospf_ism.c
@@ -244,6 +244,10 @@ void ospf_hello_timer(struct event *thread)
oi = EVENT_ARG(thread);
oi->t_hello = NULL;
+ /* Check if the GR hello-delay is active. */
+ if (oi->gr.hello_delay.t_grace_send)
+ return;
+
if (IS_DEBUG_OSPF(ism, ISM_TIMERS))
zlog_debug("ISM[%s]: Timer (Hello timer expire)", IF_NAME(oi));
@@ -282,6 +286,7 @@ static void ism_timer_set(struct ospf_interface *oi)
EVENT_OFF(oi->t_hello);
EVENT_OFF(oi->t_wait);
EVENT_OFF(oi->t_ls_ack);
+ EVENT_OFF(oi->gr.hello_delay.t_grace_send);
break;
case ISM_Loopback:
/* In this state, the interface may be looped back and will be
@@ -289,6 +294,7 @@ static void ism_timer_set(struct ospf_interface *oi)
EVENT_OFF(oi->t_hello);
EVENT_OFF(oi->t_wait);
EVENT_OFF(oi->t_ls_ack);
+ EVENT_OFF(oi->gr.hello_delay.t_grace_send);
break;
case ISM_Waiting:
/* The router is trying to determine the identity of DRouter and
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index 452a3ba37..67f1faf8a 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -608,7 +608,8 @@ static int lsa_link_loopback_set(struct stream **s, struct ospf_interface *oi)
mask.s_addr = 0xffffffff;
id.s_addr = oi->address->u.prefix4.s_addr;
- return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, 0);
+ return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0,
+ oi->output_cost);
}
/* Describe Virtual Link. */
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index c2b40af1c..6894c6a00 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -2120,14 +2120,21 @@ void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr,
lsa->data->type, &lsa->data->id);
/*
- * Since these LSA entries are not yet installed into corresponding
- * LSDB, just flush them without calling ospf_ls_maxage() afterward.
+ * Install the stale LSA into the Link State Database, add it to the
+ * MaxAge list, and flush it from the OSPF routing domain. For other
+ * LSA types, the installation is done in the refresh function. It is
+ * done inline here since the opaque refresh function is dynamically
+ * registered when opaque LSAs are originated (which is not the case
+ * for stale LSAs).
*/
lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ ospf_lsa_install(
+ top, (lsa->data->type == OSPF_OPAQUE_LINK_LSA) ? nbr->oi : NULL,
+ lsa);
+ ospf_lsa_maxage(top, lsa);
+
switch (lsa->data->type) {
case OSPF_OPAQUE_LINK_LSA:
- ospf_flood_through_area(nbr->oi->area, NULL /*inbr*/, lsa);
- break;
case OSPF_OPAQUE_AREA_LSA:
ospf_flood_through_area(nbr->oi->area, NULL /*inbr*/, lsa);
break;
@@ -2139,7 +2146,6 @@ void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr,
__func__, lsa->data->type);
return;
}
- ospf_lsa_discard(lsa); /* List "lsas" will be deleted by caller. */
}
/*------------------------------------------------------------------------*
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 552acfd6d..d010b8b6e 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -2031,7 +2031,7 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
if (current == NULL) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
- "LSA[%s]: Previously originated Opaque-LSA,not found in the LSDB.",
+ "LSA[%s]: Previously originated Opaque-LSA, not found in the LSDB.",
dump_lsa_key(lsa));
SET_FLAG(lsa->flags, OSPF_LSA_SELF);
@@ -4040,9 +4040,8 @@ static struct ospf_packet *ospf_ls_upd_packet_new(struct list *update,
return ospf_packet_new(size - sizeof(struct ip));
}
-static void ospf_ls_upd_queue_send(struct ospf_interface *oi,
- struct list *update, struct in_addr addr,
- int send_lsupd_now)
+void ospf_ls_upd_queue_send(struct ospf_interface *oi, struct list *update,
+ struct in_addr addr, int send_lsupd_now)
{
struct ospf_packet *op;
uint16_t length = OSPF_HEADER_SIZE;
diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h
index 4003e2add..234738979 100644
--- a/ospfd/ospf_packet.h
+++ b/ospfd/ospf_packet.h
@@ -132,6 +132,9 @@ extern void ospf_ls_req_send(struct ospf_neighbor *);
extern void ospf_ls_upd_send_lsa(struct ospf_neighbor *, struct ospf_lsa *,
int);
extern void ospf_ls_upd_send(struct ospf_neighbor *, struct list *, int, int);
+extern void ospf_ls_upd_queue_send(struct ospf_interface *oi,
+ struct list *update, struct in_addr addr,
+ int send_lsupd_now);
extern void ospf_ls_ack_send(struct ospf_neighbor *, struct ospf_lsa *);
extern void ospf_ls_ack_send_delayed(struct ospf_interface *);
extern void ospf_ls_retransmit(struct ospf_interface *, struct ospf_lsa *);
diff --git a/ospfd/ospf_ti_lfa.c b/ospfd/ospf_ti_lfa.c
index da9428aba..07fc50394 100644
--- a/ospfd/ospf_ti_lfa.c
+++ b/ospfd/ospf_ti_lfa.c
@@ -1078,6 +1078,7 @@ void ospf_ti_lfa_free_p_spaces(struct ospf_area *area)
q_spaces_fini(p_space->q_spaces);
XFREE(MTYPE_OSPF_Q_SPACE, p_space->q_spaces);
+ XFREE(MTYPE_OSPF_P_SPACE, p_space);
}
p_spaces_fini(area->p_spaces);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 09dd0081e..f92be3fca 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -232,9 +232,12 @@ DEFUN (no_router_ospf,
return CMD_NOT_MY_INSTANCE;
ospf = ospf_lookup(instance, vrf_name);
- if (ospf)
+ if (ospf) {
+ if (ospf->gr_info.restart_support)
+ ospf_gr_nvm_delete(ospf);
+
ospf_finish(ospf);
- else
+ } else
ret = CMD_WARNING_CONFIG_FAILED;
return ret;
@@ -3617,6 +3620,7 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
struct ospf_neighbor *nbr;
struct route_node *rn;
uint32_t bandwidth = ifp->bandwidth ? ifp->bandwidth : ifp->speed;
+ struct ospf_if_params *params;
/* Is interface up? */
if (use_json) {
@@ -3936,6 +3940,20 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
ospf_nbr_count(oi, 0),
ospf_nbr_count(oi, NSM_Full));
+
+ params = IF_DEF_PARAMS(ifp);
+ if (params &&
+ OSPF_IF_PARAM_CONFIGURED(params, v_gr_hello_delay)) {
+ if (use_json) {
+ json_object_int_add(json_interface_sub,
+ "grHelloDelaySecs",
+ params->v_gr_hello_delay);
+ } else
+ vty_out(vty,
+ " Graceful Restart hello delay: %us\n",
+ params->v_gr_hello_delay);
+ }
+
ospf_interface_bfd_show(vty, ifp, json_interface_sub);
/* OSPF Authentication information */
@@ -3996,16 +4014,15 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf,
/* Interface name is specified. */
ifp = if_lookup_by_name(intf_name, ospf->vrf_id);
if (ifp == NULL) {
- if (use_json)
+ if (use_json) {
json_object_boolean_true_add(json_vrf,
"noSuchIface");
- else
+ json_object_free(json_interface);
+ } else
vty_out(vty, "No such interface name\n");
} else {
- if (use_json) {
+ if (use_json)
json_interface_sub = json_object_new_object();
- json_interface = json_object_new_object();
- }
show_ip_ospf_interface_sub(
vty, ospf, ifp, json_interface_sub, use_json);
@@ -8640,6 +8657,59 @@ DEFUN_HIDDEN (no_ospf_retransmit_interval,
return no_ip_ospf_retransmit_interval(self, vty, argc, argv);
}
+DEFPY (ip_ospf_gr_hdelay,
+ ip_ospf_gr_hdelay_cmd,
+ "ip ospf graceful-restart hello-delay (1-1800)",
+ IP_STR
+ "OSPF interface commands\n"
+ "Graceful Restart parameters\n"
+ "Delay the sending of the first hello packets.\n"
+ "Delay in seconds\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct ospf_if_params *params;
+
+ params = IF_DEF_PARAMS(ifp);
+
+ /* Note: new or updated value won't affect ongoing graceful restart. */
+ SET_IF_PARAM(params, v_gr_hello_delay);
+ params->v_gr_hello_delay = hello_delay;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_ip_ospf_gr_hdelay,
+ no_ip_ospf_gr_hdelay_cmd,
+ "no ip ospf graceful-restart hello-delay [(1-1800)]",
+ NO_STR
+ IP_STR
+ "OSPF interface commands\n"
+ "Graceful Restart parameters\n"
+ "Delay the sending of the first hello packets.\n"
+ "Delay in seconds\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct ospf_if_params *params;
+ struct route_node *rn;
+
+ params = IF_DEF_PARAMS(ifp);
+ UNSET_IF_PARAM(params, v_gr_hello_delay);
+ params->v_gr_hello_delay = OSPF_HELLO_DELAY_DEFAULT;
+
+ for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
+ struct ospf_interface *oi;
+
+ oi = rn->info;
+ if (!oi)
+ continue;
+
+ oi->gr.hello_delay.elapsed_seconds = 0;
+ EVENT_OFF(oi->gr.hello_delay.t_grace_send);
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (ip_ospf_transmit_delay,
ip_ospf_transmit_delay_addr_cmd,
"ip ospf transmit-delay (1-65535) [A.B.C.D]",
@@ -11832,6 +11902,15 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
vty_out(vty, "\n");
}
+ /* Hello Graceful-Restart Delay print. */
+ if (OSPF_IF_PARAM_CONFIGURED(params,
+ v_gr_hello_delay) &&
+ params->v_gr_hello_delay !=
+ OSPF_HELLO_DELAY_DEFAULT)
+ vty_out(vty,
+ " ip ospf graceful-restart hello-delay %u\n",
+ params->v_gr_hello_delay);
+
/* Router Priority print. */
if (OSPF_IF_PARAM_CONFIGURED(params, priority)
&& params->priority
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 0b770a836..27d74cd4f 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -1252,12 +1252,18 @@ static int ospf_zebra_gr_update(struct ospf *ospf, int command,
int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time)
{
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug("Zebra enable GR [stale time %u]", stale_time);
+
return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_CAPABILITIES,
stale_time);
}
int ospf_zebra_gr_disable(struct ospf *ospf)
{
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug("Zebra disable GR");
+
return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_DISABLE, 0);
}
@@ -2120,10 +2126,20 @@ int ospf_zebra_label_manager_connect(void)
static void ospf_zebra_connected(struct zclient *zclient)
{
+ struct ospf *ospf;
+ struct listnode *node;
+
/* Send the client registration */
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
zclient_send_reg_requests(zclient, VRF_DEFAULT);
+
+ /* Activate graceful restart if configured. */
+ for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) {
+ if (!ospf->gr_info.restart_support)
+ continue;
+ (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period);
+ }
}
/*
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index 7e83714c0..eae1f301a 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -720,6 +720,7 @@ static void ospf_finish_final(struct ospf *ospf)
if (!ospf->gr_info.prepare_in_progress)
ospf_flush_self_originated_lsas_now(ospf);
+ XFREE(MTYPE_TMP, ospf->gr_info.exit_reason);
/* Unregister redistribution */
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
@@ -1131,6 +1132,17 @@ struct ospf_interface *add_ospf_interface(struct connected *co,
&& if_is_operative(co->ifp))
ospf_if_up(oi);
+ /*
+ * RFC 3623 - Section 5 ("Unplanned Outages"):
+ * "The grace-LSAs are encapsulated in Link State Update Packets
+ * and sent out to all interfaces, even though the restarted
+ * router has no adjacencies and no knowledge of previous
+ * adjacencies".
+ */
+ if (oi->ospf->gr_info.restart_in_progress &&
+ oi->ospf->gr_info.reason == OSPF_GR_UNKNOWN_RESTART)
+ ospf_gr_unplanned_start_interface(oi);
+
return oi;
}
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 1f8d1a32e..36936b16f 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -148,6 +148,8 @@ struct ospf_gr_info {
bool prepare_in_progress;
bool finishing_restart;
uint32_t grace_period;
+ int reason;
+ char *exit_reason;
struct event *t_grace_period;
};
diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c
index 5eedfb7dc..aa39be63d 100644
--- a/pimd/pim6_mld.c
+++ b/pimd/pim6_mld.c
@@ -344,7 +344,8 @@ static const char *const gm_states[] = {
};
/* clang-format on */
-CPP_NOTICE("TODO: S,G entries in EXCLUDE (i.e. prune) unsupported");
+/* TODO: S,G entries in EXCLUDE (i.e. prune) unsupported" */
+
/* tib_sg_gm_prune() below is an "un-join", it doesn't prune S,G when *,G is
* joined. Whether we actually want/need to support this is a separate
* question - it is almost never used. In fact this is exactly what RFC5790
@@ -645,7 +646,7 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt,
*/
gm_packet_sg_drop(old_grp);
gm_sg_update(grp, false);
- CPP_NOTICE("need S,G PRUNE => NO_INFO transition here");
+/* TODO "need S,G PRUNE => NO_INFO transition here" */
}
break;
@@ -793,7 +794,8 @@ static void gm_handle_v2_pass2_excl(struct gm_packet_state *pkt, size_t offs)
gm_sg_update(sg_grp, false);
}
-CPP_NOTICE("TODO: QRV/QQIC are not copied from queries to local state");
+/* TODO: QRV/QQIC are not copied from queries to local state" */
+
/* on receiving a query, we need to update our robustness/query interval to
* match, so we correctly process group/source specific queries after last
* member leaves
@@ -949,7 +951,8 @@ static void gm_handle_v1_report(struct gm_if *gm_ifp,
item = gm_packet_sg_setup(pkt, grp, true, false);
item->n_exclude = 0;
- CPP_NOTICE("set v1-seen timer on grp here");
+
+/* TODO "set v1-seen timer on grp here" */
/* } */
@@ -1012,7 +1015,9 @@ static void gm_handle_v1_leave(struct gm_if *gm_ifp,
if (old_grp) {
gm_packet_sg_drop(old_grp);
gm_sg_update(grp, false);
- CPP_NOTICE("need S,G PRUNE => NO_INFO transition here");
+
+/* TODO "need S,G PRUNE => NO_INFO transition here" */
+
}
}
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index b053e422e..b1beb4563 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -979,13 +979,13 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term)
}
if (ifp->ifindex < 0) {
- zlog_warn("%s: ifindex=%d < 1 on interface %s", __func__,
+ zlog_warn("%s: ifindex=%d < 0 on interface %s", __func__,
ifp->ifindex, ifp->name);
return -2;
- } else if ((ifp->ifindex == 0) &&
+ } else if ((ifp->ifindex == PIM_OIF_PIM_REGISTER_VIF) &&
((strncmp(ifp->name, "pimreg", 6)) &&
(strncmp(ifp->name, "pim6reg", 7)))) {
- zlog_warn("%s: ifindex=%d == 0 on interface %s", __func__,
+ zlog_warn("%s: ifindex=%d on interface %s", __func__,
ifp->ifindex, ifp->name);
return -2;
}
diff --git a/ripd/rip_bfd.c b/ripd/rip_bfd.c
index ac5035f54..b59db11a3 100644
--- a/ripd/rip_bfd.c
+++ b/ripd/rip_bfd.c
@@ -13,6 +13,8 @@
#include "ripd/rip_bfd.h"
#include "ripd/rip_debug.h"
+DEFINE_MTYPE(RIPD, RIP_BFD_PROFILE, "RIP BFD profile name");
+
extern struct zclient *zclient;
static const char *rip_bfd_interface_profile(struct rip_interface *ri)
diff --git a/ripd/rip_bfd.h b/ripd/rip_bfd.h
index d49ca1515..7621498b1 100644
--- a/ripd/rip_bfd.h
+++ b/ripd/rip_bfd.h
@@ -9,6 +9,8 @@
#include "frrevent.h"
+DECLARE_MTYPE(RIP_BFD_PROFILE);
+
struct rip;
struct rip_interface;
struct rip_peer;
diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c
index ac9fc4b1a..6f45bb5d9 100644
--- a/ripd/rip_cli.c
+++ b/ripd/rip_cli.c
@@ -85,14 +85,33 @@ void cli_show_router_rip(struct vty *vty, const struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/allow-ecmp
*/
-DEFPY_YANG (rip_allow_ecmp,
+DEFUN_YANG (rip_allow_ecmp,
rip_allow_ecmp_cmd,
- "[no] allow-ecmp",
+ "allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]",
+ "Allow Equal Cost MultiPath\n"
+ "Number of paths\n")
+{
+ int idx_number = 1;
+ char mpaths[3] = {};
+ uint32_t paths = MULTIPATH_NUM;
+
+ if (argv[idx_number])
+ paths = strtol(argv[idx_number]->arg, NULL, 10);
+ snprintf(mpaths, sizeof(mpaths), "%u", paths);
+
+ nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, mpaths);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_rip_allow_ecmp,
+ no_rip_allow_ecmp_cmd,
+ "no allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]",
NO_STR
- "Allow Equal Cost MultiPath\n")
+ "Allow Equal Cost MultiPath\n"
+ "Number of paths\n")
{
- nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY,
- no ? "false" : "true");
+ nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, 0);
return nb_cli_apply_changes(vty, NULL);
}
@@ -100,10 +119,14 @@ DEFPY_YANG (rip_allow_ecmp,
void cli_show_rip_allow_ecmp(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
- if (!yang_dnode_get_bool(dnode, NULL))
- vty_out(vty, " no");
+ uint8_t paths;
+
+ paths = yang_dnode_get_uint8(dnode, NULL);
- vty_out(vty, " allow-ecmp\n");
+ if (!paths)
+ vty_out(vty, " no allow-ecmp\n");
+ else
+ vty_out(vty, " allow-ecmp %d\n", paths);
}
/*
@@ -1156,6 +1179,7 @@ void rip_cli_init(void)
install_element(RIP_NODE, &rip_no_distribute_list_cmd);
install_element(RIP_NODE, &rip_allow_ecmp_cmd);
+ install_element(RIP_NODE, &no_rip_allow_ecmp_cmd);
install_element(RIP_NODE, &rip_default_information_originate_cmd);
install_element(RIP_NODE, &rip_default_metric_cmd);
install_element(RIP_NODE, &no_rip_default_metric_cmd);
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
index b383be042..b58015a67 100644
--- a/ripd/rip_interface.c
+++ b/ripd/rip_interface.c
@@ -25,6 +25,7 @@
#include "zebra/connected.h"
#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
#include "ripd/rip_debug.h"
#include "ripd/rip_interface.h"
@@ -457,7 +458,7 @@ static void rip_interface_reset(struct rip_interface *ri)
ri->sent_updates = 0;
ri->passive = 0;
- XFREE(MTYPE_TMP, ri->bfd.profile);
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
rip_interface_clean(ri);
}
@@ -1110,9 +1111,10 @@ void rip_interface_sync(struct interface *ifp)
struct rip_interface *ri;
ri = ifp->info;
- ri->ifp = ifp;
- if (ri)
+ if (ri) {
ri->rip = ifp->vrf->info;
+ ri->ifp = ifp;
+ }
}
/* Called when interface structure allocated. */
diff --git a/ripd/rip_nb_config.c b/ripd/rip_nb_config.c
index 8fe34705c..1117ec9ff 100644
--- a/ripd/rip_nb_config.c
+++ b/ripd/rip_nb_config.c
@@ -25,8 +25,6 @@
#include "ripd/rip_interface.h"
#include "ripd/rip_bfd.h"
-DEFINE_MTYPE_STATIC(RIPD, RIP_BFD_PROFILE, "RIP BFD profile name");
-
/*
* XPath: /frr-ripd:ripd/instance
*/
@@ -101,9 +99,13 @@ int ripd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args)
return NB_OK;
rip = nb_running_get_entry(args->dnode, NULL, true);
- rip->ecmp = yang_dnode_get_bool(args->dnode, NULL);
- if (!rip->ecmp)
+ rip->ecmp = yang_dnode_get_uint8(args->dnode, NULL);
+ if (!rip->ecmp) {
rip_ecmp_disable(rip);
+ return NB_OK;
+ }
+
+ rip_ecmp_change(rip);
return NB_OK;
}
diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c
index 698dcb982..834c7d2d6 100644
--- a/ripd/rip_zebra.c
+++ b/ripd/rip_zebra.c
@@ -20,6 +20,7 @@
/* All information about zebra. */
struct zclient *zclient = NULL;
+uint32_t zebra_ecmp_count = MULTIPATH_NUM;
/* Send ECMP routes to zebra. */
static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp,
@@ -30,7 +31,7 @@ static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp,
struct zapi_nexthop *api_nh;
struct listnode *listnode = NULL;
struct rip_info *rinfo = NULL;
- int count = 0;
+ uint32_t count = 0;
memset(&api, 0, sizeof(api));
api.vrf_id = rip->vrf->vrf_id;
@@ -39,7 +40,7 @@ static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp,
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
- if (count >= MULTIPATH_NUM)
+ if (count >= zebra_ecmp_count)
break;
api_nh = &api.nexthops[count];
api_nh->vrf_id = rip->vrf->vrf_id;
@@ -227,6 +228,11 @@ zclient_handler *const rip_handlers[] = {
[ZEBRA_REDISTRIBUTE_ROUTE_DEL] = rip_zebra_read_route,
};
+static void rip_zebra_capabilities(struct zclient_capabilities *cap)
+{
+ zebra_ecmp_count = MIN(cap->ecmp, zebra_ecmp_count);
+}
+
void rip_zclient_init(struct event_loop *master)
{
/* Set default value to the zebra client structure. */
@@ -234,6 +240,7 @@ void rip_zclient_init(struct event_loop *master)
array_size(rip_handlers));
zclient_init(zclient, ZEBRA_ROUTE_RIP, 0, &ripd_privs);
zclient->zebra_connected = rip_zebra_connected;
+ zclient->zebra_capabilities = rip_zebra_capabilities;
}
void rip_zclient_stop(void)
diff --git a/ripd/ripd.c b/ripd/ripd.c
index cb8dd4945..04a8cad56 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -34,6 +34,7 @@
#include "ripd/ripd.h"
#include "ripd/rip_nb.h"
+#include "ripd/rip_bfd.h"
#include "ripd/rip_debug.h"
#include "ripd/rip_errors.h"
#include "ripd/rip_interface.h"
@@ -155,7 +156,10 @@ struct rip_info *rip_ecmp_add(struct rip *rip, struct rip_info *rinfo_new)
{
struct route_node *rp = rinfo_new->rp;
struct rip_info *rinfo = NULL;
+ struct rip_info *rinfo_exist = NULL;
struct list *list = NULL;
+ struct listnode *node = NULL;
+ struct listnode *nnode = NULL;
if (rp->info == NULL)
rp->info = list_new();
@@ -166,6 +170,33 @@ struct rip_info *rip_ecmp_add(struct rip *rip, struct rip_info *rinfo_new)
if (listcount(list) && !rip->ecmp)
return NULL;
+ /* Add or replace an existing ECMP path with lower neighbor IP */
+ if (listcount(list) && listcount(list) >= rip->ecmp) {
+ struct rip_info *from_highest = NULL;
+
+ /* Find the rip_info struct that has the highest nexthop IP */
+ for (ALL_LIST_ELEMENTS(list, node, nnode, rinfo_exist))
+ if (!from_highest ||
+ (from_highest &&
+ IPV4_ADDR_CMP(&rinfo_exist->from,
+ &from_highest->from) > 0)) {
+ from_highest = rinfo_exist;
+ }
+
+ /* If we have a route in ECMP group, delete the old
+ * one that has a higher next-hop address. Lower IP is
+ * preferred.
+ */
+ if (rip->ecmp > 1 && from_highest &&
+ IPV4_ADDR_CMP(&from_highest->from, &rinfo_new->from) > 0) {
+ rip_ecmp_delete(rip, from_highest);
+ goto add_or_replace;
+ }
+
+ return NULL;
+ }
+
+add_or_replace:
rinfo = rip_info_new();
memcpy(rinfo, rinfo_new, sizeof(struct rip_info));
listnode_add(list, rinfo);
@@ -2631,6 +2662,36 @@ struct rip *rip_lookup_by_vrf_name(const char *vrf_name)
return RB_FIND(rip_instance_head, &rip_instances, &rip);
}
+/* Update ECMP routes to zebra when `allow-ecmp` changed. */
+void rip_ecmp_change(struct rip *rip)
+{
+ struct route_node *rp;
+ struct rip_info *rinfo;
+ struct list *list;
+ struct listnode *node, *nextnode;
+
+ for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
+ list = rp->info;
+ if (list && listcount(list) > 1) {
+ while (listcount(list) > rip->ecmp) {
+ struct rip_info *from_highest = NULL;
+
+ for (ALL_LIST_ELEMENTS(list, node, nextnode,
+ rinfo)) {
+ if (!from_highest ||
+ (from_highest &&
+ IPV4_ADDR_CMP(
+ &rinfo->from,
+ &from_highest->from) > 0))
+ from_highest = rinfo;
+ }
+
+ rip_ecmp_delete(rip, from_highest);
+ }
+ }
+ }
+}
+
/* Create new RIP instance and set it to global variable. */
struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket)
{
@@ -2640,7 +2701,7 @@ struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket)
rip->vrf_name = XSTRDUP(MTYPE_RIP_VRF_NAME, vrf_name);
/* Set initial value. */
- rip->ecmp = yang_get_default_bool("%s/allow-ecmp", RIP_INSTANCE);
+ rip->ecmp = yang_get_default_uint8("%s/allow-ecmp", RIP_INSTANCE);
rip->default_metric =
yang_get_default_uint8("%s/default-metric", RIP_INSTANCE);
rip->distance =
@@ -3346,7 +3407,7 @@ void rip_clean(struct rip *rip)
route_table_finish(rip->distance_table);
RB_REMOVE(rip_instance_head, &rip_instances, rip);
- XFREE(MTYPE_TMP, rip->default_bfd_profile);
+ XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
XFREE(MTYPE_RIP_VRF_NAME, rip->vrf_name);
XFREE(MTYPE_RIP, rip);
}
diff --git a/ripd/ripd.h b/ripd/ripd.h
index bba3c2806..2db1e5a6b 100644
--- a/ripd/ripd.h
+++ b/ripd/ripd.h
@@ -141,7 +141,7 @@ struct rip {
struct route_table *distance_table;
/* RIP ECMP flag */
- bool ecmp;
+ uint8_t ecmp;
/* Are we in passive-interface default mode? */
bool passive_default;
@@ -537,4 +537,6 @@ extern struct event_loop *master;
DECLARE_HOOK(rip_ifaddr_add, (struct connected * ifc), (ifc));
DECLARE_HOOK(rip_ifaddr_del, (struct connected * ifc), (ifc));
+extern void rip_ecmp_change(struct rip *rip);
+
#endif /* _ZEBRA_RIP_H */
diff --git a/tests/lib/subdir.am b/tests/lib/subdir.am
index e950d0120..c3a1a3e2c 100644
--- a/tests/lib/subdir.am
+++ b/tests/lib/subdir.am
@@ -15,8 +15,14 @@ tests_lib_test_frrscript_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_frrscript_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_frrscript_LDADD = $(ALL_TESTS_LDADD)
tests_lib_test_frrscript_SOURCES = tests/lib/test_frrscript.c
+EXTRA_tests_lib_test_frrscript_DEPENDENCIES = copy_script
EXTRA_DIST += tests/lib/test_frrscript.py tests/lib/script1.lua
+# For out-of-tree build, lua script needs to be in the build dir, rather than
+# just available somewhere in the VPATH
+copy_script: tests/lib/script1.lua
+ test -e tests/lib/script1.lua || \
+ $(INSTALL_SCRIPT) $< tests/lib/script1.lua
##############################################################################
GRPC_TESTS_LDADD = staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm
diff --git a/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref
index cb63da471..7e28f04e1 100644
--- a/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref
+++ b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref
@@ -10,6 +10,7 @@ r1-eth0 is up
Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5
Hello due in XX.XXXs
Neighbor Count is 0, Adjacent neighbor count is 0
+ Graceful Restart hello delay: 10s
r1-eth3 is up
ifindex X, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0
@@ -22,3 +23,4 @@ r1-eth3 is up
Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5
Hello due in XX.XXXs
Neighbor Count is 0, Adjacent neighbor count is 0
+ Graceful Restart hello delay: 10s
diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py
index 92bb99c8f..4b7c4de80 100644
--- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py
+++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py
@@ -198,6 +198,12 @@ def test_error_messages_daemons():
if fatal_error != "":
pytest.skip(fatal_error)
+ if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
+ print(
+ "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
+ )
+ pytest.skip("Skipping test for Stderr output")
+
print("\n\n** Check for error messages in daemons")
print("******************************************\n")
diff --git a/tests/topotests/bgp_aigp/r2/bgpd.conf b/tests/topotests/bgp_aigp/r2/bgpd.conf
index ae72f215c..4db468753 100644
--- a/tests/topotests/bgp_aigp/r2/bgpd.conf
+++ b/tests/topotests/bgp_aigp/r2/bgpd.conf
@@ -4,7 +4,8 @@ router bgp 65001
neighbor 10.0.0.1 remote-as internal
neighbor 10.0.0.1 timers 1 3
neighbor 10.0.0.1 timers connect 1
- neighbor 192.168.24.4 remote-as external
+ neighbor 10.0.0.1 route-reflector-client
+ neighbor 192.168.24.4 remote-as internal
neighbor 192.168.24.4 timers 1 3
neighbor 192.168.24.4 timers connect 1
neighbor 192.168.24.4 aigp
diff --git a/tests/topotests/bgp_aigp/r3/bgpd.conf b/tests/topotests/bgp_aigp/r3/bgpd.conf
index 7572e268c..5ab712eab 100644
--- a/tests/topotests/bgp_aigp/r3/bgpd.conf
+++ b/tests/topotests/bgp_aigp/r3/bgpd.conf
@@ -4,7 +4,8 @@ router bgp 65001
neighbor 10.0.0.1 remote-as internal
neighbor 10.0.0.1 timers 1 3
neighbor 10.0.0.1 timers connect 1
- neighbor 192.168.35.5 remote-as external
+ neighbor 10.0.0.1 route-reflector-client
+ neighbor 192.168.35.5 remote-as internal
neighbor 192.168.35.5 timers 1 3
neighbor 192.168.35.5 timers connect 1
neighbor 192.168.35.5 aigp
diff --git a/tests/topotests/bgp_aigp/r4/bgpd.conf b/tests/topotests/bgp_aigp/r4/bgpd.conf
index d2b96b7b0..aa88bac91 100644
--- a/tests/topotests/bgp_aigp/r4/bgpd.conf
+++ b/tests/topotests/bgp_aigp/r4/bgpd.conf
@@ -1,10 +1,11 @@
-router bgp 65002
+router bgp 65001
no bgp ebgp-requires-policy
no bgp network import-check
- neighbor 192.168.24.2 remote-as external
+ neighbor 192.168.24.2 remote-as internal
neighbor 192.168.24.2 timers 1 3
neighbor 192.168.24.2 timers connect 1
neighbor 192.168.24.2 aigp
+ neighbor 192.168.24.2 route-reflector-client
neighbor 10.0.0.6 remote-as internal
neighbor 10.0.0.6 timers 1 3
neighbor 10.0.0.6 timers connect 1
diff --git a/tests/topotests/bgp_aigp/r5/bgpd.conf b/tests/topotests/bgp_aigp/r5/bgpd.conf
index 944872289..4fde26205 100644
--- a/tests/topotests/bgp_aigp/r5/bgpd.conf
+++ b/tests/topotests/bgp_aigp/r5/bgpd.conf
@@ -1,10 +1,11 @@
-router bgp 65002
+router bgp 65001
no bgp ebgp-requires-policy
no bgp network import-check
- neighbor 192.168.35.3 remote-as external
+ neighbor 192.168.35.3 remote-as internal
neighbor 192.168.35.3 timers 1 3
neighbor 192.168.35.3 timers connect 1
neighbor 192.168.35.3 aigp
+ neighbor 192.168.35.3 route-reflector-client
neighbor 10.0.0.6 remote-as internal
neighbor 10.0.0.6 timers 1 3
neighbor 10.0.0.6 timers connect 1
diff --git a/tests/topotests/bgp_aigp/r6/bgpd.conf b/tests/topotests/bgp_aigp/r6/bgpd.conf
index 15d9437a8..2faae7720 100644
--- a/tests/topotests/bgp_aigp/r6/bgpd.conf
+++ b/tests/topotests/bgp_aigp/r6/bgpd.conf
@@ -1,4 +1,4 @@
-router bgp 65002
+router bgp 65001
no bgp ebgp-requires-policy
no bgp network import-check
neighbor 10.0.0.4 remote-as internal
diff --git a/tests/topotests/bgp_aigp/r7/bgpd.conf b/tests/topotests/bgp_aigp/r7/bgpd.conf
index 00d85cfad..639dcfeca 100644
--- a/tests/topotests/bgp_aigp/r7/bgpd.conf
+++ b/tests/topotests/bgp_aigp/r7/bgpd.conf
@@ -1,4 +1,4 @@
-router bgp 65002
+router bgp 65001
no bgp ebgp-requires-policy
no bgp network import-check
neighbor 192.168.67.6 remote-as internal
diff --git a/tests/topotests/bgp_aigp/test_bgp_aigp.py b/tests/topotests/bgp_aigp/test_bgp_aigp.py
index 6fda45940..655e9ad18 100644
--- a/tests/topotests/bgp_aigp/test_bgp_aigp.py
+++ b/tests/topotests/bgp_aigp/test_bgp_aigp.py
@@ -14,7 +14,7 @@ r6 receives those routes with aigp-metric TLV.
r2 and r3 receives those routes with aigp-metric TLV increased by 20,
and 30 appropriately.
-r1 receives routes with aigp-metric TLV 91,101 and 92,102 appropriately.
+r1 receives routes with aigp-metric TLV 111,131 and 112,132 appropriately.
"""
import os
@@ -109,15 +109,15 @@ def test_bgp_aigp():
expected = {
"paths": [
{
- "aigpMetric": 101,
+ "aigpMetric": 111,
"valid": True,
- "bestpath": {"selectionReason": "Router ID"},
- "nexthops": [{"hostname": "r2", "accessible": True}],
+ "nexthops": [{"hostname": "r3", "accessible": True}],
},
{
- "aigpMetric": 91,
+ "aigpMetric": 131,
"valid": True,
- "nexthops": [{"hostname": "r3", "accessible": True}],
+ "bestpath": {"selectionReason": "Neighbor IP"},
+ "nexthops": [{"hostname": "r2", "accessible": True}],
},
]
}
@@ -141,28 +141,30 @@ def test_bgp_aigp():
"10.0.0.71/32": {
"paths": [
{
- "aigpMetric": 101,
+ "aigpMetric": 111,
+ "bestpath": {"selectionReason": "AIGP"},
"valid": True,
+ "nexthops": [{"hostname": "r3", "accessible": True}],
},
{
- "aigpMetric": 91,
+ "aigpMetric": 131,
"valid": True,
- "bestpath": {"selectionReason": "AIGP"},
- "nexthops": [{"hostname": "r3", "accessible": True}],
+ "nexthops": [{"hostname": "r2", "accessible": True}],
},
],
},
"10.0.0.72/32": {
"paths": [
{
- "aigpMetric": 102,
+ "aigpMetric": 112,
+ "bestpath": {"selectionReason": "AIGP"},
"valid": True,
+ "nexthops": [{"hostname": "r3", "accessible": True}],
},
{
- "aigpMetric": 92,
+ "aigpMetric": 132,
"valid": True,
- "bestpath": {"selectionReason": "AIGP"},
- "nexthops": [{"hostname": "r3", "accessible": True}],
+ "nexthops": [{"hostname": "r2", "accessible": True}],
},
],
},
@@ -172,7 +174,7 @@ def test_bgp_aigp():
# Initial converge, AIGP is not involved in best-path selection process
test_func = functools.partial(_bgp_converge)
- _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert result is None, "can't converge initially"
# Enable `bgp bestpath aigp`
@@ -186,27 +188,27 @@ def test_bgp_aigp():
# r4, 10.0.0.71/32 with aigp-metric 71
test_func = functools.partial(_bgp_check_aigp_metric, r4, "10.0.0.71/32", 71)
- _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert result is None, "aigp-metric for 10.0.0.71/32 is not 71"
# r5, 10.0.0.72/32 with aigp-metric 72
test_func = functools.partial(_bgp_check_aigp_metric, r5, "10.0.0.72/32", 72)
- _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert result is None, "aigp-metric for 10.0.0.72/32 is not 72"
# r2, 10.0.0.71/32 with aigp-metric 101 (71 + 30)
test_func = functools.partial(_bgp_check_aigp_metric, r2, "10.0.0.71/32", 101)
- _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert result is None, "aigp-metric for 10.0.0.71/32 is not 101"
# r3, 10.0.0.72/32 with aigp-metric 92 (72 + 20)
test_func = functools.partial(_bgp_check_aigp_metric, r3, "10.0.0.72/32", 92)
- _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert result is None, "aigp-metric for 10.0.0.72/32 is not 92"
# r1, check if AIGP is considered in best-path selection (lowest wins)
test_func = functools.partial(_bgp_check_aigp_metric_bestpath)
- _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert result is None, "AIGP attribute is not considered in best-path selection"
diff --git a/tests/topotests/bgp_auth/test_bgp_auth1.py b/tests/topotests/bgp_auth/test_bgp_auth1.py
index 566d391f7..9d47106c0 100644
--- a/tests/topotests/bgp_auth/test_bgp_auth1.py
+++ b/tests/topotests/bgp_auth/test_bgp_auth1.py
@@ -158,8 +158,8 @@ def setup_module(mod):
# For all registered routers, load the zebra configuration file
for rname, router in router_list.items():
router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
- router.load_config(TopoRouter.RD_OSPF)
- router.load_config(TopoRouter.RD_BGP)
+ router.load_config(TopoRouter.RD_OSPF, "")
+ router.load_config(TopoRouter.RD_BGP, "")
# After copying the configurations, this function loads configured daemons.
tgen.start_router()
diff --git a/tests/topotests/bgp_auth/test_bgp_auth2.py b/tests/topotests/bgp_auth/test_bgp_auth2.py
index 0e9942a22..6b9203672 100644
--- a/tests/topotests/bgp_auth/test_bgp_auth2.py
+++ b/tests/topotests/bgp_auth/test_bgp_auth2.py
@@ -158,8 +158,8 @@ def setup_module(mod):
# For all registered routers, load the zebra configuration file
for rname, router in router_list.items():
router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
- router.load_config(TopoRouter.RD_OSPF)
- router.load_config(TopoRouter.RD_BGP)
+ router.load_config(TopoRouter.RD_OSPF, "")
+ router.load_config(TopoRouter.RD_BGP, "")
# After copying the configurations, this function loads configured daemons.
tgen.start_router()
diff --git a/tests/topotests/bgp_auth/test_bgp_auth3.py b/tests/topotests/bgp_auth/test_bgp_auth3.py
index 99a8953b3..2237c6b1b 100644
--- a/tests/topotests/bgp_auth/test_bgp_auth3.py
+++ b/tests/topotests/bgp_auth/test_bgp_auth3.py
@@ -157,8 +157,8 @@ def setup_module(mod):
# For all registered routers, load the zebra configuration file
for rname, router in router_list.items():
router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
- router.load_config(TopoRouter.RD_OSPF)
- router.load_config(TopoRouter.RD_BGP)
+ router.load_config(TopoRouter.RD_OSPF, "")
+ router.load_config(TopoRouter.RD_BGP, "")
# After copying the configurations, this function loads configured daemons.
tgen.start_router()
diff --git a/tests/topotests/bgp_auth/test_bgp_auth4.py b/tests/topotests/bgp_auth/test_bgp_auth4.py
index dffef0eef..d6fe42504 100644
--- a/tests/topotests/bgp_auth/test_bgp_auth4.py
+++ b/tests/topotests/bgp_auth/test_bgp_auth4.py
@@ -157,8 +157,8 @@ def setup_module(mod):
# For all registered routers, load the zebra configuration file
for rname, router in router_list.items():
router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
- router.load_config(TopoRouter.RD_OSPF)
- router.load_config(TopoRouter.RD_BGP)
+ router.load_config(TopoRouter.RD_OSPF, "")
+ router.load_config(TopoRouter.RD_BGP, "")
# After copying the configurations, this function loads configured daemons.
tgen.start_router()
diff --git a/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json
index 23afa2c91..5528b590f 100755
--- a/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json
+++ b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json
@@ -22,12 +22,7 @@
"routers": {
"r1": {
"links": {
- "lo": {
- "ipv4": "auto",
- "ipv6": "auto",
- "type": "loopback",
- "vrf": "RED"
- },
+
"r2": {
"ipv4": "auto",
"ipv6": "auto",
@@ -129,12 +124,7 @@
},
"r2": {
"links": {
- "lo": {
- "ipv4": "auto",
- "ipv6": "auto",
- "type": "loopback",
- "vrf": "RED"
- },
+
"r1": {
"ipv4": "auto",
"ipv6": "auto",
@@ -193,12 +183,7 @@
},
"r3": {
"links": {
- "lo": {
- "ipv4": "auto",
- "ipv6": "auto",
- "type": "loopback",
- "vrf": "RED"
- },
+
"r1": {
"ipv4": "auto",
"ipv6": "auto",
@@ -287,12 +272,7 @@
},
"r4": {
"links": {
- "lo": {
- "ipv4": "auto",
- "ipv6": "auto",
- "type": "loopback",
- "vrf": "RED"
- },
+
"r3": {
"ipv4": "auto",
"ipv6": "auto",
@@ -336,12 +316,7 @@
},
"r5": {
"links": {
- "lo": {
- "ipv4": "auto",
- "ipv6": "auto",
- "type": "loopback",
- "vrf": "RED"
- },
+
"r3": {
"ipv4": "auto",
"ipv6": "auto",
diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py
index c0a5a1905..e033a0f00 100644
--- a/tests/topotests/bgp_features/test_bgp_features.py
+++ b/tests/topotests/bgp_features/test_bgp_features.py
@@ -176,6 +176,19 @@ def test_bgp_convergence():
# tgen.mininet_cli()
+def get_shut_msg_count(tgen):
+ shuts = {}
+ for rtrNum in [2, 4]:
+ shutmsg = tgen.net["r{}".format(rtrNum)].cmd_nostatus(
+ 'grep -c "NOTIFICATION.*Cease/Administrative Shutdown" bgpd.log', warn=False
+ )
+ try:
+ shuts[rtrNum] = int(shutmsg.strip())
+ except ValueError:
+ shuts[rtrNum] = 0
+ return shuts
+
+
def test_bgp_shutdown():
"Test BGP instance shutdown"
@@ -185,6 +198,8 @@ def test_bgp_shutdown():
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
+ shuts_before = get_shut_msg_count(tgen)
+
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "bgp shutdown message ABCDabcd"'
)
@@ -208,6 +223,11 @@ def test_bgp_shutdown():
)
assert res is None, assertmsg
+ shuts_after = get_shut_msg_count(tgen)
+
+ for k in shuts_before:
+ assert shuts_before[k] + 1 == shuts_after[k]
+
def test_bgp_shutdown_message():
"Test BGP Peer Shutdown Message"
@@ -222,18 +242,11 @@ def test_bgp_shutdown_message():
logger.info("Checking BGP shutdown received on router r{}".format(rtrNum))
shut_message = tgen.net["r{}".format(rtrNum)].cmd(
- 'tail bgpd.log | grep "NOTIFICATION.*Cease/Administrative Shutdown"'
+ 'grep -e "NOTIFICATION.*Cease/Administrative Shutdown.*ABCDabcd" bgpd.log'
)
assertmsg = "BGP shutdown message not received on router R{}".format(rtrNum)
assert shut_message != "", assertmsg
- assertmsg = "Incorrect BGP shutdown message received on router R{}".format(
- rtrNum
- )
- assert "ABCDabcd" in shut_message, assertmsg
-
- # tgen.mininet_cli()
-
def test_bgp_no_shutdown():
"Test BGP instance no shutdown"
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/__init__.py b/tests/topotests/bgp_route_map_match_source_protocol/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/__init__.py
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf
new file mode 100644
index 000000000..7c3efeea4
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf
@@ -0,0 +1,32 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+interface r1-eth1
+ ip address 192.168.2.1/24
+!
+ip route 10.10.10.10/32 192.168.2.2
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 1 3
+ neighbor 192.168.1.2 timers connect 1
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers 1 3
+ neighbor 192.168.2.2 timers connect 1
+ address-family ipv4
+ redistribute connected
+ redistribute static
+ neighbor 192.168.1.2 route-map r2 out
+ neighbor 192.168.2.2 route-map r3 out
+ exit-address-family
+!
+route-map r2 permit 10
+ match source-protocol static
+route-map r3 permit 10
+ match source-protocol connected
+!
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf
new file mode 100644
index 000000000..7213975cc
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf
@@ -0,0 +1,10 @@
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+!
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf
new file mode 100644
index 000000000..4a1d830b0
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf
@@ -0,0 +1,10 @@
+!
+interface r3-eth0
+ ip address 192.168.2.2/24
+!
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 1 3
+ neighbor 192.168.2.1 timers connect 1
+!
diff --git a/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py
new file mode 100644
index 000000000..282879640
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if r1 can announce only static routes to r2, and only connected
+routes to r3 using `match source-protocol` with route-maps.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_route_map_match_source_protocol():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_advertised_routes_r2():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd(
+ "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "10.10.10.10/32": {
+ "valid": True,
+ }
+ },
+ "totalPrefixCounter": 1,
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_advertised_routes_r2)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Failed to filter routes by source-protocol for r2"
+
+ def _bgp_check_advertised_routes_r3():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd(
+ "show bgp ipv4 unicast neighbors 192.168.2.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "192.168.1.0/24": {
+ "valid": True,
+ },
+ "192.168.2.0/24": {
+ "valid": True,
+ },
+ "172.16.255.1/32": {
+ "valid": True,
+ },
+ },
+ "totalPrefixCounter": 3,
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_advertised_routes_r3)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert result is None, "Failed to filter routes by source-protocol for r3"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json
index 159879a85..39ba7dd62 100644
--- a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json
@@ -48,11 +48,11 @@
{
"ip":"192:2::12",
"afi":"ipv6",
- "scope":"global"
+ "scope":"global",
+ "used":true
},
{
- "scope": "link-local",
- "used":true
+ "scope": "link-local"
}
]
}
@@ -69,7 +69,8 @@
{
"ip":"192:168::255:13",
"afi":"ipv6",
- "scope": "global"
+ "scope": "global",
+ "used":true
},
{
"scope": "link-local"
@@ -123,7 +124,8 @@
{
"ip":"192:2::11",
"afi":"ipv6",
- "scope":"global"
+ "scope":"global",
+ "used":true
}
]
}
@@ -140,7 +142,8 @@
{
"ip":"192:2::11",
"afi":"ipv6",
- "scope":"global"
+ "scope":"global",
+ "used":true
}
]
}
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf
index 74e3e6fb5..6bb0a88f0 100644
--- a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf
@@ -24,7 +24,7 @@ router bgp 65500 vrf vrf1
neighbor 192:168::255:13 activate
neighbor 192:168::255:13 route-map rmap in
redistribute connected
- redistribute static
+ redistribute static route-map rmap
label vpn export allocation-mode per-nexthop
label vpn export auto
rd vpn export 444:1
@@ -39,7 +39,6 @@ interface r1-eth0
bgp community-list 1 seq 5 permit 10:10
!
route-map rmap permit 1
- match community 1
set ipv6 next-hop prefer-global
!
route-map rmap permit 2
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf
index d0d4e3dc4..cb653d61b 100644
--- a/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf
@@ -7,12 +7,9 @@ router bgp 65500
!
address-family ipv6 unicast
neighbor 192:2::100 activate
- neighbor 192:2::100 route-map rmap out
network 172:31::11/128
network 172:31::111/128
network 172:31::20/128
exit-address-family
!
-route-map rmap permit 1
- set community 10:10
-!
+
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf
index 201b905b3..04378825a 100644
--- a/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf
@@ -7,10 +7,6 @@ router bgp 65500
exit-address-family
address-family ipv6 unicast
neighbor 192:168::255:1 activate
- neighbor 192:168::255:1 route-map rmap out
network 172:31::0:13/128
exit-address-family
!
-route-map rmap permit 1
- set community 10:10
-!
diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py
index c58242e7c..719ba801c 100644
--- a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py
+++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py
@@ -52,9 +52,7 @@ from lib.topolog import logger
pytestmark = [pytest.mark.bgpd]
PREFIXES_R11 = ["172:31::11/128", "172:31::20/128", "172:31::111/128"]
-PREFIXES_R12 = ["172:31::12/128"]
-PREFIXES_REDIST_R12 = ["172:31::15/128"]
-PREFIXES_R13 = ["172:31::13/128"]
+PREFIXES_R12 = ["172:31::12/128", "172:31::15/128"]
PREFIXES_REDIST_R14 = ["172:31::14/128"]
PREFIXES_CONNECTED = ["192:168::255/112", "192:2::/64"]
@@ -105,11 +103,6 @@ def _populate_iface():
cmds_list_plus = [
"ip link set dev {0}-eth2 master vrf1",
]
- cmd_no_ll = [
- "ip link set dev {0}-eth0 down",
- "echo 1 > /proc/sys/net/ipv6/conf/{0}-eth0/addr_gen_mode",
- "ip link set dev {0}-eth0 up",
- ]
for cmd in cmds_list:
input = cmd.format("r1")
@@ -129,13 +122,6 @@ def _populate_iface():
output = tgen.net["r2"].cmd(cmd.format("r2"))
logger.info("output: " + output)
- for rtr in ("r11", "r13"):
- for cmd in cmd_no_ll:
- input = cmd.format(rtr)
- logger.info("input: " + cmd)
- output = tgen.net[rtr].cmd(cmd.format(rtr))
- logger.info("output: " + output)
-
def setup_module(mod):
"Sets up the pytest environment"
@@ -226,8 +212,6 @@ def bgp_vpnv6_table_check_all(router, label_list=None, same=False):
router,
group=PREFIXES_R11
+ PREFIXES_R12
- + PREFIXES_REDIST_R12
- + PREFIXES_R13
+ PREFIXES_REDIST_R14
+ PREFIXES_CONNECTED,
label_list=label_list,
@@ -236,8 +220,6 @@ def bgp_vpnv6_table_check_all(router, label_list=None, same=False):
for group in (
PREFIXES_R11,
PREFIXES_R12,
- PREFIXES_REDIST_R12,
- PREFIXES_R13,
PREFIXES_REDIST_R14,
PREFIXES_CONNECTED,
):
diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py
index 74e308dbc..b78a2f105 100755
--- a/tests/topotests/conftest.py
+++ b/tests/topotests/conftest.py
@@ -87,6 +87,12 @@ def pytest_addoption(parser):
)
parser.addoption(
+ "--memleaks",
+ action="store_true",
+ help="Report memstat results as errors",
+ )
+
+ parser.addoption(
"--pause",
action="store_true",
help="Pause after each test",
@@ -189,7 +195,7 @@ def pytest_addoption(parser):
)
-def check_for_memleaks():
+def check_for_valgrind_memleaks():
assert topotest.g_pytest_config.option.valgrind_memleaks
leaks = []
@@ -232,11 +238,47 @@ def check_for_memleaks():
pytest.fail("valgrind memleaks found for daemons: " + " ".join(daemons))
+def check_for_memleaks():
+ leaks = []
+ tgen = get_topogen() # pylint: disable=redefined-outer-name
+ latest = []
+ existing = []
+ if tgen is not None:
+ logdir = tgen.logdir
+ if hasattr(tgen, "memstat_existing_files"):
+ existing = tgen.memstat_existing_files
+ latest = glob.glob(os.path.join(logdir, "*/*.err"))
+
+ daemons = []
+ for vfile in latest:
+ if vfile in existing:
+ continue
+ with open(vfile, encoding="ascii") as vf:
+ vfcontent = vf.read()
+ num = vfcontent.count("memstats:")
+ if num:
+ existing.append(vfile) # have summary don't check again
+ emsg = "{} types in {}".format(num, vfile)
+ leaks.append(emsg)
+ daemon = re.match(r".*test[a-z_A-Z0-9\+]*/(.*)\.err", vfile).group(1)
+ daemons.append("{}({})".format(daemon, num))
+
+ if tgen is not None:
+ tgen.memstat_existing_files = existing
+
+ if leaks:
+ logger.error("memleaks found:\n\t%s", "\n\t".join(leaks))
+ pytest.fail("memleaks found for daemons: " + " ".join(daemons))
+
+
@pytest.fixture(autouse=True, scope="module")
def module_check_memtest(request):
yield
if request.config.option.valgrind_memleaks:
if get_topogen() is not None:
+ check_for_valgrind_memleaks()
+ if request.config.option.memleaks:
+ if get_topogen() is not None:
check_for_memleaks()
@@ -264,6 +306,8 @@ def pytest_runtest_call(item: pytest.Item) -> None:
# Check for leaks if requested
if item.config.option.valgrind_memleaks:
+ check_for_valgrind_memleaks()
+ if item.config.option.memleaks:
check_for_memleaks()
@@ -370,10 +414,22 @@ def pytest_configure(config):
if config.option.topology_only and is_xdist:
pytest.exit("Cannot use --topology-only with distributed test mode")
+ pytest.exit("Cannot use --topology-only with distributed test mode")
+
# Check environment now that we have config
if not diagnose_env(rundir):
pytest.exit("environment has errors, please read the logs in %s" % rundir)
+ # slave TOPOTESTS_CHECK_MEMLEAK to memleaks flag
+ if config.option.memleaks:
+ if "TOPOTESTS_CHECK_MEMLEAK" not in os.environ:
+ os.environ["TOPOTESTS_CHECK_MEMLEAK"] = "/dev/null"
+ else:
+ if "TOPOTESTS_CHECK_MEMLEAK" in os.environ:
+ del os.environ["TOPOTESTS_CHECK_MEMLEAK"]
+ if "TOPOTESTS_CHECK_STDERR" in os.environ:
+ del os.environ["TOPOTESTS_CHECK_STDERR"]
+
@pytest.fixture(autouse=True, scope="session")
def setup_session_auto():
diff --git a/tests/topotests/evpn_pim_1/spine/bgp.summ.json b/tests/topotests/evpn_pim_1/spine/bgp.summ.json
index 5ff4b096f..7f37cedb2 100644
--- a/tests/topotests/evpn_pim_1/spine/bgp.summ.json
+++ b/tests/topotests/evpn_pim_1/spine/bgp.summ.json
@@ -8,7 +8,6 @@
"192.168.1.2":{
"remoteAs":65002,
"version":4,
- "tableVersion":0,
"outq":0,
"inq":0,
"pfxRcd":3,
@@ -21,7 +20,6 @@
"192.168.2.3":{
"remoteAs":65003,
"version":4,
- "tableVersion":0,
"outq":0,
"inq":0,
"pfxRcd":3,
diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py
index a0b12b2d6..c58da996d 100644
--- a/tests/topotests/lib/bgp.py
+++ b/tests/topotests/lib/bgp.py
@@ -164,7 +164,6 @@ def create_router_bgp(tgen, topo=None, input_dict=None, build=False, load_config
router,
)
else:
-
ipv4_data = bgp_addr_data.setdefault("ipv4", {})
ipv6_data = bgp_addr_data.setdefault("ipv6", {})
l2vpn_data = bgp_addr_data.setdefault("l2vpn", {})
@@ -645,7 +644,6 @@ def __create_l2vpn_evpn_address_family(
if advertise_data:
for address_type, unicast_type in advertise_data.items():
-
if type(unicast_type) is dict:
for key, value in unicast_type.items():
cmd = "advertise {} {}".format(address_type, key)
@@ -1651,7 +1649,6 @@ def modify_as_number(tgen, topo, input_dict):
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
try:
-
new_topo = deepcopy(topo["routers"])
router_dict = {}
for router in input_dict.keys():
@@ -1953,7 +1950,6 @@ def clear_bgp_and_verify(tgen, topo, router, rid=None):
total_peer = 0
for addr_type in bgp_addr_type.keys():
-
if not check_address_types(addr_type):
continue
@@ -2022,7 +2018,6 @@ def clear_bgp_and_verify(tgen, topo, router, rid=None):
peer_uptime_after_clear_bgp = {}
# Verifying BGP convergence after bgp clear command
for retry in range(50):
-
# Waiting for BGP to converge
logger.info(
"Waiting for %s sec for BGP to converge on router" " %s...",
@@ -3278,7 +3273,6 @@ def verify_graceful_restart(
if lmode is None:
if "graceful-restart" in input_dict[dut]["bgp"]:
-
if (
"graceful-restart" in input_dict[dut]["bgp"]["graceful-restart"]
and input_dict[dut]["bgp"]["graceful-restart"][
@@ -3326,7 +3320,6 @@ def verify_graceful_restart(
if rmode is None:
if "graceful-restart" in input_dict[peer]["bgp"]:
-
if (
"graceful-restart"
in input_dict[peer]["bgp"]["graceful-restart"]
@@ -3628,7 +3621,6 @@ def verify_eor(tgen, topo, addr_type, input_dict, dut, peer, expected=True):
eor_json = show_bgp_graceful_json_out[afi]["endOfRibStatus"]
if "endOfRibSend" in eor_json:
-
if eor_json["endOfRibSend"]:
logger.info(
"[DUT: %s]: EOR Send true for %s " "%s", dut, neighbor_ip, afi
@@ -3918,7 +3910,6 @@ def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer)
"timer"
].items():
if rs_timer == "restart-time":
-
receivedTimer = value
if (
show_bgp_graceful_json_out["timers"][
@@ -4777,7 +4768,6 @@ def get_prefix_count_route(
tgen = get_topogen()
for router, rnode in tgen.routers().items():
if router == dut:
-
if vrf:
ipv4_cmd = "sh ip bgp vrf {} summary json".format(vrf)
show_bgp_json_ipv4 = run_frr_cmd(rnode, ipv4_cmd, isjson=True)
@@ -4871,7 +4861,6 @@ def verify_rib_default_route(
connected_routes = {}
for router, rnode in tgen.routers().items():
if router == dut:
-
ipv4_routes = run_frr_cmd(rnode, "sh ip bgp json", isjson=True)
ipv6_routes = run_frr_cmd(rnode, "sh ip bgp ipv6 unicast json", isjson=True)
is_ipv4_default_attrib_found = False
diff --git a/tests/topotests/lib/bgprib.py b/tests/topotests/lib/bgprib.py
index cd449cb50..699c7a4da 100644
--- a/tests/topotests/lib/bgprib.py
+++ b/tests/topotests/lib/bgprib.py
@@ -25,6 +25,7 @@ from lib.lutil import luCommand, luResult, LUtil
import json
import re
+
# gpz: get rib in json form and compare against desired routes
class BgpRib:
def log(self, str):
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index d19d8db75..67afe8739 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -243,7 +243,6 @@ def run_frr_cmd(rnode, cmd, isjson=False):
def apply_raw_config(tgen, input_dict):
-
"""
API to configure raw configuration on device. This can be used for any cli
which has not been implemented in JSON.
@@ -447,7 +446,6 @@ def check_router_status(tgen):
try:
router_list = tgen.routers()
for router, rnode in router_list.items():
-
result = rnode.check_router_running()
if result != "":
daemons = []
@@ -1914,7 +1912,6 @@ def interface_status(tgen, topo, input_dict):
rlist = []
for router in input_dict.keys():
-
interface_list = input_dict[router]["interface_list"]
status = input_dict[router].setdefault("status", "up")
for intf in interface_list:
@@ -2529,7 +2526,6 @@ def create_route_maps(tgen, input_dict, build=False):
continue
rmap_data = []
for rmap_name, rmap_value in input_dict[router]["route_maps"].items():
-
for rmap_dict in rmap_value:
del_action = rmap_dict.setdefault("delete", False)
@@ -3002,7 +2998,6 @@ def addKernelRoute(
group_addr_range = [group_addr_range]
for grp_addr in group_addr_range:
-
addr_type = validate_ip_address(grp_addr)
if addr_type == "ipv4":
if next_hop is not None:
@@ -3193,7 +3188,6 @@ def configure_brctl(tgen, topo, input_dict):
if "brctl" in input_dict[dut]:
for brctl_dict in input_dict[dut]["brctl"]:
-
brctl_names = brctl_dict.setdefault("brctl_name", [])
addvxlans = brctl_dict.setdefault("addvxlan", [])
stp_values = brctl_dict.setdefault("stp", [])
@@ -3203,7 +3197,6 @@ def configure_brctl(tgen, topo, input_dict):
for brctl_name, vxlan, vrf, stp in zip(
brctl_names, addvxlans, vrfs, stp_values
):
-
ip_cmd_list = []
cmd = "ip link add name {} type bridge stp_state {}".format(
brctl_name, stp
@@ -3614,7 +3607,6 @@ def verify_rib(
for static_route in static_routes:
if "vrf" in static_route and static_route["vrf"] is not None:
-
logger.info(
"[DUT: {}]: Verifying routes for VRF:"
" {}".format(router, static_route["vrf"])
@@ -4053,7 +4045,6 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None, protocol=
for static_route in static_routes:
if "vrf" in static_route and static_route["vrf"] is not None:
-
logger.info(
"[DUT: {}]: Verifying routes for VRF:"
" {}".format(router, static_route["vrf"])
diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py
index 9aef41cd1..1eb88f26d 100644
--- a/tests/topotests/lib/lutil.py
+++ b/tests/topotests/lib/lutil.py
@@ -177,7 +177,17 @@ Total %-4d %-4d %d\n\
self.log("unable to read: " + tstFile)
sys.exit(1)
- def command(self, target, command, regexp, op, result, returnJson, startt=None, force_result=False):
+ def command(
+ self,
+ target,
+ command,
+ regexp,
+ op,
+ result,
+ returnJson,
+ startt=None,
+ force_result=False,
+ ):
global net
if op == "jsoncmp_pass" or op == "jsoncmp_fail":
returnJson = True
@@ -326,7 +336,9 @@ Total %-4d %-4d %d\n\
if strict and (wait_count == 1):
force_result = True
- found = self.command(target, command, regexp, op, result, returnJson, startt, force_result)
+ found = self.command(
+ target, command, regexp, op, result, returnJson, startt, force_result
+ )
if found is not False:
break
@@ -342,6 +354,7 @@ Total %-4d %-4d %d\n\
# initialized by luStart
LUtil = None
+
# entry calls
def luStart(
baseScriptDir=".",
@@ -455,6 +468,7 @@ def luShowFail():
if printed > 0:
logger.error("See %s for details of errors" % LUtil.fout_name)
+
#
# Sets default wait type for luCommand(op="wait) (may be overridden by
# specifying luCommand(op="wait-strict") or luCommand(op="wait-nostrict")).
diff --git a/tests/topotests/lib/micronet_compat.py b/tests/topotests/lib/micronet_compat.py
index c5c2adc54..d648a120a 100644
--- a/tests/topotests/lib/micronet_compat.py
+++ b/tests/topotests/lib/micronet_compat.py
@@ -273,7 +273,7 @@ ff02::2\tip6-allrouters
shellopt = self.cfgopt.get_option_list("--shell")
if "all" in shellopt or "." in shellopt:
- self.run_in_window("bash")
+ self.run_in_window("bash", title="munet")
# This is expected by newer munet CLI code
self.config_dirname = ""
diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py
index dad87440b..ffe81fbd9 100644
--- a/tests/topotests/lib/ospf.py
+++ b/tests/topotests/lib/ospf.py
@@ -302,7 +302,6 @@ def __create_ospf_global(tgen, input_dict, router, build, load_config, ospf):
# ospf gr information
gr_data = ospf_data.setdefault("graceful-restart", {})
if gr_data:
-
if "opaque" in gr_data and gr_data["opaque"]:
cmd = "capability opaque"
if gr_data.setdefault("delete", False):
@@ -710,6 +709,7 @@ def verify_ospf_neighbor(
else:
data_ip = topo["routers"][ospf_nbr]["links"]
data_rid = topo["routers"][ospf_nbr]["ospf"]["router_id"]
+ logger.info("ospf neighbor %s: router-id: %s", router, data_rid)
if ospf_nbr in data_ip:
nbr_details = nbr_data[ospf_nbr]
elif lan:
@@ -728,8 +728,10 @@ def verify_ospf_neighbor(
try:
nh_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[0]
except KeyError:
- errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
- router, nbr_rid, ospf_nbr
+ errormsg = (
+ "[DUT: {}] missing OSPF neighbor {} with router-id {}".format(
+ router, ospf_nbr, nbr_rid
+ )
)
return errormsg
@@ -843,7 +845,6 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False)
return errormsg
for ospf_nbr, nbr_data in ospf_nbr_list.items():
-
try:
data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
except KeyError:
@@ -914,7 +915,6 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False)
return errormsg
continue
else:
-
for router, rnode in tgen.routers().items():
if "ospf6" not in topo["routers"][router]:
continue
@@ -945,7 +945,7 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False)
data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][
"router_id"
]
-
+ logger.info("ospf neighbor %s: router-id: %s", ospf_nbr, data_rid)
if ospf_nbr in data_ip:
nbr_details = nbr_data[ospf_nbr]
elif lan:
@@ -968,8 +968,10 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False)
nh_state = get_index_val.get(neighbor_ip)["state"]
intf_state = get_index_val.get(neighbor_ip)["ifState"]
except TypeError:
- errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
- router, nbr_rid, ospf_nbr
+ errormsg = (
+ "[DUT: {}] missing OSPF neighbor {} with router-id {}".format(
+ router, ospf_nbr, nbr_rid
+ )
)
return errormsg
@@ -1761,7 +1763,6 @@ def verify_ospf6_rib(
continue
if st_rt in ospf_rib_json:
-
st_found = True
found_routes.append(st_rt)
diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py
index 925890b32..e26bdb3af 100644
--- a/tests/topotests/lib/pim.py
+++ b/tests/topotests/lib/pim.py
@@ -593,7 +593,6 @@ def find_rp_details(tgen, topo):
topo_data = topo["routers"]
for router in router_list.keys():
-
if "pim" not in topo_data[router]:
continue
@@ -1495,7 +1494,6 @@ def verify_mroutes(
and data["outboundInterface"] in oil
):
if return_uptime:
-
uptime_dict[grp_addr][src_address] = data["upTime"]
logger.info(
@@ -1917,7 +1915,6 @@ def get_pim_interface_traffic(tgen, input_dict):
for intf, data in input_dict[dut].items():
interface_json = show_pim_intf_traffic_json[intf]
for state in data:
-
# Verify Tx/Rx
if state in interface_json:
output_dict[dut][state] = interface_json[state]
@@ -1990,7 +1987,6 @@ def get_pim6_interface_traffic(tgen, input_dict):
for intf, data in input_dict[dut].items():
interface_json = show_pim_intf_traffic_json[intf]
for state in data:
-
# Verify Tx/Rx
if state in interface_json:
output_dict[dut][state] = interface_json[state]
@@ -3007,7 +3003,6 @@ def verify_pim_upstream_rpf(
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
if "pim" in topo["routers"][dut]:
-
logger.info("[DUT: %s]: Verifying ip pim upstream rpf:", dut)
rnode = tgen.routers()[dut]
@@ -3245,7 +3240,6 @@ def verify_pim_join(
grp_addr = grp_addr.split("/")[0]
for source, data in interface_json[grp_addr].items():
-
# Verify pim join
if pim_join:
if data["group"] == grp_addr and data["channelJoinName"] == "JOIN":
@@ -3338,7 +3332,6 @@ def verify_igmp_config(tgen, input_dict, stats_return=False, expected=True):
rnode = tgen.routers()[dut]
for interface, data in input_dict[dut]["igmp"]["interfaces"].items():
-
statistics = False
report = False
if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]:
@@ -3623,7 +3616,6 @@ def verify_pim_config(tgen, input_dict, expected=True):
rnode = tgen.routers()[dut]
for interface, data in input_dict[dut]["pim"]["interfaces"].items():
-
logger.info("[DUT: %s]: Verifying PIM interface %s detail:", dut, interface)
show_ip_igmp_intf_json = run_frr_cmd(
@@ -3772,7 +3764,6 @@ def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=Tr
elif (
interface_json["pktsIn"] != 0 and interface_json["bytesIn"] != 0
):
-
traffic_dict[traffic_type][interface][
"pktsIn"
] = interface_json["pktsIn"]
@@ -3836,7 +3827,6 @@ def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=Tr
interface_json["pktsOut"] != 0
and interface_json["bytesOut"] != 0
):
-
traffic_dict[traffic_type][interface][
"pktsOut"
] = interface_json["pktsOut"]
@@ -4232,7 +4222,6 @@ def verify_local_igmp_groups(tgen, dut, interface, group_addresses):
group_addresses = [group_addresses]
if interface not in show_ip_local_igmp_json:
-
errormsg = (
"[DUT %s]: Verifying local IGMP group received"
" from interface %s [FAILED]!! " % (dut, interface)
@@ -4319,7 +4308,6 @@ def verify_pim_interface_traffic(tgen, input_dict, return_stats=True, addr_type=
for intf, data in input_dict[dut].items():
interface_json = show_pim_intf_traffic_json[intf]
for state in data:
-
# Verify Tx/Rx
if state in interface_json:
output_dict[dut][state] = interface_json[state]
@@ -4525,7 +4513,6 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True):
for dut in input_dict.keys():
rnode = tgen.routers()[dut]
for interface, data in input_dict[dut]["mld"]["interfaces"].items():
-
statistics = False
report = False
if "statistics" in input_dict[dut]["mld"]["interfaces"][interface]["mld"]:
@@ -5040,7 +5027,6 @@ def verify_pim6_config(tgen, input_dict, expected=True):
rnode = tgen.routers()[dut]
for interface, data in input_dict[dut]["pim6"]["interfaces"].items():
-
logger.info(
"[DUT: %s]: Verifying PIM6 interface %s detail:", dut, interface
)
@@ -5158,7 +5144,6 @@ def verify_local_mld_groups(tgen, dut, interface, group_addresses):
group_addresses = [group_addresses]
if interface not in show_ipv6_local_mld_json["default"]:
-
errormsg = (
"[DUT %s]: Verifying local MLD group received"
" from interface %s [FAILED]!! " % (dut, interface)
diff --git a/tests/topotests/lib/test/test_json.py b/tests/topotests/lib/test/test_json.py
index ae58d9b2d..230c2bd7f 100755
--- a/tests/topotests/lib/test/test_json.py
+++ b/tests/topotests/lib/test/test_json.py
@@ -616,7 +616,6 @@ def test_json_object_asterisk_matching():
def test_json_list_nested_with_objects():
-
dcomplete = [{"key": 1, "list": [123]}, {"key": 2, "list": [123]}]
dsub1 = [{"key": 2, "list": [123]}, {"key": 1, "list": [123]}]
diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py
index 53e6945be..23a6c8699 100644
--- a/tests/topotests/lib/topojson.py
+++ b/tests/topotests/lib/topojson.py
@@ -206,7 +206,6 @@ def build_topo_from_json(tgen, topo=None):
for destRouterLink, data in sorted(
topo["switches"][curSwitch]["links"].items()
):
-
# Loopback interfaces
if "dst_node" in data:
destRouter = data["dst_node"]
@@ -220,7 +219,6 @@ def build_topo_from_json(tgen, topo=None):
destRouter = destRouterLink
if destRouter in listAllRouters:
-
topo["routers"][destRouter]["links"][curSwitch] = deepcopy(
topo["switches"][curSwitch]["links"][destRouterLink]
)
@@ -316,6 +314,7 @@ def build_config_from_json(tgen, topo=None, save_bkup=True):
func_dict = OrderedDict(
[
("vrfs", create_vrf_cfg),
+ ("ospf", create_router_ospf),
("links", create_interfaces_cfg),
("static_routes", create_static_routes),
("prefix_lists", create_prefix_lists),
@@ -325,7 +324,6 @@ def build_config_from_json(tgen, topo=None, save_bkup=True):
("igmp", create_igmp_config),
("mld", create_mld_config),
("bgp", create_router_bgp),
- ("ospf", create_router_ospf),
]
)
@@ -398,7 +396,7 @@ def setup_module_from_json(testfile, json_file=None):
tgen = create_tgen_from_json(testfile, json_file)
# Start routers (and their daemons)
- start_topology(tgen, topo_daemons(tgen))
+ start_topology(tgen)
# Configure routers
build_config_from_json(tgen)
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index 967f09ecb..aeb83d429 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -1469,21 +1469,22 @@ class Router(Node):
if not running:
break
- if not running:
- return ""
-
- logger.warning(
- "%s: sending SIGBUS to: %s", self.name, ", ".join([x[0] for x in running])
- )
- for name, pid in running:
- pidfile = "/var/run/{}/{}.pid".format(self.routertype, name)
- logger.info("%s: killing %s", self.name, name)
- self.cmd("kill -SIGBUS %d" % pid)
- self.cmd("rm -- " + pidfile)
-
- sleep(
- 0.5, "%s: waiting for daemons to exit/core after initial SIGBUS" % self.name
- )
+ if running:
+ logger.warning(
+ "%s: sending SIGBUS to: %s",
+ self.name,
+ ", ".join([x[0] for x in running]),
+ )
+ for name, pid in running:
+ pidfile = "/var/run/{}/{}.pid".format(self.routertype, name)
+ logger.info("%s: killing %s", self.name, name)
+ self.cmd("kill -SIGBUS %d" % pid)
+ self.cmd("rm -- " + pidfile)
+
+ sleep(
+ 0.5,
+ "%s: waiting for daemons to exit/core after initial SIGBUS" % self.name,
+ )
errors = self.checkRouterCores(reportOnce=True)
if self.checkRouterVersion("<", minErrorVersion):
@@ -1526,6 +1527,9 @@ class Router(Node):
"""
# Unfortunately this API allowsfor source to not exist for any and all routers.
+ if source is None:
+ source = f"{daemon}.conf"
+
if source:
head, tail = os.path.split(source)
if not head and not self.path_exists(tail):
@@ -1557,7 +1561,7 @@ class Router(Node):
self.cmd_raises("cp {} {}".format(source, conf_file_mgmt))
self.cmd_raises("cp {} {}".format(source, conf_file))
- if not self.unified_config or daemon == "frr":
+ if not (self.unified_config or daemon == "frr"):
self.cmd_raises("chown {0}:{0} {1}".format(self.routertype, conf_file))
self.cmd_raises("chmod 664 {}".format(conf_file))
@@ -1683,7 +1687,11 @@ class Router(Node):
return self.getLog("out", daemon)
def getLog(self, log, daemon):
- return self.cmd("cat {}/{}/{}.{}".format(self.logdir, self.name, daemon, log))
+ filename = "{}/{}/{}.{}".format(self.logdir, self.name, daemon, log)
+ log = ""
+ with open(filename) as file:
+ log = file.read()
+ return log
def startRouterDaemons(self, daemons=None, tgen=None):
"Starts FRR daemons for this router."
@@ -1841,9 +1849,13 @@ class Router(Node):
logger.info(
"%s: %s %s launched in gdb window", self, self.routertype, daemon
)
- elif daemon in perfds and (self.name in perfds[daemon] or "all" in perfds[daemon]):
+ elif daemon in perfds and (
+ self.name in perfds[daemon] or "all" in perfds[daemon]
+ ):
cmdopt += rediropt
- cmd = " ".join(["perf record {} --".format(perf_options), binary, cmdopt])
+ cmd = " ".join(
+ ["perf record {} --".format(perf_options), binary, cmdopt]
+ )
p = self.popen(cmd)
self.perf_daemons[daemon] = p
if p.poll() and p.returncode:
@@ -1943,7 +1955,7 @@ class Router(Node):
tail_log_files.append("{}/{}/frr.log".format(self.logdir, self.name))
for tailf in tail_log_files:
- self.run_in_window("tail -f " + tailf, title=tailf, background=True)
+ self.run_in_window("tail -n10000 -F " + tailf, title=tailf, background=True)
return ""
diff --git a/tests/topotests/munet/base.py b/tests/topotests/munet/base.py
index eb4b08844..c6ae70e09 100644
--- a/tests/topotests/munet/base.py
+++ b/tests/topotests/munet/base.py
@@ -1241,9 +1241,13 @@ class Commander: # pylint: disable=R0904
# XXX not appropriate for ssh
cmd = ["sudo", "-Eu", os.environ["SUDO_USER"]] + cmd
- if not isinstance(nscmd, str):
- nscmd = shlex.join(nscmd)
- cmd.append(nscmd)
+ if title:
+ cmd.append("-t")
+ cmd.append(title)
+
+ if isinstance(nscmd, str):
+ nscmd = shlex.split(nscmd)
+ cmd.extend(nscmd)
elif "DISPLAY" in os.environ:
cmd = [get_exec_path_host("xterm")]
if "SUDO_USER" in os.environ:
diff --git a/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py
index aa43b977b..45e1bc8db 100755
--- a/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py
+++ b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py
@@ -430,6 +430,144 @@ def test_gr_rt7():
check_routers(restarting="rt7")
+#
+# Test rt1 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt1():
+ logger.info("Test: verify rt1 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt1", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt1", ["ospf6d"])
+
+ expect_grace_lsa(restarting="1.1.1.1", helper="rt2")
+ ensure_gr_is_in_zebra("rt1")
+ check_routers(restarting="rt1")
+
+
+#
+# Test rt2 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt2():
+ logger.info("Test: verify rt2 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt2", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt2", ["ospf6d"])
+
+ expect_grace_lsa(restarting="2.2.2.2", helper="rt1")
+ expect_grace_lsa(restarting="2.2.2.2", helper="rt3")
+ ensure_gr_is_in_zebra("rt2")
+ check_routers(restarting="rt2")
+
+
+#
+# Test rt3 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt3():
+ logger.info("Test: verify rt3 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt3", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt3", ["ospf6d"])
+
+ expect_grace_lsa(restarting="3.3.3.3", helper="rt2")
+ expect_grace_lsa(restarting="3.3.3.3", helper="rt4")
+ expect_grace_lsa(restarting="3.3.3.3", helper="rt6")
+ ensure_gr_is_in_zebra("rt3")
+ check_routers(restarting="rt3")
+
+
+#
+# Test rt4 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt4():
+ logger.info("Test: verify rt4 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt4", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt4", ["ospf6d"])
+
+ expect_grace_lsa(restarting="4.4.4.4", helper="rt3")
+ expect_grace_lsa(restarting="4.4.4.4", helper="rt5")
+ ensure_gr_is_in_zebra("rt4")
+ check_routers(restarting="rt4")
+
+
+#
+# Test rt5 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt5():
+ logger.info("Test: verify rt5 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt5", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt5", ["ospf6d"])
+
+ expect_grace_lsa(restarting="5.5.5.5", helper="rt4")
+ ensure_gr_is_in_zebra("rt5")
+ check_routers(restarting="rt5")
+
+
+#
+# Test rt6 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt6():
+ logger.info("Test: verify rt6 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt6", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt6", ["ospf6d"])
+
+ expect_grace_lsa(restarting="6.6.6.6", helper="rt3")
+ expect_grace_lsa(restarting="6.6.6.6", helper="rt7")
+ ensure_gr_is_in_zebra("rt6")
+ check_routers(restarting="rt6")
+
+
+#
+# Test rt7 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt7():
+ logger.info("Test: verify rt7 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt7", ["ospf6d"], save_config=False)
+ start_router_daemons(tgen, "rt7", ["ospf6d"])
+
+ expect_grace_lsa(restarting="6.6.6.6", helper="rt6")
+ ensure_gr_is_in_zebra("rt7")
+ check_routers(restarting="rt7")
+
+
# Memory leak test template
def test_memory_leak():
"Run the memory leak test and report results."
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
index 9d7a15833..0531e81d4 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
@@ -84,8 +84,8 @@ SUMMARY = {"ipv4": ["11.0.0.0/8", "12.0.0.0/8", "11.0.0.0/24"]}
"""
TOPOOLOGY =
Please view in a fixed-width font such as Courier.
- +---+ A0 +---+
- +R1 +------------+R2 |
+ +---+ A0 +---+
+ |R1 +------------+R2 |
+-+-+- +--++
| -- -- |
| -- A0 -- |
@@ -94,8 +94,8 @@ TOPOOLOGY =
| -- -- |
| -- -- |
+-+-+- +-+-+
- +R0 +-------------+R3 |
- +---+ A0 +---+
+ |R0 +-------------+R3 |
+ +---+ A0 +---+
TESTCASES =
1. OSPF summarisation functionality.
@@ -977,7 +977,7 @@ def test_ospf_type5_summary_tc42_p0(request):
ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"]
- ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ ip_net = str(ipaddress.ip_interface("{}".format(ip)).network)
ospf_summ_r1 = {
"r0": {
"ospf": {"summary-address": [{"prefix": ip_net.split("/")[0], "mask": "8"}]}
@@ -1519,7 +1519,7 @@ def test_ospf_type5_summary_tc45_p0(request):
step("Repeat steps 1 to 10 of summarisation in non Back bone area.")
reset_config_on_routers(tgen)
- step("Change the area id on the interface on R0")
+ step("Change the area id on the interface on R0 to R1 from 0.0.0.0 to 0.0.0.1")
input_dict = {
"r0": {
"links": {
@@ -1549,7 +1549,7 @@ def test_ospf_type5_summary_tc45_p0(request):
result = create_interfaces_cfg(tgen, input_dict)
assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
- step("Change the area id on the interface ")
+ step("Change the area id on the interface on R1 to R0 from 0.0.0.0 to 0.0.0.1")
input_dict = {
"r1": {
"links": {
diff --git a/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
index ca2a3b287..73185d501 100755
--- a/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
+++ b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
@@ -434,6 +434,144 @@ def test_gr_rt7():
check_routers(restarting="rt7")
+#
+# Test rt1 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt1():
+ logger.info("Test: verify rt1 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt1", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt1", ["ospfd"])
+
+ expect_grace_lsa(restarting="1.1.1.1", area="0.0.0.1", helper="rt2")
+ ensure_gr_is_in_zebra("rt1")
+ check_routers(restarting="rt1")
+
+
+#
+# Test rt2 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt2():
+ logger.info("Test: verify rt2 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt2", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt2", ["ospfd"])
+
+ expect_grace_lsa(restarting="2.2.2.2", area="0.0.0.1", helper="rt1")
+ expect_grace_lsa(restarting="2.2.2.2", area="0.0.0.0", helper="rt3")
+ ensure_gr_is_in_zebra("rt2")
+ check_routers(restarting="rt2")
+
+
+#
+# Test rt3 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt3():
+ logger.info("Test: verify rt3 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt3", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt3", ["ospfd"])
+
+ expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt2")
+ expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt4")
+ expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt6")
+ ensure_gr_is_in_zebra("rt3")
+ check_routers(restarting="rt3")
+
+
+#
+# Test rt4 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt4():
+ logger.info("Test: verify rt4 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt4", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt4", ["ospfd"])
+
+ expect_grace_lsa(restarting="4.4.4.4", area="0.0.0.0", helper="rt3")
+ expect_grace_lsa(restarting="4.4.4.4", area="0.0.0.2", helper="rt5")
+ ensure_gr_is_in_zebra("rt4")
+ check_routers(restarting="rt4")
+
+
+#
+# Test rt5 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt5():
+ logger.info("Test: verify rt5 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt5", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt5", ["ospfd"])
+
+ expect_grace_lsa(restarting="5.5.5.5", area="0.0.0.2", helper="rt4")
+ ensure_gr_is_in_zebra("rt5")
+ check_routers(restarting="rt5")
+
+
+#
+# Test rt6 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt6():
+ logger.info("Test: verify rt6 performing an unplanned graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt6", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt6", ["ospfd"])
+
+ expect_grace_lsa(restarting="6.6.6.6", area="0.0.0.0", helper="rt3")
+ expect_grace_lsa(restarting="6.6.6.6", area="0.0.0.3", helper="rt7")
+ ensure_gr_is_in_zebra("rt6")
+ check_routers(restarting="rt6")
+
+
+#
+# Test rt7 performing an unplanned graceful restart
+#
+def test_unplanned_gr_rt7():
+ logger.info("Test: verify rt7 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ kill_router_daemons(tgen, "rt7", ["ospfd"], save_config=False)
+ start_router_daemons(tgen, "rt7", ["ospfd"])
+
+ expect_grace_lsa(restarting="7.7.7.7", area="0.0.0.3", helper="rt6")
+ ensure_gr_is_in_zebra("rt7")
+ check_routers(restarting="rt7")
+
+
# Memory leak test template
def test_memory_leak():
"Run the memory leak test and report results."
diff --git a/tests/topotests/ospf_metric_propagation/r1/frr.conf b/tests/topotests/ospf_metric_propagation/r1/frr.conf
index 9f388dbd0..85230494d 100644
--- a/tests/topotests/ospf_metric_propagation/r1/frr.conf
+++ b/tests/topotests/ospf_metric_propagation/r1/frr.conf
@@ -8,18 +8,18 @@ interface r1-eth0
ip address 10.0.1.1/24
ip ospf cost 100
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface r1-eth1 vrf blue
ip address 10.0.10.1/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
!
interface r1-eth2 vrf green
ip address 10.0.91.1/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
!
router ospf
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json
index e3a5cc410..4f1ced81f 100644
--- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json
@@ -4,7 +4,6 @@
"prefix":"10.0.94.0/24",
"prefixLen":24,
"protocol":"bgp",
- "vrfId":9,
"vrfName":"green",
"selected":true,
"destSelected":true,
@@ -25,7 +24,6 @@
"fib":true,
"ip":"10.0.10.5",
"afi":"ipv4",
- "interfaceIndex":6,
"interfaceName":"r1-eth1",
"vrf":"blue",
"active":true,
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json
index f3597bf45..882d3ca4f 100644
--- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json
@@ -4,7 +4,6 @@
"prefix":"10.0.94.0/24",
"prefixLen":24,
"protocol":"bgp",
- "vrfId":9,
"vrfName":"green",
"selected":true,
"destSelected":true,
@@ -25,7 +24,6 @@
"fib":true,
"ip":"10.0.1.2",
"afi":"ipv4",
- "interfaceIndex":5,
"interfaceName":"r1-eth0",
"vrf":"default",
"active":true,
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json
index eebcab83e..cd528459a 100644
--- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json
@@ -4,7 +4,6 @@
"prefix":"10.0.94.0/24",
"prefixLen":24,
"protocol":"bgp",
- "vrfId":9,
"vrfName":"green",
"selected":true,
"destSelected":true,
@@ -25,7 +24,6 @@
"fib":true,
"ip":"10.0.1.2",
"afi":"ipv4",
- "interfaceIndex":5,
"interfaceName":"r1-eth0",
"vrf":"default",
"active":true,
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json
index d0e3d816d..133f37549 100644
--- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json
@@ -4,7 +4,6 @@
"prefix":"10.0.94.0/24",
"prefixLen":24,
"protocol":"bgp",
- "vrfId":9,
"vrfName":"green",
"selected":true,
"destSelected":true,
@@ -25,7 +24,6 @@
"fib":true,
"ip":"10.0.1.2",
"afi":"ipv4",
- "interfaceIndex":5,
"interfaceName":"r1-eth0",
"vrf":"default",
"active":true,
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json
index 989ccf779..5d8050902 100644
--- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json
@@ -4,7 +4,6 @@
"prefix":"10.0.94.0/24",
"prefixLen":24,
"protocol":"bgp",
- "vrfId":9,
"vrfName":"green",
"selected":true,
"destSelected":true,
@@ -25,7 +24,6 @@
"fib":true,
"ip":"10.0.1.2",
"afi":"ipv4",
- "interfaceIndex":5,
"interfaceName":"r1-eth0",
"vrf":"default",
"active":true,
diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json
index 84b11886e..1b59707b9 100644
--- a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json
+++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json
@@ -4,7 +4,6 @@
"prefix":"10.0.94.0/24",
"prefixLen":24,
"protocol":"bgp",
- "vrfId":9,
"vrfName":"green",
"selected":true,
"destSelected":true,
@@ -25,7 +24,6 @@
"fib":true,
"ip":"10.0.10.5",
"afi":"ipv4",
- "interfaceIndex":6,
"interfaceName":"r1-eth1",
"vrf":"blue",
"active":true,
diff --git a/tests/topotests/ospf_metric_propagation/r2/frr.conf b/tests/topotests/ospf_metric_propagation/r2/frr.conf
index 469ae5da8..e67a374ff 100644
--- a/tests/topotests/ospf_metric_propagation/r2/frr.conf
+++ b/tests/topotests/ospf_metric_propagation/r2/frr.conf
@@ -8,18 +8,18 @@ interface r2-eth0
ip address 10.0.1.2/24
ip ospf cost 100
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface r2-eth1 vrf blue
ip address 10.0.20.2/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface r2-eth2 vrf green
ip address 10.0.70.2/24
ip ospf cost 1000
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
router ospf
ospf router-id 10.0.255.2
diff --git a/tests/topotests/ospf_metric_propagation/r3/frr.conf b/tests/topotests/ospf_metric_propagation/r3/frr.conf
index 8dbbaf0fc..175851d42 100644
--- a/tests/topotests/ospf_metric_propagation/r3/frr.conf
+++ b/tests/topotests/ospf_metric_propagation/r3/frr.conf
@@ -8,18 +8,18 @@ interface r3-eth0
ip address 10.0.3.3/24
ip ospf cost 100
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface r3-eth1 vrf blue
ip address 10.0.30.3/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface r3-eth2 vrf green
ip address 10.0.80.3/24
ip ospf cost 1000
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
router ospf
ospf router-id 10.0.255.3
diff --git a/tests/topotests/ospf_metric_propagation/r4/frr.conf b/tests/topotests/ospf_metric_propagation/r4/frr.conf
index af1005063..70a47e34f 100644
--- a/tests/topotests/ospf_metric_propagation/r4/frr.conf
+++ b/tests/topotests/ospf_metric_propagation/r4/frr.conf
@@ -8,17 +8,17 @@ interface r4-eth0
ip address 10.0.3.4/24
ip ospf cost 100
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface r4-eth1 vrf blue
ip address 10.0.40.4/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface r4-eth2 vrf green
ip address 10.0.94.4/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
router ospf
ospf router-id 10.0.255.4
diff --git a/tests/topotests/ospf_metric_propagation/ra/frr.conf b/tests/topotests/ospf_metric_propagation/ra/frr.conf
index 0bc2ec567..7be9e5c33 100644
--- a/tests/topotests/ospf_metric_propagation/ra/frr.conf
+++ b/tests/topotests/ospf_metric_propagation/ra/frr.conf
@@ -7,17 +7,17 @@ ip forwarding
interface ra-eth0
ip address 10.0.50.5/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface ra-eth1
ip address 10.0.10.5/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface ra-eth2
ip address 10.0.20.5/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
router ospf
ospf router-id 10.0.255.5
diff --git a/tests/topotests/ospf_metric_propagation/rb/frr.conf b/tests/topotests/ospf_metric_propagation/rb/frr.conf
index 6f540d125..a7dbf8227 100644
--- a/tests/topotests/ospf_metric_propagation/rb/frr.conf
+++ b/tests/topotests/ospf_metric_propagation/rb/frr.conf
@@ -7,17 +7,17 @@ ip forwarding
interface rb-eth0
ip address 10.0.50.6/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface rb-eth1
ip address 10.0.30.6/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface rb-eth2
ip address 10.0.40.6/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
router ospf
ospf router-id 10.0.255.6
diff --git a/tests/topotests/ospf_metric_propagation/rc/frr.conf b/tests/topotests/ospf_metric_propagation/rc/frr.conf
index 9fc0ef718..f5a2ed7c4 100644
--- a/tests/topotests/ospf_metric_propagation/rc/frr.conf
+++ b/tests/topotests/ospf_metric_propagation/rc/frr.conf
@@ -7,12 +7,12 @@ ip forwarding
interface rc-eth0
ip address 10.0.70.7/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
interface rc-eth1
ip address 10.0.80.7/24
ip ospf hello-interval 1
- ip ospf dead-interval 3
+ ip ospf dead-interval 30
!
router ospf
ospf router-id 10.0.255.7
diff --git a/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py
index 709e07649..085eb1f9c 100644
--- a/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py
+++ b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py
@@ -71,7 +71,6 @@ def build_topo(tgen):
tgen.add_router("h1")
tgen.add_router("h2")
-
# Interconect router 1, 2
switch = tgen.add_switch("s1-2")
switch.add_link(tgen.gears["r1"])
@@ -127,6 +126,7 @@ def build_topo(tgen):
switch.add_link(tgen.gears["r3"])
switch.add_link(tgen.gears["rc"])
+
def setup_module(mod):
logger.info("OSPF Metric Propagation:\n {}".format(TOPOLOGY))
@@ -148,8 +148,12 @@ def setup_module(mod):
for cmd in vrf_setup_cmds:
tgen.net["r{}".format(routern)].cmd(cmd)
for routern in range(1, 5):
- tgen.net["r{}".format(routern)].cmd("ip link set dev r{}-eth1 vrf blue up".format(routern))
- tgen.net["r{}".format(routern)].cmd("ip link set dev r{}-eth2 vrf green up".format(routern))
+ tgen.net["r{}".format(routern)].cmd(
+ "ip link set dev r{}-eth1 vrf blue up".format(routern)
+ )
+ tgen.net["r{}".format(routern)].cmd(
+ "ip link set dev r{}-eth2 vrf green up".format(routern)
+ )
for rname, router in router_list.items():
logger.info("Loading router %s" % rname)
@@ -181,7 +185,7 @@ def test_all_links_up():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=40, wait=1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "r1 JSON output mismatches"
assert result is None, assertmsg
@@ -202,7 +206,7 @@ def test_link_1_down():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "r1 JSON output mismatches"
assert result is None, assertmsg
@@ -216,6 +220,10 @@ def test_link_1_2_down():
pytest.skip("skipped because of router(s) failure")
tgen.net["r2"].cmd("ip link set dev r2-eth1 down")
+ tgen.net["r1"].cmd("ip link set dev r1-eth0 down")
+ tgen.net["r2"].cmd("ip link set dev r2-eth2 down")
+ tgen.net["r2"].cmd("ip link set dev r2-eth2 up")
+ tgen.net["r1"].cmd("ip link set dev r1-eth0 up")
r1 = tgen.gears["r1"]
json_file = "{}/r1/show_ip_route-3.json".format(CWD)
@@ -223,7 +231,7 @@ def test_link_1_2_down():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "r1 JSON output mismatches"
assert result is None, assertmsg
@@ -236,11 +244,11 @@ def test_link_1_2_3_down():
if tgen.routers_have_failure():
pytest.skip("skipped because of router(s) failure")
- tgen.net["r3"].cmd("ip link set dev r3-eth0 down")
tgen.net["r3"].cmd("ip link set dev r3-eth1 down")
- # ospf dead-interval is set to 3 seconds, wait 5 seconds to clear the neighbor
- sleep(5)
+ tgen.net["r1"].cmd("ip link set dev r1-eth0 down")
+ tgen.net["r3"].cmd("ip link set dev r3-eth0 down")
tgen.net["r3"].cmd("ip link set dev r3-eth0 up")
+ tgen.net["r1"].cmd("ip link set dev r1-eth0 up")
r1 = tgen.gears["r1"]
json_file = "{}/r1/show_ip_route-4.json".format(CWD)
@@ -248,11 +256,12 @@ def test_link_1_2_3_down():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "r1 JSON output mismatches"
assert result is None, assertmsg
+
def test_link_1_2_3_4_down():
"Test path R1 -> R2 -> Rc -> R3 -> R4"
tgen = get_topogen()
@@ -268,11 +277,12 @@ def test_link_1_2_3_4_down():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "r1 JSON output mismatches"
assert result is None, assertmsg
+
def test_link_1_2_4_down():
"Test path R1 -> R2 -> Rc -> R3 -> R4"
tgen = get_topogen()
@@ -289,11 +299,12 @@ def test_link_1_2_4_down():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "r1 JSON output mismatches"
assert result is None, assertmsg
+
def test_link_1_4_down():
"Test path R1 -> R2 -> Ra -> Rb -> R3 -> R4"
tgen = get_topogen()
@@ -310,7 +321,7 @@ def test_link_1_4_down():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "r1 JSON output mismatches"
assert result is None, assertmsg
@@ -332,7 +343,7 @@ def test_link_4_down():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "r1 JSON output mismatches"
assert result is None, assertmsg
@@ -354,7 +365,7 @@ def test_link_1_2_3_4_up():
test_func = partial(
topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected
)
- _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assertmsg = "r1 JSON output mismatches"
assert result is None, assertmsg
diff --git a/tests/topotests/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py
index b01c96226..2e8bea265 100644
--- a/tests/topotests/ospfapi/test_ospf_clientapi.py
+++ b/tests/topotests/ospfapi/test_ospf_clientapi.py
@@ -20,7 +20,15 @@ from datetime import datetime, timedelta
import pytest
-from lib.common_config import retry, run_frr_cmd, step
+from lib.common_config import (
+ retry,
+ run_frr_cmd,
+ step,
+ kill_router_daemons,
+ start_router_daemons,
+ shutdown_bringup_interface,
+)
+
from lib.micronet import Timeout, comm_error
from lib.topogen import Topogen, TopoRouter
from lib.topotest import interface_set_status, json_cmp
@@ -936,6 +944,191 @@ def test_ospf_opaque_delete_data3(tgen):
_test_opaque_add_del(tgen, apibin)
+def _test_opaque_add_restart_add(tgen, apibin):
+ "Test adding an opaque LSA and then restarting ospfd"
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ p = None
+ pread = None
+ # Log to our stdin, stderr
+ pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+")
+ try:
+ step("reachable: check for add notification")
+ pread = r2.popen(
+ ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"],
+ encoding=None, # don't buffer
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ p = r1.popen(
+ [
+ apibin,
+ "-v",
+ "add,10,1.2.3.4,231,1",
+ "add,10,1.2.3.4,231,1,feedaceebeef",
+ "wait, 5",
+ "add,10,1.2.3.4,231,1,feedaceedeadbeef",
+ "wait, 5",
+ "add,10,1.2.3.4,231,1,feedaceebaddbeef",
+ "wait, 5",
+ ]
+ )
+ add_input_dict = {
+ "areas": {
+ "1.2.3.4": {
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000004",
+ "checksum": "3128",
+ },
+ ],
+ "areaLocalOpaqueLsaCount": 1,
+ },
+ },
+ }
+ step("Check for add LSAs")
+ json_cmd = "show ip ospf da json"
+ assert verify_ospf_database(tgen, r1, add_input_dict, json_cmd) is None
+ assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None
+
+ step("Shutdown the interface on r1 to isolate it for r2")
+ shutdown_bringup_interface(tgen, "r1", "r1-eth0", False)
+
+ time.sleep(2)
+ step("Reset the client")
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ step("Kill ospfd on R1")
+ kill_router_daemons(tgen, "r1", ["ospfd"])
+ time.sleep(2)
+
+ step("Bring ospfd on R1 back up")
+ start_router_daemons(tgen, "r1", ["ospfd"])
+
+ p = r1.popen(
+ [
+ apibin,
+ "-v",
+ "add,10,1.2.3.4,231,1",
+ "add,10,1.2.3.4,231,1,feedaceecafebeef",
+ "wait, 5",
+ ]
+ )
+
+ step("Bring the interface on r1 back up for connection to r2")
+ shutdown_bringup_interface(tgen, "r1", "r1-eth0", True)
+
+ step("Verify area opaque LSA refresh")
+ json_cmd = "show ip ospf da opaque-area json"
+ add_detail_input_dict = {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000005",
+ "checksum": "a87e",
+ "length": 28,
+ "opaqueDataLength": 8,
+ },
+ ],
+ },
+ },
+ }
+ assert verify_ospf_database(tgen, r1, add_detail_input_dict, json_cmd) is None
+ assert verify_ospf_database(tgen, r2, add_detail_input_dict, json_cmd) is None
+
+ step("Shutdown the interface on r1 to isolate it for r2")
+ shutdown_bringup_interface(tgen, "r1", "r1-eth0", False)
+
+ time.sleep(2)
+ step("Reset the client")
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ step("Kill ospfd on R1")
+ kill_router_daemons(tgen, "r1", ["ospfd"])
+ time.sleep(2)
+
+ step("Bring ospfd on R1 back up")
+ start_router_daemons(tgen, "r1", ["ospfd"])
+
+ step("Bring the interface on r1 back up for connection to r2")
+ shutdown_bringup_interface(tgen, "r1", "r1-eth0", True)
+
+ step("Verify area opaque LSA Purging")
+ json_cmd = "show ip ospf da opaque-area json"
+ add_detail_input_dict = {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "lsaAge": 3600,
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000005",
+ "checksum": "a87e",
+ "length": 28,
+ "opaqueDataLength": 8,
+ },
+ ],
+ },
+ },
+ }
+ assert verify_ospf_database(tgen, r1, add_detail_input_dict, json_cmd) is None
+ assert verify_ospf_database(tgen, r2, add_detail_input_dict, json_cmd) is None
+ step("Verify Area Opaque LSA removal after timeout (60 seconds)")
+ time.sleep(60)
+ json_cmd = "show ip ospf da opaque-area json"
+ timeout_detail_input_dict = {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [],
+ },
+ },
+ }
+ assert (
+ verify_ospf_database(tgen, r1, timeout_detail_input_dict, json_cmd) is None
+ )
+ assert (
+ verify_ospf_database(tgen, r2, timeout_detail_input_dict, json_cmd) is None
+ )
+
+ except Exception:
+ if p:
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+ p = None
+ raise
+ finally:
+ if pread:
+ pread.terminate()
+ pread.wait()
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_opaque_restart(tgen):
+ apibin = os.path.join(CLIENTDIR, "ospfclient.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
+ logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
+ _test_opaque_add_restart_add(tgen, apibin)
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
index b0e56e619..49c25ab8f 100644
--- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
@@ -565,7 +565,7 @@ def test_ospfv3_type5_summary_tc42_p0(request):
ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
- ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ ip_net = str(ipaddress.ip_interface("{}".format(ip)).network)
ospf_summ_r1 = {
"r0": {
"ospf6": {
diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini
index f779bf0a7..0062cf3de 100644
--- a/tests/topotests/pytest.ini
+++ b/tests/topotests/pytest.ini
@@ -1,7 +1,7 @@
# Skip pytests example directory
[pytest]
-asyncio_mode = auto
+# asyncio_mode = auto
# We always turn this on inside conftest.py, default shown
# addopts = --junitxml=<rundir>/topotests.xml
diff --git a/tests/topotests/rip_allow_ecmp/r4/frr.conf b/tests/topotests/rip_allow_ecmp/r4/frr.conf
new file mode 100644
index 000000000..995c2beca
--- /dev/null
+++ b/tests/topotests/rip_allow_ecmp/r4/frr.conf
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r4-eth0
+ ip address 192.168.1.4/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_allow_ecmp/r5/frr.conf b/tests/topotests/rip_allow_ecmp/r5/frr.conf
new file mode 100644
index 000000000..57a06ec0e
--- /dev/null
+++ b/tests/topotests/rip_allow_ecmp/r5/frr.conf
@@ -0,0 +1,13 @@
+!
+int lo
+ ip address 10.10.10.1/32
+!
+int r5-eth0
+ ip address 192.168.1.5/24
+!
+router rip
+ network 192.168.1.0/24
+ network 10.10.10.1/32
+ timers basic 5 15 10
+exit
+
diff --git a/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py
index acc0aea9e..7d958fd49 100644
--- a/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py
+++ b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py
@@ -21,12 +21,13 @@ sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
pytestmark = [pytest.mark.ripd]
def setup_module(mod):
- topodef = {"s1": ("r1", "r2", "r3")}
+ topodef = {"s1": ("r1", "r2", "r3", "r4", "r5")}
tgen = Topogen(topodef, mod.__name__)
tgen.start_topology()
@@ -102,11 +103,13 @@ def test_rip_allow_ecmp():
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip rip`"
- def _show_routes():
+ def _show_routes(nh_num):
output = json.loads(r1.vtysh_cmd("show ip route json"))
expected = {
"10.10.10.1/32": [
{
+ "internalNextHopNum": nh_num,
+ "internalNextHopActiveNum": nh_num,
"nexthops": [
{
"ip": "192.168.1.2",
@@ -116,15 +119,36 @@ def test_rip_allow_ecmp():
"ip": "192.168.1.3",
"active": True,
},
- ]
+ ],
}
]
}
return topotest.json_cmp(output, expected)
- test_func = functools.partial(_show_routes)
+ test_func = functools.partial(_show_routes, 4)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
- assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip route`"
+ assert result is None, "Can't see 10.10.10.1/32 as multipath (4) in `show ip route`"
+
+ step(
+ "Configure allow-ecmp 2, ECMP group routes SHOULD have next-hops with the lowest IPs"
+ )
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ router rip
+ allow-ecmp 2
+ """
+ )
+
+ test_func = functools.partial(_show_rip_routes)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert (
+ result is None
+ ), "Can't see 10.10.10.1/32 as ECMP with the lowest next-hop IPs"
+
+ test_func = functools.partial(_show_routes, 2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assert result is None, "Can't see 10.10.10.1/32 as multipath (2) in `show ip route`"
if __name__ == "__main__":
diff --git a/yang/example/ripd.json b/yang/example/ripd.json
index 00040622e..799f46a6d 100644
--- a/yang/example/ripd.json
+++ b/yang/example/ripd.json
@@ -23,7 +23,7 @@
"instance": [
{
"vrf": "default",
- "allow-ecmp": "true",
+ "allow-ecmp": 1,
"distance": {
"source": [
{
diff --git a/yang/example/ripd.xml b/yang/example/ripd.xml
index 2feddde2d..dad83619c 100644
--- a/yang/example/ripd.xml
+++ b/yang/example/ripd.xml
@@ -19,7 +19,7 @@
<ripd xmlns="http://frrouting.org/yang/ripd">
<instance>
<vrf>default</vrf>
- <allow-ecmp>true</allow-ecmp>
+ <allow-ecmp>1</allow-ecmp>
<static-route>10.0.1.0/24</static-route>
<distance>
<source>
diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang
index 23e5b0227..b557cabb2 100644
--- a/yang/frr-bgp-route-map.yang
+++ b/yang/frr-bgp-route-map.yang
@@ -23,6 +23,10 @@ module frr-bgp-route-map {
prefix rt-types;
}
+ import frr-route-types {
+ prefix frr-route-types;
+ }
+
organization
"Free Range Routing";
contact
@@ -168,6 +172,12 @@ module frr-bgp-route-map {
"Match IPv6 next hop address";
}
+ identity source-protocol {
+ base frr-route-map:rmap-match-type;
+ description
+ "Match protocol via which the route was learnt";
+ }
+
identity distance {
base frr-route-map:rmap-set-type;
description
@@ -759,6 +769,13 @@ module frr-bgp-route-map {
"IPv6 address";
}
}
+
+ case source-protocol {
+ when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:source-protocol')";
+ leaf source-protocol {
+ type frr-route-types:frr-route-types;
+ }
+ }
}
augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:rmap-set-action/frr-route-map:set-action" {
diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang
index a4bf50d95..5f85a4cab 100644
--- a/yang/frr-ripd.yang
+++ b/yang/frr-ripd.yang
@@ -119,8 +119,8 @@ module frr-ripd {
"VRF name.";
}
leaf allow-ecmp {
- type boolean;
- default "false";
+ type uint8;
+ default 0;
description
"Allow equal-cost multi-path.";
}
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index d2fa85eb6..6767000f3 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -548,6 +548,10 @@ void zebra_interface_address_add_update(struct interface *ifp,
client, ifp, ifc);
}
}
+ /* interface associated NHGs may have been deleted,
+ * re-sync zebra -> dplane NHGs
+ */
+ zebra_interface_nhg_reinstall(ifp);
}
/* Interface address deletion. */
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index f6c1fdd78..3653f7152 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -3785,18 +3785,12 @@ tc_qdisc_update_internal(enum dplane_op_e op,
/* Obtain context block */
ctx = dplane_ctx_alloc();
- if (!ctx) {
- ret = ENOMEM;
- goto done;
- }
-
/* Init context with info from zebra data structs */
ret = dplane_ctx_tc_qdisc_init(ctx, op, qdisc);
if (ret == AOK)
ret = dplane_update_enqueue(ctx);
-done:
/* Update counter */
atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1,
memory_order_relaxed);
@@ -3822,18 +3816,12 @@ tc_class_update_internal(enum dplane_op_e op, struct zebra_tc_class *class)
/* Obtain context block */
ctx = dplane_ctx_alloc();
- if (!ctx) {
- ret = ENOMEM;
- goto done;
- }
-
/* Init context with info from zebra data structs */
ret = dplane_ctx_tc_class_init(ctx, op, class);
if (ret == AOK)
ret = dplane_update_enqueue(ctx);
-done:
/* Update counter */
atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1,
memory_order_relaxed);
@@ -3859,18 +3847,12 @@ tc_filter_update_internal(enum dplane_op_e op, struct zebra_tc_filter *filter)
/* Obtain context block */
ctx = dplane_ctx_alloc();
- if (!ctx) {
- ret = ENOMEM;
- goto done;
- }
-
/* Init context with info from zebra data structs */
ret = dplane_ctx_tc_filter_init(ctx, op, filter);
if (ret == AOK)
ret = dplane_update_enqueue(ctx);
-done:
/* Update counter */
atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1,
memory_order_relaxed);
@@ -3943,16 +3925,11 @@ dplane_nexthop_update_internal(struct nhg_hash_entry *nhe, enum dplane_op_e op)
/* Obtain context block */
ctx = dplane_ctx_alloc();
- if (!ctx) {
- ret = ENOMEM;
- goto done;
- }
ret = dplane_ctx_nexthop_init(ctx, op, nhe);
if (ret == AOK)
ret = dplane_update_enqueue(ctx);
-done:
/* Update counter */
atomic_fetch_add_explicit(&zdplane_info.dg_nexthops_in, 1,
memory_order_relaxed);
@@ -4083,8 +4060,6 @@ dplane_route_notif_update(struct route_node *rn,
goto done;
new_ctx = dplane_ctx_alloc();
- if (new_ctx == NULL)
- goto done;
/* Init context with info from zebra data structs */
dplane_ctx_route_init(new_ctx, op, rn, re);
@@ -4216,10 +4191,6 @@ dplane_lsp_notif_update(struct zebra_lsp *lsp, enum dplane_op_e op,
/* Obtain context block */
ctx = dplane_ctx_alloc();
- if (ctx == NULL) {
- ret = ENOMEM;
- goto done;
- }
/* Copy info from zebra LSP */
ret = dplane_ctx_lsp_init(ctx, op, lsp);
@@ -4589,16 +4560,11 @@ dplane_intf_update_internal(const struct interface *ifp, enum dplane_op_e op)
/* Obtain context block */
ctx = dplane_ctx_alloc();
- if (!ctx) {
- ret = ENOMEM;
- goto done;
- }
ret = dplane_ctx_intf_init(ctx, op, ifp);
if (ret == AOK)
ret = dplane_update_enqueue(ctx);
-done:
/* Update counter */
atomic_fetch_add_explicit(&zdplane_info.dg_intfs_in, 1,
memory_order_relaxed);
@@ -5337,8 +5303,10 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link,
ctx = dplane_ctx_alloc();
- if (!ifp)
- return result;
+ if (!ifp) {
+ ret = EINVAL;
+ goto done;
+ }
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
zlog_debug("init dplane ctx %s: if %s link %s%s",
@@ -5350,8 +5318,11 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link,
ctx->zd_op = op;
ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
zns = zebra_ns_lookup(ifp->vrf->vrf_id);
- if (!zns)
- return result;
+ if (!zns) {
+ ret = EINVAL;
+ goto done;
+ }
+
dplane_ctx_ns_init(ctx, zns, false);
dplane_ctx_set_ifname(ctx, ifp->name);
@@ -5370,6 +5341,7 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link,
/* Enqueue context for processing */
ret = dplane_update_enqueue(ctx);
+done:
/* Update counter */
atomic_fetch_add_explicit(&zdplane_info.dg_gre_set_in, 1,
memory_order_relaxed);
@@ -5764,6 +5736,21 @@ void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov,
memory_order_relaxed);
}
+static struct zebra_dplane_ctx *
+dplane_provider_dequeue_out_ctx(struct zebra_dplane_provider *prov)
+{
+ struct zebra_dplane_ctx *ctx;
+
+ ctx = dplane_ctx_list_pop(&(prov->dp_ctx_out_list));
+ if (!ctx)
+ return NULL;
+
+ atomic_fetch_sub_explicit(&(prov->dp_out_queued), 1,
+ memory_order_relaxed);
+
+ return ctx;
+}
+
/*
* Accessor for provider object
*/
@@ -6791,7 +6778,7 @@ static void dplane_thread_loop(struct event *event)
dplane_provider_lock(prov);
while (counter < limit) {
- ctx = dplane_ctx_list_pop(&(prov->dp_ctx_out_list));
+ ctx = dplane_provider_dequeue_out_ctx(prov);
if (ctx) {
dplane_ctx_list_add_tail(&work_list, ctx);
counter++;
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 8616b1405..96e021292 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -1125,13 +1125,23 @@ static void zebra_nhg_handle_uninstall(struct nhg_hash_entry *nhe)
zebra_nhg_free(nhe);
}
-static void zebra_nhg_handle_install(struct nhg_hash_entry *nhe)
+static void zebra_nhg_handle_install(struct nhg_hash_entry *nhe, bool install)
{
/* Update validity of groups depending on it */
struct nhg_connected *rb_node_dep;
- frr_each_safe(nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep)
+ frr_each_safe (nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) {
zebra_nhg_set_valid(rb_node_dep->nhe);
+ /* install dependent NHG into kernel */
+ if (install) {
+ if (IS_ZEBRA_DEBUG_NHG_DETAIL)
+ zlog_debug(
+ "%s nh id %u (flags 0x%x) associated dependent NHG %pNG install",
+ __func__, nhe->id, nhe->flags,
+ rb_node_dep->nhe);
+ zebra_nhg_install_kernel(rb_node_dep->nhe);
+ }
+ }
}
/*
@@ -3080,6 +3090,12 @@ void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe)
/* Resolve it first */
nhe = zebra_nhg_resolve(nhe);
+ if (zebra_nhg_set_valid_if_active(nhe)) {
+ if (IS_ZEBRA_DEBUG_NHG_DETAIL)
+ zlog_debug("%s: valid flag set for nh %pNG", __func__,
+ nhe);
+ }
+
/* Make sure all depends are installed/queued */
frr_each(nhg_connected_tree, &nhe->nhg_depends, rb_node_dep) {
zebra_nhg_install_kernel(rb_node_dep->nhe);
@@ -3106,7 +3122,7 @@ void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe)
break;
case ZEBRA_DPLANE_REQUEST_SUCCESS:
SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
- zebra_nhg_handle_install(nhe);
+ zebra_nhg_handle_install(nhe, false);
break;
}
}
@@ -3180,7 +3196,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
- zebra_nhg_handle_install(nhe);
+ zebra_nhg_handle_install(nhe, true);
/* If daemon nhg, send it an update */
if (PROTO_OWNED(nhe))
@@ -3651,3 +3667,66 @@ static ssize_t printfrr_nhghe(struct fbuf *buf, struct printfrr_eargs *ea,
ret += bputs(buf, "]");
return ret;
}
+
+/*
+ * On interface add the nexthop that resolves to this intf needs
+ * a re-install. There are following scenarios when the nexthop group update
+ * gets skipped:
+ * 1. When upper level protocol sends removal of NHG, there is
+ * timer running to keep NHG for 180 seconds, during this interval, same route
+ * with same set of nexthops installation is given , the same NHG is used
+ * but since NHG is not reinstalled on interface address add, it is not aware
+ * in Dplan/Kernel.
+ * 2. Due to a quick port flap due to interface add and delete
+ * to be processed in same queue one after another. Zebra believes that
+ * there is no change in nhg in this case. Hence this re-install will
+ * make sure the nexthop group gets updated to Dplan/Kernel.
+ */
+void zebra_interface_nhg_reinstall(struct interface *ifp)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+ struct zebra_if *zif = ifp->info;
+ struct nexthop *nh;
+
+ if (IS_ZEBRA_DEBUG_NHG_DETAIL)
+ zlog_debug(
+ "%s: Installing interface %s associated NHGs into kernel",
+ __func__, ifp->name);
+
+ frr_each (nhg_connected_tree, &zif->nhg_dependents, rb_node_dep) {
+ nh = rb_node_dep->nhe->nhg.nexthop;
+ if (zebra_nhg_set_valid_if_active(rb_node_dep->nhe)) {
+ if (IS_ZEBRA_DEBUG_NHG_DETAIL)
+ zlog_debug(
+ "%s: Setting the valid flag for nhe %pNG, interface: %s",
+ __func__, rb_node_dep->nhe, ifp->name);
+ }
+ /* Check for singleton NHG associated to interface */
+ if (nexthop_is_ifindex_type(nh) &&
+ zebra_nhg_depends_is_empty(rb_node_dep->nhe)) {
+ struct nhg_connected *rb_node_dependent;
+
+ if (IS_ZEBRA_DEBUG_NHG)
+ zlog_debug(
+ "%s install nhe %pNG nh type %u flags 0x%x",
+ __func__, rb_node_dep->nhe, nh->type,
+ rb_node_dep->nhe->flags);
+ zebra_nhg_install_kernel(rb_node_dep->nhe);
+
+ /* mark depedent uninstall, when interface associated
+ * singleton is installed, install depedent
+ */
+ frr_each_safe (nhg_connected_tree,
+ &rb_node_dep->nhe->nhg_dependents,
+ rb_node_dependent) {
+ if (IS_ZEBRA_DEBUG_NHG)
+ zlog_debug(
+ "%s dependent nhe %pNG unset installed flag",
+ __func__,
+ rb_node_dependent->nhe);
+ UNSET_FLAG(rb_node_dependent->nhe->flags,
+ NEXTHOP_GROUP_INSTALLED);
+ }
+ }
+ }
+}
diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h
index b178007b4..6179be344 100644
--- a/zebra/zebra_nhg.h
+++ b/zebra/zebra_nhg.h
@@ -358,6 +358,7 @@ extern uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe,
/* Dataplane install/uninstall */
extern void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe);
extern void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe);
+extern void zebra_interface_nhg_reinstall(struct interface *ifp);
/* Forward ref of dplane update context type */
struct zebra_dplane_ctx;
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index adcaf6404..357f11282 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -4704,6 +4704,21 @@ static void rib_process_dplane_results(struct event *thread)
struct dplane_ctx_list_head ctxlist;
bool shut_p = false;
+#ifdef HAVE_SCRIPTING
+ char *script_name =
+ frrscript_names_get_script_name(ZEBRA_ON_RIB_PROCESS_HOOK_CALL);
+
+ int ret = 1;
+ struct frrscript *fs = NULL;
+
+ if (script_name) {
+ fs = frrscript_new(script_name);
+ if (fs)
+ ret = frrscript_load(fs, ZEBRA_ON_RIB_PROCESS_HOOK_CALL,
+ NULL);
+ }
+#endif /* HAVE_SCRIPTING */
+
/* Dequeue a list of completed updates with one lock/unlock cycle */
do {
@@ -4737,24 +4752,7 @@ static void rib_process_dplane_results(struct event *thread)
continue;
}
-#ifdef HAVE_SCRIPTING
- char *script_name = frrscript_names_get_script_name(
- ZEBRA_ON_RIB_PROCESS_HOOK_CALL);
-
- int ret = 1;
- struct frrscript *fs;
-
- if (script_name) {
- fs = frrscript_new(script_name);
- if (fs)
- ret = frrscript_load(
- fs, ZEBRA_ON_RIB_PROCESS_HOOK_CALL,
- NULL);
- }
-#endif /* HAVE_SCRIPTING */
-
while (ctx) {
-
#ifdef HAVE_SCRIPTING
if (ret == 0)
frrscript_call(fs,
@@ -4869,6 +4867,11 @@ static void rib_process_dplane_results(struct event *thread)
}
} while (1);
+
+#ifdef HAVE_SCRIPTING
+ if (fs)
+ frrscript_delete(fs);
+#endif
}
/*