summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_evpn_vty.c168
-rw-r--r--bgpd/bgp_lcommunity.c6
-rw-r--r--bgpd/bgp_route.c64
-rw-r--r--bgpd/bgp_routemap.c49
-rw-r--r--bgpd/bgp_vpn.c4
-rw-r--r--bgpd/bgpd.c58
-rw-r--r--bgpd/bgpd.h4
-rw-r--r--doc/developer/library.rst1
-rw-r--r--doc/developer/lists.rst32
-rw-r--r--doc/developer/rcu.rst269
-rw-r--r--doc/developer/subdir.am1
-rw-r--r--eigrpd/eigrp_cli.c920
-rw-r--r--eigrpd/eigrp_main.c9
-rw-r--r--eigrpd/eigrp_northbound.c1584
-rw-r--r--eigrpd/eigrp_vty.c1071
-rw-r--r--eigrpd/eigrp_vty.h1
-rw-r--r--eigrpd/eigrpd.h47
-rw-r--r--eigrpd/subdir.am10
-rw-r--r--lib/frr_pthread.c15
-rw-r--r--lib/frr_pthread.h3
-rw-r--r--lib/frrcu.c527
-rw-r--r--lib/frrcu.h172
-rw-r--r--lib/libfrr.c2
-rw-r--r--lib/log.c4
-rw-r--r--lib/pqueue.c185
-rw-r--r--lib/pqueue.h54
-rw-r--r--lib/seqlock.c190
-rw-r--r--lib/seqlock.h40
-rw-r--r--lib/sigevent.c42
-rw-r--r--lib/subdir.am4
-rw-r--r--lib/thread.c104
-rw-r--r--lib/thread.h7
-rw-r--r--pimd/pim_neighbor.c5
-rw-r--r--pimd/pim_nht.c10
-rw-r--r--tests/lib/cxxcompat.c1
-rw-r--r--tests/lib/test_atomlist.c10
-rw-r--r--tests/lib/test_seqlock.c34
-rw-r--r--tests/lib/test_timer_correctness.c1
-rw-r--r--tests/lib/test_timer_performance.c1
-rw-r--r--yang/frr-eigrpd.yang2
-rw-r--r--yang/subdir.am4
-rw-r--r--zebra/zebra_fpm_netlink.c2
-rw-r--r--zebra/zebra_vxlan.c35
43 files changed, 4117 insertions, 1635 deletions
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index a22082c072..d031d34f1f 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -1069,11 +1069,9 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
pi = pi->next) {
total_count++;
if (type == bgp_show_type_neighbor) {
- union sockunion *su = output_arg;
+ struct peer *peer = output_arg;
- if (pi->peer->su_remote == NULL
- || !sockunion_same(
- pi->peer->su_remote, su))
+ if (peer_cmp(peer, pi->peer) != 0)
continue;
}
if (header) {
@@ -1292,29 +1290,41 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_tags,
0);
}
-DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_routes,
- show_ip_bgp_l2vpn_evpn_all_neighbor_routes_cmd,
- "show [ip] bgp l2vpn evpn all neighbors A.B.C.D routes [json]",
+DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_routes,
+ show_ip_bgp_l2vpn_evpn_neighbor_routes_cmd,
+ "show [ip] bgp l2vpn evpn neighbors <A.B.C.D|X:X::X:X|WORD> routes [json]",
SHOW_STR
IP_STR
BGP_STR
L2VPN_HELP_STR
EVPN_HELP_STR
- "Display information about all EVPN NLRIs\n"
"Detailed information on TCP and BGP neighbor connections\n"
- "Neighbor to display information about\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
"Display routes learned from neighbor\n" JSON_STR)
{
- int idx_ipv4 = 0;
- union sockunion su;
+ int idx = 0;
struct peer *peer;
- int ret;
+ char *peerstr = NULL;
bool uj = use_json(argc, argv);
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp *bgp = NULL;
- argv_find(argv, argc, "A.B.C.D", &idx_ipv4);
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ return CMD_WARNING;
+ }
+
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
- ret = str2sockunion(argv[idx_ipv4]->arg, &su);
- if (ret < 0) {
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer) {
if (uj) {
json_object *json_no = NULL;
json_no = json_object_new_object();
@@ -1325,11 +1335,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_routes,
json_object_free(json_no);
} else
vty_out(vty, "Malformed address: %s\n",
- argv[idx_ipv4]->arg);
+ argv[idx]->arg);
return CMD_WARNING;
}
-
- peer = peer_lookup(NULL, &su);
if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
if (uj) {
json_object *json_no = NULL;
@@ -1345,13 +1353,13 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_routes,
return CMD_WARNING;
}
- return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, &su, 0,
+ return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, peer, 0,
uj);
}
DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd,
- "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors A.B.C.D routes [json]",
+ "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors <A.B.C.D|X:X::X:X|WORD> routes [json]",
SHOW_STR
IP_STR
BGP_STR
@@ -1360,20 +1368,30 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
"Display information for a route distinguisher\n"
"VPN Route Distinguisher\n"
"Detailed information on TCP and BGP neighbor connections\n"
- "Neighbor to display information about\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
"Display routes learned from neighbor\n" JSON_STR)
{
int idx_ext_community = 0;
- int idx_ipv4 = 0;
+ int idx = 0;
int ret;
- union sockunion su;
struct peer *peer;
+ char *peerstr = NULL;
struct prefix_rd prd;
bool uj = use_json(argc, argv);
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp *bgp = NULL;
- argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
- argv_find(argv, argc, "A.B.C.D", &idx_ipv4);
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ return CMD_WARNING;
+ }
+ argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
if (!ret) {
if (uj) {
@@ -1389,8 +1407,12 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
return CMD_WARNING;
}
- ret = str2sockunion(argv[idx_ipv4]->arg, &su);
- if (ret < 0) {
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
+
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer) {
if (uj) {
json_object *json_no = NULL;
json_no = json_object_new_object();
@@ -1401,11 +1423,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
json_object_free(json_no);
} else
vty_out(vty, "Malformed address: %s\n",
- argv[idx_ext_community]->arg);
+ argv[idx]->arg);
return CMD_WARNING;
}
-
- peer = peer_lookup(NULL, &su);
if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
if (uj) {
json_object *json_no = NULL;
@@ -1421,33 +1441,48 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
return CMD_WARNING;
}
- return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_neighbor, &su, 0,
+ return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_neighbor, peer, 0,
uj);
}
-DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes,
- show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes_cmd,
- "show [ip] bgp l2vpn evpn all neighbors A.B.C.D advertised-routes [json]",
+DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes,
+ show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes_cmd,
+ "show [ip] bgp l2vpn evpn neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json]",
SHOW_STR
IP_STR
BGP_STR
L2VPN_HELP_STR
EVPN_HELP_STR
- "Display information about all EVPN NLRIs\n"
"Detailed information on TCP and BGP neighbor connections\n"
- "Neighbor to display information about\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
"Display the routes advertised to a BGP neighbor\n" JSON_STR)
{
- int idx_ipv4 = 0;
- int ret;
+ int idx = 0;
struct peer *peer;
- union sockunion su;
bool uj = use_json(argc, argv);
+ struct bgp *bgp = NULL;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ char *peerstr = NULL;
+
+ if (uj)
+ argc--;
- argv_find(argv, argc, "A.B.C.D", &idx_ipv4);
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ return CMD_WARNING;
+ }
+
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
- ret = str2sockunion(argv[idx_ipv4]->arg, &su);
- if (ret < 0) {
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer) {
if (uj) {
json_object *json_no = NULL;
json_no = json_object_new_object();
@@ -1458,10 +1493,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes,
json_object_free(json_no);
} else
vty_out(vty, "Malformed address: %s\n",
- argv[idx_ipv4]->arg);
+ argv[idx]->arg);
return CMD_WARNING;
}
- peer = peer_lookup(NULL, &su);
if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
if (uj) {
json_object *json_no = NULL;
@@ -1482,7 +1516,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes,
DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes,
show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd,
- "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors A.B.C.D advertised-routes [json]",
+ "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json]",
SHOW_STR
IP_STR
BGP_STR
@@ -1491,22 +1525,43 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes,
"Display information for a route distinguisher\n"
"VPN Route Distinguisher\n"
"Detailed information on TCP and BGP neighbor connections\n"
- "Neighbor to display information about\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
"Display the routes advertised to a BGP neighbor\n" JSON_STR)
{
int idx_ext_community = 0;
- int idx_ipv4 = 0;
+ int idx = 0;
int ret;
struct peer *peer;
struct prefix_rd prd;
- union sockunion su;
+ struct bgp *bgp = NULL;
bool uj = use_json(argc, argv);
+ char *peerstr = NULL;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+
+ if (uj)
+ argc--;
+
+ if (uj)
+ argc--;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ return CMD_WARNING;
+ }
argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
- argv_find(argv, argc, "A.B.C.D", &idx_ipv4);
- ret = str2sockunion(argv[idx_ipv4]->arg, &su);
- if (ret < 0) {
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
+
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer) {
if (uj) {
json_object *json_no = NULL;
json_no = json_object_new_object();
@@ -1517,10 +1572,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes,
json_object_free(json_no);
} else
vty_out(vty, "Malformed address: %s\n",
- argv[idx_ext_community]->arg);
+ argv[idx]->arg);
return CMD_WARNING;
}
- peer = peer_lookup(NULL, &su);
if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
if (uj) {
json_object *json_no = NULL;
@@ -1599,7 +1653,7 @@ DEFUN(show_ip_bgp_evpn_rd_overlay,
use_json(argc, argv));
}
-/* For testing purpose, static route of MPLS-VPN. */
+/* For testing purpose, static route of EVPN RT-5. */
DEFUN(evpnrt5_network,
evpnrt5_network_cmd,
"network <A.B.C.D/M|X:X::X:X/M> rd ASN:NN_OR_IP-ADDRESS:NN ethtag WORD label WORD esi WORD gwip <A.B.C.D|X:X::X:X> routermac WORD [route-map WORD]",
@@ -1638,7 +1692,7 @@ DEFUN(evpnrt5_network,
argv[idx_routermac]->arg);
}
-/* For testing purpose, static route of MPLS-VPN. */
+/* For testing purpose, static route of EVPN RT-5. */
DEFUN(no_evpnrt5_network,
no_evpnrt5_network_cmd,
"no network <A.B.C.D/M|X:X::X:X/M> rd ASN:NN_OR_IP-ADDRESS:NN ethtag WORD label WORD esi WORD gwip <A.B.C.D|X:X::X:X>",
@@ -5323,12 +5377,12 @@ void bgp_ethernetvpn_init(void)
install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_tags_cmd);
install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_tags_cmd);
install_element(VIEW_NODE,
- &show_ip_bgp_l2vpn_evpn_all_neighbor_routes_cmd);
+ &show_ip_bgp_l2vpn_evpn_neighbor_routes_cmd);
install_element(VIEW_NODE,
&show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd);
install_element(
VIEW_NODE,
- &show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes_cmd);
+ &show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes_cmd);
install_element(
VIEW_NODE,
&show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd);
diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c
index 2b09a2954e..aeb290719a 100644
--- a/bgpd/bgp_lcommunity.c
+++ b/bgpd/bgp_lcommunity.c
@@ -439,7 +439,8 @@ struct lcommunity *lcommunity_str2com(const char *str)
enum lcommunity_token token = lcommunity_token_unknown;
struct lcommunity_val lval;
- while ((str = lcommunity_gettoken(str, &lval, &token))) {
+ do {
+ str = lcommunity_gettoken(str, &lval, &token);
switch (token) {
case lcommunity_token_val:
if (lcom == NULL)
@@ -452,7 +453,8 @@ struct lcommunity *lcommunity_str2com(const char *str)
lcommunity_free(&lcom);
return NULL;
}
- }
+ } while (str);
+
return lcom;
}
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index a5591e29f2..a372568373 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -5332,6 +5332,13 @@ static void bgp_purge_af_static_redist_routes(struct bgp *bgp, afi_t afi,
struct bgp_node *rn;
struct bgp_path_info *pi;
+ /* Do not install the aggregate route if BGP is in the
+ * process of termination.
+ */
+ if (bgp_flag_check(bgp, BGP_FLAG_DELETE_IN_PROGRESS) ||
+ (bgp->peer_self == NULL))
+ return;
+
table = bgp->rib[afi][safi];
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) {
@@ -10493,63 +10500,6 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
return ret;
}
-static struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,
- const char *ip_str, bool use_json)
-{
- int ret;
- struct peer *peer;
- union sockunion su;
-
- /* Get peer sockunion. */
- ret = str2sockunion(ip_str, &su);
- if (ret < 0) {
- peer = peer_lookup_by_conf_if(bgp, ip_str);
- if (!peer) {
- peer = peer_lookup_by_hostname(bgp, ip_str);
-
- if (!peer) {
- if (use_json) {
- json_object *json_no = NULL;
- json_no = json_object_new_object();
- json_object_string_add(
- json_no,
- "malformedAddressOrName",
- ip_str);
- vty_out(vty, "%s\n",
- json_object_to_json_string_ext(
- json_no,
- JSON_C_TO_STRING_PRETTY));
- json_object_free(json_no);
- } else
- vty_out(vty,
- "%% Malformed address or name: %s\n",
- ip_str);
- return NULL;
- }
- }
- return peer;
- }
-
- /* Peer structure lookup. */
- peer = peer_lookup(bgp, &su);
- if (!peer) {
- if (use_json) {
- json_object *json_no = NULL;
- json_no = json_object_new_object();
- json_object_string_add(json_no, "warning",
- "No such neighbor in this view/vrf");
- vty_out(vty, "%s\n",
- json_object_to_json_string_ext(
- json_no, JSON_C_TO_STRING_PRETTY));
- json_object_free(json_no);
- } else
- vty_out(vty, "No such neighbor in this view/vrf\n");
- return NULL;
- }
-
- return peer;
-}
-
enum bgp_stats {
BGP_STATS_MAXBITLEN = 0,
BGP_STATS_RIB,
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 5ffc416dc5..7f1a9b71c1 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -3406,31 +3406,34 @@ int bgp_route_map_update_timer(struct thread *thread)
static void bgp_route_map_mark_update(const char *rmap_name)
{
- if (bm->t_rmap_update == NULL) {
- struct listnode *node, *nnode;
- struct bgp *bgp;
-
- /* rmap_update_timer of 0 means don't do route updates */
- if (bm->rmap_update_timer) {
- bm->t_rmap_update = NULL;
- thread_add_timer(bm->master, bgp_route_map_update_timer,
- NULL, bm->rmap_update_timer,
- &bm->t_rmap_update);
-
- /* Signal the groups that a route-map update event has
- * started */
- for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
- update_group_policy_update(bgp,
- BGP_POLICY_ROUTE_MAP,
- rmap_name, 1, 1);
- } else {
- for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
- bgp_route_map_process_update(bgp, rmap_name, 0);
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ /* If new update is received before the current timer timed out,
+ * turn it off and start a new timer.
+ */
+ if (bm->t_rmap_update != NULL)
+ THREAD_OFF(bm->t_rmap_update);
+
+ /* rmap_update_timer of 0 means don't do route updates */
+ if (bm->rmap_update_timer) {
+ thread_add_timer(bm->master, bgp_route_map_update_timer,
+ NULL, bm->rmap_update_timer,
+ &bm->t_rmap_update);
+
+ /* Signal the groups that a route-map update event has
+ * started */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ update_group_policy_update(bgp,
+ BGP_POLICY_ROUTE_MAP,
+ rmap_name, 1, 1);
+ } else {
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ bgp_route_map_process_update(bgp, rmap_name, 0);
#if ENABLE_BGP_VNC
- zlog_debug("%s: calling vnc_routemap_update", __func__);
- vnc_routemap_update(bgp, __func__);
+ zlog_debug("%s: calling vnc_routemap_update", __func__);
+ vnc_routemap_update(bgp, __func__);
#endif
- }
}
}
diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c
index 54ca980cad..09b1cb429b 100644
--- a/bgpd/bgp_vpn.c
+++ b/bgpd/bgp_vpn.c
@@ -74,7 +74,7 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer,
json_object_string_add(json_ocode, "incomplete", "?");
}
- for (rn = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); rn;
+ for (rn = bgp_table_top(bgp->rib[afi][safi]); rn;
rn = bgp_route_next(rn)) {
if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0)
continue;
@@ -200,7 +200,7 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer,
json_array);
} else {
route_vty_out_tmp(vty, &rm->p, path->attr,
- SAFI_MPLS_VPN, use_json,
+ safi, use_json,
json_array);
}
}
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index d79a68dcab..b5f267cc38 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -8047,3 +8047,61 @@ void bgp_terminate(void)
bgp_mac_finish();
}
+
+struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,
+ const char *ip_str, bool use_json)
+{
+ int ret;
+ struct peer *peer;
+ union sockunion su;
+
+ /* Get peer sockunion. */
+ ret = str2sockunion(ip_str, &su);
+ if (ret < 0) {
+ peer = peer_lookup_by_conf_if(bgp, ip_str);
+ if (!peer) {
+ peer = peer_lookup_by_hostname(bgp, ip_str);
+
+ if (!peer) {
+ if (use_json) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(
+ json_no,
+ "malformedAddressOrName",
+ ip_str);
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json_no,
+ JSON_C_TO_STRING_PRETTY));
+ json_object_free(json_no);
+ } else
+ vty_out(vty,
+ "%% Malformed address or name: %s\n",
+ ip_str);
+ return NULL;
+ }
+ }
+ return peer;
+ }
+
+ /* Peer structure lookup. */
+ peer = peer_lookup(bgp, &su);
+ if (!peer) {
+ if (use_json) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "No such neighbor in this view/vrf");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json_no, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "No such neighbor in this view/vrf\n");
+ return NULL;
+ }
+
+ return peer;
+}
+
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 777db0ce22..9e05fd5629 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1927,4 +1927,8 @@ extern void bgp_unset_redist_vrf_bitmaps(struct bgp *, vrf_id_t);
/* For benefit of rfapi */
extern struct peer *peer_new(struct bgp *bgp);
+
+extern struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,
+ const char *ip_str, bool use_json);
+
#endif /* _QUAGGA_BGPD_H */
diff --git a/doc/developer/library.rst b/doc/developer/library.rst
index 4ba0c0ebc6..7cd493ccc4 100644
--- a/doc/developer/library.rst
+++ b/doc/developer/library.rst
@@ -8,6 +8,7 @@ Library Facilities (libfrr)
:maxdepth: 2
memtypes
+ rcu
lists
logging
hooks
diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst
index fc47a67e42..5f020060ce 100644
--- a/doc/developer/lists.rst
+++ b/doc/developer/lists.rst
@@ -611,6 +611,38 @@ Head removal (pop) and deallocation:
* note nothing between wrlock() and unlock() */
XFREE(MTYPE_ITEM, i);
+FAQ
+---
+
+Why is the list head not ``const`` in the list APIs?
+ The semantics that a ``const`` list head would imply are not obvious. It
+ could mean any of the following:
+
+ * the list just shouldn't be allocated/deallocated, but may be modified.
+ This doesn't actually work since the list head needs to be modified for
+ inserting or deleting items.
+
+ * the list shouldn't be modified, but items can. This may make sense for
+ iterating, but it's not exactly consistent - an item might be on more
+ than one list, does it apply to all of them? If not, which one?
+
+ * neither the list nor the items should be modified. This is consistent,
+ but hard to do without creating a ``const`` copy of every single list
+ function. Ease of use trumps this.
+
+Why is there no "is this item on a/the list" test?
+ It's slow for several of the data structures, and the work of adding it
+ just hasn't been done. It can certainly be added if it's needed.
+
+Why is it ``PREDECL`` + ``DECLARE`` instead of ``DECLARE`` + ``DEFINE``?
+ The rule is that a ``DEFINE`` must be in a ``.c`` file, and linked exactly
+ once because it defines some kind of global symbol. This is not the case
+ for the data structure macros; they only define ``static`` symbols and it
+ is perfectly fine to include both ``PREDECL`` and ``DECLARE`` in a header
+ file. It is also perfectly fine to have the same ``DECLARE`` statement in
+ 2 ``.c`` files, but only **if the macro arguments are identical.** Maybe
+ don't do that unless you really need it.
+
FRR lists
---------
diff --git a/doc/developer/rcu.rst b/doc/developer/rcu.rst
new file mode 100644
index 0000000000..c2ddf93f53
--- /dev/null
+++ b/doc/developer/rcu.rst
@@ -0,0 +1,269 @@
+.. highlight:: c
+
+RCU
+===
+
+Introduction
+------------
+
+RCU (Read-Copy-Update) is, fundamentally, a paradigm of multithreaded
+operation (and not a set of APIs.) The core ideas are:
+
+* longer, complicated updates to structures are made only on private,
+ "invisible" copies. Other threads, when they access the structure, see an
+ older (but consistent) copy.
+
+* once done, the updated copy is swapped in in a single operation so that
+ other threads see either the old or the new data but no inconsistent state
+ between.
+
+* the old instance is only released after making sure that it is impossible
+ any other thread might still be reading it.
+
+For more information, please search for general or Linux kernel RCU
+documentation; there is no way this doc can be comprehensive in explaining the
+interactions:
+
+* https://en.wikipedia.org/wiki/Read-copy-update
+* https://www.kernel.org/doc/html/latest/kernel-hacking/locking.html#avoiding-locks-read-copy-update
+* https://lwn.net/Articles/262464/
+* http://www.rdrop.com/users/paulmck/RCU/rclock_OLS.2001.05.01c.pdf
+* http://lse.sourceforge.net/locking/rcupdate.html
+
+RCU, the TL;DR
+^^^^^^^^^^^^^^
+
+#. data structures are always consistent for reading. That's the "R" part.
+#. reading never blocks / takes a lock.
+#. rcu_read_lock is not a lock in the traditional sense. Think of it as a
+ "reservation"; it notes what the *oldest* possible thing the thread might
+ be seeing is, and which thus can't be deleted yet.
+#. you create some object, finish it up, and then publish it.
+#. publishing is an ``atomic_*`` call with ``memory_order_release``, which
+ tells the compiler to make sure prior memory writes have completed before
+ doing the atomic op.
+#. ``ATOMLIST_*`` ``add`` operations do the ``memory_order_release`` for you.
+#. you can't touch the object after it is published, except with atomic ops.
+#. because you can't touch it, if you want to change it you make a new copy,
+ work on that, and then publish the new copy. That's the "CU" part.
+#. deleting the object is also an atomic op.
+#. other threads that started working before you published / deleted an object
+ might not see the new object / still see the deleted object.
+#. because other threads may still see deleted objects, the ``free()`` needs
+ to be delayed. That's what :c:func:`rcu_free()` is for.
+
+
+When (not) to use RCU
+^^^^^^^^^^^^^^^^^^^^^
+
+RCU is designed for read-heavy workloads where objects are updated relatively
+rarely, but frequently accessed. Do *not* indiscriminately replace locking by
+RCU patterns.
+
+The "copy" part of RCU implies that, while updating, several copies of a given
+object exist in parallel. Even after the updated copy is swapped in, the old
+object remains queued for freeing until all other threads are guaranteed to
+not be accessing it anymore, due to passing a sequence point. In addition to
+the increased memory usage, there may be some bursted (due to batching) malloc
+contention when the RCU cleanup thread does its thing and frees memory.
+
+Other useful patterns
+^^^^^^^^^^^^^^^^^^^^^
+
+In addition to the full "copy object, apply changes, atomically update"
+approach, there are 2 "reduced" usage cases that can be done:
+
+* atomically updating single pieces of a particular object, e.g. some flags
+ or configuration piece
+
+* straight up read-only / immutable objects
+
+Both of these cases can be considered RCU "subsets". For example, when
+maintaining an atomic list of items, but these items only have a single
+integer value that needs to be updated, that value can be atomically updated
+without copying the entire object. However, the object still needs to be
+free'd through :c:func:`rcu_free()` since reading/updating and deleting might
+be happening concurrently. The same applies for immutable objects; deletion
+might still race with reading so they need to be free'd through RCU.
+
+FRR API
+-------
+
+Before diving into detail on the provided functions, it is important to note
+that the FRR RCU API covers the **cleanup part of RCU, not the read-copy-update
+paradigm itself**. These parts are handled by standard C11 atomic operations,
+and by extension through the atomic data structures (ATOMLIST, ATOMSORT & co.)
+
+The ``rcu_*`` functions only make sense in conjunction with these RCU access
+patterns. If you're calling the RCU API but not using these, something is
+wrong. The other way around is not necessarily true; it is possible to use
+atomic ops & datastructures with other types of locking, e.g. rwlocks.
+
+.. c:function:: void rcu_read_lock()
+.. c:function:: void rcu_read_unlock()
+
+ These functions acquire / release the RCU read-side lock. All access to
+ RCU-guarded data must be inside a block guarded by these. Any number of
+ threads may hold the RCU read-side lock at a given point in time, including
+ both no threads at all and all threads.
+
+ The functions implement a depth counter, i.e. can be nested. The nested
+ calls are cheap, since they only increment/decrement the counter.
+ Therefore, any place that uses RCU data and doesn't have a guarantee that
+ the caller holds RCU (e.g. ``lib/`` code) should just have its own
+ rcu_read_lock/rcu_read_unlock pair.
+
+ At the "root" level (e.g. un-nested), these calls can incur the cost of one
+ syscall (to ``futex()``). That puts them on about the same cost as a
+ mutex lock/unlock.
+
+ The ``thread_master`` code currently always holds RCU everywhere, except
+ while doing the actual ``poll()`` syscall. This is both an optimization as
+ well as an "easement" into getting RCU going. The current implementation
+ contract is that any ``struct thread *`` callback is called with a RCU
+ holding depth of 1, and that this is owned by the thread so it may (should)
+ drop and reacquire it when doing some longer-running work.
+
+ .. warning::
+
+ The RCU read-side lock must be held **continuously** for the entire time
+ any piece of RCU data is used. This includes any access to RCU data
+ after the initial ``atomic_load``. If the RCU read-side lock is
+ released, any RCU-protected pointers as well as the data they refer to
+ become invalid, as another thread may have called :c:func:`rcu_free` on
+ them.
+
+.. c:type:: struct rcu_head
+.. c:type:: struct rcu_head_close
+.. c:type:: struct rcu_action
+
+ The ``rcu_head`` structures are small (16-byte) bits that contain the
+ queueing machinery for the RCU sweeper/cleanup mechanisms.
+
+ Any piece of data that is cleaned up by RCU needs to have a matching
+ ``rcu_head`` embedded in it. If there is more than one cleanup operation
+ to be done (e.g. closing a file descriptor), more than one ``rcu_head`` may
+ be embedded.
+
+ .. warning::
+
+ It is not possible to reuse a ``rcu_head``. It is owned by the RCU code
+ as soon as ``rcu_*`` is called on it.
+
+ The ``_close`` variant carries an extra ``int fd`` field to store the fd to
+ be closed.
+
+ To minimize the amount of memory used for ``rcu_head``, details about the
+ RCU operation to be performed are moved into the ``rcu_action`` structure.
+ It contains e.g. the MTYPE for :c:func:`rcu_free` calls. The pointer to be
+ freed is stored as an offset relative to the ``rcu_head``, which means it
+ must be embedded as a struct field so the offset is constant.
+
+ The ``rcu_action`` structure is an implementation detail. Using
+ ``rcu_free`` or ``rcu_close`` will set it up correctly without further
+ code needed.
+
+ The ``rcu_head`` may be put in an union with other data if the other data
+ is only used during "life" of the data, since the ``rcu_head`` is used only
+ for the "death" of data. But note that other threads may still be reading
+ a piece of data while a thread is working to free it.
+
+.. c:function:: void rcu_free(struct memtype *mtype, struct X *ptr, field)
+
+ Free a block of memory after RCU has ensured no other thread can be
+ accessing it anymore. The pointer remains valid for any other thread that
+ has called :c:func:`rcu_read_lock` before the ``rcu_free`` call.
+
+ .. warning::
+
+ In some other RCU implementations, the pointer remains valid to the
+ *calling* thread if it is holding the RCU read-side lock. This is not
+ the case in FRR, particularly when running single-threaded. Enforcing
+ this rule also allows static analysis to find use-after-free issues.
+
+ ``mtype`` is the libfrr ``MTYPE_FOO`` allocation type to pass to
+ :c:func:`XFREE`.
+
+ ``field`` must be the name of a ``struct rcu_head`` member field in ``ptr``.
+ The offset of this field (which must be constant) is used to reduce the
+ memory size of ``struct rcu_head``.
+
+ .. note::
+
+ ``rcu_free`` (and ``rcu_close``) calls are more efficient if they are
+ put close to each other. When freeing several RCU'd resources, try to
+ move the calls next to each other (even if the data structures do not
+ directly point to each other.)
+
+ Having the calls bundled reduces the cost of adding the ``rcu_head`` to
+ the RCU queue; the RCU queue is an atomic data structure whose usage
+ will require the CPU to acquire an exclusive hold on relevant cache
+ lines.
+
+.. c:function:: void rcu_close(struct rcu_head_close *head, int fd)
+
+ Close a file descriptor after ensuring no other thread might be using it
+ anymore. Same as :c:func:`rcu_free`, except it calls ``close`` instead of
+ ``free``.
+
+Internals
+^^^^^^^^^
+
+.. c:type:: struct rcu_thread
+
+ Per-thread state maintained by the RCU code, set up by the following
+ functions. A pointer to a thread's own ``rcu_thread`` is saved in
+ thread-local storage.
+
+.. c:function:: struct rcu_thread *rcu_thread_prepare(void)
+.. c:function:: void rcu_thread_unprepare(struct rcu_thread *rcu_thread)
+.. c:function:: void rcu_thread_start(struct rcu_thread *rcu_thread)
+
+ Since the RCU code needs to have a list of all active threads, these
+ functions are used by the ``frr_pthread`` code to set up threads. Teardown
+ is automatic. It should not be necessary to call these functions.
+
+ Any thread that accesses RCU-protected data needs to be registered with
+ these functions. Threads that do not access RCU-protected data may call
+ these functions but do not need to.
+
+ Note that passing a pointer to RCU-protected data to some library which
+ accesses that pointer makes the library "access RCU-protected data". In
+ that case, either all of the library's threads must be registered for RCU,
+ or the code must instead pass a (non-RCU) copy of the data to the library.
+
+.. c:function:: void rcu_shutdown(void)
+
+ Stop the RCU sweeper thread and make sure all cleanup has finished.
+
+ This function is called on daemon exit by the libfrr code to ensure pending
+ RCU operations are completed. This is mostly to get a clean exit without
+ memory leaks from queued RCU operations. It should not be necessary to
+ call this function as libfrr handles this.
+
+FRR specifics and implementation details
+----------------------------------------
+
+The FRR RCU infrastructure has the following characteristics:
+
+* it is Epoch-based with a 32-bit wrapping counter. (This is somewhat
+ different from other Epoch-based approaches which may be designed to only
+ use 3 counter values, but works out to a simple implementation.)
+
+* instead of tracking CPUs as the Linux kernel does, threads are tracked. This
+ has exactly zero semantic impact, RCU just cares about "threads of
+ execution", which the kernel can optimize to CPUs but we can't. But it
+ really boils down to the same thing.
+
+* there are no ``rcu_dereference`` and ``rcu_assign_pointer`` - use
+ ``atomic_load`` and ``atomic_store`` instead. (These didn't exist when the
+ Linux RCU code was created.)
+
+* there is no ``synchronize_rcu``; this is a design choice but may be revisited
+ at a later point. ``synchronize_rcu`` blocks a thread until it is guaranteed
+ that no other threads might still be accessing data structures that they may
+ have access to at the beginning of the function call. This is a blocking
+ design and probably not appropriate for FRR. Instead, ``rcu_call`` can be
+ used to have the RCU sweeper thread make a callback after the same constraint
+ is fulfilled in an asynchronous way. Most needs should be covered by
+ ``rcu_free`` and ``rcu_close``.
diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am
index 996f12d47f..1fc593e566 100644
--- a/doc/developer/subdir.am
+++ b/doc/developer/subdir.am
@@ -42,6 +42,7 @@ dev_RSTFILES = \
doc/developer/packaging-debian.rst \
doc/developer/packaging-redhat.rst \
doc/developer/packaging.rst \
+ doc/developer/rcu.rst \
doc/developer/testing.rst \
doc/developer/topotests-snippets.rst \
doc/developer/topotests.rst \
diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c
new file mode 100644
index 0000000000..ba657a7d5d
--- /dev/null
+++ b/eigrpd/eigrp_cli.c
@@ -0,0 +1,920 @@
+/*
+ * EIGRP daemon CLI implementation.
+ *
+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include <zebra.h>
+
+#include "lib/command.h"
+#include "lib/log.h"
+#include "lib/northbound_cli.h"
+
+#include "eigrp_structs.h"
+#include "eigrpd.h"
+#include "eigrp_zebra.h"
+
+#ifndef VTYSH_EXTRACT_PL
+#include "eigrpd/eigrp_cli_clippy.c"
+#endif /* VTYSH_EXTRACT_PL */
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance
+ */
+DEFPY_NOSH(
+ router_eigrp,
+ router_eigrp_cmd,
+ "router eigrp (1-65535)$as",
+ ROUTER_STR
+ EIGRP_STR
+ AS_STR)
+{
+ char xpath[XPATH_MAXLEN];
+ int rv;
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-eigrpd:eigrpd/instance[asn='%s'][vrf='']",
+ as_str);
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ rv = nb_cli_apply_changes(vty, NULL);
+ if (rv == CMD_SUCCESS)
+ VTY_PUSH_XPATH(EIGRP_NODE, xpath);
+
+ return rv;
+}
+
+DEFPY_NOSH(
+ no_router_eigrp,
+ no_router_eigrp_cmd,
+ "no router eigrp (1-65535)$as",
+ NO_STR
+ ROUTER_STR
+ EIGRP_STR
+ AS_STR)
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-eigrpd:eigrpd/instance[asn='%s'][vrf='']",
+ as_str);
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_header(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *asn = yang_dnode_get_string(dnode, "./asn");
+
+ vty_out(vty, "router eigrp %s\n", asn);
+}
+
+void eigrp_cli_show_end_header(struct vty *vty, struct lyd_node *dnode)
+{
+ vty_out(vty, "!\n");
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/router-id
+ */
+DEFPY(
+ eigrp_router_id,
+ eigrp_router_id_cmd,
+ "eigrp router-id A.B.C.D$addr",
+ EIGRP_STR
+ "Router ID for this EIGRP process\n"
+ "EIGRP Router-ID in IP address format\n")
+{
+ nb_cli_enqueue_change(vty, "./router-id", NB_OP_MODIFY, addr_str);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_router_id,
+ no_eigrp_router_id_cmd,
+ "no eigrp router-id [A.B.C.D]",
+ NO_STR
+ EIGRP_STR
+ "Router ID for this EIGRP process\n"
+ "EIGRP Router-ID in IP address format\n")
+{
+ nb_cli_enqueue_change(vty, "./router-id", NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_router_id(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *router_id = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " eigrp router-id %s\n", router_id);
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/passive-interface
+ */
+DEFPY(
+ eigrp_passive_interface,
+ eigrp_passive_interface_cmd,
+ "[no] passive-interface IFNAME",
+ NO_STR
+ "Suppress routing updates on an interface\n"
+ "Interface to suppress on\n")
+{
+ if (no)
+ nb_cli_enqueue_change(vty, "./passive-interface",
+ NB_OP_DESTROY, ifname);
+ else
+ nb_cli_enqueue_change(vty, "./passive-interface",
+ NB_OP_CREATE, ifname);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_passive_interface(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *ifname = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " passive-interface %s\n", ifname);
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/active-time
+ */
+DEFPY(
+ eigrp_timers_active,
+ eigrp_timers_active_cmd,
+ "timers active-time <(1-65535)$timer|disabled$disabled>",
+ "Adjust routing timers\n"
+ "Time limit for active state\n"
+ "Active state time limit in seconds\n"
+ "Disable time limit for active state\n")
+{
+ if (disabled)
+ nb_cli_enqueue_change(vty, "./active-time", NB_OP_MODIFY, "0");
+ else
+ nb_cli_enqueue_change(vty, "./active-time",
+ NB_OP_MODIFY, timer_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_timers_active,
+ no_eigrp_timers_active_cmd,
+ "no timers active-time [<(1-65535)|disabled>]",
+ NO_STR
+ "Adjust routing timers\n"
+ "Time limit for active state\n"
+ "Active state time limit in seconds\n"
+ "Disable time limit for active state\n")
+{
+ nb_cli_enqueue_change(vty, "./active-time", NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_active_time(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *timer = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " timers active-time %s\n", timer);
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/variance
+ */
+DEFPY(
+ eigrp_variance,
+ eigrp_variance_cmd,
+ "variance (1-128)$variance",
+ "Control load balancing variance\n"
+ "Metric variance multiplier\n")
+{
+ nb_cli_enqueue_change(vty, "./variance", NB_OP_MODIFY, variance_str);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_variance,
+ no_eigrp_variance_cmd,
+ "no variance [(1-128)]",
+ NO_STR
+ "Control load balancing variance\n"
+ "Metric variance multiplier\n")
+{
+ nb_cli_enqueue_change(vty, "./variance", NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_variance(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *variance = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " variance %s\n", variance);
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/maximum-paths
+ */
+DEFPY(
+ eigrp_maximum_paths,
+ eigrp_maximum_paths_cmd,
+ "maximum-paths (1-32)$maximum_paths",
+ "Forward packets over multiple paths\n"
+ "Number of paths\n")
+{
+ nb_cli_enqueue_change(vty, "./maximum-paths", NB_OP_MODIFY,
+ maximum_paths_str);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_maximum_paths,
+ no_eigrp_maximum_paths_cmd,
+ "no maximum-paths [(1-32)]",
+ NO_STR
+ "Forward packets over multiple paths\n"
+ "Number of paths\n")
+{
+ nb_cli_enqueue_change(vty, "./maximum-paths", NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_maximum_paths(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *maximum_paths = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " maximum-paths %s\n", maximum_paths);
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K1
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K2
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K3
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K4
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K5
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K6
+ */
+DEFPY(
+ eigrp_metric_weights,
+ eigrp_metric_weights_cmd,
+ "metric weights (0-255)$k1 (0-255)$k2 (0-255)$k3 (0-255)$k4 (0-255)$k5 [(0-255)$k6]",
+ "Modify metrics and parameters for advertisement\n"
+ "Modify metric coefficients\n"
+ "K1\n"
+ "K2\n"
+ "K3\n"
+ "K4\n"
+ "K5\n"
+ "K6\n")
+{
+ nb_cli_enqueue_change(vty, "./metric-weights/K1", NB_OP_MODIFY, k1_str);
+ nb_cli_enqueue_change(vty, "./metric-weights/K2", NB_OP_MODIFY, k2_str);
+ nb_cli_enqueue_change(vty, "./metric-weights/K3", NB_OP_MODIFY, k3_str);
+ nb_cli_enqueue_change(vty, "./metric-weights/K4", NB_OP_MODIFY, k4_str);
+ nb_cli_enqueue_change(vty, "./metric-weights/K5", NB_OP_MODIFY, k5_str);
+ if (k6)
+ nb_cli_enqueue_change(vty, "./metric-weights/K6",
+ NB_OP_MODIFY, k6_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_metric_weights,
+ no_eigrp_metric_weights_cmd,
+ "no metric weights [(0-255) (0-255) (0-255) (0-255) (0-255) (0-255)]",
+ NO_STR
+ "Modify metrics and parameters for advertisement\n"
+ "Modify metric coefficients\n"
+ "K1\n"
+ "K2\n"
+ "K3\n"
+ "K4\n"
+ "K5\n"
+ "K6\n")
+{
+ nb_cli_enqueue_change(vty, "./metric-weights/K1", NB_OP_DESTROY, NULL);
+ nb_cli_enqueue_change(vty, "./metric-weights/K2", NB_OP_DESTROY, NULL);
+ nb_cli_enqueue_change(vty, "./metric-weights/K3", NB_OP_DESTROY, NULL);
+ nb_cli_enqueue_change(vty, "./metric-weights/K4", NB_OP_DESTROY, NULL);
+ nb_cli_enqueue_change(vty, "./metric-weights/K5", NB_OP_DESTROY, NULL);
+ nb_cli_enqueue_change(vty, "./metric-weights/K6", NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_metrics(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *k1, *k2, *k3, *k4, *k5, *k6;
+
+ k1 = yang_dnode_exists(dnode, "./K1") ?
+ yang_dnode_get_string(dnode, "./K1") : "0";
+ k2 = yang_dnode_exists(dnode, "./K2") ?
+ yang_dnode_get_string(dnode, "./K2") : "0";
+ k3 = yang_dnode_exists(dnode, "./K3") ?
+ yang_dnode_get_string(dnode, "./K3") : "0";
+ k4 = yang_dnode_exists(dnode, "./K4") ?
+ yang_dnode_get_string(dnode, "./K4") : "0";
+ k5 = yang_dnode_exists(dnode, "./K5") ?
+ yang_dnode_get_string(dnode, "./K5") : "0";
+ k6 = yang_dnode_exists(dnode, "./K6") ?
+ yang_dnode_get_string(dnode, "./K6") : "0";
+
+ vty_out(vty, " metric weights %s %s %s %s %s",
+ k1, k2, k3, k4, k5);
+ if (k6)
+ vty_out(vty, " %s", k6);
+ vty_out(vty, "\n");
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/network
+ */
+DEFPY(
+ eigrp_network,
+ eigrp_network_cmd,
+ "[no] network A.B.C.D/M$prefix",
+ NO_STR
+ "Enable routing on an IP network\n"
+ "EIGRP network prefix\n")
+{
+ if (no)
+ nb_cli_enqueue_change(vty, "./network", NB_OP_DESTROY,
+ prefix_str);
+ else
+ nb_cli_enqueue_change(vty, "./network", NB_OP_CREATE,
+ prefix_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_network(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *prefix = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " network %s\n", prefix);
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/neighbor
+ */
+DEFPY(
+ eigrp_neighbor,
+ eigrp_neighbor_cmd,
+ "[no] neighbor A.B.C.D$addr",
+ NO_STR
+ "Specify a neighbor router\n"
+ "Neighbor address\n")
+{
+ if (no)
+ nb_cli_enqueue_change(vty, "./neighbor", NB_OP_DESTROY,
+ addr_str);
+ else
+ nb_cli_enqueue_change(vty, "./neighbor", NB_OP_CREATE,
+ addr_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_neighbor(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *prefix = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " neighbor %s\n", prefix);
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/route-map
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/bandwidth
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/delay
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/reliability
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/load
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/mtu
+ */
+DEFPY(
+ eigrp_redistribute_source_metric,
+ eigrp_redistribute_source_metric_cmd,
+ "[no] redistribute " FRR_REDIST_STR_EIGRPD
+ "$proto [metric (1-4294967295)$bw (0-4294967295)$delay (0-255)$rlbt (1-255)$load (1-65535)$mtu]",
+ NO_STR
+ REDIST_STR
+ FRR_REDIST_HELP_STR_EIGRPD
+ "Metric for redistributed routes\n"
+ "Bandwidth metric in Kbits per second\n"
+ "EIGRP delay metric, in 10 microsecond units\n"
+ "EIGRP reliability metric where 255 is 100% reliable2 ?\n"
+ "EIGRP Effective bandwidth metric (Loading) where 255 is 100% loaded\n"
+ "EIGRP MTU of the path\n")
+{
+ char xpath[XPATH_MAXLEN], xpath_metric[XPATH_MAXLEN + 64];
+
+ snprintf(xpath, sizeof(xpath), "./redistribute[protocol='%s']", proto);
+
+ if (no) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ if (bw == 0 || delay == 0 || rlbt == 0 || load == 0 || mtu == 0)
+ return nb_cli_apply_changes(vty, NULL);
+
+ snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/bandwidth",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, bw_str);
+ snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/delay", xpath);
+ nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, delay_str);
+ snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/reliability",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, rlbt_str);
+ snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/load", xpath);
+ nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, load_str);
+ snprintf(xpath_metric, sizeof(xpath_metric), "%s/metrics/mtu", xpath);
+ nb_cli_enqueue_change(vty, xpath_metric, NB_OP_MODIFY, mtu_str);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_redistribute(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *proto = yang_dnode_get_string(dnode, "./protocol");
+ const char *bw, *delay, *load, *mtu, *rlbt;
+
+ bw = yang_dnode_exists(dnode, "./metrics/bandwidth") ?
+ yang_dnode_get_string(dnode, "./metrics/bandwidth") : NULL;
+ delay = yang_dnode_exists(dnode, "./metrics/delay") ?
+ yang_dnode_get_string(dnode, "./metrics/delay") : NULL;
+ rlbt = yang_dnode_exists(dnode, "./metrics/reliability") ?
+ yang_dnode_get_string(dnode, "./metrics/reliability") : NULL;
+ load = yang_dnode_exists(dnode, "./metrics/load") ?
+ yang_dnode_get_string(dnode, "./metrics/load") : NULL;
+ mtu = yang_dnode_exists(dnode, "./metrics/mtu") ?
+ yang_dnode_get_string(dnode, "./metrics/mtu") : NULL;
+
+ vty_out(vty, " redistribute %s", proto);
+ if (bw || rlbt || delay || load || mtu)
+ vty_out(vty, " metric %s %s %s %s %s", bw, delay, rlbt, load,
+ mtu);
+ vty_out(vty, "\n");
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/delay
+ */
+DEFPY(
+ eigrp_if_delay,
+ eigrp_if_delay_cmd,
+ "delay (1-16777215)$delay",
+ "Specify interface throughput delay\n"
+ "Throughput delay (tens of microseconds)\n")
+{
+ nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/delay",
+ NB_OP_MODIFY, delay_str);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_if_delay,
+ no_eigrp_if_delay_cmd,
+ "no delay [(1-16777215)]",
+ NO_STR
+ "Specify interface throughput delay\n"
+ "Throughput delay (tens of microseconds)\n")
+{
+ nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/delay",
+ NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_delay(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *delay = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " delay %s\n", delay);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/bandwidth
+ */
+DEFPY(
+ eigrp_if_bandwidth,
+ eigrp_if_bandwidth_cmd,
+ "eigrp bandwidth (1-10000000)$bw",
+ EIGRP_STR
+ "Set bandwidth informational parameter\n"
+ "Bandwidth in kilobits\n")
+{
+ nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/bandwidth",
+ NB_OP_MODIFY, bw_str);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_if_bandwidth,
+ no_eigrp_if_bandwidth_cmd,
+ "no eigrp bandwidth [(1-10000000)]",
+ NO_STR
+ EIGRP_STR
+ "Set bandwidth informational parameter\n"
+ "Bandwidth in kilobits\n")
+{
+ nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/bandwidth",
+ NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_bandwidth(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *bandwidth = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " eigrp bandwidth %s\n", bandwidth);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hello-interval
+ */
+DEFPY(
+ eigrp_if_ip_hellointerval,
+ eigrp_if_ip_hellointerval_cmd,
+ "ip hello-interval eigrp (1-65535)$hello",
+ "Interface Internet Protocol config commands\n"
+ "Configures EIGRP hello interval\n"
+ EIGRP_STR
+ "Seconds between hello transmissions\n")
+{
+ nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hello-interval",
+ NB_OP_MODIFY, hello_str);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_if_ip_hellointerval,
+ no_eigrp_if_ip_hellointerval_cmd,
+ "no ip hello-interval eigrp [(1-65535)]",
+ NO_STR
+ "Interface Internet Protocol config commands\n"
+ "Configures EIGRP hello interval\n"
+ EIGRP_STR
+ "Seconds between hello transmissions\n")
+{
+ nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hello-interval",
+ NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+void eigrp_cli_show_hello_interval(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *hello = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " ip hello-interval eigrp %s\n", hello);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hold-time
+ */
+DEFPY(
+ eigrp_if_ip_holdinterval,
+ eigrp_if_ip_holdinterval_cmd,
+ "ip hold-time eigrp (1-65535)$hold",
+ "Interface Internet Protocol config commands\n"
+ "Configures EIGRP IPv4 hold time\n"
+ EIGRP_STR
+ "Seconds before neighbor is considered down\n")
+{
+ nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hold-time",
+ NB_OP_MODIFY, hold_str);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_if_ip_holdinterval,
+ no_eigrp_if_ip_holdinterval_cmd,
+ "no ip hold-time eigrp [(1-65535)]",
+ NO_STR
+ "Interface Internet Protocol config commands\n"
+ "Configures EIGRP IPv4 hold time\n"
+ EIGRP_STR
+ "Seconds before neighbor is considered down\n")
+{
+ nb_cli_enqueue_change(vty, "./frr-eigrpd:eigrp/hold-time",
+ NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_hold_time(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *holdtime = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " ip hold-time eigrp %s\n", holdtime);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/split-horizon
+ */
+/* NOT implemented. */
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/summarize-addresses
+ */
+DEFPY(
+ eigrp_ip_summary_address,
+ eigrp_ip_summary_address_cmd,
+ "ip summary-address eigrp (1-65535)$as A.B.C.D/M$prefix",
+ "Interface Internet Protocol config commands\n"
+ "Perform address summarization\n"
+ EIGRP_STR
+ AS_STR
+ "Summary <network>/<length>, e.g. 192.168.0.0/16\n")
+{
+ char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64];
+
+ snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']",
+ as_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-address", xpath);
+ nb_cli_enqueue_change(vty, xpath_auth, NB_OP_CREATE, prefix_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_ip_summary_address,
+ no_eigrp_ip_summary_address_cmd,
+ "no ip summary-address eigrp (1-65535)$as A.B.C.D/M$prefix",
+ NO_STR
+ "Interface Internet Protocol config commands\n"
+ "Perform address summarization\n"
+ EIGRP_STR
+ AS_STR
+ "Summary <network>/<length>, e.g. 192.168.0.0/16\n")
+{
+ char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64];
+
+ snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']",
+ as_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_auth, sizeof(xpath_auth), "%s/summarize-address", xpath);
+ nb_cli_enqueue_change(vty, xpath_auth, NB_OP_DESTROY, prefix_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_summarize_address(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const struct eigrp_interface *eif = nb_running_get_entry(dnode, NULL,
+ true);
+ const char *summarize_address = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " ip summary-address eigrp %d %s\n",
+ eif->eigrp->AS, summarize_address);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/authentication
+ */
+DEFPY(
+ eigrp_authentication_mode,
+ eigrp_authentication_mode_cmd,
+ "ip authentication mode eigrp (1-65535)$as <md5|hmac-sha-256>$crypt",
+ "Interface Internet Protocol config commands\n"
+ "Authentication subcommands\n"
+ "Mode\n"
+ EIGRP_STR
+ AS_STR
+ "Keyed message digest\n"
+ "HMAC SHA256 algorithm \n")
+{
+ char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64];
+
+ snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']",
+ as_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_auth, sizeof(xpath_auth), "%s/authentication", xpath);
+ nb_cli_enqueue_change(vty, xpath_auth, NB_OP_MODIFY, crypt);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_authentication_mode,
+ no_eigrp_authentication_mode_cmd,
+ "no ip authentication mode eigrp (1-65535)$as [<md5|hmac-sha-256>]",
+ NO_STR
+ "Interface Internet Protocol config commands\n"
+ "Authentication subcommands\n"
+ "Mode\n"
+ EIGRP_STR
+ AS_STR
+ "Keyed message digest\n"
+ "HMAC SHA256 algorithm \n")
+{
+ char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64];
+
+ snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']",
+ as_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_auth, sizeof(xpath_auth), "%s/authentication", xpath);
+ nb_cli_enqueue_change(vty, xpath_auth, NB_OP_MODIFY, "none");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_authentication(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const struct eigrp_interface *eif = nb_running_get_entry(dnode, NULL,
+ true);
+ const char *crypt = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " ip authentication mode eigrp %d %s\n",
+ eif->eigrp->AS, crypt);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/keychain
+ */
+DEFPY(
+ eigrp_authentication_keychain,
+ eigrp_authentication_keychain_cmd,
+ "ip authentication key-chain eigrp (1-65535)$as WORD$name",
+ "Interface Internet Protocol config commands\n"
+ "Authentication subcommands\n"
+ "Key-chain\n"
+ EIGRP_STR
+ AS_STR
+ "Name of key-chain\n")
+{
+ char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64];
+
+ snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']",
+ as_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_auth, sizeof(xpath_auth), "%s/keychain", xpath);
+ nb_cli_enqueue_change(vty, xpath_auth, NB_OP_MODIFY, name);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(
+ no_eigrp_authentication_keychain,
+ no_eigrp_authentication_keychain_cmd,
+ "no ip authentication key-chain eigrp (1-65535)$as [WORD]",
+ NO_STR
+ "Interface Internet Protocol config commands\n"
+ "Authentication subcommands\n"
+ "Key-chain\n"
+ EIGRP_STR
+ AS_STR
+ "Name of key-chain\n")
+{
+ char xpath[XPATH_MAXLEN], xpath_auth[XPATH_MAXLEN + 64];
+
+ snprintf(xpath, sizeof(xpath), "./frr-eigrpd:eigrp/instance[asn='%s']",
+ as_str);
+ snprintf(xpath_auth, sizeof(xpath_auth), "%s/keychain", xpath);
+ nb_cli_enqueue_change(vty, xpath_auth, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void eigrp_cli_show_keychain(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const struct eigrp_interface *eif = nb_running_get_entry(dnode, NULL,
+ true);
+ const char *keychain = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " ip authentication key-chain eigrp %d %s\n",
+ eif->eigrp->AS, keychain);
+}
+
+
+/*
+ * CLI installation procedures.
+ */
+static struct cmd_node eigrp_node = {EIGRP_NODE, "%s(config-router)# ", 1};
+
+static int eigrp_config_write(struct vty *vty)
+{
+ struct lyd_node *dnode;
+ int written = 0;
+
+ dnode = yang_dnode_get(running_config->dnode, "/frr-eigrpd:eigrpd");
+ if (dnode) {
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+ written = 1;
+ }
+
+ return written;
+}
+
+static struct cmd_node eigrp_interface_node = {INTERFACE_NODE,
+ "%s(config-if)# ", 1};
+
+
+static int eigrp_write_interface(struct vty *vty)
+{
+ struct lyd_node *dnode;
+ struct interface *ifp;
+ struct vrf *vrf;
+ int written = 0;
+
+ RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+ FOR_ALL_INTERFACES(vrf, ifp) {
+ dnode = yang_dnode_get(
+ running_config->dnode,
+ "/frr-interface:lib/interface[name='%s'][vrf='%s']",
+ ifp->name, vrf->name);
+ if (dnode == NULL)
+ continue;
+
+ written = 1;
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+ }
+ }
+
+ return written;
+}
+
+void
+eigrp_cli_init(void)
+{
+ install_element(CONFIG_NODE, &router_eigrp_cmd);
+ install_element(CONFIG_NODE, &no_router_eigrp_cmd);
+
+ install_node(&eigrp_node, eigrp_config_write);
+ install_default(EIGRP_NODE);
+
+ install_element(EIGRP_NODE, &eigrp_router_id_cmd);
+ install_element(EIGRP_NODE, &no_eigrp_router_id_cmd);
+ install_element(EIGRP_NODE, &eigrp_passive_interface_cmd);
+ install_element(EIGRP_NODE, &eigrp_timers_active_cmd);
+ install_element(EIGRP_NODE, &no_eigrp_timers_active_cmd);
+ install_element(EIGRP_NODE, &eigrp_variance_cmd);
+ install_element(EIGRP_NODE, &no_eigrp_variance_cmd);
+ install_element(EIGRP_NODE, &eigrp_maximum_paths_cmd);
+ install_element(EIGRP_NODE, &no_eigrp_maximum_paths_cmd);
+ install_element(EIGRP_NODE, &eigrp_metric_weights_cmd);
+ install_element(EIGRP_NODE, &no_eigrp_metric_weights_cmd);
+ install_element(EIGRP_NODE, &eigrp_network_cmd);
+ install_element(EIGRP_NODE, &eigrp_neighbor_cmd);
+ install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd);
+
+ install_node(&eigrp_interface_node, eigrp_write_interface);
+ if_cmd_init();
+
+ install_element(INTERFACE_NODE, &eigrp_if_delay_cmd);
+ install_element(INTERFACE_NODE, &no_eigrp_if_delay_cmd);
+ install_element(INTERFACE_NODE, &eigrp_if_bandwidth_cmd);
+ install_element(INTERFACE_NODE, &no_eigrp_if_bandwidth_cmd);
+ install_element(INTERFACE_NODE, &eigrp_if_ip_hellointerval_cmd);
+ install_element(INTERFACE_NODE, &no_eigrp_if_ip_hellointerval_cmd);
+ install_element(INTERFACE_NODE, &eigrp_if_ip_holdinterval_cmd);
+ install_element(INTERFACE_NODE, &no_eigrp_if_ip_holdinterval_cmd);
+ install_element(INTERFACE_NODE, &eigrp_ip_summary_address_cmd);
+ install_element(INTERFACE_NODE, &no_eigrp_ip_summary_address_cmd);
+ install_element(INTERFACE_NODE, &eigrp_authentication_mode_cmd);
+ install_element(INTERFACE_NODE, &no_eigrp_authentication_mode_cmd);
+ install_element(INTERFACE_NODE, &eigrp_authentication_keychain_cmd);
+ install_element(INTERFACE_NODE, &no_eigrp_authentication_keychain_cmd);
+}
diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c
index 3db59a838b..1781a88173 100644
--- a/eigrpd/eigrp_main.c
+++ b/eigrpd/eigrp_main.c
@@ -90,10 +90,16 @@ struct option longopts[] = {{0}};
/* Master of threads. */
struct thread_master *master;
+/* Forward declaration of daemon info structure. */
+static struct frr_daemon_info eigrpd_di;
+
/* SIGHUP handler. */
static void sighup(void)
{
zlog_info("SIGHUP received");
+
+ /* Reload config file. */
+ vty_read_config(NULL, eigrpd_di.config_file, config_default);
}
/* SIGINT / SIGTERM handler. */
@@ -131,6 +137,7 @@ struct quagga_signal_t eigrp_signals[] = {
};
static const struct frr_yang_module_info *eigrpd_yang_modules[] = {
+ &frr_eigrpd_info,
&frr_interface_info,
};
@@ -187,7 +194,7 @@ int main(int argc, char **argv, char **envp)
eigrp_vty_init();
keychain_init();
eigrp_vty_show_init();
- eigrp_vty_if_init();
+ eigrp_cli_init();
#ifdef HAVE_SNMP
eigrp_snmp_init();
diff --git a/eigrpd/eigrp_northbound.c b/eigrpd/eigrp_northbound.c
new file mode 100644
index 0000000000..5f0c91809e
--- /dev/null
+++ b/eigrpd/eigrp_northbound.c
@@ -0,0 +1,1584 @@
+/*
+ * EIGRP daemon northbound implementation.
+ *
+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include <zebra.h>
+
+#include "lib/keychain.h"
+#include "lib/log.h"
+#include "lib/northbound.h"
+#include "lib/table.h"
+#include "lib/vrf.h"
+#include "lib/zclient.h"
+
+#include "eigrp_structs.h"
+#include "eigrpd.h"
+#include "eigrp_interface.h"
+#include "eigrp_network.h"
+#include "eigrp_zebra.h"
+
+/* Helper functions. */
+static void redistribute_get_metrics(const struct lyd_node *dnode,
+ struct eigrp_metrics *em)
+{
+ memset(em, 0, sizeof(*em));
+
+ if (yang_dnode_exists(dnode, "./bandwidth"))
+ em->bandwidth = yang_dnode_get_uint32(dnode, "./bandwidth");
+ if (yang_dnode_exists(dnode, "./delay"))
+ em->delay = yang_dnode_get_uint32(dnode, "./delay");
+#if 0 /* TODO: How does MTU work? */
+ if (yang_dnode_exists(dnode, "./mtu"))
+ em->mtu[0] = yang_dnode_get_uint32(dnode, "./mtu");
+#endif
+ if (yang_dnode_exists(dnode, "./load"))
+ em->load = yang_dnode_get_uint32(dnode, "./load");
+ if (yang_dnode_exists(dnode, "./reliability"))
+ em->reliability = yang_dnode_get_uint32(dnode, "./reliability");
+}
+
+static struct eigrp_interface *eigrp_interface_lookup(const struct eigrp *eigrp,
+ const char *ifname)
+{
+ struct eigrp_interface *eif;
+ struct listnode *ln;
+
+ for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, ln, eif)) {
+ if (strcmp(ifname, eif->ifp->name))
+ continue;
+
+ return eif;
+ }
+
+ return NULL;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance
+ */
+static int eigrpd_instance_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* NOTHING */
+ break;
+ case NB_EV_PREPARE:
+ eigrp = eigrp_get(yang_dnode_get_string(dnode, "./asn"));
+ resource->ptr = eigrp;
+ break;
+ case NB_EV_ABORT:
+ eigrp_finish_final(resource->ptr);
+ break;
+ case NB_EV_APPLY:
+ nb_running_set_entry(dnode, resource->ptr);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int eigrpd_instance_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_unset_entry(dnode);
+ eigrp_finish_final(eigrp);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/router-id
+ */
+static int eigrpd_instance_router_id_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ipv4(&eigrp->router_id_static, dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int eigrpd_instance_router_id_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->router_id_static.s_addr = 0;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/passive-interface
+ */
+static int
+eigrpd_instance_passive_interface_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp_interface *eif;
+ struct eigrp *eigrp;
+ const char *ifname;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ eigrp = nb_running_get_entry(dnode, NULL, false);
+ if (eigrp == NULL) {
+ /*
+ * XXX: we can't verify if the interface exists
+ * and is active until EIGRP is up.
+ */
+ break;
+ }
+
+ ifname = yang_dnode_get_string(dnode, NULL);
+ eif = eigrp_interface_lookup(eigrp, ifname);
+ if (eif == NULL)
+ return NB_ERR_INCONSISTENCY;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+ eif = eigrp_interface_lookup(eigrp, ifname);
+ if (eif == NULL)
+ return NB_ERR_INCONSISTENCY;
+
+ eif->params.passive_interface = EIGRP_IF_PASSIVE;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+eigrpd_instance_passive_interface_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp_interface *eif;
+ struct eigrp *eigrp;
+ const char *ifname;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ ifname = yang_dnode_get_string(dnode, NULL);
+ eif = eigrp_interface_lookup(eigrp, ifname);
+ if (eif == NULL)
+ break;
+
+ eif->params.passive_interface = EIGRP_IF_ACTIVE;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/active-time
+ */
+static int eigrpd_instance_active_time_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* TODO: Not implemented. */
+ return NB_ERR_INCONSISTENCY;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/variance
+ */
+static int eigrpd_instance_variance_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->variance = yang_dnode_get_uint8(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int eigrpd_instance_variance_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->variance = EIGRP_VARIANCE_DEFAULT;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/maximum-paths
+ */
+static int eigrpd_instance_maximum_paths_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->max_paths = yang_dnode_get_uint8(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int eigrpd_instance_maximum_paths_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->max_paths = EIGRP_MAX_PATHS_DEFAULT;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K1
+ */
+static int
+eigrpd_instance_metric_weights_K1_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[0] = yang_dnode_get_uint8(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+eigrpd_instance_metric_weights_K1_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[0] = EIGRP_K1_DEFAULT;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K2
+ */
+static int
+eigrpd_instance_metric_weights_K2_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[1] = yang_dnode_get_uint8(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+eigrpd_instance_metric_weights_K2_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[1] = EIGRP_K2_DEFAULT;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K3
+ */
+static int
+eigrpd_instance_metric_weights_K3_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[2] = yang_dnode_get_uint8(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+eigrpd_instance_metric_weights_K3_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[2] = EIGRP_K3_DEFAULT;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K4
+ */
+static int
+eigrpd_instance_metric_weights_K4_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[3] = yang_dnode_get_uint8(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+eigrpd_instance_metric_weights_K4_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[3] = EIGRP_K4_DEFAULT;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K5
+ */
+static int
+eigrpd_instance_metric_weights_K5_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[4] = yang_dnode_get_uint8(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+eigrpd_instance_metric_weights_K5_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[4] = EIGRP_K5_DEFAULT;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/metric-weights/K6
+ */
+static int
+eigrpd_instance_metric_weights_K6_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[5] = yang_dnode_get_uint8(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+eigrpd_instance_metric_weights_K6_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp->k_values[5] = EIGRP_K6_DEFAULT;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/network
+ */
+static int eigrpd_instance_network_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct route_node *rnode;
+ struct prefix prefix;
+ struct eigrp *eigrp;
+ int exists;
+
+ yang_dnode_get_ipv4p(&prefix, dnode, NULL);
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ eigrp = nb_running_get_entry(dnode, NULL, false);
+ /* If entry doesn't exist it means the list is empty. */
+ if (eigrp == NULL)
+ break;
+
+ rnode = route_node_get(eigrp->networks, &prefix);
+ exists = (rnode->info != NULL);
+ route_unlock_node(rnode);
+ if (exists)
+ return NB_ERR_INCONSISTENCY;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ if (eigrp_network_set(eigrp, &prefix) == 0)
+ return NB_ERR_INCONSISTENCY;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int eigrpd_instance_network_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct route_node *rnode;
+ struct prefix prefix;
+ struct eigrp *eigrp;
+ int exists = 0;
+
+ yang_dnode_get_ipv4p(&prefix, dnode, NULL);
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ eigrp = nb_running_get_entry(dnode, NULL, false);
+ /* If entry doesn't exist it means the list is empty. */
+ if (eigrp == NULL)
+ break;
+
+ rnode = route_node_get(eigrp->networks, &prefix);
+ exists = (rnode->info != NULL);
+ route_unlock_node(rnode);
+ if (exists == 0)
+ return NB_ERR_INCONSISTENCY;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ eigrp_network_unset(eigrp, &prefix);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/neighbor
+ */
+static int eigrpd_instance_neighbor_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* TODO: Not implemented. */
+ return NB_ERR_INCONSISTENCY;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int eigrpd_instance_neighbor_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* TODO: Not implemented. */
+ return NB_ERR_INCONSISTENCY;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute
+ */
+static int eigrpd_instance_redistribute_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp_metrics metrics;
+ struct eigrp *eigrp;
+ uint32_t proto;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ proto = yang_dnode_get_enum(dnode, "./protocol");
+ if (vrf_bitmap_check(zclient->redist[AFI_IP][proto],
+ VRF_DEFAULT))
+ return NB_ERR_INCONSISTENCY;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ proto = yang_dnode_get_enum(dnode, "./protocol");
+ redistribute_get_metrics(dnode, &metrics);
+ eigrp_redistribute_set(eigrp, proto, metrics);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int eigrpd_instance_redistribute_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp *eigrp;
+ uint32_t proto;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ proto = yang_dnode_get_enum(dnode, "./protocol");
+ eigrp_redistribute_unset(eigrp, proto);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/route-map
+ */
+static int
+eigrpd_instance_redistribute_route_map_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* TODO: Not implemented. */
+ return NB_ERR_INCONSISTENCY;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+eigrpd_instance_redistribute_route_map_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* TODO: Not implemented. */
+ return NB_ERR_INCONSISTENCY;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/bandwidth
+ */
+static int eigrpd_instance_redistribute_metrics_bandwidth_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp_metrics metrics;
+ struct eigrp *eigrp;
+ uint32_t proto;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ proto = yang_dnode_get_enum(dnode, "../../protocol");
+ redistribute_get_metrics(dnode, &metrics);
+ eigrp_redistribute_set(eigrp, proto, metrics);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int eigrpd_instance_redistribute_metrics_bandwidth_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ struct eigrp_metrics metrics;
+ struct eigrp *eigrp;
+ uint32_t proto;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eigrp = nb_running_get_entry(dnode, NULL, true);
+ proto = yang_dnode_get_enum(dnode, "../../protocol");
+ redistribute_get_metrics(dnode, &metrics);
+ eigrp_redistribute_set(eigrp, proto, metrics);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/delay
+ */
+static int
+eigrpd_instance_redistribute_metrics_delay_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return eigrpd_instance_redistribute_metrics_bandwidth_modify(event,
+ dnode,
+ resource);
+}
+
+static int
+eigrpd_instance_redistribute_metrics_delay_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ return eigrpd_instance_redistribute_metrics_bandwidth_destroy(event,
+ dnode);
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/reliability
+ */
+static int eigrpd_instance_redistribute_metrics_reliability_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return eigrpd_instance_redistribute_metrics_bandwidth_modify(event,
+ dnode,
+ resource);
+}
+
+static int eigrpd_instance_redistribute_metrics_reliability_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ return eigrpd_instance_redistribute_metrics_bandwidth_destroy(event,
+ dnode);
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/load
+ */
+static int
+eigrpd_instance_redistribute_metrics_load_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return eigrpd_instance_redistribute_metrics_bandwidth_modify(event,
+ dnode,
+ resource);
+}
+
+static int
+eigrpd_instance_redistribute_metrics_load_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ return eigrpd_instance_redistribute_metrics_bandwidth_destroy(event,
+ dnode);
+}
+
+/*
+ * XPath: /frr-eigrpd:eigrpd/instance/redistribute/metrics/mtu
+ */
+static int
+eigrpd_instance_redistribute_metrics_mtu_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ return eigrpd_instance_redistribute_metrics_bandwidth_modify(event,
+ dnode,
+ resource);
+}
+
+static int
+eigrpd_instance_redistribute_metrics_mtu_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ return eigrpd_instance_redistribute_metrics_bandwidth_destroy(event,
+ dnode);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/delay
+ */
+static int lib_interface_eigrp_delay_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp_interface *ei;
+ struct interface *ifp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ ifp = nb_running_get_entry(dnode, NULL, false);
+ if (ifp == NULL) {
+ /*
+ * XXX: we can't verify if the interface exists
+ * and is active until EIGRP is up.
+ */
+ break;
+ }
+
+ ei = ifp->info;
+ if (ei == NULL)
+ return NB_ERR_INCONSISTENCY;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ei = ifp->info;
+ if (ei == NULL)
+ return NB_ERR_INCONSISTENCY;
+
+ ei->params.delay = yang_dnode_get_uint32(dnode, NULL);
+ eigrp_if_reset(ifp);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/bandwidth
+ */
+static int lib_interface_eigrp_bandwidth_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct eigrp_interface *ei;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ ifp = nb_running_get_entry(dnode, NULL, false);
+ if (ifp == NULL) {
+ /*
+ * XXX: we can't verify if the interface exists
+ * and is active until EIGRP is up.
+ */
+ break;
+ }
+
+ ei = ifp->info;
+ if (ei == NULL)
+ return NB_ERR_INCONSISTENCY;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ei = ifp->info;
+ if (ei == NULL)
+ return NB_ERR_INCONSISTENCY;
+
+ ei->params.bandwidth = yang_dnode_get_uint32(dnode, NULL);
+ eigrp_if_reset(ifp);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hello-interval
+ */
+static int
+lib_interface_eigrp_hello_interval_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct eigrp_interface *ei;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ ifp = nb_running_get_entry(dnode, NULL, false);
+ if (ifp == NULL) {
+ /*
+ * XXX: we can't verify if the interface exists
+ * and is active until EIGRP is up.
+ */
+ break;
+ }
+
+ ei = ifp->info;
+ if (ei == NULL)
+ return NB_ERR_INCONSISTENCY;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ei = ifp->info;
+ if (ei == NULL)
+ return NB_ERR_INCONSISTENCY;
+
+ ei->params.v_hello = yang_dnode_get_uint16(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/hold-time
+ */
+static int lib_interface_eigrp_hold_time_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ struct eigrp_interface *ei;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ ifp = nb_running_get_entry(dnode, NULL, false);
+ if (ifp == NULL) {
+ /*
+ * XXX: we can't verify if the interface exists
+ * and is active until EIGRP is up.
+ */
+ break;
+ }
+
+ ei = ifp->info;
+ if (ei == NULL)
+ return NB_ERR_INCONSISTENCY;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ ei = ifp->info;
+ if (ei == NULL)
+ return NB_ERR_INCONSISTENCY;
+
+ ei->params.v_wait = yang_dnode_get_uint16(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/split-horizon
+ */
+static int
+lib_interface_eigrp_split_horizon_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* TODO: Not implemented. */
+ return NB_ERR_INCONSISTENCY;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance
+ */
+static int lib_interface_eigrp_instance_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp_interface *eif;
+ struct interface *ifp;
+ struct eigrp *eigrp;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ ifp = nb_running_get_entry(dnode, NULL, false);
+ if (ifp == NULL) {
+ /*
+ * XXX: we can't verify if the interface exists
+ * and is active until EIGRP is up.
+ */
+ break;
+ }
+
+ eigrp = eigrp_get(yang_dnode_get_string(dnode, "./asn"));
+ eif = eigrp_interface_lookup(eigrp, ifp->name);
+ if (eif == NULL)
+ return NB_ERR_INCONSISTENCY;
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ eigrp = eigrp_get(yang_dnode_get_string(dnode, "./asn"));
+ eif = eigrp_interface_lookup(eigrp, ifp->name);
+ if (eif == NULL)
+ return NB_ERR_INCONSISTENCY;
+
+ nb_running_set_entry(dnode, eif);
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int lib_interface_eigrp_instance_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ nb_running_unset_entry(dnode);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/summarize-addresses
+ */
+static int lib_interface_eigrp_instance_summarize_addresses_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* TODO: Not implemented. */
+ return NB_ERR_INCONSISTENCY;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int lib_interface_eigrp_instance_summarize_addresses_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ switch (event) {
+ case NB_EV_VALIDATE:
+ /* TODO: Not implemented. */
+ return NB_ERR_INCONSISTENCY;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /* NOTHING */
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/authentication
+ */
+static int
+lib_interface_eigrp_instance_authentication_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp_interface *eif;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eif = nb_running_get_entry(dnode, NULL, true);
+ eif->params.auth_type = yang_dnode_get_enum(dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-eigrpd:eigrp/instance/keychain
+ */
+static int
+lib_interface_eigrp_instance_keychain_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct eigrp_interface *eif;
+ struct keychain *keychain;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ keychain = keychain_lookup(yang_dnode_get_string(dnode, NULL));
+ if (keychain == NULL)
+ return NB_ERR_INCONSISTENCY;
+ break;
+ case NB_EV_PREPARE:
+ resource->ptr = strdup(yang_dnode_get_string(dnode, NULL));
+ if (resource->ptr == NULL)
+ return NB_ERR_RESOURCE;
+ break;
+ case NB_EV_ABORT:
+ free(resource->ptr);
+ resource->ptr = NULL;
+ break;
+ case NB_EV_APPLY:
+ eif = nb_running_get_entry(dnode, NULL, true);
+ if (eif->params.auth_keychain)
+ free(eif->params.auth_keychain);
+
+ eif->params.auth_keychain = resource->ptr;
+ break;
+ }
+
+ return NB_OK;
+}
+
+static int
+lib_interface_eigrp_instance_keychain_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct eigrp_interface *eif;
+
+ switch (event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ /* NOTHING */
+ break;
+ case NB_EV_APPLY:
+ eif = nb_running_get_entry(dnode, NULL, true);
+ if (eif->params.auth_keychain)
+ free(eif->params.auth_keychain);
+
+ eif->params.auth_keychain = NULL;
+ break;
+ }
+
+ return NB_OK;
+}
+
+/* clang-format off */
+const struct frr_yang_module_info frr_eigrpd_info = {
+ .name = "frr-eigrpd",
+ .nodes = {
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance",
+ .cbs = {
+ .create = eigrpd_instance_create,
+ .destroy = eigrpd_instance_destroy,
+ .cli_show = eigrp_cli_show_header,
+ .cli_show_end = eigrp_cli_show_end_header,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/router-id",
+ .cbs = {
+ .modify = eigrpd_instance_router_id_modify,
+ .destroy = eigrpd_instance_router_id_destroy,
+ .cli_show = eigrp_cli_show_router_id,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/passive-interface",
+ .cbs = {
+ .create = eigrpd_instance_passive_interface_create,
+ .destroy = eigrpd_instance_passive_interface_destroy,
+ .cli_show = eigrp_cli_show_passive_interface,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/active-time",
+ .cbs = {
+ .modify = eigrpd_instance_active_time_modify,
+ .cli_show = eigrp_cli_show_active_time,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/variance",
+ .cbs = {
+ .modify = eigrpd_instance_variance_modify,
+ .destroy = eigrpd_instance_variance_destroy,
+ .cli_show = eigrp_cli_show_variance,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/maximum-paths",
+ .cbs = {
+ .modify = eigrpd_instance_maximum_paths_modify,
+ .destroy = eigrpd_instance_maximum_paths_destroy,
+ .cli_show = eigrp_cli_show_maximum_paths,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights",
+ .cbs = {
+ .cli_show = eigrp_cli_show_metrics,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K1",
+ .cbs = {
+ .modify = eigrpd_instance_metric_weights_K1_modify,
+ .destroy = eigrpd_instance_metric_weights_K1_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K2",
+ .cbs = {
+ .modify = eigrpd_instance_metric_weights_K2_modify,
+ .destroy = eigrpd_instance_metric_weights_K2_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K3",
+ .cbs = {
+ .modify = eigrpd_instance_metric_weights_K3_modify,
+ .destroy = eigrpd_instance_metric_weights_K3_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K4",
+ .cbs = {
+ .modify = eigrpd_instance_metric_weights_K4_modify,
+ .destroy = eigrpd_instance_metric_weights_K4_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K5",
+ .cbs = {
+ .modify = eigrpd_instance_metric_weights_K5_modify,
+ .destroy = eigrpd_instance_metric_weights_K5_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/metric-weights/K6",
+ .cbs = {
+ .modify = eigrpd_instance_metric_weights_K6_modify,
+ .destroy = eigrpd_instance_metric_weights_K6_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/network",
+ .cbs = {
+ .create = eigrpd_instance_network_create,
+ .destroy = eigrpd_instance_network_destroy,
+ .cli_show = eigrp_cli_show_network,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/neighbor",
+ .cbs = {
+ .create = eigrpd_instance_neighbor_create,
+ .destroy = eigrpd_instance_neighbor_destroy,
+ .cli_show = eigrp_cli_show_neighbor,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/redistribute",
+ .cbs = {
+ .create = eigrpd_instance_redistribute_create,
+ .destroy = eigrpd_instance_redistribute_destroy,
+ .cli_show = eigrp_cli_show_redistribute,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/route-map",
+ .cbs = {
+ .modify = eigrpd_instance_redistribute_route_map_modify,
+ .destroy = eigrpd_instance_redistribute_route_map_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/bandwidth",
+ .cbs = {
+ .modify = eigrpd_instance_redistribute_metrics_bandwidth_modify,
+ .destroy = eigrpd_instance_redistribute_metrics_bandwidth_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/delay",
+ .cbs = {
+ .modify = eigrpd_instance_redistribute_metrics_delay_modify,
+ .destroy = eigrpd_instance_redistribute_metrics_delay_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/reliability",
+ .cbs = {
+ .modify = eigrpd_instance_redistribute_metrics_reliability_modify,
+ .destroy = eigrpd_instance_redistribute_metrics_reliability_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/load",
+ .cbs = {
+ .modify = eigrpd_instance_redistribute_metrics_load_modify,
+ .destroy = eigrpd_instance_redistribute_metrics_load_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-eigrpd:eigrpd/instance/redistribute/metrics/mtu",
+ .cbs = {
+ .modify = eigrpd_instance_redistribute_metrics_mtu_modify,
+ .destroy = eigrpd_instance_redistribute_metrics_mtu_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/delay",
+ .cbs = {
+ .modify = lib_interface_eigrp_delay_modify,
+ .cli_show = eigrp_cli_show_delay,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/bandwidth",
+ .cbs = {
+ .modify = lib_interface_eigrp_bandwidth_modify,
+ .cli_show = eigrp_cli_show_bandwidth,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/hello-interval",
+ .cbs = {
+ .modify = lib_interface_eigrp_hello_interval_modify,
+ .cli_show = eigrp_cli_show_hello_interval,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/hold-time",
+ .cbs = {
+ .modify = lib_interface_eigrp_hold_time_modify,
+ .cli_show = eigrp_cli_show_hold_time,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/split-horizon",
+ .cbs = {
+ .modify = lib_interface_eigrp_split_horizon_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance",
+ .cbs = {
+ .create = lib_interface_eigrp_instance_create,
+ .destroy = lib_interface_eigrp_instance_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance/summarize-addresses",
+ .cbs = {
+ .create = lib_interface_eigrp_instance_summarize_addresses_create,
+ .destroy = lib_interface_eigrp_instance_summarize_addresses_destroy,
+ .cli_show = eigrp_cli_show_summarize_address,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance/authentication",
+ .cbs = {
+ .modify = lib_interface_eigrp_instance_authentication_modify,
+ .cli_show = eigrp_cli_show_authentication,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-eigrpd:eigrp/instance/keychain",
+ .cbs = {
+ .modify = lib_interface_eigrp_instance_keychain_modify,
+ .destroy = lib_interface_eigrp_instance_keychain_destroy,
+ .cli_show = eigrp_cli_show_keychain,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/eigrpd/eigrp_vty.c b/eigrpd/eigrp_vty.c
index d51faaac59..207622e659 100644
--- a/eigrpd/eigrp_vty.c
+++ b/eigrpd/eigrp_vty.c
@@ -59,402 +59,6 @@
#include "eigrpd/eigrp_vty_clippy.c"
#endif
-static int config_write_network(struct vty *vty, struct eigrp *eigrp)
-{
- struct route_node *rn;
- int i;
-
- /* `network area' print. */
- for (rn = route_top(eigrp->networks); rn; rn = route_next(rn))
- if (rn->info) {
- /* Network print. */
- vty_out(vty, " network %s/%d \n",
- inet_ntoa(rn->p.u.prefix4), rn->p.prefixlen);
- }
-
- if (eigrp->max_paths != EIGRP_MAX_PATHS_DEFAULT)
- vty_out(vty, " maximum-paths %d\n", eigrp->max_paths);
-
- if (eigrp->variance != EIGRP_VARIANCE_DEFAULT)
- vty_out(vty, " variance %d\n", eigrp->variance);
-
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
- if (i != zclient->redist_default
- && vrf_bitmap_check(zclient->redist[AFI_IP][i],
- VRF_DEFAULT))
- vty_out(vty, " redistribute %s\n",
- zebra_route_string(i));
-
- /*Separate EIGRP configuration from the rest of the config*/
- vty_out(vty, "!\n");
-
- return 0;
-}
-
-static int config_write_interfaces(struct vty *vty, struct eigrp *eigrp)
-{
- struct eigrp_interface *ei;
- struct listnode *node;
-
- for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) {
- vty_frame(vty, "interface %s\n", ei->ifp->name);
-
- if (ei->params.auth_type == EIGRP_AUTH_TYPE_MD5) {
- vty_out(vty, " ip authentication mode eigrp %d md5\n",
- eigrp->AS);
- }
-
- if (ei->params.auth_type == EIGRP_AUTH_TYPE_SHA256) {
- vty_out(vty,
- " ip authentication mode eigrp %d hmac-sha-256\n",
- eigrp->AS);
- }
-
- if (ei->params.auth_keychain) {
- vty_out(vty,
- " ip authentication key-chain eigrp %d %s\n",
- eigrp->AS, ei->params.auth_keychain);
- }
-
- if (ei->params.v_hello != EIGRP_HELLO_INTERVAL_DEFAULT) {
- vty_out(vty, " ip hello-interval eigrp %d\n",
- ei->params.v_hello);
- }
-
- if (ei->params.v_wait != EIGRP_HOLD_INTERVAL_DEFAULT) {
- vty_out(vty, " ip hold-time eigrp %d\n",
- ei->params.v_wait);
- }
-
- /*Separate this EIGRP interface configuration from the others*/
- vty_endframe(vty, "!\n");
- }
-
- return 0;
-}
-
-static int eigrp_write_interface(struct vty *vty)
-{
- struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
- struct interface *ifp;
- struct eigrp_interface *ei;
-
- FOR_ALL_INTERFACES (vrf, ifp) {
- ei = ifp->info;
- if (!ei)
- continue;
-
- vty_frame(vty, "interface %s\n", ifp->name);
-
- if (ifp->desc)
- vty_out(vty, " description %s\n", ifp->desc);
-
- if (ei->params.bandwidth != EIGRP_BANDWIDTH_DEFAULT)
- vty_out(vty, " bandwidth %u\n", ei->params.bandwidth);
- if (ei->params.delay != EIGRP_DELAY_DEFAULT)
- vty_out(vty, " delay %u\n", ei->params.delay);
- if (ei->params.v_hello != EIGRP_HELLO_INTERVAL_DEFAULT)
- vty_out(vty, " ip hello-interval eigrp %u\n",
- ei->params.v_hello);
- if (ei->params.v_wait != EIGRP_HOLD_INTERVAL_DEFAULT)
- vty_out(vty, " ip hold-time eigrp %u\n",
- ei->params.v_wait);
-
- vty_endframe(vty, "!\n");
- }
-
- return 0;
-}
-
-/**
- * Writes distribute lists to config
- */
-static int config_write_eigrp_distribute(struct vty *vty, struct eigrp *eigrp)
-{
- int write = 0;
-
- /* Distribute configuration. */
- write += config_write_distribute(vty, eigrp->distribute_ctx);
-
- return write;
-}
-
-/**
- * Writes 'router eigrp' section to config
- */
-static int config_write_eigrp_router(struct vty *vty, struct eigrp *eigrp)
-{
- int write = 0;
-
- /* `router eigrp' print. */
- vty_out(vty, "router eigrp %d\n", eigrp->AS);
-
- write++;
-
- /* Router ID print. */
- if (eigrp->router_id_static.s_addr != 0) {
- vty_out(vty, " eigrp router-id %s\n",
- inet_ntoa(eigrp->router_id_static));
- }
-
- /* Network area print. */
- config_write_network(vty, eigrp);
-
- /* Distribute-list and default-information print. */
- config_write_eigrp_distribute(vty, eigrp);
-
- /*Separate EIGRP configuration from the rest of the config*/
- vty_out(vty, "!\n");
-
- return write;
-}
-
-DEFUN_NOSH (router_eigrp,
- router_eigrp_cmd,
- "router eigrp (1-65535)",
- "Enable a routing process\n"
- "Start EIGRP configuration\n"
- "AS Number to use\n")
-{
- struct eigrp *eigrp = eigrp_get(argv[2]->arg);
- VTY_PUSH_CONTEXT(EIGRP_NODE, eigrp);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_router_eigrp,
- no_router_eigrp_cmd,
- "no router eigrp (1-65535)",
- NO_STR
- "Routing process\n"
- "EIGRP configuration\n"
- "AS number to use\n")
-{
- vty->node = CONFIG_NODE;
-
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- if (eigrp->AS != atoi(argv[3]->arg)) {
- vty_out(vty, "%% Attempting to deconfigure non-existent AS\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- eigrp_finish_final(eigrp);
-
- return CMD_SUCCESS;
-}
-
-DEFPY (eigrp_router_id,
- eigrp_router_id_cmd,
- "eigrp router-id A.B.C.D$addr",
- "EIGRP specific commands\n"
- "Router ID for this EIGRP process\n"
- "EIGRP Router-ID in IP address format\n")
-{
- VTY_DECLVAR_CONTEXT(eigrp, eigrp);
-
- eigrp->router_id_static = addr;
-
- return CMD_SUCCESS;
-}
-
-DEFPY (no_eigrp_router_id,
- no_eigrp_router_id_cmd,
- "no eigrp router-id [A.B.C.D$addr]",
- NO_STR
- "EIGRP specific commands\n"
- "Router ID for this EIGRP process\n"
- "EIGRP Router-ID in IP address format\n")
-{
- VTY_DECLVAR_CONTEXT(eigrp, eigrp);
-
- eigrp->router_id_static.s_addr = 0;
-
- return CMD_SUCCESS;
-}
-
-DEFUN (eigrp_passive_interface,
- eigrp_passive_interface_cmd,
- "passive-interface IFNAME",
- "Suppress routing updates on an interface\n"
- "Interface to suppress on\n")
-{
- VTY_DECLVAR_CONTEXT(eigrp, eigrp);
- struct eigrp_interface *ei;
- struct listnode *node;
- char *ifname = argv[1]->arg;
-
- for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) {
- if (strcmp(ifname, ei->ifp->name) == 0) {
- ei->params.passive_interface = EIGRP_IF_PASSIVE;
- return CMD_SUCCESS;
- }
- }
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_passive_interface,
- no_eigrp_passive_interface_cmd,
- "no passive-interface IFNAME",
- NO_STR
- "Suppress routing updates on an interface\n"
- "Interface to suppress on\n")
-{
- VTY_DECLVAR_CONTEXT(eigrp, eigrp);
- struct eigrp_interface *ei;
- struct listnode *node;
- char *ifname = argv[2]->arg;
-
- for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) {
- if (strcmp(ifname, ei->ifp->name) == 0) {
- ei->params.passive_interface = EIGRP_IF_ACTIVE;
- return CMD_SUCCESS;
- }
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN (eigrp_timers_active,
- eigrp_timers_active_cmd,
- "timers active-time <(1-65535)|disabled>",
- "Adjust routing timers\n"
- "Time limit for active state\n"
- "Active state time limit in seconds\n"
- "Disable time limit for active state\n")
-{
- // struct eigrp *eigrp = vty->index;
- /*TODO: */
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_timers_active,
- no_eigrp_timers_active_cmd,
- "no timers active-time <(1-65535)|disabled>",
- NO_STR
- "Adjust routing timers\n"
- "Time limit for active state\n"
- "Active state time limit in seconds\n"
- "Disable time limit for active state\n")
-{
- // struct eigrp *eigrp = vty->index;
- /*TODO: */
-
- return CMD_SUCCESS;
-}
-
-
-DEFUN (eigrp_metric_weights,
- eigrp_metric_weights_cmd,
- "metric weights (0-255) (0-255) (0-255) (0-255) (0-255) ",
- "Modify metrics and parameters for advertisement\n"
- "Modify metric coefficients\n"
- "K1\n"
- "K2\n"
- "K3\n"
- "K4\n"
- "K5\n")
-{
- // struct eigrp *eigrp = vty->index;
- /*TODO: */
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_metric_weights,
- no_eigrp_metric_weights_cmd,
- "no metric weights (0-255) (0-255) (0-255) (0-255) (0-255)",
- NO_STR
- "Modify metrics and parameters for advertisement\n"
- "Modify metric coefficients\n"
- "K1\n"
- "K2\n"
- "K3\n"
- "K4\n"
- "K5\n")
-{
- // struct eigrp *eigrp = vty->index;
- /*TODO: */
-
- return CMD_SUCCESS;
-}
-
-
-DEFUN (eigrp_network,
- eigrp_network_cmd,
- "network A.B.C.D/M",
- "Enable routing on an IP network\n"
- "EIGRP network prefix\n")
-{
- VTY_DECLVAR_CONTEXT(eigrp, eigrp);
- struct prefix p;
- int ret;
-
- (void)str2prefix(argv[1]->arg, &p);
-
- ret = eigrp_network_set(eigrp, &p);
-
- if (ret == 0) {
- vty_out(vty, "There is already same network statement.\n");
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_network,
- no_eigrp_network_cmd,
- "no network A.B.C.D/M",
- NO_STR
- "Disable routing on an IP network\n"
- "EIGRP network prefix\n")
-{
- VTY_DECLVAR_CONTEXT(eigrp, eigrp);
- struct prefix p;
- int ret;
-
- (void)str2prefix(argv[2]->arg, &p);
-
- ret = eigrp_network_unset(eigrp, &p);
-
- if (ret == 0) {
- vty_out(vty, "Can't find specified network configuration.\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN (eigrp_neighbor,
- eigrp_neighbor_cmd,
- "neighbor A.B.C.D",
- "Specify a neighbor router\n"
- "Neighbor address\n")
-{
- // struct eigrp *eigrp = vty->index;
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_neighbor,
- no_eigrp_neighbor_cmd,
- "no neighbor A.B.C.D",
- NO_STR
- "Specify a neighbor router\n"
- "Neighbor address\n")
-{
- // struct eigrp *eigrp = vty->index;
-
- return CMD_SUCCESS;
-}
-
static void eigrp_vty_display_prefix_entry(struct vty *vty,
struct eigrp *eigrp,
struct eigrp_prefix_entry *pe,
@@ -650,585 +254,6 @@ DEFUN (show_ip_eigrp_neighbors,
return CMD_SUCCESS;
}
-DEFUN (eigrp_if_delay,
- eigrp_if_delay_cmd,
- "delay (1-16777215)",
- "Specify interface throughput delay\n"
- "Throughput delay (tens of microseconds)\n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- struct eigrp *eigrp;
- uint32_t delay;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
-
- return CMD_SUCCESS;
- }
-
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
- delay = atoi(argv[1]->arg);
-
- ei->params.delay = delay;
- eigrp_if_reset(ifp);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_if_delay,
- no_eigrp_if_delay_cmd,
- "no delay (1-16777215)",
- NO_STR
- "Specify interface throughput delay\n"
- "Throughput delay (tens of microseconds)\n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
-
- return CMD_SUCCESS;
- }
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
-
- ei->params.delay = EIGRP_DELAY_DEFAULT;
- eigrp_if_reset(ifp);
-
- return CMD_SUCCESS;
-}
-
-DEFPY (eigrp_if_bandwidth,
- eigrp_if_bandwidth_cmd,
- "eigrp bandwidth (1-10000000)$bw",
- "EIGRP specific commands\n"
- "Set bandwidth informational parameter\n"
- "Bandwidth in kilobits\n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
-
- ei->params.bandwidth = bw;
- eigrp_if_reset(ifp);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_if_bandwidth,
- no_eigrp_if_bandwidth_cmd,
- "no eigrp bandwidth [(1-10000000)]",
- NO_STR
- "EIGRP specific commands\n"
- "Set bandwidth informational parameter\n"
- "Bandwidth in kilobits\n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
-
- ei->params.bandwidth = EIGRP_BANDWIDTH_DEFAULT;
- eigrp_if_reset(ifp);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (eigrp_if_ip_hellointerval,
- eigrp_if_ip_hellointerval_cmd,
- "ip hello-interval eigrp (1-65535)",
- "Interface Internet Protocol config commands\n"
- "Configures EIGRP hello interval\n"
- "Enhanced Interior Gateway Routing Protocol (EIGRP)\n"
- "Seconds between hello transmissions\n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- uint32_t hello;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
-
- hello = atoi(argv[3]->arg);
-
- ei->params.v_hello = hello;
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_if_ip_hellointerval,
- no_eigrp_if_ip_hellointerval_cmd,
- "no ip hello-interval eigrp [(1-65535)]",
- NO_STR
- "Interface Internet Protocol config commands\n"
- "Configures EIGRP hello interval\n"
- "Enhanced Interior Gateway Routing Protocol (EIGRP)\n"
- "Seconds between hello transmissions\n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
-
- ei->params.v_hello = EIGRP_HELLO_INTERVAL_DEFAULT;
-
- THREAD_TIMER_OFF(ei->t_hello);
- thread_add_timer(master, eigrp_hello_timer, ei, 1, &ei->t_hello);
-
- return CMD_SUCCESS;
-}
-
-DEFUN (eigrp_if_ip_holdinterval,
- eigrp_if_ip_holdinterval_cmd,
- "ip hold-time eigrp (1-65535)",
- "Interface Internet Protocol config commands\n"
- "Configures EIGRP IPv4 hold time\n"
- "Enhanced Interior Gateway Routing Protocol (EIGRP)\n"
- "Seconds before neighbor is considered down\n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- uint32_t hold;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
-
- hold = atoi(argv[3]->arg);
-
- ei->params.v_wait = hold;
-
- return CMD_SUCCESS;
-}
-
-DEFUN (eigrp_ip_summary_address,
- eigrp_ip_summary_address_cmd,
- "ip summary-address eigrp (1-65535) A.B.C.D/M",
- "Interface Internet Protocol config commands\n"
- "Perform address summarization\n"
- "Enhanced Interior Gateway Routing Protocol (EIGRP)\n"
- "AS number\n"
- "Summary <network>/<length>, e.g. 192.168.0.0/16\n")
-{
- // VTY_DECLVAR_CONTEXT(interface, ifp);
- // uint32_t AS;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- // AS = atoi (argv[3]->arg);
-
- /*TODO: */
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_ip_summary_address,
- no_eigrp_ip_summary_address_cmd,
- "no ip summary-address eigrp (1-65535) A.B.C.D/M",
- NO_STR
- "Interface Internet Protocol config commands\n"
- "Perform address summarization\n"
- "Enhanced Interior Gateway Routing Protocol (EIGRP)\n"
- "AS number\n"
- "Summary <network>/<length>, e.g. 192.168.0.0/16\n")
-{
- // VTY_DECLVAR_CONTEXT(interface, ifp);
- // uint32_t AS;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- // AS = atoi (argv[4]->arg);
-
- /*TODO: */
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_if_ip_holdinterval,
- no_eigrp_if_ip_holdinterval_cmd,
- "no ip hold-time eigrp",
- NO_STR
- "Interface Internet Protocol config commands\n"
- "Configures EIGRP hello interval\n"
- "Enhanced Interior Gateway Routing Protocol (EIGRP)\n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
-
- ei->params.v_wait = EIGRP_HOLD_INTERVAL_DEFAULT;
-
- return CMD_SUCCESS;
-}
-
-static int str2auth_type(const char *str, struct eigrp_interface *ei)
-{
- /* Sanity check. */
- if (str == NULL)
- return CMD_WARNING_CONFIG_FAILED;
-
- if (strncmp(str, "md5", 3) == 0) {
- ei->params.auth_type = EIGRP_AUTH_TYPE_MD5;
- return CMD_SUCCESS;
- } else if (strncmp(str, "hmac-sha-256", 12) == 0) {
- ei->params.auth_type = EIGRP_AUTH_TYPE_SHA256;
- return CMD_SUCCESS;
- }
-
- return CMD_WARNING_CONFIG_FAILED;
-}
-
-DEFUN (eigrp_authentication_mode,
- eigrp_authentication_mode_cmd,
- "ip authentication mode eigrp (1-65535) <md5|hmac-sha-256>",
- "Interface Internet Protocol config commands\n"
- "Authentication subcommands\n"
- "Mode\n"
- "Enhanced Interior Gateway Routing Protocol (EIGRP)\n"
- "Autonomous system number\n"
- "Keyed message digest\n"
- "HMAC SHA256 algorithm \n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
-
- // if(strncmp(argv[2], "md5",3))
- // IF_DEF_PARAMS (ifp)->auth_type = EIGRP_AUTH_TYPE_MD5;
- // else if(strncmp(argv[2], "hmac-sha-256",12))
- // IF_DEF_PARAMS (ifp)->auth_type = EIGRP_AUTH_TYPE_SHA256;
-
- return str2auth_type(argv[5]->arg, ei);
-}
-
-DEFUN (no_eigrp_authentication_mode,
- no_eigrp_authentication_mode_cmd,
- "no ip authentication mode eigrp (1-65535) <md5|hmac-sha-256>",
- "Disable\n"
- "Interface Internet Protocol config commands\n"
- "Authentication subcommands\n"
- "Mode\n"
- "Enhanced Interior Gateway Routing Protocol (EIGRP)\n"
- "Autonomous system number\n"
- "Keyed message digest\n"
- "HMAC SHA256 algorithm \n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, " EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
-
- ei->params.auth_type = EIGRP_AUTH_TYPE_NONE;
-
- return CMD_SUCCESS;
-}
-
-DEFPY (eigrp_authentication_keychain,
- eigrp_authentication_keychain_cmd,
- "[no] ip authentication key-chain eigrp (1-65535)$as WORD$name",
- NO_STR
- "Interface Internet Protocol config commands\n"
- "Authentication subcommands\n"
- "Key-chain\n"
- "Enhanced Interior Gateway Routing Protocol (EIGRP)\n"
- "Autonomous system number\n"
- "Name of key-chain\n")
-{
- VTY_DECLVAR_CONTEXT(interface, ifp);
- struct eigrp_interface *ei = ifp->info;
- struct eigrp *eigrp;
- struct keychain *keychain;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, "EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- if (!ei) {
- vty_out(vty, " EIGRP not configured on this interface\n");
- return CMD_SUCCESS;
- }
-
- if (no) {
- if ((ei->params.auth_keychain != NULL)
- && (strcmp(ei->params.auth_keychain, name) == 0)) {
- free(ei->params.auth_keychain);
- ei->params.auth_keychain = NULL;
- } else
- vty_out(vty,
- "Key chain with specified name not configured on interface\n");
- return CMD_SUCCESS;
- }
-
- keychain = keychain_lookup(name);
- if (keychain != NULL) {
- if (ei->params.auth_keychain) {
- free(ei->params.auth_keychain);
- ei->params.auth_keychain = strdup(keychain->name);
- } else
- ei->params.auth_keychain = strdup(keychain->name);
- } else {
- vty_out(vty,
- "Key chain with specified name not found\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN (eigrp_redistribute_source_metric,
- eigrp_redistribute_source_metric_cmd,
- "redistribute " FRR_REDIST_STR_EIGRPD
- " [metric (1-4294967295) (0-4294967295) (0-255) (1-255) (1-65535)]",
- REDIST_STR
- FRR_REDIST_HELP_STR_EIGRPD
- "Metric for redistributed routes\n"
- "Bandwidth metric in Kbits per second\n"
- "EIGRP delay metric, in 10 microsecond units\n"
- "EIGRP reliability metric where 255 is 100% reliable2 ?\n"
- "EIGRP Effective bandwidth metric (Loading) where 255 is 100% loaded\n"
- "EIGRP MTU of the path\n")
-{
- VTY_DECLVAR_CONTEXT(eigrp, eigrp);
- struct eigrp_metrics metrics_from_command = {0};
- int source;
- int idx = 0;
-
- /* Get distribute source. */
- argv_find(argv, argc, "redistribute", &idx);
- source = proto_redistnum(AFI_IP, argv[idx + 1]->text);
- if (source < 0) {
- vty_out(vty, "%% Invalid route type\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* Get metrics values */
-
- return eigrp_redistribute_set(eigrp, source, metrics_from_command);
-}
-
-DEFUN (no_eigrp_redistribute_source_metric,
- no_eigrp_redistribute_source_metric_cmd,
- "no redistribute " FRR_REDIST_STR_EIGRPD
- " [metric (1-4294967295) (0-4294967295) (0-255) (1-255) (1-65535)]",
- "Disable\n"
- REDIST_STR
- FRR_REDIST_HELP_STR_EIGRPD
- "Metric for redistributed routes\n"
- "Bandwidth metric in Kbits per second\n"
- "EIGRP delay metric, in 10 microsecond units\n"
- "EIGRP reliability metric where 255 is 100% reliable2 ?\n"
- "EIGRP Effective bandwidth metric (Loading) where 255 is 100% loaded\n"
- "EIGRP MTU of the path\n")
-{
- VTY_DECLVAR_CONTEXT(eigrp, eigrp);
- int source;
- int idx = 0;
-
- /* Get distribute source. */
- argv_find(argv, argc, "redistribute", &idx);
- source = proto_redistnum(AFI_IP, argv[idx + 1]->text);
- if (source < 0) {
- vty_out(vty, "%% Invalid route type\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- /* Get metrics values */
-
- return eigrp_redistribute_unset(eigrp, source);
-}
-
-DEFUN (eigrp_variance,
- eigrp_variance_cmd,
- "variance (1-128)",
- "Control load balancing variance\n"
- "Metric variance multiplier\n")
-{
- struct eigrp *eigrp;
- uint8_t variance;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, "EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
- variance = atoi(argv[1]->arg);
-
- eigrp->variance = variance;
-
- /*TODO: */
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_variance,
- no_eigrp_variance_cmd,
- "no variance (1-128)",
- "Disable\n"
- "Control load balancing variance\n"
- "Metric variance multiplier\n")
-{
- struct eigrp *eigrp;
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, "EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- eigrp->variance = EIGRP_VARIANCE_DEFAULT;
-
- /*TODO: */
-
- return CMD_SUCCESS;
-}
-
-DEFUN (eigrp_maximum_paths,
- eigrp_maximum_paths_cmd,
- "maximum-paths (1-32)",
- "Forward packets over multiple paths\n"
- "Number of paths\n")
-{
- struct eigrp *eigrp;
- uint8_t max;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, "EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- max = atoi(argv[1]->arg);
-
- eigrp->max_paths = max;
-
- /*TODO: */
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_eigrp_maximum_paths,
- no_eigrp_maximum_paths_cmd,
- "no maximum-paths (1-32)",
- NO_STR
- "Forward packets over multiple paths\n"
- "Number of paths\n")
-{
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp == NULL) {
- vty_out(vty, "EIGRP Routing Process not enabled\n");
- return CMD_SUCCESS;
- }
-
- eigrp->max_paths = EIGRP_MAX_PATHS_DEFAULT;
-
- /*TODO: */
-
- return CMD_SUCCESS;
-}
-
/*
* Execute hard restart for all neighbors
*/
@@ -1492,40 +517,6 @@ DEFUN (clear_ip_eigrp_neighbors_IP_soft,
return CMD_SUCCESS;
}
-static struct cmd_node eigrp_node = {EIGRP_NODE, "%s(config-router)# ", 1};
-
-/* Save EIGRP configuration */
-static int eigrp_config_write(struct vty *vty)
-{
- struct eigrp *eigrp;
-
- eigrp = eigrp_lookup();
- if (eigrp != NULL) {
- /* Writes 'router eigrp' section to config */
- config_write_eigrp_router(vty, eigrp);
-
- /* Interface config print */
- config_write_interfaces(vty, eigrp);
- //
- // /* static neighbor print. */
- // config_write_eigrp_nbr_nbma (vty, eigrp);
- //
- // /* Virtual-Link print. */
- // config_write_virtual_link (vty, eigrp);
- //
- // /* Default metric configuration. */
- // config_write_eigrp_default_metric (vty, eigrp);
- //
- // /* Distribute-list and default-information print. */
- // config_write_eigrp_distribute (vty, eigrp);
- //
- // /* Distance configuration. */
- // config_write_eigrp_distance (vty, eigrp)
- }
-
- return 0;
-}
-
void eigrp_vty_show_init(void)
{
install_element(VIEW_NODE, &show_ip_eigrp_interfaces_cmd);
@@ -1536,69 +527,9 @@ void eigrp_vty_show_init(void)
install_element(VIEW_NODE, &show_ip_eigrp_topology_all_cmd);
}
-/* eigrpd's interface node. */
-static struct cmd_node eigrp_interface_node = {INTERFACE_NODE,
- "%s(config-if)# ", 1};
-
-void eigrp_vty_if_init(void)
-{
- install_node(&eigrp_interface_node, eigrp_write_interface);
- if_cmd_init();
-
- /* Delay and bandwidth configuration commands*/
- install_element(INTERFACE_NODE, &eigrp_if_delay_cmd);
- install_element(INTERFACE_NODE, &no_eigrp_if_delay_cmd);
- install_element(INTERFACE_NODE, &eigrp_if_bandwidth_cmd);
- install_element(INTERFACE_NODE, &no_eigrp_if_bandwidth_cmd);
-
- /*Hello-interval and hold-time interval configuration commands*/
- install_element(INTERFACE_NODE, &eigrp_if_ip_holdinterval_cmd);
- install_element(INTERFACE_NODE, &no_eigrp_if_ip_holdinterval_cmd);
- install_element(INTERFACE_NODE, &eigrp_if_ip_hellointerval_cmd);
- install_element(INTERFACE_NODE, &no_eigrp_if_ip_hellointerval_cmd);
-
- /* "Authentication configuration commands */
- install_element(INTERFACE_NODE, &eigrp_authentication_mode_cmd);
- install_element(INTERFACE_NODE, &no_eigrp_authentication_mode_cmd);
- install_element(INTERFACE_NODE, &eigrp_authentication_keychain_cmd);
-
- /*EIGRP Summarization commands*/
- install_element(INTERFACE_NODE, &eigrp_ip_summary_address_cmd);
- install_element(INTERFACE_NODE, &no_eigrp_ip_summary_address_cmd);
-}
-
-static void eigrp_vty_zebra_init(void)
-{
- install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd);
- install_element(EIGRP_NODE, &no_eigrp_redistribute_source_metric_cmd);
-}
-
/* Install EIGRP related vty commands. */
void eigrp_vty_init(void)
{
- install_node(&eigrp_node, eigrp_config_write);
-
- install_default(EIGRP_NODE);
-
- install_element(CONFIG_NODE, &router_eigrp_cmd);
- install_element(CONFIG_NODE, &no_router_eigrp_cmd);
- install_element(EIGRP_NODE, &eigrp_network_cmd);
- install_element(EIGRP_NODE, &no_eigrp_network_cmd);
- install_element(EIGRP_NODE, &eigrp_variance_cmd);
- install_element(EIGRP_NODE, &no_eigrp_variance_cmd);
- install_element(EIGRP_NODE, &eigrp_router_id_cmd);
- install_element(EIGRP_NODE, &no_eigrp_router_id_cmd);
- install_element(EIGRP_NODE, &eigrp_passive_interface_cmd);
- install_element(EIGRP_NODE, &no_eigrp_passive_interface_cmd);
- install_element(EIGRP_NODE, &eigrp_timers_active_cmd);
- install_element(EIGRP_NODE, &no_eigrp_timers_active_cmd);
- install_element(EIGRP_NODE, &eigrp_metric_weights_cmd);
- install_element(EIGRP_NODE, &no_eigrp_metric_weights_cmd);
- install_element(EIGRP_NODE, &eigrp_maximum_paths_cmd);
- install_element(EIGRP_NODE, &no_eigrp_maximum_paths_cmd);
- install_element(EIGRP_NODE, &eigrp_neighbor_cmd);
- install_element(EIGRP_NODE, &no_eigrp_neighbor_cmd);
-
/* commands for manual hard restart */
install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_cmd);
install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_int_cmd);
@@ -1607,6 +538,4 @@ void eigrp_vty_init(void)
install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_soft_cmd);
install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_int_soft_cmd);
install_element(ENABLE_NODE, &clear_ip_eigrp_neighbors_IP_soft_cmd);
-
- eigrp_vty_zebra_init();
}
diff --git a/eigrpd/eigrp_vty.h b/eigrpd/eigrp_vty.h
index 3fbadf6dfb..ebbf503857 100644
--- a/eigrpd/eigrp_vty.h
+++ b/eigrpd/eigrp_vty.h
@@ -35,6 +35,5 @@
/* Prototypes. */
extern void eigrp_vty_init(void);
extern void eigrp_vty_show_init(void);
-extern void eigrp_vty_if_init(void);
#endif /* _Quagga_EIGRP_VTY_H_ */
diff --git a/eigrpd/eigrpd.h b/eigrpd/eigrpd.h
index de7c881ac0..3ef3a9c0cc 100644
--- a/eigrpd/eigrpd.h
+++ b/eigrpd/eigrpd.h
@@ -52,4 +52,51 @@ extern struct eigrp *eigrp_get(const char *);
extern struct eigrp *eigrp_lookup(void);
extern void eigrp_router_id_update(struct eigrp *);
+/* eigrp_cli.c */
+extern void eigrp_cli_show_header(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_end_header(struct vty *vty, struct lyd_node *dnode);
+extern void eigrp_cli_show_router_id(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_passive_interface(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_active_time(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_variance(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_maximum_paths(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_metrics(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_network(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_neighbor(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_redistribute(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_delay(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_bandwidth(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_hello_interval(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_hold_time(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_summarize_address(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_authentication(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_keychain(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_init(void);
+
+/* eigrp_northbound.c */
+extern const struct frr_yang_module_info frr_eigrpd_info;
+
#endif /* _ZEBRA_EIGRPD_H */
diff --git a/eigrpd/subdir.am b/eigrpd/subdir.am
index 4503030fdf..cc46766586 100644
--- a/eigrpd/subdir.am
+++ b/eigrpd/subdir.am
@@ -7,6 +7,7 @@ noinst_LIBRARIES += eigrpd/libeigrp.a
sbin_PROGRAMS += eigrpd/eigrpd
dist_examples_DATA += eigrpd/eigrpd.conf.sample
vtysh_scan += \
+ $(top_srcdir)/eigrpd/eigrp_cli.c \
$(top_srcdir)/eigrpd/eigrp_dump.c \
$(top_srcdir)/eigrpd/eigrp_vty.c \
# end
@@ -15,6 +16,7 @@ man8 += $(MANBUILD)/eigrpd.8
endif
eigrpd_libeigrp_a_SOURCES = \
+ eigrpd/eigrp_cli.c \
eigrpd/eigrp_dump.c \
eigrpd/eigrp_errors.c \
eigrpd/eigrp_filter.c \
@@ -24,6 +26,7 @@ eigrpd_libeigrp_a_SOURCES = \
eigrpd/eigrp_memory.c \
eigrpd/eigrp_neighbor.c \
eigrpd/eigrp_network.c \
+ eigrpd/eigrp_northbound.c \
eigrpd/eigrp_packet.c \
eigrpd/eigrp_query.c \
eigrpd/eigrp_reply.c \
@@ -47,6 +50,9 @@ eigrpdheader_HEADERS = \
eigrpd/eigrp_vty_clippy.c: $(CLIPPY_DEPS)
eigrpd/eigrp_vty.$(OBJEXT): eigrpd/eigrp_vty_clippy.c
+eigrpd/eigrp_cli_clippy.c: $(CLIPPY_DEPS)
+eigrpd/eigrp_cli.$(OBJEXT): eigrpd/eigrp_cli_clippy.c
+
noinst_HEADERS += \
eigrpd/eigrp_const.h \
eigrpd/eigrp_errors.h \
@@ -64,5 +70,9 @@ noinst_HEADERS += \
eigrpd/eigrp_zebra.h \
# end
+nodist_eigrpd_eigrpd_SOURCES = \
+ yang/frr-eigrpd.yang.c \
+ # end
+
eigrpd_eigrpd_SOURCES = eigrpd/eigrp_main.c
eigrpd_eigrpd_LDADD = eigrpd/libeigrp.a lib/libfrr.la $(LIBCAP)
diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c
index e588571c01..bdb6c2a397 100644
--- a/lib/frr_pthread.c
+++ b/lib/frr_pthread.c
@@ -133,18 +133,29 @@ int frr_pthread_set_name(struct frr_pthread *fpt)
return ret;
}
+static void *frr_pthread_inner(void *arg)
+{
+ struct frr_pthread *fpt = arg;
+
+ rcu_thread_start(fpt->rcu_thread);
+ return fpt->attr.start(fpt);
+}
+
int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
{
int ret;
- ret = pthread_create(&fpt->thread, attr, fpt->attr.start, fpt);
+ fpt->rcu_thread = rcu_thread_prepare();
+ ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt);
/*
* Per pthread_create(3), the contents of fpt->thread are undefined if
* pthread_create() did not succeed. Reset this value to zero.
*/
- if (ret < 0)
+ if (ret < 0) {
+ rcu_thread_unprepare(fpt->rcu_thread);
memset(&fpt->thread, 0x00, sizeof(fpt->thread));
+ }
return ret;
}
diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h
index 3afe7ba966..6096a50370 100644
--- a/lib/frr_pthread.h
+++ b/lib/frr_pthread.h
@@ -23,6 +23,7 @@
#include <pthread.h>
#include "frratomic.h"
#include "memory.h"
+#include "frrcu.h"
#include "thread.h"
#ifdef __cplusplus
@@ -50,6 +51,8 @@ struct frr_pthread {
/* pthread id */
pthread_t thread;
+ struct rcu_thread *rcu_thread;
+
/* thread master for this pthread's thread.c event loop */
struct thread_master *master;
diff --git a/lib/frrcu.c b/lib/frrcu.c
new file mode 100644
index 0000000000..7e6475b648
--- /dev/null
+++ b/lib/frrcu.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2017-19 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* implementation notes: this is an epoch-based RCU implementation. rcu_seq
+ * (global variable) counts the current epoch. Threads hold a specific epoch
+ * in rcu_read_lock(). This is the oldest epoch a thread might be accessing
+ * data from.
+ *
+ * The rcu_seq global is only pushed forward on rcu_read_lock() and
+ * rcu_read_unlock() calls. This makes things a tad more efficient since
+ * those are the only places it matters:
+ * - on rcu_read_lock, we don't want to hold an old epoch pointlessly
+ * - on rcu_read_unlock, we want to make sure we're not stuck on an old epoch
+ * when heading into a long idle period where no thread holds RCU
+ *
+ * rcu_thread structures themselves are RCU-free'd.
+ *
+ * rcu_head structures are the most iffy; normally for an ATOMLIST we would
+ * need to make sure we use rcu_free or pthread_rwlock to deallocate old items
+ * to prevent ABA or use-after-free problems. However, our ATOMLIST code
+ * guarantees that if the list remains non-empty in all cases, we only need
+ * the "last" pointer to do an "add_tail()", i.e. we can't run into ABA/UAF
+ * issues - but we do need to keep at least 1 item on the list.
+ *
+ * (Search the atomlist code for all uses of "last")
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <pthread.h>
+#ifdef HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include "frrcu.h"
+#include "seqlock.h"
+#include "atomlist.h"
+
+DEFINE_MTYPE_STATIC(LIB, RCU_THREAD, "RCU thread")
+DEFINE_MTYPE_STATIC(LIB, RCU_NEXT, "RCU sequence barrier")
+
+DECLARE_ATOMLIST(rcu_heads, struct rcu_head, head)
+
+PREDECL_ATOMLIST(rcu_threads)
+struct rcu_thread {
+ struct rcu_threads_item head;
+
+ struct rcu_head rcu_head;
+
+ struct seqlock rcu;
+
+ /* only accessed by thread itself, not atomic */
+ unsigned depth;
+};
+DECLARE_ATOMLIST(rcu_threads, struct rcu_thread, head)
+
+static const struct rcu_action rcua_next = { .type = RCUA_NEXT };
+static const struct rcu_action rcua_end = { .type = RCUA_END };
+static const struct rcu_action rcua_close = { .type = RCUA_CLOSE };
+
+struct rcu_next {
+ struct rcu_head head_free;
+ struct rcu_head head_next;
+};
+
+#define rcu_free_internal(mtype, ptr, field) \
+ do { \
+ typeof(ptr) _ptr = (ptr); \
+ struct rcu_head *_rcu_head = &_ptr->field; \
+ static const struct rcu_action _rcu_action = { \
+ .type = RCUA_FREE, \
+ .u.free = { \
+ .mt = mtype, \
+ .offset = offsetof(typeof(*_ptr), field), \
+ }, \
+ }; \
+ _rcu_head->action = &_rcu_action; \
+ rcu_heads_add_tail(&rcu_heads, _rcu_head); \
+ } while (0)
+
+/* primary global RCU position */
+static struct seqlock rcu_seq;
+/* this is set to rcu_seq whenever something is added on the RCU queue.
+ * rcu_read_lock() and rcu_read_unlock() will then bump rcu_seq up one step.
+ */
+static _Atomic seqlock_val_t rcu_dirty;
+
+static struct rcu_threads_head rcu_threads;
+static struct rcu_heads_head rcu_heads;
+
+/* main thread & RCU sweeper have pre-setup rcu_thread structures. The
+ * reasons are different:
+ *
+ * - rcu_thread_main is there because the main thread isn't started like
+ * other threads, it's implicitly created when the program is started. So
+ * rcu_thread_main matches up implicitly.
+ *
+ * - rcu_thread_rcu isn't actually put on the rcu_threads list (makes no
+ * sense really), it only exists so we can call RCU-using functions from
+ * the RCU thread without special handling in rcu_read_lock/unlock.
+ */
+static struct rcu_thread rcu_thread_main;
+static struct rcu_thread rcu_thread_rcu;
+
+static pthread_t rcu_pthread;
+static pthread_key_t rcu_thread_key;
+static bool rcu_active;
+
+static void rcu_start(void);
+static void rcu_bump(void);
+
+/*
+ * preinitialization for main thread
+ */
+static void rcu_thread_end(void *rcu_thread);
+
+static void rcu_preinit(void) __attribute__((constructor));
+static void rcu_preinit(void)
+{
+ struct rcu_thread *rt;
+
+ rt = &rcu_thread_main;
+ rt->depth = 1;
+ seqlock_init(&rt->rcu);
+ seqlock_acquire_val(&rt->rcu, SEQLOCK_STARTVAL);
+
+ pthread_key_create(&rcu_thread_key, rcu_thread_end);
+ pthread_setspecific(rcu_thread_key, rt);
+
+ rcu_threads_add_tail(&rcu_threads, rt);
+
+ /* RCU sweeper's rcu_thread is a dummy, NOT added to rcu_threads */
+ rt = &rcu_thread_rcu;
+ rt->depth = 1;
+
+ seqlock_init(&rcu_seq);
+ seqlock_acquire_val(&rcu_seq, SEQLOCK_STARTVAL);
+}
+
+static struct rcu_thread *rcu_self(void)
+{
+ return (struct rcu_thread *)pthread_getspecific(rcu_thread_key);
+}
+
+/*
+ * thread management (for the non-main thread)
+ */
+struct rcu_thread *rcu_thread_prepare(void)
+{
+ struct rcu_thread *rt, *cur;
+
+ rcu_assert_read_locked();
+
+ if (!rcu_active)
+ rcu_start();
+
+ cur = rcu_self();
+ assert(cur->depth);
+
+ /* new thread always starts with rcu_read_lock held at depth 1, and
+ * holding the same epoch as the parent (this makes it possible to
+ * use RCU for things passed into the thread through its arg)
+ */
+ rt = XCALLOC(MTYPE_RCU_THREAD, sizeof(*rt));
+ rt->depth = 1;
+
+ seqlock_init(&rt->rcu);
+ seqlock_acquire(&rt->rcu, &cur->rcu);
+
+ rcu_threads_add_tail(&rcu_threads, rt);
+
+ return rt;
+}
+
+void rcu_thread_start(struct rcu_thread *rt)
+{
+ pthread_setspecific(rcu_thread_key, rt);
+}
+
+void rcu_thread_unprepare(struct rcu_thread *rt)
+{
+ if (rt == &rcu_thread_rcu)
+ return;
+
+ rt->depth = 1;
+ seqlock_acquire(&rt->rcu, &rcu_seq);
+
+ rcu_bump();
+ if (rt != &rcu_thread_main)
+ /* this free() happens after seqlock_release() below */
+ rcu_free_internal(MTYPE_RCU_THREAD, rt, rcu_head);
+
+ rcu_threads_del(&rcu_threads, rt);
+ seqlock_release(&rt->rcu);
+}
+
+static void rcu_thread_end(void *rtvoid)
+{
+ struct rcu_thread *rt = rtvoid;
+ rcu_thread_unprepare(rt);
+}
+
+/*
+ * main RCU control aspects
+ */
+
+static void rcu_bump(void)
+{
+ struct rcu_next *rn;
+
+ rn = XMALLOC(MTYPE_RCU_NEXT, sizeof(*rn));
+
+ /* note: each RCUA_NEXT item corresponds to exactly one seqno bump.
+ * This means we don't need to communicate which seqno is which
+ * RCUA_NEXT, since we really don't care.
+ */
+
+ /*
+ * Important race condition: while rcu_heads_add_tail is executing,
+ * there is an intermediate point where the rcu_heads "last" pointer
+ * already points to rn->head_next, but rn->head_next isn't added to
+ * the list yet. That means any other "add_tail" calls append to this
+ * item, which isn't fully on the list yet. Freeze this thread at
+ * that point and look at another thread doing a rcu_bump. It adds
+ * these two items and then does a seqlock_bump. But the rcu_heads
+ * list is still "interrupted" and there's no RCUA_NEXT on the list
+ * yet (from either the frozen thread or the second thread). So
+ * rcu_main() might actually hit the end of the list at the
+ * "interrupt".
+ *
+ * This situation is prevented by requiring that rcu_read_lock is held
+ * for any calls to rcu_bump, since if we're holding the current RCU
+ * epoch, that means rcu_main can't be chewing on rcu_heads and hit
+ * that interruption point. Only by the time the thread has continued
+ * to rcu_read_unlock() - and therefore completed the add_tail - the
+ * RCU sweeper gobbles up the epoch and can be sure to find at least
+ * the RCUA_NEXT and RCUA_FREE items on rcu_heads.
+ */
+ rn->head_next.action = &rcua_next;
+ rcu_heads_add_tail(&rcu_heads, &rn->head_next);
+
+ /* free rn that we allocated above.
+ *
+ * This is INTENTIONALLY not built into the RCUA_NEXT action. This
+ * ensures that after the action above is popped off the queue, there
+ * is still at least 1 item on the RCU queue. This means we never
+ * delete the last item, which is extremely important since it keeps
+ * the atomlist ->last pointer alive and well.
+ *
+ * If we were to "run dry" on the RCU queue, add_tail may run into the
+ * "last item is being deleted - start over" case, and then we may end
+ * up accessing old RCU queue items that are already free'd.
+ */
+ rcu_free_internal(MTYPE_RCU_NEXT, rn, head_free);
+
+ /* Only allow the RCU sweeper to run after these 2 items are queued.
+ *
+ * If another thread enqueues some RCU action in the intermediate
+ * window here, nothing bad happens - the queued action is associated
+ * with a larger seq# than strictly necessary. Thus, it might get
+ * executed a bit later, but that's not a problem.
+ *
+ * If another thread acquires the read lock in this window, it holds
+ * the previous epoch, but its RCU queue actions will be in the next
+ * epoch. This isn't a problem either, just a tad inefficient.
+ */
+ seqlock_bump(&rcu_seq);
+}
+
+static void rcu_bump_maybe(void)
+{
+ seqlock_val_t dirty;
+
+ dirty = atomic_load_explicit(&rcu_dirty, memory_order_relaxed);
+ /* no problem if we race here and multiple threads bump rcu_seq;
+ * bumping too much causes no issues while not bumping enough will
+ * result in delayed cleanup
+ */
+ if (dirty == seqlock_cur(&rcu_seq))
+ rcu_bump();
+}
+
+void rcu_read_lock(void)
+{
+ struct rcu_thread *rt = rcu_self();
+
+ assert(rt);
+ if (rt->depth++ > 0)
+ return;
+
+ seqlock_acquire(&rt->rcu, &rcu_seq);
+ /* need to hold RCU for bump ... */
+ rcu_bump_maybe();
+ /* ... but no point in holding the old epoch if we just bumped */
+ seqlock_acquire(&rt->rcu, &rcu_seq);
+}
+
+void rcu_read_unlock(void)
+{
+ struct rcu_thread *rt = rcu_self();
+
+ assert(rt && rt->depth);
+ if (--rt->depth > 0)
+ return;
+ rcu_bump_maybe();
+ seqlock_release(&rt->rcu);
+}
+
+void rcu_assert_read_locked(void)
+{
+ struct rcu_thread *rt = rcu_self();
+ assert(rt && rt->depth && seqlock_held(&rt->rcu));
+}
+
+void rcu_assert_read_unlocked(void)
+{
+ struct rcu_thread *rt = rcu_self();
+ assert(rt && !rt->depth && !seqlock_held(&rt->rcu));
+}
+
+/*
+ * RCU resource-release thread
+ */
+
+static void *rcu_main(void *arg);
+
+static void rcu_start(void)
+{
+ /* ensure we never handle signals on the RCU thread by blocking
+ * everything here (new thread inherits signal mask)
+ */
+ sigset_t oldsigs, blocksigs;
+
+ sigfillset(&blocksigs);
+ pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs);
+
+ rcu_active = true;
+
+ assert(!pthread_create(&rcu_pthread, NULL, rcu_main, NULL));
+
+ pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
+
+#ifdef HAVE_PTHREAD_SETNAME_NP
+# ifdef GNU_LINUX
+ pthread_setname_np(rcu_pthread, "RCU sweeper");
+# elif defined(__NetBSD__)
+ pthread_setname_np(rcu_pthread, "RCU sweeper", NULL);
+# endif
+#elif defined(HAVE_PTHREAD_SET_NAME_NP)
+ pthread_set_name_np(rcu_pthread, "RCU sweeper");
+#endif
+}
+
+static void rcu_do(struct rcu_head *rh)
+{
+ struct rcu_head_close *rhc;
+ void *p;
+
+ switch (rh->action->type) {
+ case RCUA_FREE:
+ p = (char *)rh - rh->action->u.free.offset;
+ if (rh->action->u.free.mt)
+ qfree(rh->action->u.free.mt, p);
+ else
+ free(p);
+ break;
+ case RCUA_CLOSE:
+ rhc = container_of(rh, struct rcu_head_close,
+ rcu_head);
+ close(rhc->fd);
+ break;
+ case RCUA_CALL:
+ p = (char *)rh - rh->action->u.call.offset;
+ rh->action->u.call.fptr(p);
+ break;
+
+ case RCUA_INVALID:
+ case RCUA_NEXT:
+ case RCUA_END:
+ default:
+ assert(0);
+ }
+}
+
+static void rcu_watchdog(struct rcu_thread *rt)
+{
+#if 0
+ /* future work: print a backtrace for the thread that's holding up
+ * RCU. The only (good) way of doing that is to send a signal to the
+ * other thread, save away the backtrace in the signal handler, and
+ * block here until the signal is done processing.
+ *
+ * Just haven't implemented that yet.
+ */
+ fprintf(stderr, "RCU watchdog %p\n", rt);
+#endif
+}
+
+static void *rcu_main(void *arg)
+{
+ struct rcu_thread *rt;
+ struct rcu_head *rh = NULL;
+ bool end = false;
+ struct timespec maxwait;
+
+ seqlock_val_t rcuval = SEQLOCK_STARTVAL;
+
+ pthread_setspecific(rcu_thread_key, &rcu_thread_rcu);
+
+ while (!end) {
+ seqlock_wait(&rcu_seq, rcuval);
+
+ /* RCU watchdog timeout, TODO: configurable value */
+ clock_gettime(CLOCK_MONOTONIC, &maxwait);
+ maxwait.tv_nsec += 100 * 1000 * 1000;
+ if (maxwait.tv_nsec >= 1000000000) {
+ maxwait.tv_sec++;
+ maxwait.tv_nsec -= 1000000000;
+ }
+
+ frr_each (rcu_threads, &rcu_threads, rt)
+ if (!seqlock_timedwait(&rt->rcu, rcuval, &maxwait)) {
+ rcu_watchdog(rt);
+ seqlock_wait(&rt->rcu, rcuval);
+ }
+
+ while ((rh = rcu_heads_pop(&rcu_heads))) {
+ if (rh->action->type == RCUA_NEXT)
+ break;
+ else if (rh->action->type == RCUA_END)
+ end = true;
+ else
+ rcu_do(rh);
+ }
+
+ rcuval += SEQLOCK_INCR;
+ }
+
+ /* rcu_shutdown can only be called singlethreaded, and it does a
+ * pthread_join, so it should be impossible that anything ended up
+ * on the queue after RCUA_END
+ */
+#if 1
+ assert(!rcu_heads_first(&rcu_heads));
+#else
+ while ((rh = rcu_heads_pop(&rcu_heads)))
+ if (rh->action->type >= RCUA_FREE)
+ rcu_do(rh);
+#endif
+ return NULL;
+}
+
+void rcu_shutdown(void)
+{
+ static struct rcu_head rcu_head_end;
+ struct rcu_thread *rt = rcu_self();
+ void *retval;
+
+ if (!rcu_active)
+ return;
+
+ rcu_assert_read_locked();
+ assert(rcu_threads_count(&rcu_threads) == 1);
+
+ rcu_enqueue(&rcu_head_end, &rcua_end);
+
+ rt->depth = 0;
+ seqlock_release(&rt->rcu);
+ seqlock_release(&rcu_seq);
+ rcu_active = false;
+
+ /* clearing rcu_active is before pthread_join in case we hang in
+ * pthread_join & get a SIGTERM or something - in that case, just
+ * ignore the maybe-still-running RCU thread
+ */
+ if (pthread_join(rcu_pthread, &retval) == 0) {
+ seqlock_acquire_val(&rcu_seq, SEQLOCK_STARTVAL);
+ seqlock_acquire_val(&rt->rcu, SEQLOCK_STARTVAL);
+ rt->depth = 1;
+ }
+}
+
+/*
+ * RCU'd free functions
+ */
+
+void rcu_enqueue(struct rcu_head *rh, const struct rcu_action *action)
+{
+ /* refer to rcu_bump() for why we need to hold RCU when adding items
+ * to rcu_heads
+ */
+ rcu_assert_read_locked();
+
+ rh->action = action;
+
+ if (!rcu_active) {
+ rcu_do(rh);
+ return;
+ }
+ rcu_heads_add_tail(&rcu_heads, rh);
+ atomic_store_explicit(&rcu_dirty, seqlock_cur(&rcu_seq),
+ memory_order_relaxed);
+}
+
+void rcu_close(struct rcu_head_close *rhc, int fd)
+{
+ rhc->fd = fd;
+ rcu_enqueue(&rhc->rcu_head, &rcua_close);
+}
diff --git a/lib/frrcu.h b/lib/frrcu.h
new file mode 100644
index 0000000000..8f789303cc
--- /dev/null
+++ b/lib/frrcu.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2017-19 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRRCU_H
+#define _FRRCU_H
+
+#include "memory.h"
+#include "atomlist.h"
+#include "seqlock.h"
+
+/* quick RCU primer:
+ * There's a global sequence counter. Whenever a thread does a
+ * rcu_read_lock(), it is marked as holding the current sequence counter.
+ * When something is cleaned with RCU, the global sequence counter is
+ * increased and the item is queued for cleanup - *after* all threads are
+ * at a more recent sequence counter (or no sequence counter / unheld).
+ *
+ * So, by delaying resource cleanup, RCU ensures that things don't go away
+ * while another thread may hold a (stale) reference.
+ *
+ * Note that even if a thread is in rcu_read_lock(), it is invalid for that
+ * thread to access bits after rcu_free() & co on them. This is a design
+ * choice to allow no-op'ing out the entire RCU mechanism if we're running
+ * singlethreaded. (Also allows some optimization on the counter bumping.)
+ *
+ * differences from Linux Kernel RCU:
+ * - there's no rcu_synchronize(), if you really need to defer something
+ * use rcu_call() (and double check it's really necessary)
+ * - rcu_dereference() and rcu_assign_pointer() don't exist, use atomic_*
+ * instead (ATOM* list structures do the right thing)
+ */
+
+/* opaque */
+struct rcu_thread;
+
+/* called before new thread creation, sets up rcu thread info for new thread
+ * before it actually exits. This ensures possible RCU references are held
+ * for thread startup.
+ *
+ * return value must be passed into the new thread's call to rcu_thread_start()
+ */
+extern struct rcu_thread *rcu_thread_prepare(void);
+
+/* cleanup in case pthread_create() fails */
+extern void rcu_thread_unprepare(struct rcu_thread *rcu_thread);
+
+/* called early in the new thread, with the return value from the above.
+ * NB: new thread is initially in RCU-held state! (at depth 1)
+ *
+ * TBD: maybe inherit RCU state from rcu_thread_prepare()?
+ */
+extern void rcu_thread_start(struct rcu_thread *rcu_thread);
+
+/* thread exit is handled through pthread_key_create's destructor function */
+
+/* global RCU shutdown - must be called with only 1 active thread left. waits
+ * until remaining RCU actions are done & RCU thread has exited.
+ *
+ * This is mostly here to get a clean exit without memleaks.
+ */
+extern void rcu_shutdown(void);
+
+/* enter / exit RCU-held state. counter-based, so can be called nested. */
+extern void rcu_read_lock(void);
+extern void rcu_read_unlock(void);
+
+/* for debugging / safety checks */
+extern void rcu_assert_read_locked(void);
+extern void rcu_assert_read_unlocked(void);
+
+enum rcu_action_type {
+ RCUA_INVALID = 0,
+ /* used internally by the RCU code, shouldn't ever show up outside */
+ RCUA_NEXT,
+ RCUA_END,
+ /* normal RCU actions, for outside use */
+ RCUA_FREE,
+ RCUA_CLOSE,
+ RCUA_CALL,
+};
+
+/* since rcu_head is intended to be embedded into structs which may exist
+ * with lots of copies, rcu_head is shrunk down to its absolute minimum -
+ * the atomlist pointer + a pointer to this action struct.
+ */
+struct rcu_action {
+ enum rcu_action_type type;
+
+ union {
+ struct {
+ struct memtype *mt;
+ ptrdiff_t offset;
+ } free;
+
+ struct {
+ void (*fptr)(void *arg);
+ ptrdiff_t offset;
+ } call;
+ } u;
+};
+
+/* RCU cleanup function queue item */
+PREDECL_ATOMLIST(rcu_heads)
+struct rcu_head {
+ struct rcu_heads_item head;
+ const struct rcu_action *action;
+};
+
+/* special RCU head for delayed fd-close */
+struct rcu_head_close {
+ struct rcu_head rcu_head;
+ int fd;
+};
+
+/* enqueue RCU action - use the macros below to get the rcu_action set up */
+extern void rcu_enqueue(struct rcu_head *head, const struct rcu_action *action);
+
+/* RCU free() and file close() operations.
+ *
+ * freed memory / closed fds become _immediately_ unavailable to the calling
+ * thread, but will remain available for other threads until they have passed
+ * into RCU-released state.
+ */
+
+/* may be called with NULL mt to do non-MTYPE free() */
+#define rcu_free(mtype, ptr, field) \
+ do { \
+ typeof(ptr) _ptr = (ptr); \
+ struct rcu_head *_rcu_head = &_ptr->field; \
+ static const struct rcu_action _rcu_action = { \
+ .type = RCUA_FREE, \
+ .u.free = { \
+ .mt = mtype, \
+ .offset = offsetof(typeof(*_ptr), field), \
+ }, \
+ }; \
+ rcu_enqueue(_rcu_head, &_rcu_action); \
+ } while (0)
+
+/* use this sparingly, it runs on (and blocks) the RCU thread */
+#define rcu_call(func, ptr, field) \
+ do { \
+ typeof(ptr) _ptr = (ptr); \
+ void (*fptype)(typeof(ptr)); \
+ struct rcu_head *_rcu_head = &_ptr->field; \
+ static const struct rcu_action _rcu_action = { \
+ .type = RCUA_CALL, \
+ .u.call = { \
+ .fptr = (void *)func, \
+ .offset = offsetof(typeof(*_ptr), field), \
+ }, \
+ }; \
+ (void)(_fptype = func); \
+ rcu_enqueue(_rcu_head, &_rcu_action); \
+ } while (0)
+
+extern void rcu_close(struct rcu_head_close *head, int fd);
+
+#endif /* _FRRCU_H */
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 0fc321d6e0..35c6092140 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -41,6 +41,7 @@
#include "northbound_cli.h"
#include "northbound_db.h"
#include "debug.h"
+#include "frrcu.h"
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
DEFINE_KOOH(frr_early_fini, (), ())
@@ -1081,6 +1082,7 @@ void frr_fini(void)
master = NULL;
closezlog();
/* frrmod_init -> nothing needed / hooks */
+ rcu_shutdown();
if (!debug_memstats_at_exit)
return;
diff --git a/lib/log.c b/lib/log.c
index c577239908..51a0ddd6b7 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -559,9 +559,7 @@ static void crash_write(struct fbuf *fb, char *msgstart)
void zlog_signal(int signo, const char *action, void *siginfo_v,
void *program_counter)
{
-#ifdef SA_SIGINFO
siginfo_t *siginfo = siginfo_v;
-#endif
time_t now;
char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")
+ 100];
@@ -575,7 +573,6 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
msgstart = fb.pos;
bprintfrr(&fb, "Received signal %d at %lld", signo, (long long)now);
-#ifdef SA_SIGINFO
if (program_counter)
bprintfrr(&fb, " (si_addr 0x%tx, PC 0x%tx)",
(ptrdiff_t)siginfo->si_addr,
@@ -583,7 +580,6 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
else
bprintfrr(&fb, " (si_addr 0x%tx)",
(ptrdiff_t)siginfo->si_addr);
-#endif /* SA_SIGINFO */
bprintfrr(&fb, "; %s\n", action);
crash_write(&fb, msgstart);
diff --git a/lib/pqueue.c b/lib/pqueue.c
deleted file mode 100644
index 87b54a681a..0000000000
--- a/lib/pqueue.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/* Priority queue functions.
- * Copyright (C) 2003 Yasuhiro Ohara
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <zebra.h>
-
-#include "memory.h"
-#include "pqueue.h"
-
-DEFINE_MTYPE_STATIC(LIB, PQUEUE, "Priority queue")
-DEFINE_MTYPE_STATIC(LIB, PQUEUE_DATA, "Priority queue data")
-
-/* priority queue using heap sort */
-
-/* pqueue->cmp() controls the order of sorting (i.e, ascending or
- descending). If you want the left node to move upper of the heap
- binary tree, make cmp() to return less than 0. for example, if cmp
- (10, 20) returns -1, the sorting is ascending order. if cmp (10,
- 20) returns 1, the sorting is descending order. if cmp (10, 20)
- returns 0, this library does not do sorting (which will not be what
- you want). To be brief, if the contents of cmp_func (left, right)
- is left - right, dequeue () returns the smallest node. Otherwise
- (if the contents is right - left), dequeue () returns the largest
- node. */
-
-#define DATA_SIZE (sizeof (void *))
-#define PARENT_OF(x) ((x - 1) / 2)
-#define LEFT_OF(x) (2 * x + 1)
-#define RIGHT_OF(x) (2 * x + 2)
-#define HAVE_CHILD(x,q) (x < (q)->size / 2)
-
-void trickle_up(int index, struct pqueue *queue)
-{
- void *tmp;
-
- /* Save current node as tmp node. */
- tmp = queue->array[index];
-
- /* Continue until the node reaches top or the place where the parent
- node should be upper than the tmp node. */
- while (index > 0
- && (*queue->cmp)(tmp, queue->array[PARENT_OF(index)]) < 0) {
- /* actually trickle up */
- queue->array[index] = queue->array[PARENT_OF(index)];
- if (queue->update != NULL)
- (*queue->update)(queue->array[index], index);
- index = PARENT_OF(index);
- }
-
- /* Restore the tmp node to appropriate place. */
- queue->array[index] = tmp;
- if (queue->update != NULL)
- (*queue->update)(tmp, index);
-}
-
-void trickle_down(int index, struct pqueue *queue)
-{
- void *tmp;
- int which;
-
- /* Save current node as tmp node. */
- tmp = queue->array[index];
-
- /* Continue until the node have at least one (left) child. */
- while (HAVE_CHILD(index, queue)) {
- /* If right child exists, and if the right child is more proper
- to be moved upper. */
- if (RIGHT_OF(index) < queue->size
- && (*queue->cmp)(queue->array[LEFT_OF(index)],
- queue->array[RIGHT_OF(index)])
- > 0)
- which = RIGHT_OF(index);
- else
- which = LEFT_OF(index);
-
- /* If the tmp node should be upper than the child, break. */
- if ((*queue->cmp)(queue->array[which], tmp) > 0)
- break;
-
- /* Actually trickle down the tmp node. */
- queue->array[index] = queue->array[which];
- if (queue->update != NULL)
- (*queue->update)(queue->array[index], index);
- index = which;
- }
-
- /* Restore the tmp node to appropriate place. */
- queue->array[index] = tmp;
- if (queue->update != NULL)
- (*queue->update)(tmp, index);
-}
-
-struct pqueue *pqueue_create(void)
-{
- struct pqueue *queue;
-
- queue = XCALLOC(MTYPE_PQUEUE, sizeof(struct pqueue));
-
- queue->array =
- XCALLOC(MTYPE_PQUEUE_DATA, DATA_SIZE * PQUEUE_INIT_ARRAYSIZE);
- queue->array_size = PQUEUE_INIT_ARRAYSIZE;
-
- /* By default we want nothing to happen when a node changes. */
- queue->update = NULL;
- return queue;
-}
-
-void pqueue_delete(struct pqueue *queue)
-{
- XFREE(MTYPE_PQUEUE_DATA, queue->array);
- XFREE(MTYPE_PQUEUE, queue);
-}
-
-static int pqueue_expand(struct pqueue *queue)
-{
- void **newarray;
-
- newarray =
- XCALLOC(MTYPE_PQUEUE_DATA, queue->array_size * DATA_SIZE * 2);
-
- memcpy(newarray, queue->array, queue->array_size * DATA_SIZE);
-
- XFREE(MTYPE_PQUEUE_DATA, queue->array);
- queue->array = newarray;
- queue->array_size *= 2;
-
- return 1;
-}
-
-void pqueue_enqueue(void *data, struct pqueue *queue)
-{
- if (queue->size + 2 >= queue->array_size && !pqueue_expand(queue))
- return;
-
- queue->array[queue->size] = data;
- if (queue->update != NULL)
- (*queue->update)(data, queue->size);
- trickle_up(queue->size, queue);
- queue->size++;
-}
-
-void *pqueue_dequeue(struct pqueue *queue)
-{
- void *data = queue->array[0];
- queue->array[0] = queue->array[--queue->size];
- trickle_down(0, queue);
- return data;
-}
-
-void pqueue_remove_at(int index, struct pqueue *queue)
-{
- queue->array[index] = queue->array[--queue->size];
-
- if (index > 0
- && (*queue->cmp)(queue->array[index],
- queue->array[PARENT_OF(index)])
- < 0) {
- trickle_up(index, queue);
- } else {
- trickle_down(index, queue);
- }
-}
-
-void pqueue_remove(void *data, struct pqueue *queue)
-{
- for (int i = 0; i < queue->size; i++)
- if (queue->array[i] == data)
- pqueue_remove_at(i, queue);
-}
diff --git a/lib/pqueue.h b/lib/pqueue.h
deleted file mode 100644
index 032ee9db4c..0000000000
--- a/lib/pqueue.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Priority queue functions.
- * Copyright (C) 2003 Yasuhiro Ohara
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef _ZEBRA_PQUEUE_H
-#define _ZEBRA_PQUEUE_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct pqueue {
- void **array;
- int array_size;
- int size;
-
- int (*cmp)(void *, void *);
- void (*update)(void *node, int actual_position);
-};
-
-#define PQUEUE_INIT_ARRAYSIZE 32
-
-extern struct pqueue *pqueue_create(void);
-extern void pqueue_delete(struct pqueue *queue);
-
-extern void pqueue_enqueue(void *data, struct pqueue *queue);
-extern void *pqueue_dequeue(struct pqueue *queue);
-extern void pqueue_remove_at(int index, struct pqueue *queue);
-extern void pqueue_remove(void *data, struct pqueue *queue);
-
-extern void trickle_down(int index, struct pqueue *queue);
-extern void trickle_up(int index, struct pqueue *queue);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ZEBRA_PQUEUE_H */
diff --git a/lib/seqlock.c b/lib/seqlock.c
index 223d14952c..c05ec19db4 100644
--- a/lib/seqlock.c
+++ b/lib/seqlock.c
@@ -25,6 +25,7 @@
#include "config.h"
#endif
+#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
@@ -35,44 +36,75 @@
#include "seqlock.h"
+/****************************************
+ * OS specific synchronization wrappers *
+ ****************************************/
+
+/*
+ * Linux: sys_futex()
+ */
#ifdef HAVE_SYNC_LINUX_FUTEX
-/* Linux-specific - sys_futex() */
#include <sys/syscall.h>
#include <linux/futex.h>
-static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout,
- void *addr2, int val3)
+static long sys_futex(void *addr1, int op, int val1,
+ const struct timespec *timeout, void *addr2, int val3)
{
return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
}
#define wait_once(sqlo, val) \
sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
+#define wait_time(sqlo, val, time, reltime) \
+ sys_futex((int *)&sqlo->pos, FUTEX_WAIT_BITSET, (int)val, time, \
+ NULL, ~0U)
#define wait_poke(sqlo) \
sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
+/*
+ * OpenBSD: sys_futex(), almost the same as on Linux
+ */
#elif defined(HAVE_SYNC_OPENBSD_FUTEX)
-/* OpenBSD variant of the above. untested, not upstream in OpenBSD. */
#include <sys/syscall.h>
#include <sys/futex.h>
+#define TIME_RELATIVE 1
+
#define wait_once(sqlo, val) \
futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
+#define wait_time(sqlo, val, time, reltime) \
+ futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, reltime, NULL, 0)
#define wait_poke(sqlo) \
futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
+/*
+ * FreeBSD: _umtx_op()
+ */
#elif defined(HAVE_SYNC_UMTX_OP)
-/* FreeBSD-specific: umtx_op() */
#include <sys/umtx.h>
#define wait_once(sqlo, val) \
_umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL)
+static int wait_time(struct seqlock *sqlo, uint32_t val,
+ const struct timespec *abstime,
+ const struct timespec *reltime)
+{
+ struct _umtx_time t;
+ t._flags = UMTX_ABSTIME;
+ t._clockid = CLOCK_MONOTONIC;
+ memcpy(&t._timeout, abstime, sizeof(t._timeout));
+ return _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val,
+ (void *)(uintptr_t) sizeof(t), &t);
+}
#define wait_poke(sqlo) \
_umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL)
-#else
-/* generic version. used on *BSD, Solaris and OSX.
+/*
+ * generic version. used on NetBSD, Solaris and OSX. really shitty.
*/
+#else
+
+#define TIME_ABS_REALTIME 1
#define wait_init(sqlo) do { \
pthread_mutex_init(&sqlo->lock, NULL); \
@@ -80,6 +112,9 @@ static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout,
} while (0)
#define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock)
#define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock)
+#define wait_time(sqlo, val, time, reltime) \
+ pthread_cond_timedwait(&sqlo->wake, \
+ &sqlo->lock, time);
#define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock)
#define wait_poke(sqlo) do { \
pthread_mutex_lock(&sqlo->lock); \
@@ -103,18 +138,112 @@ void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val)
seqlock_assert_valid(val);
wait_prep(sqlo);
- while (1) {
- cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire);
- if (!(cur & 1))
+ cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
+
+ while (cur & SEQLOCK_HELD) {
+ cal = SEQLOCK_VAL(cur) - val - 1;
+ assert(cal < 0x40000000 || cal > 0xc0000000);
+ if (cal < 0x80000000)
break;
- cal = cur - val - 1;
+
+ if ((cur & SEQLOCK_WAITERS)
+ || atomic_compare_exchange_weak_explicit(
+ &sqlo->pos, &cur, cur | SEQLOCK_WAITERS,
+ memory_order_relaxed, memory_order_relaxed)) {
+ wait_once(sqlo, cur | SEQLOCK_WAITERS);
+ cur = atomic_load_explicit(&sqlo->pos,
+ memory_order_relaxed);
+ }
+ /* else: we failed to swap in cur because it just changed */
+ }
+ wait_done(sqlo);
+}
+
+bool seqlock_timedwait(struct seqlock *sqlo, seqlock_val_t val,
+ const struct timespec *abs_monotime_limit)
+{
+/*
+ * ABS_REALTIME - used on NetBSD, Solaris and OSX
+ */
+#if TIME_ABS_REALTIME
+#define time_arg1 &abs_rt
+#define time_arg2 NULL
+#define time_prep
+ struct timespec curmono, abs_rt;
+
+ clock_gettime(CLOCK_MONOTONIC, &curmono);
+ clock_gettime(CLOCK_REALTIME, &abs_rt);
+
+ abs_rt.tv_nsec += abs_monotime_limit->tv_nsec - curmono.tv_nsec;
+ if (abs_rt.tv_nsec < 0) {
+ abs_rt.tv_sec--;
+ abs_rt.tv_nsec += 1000000000;
+ } else if (abs_rt.tv_nsec >= 1000000000) {
+ abs_rt.tv_sec++;
+ abs_rt.tv_nsec -= 1000000000;
+ }
+ abs_rt.tv_sec += abs_monotime_limit->tv_sec - curmono.tv_sec;
+
+/*
+ * RELATIVE - used on OpenBSD (might get a patch to get absolute monotime)
+ */
+#elif TIME_RELATIVE
+ struct timespec reltime;
+
+#define time_arg1 abs_monotime_limit
+#define time_arg2 &reltime
+#define time_prep \
+ clock_gettime(CLOCK_MONOTONIC, &reltime); \
+ reltime.tv_sec = abs_monotime_limit.tv_sec - reltime.tv_sec; \
+ reltime.tv_nsec = abs_monotime_limit.tv_nsec - reltime.tv_nsec; \
+ if (reltime.tv_nsec < 0) { \
+ reltime.tv_sec--; \
+ reltime.tv_nsec += 1000000000; \
+ }
+/*
+ * FreeBSD & Linux: absolute time re. CLOCK_MONOTONIC
+ */
+#else
+#define time_arg1 abs_monotime_limit
+#define time_arg2 NULL
+#define time_prep
+#endif
+
+ bool ret = true;
+ seqlock_val_t cur, cal;
+
+ seqlock_assert_valid(val);
+
+ wait_prep(sqlo);
+ cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
+
+ while (cur & SEQLOCK_HELD) {
+ cal = SEQLOCK_VAL(cur) - val - 1;
assert(cal < 0x40000000 || cal > 0xc0000000);
if (cal < 0x80000000)
break;
- wait_once(sqlo, cur);
+ if ((cur & SEQLOCK_WAITERS)
+ || atomic_compare_exchange_weak_explicit(
+ &sqlo->pos, &cur, cur | SEQLOCK_WAITERS,
+ memory_order_relaxed, memory_order_relaxed)) {
+ int rv;
+
+ time_prep
+
+ rv = wait_time(sqlo, cur | SEQLOCK_WAITERS, time_arg1,
+ time_arg2);
+ if (rv) {
+ ret = false;
+ break;
+ }
+ cur = atomic_load_explicit(&sqlo->pos,
+ memory_order_relaxed);
+ }
}
wait_done(sqlo);
+
+ return ret;
}
bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val)
@@ -123,26 +252,32 @@ bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val)
seqlock_assert_valid(val);
- cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire);
- if (!(cur & 1))
+ cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
+ if (!(cur & SEQLOCK_HELD))
return 1;
- cur -= val;
+ cur = SEQLOCK_VAL(cur) - val - 1;
assert(cur < 0x40000000 || cur > 0xc0000000);
return cur < 0x80000000;
}
void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val)
{
+ seqlock_val_t prev;
+
seqlock_assert_valid(val);
- atomic_store_explicit(&sqlo->pos, val, memory_order_release);
- wait_poke(sqlo);
+ prev = atomic_exchange_explicit(&sqlo->pos, val, memory_order_relaxed);
+ if (prev & SEQLOCK_WAITERS)
+ wait_poke(sqlo);
}
void seqlock_release(struct seqlock *sqlo)
{
- atomic_store_explicit(&sqlo->pos, 0, memory_order_release);
- wait_poke(sqlo);
+ seqlock_val_t prev;
+
+ prev = atomic_exchange_explicit(&sqlo->pos, 0, memory_order_relaxed);
+ if (prev & SEQLOCK_WAITERS)
+ wait_poke(sqlo);
}
void seqlock_init(struct seqlock *sqlo)
@@ -154,14 +289,23 @@ void seqlock_init(struct seqlock *sqlo)
seqlock_val_t seqlock_cur(struct seqlock *sqlo)
{
- return atomic_load_explicit(&sqlo->pos, memory_order_acquire);
+ return SEQLOCK_VAL(atomic_load_explicit(&sqlo->pos,
+ memory_order_relaxed));
}
seqlock_val_t seqlock_bump(struct seqlock *sqlo)
{
- seqlock_val_t val;
+ seqlock_val_t val, cur;
+
+ cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
+ seqlock_assert_valid(cur);
+
+ do {
+ val = SEQLOCK_VAL(cur) + SEQLOCK_INCR;
+ } while (!atomic_compare_exchange_weak_explicit(&sqlo->pos, &cur, val,
+ memory_order_relaxed, memory_order_relaxed));
- val = atomic_fetch_add_explicit(&sqlo->pos, 2, memory_order_release);
- wait_poke(sqlo);
+ if (cur & SEQLOCK_WAITERS)
+ wait_poke(sqlo);
return val;
}
diff --git a/lib/seqlock.h b/lib/seqlock.h
index eef05a4307..b551e3ffc4 100644
--- a/lib/seqlock.h
+++ b/lib/seqlock.h
@@ -54,12 +54,28 @@
*/
/* use sequentially increasing "ticket numbers". lowest bit will always
- * be 1 to have a 'cleared' indication (i.e., counts 1,3,5,7,etc. )
+ * be 1 to have a 'cleared' indication (i.e., counts 1,5,9,13,etc. )
+ * 2nd lowest bit is used to indicate we have waiters.
*/
typedef _Atomic uint32_t seqlock_ctr_t;
typedef uint32_t seqlock_val_t;
-#define seqlock_assert_valid(val) assert(val & 1)
+#define seqlock_assert_valid(val) assert((val) & SEQLOCK_HELD)
+/* NB: SEQLOCK_WAITERS is only allowed if SEQLOCK_HELD is also set; can't
+ * have waiters on an unheld seqlock
+ */
+#define SEQLOCK_HELD (1U << 0)
+#define SEQLOCK_WAITERS (1U << 1)
+#define SEQLOCK_VAL(n) ((n) & ~SEQLOCK_WAITERS)
+#define SEQLOCK_STARTVAL 1U
+#define SEQLOCK_INCR 4U
+
+/* TODO: originally, this was using "atomic_fetch_add", which is the reason
+ * bit 0 is used to indicate held state. With SEQLOCK_WAITERS added, there's
+ * no fetch_add anymore (cmpxchg loop instead), so we don't need to use bit 0
+ * for this anymore & can just special-case the value 0 for it and skip it in
+ * counting.
+ */
struct seqlock {
/* always used */
@@ -74,8 +90,16 @@ struct seqlock {
extern void seqlock_init(struct seqlock *sqlo);
-/* while (sqlo <= val) - wait until seqlock->pos > val, or seqlock unheld */
+/* basically: "while (sqlo <= val) wait();"
+ * returns when sqlo > val || !seqlock_held(sqlo)
+ */
extern void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val);
+
+/* same, but time-limited (limit is an absolute CLOCK_MONOTONIC value) */
+extern bool seqlock_timedwait(struct seqlock *sqlo, seqlock_val_t val,
+ const struct timespec *abs_monotime_limit);
+
+/* one-shot test, returns true if seqlock_wait would return immediately */
extern bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val);
static inline bool seqlock_held(struct seqlock *sqlo)
@@ -85,12 +109,20 @@ static inline bool seqlock_held(struct seqlock *sqlo)
/* sqlo - get seqlock position -- for the "counter" seqlock */
extern seqlock_val_t seqlock_cur(struct seqlock *sqlo);
-/* sqlo++ - note: like x++, returns previous value, before bumping */
+
+/* ++sqlo (but atomic & wakes waiters) - returns value that we bumped to.
+ *
+ * guarantees:
+ * - each seqlock_bump call bumps the position by exactly one SEQLOCK_INCR.
+ * There are no skipped/missed or multiple increments.
+ * - each return value is only returned from one seqlock_bump() call
+ */
extern seqlock_val_t seqlock_bump(struct seqlock *sqlo);
/* sqlo = val - can be used on held seqlock. */
extern void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val);
+
/* sqlo = ref - standard pattern: acquire relative to other seqlock */
static inline void seqlock_acquire(struct seqlock *sqlo, struct seqlock *ref)
{
diff --git a/lib/sigevent.c b/lib/sigevent.c
index d02b074223..fcd85d0d43 100644
--- a/lib/sigevent.c
+++ b/lib/sigevent.c
@@ -24,7 +24,6 @@
#include <memory.h>
#include <lib_errors.h>
-#ifdef SA_SIGINFO
#ifdef HAVE_UCONTEXT_H
#ifdef GNU_LINUX
/* get REG_EIP from ucontext.h */
@@ -34,7 +33,6 @@
#endif /* GNU_LINUX */
#include <ucontext.h>
#endif /* HAVE_UCONTEXT_H */
-#endif /* SA_SIGINFO */
/* master signals descriptor struct */
@@ -158,8 +156,6 @@ static int signal_set(int signo)
return 0;
}
-#ifdef SA_SIGINFO
-
/* XXX This function should be enhanced to support more platforms
(it currently works only on Linux/x86). */
static void *program_counter(void *context)
@@ -199,41 +195,19 @@ static void *program_counter(void *context)
return NULL;
}
-#endif /* SA_SIGINFO */
-
static void __attribute__((noreturn))
-exit_handler(int signo
-#ifdef SA_SIGINFO
- ,
- siginfo_t *siginfo, void *context
-#endif
- )
+exit_handler(int signo, siginfo_t *siginfo, void *context)
{
-#ifndef SA_SIGINFO
- void *siginfo = NULL;
- void *pc = NULL;
-#else
void *pc = program_counter(context);
-#endif
zlog_signal(signo, "exiting...", siginfo, pc);
_exit(128 + signo);
}
static void __attribute__((noreturn))
-core_handler(int signo
-#ifdef SA_SIGINFO
- ,
- siginfo_t *siginfo, void *context
-#endif
- )
+core_handler(int signo, siginfo_t *siginfo, void *context)
{
-#ifndef SA_SIGINFO
- void *siginfo = NULL;
- void *pc = NULL;
-#else
void *pc = program_counter(context);
-#endif
/* make sure we don't hang in here. default for SIGALRM is terminate.
* - if we're in backtrace for more than a second, abort. */
@@ -290,12 +264,7 @@ static void trap_default_signals(void)
static const struct {
const int *sigs;
unsigned int nsigs;
- void (*handler)(int signo
-#ifdef SA_SIGINFO
- ,
- siginfo_t *info, void *context
-#endif
- );
+ void (*handler)(int signo, siginfo_t *info, void *context);
} sigmap[] = {
{core_signals, array_size(core_signals), core_handler},
{exit_signals, array_size(exit_signals), exit_handler},
@@ -316,15 +285,10 @@ static void trap_default_signals(void)
act.sa_handler = SIG_IGN;
act.sa_flags = 0;
} else {
-#ifdef SA_SIGINFO
/* Request extra arguments to signal
* handler. */
act.sa_sigaction = sigmap[i].handler;
act.sa_flags = SA_SIGINFO;
-#else
- act.sa_handler = sigmap[i].handler;
- act.sa_flags = 0;
-#endif
#ifdef SA_RESETHAND
/* don't try to print backtraces
* recursively */
diff --git a/lib/subdir.am b/lib/subdir.am
index f4fe369a97..2be7537bcc 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -21,6 +21,7 @@ lib_libfrr_la_SOURCES = \
lib/distribute.c \
lib/ferr.c \
lib/filter.c \
+ lib/frrcu.c \
lib/frrlua.c \
lib/frr_pthread.c \
lib/frrstr.c \
@@ -61,7 +62,6 @@ lib_libfrr_la_SOURCES = \
lib/openbsd-tree.c \
lib/pid_output.c \
lib/plist.c \
- lib/pqueue.c \
lib/prefix.c \
lib/privs.c \
lib/ptm_lib.c \
@@ -161,6 +161,7 @@ pkginclude_HEADERS += \
lib/frrlua.h \
lib/frr_pthread.h \
lib/frratomic.h \
+ lib/frrcu.h \
lib/frrstr.h \
lib/getopt.h \
lib/graph.h \
@@ -198,7 +199,6 @@ pkginclude_HEADERS += \
lib/openbsd-queue.h \
lib/openbsd-tree.h \
lib/plist.h \
- lib/pqueue.h \
lib/prefix.h \
lib/printfrr.h \
lib/privs.h \
diff --git a/lib/thread.c b/lib/thread.c
index fc2de09df0..5756ebc1f9 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -25,9 +25,9 @@
#include "thread.h"
#include "memory.h"
+#include "frrcu.h"
#include "log.h"
#include "hash.h"
-#include "pqueue.h"
#include "command.h"
#include "sigevent.h"
#include "network.h"
@@ -42,6 +42,22 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats")
DECLARE_LIST(thread_list, struct thread, threaditem)
+static int thread_timer_cmp(const struct thread *a, const struct thread *b)
+{
+ if (a->u.sands.tv_sec < b->u.sands.tv_sec)
+ return -1;
+ if (a->u.sands.tv_sec > b->u.sands.tv_sec)
+ return 1;
+ if (a->u.sands.tv_usec < b->u.sands.tv_usec)
+ return -1;
+ if (a->u.sands.tv_usec > b->u.sands.tv_usec)
+ return 1;
+ return 0;
+}
+
+DECLARE_HEAP(thread_timer_list, struct thread, timeritem,
+ thread_timer_cmp)
+
#if defined(__APPLE__)
#include <mach/mach.h>
#include <mach/mach_time.h>
@@ -401,25 +417,6 @@ void thread_cmd_init(void)
/* CLI end ------------------------------------------------------------------ */
-static int thread_timer_cmp(void *a, void *b)
-{
- struct thread *thread_a = a;
- struct thread *thread_b = b;
-
- if (timercmp(&thread_a->u.sands, &thread_b->u.sands, <))
- return -1;
- if (timercmp(&thread_a->u.sands, &thread_b->u.sands, >))
- return 1;
- return 0;
-}
-
-static void thread_timer_update(void *node, int actual_position)
-{
- struct thread *thread = node;
-
- thread->index = actual_position;
-}
-
static void cancelreq_del(void *cr)
{
XFREE(MTYPE_TMP, cr);
@@ -464,11 +461,7 @@ struct thread_master *thread_master_create(const char *name)
thread_list_init(&rv->event);
thread_list_init(&rv->ready);
thread_list_init(&rv->unuse);
-
- /* Initialize the timer queues */
- rv->timer = pqueue_create();
- rv->timer->cmp = thread_timer_cmp;
- rv->timer->update = thread_timer_update;
+ thread_timer_list_init(&rv->timer);
/* Initialize thread_fetch() settings */
rv->spin = true;
@@ -566,16 +559,6 @@ static void thread_array_free(struct thread_master *m,
XFREE(MTYPE_THREAD_POLL, thread_array);
}
-static void thread_queue_free(struct thread_master *m, struct pqueue *queue)
-{
- int i;
-
- for (i = 0; i < queue->size; i++)
- thread_free(m, queue->array[i]);
-
- pqueue_delete(queue);
-}
-
/*
* thread_master_free_unused
*
@@ -598,6 +581,8 @@ void thread_master_free_unused(struct thread_master *m)
/* Stop thread scheduler. */
void thread_master_free(struct thread_master *m)
{
+ struct thread *t;
+
pthread_mutex_lock(&masters_mtx);
{
listnode_delete(masters, m);
@@ -609,7 +594,8 @@ void thread_master_free(struct thread_master *m)
thread_array_free(m, m->read);
thread_array_free(m, m->write);
- thread_queue_free(m, m->timer);
+ while ((t = thread_timer_list_pop(&m->timer)))
+ thread_free(m, t);
thread_list_free(m, &m->event);
thread_list_free(m, &m->ready);
thread_list_free(m, &m->unuse);
@@ -683,7 +669,6 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type,
thread->add_type = type;
thread->master = m;
thread->arg = arg;
- thread->index = -1;
thread->yield = THREAD_YIELD_TIME_SLOT; /* default */
thread->ref = NULL;
@@ -753,6 +738,9 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
< 0) // effect a poll (return immediately)
timeout = 0;
+ rcu_read_unlock();
+ rcu_assert_read_unlocked();
+
/* add poll pipe poker */
assert(count + 1 < pfdsize);
pfds[count].fd = m->io_pipe[0];
@@ -766,6 +754,8 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
while (read(m->io_pipe[0], &trash, sizeof(trash)) > 0)
;
+ rcu_read_lock();
+
return num;
}
@@ -854,7 +844,6 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
struct thread **t_ptr, debugargdef)
{
struct thread *thread;
- struct pqueue *queue;
assert(m != NULL);
@@ -870,7 +859,6 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
return NULL;
}
- queue = m->timer;
thread = thread_get(m, type, func, arg, debugargpass);
pthread_mutex_lock(&thread->mtx);
@@ -878,7 +866,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
monotime(&thread->u.sands);
timeradd(&thread->u.sands, time_relative,
&thread->u.sands);
- pqueue_enqueue(thread, queue);
+ thread_timer_list_add(&m->timer, thread);
if (t_ptr) {
*t_ptr = thread;
thread->ref = t_ptr;
@@ -1055,7 +1043,6 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state)
static void do_thread_cancel(struct thread_master *master)
{
struct thread_list_head *list = NULL;
- struct pqueue *queue = NULL;
struct thread **thread_array = NULL;
struct thread *thread;
@@ -1111,7 +1098,7 @@ static void do_thread_cancel(struct thread_master *master)
thread_array = master->write;
break;
case THREAD_TIMER:
- queue = master->timer;
+ thread_timer_list_del(&master->timer, thread);
break;
case THREAD_EVENT:
list = &master->event;
@@ -1124,16 +1111,10 @@ static void do_thread_cancel(struct thread_master *master)
break;
}
- if (queue) {
- assert(thread->index >= 0);
- assert(thread == queue->array[thread->index]);
- pqueue_remove_at(thread->index, queue);
- } else if (list) {
+ if (list) {
thread_list_del(list, thread);
} else if (thread_array) {
thread_array[thread->u.fd] = NULL;
- } else {
- assert(!"Thread should be either in queue or list or array!");
}
if (thread->ref)
@@ -1251,15 +1232,15 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread,
}
/* ------------------------------------------------------------------------- */
-static struct timeval *thread_timer_wait(struct pqueue *queue,
+static struct timeval *thread_timer_wait(struct thread_timer_list_head *timers,
struct timeval *timer_val)
{
- if (queue->size) {
- struct thread *next_timer = queue->array[0];
- monotime_until(&next_timer->u.sands, timer_val);
- return timer_val;
- }
- return NULL;
+ if (!thread_timer_list_count(timers))
+ return NULL;
+
+ struct thread *next_timer = thread_timer_list_first(timers);
+ monotime_until(&next_timer->u.sands, timer_val);
+ return timer_val;
}
static struct thread *thread_run(struct thread_master *m, struct thread *thread,
@@ -1369,17 +1350,16 @@ static void thread_process_io(struct thread_master *m, unsigned int num)
}
/* Add all timers that have popped to the ready list. */
-static unsigned int thread_process_timers(struct pqueue *queue,
+static unsigned int thread_process_timers(struct thread_timer_list_head *timers,
struct timeval *timenow)
{
struct thread *thread;
unsigned int ready = 0;
- while (queue->size) {
- thread = queue->array[0];
+ while ((thread = thread_timer_list_first(timers))) {
if (timercmp(timenow, &thread->u.sands, <))
return ready;
- pqueue_dequeue(queue);
+ thread_timer_list_pop(timers);
thread->type = THREAD_READY;
thread_list_add_tail(&thread->master->ready, thread);
ready++;
@@ -1461,7 +1441,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
* once per loop to avoid starvation by events
*/
if (!thread_list_count(&m->ready))
- tw = thread_timer_wait(m->timer, &tv);
+ tw = thread_timer_wait(&m->timer, &tv);
if (thread_list_count(&m->ready) ||
(tw && !timercmp(tw, &zerotime, >)))
@@ -1506,7 +1486,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
/* Post timers to ready queue. */
monotime(&now);
- thread_process_timers(m->timer, &now);
+ thread_process_timers(&m->timer, &now);
/* Post I/O to ready queue. */
if (num > 0)
diff --git a/lib/thread.h b/lib/thread.h
index 7897265120..412a4d93bf 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -41,8 +41,7 @@ struct rusage_t {
#define GETRUSAGE(X) thread_getrusage(X)
PREDECL_LIST(thread_list)
-
-struct pqueue;
+PREDECL_HEAP(thread_timer_list)
struct fd_handler {
/* number of pfd that fit in the allocated space of pfds. This is a
@@ -73,7 +72,7 @@ struct thread_master {
struct thread **read;
struct thread **write;
- struct pqueue *timer;
+ struct thread_timer_list_head timer;
struct thread_list_head event, ready, unuse;
struct list *cancel_req;
bool canceled;
@@ -95,6 +94,7 @@ struct thread {
uint8_t type; /* thread type */
uint8_t add_type; /* thread type */
struct thread_list_item threaditem;
+ struct thread_timer_list_item timeritem;
struct thread **ref; /* external reference (if given) */
struct thread_master *master; /* pointer to the struct thread_master */
int (*func)(struct thread *); /* event function */
@@ -104,7 +104,6 @@ struct thread {
int fd; /* file descriptor in case of r/w */
struct timeval sands; /* rest of time sands value. */
} u;
- int index; /* queue position for timers */
struct timeval real;
struct cpu_thread_history *hist; /* cache pointer to cpu_history */
unsigned long yield; /* yield time in microseconds */
diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c
index a63b09fc1f..722ecb2a72 100644
--- a/pimd/pim_neighbor.c
+++ b/pimd/pim_neighbor.c
@@ -424,10 +424,11 @@ struct pim_neighbor *pim_neighbor_find_by_secondary(struct interface *ifp,
struct pim_neighbor *neigh;
struct prefix *p;
- pim_ifp = ifp->info;
- if (!pim_ifp)
+ if (!ifp || !ifp->info)
return NULL;
+ pim_ifp = ifp->info;
+
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, pnode, p)) {
if (prefix_same(p, src))
diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c
index 65ea858cb6..39dc8ad2fa 100644
--- a/pimd/pim_nht.c
+++ b/pimd/pim_nht.c
@@ -842,6 +842,14 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
}
if (!ifp->info) {
+ /*
+ * Though Multicast is not enabled on this
+ * Interface store it in database otheriwse we
+ * may miss this update and this will not cause
+ * any issue, because while choosing the path we
+ * are ommitting the Interfaces which are not
+ * multicast enabled
+ */
if (PIM_DEBUG_PIM_NHT) {
char buf[NEXTHOP_STRLEN];
@@ -853,8 +861,6 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
nexthop2str(nexthop, buf,
sizeof(buf)));
}
- nexthop_free(nexthop);
- continue;
}
if (nhlist_tail) {
diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c
index 517e538abf..88126e84bc 100644
--- a/tests/lib/cxxcompat.c
+++ b/tests/lib/cxxcompat.c
@@ -72,7 +72,6 @@
#include "lib/openbsd-tree.h"
#include "lib/pbr.h"
#include "lib/plist.h"
-#include "lib/pqueue.h"
#include "lib/prefix.h"
#include "lib/privs.h"
#include "lib/ptm_lib.h"
diff --git a/tests/lib/test_atomlist.c b/tests/lib/test_atomlist.c
index 249fff8edb..238ee9539e 100644
--- a/tests/lib/test_atomlist.c
+++ b/tests/lib/test_atomlist.c
@@ -253,7 +253,7 @@ static void *thr1func(void *arg)
struct testrun *tr;
for (tr = runs; tr; tr = tr->next) {
- sv = seqlock_bump(&p->sqlo);
+ sv = seqlock_bump(&p->sqlo) - SEQLOCK_INCR;
seqlock_wait(&sqlo, sv);
tr->func(offset);
@@ -288,14 +288,14 @@ static void run_tr(struct testrun *tr)
size_t c = 0, s = 0, n = 0;
struct item *item, *prev, dummy;
- printf("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 1, "", desc);
+ printf("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 2, "", desc);
fflush(stdout);
if (tr->prefill != NOCLEAR)
clear_list(tr->prefill);
monotime(&tv);
- sv = seqlock_bump(&sqlo);
+ sv = seqlock_bump(&sqlo) - SEQLOCK_INCR;
for (size_t i = 0; i < NTHREADS; i++) {
seqlock_wait(&thr[i].sqlo, seqlock_cur(&sqlo));
s += thr[i].counter;
@@ -325,7 +325,7 @@ static void run_tr(struct testrun *tr)
assert(c == alist_count(&ahead));
}
printf("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n",
- sv >> 1, delta, c, s, n, desc);
+ sv >> 2, delta, c, s, n, desc);
}
#ifdef BASIC_TESTS
@@ -381,7 +381,7 @@ int main(int argc, char **argv)
basic_tests();
seqlock_init(&sqlo);
- seqlock_acquire_val(&sqlo, 1);
+ seqlock_acquire_val(&sqlo, SEQLOCK_STARTVAL);
for (i = 0; i < NTHREADS; i++) {
seqlock_init(&thr[i].sqlo);
diff --git a/tests/lib/test_seqlock.c b/tests/lib/test_seqlock.c
index 9cc6f80702..639c2bdc2b 100644
--- a/tests/lib/test_seqlock.c
+++ b/tests/lib/test_seqlock.c
@@ -66,20 +66,20 @@ static void *thr1func(void *arg)
seqlock_wait(&sqlo, 1);
writestr("thr1 @1\n");
- seqlock_wait(&sqlo, 3);
- writestr("thr1 @3\n");
-
seqlock_wait(&sqlo, 5);
writestr("thr1 @5\n");
- seqlock_wait(&sqlo, 7);
- writestr("thr1 @7\n");
-
seqlock_wait(&sqlo, 9);
writestr("thr1 @9\n");
- seqlock_wait(&sqlo, 11);
- writestr("thr1 @11\n");
+ seqlock_wait(&sqlo, 13);
+ writestr("thr1 @13\n");
+
+ seqlock_wait(&sqlo, 17);
+ writestr("thr1 @17\n");
+
+ seqlock_wait(&sqlo, 21);
+ writestr("thr1 @21\n");
return NULL;
}
@@ -95,11 +95,11 @@ int main(int argc, char **argv)
assert(seqlock_cur(&sqlo) == 1);
assert(seqlock_bump(&sqlo) == 1);
- assert(seqlock_cur(&sqlo) == 3);
- assert(seqlock_bump(&sqlo) == 3);
+ assert(seqlock_cur(&sqlo) == 5);
assert(seqlock_bump(&sqlo) == 5);
- assert(seqlock_bump(&sqlo) == 7);
- assert(seqlock_cur(&sqlo) == 9);
+ assert(seqlock_bump(&sqlo) == 9);
+ assert(seqlock_bump(&sqlo) == 13);
+ assert(seqlock_cur(&sqlo) == 17);
assert(seqlock_held(&sqlo));
seqlock_release(&sqlo);
@@ -108,16 +108,16 @@ int main(int argc, char **argv)
pthread_create(&thr1, NULL, thr1func, NULL);
sleep(1);
- writestr("main @3\n");
- seqlock_acquire_val(&sqlo, 3);
+ writestr("main @5\n");
+ seqlock_acquire_val(&sqlo, 5);
sleep(2);
- writestr("main @5\n");
+ writestr("main @9\n");
seqlock_bump(&sqlo);
sleep(1);
- writestr("main @9\n");
- seqlock_acquire_val(&sqlo, 9);
+ writestr("main @17\n");
+ seqlock_acquire_val(&sqlo, 17);
sleep(1);
writestr("main @release\n");
diff --git a/tests/lib/test_timer_correctness.c b/tests/lib/test_timer_correctness.c
index 43e79ba9d0..cbf9b05546 100644
--- a/tests/lib/test_timer_correctness.c
+++ b/tests/lib/test_timer_correctness.c
@@ -28,7 +28,6 @@
#include <unistd.h>
#include "memory.h"
-#include "pqueue.h"
#include "prng.h"
#include "thread.h"
diff --git a/tests/lib/test_timer_performance.c b/tests/lib/test_timer_performance.c
index d5f4badc85..2960e0d81e 100644
--- a/tests/lib/test_timer_performance.c
+++ b/tests/lib/test_timer_performance.c
@@ -28,7 +28,6 @@
#include <unistd.h>
#include "thread.h"
-#include "pqueue.h"
#include "prng.h"
#define SCHEDULE_TIMERS 1000000
diff --git a/yang/frr-eigrpd.yang b/yang/frr-eigrpd.yang
index 26de7a71ae..853d823880 100644
--- a/yang/frr-eigrpd.yang
+++ b/yang/frr-eigrpd.yang
@@ -57,7 +57,7 @@ module frr-eigrpd {
value 2;
}
- enum hmac-sha2 {
+ enum hmac-sha-256 {
description "HMAC SHA256 algorithm";
value 3;
}
diff --git a/yang/subdir.am b/yang/subdir.am
index 4b3baeea9d..18d2bf160c 100644
--- a/yang/subdir.am
+++ b/yang/subdir.am
@@ -28,6 +28,10 @@ if BFDD
dist_yangmodels_DATA += yang/frr-bfdd.yang
endif
+if EIGRPD
+dist_yangmodels_DATA += yang/frr-eigrpd.yang
+endif
+
if RIPD
dist_yangmodels_DATA += yang/frr-ripd.yang
endif
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index 822def318a..f347d3955c 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -238,7 +238,7 @@ static int netlink_route_info_add_nh(netlink_route_info_t *ri,
if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) {
nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN;
- zl3vni = zl3vni_from_vrf(ri->rtm_table);
+ zl3vni = zl3vni_from_vrf(nexthop->vrf_id);
if (zl3vni && is_l3vni_oper_up(zl3vni)) {
/* Add VNI to VxLAN encap info */
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 2e8c81bddd..1450072aa9 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -2787,27 +2787,40 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
/* Set "local" forwarding info. */
SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
- SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW);
ZEBRA_NEIGH_SET_ACTIVE(n);
- /* Set Router flag (R-bit) */
- if (ip->ipa_type == IPADDR_V6)
- SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
memcpy(&n->emac, macaddr, ETH_ALEN);
n->ifindex = ifp->ifindex;
/* Only advertise in BGP if the knob is enabled */
- if (!advertise_gw_macip_enabled(zvni))
- return 0;
+ if (advertise_gw_macip_enabled(zvni)) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
+ SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW);
+ /* Set Router flag (R-bit) */
+ if (ip->ipa_type == IPADDR_V6)
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
"SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x",
ifp->name, ifp->ifindex, zvni->vni,
prefix_mac2str(macaddr, buf, sizeof(buf)),
ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
- zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
- n->flags, n->loc_seq);
+ zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
+ n->flags, n->loc_seq);
+ } else if (advertise_svi_macip_enabled(zvni)) {
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "SVI %s(%u) L2-VNI %u, sending SVI MAC %s IP %s add to BGP with flags 0x%x",
+ ifp->name, ifp->ifindex, zvni->vni,
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
+
+ zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
+ n->flags, n->loc_seq);
+ }
return 0;
}
@@ -9015,7 +9028,7 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS)
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("EVPN SVI-MACIP Adv %s, currently %s",
advertise ? "enabled" : "disabled",
- advertise_gw_macip_enabled(NULL)
+ advertise_svi_macip_enabled(NULL)
? "enabled"
: "disabled");