diff options
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; @@ -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"); |
