diff options
77 files changed, 1814 insertions, 920 deletions
diff --git a/Makefile.am b/Makefile.am index 3b8deb5884..df03da3b60 100644 --- a/Makefile.am +++ b/Makefile.am @@ -83,9 +83,7 @@ rc_SCRIPTS = \ endif EXTRA_DIST += \ - SERVICES \ aclocal.m4 \ - update-autotools \ m4/README.txt \ \ python/clidef.py \ diff --git a/SERVICES b/SERVICES deleted file mode 100644 index f7f92a736b..0000000000 --- a/SERVICES +++ /dev/null @@ -1,24 +0,0 @@ -# As long as this software is in alpha testing it is not yet included -# in /etc/services files. This means that you may need to add the following -# lines into your /etc/services file on your hosts. -# -# --- Please add this to your /etc/services --- - -# -# GNU Zebra services -# - -zebrasrv 2600/tcp -zebra 2601/tcp -ripd 2602/tcp -ripng 2603/tcp -ospfd 2604/tcp -bgpd 2605/tcp -ospf6d 2606/tcp -ospfapi 2607/tcp -isisd 2608/tcp -babeld 2609/tcp -nhrpd 2610/tcp -pimd 2611/tcp -ldpd 2612/tcp -eigrpd 2613/tcp diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 85b9ffd8ca..99fe80f055 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -52,6 +52,11 @@ struct ecommunity *ecommunity_new(void) sizeof(struct ecommunity)); } +void ecommunity_strfree(char **s) +{ + XFREE(MTYPE_ECOMMUNITY_STR, *s); +} + /* Allocate ecommunities. */ void ecommunity_free(struct ecommunity **ecom) { diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 88bdb5e2ae..2f59308d65 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -156,6 +156,7 @@ extern void ecommunity_unintern(struct ecommunity **); extern unsigned int ecommunity_hash_make(void *); extern struct ecommunity *ecommunity_str2com(const char *, int, int); extern char *ecommunity_ecom2str(struct ecommunity *, int, int); +extern void ecommunity_strfree(char **s); extern int ecommunity_match(const struct ecommunity *, const struct ecommunity *); extern char *ecommunity_str(struct ecommunity *); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 74a4f66098..9f3fb3498c 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -164,6 +164,7 @@ static void vrf_import_rt_free(struct vrf_irt_node *irt) } hash_release(bgp_def->vrf_import_rt_hash, irt); + list_delete_and_null(&irt->vrfs); XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt); } @@ -265,6 +266,7 @@ static struct irt_node *import_rt_new(struct bgp *bgp, static void import_rt_free(struct bgp *bgp, struct irt_node *irt) { hash_release(bgp->import_rt_hash, irt); + list_delete_and_null(&irt->vnis); XFREE(MTYPE_BGP_EVPN_IMPORT_RT, irt); } @@ -327,6 +329,12 @@ static int evpn_route_target_cmp(struct ecommunity *ecom1, return strcmp(ecom1->str, ecom2->str); } +static void evpn_xxport_delete_ecomm(void *val) +{ + struct ecommunity *ecomm = val; + ecommunity_free(&ecomm); +} + /* * Mask off global-admin field of specified extended community (RT), * just retain the local-admin field. @@ -387,7 +395,6 @@ static void unmap_vrf_from_rt(struct bgp *bgp_vrf, struct vrf_irt_node *irt) /* Delete VRF from list for this RT. */ listnode_delete(irt->vrfs, bgp_vrf); if (!listnode_head(irt->vrfs)) { - list_delete_and_null(&irt->vrfs); vrf_import_rt_free(irt); } } @@ -410,7 +417,7 @@ static void map_vni_to_rt(struct bgp *bgp, struct bgpevpn *vpn, mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); - if (irt && irt->vnis) + if (irt) if (is_vni_present_in_irt_vnis(irt->vnis, vpn)) /* Already mapped. */ return; @@ -434,7 +441,6 @@ static void unmap_vni_from_rt(struct bgp *bgp, struct bgpevpn *vpn, /* Delete VNI from hash list for this RT. */ listnode_delete(irt->vnis, vpn); if (!listnode_head(irt->vnis)) { - list_delete_and_null(&irt->vnis); import_rt_free(bgp, irt); } } @@ -765,8 +771,11 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) ecom_tmp.size = 1; ecom_tmp.val = (uint8_t *)eval.val; - attr->ecommunity = - ecommunity_merge(attr->ecommunity, &ecom_tmp); + if (attr->ecommunity) + attr->ecommunity = + ecommunity_merge(attr->ecommunity, &ecom_tmp); + else + attr->ecommunity = ecommunity_dup(&ecom_tmp); } } @@ -1847,6 +1856,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, SET_FLAG(ri->flags, BGP_INFO_VALID); bgp_info_extra_get(ri); ri->extra->parent = bgp_info_lock(parent_ri); + bgp_lock_node((struct bgp_node *)parent_ri->net); if (parent_ri->extra) { memcpy(&ri->extra->label, &parent_ri->extra->label, sizeof(ri->extra->label)); @@ -1919,6 +1929,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, SET_FLAG(ri->flags, BGP_INFO_VALID); bgp_info_extra_get(ri); ri->extra->parent = bgp_info_lock(parent_ri); + bgp_lock_node((struct bgp_node *)parent_ri->net); if (parent_ri->extra) { memcpy(&ri->extra->label, &parent_ri->extra->label, sizeof(ri->extra->label)); @@ -2099,7 +2110,7 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf, struct bgp_info *ri) /* See if this RT matches specified VNIs import RTs */ irt = lookup_vrf_import_rt(eval); - if (irt && irt->vrfs) + if (irt) if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) return 1; @@ -2117,7 +2128,7 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf, struct bgp_info *ri) mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_vrf_import_rt(&eval_tmp); } - if (irt && irt->vrfs) + if (irt) if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf)) return 1; } @@ -2166,7 +2177,7 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, /* See if this RT matches specified VNIs import RTs */ irt = lookup_import_rt(bgp, eval); - if (irt && irt->vnis) + if (irt) if (is_vni_present_in_irt_vnis(irt->vnis, vpn)) return 1; @@ -2184,7 +2195,7 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); } - if (irt && irt->vnis) + if (irt) if (is_vni_present_in_irt_vnis(irt->vnis, vpn)) return 1; } @@ -2538,7 +2549,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, * into l2vni table) */ irt = lookup_import_rt(bgp, eval); - if (irt && irt->vnis) + if (irt) install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri, irt->vnis, import); @@ -2546,7 +2557,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, * into l3vni/vrf table) */ vrf_irt = lookup_vrf_import_rt(eval); - if (vrf_irt && vrf_irt->vrfs) + if (vrf_irt) install_uninstall_route_in_vrfs(bgp, afi, safi, evp, ri, vrf_irt->vrfs, import); @@ -2566,10 +2577,10 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, irt = lookup_import_rt(bgp, &eval_tmp); vrf_irt = lookup_vrf_import_rt(&eval_tmp); } - if (irt && irt->vnis) + if (irt) install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri, irt->vnis, import); - if (vrf_irt && vrf_irt->vrfs) + if (vrf_irt) install_uninstall_route_in_vrfs(bgp, afi, safi, evp, ri, vrf_irt->vrfs, import); } @@ -3240,8 +3251,6 @@ static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf) static void update_autort_vni(struct hash_backet *backet, struct bgp *bgp) { struct bgpevpn *vpn = backet->data; - struct listnode *node, *nnode; - struct ecommunity *ecom; if (!vpn) { zlog_warn("%s: VNI hash entry for VNI not found", __PRETTY_FUNCTION__); @@ -3252,16 +3261,12 @@ static void update_autort_vni(struct hash_backet *backet, struct bgp *bgp) if (is_vni_live(vpn)) bgp_evpn_uninstall_routes(bgp, vpn); bgp_evpn_unmap_vni_from_its_rts(bgp, vpn); - for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) - ecommunity_free(&ecom); list_delete_all_node(vpn->import_rtl); bgp_evpn_derive_auto_rt_import(bgp, vpn); if (is_vni_live(vpn)) bgp_evpn_install_routes(bgp, vpn); } if (!is_export_rt_configured(vpn)) { - for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) - ecommunity_free(&ecom); list_delete_all_node(vpn->export_rtl); bgp_evpn_derive_auto_rt_export(bgp, vpn); if (is_vni_live(vpn)) @@ -3326,7 +3331,7 @@ void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct prefix *p, int ret = 0; struct prefix_evpn evp; char buf[PREFIX_STRLEN]; - + build_type5_prefix_from_ip_prefix(&evp, p); ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr); if (ret) @@ -4100,8 +4105,10 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni, /* Initialize route-target import and export lists */ vpn->import_rtl = list_new(); vpn->import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; + vpn->import_rtl->del = evpn_xxport_delete_ecomm; vpn->export_rtl = list_new(); vpn->export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; + vpn->export_rtl->del = evpn_xxport_delete_ecomm; bf_assign_index(bm->rd_idspace, vpn->rd_id); derive_rd_rt_for_vni(bgp, vpn); @@ -4651,10 +4658,11 @@ void bgp_evpn_init(struct bgp *bgp) bgp->vrf_import_rtl = list_new(); bgp->vrf_import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; - + bgp->vrf_import_rtl->del = evpn_xxport_delete_ecomm; bgp->vrf_export_rtl = list_new(); bgp->vrf_export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp; + bgp->vrf_export_rtl->del = evpn_xxport_delete_ecomm; bgp->l2vnis = list_new(); bgp->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp; } diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index e920bbb212..12f18016b6 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -2578,13 +2578,6 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn) } } -static void write_vni_config_for_entry(struct hash_backet *backet, - struct vty *vty) -{ - struct bgpevpn *vpn = (struct bgpevpn *)backet->data; - write_vni_config(vty, vpn); -} - #if defined(HAVE_CUMULUS) DEFUN (bgp_evpn_advertise_default_gw_vni, bgp_evpn_advertise_default_gw_vni_cmd, @@ -4419,6 +4412,15 @@ DEFUN (no_bgp_evpn_vni_rt_without_val, return CMD_SUCCESS; } #endif + +static int vni_cmp(const void **a, const void **b) +{ + const struct bgpevpn *first = *a; + const struct bgpevpn *secnd = *b; + + return secnd->vni - first->vni; +} + /* * Output EVPN configuration information. */ @@ -4427,11 +4429,17 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, { char buf1[RD_ADDRSTRLEN]; - if (bgp->vnihash) - hash_iterate(bgp->vnihash, - (void (*)(struct hash_backet *, - void *))write_vni_config_for_entry, - vty); + if (bgp->vnihash) { + struct list *vnilist = hash_to_list(bgp->vnihash); + struct listnode *ln; + struct bgpevpn *data; + + list_sort(vnilist, vni_cmp); + for (ALL_LIST_ELEMENTS_RO(vnilist, ln, data)) + write_vni_config(vty, data); + + list_delete_and_null(&vnilist); + } if (bgp->advertise_all_vni) vty_out(vty, " advertise-all-vni\n"); diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 5db7e37089..6eb1e39884 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -166,6 +166,9 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, BGP_FLOWSPEC_NLRI_STRING_MAX, "EC{%s}", s == NULL ? "none" : s); + + if (s) + ecommunity_strfree(&s); } snprintf(local_string, BGP_FLOWSPEC_NLRI_STRING_MAX, "FS Rx %s %s %s %s", withdraw ? diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 004bdd90a2..acb4272cd1 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -68,6 +68,9 @@ static const struct option longopts[] = { {"bgp_port", required_argument, NULL, 'p'}, {"listenon", required_argument, NULL, 'l'}, +#if CONFDATE > 20190521 + CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") +#endif {"retain", no_argument, NULL, 'r'}, {"no_kernel", no_argument, NULL, 'n'}, {"skip_runas", no_argument, NULL, 'S'}, @@ -101,9 +104,6 @@ static struct quagga_signal_t bgp_signals[] = { }, }; -/* Route retain mode flag. */ -static int retain_mode = 0; - /* privileges */ static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_NET_RAW, ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN}; @@ -146,8 +146,7 @@ __attribute__((__noreturn__)) void sigint(void) assert(bm->terminating == false); bm->terminating = true; /* global flag that shutting down */ - if (!retain_mode) - bgp_terminate(); + bgp_terminate(); bgp_exit(0); @@ -324,6 +323,11 @@ FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT, .privs = &bgpd_privs, ) +#if CONFDATE > 20190521 +CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") +#endif +#define DEPRECATED_OPTIONS "r" + /* Main routine of bgpd. Treatment of argument and start bgp finite state machine is handled at here. */ int main(int argc, char **argv) @@ -338,10 +342,9 @@ int main(int argc, char **argv) frr_preinit(&bgpd_di, argc, argv); frr_opt_add( - "p:l:rSne:", longopts, + "p:l:Sne:" DEPRECATED_OPTIONS, longopts, " -p, --bgp_port Set BGP listen port number (0 means do not listen).\n" " -l, --listenon Listen on specified address (implies -n)\n" - " -r, --retain When program terminates, retain added route by bgpd.\n" " -n, --no_kernel Do not install route to kernel.\n" " -S, --skip_runas Skip capabilities checks, and changing user and group IDs.\n" " -e, --ecmp Specify ECMP to use.\n"); @@ -350,6 +353,13 @@ int main(int argc, char **argv) while (1) { opt = frr_getopt(argc, argv, 0); + if (opt && opt < 128 && strchr(DEPRECATED_OPTIONS, opt)) { + fprintf(stderr, + "The -%c option no longer exists.\nPlease refer to the manual.\n", + opt); + continue; + } + if (opt == EOF) break; @@ -373,9 +383,6 @@ int main(int argc, char **argv) return 1; } break; - case 'r': - retain_mode = 1; - break; case 'l': bgp_address = optarg; /* listenon implies -n */ diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 55365df299..b58e9da6f4 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -565,12 +565,13 @@ leak_update( if (nexthop_self_flag) bgp_info_set_flag(bn, new, BGP_INFO_ANNC_NH_SELF); + bgp_info_extra_get(new); + if (num_labels) setlabels(new, label, num_labels); - bgp_info_extra_get(new); new->extra->parent = bgp_info_lock(parent); - + bgp_lock_node((struct bgp_node *)((struct bgp_info *)parent)->net); if (bgp_orig) new->extra->bgp_orig = bgp_orig; if (nexthop_orig) @@ -582,7 +583,7 @@ leak_update( struct bgp *bgp_nexthop = bgp; int nh_valid; - if (new->extra && new->extra->bgp_orig) + if (new->extra->bgp_orig) bgp_nexthop = new->extra->bgp_orig; /* diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 8b6ff3fa22..2c9e379299 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -88,7 +88,7 @@ int bgp_find_nexthop(struct bgp_info *path, int connected) static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc) { - if (LIST_EMPTY(&(bnc->paths)) && !bnc->nht_info) { + if (LIST_EMPTY(&(bnc->paths)) && bnc->nht_info) { if (BGP_DEBUG(nht, NHT)) { char buf[PREFIX2STR_BUFFER]; zlog_debug("bgp_unlink_nexthop: freeing bnc %s", diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 84ea33b586..132fa5fc9d 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -205,6 +205,14 @@ struct bgp_info *bgp_info_new(void) /* Free bgp route information. */ static void bgp_info_free(struct bgp_info *binfo) { + /* unlink reference to parent, if any. */ + if (binfo->extra && binfo->extra->parent) { + bgp_info_unlock((struct bgp_info *)binfo->extra->parent); + bgp_unlock_node((struct bgp_node *)((struct bgp_info *)binfo + ->extra->parent)->net); + binfo->extra->parent = NULL; + } + if (binfo->attr) bgp_attr_unintern(&binfo->attr); @@ -225,11 +233,6 @@ struct bgp_info *bgp_info_lock(struct bgp_info *binfo) struct bgp_info *bgp_info_unlock(struct bgp_info *binfo) { - /* unlink reference to parent, if any. */ - if (binfo->extra && binfo->extra->parent) { - bgp_info_unlock((struct bgp_info *)binfo->extra->parent); - binfo->extra->parent = NULL; - } assert(binfo && binfo->lock > 0); binfo->lock--; @@ -9645,13 +9648,13 @@ static struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, json_object *json_no = NULL; json_no = json_object_new_object(); json_object_string_add(json_no, "warning", - "No such neighbor"); + "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\n"); + vty_out(vty, "No such neighbor in this view/vrf\n"); return NULL; } @@ -10762,10 +10765,8 @@ DEFUN (show_ip_bgp_neighbor_routes, peerstr = argv[++idx]->arg; peer = peer_lookup_in_view(vty, bgp, peerstr, uj); - if (!peer) { - vty_out(vty, "No such neighbor\n"); + if (!peer) return CMD_WARNING; - } if (argv_find(argv, argc, "flap-statistics", &idx)) sh_type = bgp_show_type_flap_neighbor; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 35477ba164..257adda3f3 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -553,39 +553,46 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, const char *arg) { int ret; + bool found = false; struct peer *peer; struct listnode *node, *nnode; /* Clear all neighbors. */ /* * Pass along pointer to next node to peer_clear() when walking all - * nodes - * on the BGP instance as that may get freed if it is a doppelganger + * nodes on the BGP instance as that may get freed if it is a + * doppelganger */ if (sort == clear_all) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (!peer->afc[afi][safi]) + continue; + if (stype == BGP_CLEAR_SOFT_NONE) ret = peer_clear(peer, &nnode); - else if (peer->afc[afi][safi]) - ret = peer_clear_soft(peer, afi, safi, stype); else - ret = 0; + ret = peer_clear_soft(peer, afi, safi, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); + else + found = true; } /* This is to apply read-only mode on this clear. */ if (stype == BGP_CLEAR_SOFT_NONE) bgp->update_delay_over = 0; + if (!found) + vty_out(vty, "%%BGP: No %s peer configured", + afi_safi_print(afi, safi)); + return CMD_SUCCESS; } - /* Clear specified neighbors. */ + /* Clear specified neighbor. */ if (sort == clear_peer) { union sockunion su; - int ret; /* Make sockunion for lookup. */ ret = str2sockunion(arg, &su); @@ -610,7 +617,9 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, } } - if (stype == BGP_CLEAR_SOFT_NONE) + if (!peer->afc[afi][safi]) + ret = BGP_ERR_AF_UNCONFIGURED; + else if (stype == BGP_CLEAR_SOFT_NONE) ret = peer_clear(peer, NULL); else ret = peer_clear_soft(peer, afi, safi, stype); @@ -621,7 +630,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, return CMD_SUCCESS; } - /* Clear all peer-group members. */ + /* Clear all neighbors belonging to a specific peer-group. */ if (sort == clear_group) { struct peer_group *group; @@ -632,27 +641,37 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, } for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (stype == BGP_CLEAR_SOFT_NONE) { - peer_clear(peer, NULL); - continue; - } - if (!peer->afc[afi][safi]) continue; - ret = peer_clear_soft(peer, afi, safi, stype); + if (stype == BGP_CLEAR_SOFT_NONE) + ret = peer_clear(peer, NULL); + else + ret = peer_clear_soft(peer, afi, safi, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); + else + found = true; } + + if (!found) + vty_out(vty, + "%%BGP: No %s peer belonging to peer-group %s is configured\n", + afi_safi_print(afi, safi), arg); + return CMD_SUCCESS; } + /* Clear all external (eBGP) neighbors. */ if (sort == clear_external) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (peer->sort == BGP_PEER_IBGP) continue; + if (!peer->afc[afi][safi]) + continue; + if (stype == BGP_CLEAR_SOFT_NONE) ret = peer_clear(peer, &nnode); else @@ -660,33 +679,44 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); + else + found = true; } + + if (!found) + vty_out(vty, + "%%BGP: No external %s peer is configured\n", + afi_safi_print(afi, safi)); + return CMD_SUCCESS; } + /* Clear all neighbors belonging to a specific AS. */ if (sort == clear_as) { - as_t as; - int find = 0; - - as = strtoul(arg, NULL, 10); + as_t as = strtoul(arg, NULL, 10); for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (peer->as != as) continue; - find = 1; - if (stype == BGP_CLEAR_SOFT_NONE) + if (!peer->afc[afi][safi]) + ret = BGP_ERR_AF_UNCONFIGURED; + else if (stype == BGP_CLEAR_SOFT_NONE) ret = peer_clear(peer, &nnode); else ret = peer_clear_soft(peer, afi, safi, stype); if (ret < 0) bgp_clear_vty_error(vty, peer, afi, safi, ret); + else + found = true; } - if (!find) + + if (!found) vty_out(vty, - "%%BGP: No peer is configured with AS %s\n", - arg); + "%%BGP: No %s peer is configured with AS %s\n", + afi_safi_print(afi, safi), arg); + return CMD_SUCCESS; } @@ -6281,6 +6311,10 @@ DEFPY (af_label_vpn_export, if (argv_find(argv, argc, "no", &idx)) yes = 0; + /* If "no ...", squash trailing parameter */ + if (!yes) + label_auto = NULL; + if (yes) { if (!label_auto) label = label_val; /* parser should force unsigned */ @@ -10625,7 +10659,7 @@ static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp, if (use_json) json_object_boolean_true_add(json, "bgpNoSuchNeighbor"); else - vty_out(vty, "%% No such neighbor\n"); + vty_out(vty, "%% No such neighbor in this view/vrf\n"); } if (use_json) { diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 4a909d8cdc..390eb44eb8 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1193,7 +1193,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, return; if (bgp_debug_zebra(p)) - prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); + prefix2str(p, buf_prefix, sizeof(buf_prefix)); if (safi == SAFI_FLOWSPEC) return bgp_pbr_update_entry(bgp, &rn->p, diff --git a/configure.ac b/configure.ac index 8b49295444..7c7de19e1f 100755 --- a/configure.ac +++ b/configure.ac @@ -192,6 +192,7 @@ AC_ARG_ENABLE([address-sanitizer], AS_HELP_STRING([--enable-address-sanitizer], [AC_DEFINE(HAVE_ADDRESS_SANITIZER, 1, [enable AddressSanitizer]) CFLAGS="$CFLAGS -fsanitize=address" CXXFLAGS="$CXXFLAGS -fsanitize=address" + LIBS="-ldl $LIBS" AC_TRY_COMPILE([],[const int i=0;],[AC_MSG_NOTICE([Address Sanitizer Enabled])], [AC_MSG_ERROR([Address Sanitizer not available])]) ]) @@ -202,6 +203,7 @@ AC_ARG_ENABLE([thread-sanitizer], AS_HELP_STRING([--enable-thread-sanitizer], \ [AC_DEFINE(HAVE_THREAD_SANITIZER, 1, [enable ThreadSanitizer]) CFLAGS="$CFLAGS -fsanitize=thread" CXXFLAGS="$CXXFLAGS -fsanitize=thread" + LIBS="-ldl $LIBS" AC_TRY_COMPILE([],[const int i=0;],[AC_MSG_NOTICE([Thread Sanitizer Enabled])], [AC_MSG_ERROR([Thread Sanitizer not available])]) ]) @@ -212,6 +214,7 @@ AC_ARG_ENABLE([memory-sanitizer], AS_HELP_STRING([--enable-memory-sanitizer], \ [AC_DEFINE(HAVE_THREAD_SANITIZER, 1, [enable MemorySanitizer]) CFLAGS="$CFLAGS -fsanitize=memory -fPIE -pie" CXXFLAGS="$CXXFLAGS -fsanitize=memory -fPIE -pie" + LIBS="-ldl $LIBS" AC_TRY_COMPILE([],[const int i=0;],[AC_MSG_NOTICE([Memory Sanitizer Enabled])], [AC_MSG_ERROR([Memory Sanitizer not available])]) ]) diff --git a/doc/Makefile.am b/doc/Makefile.am index 8fa057424a..a0ff314786 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -140,7 +140,7 @@ EXTRA_DIST = frr-sphinx.mk \ manpages/index.rst \ manpages/isisd.rst \ manpages/ldpd.rst \ - manpages/Makefile \ + manpages/Makefile.am \ manpages/mtracebis.rst \ manpages/nhrpd.rst \ manpages/ospf6d.rst \ @@ -182,7 +182,7 @@ EXTRA_DIST = frr-sphinx.mk \ developer/index.rst \ developer/ldpd-basic-test-setup.md \ developer/library.rst \ - developer/Makefile \ + developer/Makefile.in \ developer/memtypes.rst \ developer/modules.rst \ developer/next-hop-tracking.rst \ @@ -195,16 +195,18 @@ EXTRA_DIST = frr-sphinx.mk \ user/babeld.rst \ user/basic.rst \ user/bgp.rst \ + user/bugs.rst \ user/conf.py \ user/eigrpd.rst \ user/filter.rst \ + user/getting-started.rst \ user/glossary.rst \ user/index.rst \ user/installation.rst \ user/ipv6.rst \ user/isisd.rst \ user/kernel.rst \ - user/Makefile \ + user/Makefile.am \ user/nhrpd.rst \ user/ospf6d.rst \ user/ospfd.rst \ @@ -217,6 +219,7 @@ EXTRA_DIST = frr-sphinx.mk \ user/routemap.rst \ user/routeserver.rst \ user/rpki.rst \ + user/setup.rst \ user/sharp.rst \ user/snmp.rst \ user/snmptrap.rst \ diff --git a/doc/developer/.gitignore b/doc/developer/.gitignore index 2e7d8573f1..81c60dc0a3 100644 --- a/doc/developer/.gitignore +++ b/doc/developer/.gitignore @@ -1,3 +1,2 @@ /_templates /_build -!/Makefile.in diff --git a/doc/developer/Makefile.in b/doc/developer/Makefile.am index 76758f9242..76758f9242 100644 --- a/doc/developer/Makefile.in +++ b/doc/developer/Makefile.am diff --git a/doc/developer/building-frr-on-ubuntu1204.rst b/doc/developer/building-frr-on-ubuntu1204.rst index bba49c1ce7..459d411ebc 100644 --- a/doc/developer/building-frr-on-ubuntu1204.rst +++ b/doc/developer/building-frr-on-ubuntu1204.rst @@ -13,9 +13,10 @@ Add packages: :: - apt-get install git autoconf automake libtool make gawk libreadline-dev \ - texinfo libpam0g-dev dejagnu libjson0-dev pkg-config libpam0g-dev \ - libjson0-dev flex python-pip libc-ares-dev python3-dev python3-sphinx + apt-get install \ + git autoconf automake libtool make gawk libreadline-dev texinfo \ + dejagnu pkg-config libpam0g-dev libjson0-dev flex python-pip \ + libc-ares-dev python3-dev python3-sphinx install-info Install newer bison from 14.04 package source (Ubuntu 12.04 package source is too old) @@ -74,7 +75,7 @@ Add frr groups and user :: - sudo groupadd -g 92 frr + sudo groupadd -r -g 92 frr sudo groupadd -r -g 85 frrvty sudo adduser --system --ingroup frr --home /var/run/frr/ \ --gecos "FRR suite" --shell /sbin/nologin frr diff --git a/doc/developer/building-frr-on-ubuntu1404.rst b/doc/developer/building-frr-on-ubuntu1404.rst index c86f1124a7..681cc30a3b 100644 --- a/doc/developer/building-frr-on-ubuntu1404.rst +++ b/doc/developer/building-frr-on-ubuntu1404.rst @@ -13,9 +13,10 @@ Add packages: :: - apt-get install git autoconf automake libtool make gawk libreadline-dev \ - texinfo dejagnu pkg-config libpam0g-dev libjson-c-dev bison flex \ - python-pytest libc-ares-dev python3-dev python3-sphinx + apt-get install \ + git autoconf automake libtool make gawk libreadline-dev texinfo dejagnu \ + pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + libc-ares-dev python3-dev python3-sphinx install-info Get FRR, compile it and install it (from Git) --------------------------------------------- @@ -28,7 +29,7 @@ Add frr groups and user :: - sudo groupadd -g 92 frr + sudo groupadd -r -g 92 frr sudo groupadd -r -g 85 frrvty sudo adduser --system --ingroup frr --home /var/run/frr/ \ --gecos "FRR suite" --shell /sbin/nologin frr diff --git a/doc/developer/building-frr-on-ubuntu1604.rst b/doc/developer/building-frr-on-ubuntu1604.rst index 1b371893e2..69c4e44d98 100644 --- a/doc/developer/building-frr-on-ubuntu1604.rst +++ b/doc/developer/building-frr-on-ubuntu1604.rst @@ -13,10 +13,11 @@ Add packages: :: - apt-get install git autoconf automake libtool make gawk libreadline-dev \ - texinfo dejagnu pkg-config libpam0g-dev libjson-c-dev bison flex \ - python-pytest libc-ares-dev python3-dev libsystemd-dev python-ipaddr \ - python3-sphinx + apt-get install \ + git autoconf automake libtool make gawk libreadline-dev texinfo dejagnu \ + pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + libc-ares-dev python3-dev libsystemd-dev python-ipaddr python3-sphinx \ + install-info Get FRR, compile it and install it (from Git) --------------------------------------------- @@ -29,7 +30,7 @@ Add frr groups and user :: - sudo groupadd -g 92 frr + sudo groupadd -r -g 92 frr sudo groupadd -r -g 85 frrvty sudo adduser --system --ingroup frr --home /var/run/frr/ \ --gecos "FRR suite" --shell /sbin/nologin frr diff --git a/doc/developer/building-frr-on-ubuntu1804.rst b/doc/developer/building-frr-on-ubuntu1804.rst index d9fc37c172..50e90fc7ea 100644 --- a/doc/developer/building-frr-on-ubuntu1804.rst +++ b/doc/developer/building-frr-on-ubuntu1804.rst @@ -1,33 +1,19 @@ -Ubuntu 18.04LTS -=============================================== +Ubuntu 18.04 LTS +================ Install dependencies -------------------------- +-------------------- + Required packages ^^^^^^^^^^^^^^^^^ :: - sudo apt-get install \ - git \ - autoconf \ - automake \ - libtool \ - make \ - gawk \ - libreadline-dev \ - texinfo \ - pkg-config \ - libpam0g-dev \ - libjson-c-dev \ - bison \ - flex \ - python-pytest \ - libc-ares-dev \ - python3-dev \ - libsystemd-dev \ - python-ipaddr \ - python3-sphinx + sudo apt-get install \ + git autoconf automake libtool make gawk libreadline-dev texinfo \ + pkg-config libpam0g-dev libjson-c-dev bison flex python-pytest \ + libc-ares-dev python3-dev libsystemd-dev python-ipaddr python3-sphinx \ + install-info Optional packages ^^^^^^^^^^^^^^^^^ @@ -39,18 +25,18 @@ Protobuf :: - sudo apt-get install \ - protobuf-c-compiler \ - libprotobuf-c-dev + sudo apt-get install \ + protobuf-c-compiler \ + libprotobuf-c-dev ZeroMQ ~~~~~~ :: - sudo apt-get install \ - libzmq5 \ - libzmq3-dev + sudo apt-get install \ + libzmq5 \ + libzmq3-dev Get FRR, compile it and install it (from Git) --------------------------------------------- @@ -63,18 +49,18 @@ Add frr groups and user :: - sudo groupadd -g 92 frr - sudo groupadd -r -g 85 frrvty - sudo adduser --system --ingroup frr --home /var/run/frr/ \ - --gecos "FRR suite" --shell /sbin/nologin frr - sudo usermod -a -G frrvty frr + sudo groupadd -r -g 92 frr + sudo groupadd -r -g 85 frrvty + sudo adduser --system --ingroup frr --home /var/run/frr/ \ + --gecos "FRR suite" --shell /sbin/nologin frr + sudo usermod -a -G frrvty frr Download source -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^ :: - git clone https://github.com/frrouting/frr.git frr + git clone https://github.com/frrouting/frr.git frr Configure ^^^^^^^^^ @@ -82,31 +68,31 @@ Options below are provided as an example. .. seealso:: *Installation* section of user guide -:: - - cd frr - ./bootstrap.sh - ./configure \ - --prefix=/usr \ - --enable-exampledir=/usr/share/doc/frr/examples/ \ - --localstatedir=/var/run/frr \ - --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ - --enable-pimd \ - --enable-watchfrr \ - --enable-ospfclient=yes \ - --enable-ospfapi=yes \ - --enable-multipath=64 \ - --enable-user=frr \ - --enable-group=frr \ - --enable-vty-group=frrvty \ - --enable-configfile-mask=0640 \ - --enable-logfile-mask=0640 \ - --enable-rtadv \ - --enable-fpm \ - --enable-systemd=yes \ - --with-pkg-git-version \ - --with-pkg-extra-version=-MyOwnFRRVersion +.. code-block:: shell + + cd frr + ./bootstrap.sh + ./configure \ + --prefix=/usr \ + --enable-exampledir=/usr/share/doc/frr/examples/ \ + --localstatedir=/var/run/frr \ + --sbindir=/usr/lib/frr \ + --sysconfdir=/etc/frr \ + --enable-pimd \ + --enable-watchfrr \ + --enable-ospfclient=yes \ + --enable-ospfapi=yes \ + --enable-multipath=64 \ + --enable-user=frr \ + --enable-group=frr \ + --enable-vty-group=frrvty \ + --enable-configfile-mask=0640 \ + --enable-logfile-mask=0640 \ + --enable-rtadv \ + --enable-fpm \ + --enable-systemd=yes \ + --with-pkg-git-version \ + --with-pkg-extra-version=-MyOwnFRRVersion If optional packages were installed, the associated feature may now be enabled. @@ -124,9 +110,9 @@ Compile :: - make - make check - sudo make install + make + make check + sudo make install Create empty FRR configuration files ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,122 +122,128 @@ configuration files _before_ starting FRR. This assures that the permissions are correct. If the files are not already present, FRR will create them. It's also important to consider _which_ files to create. FRR supports writing -configuration to a monolithic file, ``/etc/frr/frr.conf``, which is not -recommended +configuration to a monolithic file, :file:`/etc/frr/frr.conf`. + .. seealso:: *VTYSH* section of user guide -The presence of ``/etc/frr/frr.conf`` on startup implicitly configures FRR to -ignore daemon-specific configuration files. +The presence of :file:`/etc/frr/frr.conf` on startup implicitly configures FRR +to ignore daemon-specific configuration files. Daemon-specific configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: - sudo install -m 755 -o frr -g frr -d /var/log/frr - sudo install -m 775 -o frr -g frrvty -d /etc/frr - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/zebra.conf - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/bgpd.conf - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ospfd.conf - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ospf6d.conf - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/isisd.conf - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ripd.conf - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ripngd.conf - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/pimd.conf - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ldpd.conf - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/nhrpd.conf + sudo install -m 755 -o frr -g frr -d /var/log/frr + sudo install -m 775 -o frr -g frrvty -d /etc/frr + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/zebra.conf + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/bgpd.conf + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ospfd.conf + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ospf6d.conf + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/isisd.conf + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ripd.conf + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ripngd.conf + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/pimd.conf + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/ldpd.conf + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/nhrpd.conf Monolithic configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~ :: - sudo install -m 755 -o frr -g frr -d /var/log/frr - sudo install -m 775 -o frr -g frrvty -d /etc/frr - sudo install -m 640 -o frr -g frr /dev/null /etc/frr/frr.conf + sudo install -m 755 -o frr -g frr -d /var/log/frr + sudo install -m 775 -o frr -g frrvty -d /etc/frr + sudo install -m 640 -o frr -g frr /dev/null /etc/frr/frr.conf Enable IPv4 & IPv6 forwarding ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Edit ``/etc/sysctl.conf`` and uncomment the following values (ignore the -other settings) +Edit :file:`/etc/sysctl.conf` and uncomment the following values (ignore the +other settings): :: - # Uncomment the next line to enable packet forwarding for IPv4 - net.ipv4.ip_forward=1 + # Uncomment the next line to enable packet forwarding for IPv4 + net.ipv4.ip_forward=1 - # Uncomment the next line to enable packet forwarding for IPv6 - # Enabling this option disables Stateless Address Autoconfiguration - # based on Router Advertisements for this host - net.ipv6.conf.all.forwarding=1 + # Uncomment the next line to enable packet forwarding for IPv6 + # Enabling this option disables Stateless Address Autoconfiguration + # based on Router Advertisements for this host + net.ipv6.conf.all.forwarding=1 Add MPLS kernel modules ^^^^^^^^^^^^^^^^^^^^^^^ -Ubuntu 18.04 ships with kernel 4.15. MPLS modules are present by default. -To enable, add the following lines to ``/etc/modules-load.d/modules.conf``: +Ubuntu 18.04 ships with kernel 4.15. MPLS modules are present by default. To +enable, add the following lines to :file:`/etc/modules-load.d/modules.conf`: :: - # Load MPLS Kernel Modules - mpls_router - mpls_iptunnel + # Load MPLS Kernel Modules + mpls_router + mpls_iptunnel -**Reboot** or use ``sysctl -p`` to apply the same config to the running -system +Reboot or use ``sysctl -p`` to apply the same config to the running system. Enable MPLS Forwarding -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^ -Edit ``/etc/sysctl.conf`` and the following lines. Make sure to add a -line equal to ``net.mpls.conf.eth0.input`` or each interface used with -MPLS +Edit :file:`/etc/sysctl.conf` and the following lines. Make sure to add a line +equal to :file:`net.mpls.conf.eth0.input` for each interface used with MPLS. :: - # Enable MPLS Label processing on all interfaces - net.mpls.conf.eth0.input=1 - net.mpls.conf.eth1.input=1 - net.mpls.conf.eth2.input=1 - net.mpls.platform_labels=100000 + # Enable MPLS Label processing on all interfaces + net.mpls.conf.eth0.input=1 + net.mpls.conf.eth1.input=1 + net.mpls.conf.eth2.input=1 + net.mpls.platform_labels=100000 -Install the systemd service (if rebooted from last step, change directory back to frr directory) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Install the systemd service +^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: - sudo install -m 644 tools/frr.service /etc/systemd/system/frr.service - sudo install -m 644 tools/etc/default/frr /etc/default/frr - sudo install -m 644 tools/etc/frr/daemons /etc/frr/daemons - sudo install -m 644 tools/etc/frr/daemons.conf /etc/frr/daemons.conf - sudo install -m 644 tools/etc/frr/frr.conf /etc/frr/frr.conf - sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf + sudo install -m 644 tools/frr.service /etc/systemd/system/frr.service + sudo install -m 644 tools/etc/default/frr /etc/default/frr + sudo install -m 644 tools/etc/frr/daemons /etc/frr/daemons + sudo install -m 644 tools/etc/frr/daemons.conf /etc/frr/daemons.conf + sudo install -m 644 tools/etc/frr/frr.conf /etc/frr/frr.conf + sudo install -m 644 -o frr -g frr tools/etc/frr/vtysh.conf /etc/frr/vtysh.conf Enable daemons ^^^^^^^^^^^^^^ -| Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for - those daemons you want to start by systemd. -| For example. +Edit ``/etc/frr/daemons`` and change the value from "no" to "yes" for those +daemons you want to start by systemd. For example: :: - zebra=yes - bgpd=yes - ospfd=yes - ospf6d=yes - ripd=yes - ripngd=yes - isisd=yes + zebra=yes + bgpd=yes + ospfd=yes + ospf6d=yes + ripd=yes + ripngd=yes + isisd=yes Enable the systemd service ^^^^^^^^^^^^^^^^^^^^^^^^^^ -- systemctl enable frr +Enabling the systemd service causes FRR to be started upon boot. To enable it, +use the following command: + +.. code-block:: shell + + systemctl enable frr Start the systemd service ^^^^^^^^^^^^^^^^^^^^^^^^^ -- systemctl start frr -- use ``systemctl status frr`` to check its status. +.. code-block:: shell + + systemctl start frr + +After starting the service, you can use ``systemctl status frr`` to check its +status. diff --git a/doc/developer/ospf-api.rst b/doc/developer/ospf-api.rst index 5f0b830c8c..90fc52efd0 100644 --- a/doc/developer/ospf-api.rst +++ b/doc/developer/ospf-api.rst @@ -77,7 +77,6 @@ the ospfapi server and ospfclient). :: - % update-autotools % sh ./configure --enable-opaque-lsa % make diff --git a/doc/manpages/.gitignore b/doc/manpages/.gitignore index 2e7d8573f1..81c60dc0a3 100644 --- a/doc/manpages/.gitignore +++ b/doc/manpages/.gitignore @@ -1,3 +1,2 @@ /_templates /_build -!/Makefile.in diff --git a/doc/manpages/Makefile.in b/doc/manpages/Makefile.am index f28746cee6..009c723823 100644 --- a/doc/manpages/Makefile.in +++ b/doc/manpages/Makefile.am @@ -34,11 +34,6 @@ include @srcdir@/../frr-sphinx.mk # tags # ctags -# ignore these targets -EMPTY_AUTOMAKE_TARGETS = dvi pdf ps tags ctags distdir installdirs check installcheck install-dvi install-ps install-html install-pdf install-info install-exec -.PHONY: $(EMPTY_AUTOMAKE_TARGETS) -$(EMPTY_AUTOMAKE_TARGETS): - # These targets are automatically generated by Sphinx but conflict with # implicitly defined Automake rules, so we manually override them to nothing. # The other option is deleting the Sphinx-generated rules, which suppresses the @@ -51,5 +46,3 @@ all: man install-data: man install: install-data - -mostlyclean distclean maintainer-clean: clean diff --git a/doc/manpages/common-options.rst b/doc/manpages/common-options.rst index e433c720af..e9241b7438 100644 --- a/doc/manpages/common-options.rst +++ b/doc/manpages/common-options.rst @@ -157,10 +157,3 @@ frr supports optional dynamically loadable modules, although these can only be l The list of loaded modules can be inspected at runtime with the show modules VTY command. -ROUTES ------- - -.. option:: -r, --retain - - When the program terminates, retain routes added by the daemon. - diff --git a/doc/manpages/zebra.rst b/doc/manpages/zebra.rst index 4ddf989951..a8a9301588 100644 --- a/doc/manpages/zebra.rst +++ b/doc/manpages/zebra.rst @@ -42,6 +42,14 @@ OPTIONS available for the |DAEMON| command: Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite support from the Linux kernel. This option permits discovering Linux named network namespaces and mapping it to FRR VRF contexts. +ROUTES +------ + +.. option:: -r, --retain + + When the program terminates, do not flush routes installed by zebra from the kernel. + + FILES ===== diff --git a/doc/user/.gitignore b/doc/user/.gitignore index 2e7d8573f1..81c60dc0a3 100644 --- a/doc/user/.gitignore +++ b/doc/user/.gitignore @@ -1,3 +1,2 @@ /_templates /_build -!/Makefile.in diff --git a/doc/user/Makefile.in b/doc/user/Makefile.am index 77c6abf917..64af2ff145 100644 --- a/doc/user/Makefile.in +++ b/doc/user/Makefile.am @@ -34,11 +34,6 @@ include @srcdir@/../frr-sphinx.mk # tags # ctags -# ignore these targets -EMPTY_AUTOMAKE_TARGETS = dvi ps tags ctags distdir install-exec install-dvi install-ps installdirs check installcheck install-html install-pdf install-data install -.PHONY: $(EMPTY_AUTOMAKE_TARGETS) -$(EMPTY_AUTOMAKE_TARGETS): - # When building 'all', the logic is that we want to make docs that are easily # readable by the person that just built them. Technically the reST source is # readable in its own right, but we'll also build info and html because those @@ -58,5 +53,3 @@ install-info: info install-data: install-info install: install-data - -mostlyclean distclean maintainer-clean: clean diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 861e3d0d6d..9332fdf3b7 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -30,10 +30,6 @@ be specified (:ref:`common-invocation-options`). Set the bgp protocol's port number. When port number is 0, that means do not listen bgp port. -.. option:: -r, --retain - - When program terminates, retain BGP routes added by zebra. - .. option:: -l, --listenon Specify a specific IP address for bgpd to listen on, rather than its diff --git a/doc/user/eigrpd.rst b/doc/user/eigrpd.rst index 6034bf8948..330267a8ee 100644 --- a/doc/user/eigrpd.rst +++ b/doc/user/eigrpd.rst @@ -60,10 +60,6 @@ Certain signals have special meanings to *eigrpd*. .. program:: eigrpd -.. option:: -r, --retain - - When the program terminates, retain routes added by *eigrpd*. - .. _eigrp-configuration: EIGRP Configuration diff --git a/doc/user/getting-started.rst b/doc/user/getting-started.rst new file mode 100644 index 0000000000..4ef293c711 --- /dev/null +++ b/doc/user/getting-started.rst @@ -0,0 +1,9 @@ +*************** +Getting Started +*************** + +.. toctree:: + :maxdepth: 2 + + installation + setup diff --git a/doc/user/index.rst b/doc/user/index.rst index f20ff8ec2b..7118022df7 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -5,7 +5,7 @@ FRRouting User Guide :maxdepth: 2 overview - installation + getting-started basic vtysh filter diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 700a727d8f..7a430fdf98 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -1,8 +1,7 @@ .. _installation: -************ Installation -************ +============ .. index:: How to install FRR .. index:: Installation @@ -10,46 +9,73 @@ Installation .. index:: Building the system .. index:: Making FRR -Several distributions provide packages for FRR. Check your distribution's -repositories to find out if a suitable version is available. +This section covers the basics of building, installing and setting up FRR. -FRR depends on various libraries depending on your operating system. +From Packages +------------- -After installing these dependencies, change to the frr source directory and -issue the following commands: +The project publishes packages for Red Hat, Centos, Debian and Ubuntu on the +`GitHub releases <https://github.com/FRRouting/frr/releases>`_. page. External +contributors offer packages for many other platforms including \*BSD, Alpine, +Gentoo, Docker, and others. There is currently no documentation on how to use +those but we hope to add it soon. -:: +From Snapcraft +-------------- - $ ./bootstrap.sh - $ ./configure - $ make - $ make install +In addition to traditional packages the project also builds and publishes +universal Snap images, available at https://snapcraft.io/frr. +From Source +----------- -.. _configure-the-software: +Building FRR from source is the best way to ensure you have the latest features +and bug fixes. Details for each supported platform, including dependency +package listings, permissions, and other gotchas, are in the developer's +documentation. This section provides a brief overview on the process. -Configure the Software -====================== +Getting the Source +^^^^^^^^^^^^^^^^^^ +FRR's source is available on the project +`GitHub page <https://github.com/FRRouting/frr>`_. -.. _the-configure-script: +.. code-block:: shell -The Configure Script --------------------- + git clone https://github.com/FRRouting/frr.git -.. index:: Configuration options +When building from Git there are several branches to choose from. The +``master`` branch is the primary development branch. It should be considered +unstable. Each release has its own branch named ``stable/X.X``, where ``X.X`` +is the release version. -.. index:: Options for configuring +In addition, release tarballs are published on the GitHub releases page +`here <https://github.com/FRRouting/frr/releases>`_. -.. index:: Build options +Configuration +^^^^^^^^^^^^^ +.. index:: Configuration options +.. index:: Options for configuring +.. index:: Build options .. index:: Distribution configuration - .. index:: Options to `./configure` -FRR has an excellent configure script which automatically detects most -host configurations. There are several additional configure options to -customize the build to include or exclude specific features and dependencies. +FRR has an excellent configure script which automatically detects most host +configurations. There are several additional configure options to customize the +build to include or exclude specific features and dependencies. + +First, update the build system. Change into your FRR source directory and issue: + +.. code-block:: shell + + ./bootstrap.sh + +This will install any missing build scripts and update the Autotools +configuration. Once this is done you can move on to choosing your configuration +options from the list below. + +.. _frr-configuration: .. program:: configure @@ -202,10 +228,9 @@ options to the configuration script. .. _least-privilege-support: Least-Privilege Support ------------------------ +""""""""""""""""""""""" .. index:: FRR Least-Privileges - .. index:: FRR Privileges Additionally, you may configure zebra to drop its elevated privileges @@ -240,112 +265,54 @@ only Linux), FRR will retain only minimal capabilities required and will only raise these capabilities for brief periods. On systems without libcap, FRR will run as the user specified and only raise its UID to 0 for brief periods. -.. _linux-notes: - Linux Notes ------------ - -.. index:: Configuring FRR +""""""""""" .. index:: Building on Linux boxes - .. index:: Linux configurations -There are several options available only to GNU/Linux systems [#]_. -If you use GNU/Linux, make sure that the current kernel configuration is what -you want. FRR will run with any kernel configuration but some recommendations -do exist. - - -- :makevar:`CONFIG_NETLINK` - Kernel/User Netlink socket. This is a brand new feature which enables an - advanced interface between the Linux kernel and zebra (:ref:`kernel-interface`). -- :makevar:`CONFIG_RTNETLINK` - Routing messages. - This makes it possible to receive Netlink routing messages. If you - specify this option, *zebra* can detect routing information - updates directly from the kernel (:ref:`kernel-interface`). -- :makevar:`CONFIG_IP_MULTICAST` - IP: multicasting. - This option should be specified when you use *ripd* (:ref:`rip`) or - *ospfd* (:ref:`ospfv2`) because these protocols use multicast. - -IPv6 support has been added in GNU/Linux kernel version 2.2. If you -try to use the FRR IPv6 feature on a GNU/Linux kernel, please -make sure the following libraries have been installed. Please note that -these libraries will not be needed when you uses GNU C library 2.1 -or upper. - -- inet6-apps - - The `inet6-apps` package includes basic IPv6 related libraries such - as `inet_ntop` and `inet_pton`. Some basic IPv6 programs such - as *ping*, *ftp*, and *inetd* are also - included. The `inet-apps` can be found at - `ftp://ftp.inner.net/pub/ipv6/ <ftp://ftp.inner.net/pub/ipv6/>`_. - -- net-tools - - The `net-tools` package provides an IPv6 enabled interface and routing - utility. It contains *ifconfig*, *route*, *netstat*, and other tools. - `net-tools` may be found at http://www.tazenda.demon.co.uk/phil/net-tools/. - -.. _build-the-software: - -Build the Software -================== - -After configuring the software, you will need to compile it for your system. -Simply issue the command *make* in the root of the source directory and the -software will be compiled. Cliff Notes versions of different compilation -examples can be found in the Developer's Manual Appendix. If you have *any* -problems at this stage, please send a bug report :ref:`bug-reports`. - -:: - - $ ./bootstrap.sh - $ ./configure <appropriate to your system> - $ make - +There are several options available only to GNU/Linux systems. If you use +GNU/Linux, make sure that the current kernel configuration is what you want. +FRR will run with any kernel configuration but some recommendations do exist. -Install the Software -==================== +:makevar:`CONFIG_NETLINK` + Kernel/User Netlink socket. This is a enables an advanced interface between + the Linux kernel and *zebra* (:ref:`kernel-interface`). -Installing the software to your system consists of copying the compiled -programs and supporting files to a standard location. After the -installation process has completed, these files have been copied -from your work directory to :file:`/usr/local/bin`, and :file:`/usr/local/etc`. +:makevar:`CONFIG_RTNETLINK` + This makes it possible to receive Netlink routing messages. If you specify + this option, *zebra* can detect routing information updates directly from + the kernel (:ref:`kernel-interface`). -To install the FRR suite, issue the following command at your shell -prompt::: +:makevar:`CONFIG_IP_MULTICAST` + This option enables IP multicast and should be specified when you use *ripd* + (:ref:`rip`) or *ospfd* (:ref:`ospfv2`) because these protocols use + multicast. - $ make install +Building +^^^^^^^^ -FRR daemons have their own terminal interface or VTY. After -installation, you have to setup each beast's port number to connect to -them. Please add the following entries to :file:`/etc/services`. +Once you have chosen your configure options, run the configure script and pass +the options you chose: -:: +.. code-block:: shell - zebrasrv 2600/tcp # zebra service - zebra 2601/tcp # zebra vty - ripd 2602/tcp # RIPd vty - ripngd 2603/tcp # RIPngd vty - ospfd 2604/tcp # OSPFd vty - bgpd 2605/tcp # BGPd vty - ospf6d 2606/tcp # OSPF6d vty - ospfapi 2607/tcp # ospfapi - isisd 2608/tcp # ISISd vty - nhrpd 2610/tcp # nhrpd vty - pimd 2611/tcp # PIMd vty + ./configure \ + --prefix=/usr \ + --enable-exampledir=/usr/share/doc/frr/examples/ \ + --localstatedir=/var/run/frr \ + --sbindir=/usr/lib/frr \ + --sysconfdir=/etc/frr \ + --enable-pimd \ + --enable-watchfrr \ + ... +After configuring the software, you are ready to build and install it for your +system. -If you use a FreeBSD newer than 2.2.8, the above entries are already -added to :file:`/etc/services` so there is no need to add it. If you -specify a port number when starting the daemon, these entries may not be -needed. +.. code-block:: shell -You may need to make changes to the config files in -|INSTALL_PREFIX_ETC|. :ref:`config-commands`. + make && sudo make install -.. [#] GNU/Linux has very flexible kernel configuration features. +If everything finishes successfully, FRR should be installed. You should now +skip to the section on :ref:`basic-setup`. diff --git a/doc/user/overview.rst b/doc/user/overview.rst index 38d55d68ad..eb24970bee 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -20,6 +20,8 @@ can use FRR library as your program's client user interface. FRR is distributed under the GNU General Public License. +FRR is a fork of `Quagga <http://www.quagga.net/>`_. + .. _about-frr: About FRR @@ -235,7 +237,8 @@ How to get FRR The official FRR website is located at |PACKAGE_URL| and contains further information, as well as links to additional resources. -FRR is a fork of `Quagga <http://www.quagga.net/>`_. +Several distributions provide packages for FRR. Check your distribution's +repositories to find out if a suitable version is available. Mailing Lists ============= diff --git a/doc/user/ripd.rst b/doc/user/ripd.rst index 973e61949e..372c30f587 100644 --- a/doc/user/ripd.rst +++ b/doc/user/ripd.rst @@ -60,9 +60,6 @@ Certain signals have special meanings to *ripd*. *ripd* invocation options. Common options that can be specified (:ref:`common-invocation-options`). -.. option:: -r, --retain - - When the program terminates, retain routes added by *ripd*. .. _rip-netmask: diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index bddf2ba26d..8080e001f1 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -180,6 +180,18 @@ Route Map Match Command interface name specified if the neighbor was specified in this manner. +.. index:: match source-protocol PROTOCOL_NAME +.. clicmd:: match source-protocol PROTOCOL_NAME + + This is a ZEBRA specific match command. Matches the + originating protocol specified. + +.. index:: match source-instance NUMBER +.. clicmd:: match source-instance NUMBER + + This is a ZEBRA specific match command. The number is a range from (0-255). + Matches the originating protocols instance specified. + .. _route-map-set-command: Route Map Set Command diff --git a/doc/user/setup.rst b/doc/user/setup.rst new file mode 100644 index 0000000000..1bbbe36931 --- /dev/null +++ b/doc/user/setup.rst @@ -0,0 +1,155 @@ +.. _basic-setup: + +Basic Setup +============ + +After installing FRR, some basic configuration must be completed before it is +ready to use. + +Daemons File +------------ +After a fresh install, starting FRR will do nothing. This is because daemons +must be explicitly enabled by editing a file in your configuration directory. +This file is usually located at :file:`/etc/frr/daemons` and determines which +daemons are activated when issuing a service start / stop command via init or +systemd. The file initially looks like this: + +:: + + zebra=no + bgpd=no + ospfd=no + ospf6d=no + ripd=no + ripngd=no + isisd=no + pimd=no + ldpd=no + nhrpd=no + eigrpd=no + babeld=no + sharpd=no + pbrd=no + +To enable a particular daemon, simply change the corresponding 'no' to 'yes'. +Subsequent service restarts should start the daemon. + +Daemons Configuration File +-------------------------- +There is another file that controls the default options passed to daemons when +starting FRR as a service. This file is located in your configuration +directory, usually at :file:`/etc/frr/daemons.conf`. + +This file has several parts. Here is an example: + +:: + + # + # If this option is set the /etc/init.d/frr script automatically loads + # the config via "vtysh -b" when the servers are started. + # Check /etc/pam.d/frr if you intend to use "vtysh"! + # + vtysh_enable=yes + zebra_options=" -s 90000000 --daemon -A 127.0.0.1" + bgpd_options=" --daemon -A 127.0.0.1" + ospfd_options=" --daemon -A 127.0.0.1" + ospf6d_options=" --daemon -A ::1" + ripd_options=" --daemon -A 127.0.0.1" + ripngd_options=" --daemon -A ::1" + isisd_options=" --daemon -A 127.0.0.1" + pimd_options=" --daemon -A 127.0.0.1" + ldpd_options=" --daemon -A 127.0.0.1" + nhrpd_options=" --daemon -A 127.0.0.1" + eigrpd_options=" --daemon -A 127.0.0.1" + babeld_options=" --daemon -A 127.0.0.1" + sharpd_options=" --daemon -A 127.0.0.1" + pbrd_options=" --daemon -A 127.0.0.1" + + # The list of daemons to watch is automatically generated by the init script. + watchfrr_enable=yes + watchfrr_options=(-d -r /usr/sbin/servicebBfrrbBrestartbB%s -s /usr/sbin/servicebBfrrbBstartbB%s -k /usr/sbin/servicebBfrrbBstopbB%s -b bB) + + # If valgrind_enable is 'yes' the frr daemons will be started via valgrind. + # The use case for doing so is tracking down memory leaks, etc in frr. + valgrind_enable=no + valgrind=/usr/bin/valgrind + +Breaking this file down: + +:: + + vtysh_enable=yes + +As the comment says, this causes :ref:`VTYSH <vty-shell>` to apply +configuration when starting the daemons. This is useful for a variety of +reasons touched on in the VTYSH documentation and should generally be enabled. + +:: + + zebra_options=" -s 90000000 --daemon -A 127.0.0.1" + bgpd_options=" --daemon -A 127.0.0.1" + ... + +The next set of lines controls what options are passed to daemons when started +from the service script. Usually daemons will have ``--daemon`` and ``-A +<address>`` specified in order to daemonize and listen for VTY commands on a +particular address. + +:: + + # The list of daemons to watch is automatically generated by the init script. + watchfrr_enable=yes + watchfrr_options=(-d -r /usr/sbin/servicebBfrrbBrestartbB%s -s /usr/sbin/servicebBfrrbBstartbB%s -k /usr/sbin/servicebBfrrbBstopbB%s -b bB) + +Options for the ``watchfrr``, the watchdog daemon. + +:: + + valgrind_enable=no + valgrind=/usr/bin/valgrind + +Whether or not to start FRR daemons under Valgrind. This is primarily useful +for gathering information for bug reports and for developers. +``valgrind_enable`` should be ``no`` for production use. + +Services +-------- +FRR daemons have their own terminal interface or VTY. After installation, it's +a good idea to setup each daemon's port number to connect to them. To do this +add the following entries to :file:`/etc/services`. + +:: + + zebrasrv 2600/tcp # zebra service + zebra 2601/tcp # zebra vty + ripd 2602/tcp # RIPd vty + ripngd 2603/tcp # RIPngd vty + ospfd 2604/tcp # OSPFd vty + bgpd 2605/tcp # BGPd vty + ospf6d 2606/tcp # OSPF6d vty + ospfapi 2607/tcp # ospfapi + isisd 2608/tcp # ISISd vty + babeld 2609/tcp # BABELd vty + nhrpd 2610/tcp # nhrpd vty + pimd 2611/tcp # PIMd vty + ldpd 2612/tcp # LDPd vty + eigprd 2613/tcp # EIGRPd vty + + +If you use a FreeBSD newer than 2.2.8, the above entries are already added to +:file:`/etc/services` so there is no need to add it. If you specify a port +number when starting the daemon, these entries may not be needed. + +You may need to make changes to the config files in |INSTALL_PREFIX_ETC|. + +systemd +------- +Although not installed when installing from source, FRR provides a service file +for use with ``systemd``. It is located in :file:`tools/frr.service` in the Git +repository. If ``systemctl status frr.service`` indicates that the FRR service +is not found, copy the service file from the Git repository into your preferred +location. A good place is usually ``/etc/systemd/system/``. + +After issuing a ``systemctl daemon-reload``, you should be able to start the +FRR service via ``systemctl start frr``. If this fails, or no daemons are +started. check the ``journalctl`` logs for an indication of what went wrong. diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 9d927359ba..9e377330ee 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -29,7 +29,8 @@ Besides the common invocation options (:ref:`common-invocation-options`), the .. option:: -r, --retain - When program terminates, retain routes added by zebra. + When program terminates, do not flush routes installed by *zebra* from the + kernel. .. option:: -e X, --ecmp X @@ -47,6 +48,14 @@ Besides the common invocation options (:ref:`common-invocation-options`), the .. seealso:: :ref:`vrf` +.. option:: --v6-rr-semantics + + The linux kernel is receiving the ability to use the same route + replacement semantics for v6 that v4 uses. If you are using a + kernel that supports this functionality then run *Zebra* with this + option and we will use Route Replace Semantics instead of delete + than add. + .. _interface-commands: Interface Commands diff --git a/lib/hash.c b/lib/hash.c index 4894b65aff..ee5401b236 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -36,7 +36,6 @@ DEFINE_MTYPE_STATIC(LIB, HASH_INDEX, "Hash Index") pthread_mutex_t _hashes_mtx = PTHREAD_MUTEX_INITIALIZER; static struct list *_hashes; -/* Allocate a new hash. */ struct hash *hash_create_size(unsigned int size, unsigned int (*hash_key)(void *), int (*hash_cmp)(const void *, const void *), @@ -67,7 +66,6 @@ struct hash *hash_create_size(unsigned int size, return hash; } -/* Allocate a new hash with default hash size. */ struct hash *hash_create(unsigned int (*hash_key)(void *), int (*hash_cmp)(const void *, const void *), const char *name) @@ -75,9 +73,6 @@ struct hash *hash_create(unsigned int (*hash_key)(void *), return hash_create_size(HASH_INITIAL_SIZE, hash_key, hash_cmp, name); } -/* Utility function for hash_get(). When this function is specified - as alloc_func, return arugment as it is. This function is used for - intern already allocated value. */ void *hash_alloc_intern(void *arg) { return arg; @@ -133,9 +128,6 @@ static void hash_expand(struct hash *hash) hash->index = new_index; } -/* Lookup and return hash backet in hash. If there is no - corresponding hash backet and alloc_func is specified, create new - hash backet. */ void *hash_get(struct hash *hash, void *data, void *(*alloc_func)(void *)) { unsigned int key; @@ -189,13 +181,11 @@ void *hash_get(struct hash *hash, void *data, void *(*alloc_func)(void *)) return NULL; } -/* Hash lookup. */ void *hash_lookup(struct hash *hash, void *data) { return hash_get(hash, data, NULL); } -/* Simple Bernstein hash which is simple and fast for common case */ unsigned int string_hash_make(const char *str) { unsigned int hash = 0; @@ -206,9 +196,6 @@ unsigned int string_hash_make(const char *str) return hash; } -/* This function release registered value from specified hash. When - release is successfully finished, return the data pointer in the - hash backet. */ void *hash_release(struct hash *hash, void *data) { void *ret; @@ -248,7 +235,6 @@ void *hash_release(struct hash *hash, void *data) return NULL; } -/* Iterator function for hash. */ void hash_iterate(struct hash *hash, void (*func)(struct hash_backet *, void *), void *arg) { @@ -266,7 +252,6 @@ void hash_iterate(struct hash *hash, void (*func)(struct hash_backet *, void *), } } -/* Iterator function for hash. */ void hash_walk(struct hash *hash, int (*func)(struct hash_backet *, void *), void *arg) { @@ -288,7 +273,6 @@ void hash_walk(struct hash *hash, int (*func)(struct hash_backet *, void *), } } -/* Clean up hash. */ void hash_clean(struct hash *hash, void (*free_func)(void *)) { unsigned int i; @@ -312,8 +296,21 @@ void hash_clean(struct hash *hash, void (*free_func)(void *)) hash->stats.empty = hash->size; } -/* Free hash memory. You may call hash_clean before call this - function. */ +static void hash_to_list_iter(struct hash_backet *hb, void *arg) +{ + struct list *list = arg; + + listnode_add(list, hb->data); +} + +struct list *hash_to_list(struct hash *hash) +{ + struct list *list = list_new(); + + hash_iterate(hash, hash_to_list_iter, list); + return list; +} + void hash_free(struct hash *hash) { pthread_mutex_lock(&_hashes_mtx); diff --git a/lib/hash.h b/lib/hash.h index b6fe27e257..c7e670b723 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -36,9 +36,10 @@ DECLARE_MTYPE(HASH_BACKET) #define HASHWALK_ABORT -1 struct hash_backet { - /* if this backet is the head of the linked listed, len denotes the - * number of - * elements in the list */ + /* + * if this backet is the head of the linked listed, len denotes the + * number of elements in the list + */ int len; /* Linked list. */ @@ -85,29 +86,232 @@ struct hash { #define hashcount(X) ((X)->count) -extern struct hash *hash_create(unsigned int (*)(void *), - int (*)(const void *, const void *), - const char *); -extern struct hash *hash_create_size(unsigned int, unsigned int (*)(void *), - int (*)(const void *, const void *), - const char *); +/* + * Create a hash table. + * + * The created hash table uses chaining and a user-provided comparator function + * to resolve collisions. For best performance use a perfect hash function. + * Worst case lookup time is O(N) when using a constant hash function. Best + * case lookup time is O(1) when using a perfect hash function. + * + * The initial size of the created hash table is HASH_INITIAL_SIZE. + * + * hash_key + * hash function to use; should return a unique unsigned integer when called + * with a data item. Collisions are acceptable. + * + * hash_cmp + * comparison function used for resolving collisions; when called with two + * data items, should return nonzero if the two items are equal and 0 + * otherwise + * + * name + * optional name for the hashtable; this is used when displaying global + * hashtable statistics. If this parameter is NULL the hash's name will be + * set to NULL and the default name will be displayed when showing + * statistics. + * + * Returns: + * a new hash table + */ +extern struct hash *hash_create(unsigned int (*hash_key)(void *), + int (*hash_cmp)(const void *, const void *), + const char *name); + +/* + * Create a hash table. + * + * The created hash table uses chaining and a user-provided comparator function + * to resolve collisions. For best performance use a perfect hash function. + * Worst case lookup time is O(N) when using a constant hash function. Best + * case lookup time is O(1) when using a perfect hash function. + * + * size + * initial number of hash buckets to allocate; must be a power of 2 or the + * program will assert + * + * hash_key + * hash function to use; should return a unique unsigned integer when called + * with a data item. Collisions are acceptable. + * + * hash_cmp + * comparison function used for resolving collisions; when called with two + * data items, should return nonzero if the two items are equal and 0 + * otherwise + * + * name + * optional name for the hashtable; this is used when displaying global + * hashtable statistics. If this parameter is NULL the hash's name will be + * set to NULL and the default name will be displayed when showing + * statistics. + * + * Returns: + * a new hash table + */ +extern struct hash * +hash_create_size(unsigned int size, unsigned int (*hash_key)(void *), + int (*hash_cmp)(const void *, const void *), const char *name); + +/* + * Retrieve or insert data from / into a hash table. + * + * This function is somewhat counterintuitive in its usage. In order to look up + * an element from its key, you must provide the data item itself, with the + * portions used in the hash function set to the same values as the data item + * to retrieve. To insert a data element, either provide the key as just + * described and provide alloc_func as descrbied below to allocate the full + * data element, or provide the full data element and pass 'hash_alloc_intern' + * to alloc_func. + * + * hash + * hash table to operate on + * + * data + * data to insert or retrieve + * + * alloc_func + * function to call if the item is not found in the hash table. This + * function is called with the value of 'data' and should create the data + * item to insert and return a pointer to it. If the data has already been + * completely created and provided in the 'data' parameter, passing + * 'hash_alloc_intern' to this parameter will cause 'data' to be inserted. + * If this parameter is NULL, then this call to hash_get is equivalent to + * hash_lookup. + * + * Returns: + * the data item found or inserted, or NULL if alloc_func is NULL and the + * data is not found + */ +extern void *hash_get(struct hash *hash, void *data, + void *(*alloc_func)(void *)); + +/* + * Dummy element allocation function. + * + * See hash_get for details. + * + * data + * data to insert into the hash table + * + * Returns: + * data + */ +extern void *hash_alloc_intern(void *data); + +/* + * Retrieve an item from a hash table. + * + * This function is equivalent to calling hash_get with alloc_func set to NULL. + * + * hash + * hash table to operate on + * + * data + * data element with values used for key computation set + * + * Returns: + * the data element if found, or NULL if not found + */ +extern void *hash_lookup(struct hash *hash, void *data); -extern void *hash_get(struct hash *, void *, void *(*)(void *)); -extern void *hash_alloc_intern(void *); -extern void *hash_lookup(struct hash *, void *); -extern void *hash_release(struct hash *, void *); +/* + * Remove an element from a hash table. + * + * hash + * hash table to operate on + * + * data + * data element to remove with values used for key computation set + * + * Returns: + * the removed element if found, or NULL if not found + */ +extern void *hash_release(struct hash *hash, void *data); -extern void hash_iterate(struct hash *, void (*)(struct hash_backet *, void *), - void *); +/* + * Iterate over the elements in a hash table. + * + * It is safe to delete items passed to the iteration function from the hash + * table during iteration. + * + * hash + * hash table to operate on + * + * func + * function to call with each data item + * + * arg + * arbitrary argument passed as the second parameter in each call to 'func' + */ +extern void hash_iterate(struct hash *hash, + void (*func)(struct hash_backet *, void *), void *arg); -extern void hash_walk(struct hash *, int (*)(struct hash_backet *, void *), - void *); +/* + * Iterate over the elements in a hash table, stopping on condition. + * + * It is safe to delete items passed to the iteration function from the hash + * table during iteration. + * + * hash + * hash table to operate on + * + * func + * function to call with each data item. If this function returns + * HASHWALK_ABORT then the iteration stops. + * + * arg + * arbitrary argument passed as the second parameter in each call to 'func' + */ +extern void hash_walk(struct hash *hash, + int (*func)(struct hash_backet *, void *), void *arg); -extern void hash_clean(struct hash *, void (*)(void *)); -extern void hash_free(struct hash *); +/* + * Remove all elements from a hash table. + * + * hash + * hash table to operate on + * + * free_func + * function to call with each removed item; intended to free the data + */ +extern void hash_clean(struct hash *hash, void (*free_func)(void *)); + +/* + * Delete a hash table. + * + * This function assumes the table is empty. Call hash_clean to delete the + * hashtable contents if necessary. + * + * hash + * hash table to delete + */ +extern void hash_free(struct hash *hash); +/* + * Converts a hash table to an unsorted linked list. + * Does not modify the hash table in any way. + * + * hash + * hash table to convert + */ +extern struct list *hash_to_list(struct hash *hash); + +/* + * Hash a string using the modified Bernstein hash. + * + * This is not a perfect hash function. + * + * str + * string to hash + * + * Returns: + * modified Bernstein hash of the string + */ extern unsigned int string_hash_make(const char *); +/* + * Install CLI commands for viewing global hash table statistics. + */ extern void hash_cmd_init(void); #endif /* _ZEBRA_HASH_H */ diff --git a/lib/linklist.c b/lib/linklist.c index 2306dd6d00..2cfa3e7482 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -19,6 +19,7 @@ */ #include <zebra.h> +#include <stdlib.h> #include "linklist.h" #include "memory.h" @@ -26,7 +27,6 @@ DEFINE_MTYPE_STATIC(LIB, LINK_LIST, "Link List") DEFINE_MTYPE_STATIC(LIB, LINK_NODE, "Link Node") -/* Allocate new list. */ struct list *list_new(void) { return XCALLOC(MTYPE_LINK_LIST, sizeof(struct list)); @@ -50,7 +50,6 @@ static void listnode_free(struct listnode *node) XFREE(MTYPE_LINK_NODE, node); } -/* Add new data to the list. */ void listnode_add(struct list *list, void *val) { struct listnode *node; @@ -71,12 +70,6 @@ void listnode_add(struct list *list, void *val) list->count++; } -/* - * Add a node to the list. If the list was sorted according to the - * cmp function, insert a new node with the given val such that the - * list remains sorted. The new node is always inserted; there is no - * notion of omitting duplicates. - */ void listnode_add_sort(struct list *list, void *val) { struct listnode *n; @@ -185,14 +178,12 @@ struct listnode *listnode_add_before(struct list *list, struct listnode *pp, return nn; } -/* Move given listnode to tail of the list */ void listnode_move_to_tail(struct list *l, struct listnode *n) { LISTNODE_DETACH(l, n); LISTNODE_ATTACH(l, n); } -/* Delete specific date pointer from the list. */ void listnode_delete(struct list *list, void *val) { struct listnode *node; @@ -217,7 +208,6 @@ void listnode_delete(struct list *list, void *val) } } -/* Return first node's data if it is there. */ void *listnode_head(struct list *list) { struct listnode *node; @@ -230,7 +220,6 @@ void *listnode_head(struct list *list) return NULL; } -/* Delete all listnode from the list. */ void list_delete_all_node(struct list *list) { struct listnode *node; @@ -247,7 +236,6 @@ void list_delete_all_node(struct list *list) list->count = 0; } -/* Delete all listnode then free list itself. */ void list_delete_and_null(struct list **list) { assert(*list); @@ -261,7 +249,6 @@ void list_delete_original(struct list *list) list_delete_and_null(&list); } -/* Lookup the node which has given data. */ struct listnode *listnode_lookup(struct list *list, void *data) { struct listnode *node; @@ -273,7 +260,6 @@ struct listnode *listnode_lookup(struct list *list, void *data) return NULL; } -/* Delete the node from list. For ospfd and ospf6d. */ void list_delete_node(struct list *list, struct listnode *node) { if (node->prev) @@ -288,11 +274,48 @@ void list_delete_node(struct list *list, struct listnode *node) listnode_free(node); } -/* ospf_spf.c */ -void list_add_list(struct list *l, struct list *m) +void list_add_list(struct list *list, struct list *add) { struct listnode *n; - for (n = listhead(m); n; n = listnextnode(n)) - listnode_add(l, n->data); + for (n = listhead(add); n; n = listnextnode(n)) + listnode_add(list, n->data); +} + +struct list *list_dup(struct list *list) +{ + struct list *new = list_new(); + struct listnode *ln; + void *data; + + new->cmp = list->cmp; + new->del = list->del; + + for (ALL_LIST_ELEMENTS_RO(list, ln, data)) + listnode_add(new, data); + + return new; +} + +void list_sort(struct list *list, int (*cmp)(const void **, const void **)) +{ + struct listnode *ln, *nn; + int i = -1; + void *data; + size_t n = list->count; + void **items = XCALLOC(MTYPE_TMP, (sizeof(void *)) * n); + int (*realcmp)(const void *, const void *) = + (int (*)(const void *, const void *))cmp; + + for (ALL_LIST_ELEMENTS(list, ln, nn, data)) { + items[++i] = data; + list_delete_node(list, ln); + } + + qsort(items, n, sizeof(void *), realcmp); + + for (unsigned int i = 0; i < n; ++i) + listnode_add(list, items[i]); + + XFREE(MTYPE_TMP, items); } diff --git a/lib/linklist.h b/lib/linklist.h index ae150158a6..39e70293d2 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -59,20 +59,166 @@ struct list { /* return X->data only if X and X->data are not NULL */ #define listgetdata(X) (assert(X), assert((X)->data != NULL), (X)->data) -/* Prototypes. */ -extern struct list * -list_new(void); /* encouraged: set list.del callback on new lists */ - -extern void listnode_add(struct list *, void *); -extern void listnode_add_sort(struct list *, void *); -extern struct listnode *listnode_add_after(struct list *, struct listnode *, - void *); -extern struct listnode *listnode_add_before(struct list *, struct listnode *, - void *); -extern void listnode_move_to_tail(struct list *, struct listnode *); -extern void listnode_delete(struct list *, void *); -extern struct listnode *listnode_lookup(struct list *, void *); -extern void *listnode_head(struct list *); +/* + * Create a new linked list. + * + * Returns: + * the created linked list + */ +extern struct list *list_new(void); + +/* + * Add a new element to the tail of a list. + * + * Runtime is O(1). + * + * list + * list to operate on + * + * data + * element to add + */ +extern void listnode_add(struct list *list, void *data); + +/* + * Insert a new element into a list with insertion sort. + * + * If list->cmp is set, this function is used to determine the position to + * insert the new element. If it is not set, this function is equivalent to + * listnode_add. + * + * Runtime is O(N). + * + * list + * list to operate on + * + * val + * element to add + */ +extern void listnode_add_sort(struct list *list, void *val); + +/* + * Insert a new element into a list after another element. + * + * Runtime is O(1). + * + * list + * list to operate on + * + * pp + * listnode to insert after + * + * data + * data to insert + * + * Returns: + * pointer to newly created listnode that contains the inserted data + */ +extern struct listnode *listnode_add_after(struct list *list, + struct listnode *pp, void *data); + +/* + * Insert a new element into a list before another element. + * + * Runtime is O(1). + * + * list + * list to operate on + * + * pp + * listnode to insert before + * + * data + * data to insert + * + * Returns: + * pointer to newly created listnode that contains the inserted data + */ +extern struct listnode *listnode_add_before(struct list *list, + struct listnode *pp, void *data); + +/* + * Move a node to the tail of a list. + * + * Runtime is O(1). + * + * list + * list to operate on + * + * node + * node to move to tail + */ +extern void listnode_move_to_tail(struct list *list, struct listnode *node); + +/* + * Delete an element from a list. + * + * Runtime is O(N). + * + * list + * list to operate on + * + * data + * data to insert into list + */ +extern void listnode_delete(struct list *list, void *data); + +/* + * Find the listnode corresponding to an element in a list. + * + * list + * list to operate on + * + * data + * data to search for + * + * Returns: + * pointer to listnode storing the given data if found, NULL otherwise + */ +extern struct listnode *listnode_lookup(struct list *list, void *data); + +/* + * Retrieve the element at the head of a list. + * + * list + * list to operate on + * + * Returns: + * data at head of list, or NULL if list is empty + */ +extern void *listnode_head(struct list *list); + +/* + * Duplicate a list. + * + * list + * list to duplicate + * + * Returns: + * copy of the list + */ +extern struct list *list_dup(struct list *l); + +/* + * Sort a list in place. + * + * The sorting algorithm used is quicksort. Runtimes are equivalent to those of + * quicksort plus N. The sort is not stable. + * + * For portability reasons, the comparison function takes a pointer to pointer + * to void. This pointer should be dereferenced to get the actual data pointer. + * It is always safe to do this. + * + * list + * list to sort + * + * cmp + * comparison function for quicksort. Should return less than, equal to or + * greater than zero if the first argument is less than, equal to or greater + * than the second argument. + */ +extern void list_sort(struct list *list, + int (*cmp)(const void **, const void **)); /* * The usage of list_delete is being transitioned to pass in @@ -87,8 +233,27 @@ extern void *listnode_head(struct list *); #if defined(VERSION_TYPE_DEV) && CONFDATE > 20181001 CPP_NOTICE("list_delete without double pointer is deprecated, please fixup") #endif -extern void list_delete_and_null(struct list **); -extern void list_delete_original(struct list *); + +/* + * Delete a list and NULL its pointer. + * + * If non-null, list->del is called with each data element. + * + * plist + * pointer to list pointer; this will be set to NULL after the list has been + * deleted + */ +extern void list_delete_and_null(struct list **plist); + +/* + * Delete a list. + * + * If non-null, list->del is called with each data element. + * + * plist + * pointer to list pointer + */ +extern void list_delete_original(struct list *list); #define list_delete(X) \ list_delete_original((X)) \ CPP_WARN("Please transition to using list_delete_and_null") @@ -96,13 +261,43 @@ extern void list_delete_original(struct list *); list_delete_original((X)) \ CPP_WARN("Please transition tousing list_delete_and_null") -extern void list_delete_all_node(struct list *); +/* + * Delete all nodes from a list without deleting the list itself. + * + * If non-null, list->del is called with each data element. + * + * list + * list to operate on + */ +extern void list_delete_all_node(struct list *list); -/* For ospfd and ospf6d. */ -extern void list_delete_node(struct list *, struct listnode *); +/* + * Delete a node from a list. + * + * list->del is not called with the data associated with the node. + * + * Runtime is O(1). + * + * list + * list to operate on + * + * node + * the node to delete + */ +extern void list_delete_node(struct list *list, struct listnode *node); -/* For ospf_spf.c */ -extern void list_add_list(struct list *, struct list *); +/* + * Append a list to an existing list. + * + * Runtime is O(N) where N = listcount(add). + * + * list + * list to append to + * + * add + * list to append + */ +extern void list_add_list(struct list *list, struct list *add); /* List iteration macro. * Usage: for (ALL_LIST_ELEMENTS (...) { ... } diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 0fe0cada39..8bd0683f14 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -260,12 +260,12 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, listnode_delete(old_route->nh_list, rnh); ospf6_nexthop_delete(rnh); - route_updated = true; } } listnode_delete(old_route->paths, o_path); ospf6_path_free(o_path); + route_updated = true; /* Current route's path (adv_router info) is similar * to route being added. @@ -273,6 +273,19 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, * Update FIB with effective NHs. */ if (listcount(old_route->paths)) { + for (ALL_LIST_ELEMENTS(old_route->paths, + anode, anext, o_path)) { + ospf6_merge_nexthops( + old_route->nh_list, + o_path->nh_list); + } + /* Update RIB/FIB with effective + * nh_list + */ + if (ospf6->route_table->hook_add) + (*ospf6->route_table->hook_add) + (old_route); + if (old_route->path.origin.id == route->path.origin.id && old_route->path.origin.adv_router @@ -290,23 +303,7 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, old_route->path.origin.adv_router = h_path->origin.adv_router; } - - if (route_updated) { - for (ALL_LIST_ELEMENTS(old_route->paths, - anode, anext, - o_path)) { - ospf6_merge_nexthops( - old_route->nh_list, - o_path->nh_list); - } - /* Update RIB/FIB with effective - * nh_list - */ - if (ospf6->route_table->hook_add) - (*ospf6->route_table->hook_add)( - old_route); - break; - } + break; } else { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { prefix2str(&old_route->prefix, buf, @@ -374,13 +371,6 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, /* Add a nh_list to new ecmp path */ ospf6_copy_nexthops(ecmp_path->nh_list, route->nh_list); - /* Merge nexthop to existing route's nh_list */ - ospf6_route_merge_nexthops(old_route, route); - - /* Update RIB/FIB */ - if (ospf6->route_table->hook_add) - (*ospf6->route_table->hook_add)( - old_route); /* Add the new path to route's path list */ listnode_add_sort(old_route->paths, ecmp_path); @@ -400,46 +390,52 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, listcount(old_route->nh_list)); } } else { - for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, - nnode, nh)) { - for (ALL_LIST_ELEMENTS( - old_route->nh_list, rnode, - rnext, rnh)) { - if (!ospf6_nexthop_is_same(rnh, - nh)) - continue; - - listnode_delete( - old_route->nh_list, - rnh); - ospf6_nexthop_delete(rnh); - } - } list_delete_all_node(o_path->nh_list); ospf6_copy_nexthops(o_path->nh_list, route->nh_list); + } - /* Merge nexthop to existing route's nh_list */ - ospf6_route_merge_nexthops(old_route, route); + /* Reset nexthop lists, rebuild from brouter table + * for each adv. router. + */ + list_delete_all_node(old_route->nh_list); - if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { - prefix2str(&route->prefix, buf, - sizeof(buf)); - zlog_debug( - "%s: existing route %s with effective nh count %u", - __PRETTY_FUNCTION__, buf, - old_route->nh_list - ? listcount( - old_route - ->nh_list) - : 0); + for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode, + o_path)) { + struct ospf6_route *asbr_entry; + + asbr_entry = ospf6_route_lookup( + &o_path->ls_prefix, + ospf6->brouter_table); + if (asbr_entry == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN( + AS_EXTERNAL)) { + prefix2str(&old_route->prefix, + buf, sizeof(buf)); + zlog_debug("%s: ls_prfix %s asbr_entry not found.", + __PRETTY_FUNCTION__, + buf); + } + continue; } + ospf6_route_merge_nexthops(old_route, + asbr_entry); + } - /* Update RIB/FIB */ - if (ospf6->route_table->hook_add) - (*ospf6->route_table->hook_add)( - old_route); + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug("%s: route %s with effective paths %u nh %u", + __PRETTY_FUNCTION__, buf, + old_route->paths ? + listcount(old_route->paths) : 0, + old_route->nh_list ? + listcount(old_route->nh_list) : 0); } + + /* Update RIB/FIB */ + if (ospf6->route_table->hook_add) + (*ospf6->route_table->hook_add)(old_route); + /* Delete the new route its info added to existing * route. */ @@ -509,8 +505,9 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) route->path.origin.type = lsa->header->type; route->path.origin.id = lsa->header->id; route->path.origin.adv_router = lsa->header->adv_router; - route->path.prefix_options = external->prefix.prefix_options; + memcpy(&route->path.ls_prefix, &asbr_id, sizeof(struct prefix)); + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) { route->path.type = OSPF6_PATH_TYPE_EXTERNAL2; route->path.metric_type = 2; diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 26e6deadae..d99541ebad 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -2037,6 +2037,8 @@ static void ospf6_brouter_debug_print(struct ospf6_route *brouter) zlog_info(" options: %s router-bits: %s metric-type: %d metric: %d/%d", options, capa, brouter->path.metric_type, brouter->path.cost, brouter->path.u.cost_e2); + zlog_info(" paths %u nh %u", listcount(brouter->paths), + listcount(brouter->nh_list)); } void ospf6_intra_brouter_calculation(struct ospf6_area *oa) diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 8632a4fb2f..b3e7e31aaa 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -322,10 +322,10 @@ developing OSPF-API and frr applications. %if "%{initsystem}" == "systemd" --enable-systemd=yes \ %endif - --enable-poll=yes %if %{with_rpki} - --enable-rpki + --enable-rpki \ %endif + --enable-poll=yes make %{?_smp_mflags} MAKEINFO="makeinfo --no-split" SPHINXBUILD=%{sphinx} @@ -534,7 +534,7 @@ rm -rf %{buildroot} %defattr(-,root,root) %doc */*.sample* AUTHORS COPYING %doc doc/mpls -%doc ChangeLog NEWS README SERVICES +%doc ChangeLog NEWS README %if 0%{?frr_user:1} %dir %attr(751,%frr_user,%frr_user) %{_sysconfdir} %dir %attr(750,%frr_user,%frr_user) /var/log/frr @@ -586,6 +586,9 @@ rm -rf %{buildroot} %if %{with_fpm} %attr(755,root,root) %{_libdir}/frr/modules/zebra_fpm.so %endif +%if %{with_rpki} +%attr(755,root,root) %{_libdir}/frr/modules/bgpd_rpki.so +%endif %attr(755,root,root) %{_libdir}/frr/modules/zebra_irdp.so %{_bindir}/* %config(noreplace) /etc/frr/[!v]*.conf* @@ -632,7 +635,10 @@ rm -rf %{buildroot} %endif %changelog -* Sun Mar 4 2018 Martin Winter <mwinter@opensourcerouting.org> - %{version} +* Sun May 20 2018 Martin Winter <mwinter@opensourcerouting.org> - %{version} +- Fixed RPKI RPM build + +* Sun Mar 4 2018 Martin Winter <mwinter@opensourcerouting.org> - Add option to build with RPKI (default: disabled) * Tue Feb 20 2018 Martin Winter <mwinter@opensourcerouting.org> diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 9a448e8958..0194e53128 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -39,6 +39,9 @@ #include "ripd/ripd.h" /* ripd options. */ +#if CONFDATE > 20190521 + CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") +#endif static struct option longopts[] = {{"retain", no_argument, NULL, 'r'}, {0}}; /* ripd privileges */ @@ -58,9 +61,6 @@ struct zebra_privs_t ripd_privs = { .cap_num_p = 2, .cap_num_i = 0}; -/* Route retain mode flag. */ -int retain_mode = 0; - /* Master of threads. */ struct thread_master *master; @@ -85,8 +85,7 @@ static void sigint(void) { zlog_notice("Terminating on signal"); - if (!retain_mode) - rip_clean(); + rip_clean(); rip_zclient_stop(); frr_fini(); @@ -127,13 +126,17 @@ FRR_DAEMON_INFO(ripd, RIP, .vty_port = RIP_VTY_PORT, .privs = &ripd_privs, ) +#if CONFDATE > 20190521 +CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") +#endif +#define DEPRECATED_OPTIONS "r" + /* Main routine of ripd. */ int main(int argc, char **argv) { frr_preinit(&ripd_di, argc, argv); - frr_opt_add( - "r", longopts, - " -r, --retain When program terminates, retain added route by ripd.\n"); + + frr_opt_add("" DEPRECATED_OPTIONS, longopts, ""); /* Command line option parse. */ while (1) { @@ -141,15 +144,19 @@ int main(int argc, char **argv) opt = frr_getopt(argc, argv, NULL); + if (opt && opt < 128 && strchr(DEPRECATED_OPTIONS, opt)) { + fprintf(stderr, + "The -%c option no longer exists.\nPlease refer to the manual.\n", + opt); + continue; + } + if (opt == EOF) break; switch (opt) { case 0: break; - case 'r': - retain_mode = 1; - break; default: frr_help_exit(1); break; diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index 8ef27daaba..e4501d6f80 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -40,6 +40,9 @@ #include "ripngd/ripngd.h" /* RIPngd options. */ +#if CONFDATE > 20190521 + CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") +#endif struct option longopts[] = {{"retain", no_argument, NULL, 'r'}, {0}}; /* ripngd privileges */ @@ -60,11 +63,6 @@ struct zebra_privs_t ripngd_privs = { .cap_num_i = 0}; -/* RIPngd program name */ - -/* Route retain mode flag. */ -int retain_mode = 0; - /* Master of threads. */ struct thread_master *master; @@ -88,8 +86,7 @@ static void sigint(void) { zlog_notice("Terminating on signal"); - if (!retain_mode) - ripng_clean(); + ripng_clean(); ripng_zebra_stop(); frr_fini(); @@ -130,28 +127,36 @@ FRR_DAEMON_INFO(ripngd, RIPNG, .vty_port = RIPNG_VTY_PORT, .privs = &ripngd_privs, ) +#if CONFDATE > 20190521 +CPP_NOTICE("-r / --retain has reached deprecation EOL, remove") +#endif +#define DEPRECATED_OPTIONS "r" + /* RIPngd main routine. */ int main(int argc, char **argv) { frr_preinit(&ripngd_di, argc, argv); - frr_opt_add( - "r", longopts, - " -r, --retain When program terminates, retain added route by ripd.\n"); + + frr_opt_add("" DEPRECATED_OPTIONS, longopts, ""); while (1) { int opt; opt = frr_getopt(argc, argv, NULL); + if (opt && opt < 128 && strchr(DEPRECATED_OPTIONS, opt)) { + fprintf(stderr, + "The -%c option no longer exists.\nPlease refer to the manual.\n", + opt); + continue; + } + if (opt == EOF) break; switch (opt) { case 0: break; - case 'r': - retain_mode = 1; - break; default: frr_help_exit(1); break; diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c index 1c80cf055a..a478b416bf 100644 --- a/sharpd/sharp_main.c +++ b/sharpd/sharp_main.c @@ -149,6 +149,8 @@ int main(int argc, char **argv, char **envp) vrf_init(NULL, NULL, NULL, NULL); + route_map_init(); + sharp_zebra_init(); /* Get configuration file. */ diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 4d19484a64..956da9d4ed 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -81,14 +81,16 @@ DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd, DEFPY (install_routes, install_routes_cmd, - "sharp install routes A.B.C.D$start nexthop A.B.C.D$nexthop (1-1000000)$routes", + "sharp install routes A.B.C.D$start nexthop A.B.C.D$nexthop (1-1000000)$routes [instance (0-255)$instance]", "Sharp routing Protocol\n" "install some routes\n" "Routes to install\n" "Address to start /32 generation at\n" "Nexthop to use\n" "Nexthop address\n" - "How many to create\n") + "How many to create\n" + "Instance to use\n" + "Instance\n") { int i; struct prefix p; @@ -112,7 +114,7 @@ DEFPY (install_routes, temp = ntohl(p.u.prefix4.s_addr); for (i = 0; i < routes; i++) { - route_add(&p, &nhop); + route_add(&p, (uint8_t)instance, &nhop); p.u.prefix4.s_addr = htonl(++temp); } @@ -151,17 +153,18 @@ DEFPY(vrf_label, vrf_label_cmd, DEFPY (remove_routes, remove_routes_cmd, - "sharp remove routes A.B.C.D$start (1-1000000)$routes", + "sharp remove routes A.B.C.D$start (1-1000000)$routes [instance (0-255)$instance]", "Sharp Routing Protocol\n" "Remove some routes\n" "Routes to remove\n" "Starting spot\n" - "Routes to uniinstall\n") + "Routes to uniinstall\n" + "instance to use\n" + "Value of instance\n") { int i; struct prefix p; uint32_t temp; - total_routes = routes; removed_routes = 0; @@ -175,7 +178,7 @@ DEFPY (remove_routes, temp = ntohl(p.u.prefix4.s_addr); for (i = 0; i < routes; i++) { - route_delete(&p); + route_delete(&p, (uint8_t)instance); p.u.prefix4.s_addr = htonl(++temp); } diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 999255e925..fcb555170b 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -176,7 +176,7 @@ void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label) zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP); } -void route_add(struct prefix *p, struct nexthop *nh) +void route_add(struct prefix *p, uint8_t instance, struct nexthop *nh) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -184,6 +184,7 @@ void route_add(struct prefix *p, struct nexthop *nh) memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_SHARP; + api.instance = instance; api.safi = SAFI_UNICAST; memcpy(&api.prefix, p, sizeof(*p)); @@ -200,7 +201,7 @@ void route_add(struct prefix *p, struct nexthop *nh) zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); } -void route_delete(struct prefix *p) +void route_delete(struct prefix *p, uint8_t instance) { struct zapi_route api; @@ -208,6 +209,7 @@ void route_delete(struct prefix *p) api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_SHARP; api.safi = SAFI_UNICAST; + api.instance = instance; memcpy(&api.prefix, p, sizeof(*p)); zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 0c906fc4ff..58438ed01d 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -25,7 +25,7 @@ extern void sharp_zebra_init(void); extern void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label); -extern void route_add(struct prefix *p, struct nexthop *nh); -extern void route_delete(struct prefix *p); +extern void route_add(struct prefix *p, uint8_t instance, struct nexthop *nh); +extern void route_delete(struct prefix *p, uint8_t instance); extern void sharp_zebra_nexthop_watch(struct prefix *p, bool watch); #endif diff --git a/tests/Makefile.am b/tests/Makefile.am index 703c1d05fc..6a19325927 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -215,6 +215,7 @@ EXTRA_DIST = \ lib/test_ttable.refout \ lib/test_zlog.py \ lib/test_graph.py \ + lib/test_graph.refout \ ospf6d/test_lsdb.py \ ospf6d/test_lsdb.in \ ospf6d/test_lsdb.refout \ diff --git a/tools/etc/iproute2/rt_protos.d/frr.conf b/tools/etc/iproute2/rt_protos.d/frr.conf index cac75bdfba..4c6968ac27 100644 --- a/tools/etc/iproute2/rt_protos.d/frr.conf +++ b/tools/etc/iproute2/rt_protos.d/frr.conf @@ -10,3 +10,4 @@ 193 ldp 194 sharp 195 pbr +196 static @@ -565,6 +565,7 @@ case "$1" in ip route flush proto 193 ip route flush proto 194 ip route flush proto 195 + ip route flush proto 196 else [ -n "$dmn" ] && eval "${dmn/-/_}=0" start_watchfrr diff --git a/tools/lsan-suppressions.txt b/tools/lsan-suppressions.txt index 5184b55699..5d8bf63580 100644 --- a/tools/lsan-suppressions.txt +++ b/tools/lsan-suppressions.txt @@ -3,3 +3,4 @@ leak:PyObject_Malloc leak:PyObject_Realloc leak:PyList_Append leak:malloc +leak:PyObject_GC_Resize diff --git a/update-autotools b/update-autotools deleted file mode 100755 index d5db16d619..0000000000 --- a/update-autotools +++ /dev/null @@ -1,28 +0,0 @@ -#! /bin/sh -# -# When local system does not have the latest autoconf/automake -# -- Kunihiro Ishiguro <kunihiro@zebra.org> -# - -rm -f config.cache Makefile.in aclocal.m4 config.h.in configure -rm -rf config.guess config.sub ltmain.sh -rm -rf autom4te.cache - -echo "This $0 script is deprecated, and will be removed at some stage." -echo "Please use the 'autoreconf' command included with autoconf." - -echo "TOOLS VERIONS:" -for tool in autoheader autoconf libtool libtoolize aclocal automake; do - $tool --version | head -1 -done - -echo "ACLOCAL:" -aclocal -I m4 -echo "AUTOHEADER:" -autoheader -echo "AUTOCONF:" -autoconf -echo "LIBTOOLIZE:" -libtoolize -c -echo "AUTOMAKE" -automake --gnu --add-missing --copy diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 9fff2ee58c..90c387b48c 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -86,6 +86,8 @@ struct vtysh_client vtysh_client[] = { enum vtysh_write_integrated vtysh_write_integrated = WRITE_INTEGRATED_UNSPECIFIED; +static int vtysh_reconnect(struct vtysh_client *vclient); + static void vclient_close(struct vtysh_client *vclient) { if (vclient->fd >= 0) { @@ -93,7 +95,8 @@ static void vclient_close(struct vtysh_client *vclient) "Warning: closing connection to %s because of an I/O error!\n", vclient->name); close(vclient->fd); - vclient->fd = -1; + /* indicate as candidate for reconnect */ + vclient->fd = VTYSH_WAS_ACTIVE; } } @@ -120,12 +123,28 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line, char *bufvalid, *end = NULL; char terminator[3] = {0, 0, 0}; + /* vclinet was previously active, try to reconnect */ + if (vclient->fd == VTYSH_WAS_ACTIVE) { + ret = vtysh_reconnect(vclient); + if (ret < 0) + goto out_err; + } + if (vclient->fd < 0) return CMD_SUCCESS; ret = write(vclient->fd, line, strlen(line) + 1); - if (ret <= 0) - goto out_err; + if (ret <= 0) { + /* close connection and try to reconnect */ + vclient_close(vclient); + ret = vtysh_reconnect(vclient); + if (ret < 0) + goto out_err; + /* retry line */ + ret = write(vclient->fd, line, strlen(line) + 1); + if (ret <= 0) + goto out_err; + } bufvalid = buf; do { @@ -341,6 +360,7 @@ static int vtysh_execute_func(const char *line, int pager) if (user_mode) { if (strncmp("en", vector_slot(vline, 0), 2) == 0) { + cmd_free_strvec(vline); fprintf(stdout, "%% Command not allowed: enable\n"); return CMD_WARNING; } @@ -349,11 +369,11 @@ static int vtysh_execute_func(const char *line, int pager) saved_ret = ret = cmd_execute_command(vline, vty, &cmd, 1); saved_node = vty->node; - /* If command doesn't succeeded in current node, try to walk up in node - * tree. - * Changing vty->node is enough to try it just out without actual walkup - * in - * the vtysh. */ + /* + * If command doesn't succeeded in current node, try to walk up in node + * tree. Changing vty->node is enough to try it just out without actual + * walkup in the vtysh. + */ while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON && ret != CMD_WARNING && ret != CMD_WARNING_CONFIG_FAILED && vty->node > CONFIG_NODE) { @@ -364,9 +384,10 @@ static int vtysh_execute_func(const char *line, int pager) vty->node = saved_node; - /* If command succeeded in any other node than current (tried > 0) we - * have - * to move into node in the vtysh where it succeeded. */ + /* + * If command succeeded in any other node than current (tried > 0) we + * have to move into node in the vtysh where it succeeded. + */ if (ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret == CMD_WARNING) { if ((saved_node == BGP_VPNV4_NODE @@ -406,9 +427,10 @@ static int vtysh_execute_func(const char *line, int pager) vtysh_execute("configure terminal"); } } - /* If command didn't succeed in any node, continue with return value - * from - * first try. */ + /* + * If command didn't succeed in any node, continue with return value + * from first try. + */ else if (tried) { ret = saved_ret; } @@ -487,6 +509,13 @@ static int vtysh_execute_func(const char *line, int pager) if (cmd->daemon & vtysh_client[i].flag) { if (vtysh_client[i].fd < 0 && (cmd->daemon == vtysh_client[i].flag)) { + for (vc = &vtysh_client[i]; vc; + vc = vc->next) + if (vc->fd < 0) + vtysh_reconnect(vc); + } + if (vtysh_client[i].fd < 0 + && (cmd->daemon == vtysh_client[i].flag)) { bool any_inst = false; for (vc = &vtysh_client[i]; vc; vc = vc->next) @@ -627,8 +656,10 @@ int vtysh_mark_file(const char *filename) continue; } - /* Ignore the "end" lines, we will generate these where - * appropriate */ + /* + * Ignore the "end" lines, we will generate these where + * appropriate + */ if (strlen(vty_buf_trimmed) == 3 && strncmp("end", vty_buf_trimmed, 3) == 0) { cmd_free_strvec(vline); @@ -638,11 +669,11 @@ int vtysh_mark_file(const char *filename) prev_node = vty->node; saved_ret = ret = cmd_execute_command_strict(vline, vty, &cmd); - /* If command doesn't succeeded in current node, try to walk up - * in node tree. - * Changing vty->node is enough to try it just out without - * actual walkup in - * the vtysh. */ + /* + * If command doesn't succeeded in current node, try to walk up + * in node tree. Changing vty->node is enough to try it just + * out without actual walkup in the vtysh. + */ while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON && ret != CMD_WARNING && ret != CMD_WARNING_CONFIG_FAILED && vty->node > CONFIG_NODE) { @@ -651,9 +682,11 @@ int vtysh_mark_file(const char *filename) tried++; } - /* If command succeeded in any other node than current (tried > - * 0) we have - * to move into node in the vtysh where it succeeded. */ + /* + * If command succeeded in any other node than current (tried > + * 0) we have to move into node in the vtysh where it + * succeeded. + */ if (ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret == CMD_WARNING) { if ((prev_node == BGP_VPNV4_NODE @@ -679,9 +712,10 @@ int vtysh_mark_file(const char *filename) fprintf(outputfile, "end\n"); } } - /* If command didn't succeed in any node, continue with return - * value from - * first try. */ + /* + * If command didn't succeed in any node, continue with return + * value from first try. + */ else if (tried) { ret = saved_ret; vty->node = prev_node; @@ -761,6 +795,7 @@ int vtysh_config_from_file(struct vty *vty, FILE *fp) int ret; const struct cmd_element *cmd; int lineno = 0; + /* once we have an error, we remember & return that */ int retcode = CMD_SUCCESS; while (fgets(vty->buf, VTY_BUFSIZ, fp)) { @@ -774,30 +809,25 @@ int vtysh_config_from_file(struct vty *vty, FILE *fp) if (vty->type == VTY_FILE) fprintf(stderr, "line %d: Warning[%d]...: %s\n", lineno, vty->node, vty->buf); - retcode = ret; /* once we have an error, we remember & - return that */ + retcode = ret; + break; case CMD_ERR_AMBIGUOUS: fprintf(stderr, "line %d: %% Ambiguous command[%d]: %s\n", lineno, vty->node, vty->buf); - retcode = CMD_ERR_AMBIGUOUS; /* once we have an error, - we remember & return - that */ + retcode = CMD_ERR_AMBIGUOUS; break; case CMD_ERR_NO_MATCH: fprintf(stderr, "line %d: %% Unknown command[%d]: %s", lineno, vty->node, vty->buf); - retcode = CMD_ERR_NO_MATCH; /* once we have an error, we - remember & return that */ + retcode = CMD_ERR_NO_MATCH; break; case CMD_ERR_INCOMPLETE: fprintf(stderr, "line %d: %% Command incomplete[%d]: %s\n", lineno, vty->node, vty->buf); - retcode = CMD_ERR_INCOMPLETE; /* once we have an error, - we remember & return - that */ + retcode = CMD_ERR_INCOMPLETE; break; case CMD_SUCCESS_DAEMON: { unsigned int i; @@ -810,16 +840,12 @@ int vtysh_config_from_file(struct vty *vty, FILE *fp) outputfile); /* * CMD_WARNING - Can mean that the - * command was - * parsed successfully but it was - * already entered - * in a few spots. As such if we - * receive a + * command was parsed successfully but + * it was already entered in a few + * spots. As such if we receive a * CMD_WARNING from a daemon we - * shouldn't stop - * talking to the other daemons for the - * particular - * command. + * shouldn't stop talking to the other + * daemons for the particular command. */ if (cmd_stat != CMD_SUCCESS && cmd_stat != CMD_WARNING) { @@ -845,27 +871,29 @@ int vtysh_config_from_file(struct vty *vty, FILE *fp) return (retcode); } -/* We don't care about the point of the cursor when '?' is typed. */ -static int vtysh_rl_describe(void) +/* + * Function processes cli commands terminated with '?' character when entered + * through either 'vtysh' or 'vtysh -c' interfaces. + */ +static int vtysh_process_questionmark(const char *input, int input_len) { - int ret; + int ret, width = 0; unsigned int i; - vector vline; - vector describe; - int width; + vector vline, describe; struct cmd_token *token; - vline = cmd_make_strvec(rl_line_buffer); + if (!input) + return 1; + + vline = cmd_make_strvec(input); /* In case of '> ?'. */ if (vline == NULL) { vline = vector_init(1); vector_set(vline, NULL); - } else if (rl_end && isspace((int)rl_line_buffer[rl_end - 1])) + } else if (input_len && isspace((int)input[input_len - 1])) vector_set(vline, NULL); - fprintf(stdout, "\n"); - describe = cmd_describe_command(vline, vty, &ret); /* Ambiguous and no match error. */ @@ -874,7 +902,6 @@ static int vtysh_rl_describe(void) cmd_free_strvec(vline); vector_free(describe); fprintf(stdout, "%% Ambiguous command.\n"); - rl_on_new_line(); return 0; break; case CMD_ERR_NO_MATCH: @@ -882,7 +909,6 @@ static int vtysh_rl_describe(void) if (describe) vector_free(describe); fprintf(stdout, "%% There is no matched command.\n"); - rl_on_new_line(); return 0; break; } @@ -932,9 +958,61 @@ static int vtysh_rl_describe(void) cmd_free_strvec(vline); vector_free(describe); + return 0; +} + +/* + * Entry point for user commands terminated with '?' character and typed through + * the usual vtysh's stdin interface. This is the function being registered with + * readline() api's. + */ +static int vtysh_rl_describe(void) +{ + int ret; + + fprintf(stdout, "\n"); + + ret = vtysh_process_questionmark(rl_line_buffer, rl_end); rl_on_new_line(); - return 0; + return ret; +} + +/* + * Function in charged of processing vtysh instructions terminating with '?' + * character and received through the 'vtysh -c' interface. If user's + * instruction is well-formatted, we will call the same processing routine + * utilized by the traditional vtysh's stdin interface. + */ +int vtysh_execute_command_questionmark(char *input) +{ + int input_len, qmark_count = 0; + const char *str; + + if (!(input && *input)) + return 1; + + /* Finding out question_mark count and strlen */ + for (str = input; *str; ++str) { + if (*str == '?') + qmark_count++; + } + input_len = str - input; + + /* + * Verify that user's input terminates in '?' and that patterns such as + * 'cmd ? subcmd ?' are prevented. + */ + if (qmark_count != 1 || input[input_len - 1] != '?') + return 1; + + /* + * Questionmark-processing function is not expecting to receive '?' + * character in input string. + */ + input[input_len - 1] = '\0'; + + return vtysh_process_questionmark(input, input_len - 1); } /* Result of cmd_complete_command() call will be stored here @@ -967,8 +1045,10 @@ static char *command_generator(const char *text, int state) } if (matched && matched[index]) - /* this is free()'d by readline, but we leak 1 count of - * MTYPE_COMPLETION */ + /* + * this is free()'d by readline, but we leak 1 count of + * MTYPE_COMPLETION + */ return matched[index++]; XFREE(MTYPE_TMP, matched); @@ -1979,8 +2059,10 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd, return vtysh_exit_nexthop_group(self, vty, argc, argv); } -/* TODO Implement interface description commands in ripngd, ospf6d - * and isisd. */ +/* + * TODO Implement interface description commands in ripngd, ospf6d + * and isisd. + */ DEFSH(VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_OSPFD | VTYSH_EIGRPD, vtysh_interface_desc_cmd, "description LINE...", "Interface specific description\n" @@ -3061,6 +3143,22 @@ static int vtysh_connect(struct vtysh_client *vclient) return 0; } +static int vtysh_reconnect(struct vtysh_client *vclient) +{ + int ret; + + fprintf(stderr, "Warning: connecting to %s...", vclient->name); + ret = vtysh_connect(vclient); + if (ret < 0) { + fprintf(stderr, "failed!\n"); + return ret; + } + fprintf(stderr, "success!\n"); + if (vtysh_client_execute(vclient, "enable", NULL) < 0) + return -1; + return vtysh_execute_no_pager("end"); +} + /* Return true if str ends with suffix, else return false */ static int ends_with(const char *str, const char *suffix) { diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index f3e58f309e..cbfc1b5b23 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -40,6 +40,8 @@ DECLARE_MGROUP(MVTYSH) #define VTYSH_SHARPD 0x2000 #define VTYSH_PBRD 0x4000 +#define VTYSH_WAS_ACTIVE (-2) + /* commands in REALLYALL are crucial to correct vtysh operation */ #define VTYSH_REALLYALL ~0U /* watchfrr is not in ALL since library CLI functions should not be @@ -71,6 +73,7 @@ void vtysh_user_init(void); int vtysh_execute(const char *); int vtysh_execute_no_pager(const char *); +int vtysh_execute_command_questionmark(char *input); char *vtysh_prompt(void); diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index cd59d8094b..ff5e6bb784 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -611,7 +611,17 @@ int main(int argc, char **argv, char **env) if (logfile) log_it(cmd->line); - ret = vtysh_execute_no_pager(cmd->line); + /* + * Parsing logic for regular commands will be different + * than for those commands requiring further + * processing, such as cli instructions terminating + * with question-mark character. + */ + if (!vtysh_execute_command_questionmark(cmd->line)) + ret = CMD_SUCCESS; + else + ret = vtysh_execute_no_pager(cmd->line); + if (!no_error && !(ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret == CMD_WARNING)) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 7423e9237f..f153cc3510 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -557,10 +557,11 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id, return 0; } -/* Called from interface_lookup_netlink(). This function is only used - during bootstrap. */ -static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup) +/* + * Called from interface_lookup_netlink(). This function is only used + * during bootstrap. + */ +static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; struct ifinfomsg *ifi; @@ -597,7 +598,6 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h, memset(linkinfo, 0, sizeof linkinfo); netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); -#ifdef IFLA_WIRELESS /* check for wireless messages to ignore */ if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) { if (IS_ZEBRA_DEBUG_KERNEL) @@ -605,7 +605,6 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h, __func__); return 0; } -#endif /* IFLA_WIRELESS */ if (tb[IFLA_IFNAME] == NULL) return -1; @@ -874,8 +873,7 @@ int kernel_address_delete_ipv6(struct interface *ifp, struct connected *ifc) return netlink_address(RTM_DELADDR, AF_INET6, ifp, ifc); } -int netlink_interface_addr(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup) +int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; struct ifaddrmsg *ifa; @@ -1078,8 +1076,7 @@ static void if_netlink_check_ifp_instance_consistency(uint16_t cmd, */ } -int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup) +int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; struct ifinfomsg *ifi; @@ -1122,7 +1119,6 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, memset(linkinfo, 0, sizeof linkinfo); netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); -#ifdef IFLA_WIRELESS /* check for wireless messages to ignore */ if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) { if (IS_ZEBRA_DEBUG_KERNEL) @@ -1130,7 +1126,6 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, __func__); return 0; } -#endif /* IFLA_WIRELESS */ if (tb[IFLA_IFNAME] == NULL) return -1; diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index 769c68a87e..65a266a519 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -23,10 +23,9 @@ #ifdef HAVE_NETLINK -extern int netlink_interface_addr(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup); -extern int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup); +extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, + int startup); +extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int interface_lookup_netlink(struct zebra_ns *zns); #endif /* HAVE_NETLINK */ diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 6b587dab38..0e79b82533 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -110,6 +110,7 @@ static const struct message rtproto_str[] = { {RTPROT_ISIS, "IS-IS"}, {RTPROT_RIP, "RIP"}, {RTPROT_RIPNG, "RIPNG"}, + {RTPROT_ZSTATIC, "static"}, {0}}; static const struct message family_str[] = {{AF_INET, "ipv4"}, @@ -128,11 +129,22 @@ extern uint32_t nl_rcvbufsize; extern struct zebra_privs_t zserv_privs; -int netlink_talk_filter(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup) +int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup) { - zlog_warn("netlink_talk: ignoring message type 0x%04x NS %u", - h->nlmsg_type, ns_id); + /* + * This is an error condition that must be handled during + * development. + * + * The netlink_talk_filter function is used for communication + * down the netlink_cmd pipe and we are expecting + * an ack being received. So if we get here + * then we did not receive the ack and instead + * received some other message in an unexpected + * way. + */ + zlog_err("%s: ignoring message type 0x%04x(%s) NS %u", + __PRETTY_FUNCTION__, h->nlmsg_type, + nl_msg_type_to_str(h->nlmsg_type), ns_id); return 0; } @@ -232,41 +244,50 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups, return ret; } -static int netlink_information_fetch(struct sockaddr_nl *snl, - struct nlmsghdr *h, ns_id_t ns_id, +static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, int startup) { - /* JF: Ignore messages that aren't from the kernel */ - if (snl->nl_pid != 0) { - zlog_err("Ignoring message from pid %u", snl->nl_pid); - return 0; - } - + /* + * When we handle new message types here + * because we are starting to install them + * then lets check the netlink_install_filter + * and see if we should add the corresponding + * allow through entry there. + * Probably not needed to do but please + * think about it. + */ switch (h->nlmsg_type) { case RTM_NEWROUTE: - return netlink_route_change(snl, h, ns_id, startup); + return netlink_route_change(h, ns_id, startup); case RTM_DELROUTE: - return netlink_route_change(snl, h, ns_id, startup); + return netlink_route_change(h, ns_id, startup); case RTM_NEWLINK: - return netlink_link_change(snl, h, ns_id, startup); + return netlink_link_change(h, ns_id, startup); case RTM_DELLINK: - return netlink_link_change(snl, h, ns_id, startup); + return netlink_link_change(h, ns_id, startup); case RTM_NEWADDR: - return netlink_interface_addr(snl, h, ns_id, startup); + return netlink_interface_addr(h, ns_id, startup); case RTM_DELADDR: - return netlink_interface_addr(snl, h, ns_id, startup); + return netlink_interface_addr(h, ns_id, startup); case RTM_NEWNEIGH: - return netlink_neigh_change(snl, h, ns_id); + return netlink_neigh_change(h, ns_id); case RTM_DELNEIGH: - return netlink_neigh_change(snl, h, ns_id); + return netlink_neigh_change(h, ns_id); case RTM_NEWRULE: - return netlink_rule_change(snl, h, ns_id, startup); + return netlink_rule_change(h, ns_id, startup); case RTM_DELRULE: - return netlink_rule_change(snl, h, ns_id, startup); + return netlink_rule_change(h, ns_id, startup); default: - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Unknown netlink nlmsg_type %d vrf %u\n", - h->nlmsg_type, ns_id); + /* + * If we have received this message then + * we have made a mistake during development + * and we need to write some code to handle + * this message type or not ask for + * it to be sent up to us + */ + zlog_err("Unknown netlink nlmsg_type %s(%d) vrf %u\n", + nl_msg_type_to_str(h->nlmsg_type), h->nlmsg_type, + ns_id); break; } return 0; @@ -283,31 +304,69 @@ static int kernel_read(struct thread *thread) return 0; } -/* Filter out messages from self that occur on listener socket, +/* + * Filter out messages from self that occur on listener socket, * caused by our actions on the command socket + * + * When we add new Netlink message types we probably + * do not need to add them here as that we are filtering + * on the routes we actually care to receive( which is rarer + * then the normal course of operations). We are intentionally + * allowing some messages from ourselves through + * ( I'm looking at you Interface based netlink messages ) + * so that we only had to write one way to handle incoming + * address add/delete changes. */ static void netlink_install_filter(int sock, __u32 pid) { + /* + * BPF_JUMP instructions and where you jump to are based upon + * 0 as being the next statement. So count from 0. Writing + * this down because every time I look at this I have to + * re-remember it. + */ struct sock_filter filter[] = { - /* 0: ldh [4] */ - BPF_STMT(BPF_LD | BPF_ABS | BPF_H, - offsetof(struct nlmsghdr, nlmsg_type)), - /* 1: jeq 0x18 jt 5 jf next */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_NEWROUTE), 3, 0), - /* 2: jeq 0x19 jt 5 jf next */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_DELROUTE), 2, 0), - /* 3: jeq 0x19 jt 5 jf next */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_NEWNEIGH), 1, 0), - /* 4: jeq 0x19 jt 5 jf 8 */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_DELNEIGH), 0, 3), - /* 5: ldw [12] */ + /* + * Logic: + * if (nlmsg_pid == pid) { + * if (the incoming nlmsg_type == + * RTM_NEWADDR | RTM_DELADDR) + * keep this message + * else + * skip this message + * } else + * keep this netlink message + */ + /* + * 0: Load the nlmsg_pid into the BPF register + */ BPF_STMT(BPF_LD | BPF_ABS | BPF_W, offsetof(struct nlmsghdr, nlmsg_pid)), - /* 6: jeq XX jt 7 jf 8 */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(pid), 0, 1), - /* 7: ret 0 (skip) */ + /* + * 1: Compare to pid + */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(pid), 0, 4), + /* + * 2: Load the nlmsg_type into BPF register + */ + BPF_STMT(BPF_LD | BPF_ABS | BPF_H, + offsetof(struct nlmsghdr, nlmsg_type)), + /* + * 3: Compare to RTM_NEWADDR + */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_NEWADDR), 2, 0), + /* + * 4: Compare to RTM_DELADDR + */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(RTM_DELADDR), 1, 0), + /* + * 5: This is the end state of we want to skip the + * message + */ BPF_STMT(BPF_RET | BPF_K, 0), - /* 8: ret 0xffff (keep) */ + /* 6: This is the end state of we want to keep + * the message + */ BPF_STMT(BPF_RET | BPF_K, 0xffff), }; @@ -452,8 +511,7 @@ const char *nl_rttype_to_str(uint8_t rttype) * startup -> Are we reading in under startup conditions? passed to * the filter. */ -int netlink_parse_info(int (*filter)(struct sockaddr_nl *, struct nlmsghdr *, - ns_id_t, int), +int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), struct nlsock *nl, struct zebra_ns *zns, int count, int startup) { @@ -620,24 +678,18 @@ int netlink_parse_info(int (*filter)(struct sockaddr_nl *, struct nlmsghdr *, h->nlmsg_type, h->nlmsg_len, h->nlmsg_seq, h->nlmsg_pid); - /* skip unsolicited messages originating from command - * socket - * linux sets the originators port-id for {NEW|DEL}ADDR - * messages, - * so this has to be checked here. */ - if (nl != &zns->netlink_cmd - && h->nlmsg_pid == zns->netlink_cmd.snl.nl_pid - && (h->nlmsg_type != RTM_NEWADDR - && h->nlmsg_type != RTM_DELADDR)) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "netlink_parse_info: %s packet comes from %s", - zns->netlink_cmd.name, - nl->name); + + /* + * Ignore messages that maybe sent from + * other actors besides the kernel + */ + if (snl.nl_pid != 0) { + zlog_err("Ignoring message from pid %u", + snl.nl_pid); continue; } - error = (*filter)(&snl, h, zns->ns_id, startup); + error = (*filter)(h, zns->ns_id, startup); if (error < 0) { zlog_err("%s filter function error", nl->name); ret = error; @@ -671,8 +723,7 @@ int netlink_parse_info(int (*filter)(struct sockaddr_nl *, struct nlmsghdr *, * startup -> Are we reading in under startup conditions * This is passed through eventually to filter. */ -int netlink_talk(int (*filter)(struct sockaddr_nl *, struct nlmsghdr *, ns_id_t, - int startup), +int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, int startup) { @@ -786,11 +837,24 @@ void kernel_init(struct zebra_ns *zns) { unsigned long groups; - /* Initialize netlink sockets */ - groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR - | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_MROUTE - | RTMGRP_NEIGH - | RTNLGRP_IPV4_RULE | RTNLGRP_IPV6_RULE; + /* + * Initialize netlink sockets + * + * If RTMGRP_XXX exists use that, but at some point + * I think the kernel developers realized that + * keeping track of all the different values would + * lead to confusion, so we need to convert the + * RTNLGRP_XXX to a bit position for ourself + */ + groups = RTMGRP_LINK | + RTMGRP_IPV4_ROUTE | + RTMGRP_IPV4_IFADDR | + RTMGRP_IPV6_ROUTE | + RTMGRP_IPV6_IFADDR | + RTMGRP_IPV4_MROUTE | + RTMGRP_NEIGH | + (1 << (RTNLGRP_IPV4_RULE - 1)) | + (1 << (RTNLGRP_IPV6_RULE - 1)); snprintf(zns->netlink.name, sizeof(zns->netlink.name), "netlink-listen (NS %u)", zns->ns_id); diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index dc075b9aff..3b4048ff69 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -45,14 +45,11 @@ extern const char *nl_rtproto_to_str(uint8_t rtproto); extern const char *nl_family_to_str(uint8_t family); extern const char *nl_rttype_to_str(uint8_t rttype); -extern int netlink_parse_info(int (*filter)(struct sockaddr_nl *, - struct nlmsghdr *, ns_id_t, int), +extern int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), struct nlsock *nl, struct zebra_ns *zns, int count, int startup); -extern int netlink_talk_filter(struct sockaddr_nl *, struct nlmsghdr *, ns_id_t, - int startup); -extern int netlink_talk(int (*filter)(struct sockaddr_nl *, struct nlmsghdr *, - ns_id_t, int startup), +extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup); +extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, int startup); extern int netlink_request(struct nlsock *nl, struct nlmsghdr *n); diff --git a/zebra/main.c b/zebra/main.c index 9a495c8940..9c721f0a7e 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -51,6 +51,7 @@ #include "zebra/label_manager.h" #include "zebra/zebra_netns_notify.h" #include "zebra/zebra_rnh.h" +#include "zebra/zebra_pbr.h" #define ZEBRA_PTM_SUPPORT @@ -75,24 +76,29 @@ int allow_delete = 0; /* Don't delete kernel route. */ int keep_kernel_mode = 0; +bool v6_rr_semantics = false; + #ifdef HAVE_NETLINK /* Receive buffer size for netlink socket */ uint32_t nl_rcvbufsize = 4194304; #endif /* HAVE_NETLINK */ +#define OPTION_V6_RR_SEMANTICS 2000 /* Command line options. */ -struct option longopts[] = {{"batch", no_argument, NULL, 'b'}, - {"allow_delete", no_argument, NULL, 'a'}, - {"keep_kernel", no_argument, NULL, 'k'}, - {"socket", required_argument, NULL, 'z'}, - {"ecmp", required_argument, NULL, 'e'}, - {"label_socket", no_argument, NULL, 'l'}, - {"retain", no_argument, NULL, 'r'}, +struct option longopts[] = { + {"batch", no_argument, NULL, 'b'}, + {"allow_delete", no_argument, NULL, 'a'}, + {"keep_kernel", no_argument, NULL, 'k'}, + {"socket", required_argument, NULL, 'z'}, + {"ecmp", required_argument, NULL, 'e'}, + {"label_socket", no_argument, NULL, 'l'}, + {"retain", no_argument, NULL, 'r'}, #ifdef HAVE_NETLINK - {"vrfwnetns", no_argument, NULL, 'n'}, - {"nl-bufsize", required_argument, NULL, 's'}, + {"vrfwnetns", no_argument, NULL, 'n'}, + {"nl-bufsize", required_argument, NULL, 's'}, + {"v6-rr-semantics", no_argument, NULL, OPTION_V6_RR_SEMANTICS}, #endif /* HAVE_NETLINK */ - {0}}; + {0}}; zebra_capabilities_t _caps_p[] = { ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN, ZCAP_NET_RAW, @@ -224,21 +230,22 @@ int main(int argc, char **argv) #endif , longopts, - " -b, --batch Runs in batch mode\n" - " -a, --allow_delete Allow other processes to delete zebra routes\n" - " -z, --socket Set path of zebra socket\n" - " -e, --ecmp Specify ECMP to use.\n" - " -l, --label_socket Socket to external label manager\n" - " -k, --keep_kernel Don't delete old routes which installed by zebra.\n" - " -r, --retain When program terminates, retain added route by zebra.\n" + " -b, --batch Runs in batch mode\n" + " -a, --allow_delete Allow other processes to delete zebra routes\n" + " -z, --socket Set path of zebra socket\n" + " -e, --ecmp Specify ECMP to use.\n" + " -l, --label_socket Socket to external label manager\n" + " -k, --keep_kernel Don't delete old routes which installed by zebra.\n" + " -r, --retain When program terminates, retain added route by zebra.\n" #ifdef HAVE_NETLINK - " -n, --vrfwnetns Set VRF with NetNS\n" - " -s, --nl-bufsize Set netlink receive buffer size\n" + " -n, --vrfwnetns Set VRF with NetNS\n" + " -s, --nl-bufsize Set netlink receive buffer size\n" + " --v6-rr-semantics Use v6 RR semantics\n" #endif /* HAVE_NETLINK */ #if defined(HANDLE_ZAPI_FUZZING) - " -c <file> Bypass normal startup use this file for tetsting of zapi" + " -c <file> Bypass normal startup use this file for tetsting of zapi" #endif - ); + ); while (1) { int opt = frr_getopt(argc, argv, NULL); @@ -292,6 +299,9 @@ int main(int argc, char **argv) logicalrouter_configure_backend( LOGICALROUTER_BACKEND_OFF); break; + case OPTION_V6_RR_SEMANTICS: + v6_rr_semantics = true; + break; #endif /* HAVE_NETLINK */ #if defined(HANDLE_ZAPI_FUZZING) case 'c': @@ -333,6 +343,7 @@ int main(int argc, char **argv) zebra_mpls_init(); zebra_mpls_vty_init(); zebra_pw_vty_init(); + zebra_pbr_init(); /* For debug purpose. */ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 5a6565aec9..b1387815ba 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -539,8 +539,8 @@ int zebra_add_import_table_entry(struct route_node *rn, struct route_entry *re, afi = family2afi(rn->p.family); if (rmap_name) ret = zebra_import_table_route_map_check( - afi, re->type, &rn->p, re->ng.nexthop, re->vrf_id, - re->tag, rmap_name); + afi, re->type, re->instance, &rn->p, re->ng.nexthop, + re->vrf_id, re->tag, rmap_name); if (ret != RMAP_MATCH) { UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED); diff --git a/zebra/rib.h b/zebra/rib.h index 7b9e6d56a7..209f085ed1 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -456,4 +456,5 @@ extern void static_config_install_delayed_routes(struct zebra_vrf *zvrf); extern pid_t pid; +extern bool v6_rr_semantics; #endif /*_ZEBRA_RIB_H */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 9510a0e12c..e9b3e59d0e 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -94,7 +94,7 @@ void rt_netlink_init(void) static inline int is_selfroute(int proto) { if ((proto == RTPROT_BGP) || (proto == RTPROT_OSPF) - || (proto == RTPROT_STATIC) || (proto == RTPROT_ZEBRA) + || (proto == RTPROT_ZSTATIC) || (proto == RTPROT_ZEBRA) || (proto == RTPROT_ISIS) || (proto == RTPROT_RIPNG) || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP) || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL) @@ -120,7 +120,7 @@ static inline int zebra2proto(int proto) proto = RTPROT_OSPF; break; case ZEBRA_ROUTE_STATIC: - proto = RTPROT_STATIC; + proto = RTPROT_ZSTATIC; break; case ZEBRA_ROUTE_ISIS: proto = RTPROT_ISIS; @@ -194,6 +194,7 @@ static inline int proto2zebra(int proto, int family) proto = ZEBRA_ROUTE_LDP; break; case RTPROT_STATIC: + case RTPROT_ZSTATIC: proto = ZEBRA_ROUTE_STATIC; break; case RTPROT_SHARP: @@ -246,8 +247,7 @@ static vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id) } /* Looking up routing table by netlink interface. */ -static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, - struct nlmsghdr *h, ns_id_t ns_id, +static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; @@ -309,8 +309,12 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, return 0; if (!startup && is_selfroute(rtm->rtm_protocol) - && h->nlmsg_type == RTM_NEWROUTE) + && h->nlmsg_type == RTM_NEWROUTE) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Route type: %d Received that we think we have originated, ignoring", + rtm->rtm_protocol); return 0; + } /* We don't care about change notifications for the MPLS table. */ /* TODO: Revisit this. */ @@ -627,8 +631,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, static struct mcast_route_data *mroute = NULL; -static int netlink_route_change_read_multicast(struct sockaddr_nl *snl, - struct nlmsghdr *h, +static int netlink_route_change_read_multicast(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; @@ -717,8 +720,7 @@ static int netlink_route_change_read_multicast(struct sockaddr_nl *snl, return 0; } -int netlink_route_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup) +int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; struct rtmsg *rtm; @@ -749,9 +751,9 @@ int netlink_route_change(struct sockaddr_nl *snl, struct nlmsghdr *h, return -1; if (rtm->rtm_type == RTN_MULTICAST) - netlink_route_change_read_multicast(snl, h, ns_id, startup); + netlink_route_change_read_multicast(h, ns_id, startup); else - netlink_route_change_read_unicast(snl, h, ns_id, startup); + netlink_route_change_read_unicast(h, ns_id, startup); return 0; } @@ -1308,8 +1310,7 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, struct zebra_ns *zns = zebra_ns_lookup(ns_id); - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; @@ -1666,8 +1667,7 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) struct zebra_ns *zns; zns = zvrf->zns; - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; @@ -1698,7 +1698,7 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p, assert(old || new); if (new) { - if (p->family == AF_INET) + if (p->family == AF_INET || v6_rr_semantics) ret = netlink_route_multipath(RTM_NEWROUTE, p, src_p, new, (old) ? 1 : 0); else { @@ -1759,8 +1759,7 @@ static int netlink_vxlan_flood_list_update(struct interface *ifp, struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); zns = zvrf->zns; - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; @@ -1813,8 +1812,7 @@ int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) #endif -static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - int len, ns_id_t ns_id) +static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) { struct ndmsg *ndm; struct interface *ifp; @@ -1949,8 +1947,7 @@ static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h, return zebra_vxlan_local_mac_del(ifp, br_if, &mac, vid); } -static int netlink_macfdb_table(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup) +static int netlink_macfdb_table(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; struct ndmsg *ndm; @@ -1968,7 +1965,7 @@ static int netlink_macfdb_table(struct sockaddr_nl *snl, struct nlmsghdr *h, if (ndm->ndm_family != AF_BRIDGE) return 0; - return netlink_macfdb_change(snl, h, len, ns_id); + return netlink_macfdb_change(h, len, ns_id); } /* Request for MAC FDB information from the kernel */ @@ -2075,8 +2072,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, return -1; } - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; @@ -2125,8 +2121,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE | NUD_PROBE | NUD_STALE \ | NUD_DELAY) -static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - int len, ns_id_t ns_id) +static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) { struct ndmsg *ndm; struct interface *ifp; @@ -2269,8 +2264,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, return zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip); } -static int netlink_neigh_table(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup) +static int netlink_neigh_table(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; struct ndmsg *ndm; @@ -2288,7 +2282,7 @@ static int netlink_neigh_table(struct sockaddr_nl *snl, struct nlmsghdr *h, if (ndm->ndm_family != AF_INET && ndm->ndm_family != AF_INET6) return 0; - return netlink_neigh_change(snl, h, len); + return netlink_neigh_change(h, len); } /* Request for IP neighbor information from the kernel */ @@ -2348,8 +2342,7 @@ int netlink_neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if) return ret; } -int netlink_neigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id) +int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id) { int len; struct ndmsg *ndm; @@ -2365,13 +2358,13 @@ int netlink_neigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, /* Is this a notification for the MAC FDB or IP neighbor table? */ ndm = NLMSG_DATA(h); if (ndm->ndm_family == AF_BRIDGE) - return netlink_macfdb_change(snl, h, len, ns_id); + return netlink_macfdb_change(h, len, ns_id); if (ndm->ndm_type != RTN_UNICAST) return 0; if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6) - return netlink_ipneigh_change(snl, h, len, ns_id); + return netlink_ipneigh_change(h, len, ns_id); return 0; } @@ -2392,8 +2385,7 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip, struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); zns = zvrf->zns; - memset(&req.n, 0, sizeof(req.n)); - memset(&req.ndm, 0, sizeof(req.ndm)); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 78888f48ca..c4f21d1504 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -53,17 +53,16 @@ #define RTPROT_LDP 193 #define RTPROT_SHARP 194 #define RTPROT_PBR 195 +#define RTPROT_ZSTATIC 196 void rt_netlink_init(void); extern int netlink_mpls_multipath(int cmd, zebra_lsp_t *lsp); -extern int netlink_route_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup); +extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int netlink_route_read(struct zebra_ns *zns); -extern int netlink_neigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id); +extern int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id); extern int netlink_macfdb_read(struct zebra_ns *zns); extern int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 310f0952fa..5f73545855 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -166,8 +166,7 @@ void kernel_del_pbr_rule(struct zebra_pbr_rule *rule) * notification of interest. The expectation is that if this corresponds * to a PBR rule added by FRR, it will be readded. */ -int netlink_rule_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup) +int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { struct zebra_ns *zns; struct fib_rule_hdr *frh; diff --git a/zebra/rule_netlink.h b/zebra/rule_netlink.h index 3a9b51309e..4547a1bb3b 100644 --- a/zebra/rule_netlink.h +++ b/zebra/rule_netlink.h @@ -29,8 +29,7 @@ /* * Handle netlink notification informing a rule add or delete. */ -extern int netlink_rule_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - ns_id_t ns_id, int startup); +extern int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); /* * Get to know existing PBR rules in the kernel - typically called at startup. diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 93c523bf50..54a9cdbc5b 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -320,11 +320,20 @@ static void zebra_pbr_cleanup_rules(struct hash_backet *b, void *data) } } -void zebra_pbr_client_close_cleanup(int sock) +static int zebra_pbr_client_close_cleanup(struct zserv *client) { + int sock = client->sock; struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + if (!sock) + return 0; hash_iterate(zns->rules_hash, zebra_pbr_cleanup_rules, &sock); + return 1; +} + +void zebra_pbr_init(void) +{ + hook_register(zapi_client_close, zebra_pbr_client_close_cleanup); } static void *pbr_ipset_alloc_intern(void *arg) diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 0ac629cc65..6b5cd1e8d1 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -182,8 +182,6 @@ extern void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable, */ extern int kernel_pbr_rule_del(struct zebra_pbr_rule *rule); -extern void zebra_pbr_client_close_cleanup(int sock); - extern void zebra_pbr_rules_free(void *arg); extern uint32_t zebra_pbr_rules_hash_key(void *arg); extern int zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2); @@ -205,4 +203,5 @@ extern void zebra_pbr_iptable_free(void *arg); extern uint32_t zebra_pbr_iptable_hash_key(void *arg); extern int zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2); +extern void zebra_pbr_init(void); #endif /* _ZEBRA_PBR_H */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 7ec640164a..dec4ed06a9 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -924,7 +924,7 @@ static unsigned nexthop_active_check(struct route_node *rn, memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr)); /* It'll get set if required inside */ - ret = zebra_route_map_check(family, re->type, p, nexthop, + ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop, nexthop->vrf_id, re->tag); if (ret == RMAP_DENYMATCH) { if (IS_ZEBRA_DEBUG_RIB) { diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 013e841a5c..10ba88880a 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -49,6 +49,7 @@ struct nh_rmap_obj { struct nexthop *nexthop; vrf_id_t vrf_id; uint32_t source_protocol; + uint8_t instance; int metric; route_tag_t tag; }; @@ -287,7 +288,7 @@ DEFUN (no_match_ip_nexthop_prefix_len, DEFUN (match_source_protocol, match_source_protocol_cmd, - "match source-protocol <bgp|ospf|rip|ripng|isis|ospf6|pim|nhrp|eigrp|babel|connected|system|kernel|static>", + "match source-protocol <bgp|ospf|rip|ripng|isis|ospf6|pim|nhrp|eigrp|babel|connected|system|kernel|static|sharp>", MATCH_STR "Match protocol via which the route was learnt\n" "BGP protocol\n" @@ -303,7 +304,8 @@ DEFUN (match_source_protocol, "Routes from directly connected peer\n" "Routes from system configuration\n" "Routes from kernel\n" - "Statically configured routes\n") + "Statically configured routes\n" + "SHARP process\n") { char *proto = argv[2]->text; int i; @@ -319,7 +321,7 @@ DEFUN (match_source_protocol, DEFUN (no_match_source_protocol, no_match_source_protocol_cmd, - "no match source-protocol [<bgp|ospf|rip|ripng|isis|ospf6|pim|nhrp|eigrp|babel|connected|system|kernel|static>]", + "no match source-protocol [<bgp|ospf|rip|ripng|isis|ospf6|pim|nhrp|eigrp|babel|connected|system|kernel|static|sharp>]", NO_STR MATCH_STR "No match protocol via which the route was learnt\n" @@ -336,13 +338,40 @@ DEFUN (no_match_source_protocol, "Routes from directly connected peer\n" "Routes from system configuration\n" "Routes from kernel\n" - "Statically configured routes\n") + "Statically configured routes\n" + "SHARP process\n") { char *proto = (argc == 4) ? argv[3]->text : NULL; return zebra_route_match_delete(vty, "source-protocol", proto, RMAP_EVENT_MATCH_DELETED); } +DEFUN (match_source_instance, + match_source_instance_cmd, + "match source-instance (0-255)", + MATCH_STR + "Match the protocol's instance number\n" + "The instance number\n") +{ + char *instance = argv[2]->arg; + + return zebra_route_match_add(vty, "source-instance", instance, + RMAP_EVENT_MATCH_ADDED); +} + +DEFUN (no_match_source_instance, + no_match_source_instance_cmd, + "no match source-instance [(0-255)]", + NO_STR MATCH_STR + "Match the protocol's instance number\n" + "The instance number\n") +{ + char *instance = (argc == 4) ? argv[3]->arg : NULL; + + return zebra_route_match_delete(vty, "source-instance", instance, + RMAP_EVENT_MATCH_ADDED); +} + /* set functions */ DEFUN (set_src, @@ -1172,6 +1201,47 @@ static struct route_map_rule_cmd route_match_source_protocol_cmd = { "source-protocol", route_match_source_protocol, route_match_source_protocol_compile, route_match_source_protocol_free}; +/* `source-instance` */ +static route_map_result_t route_match_source_instance(void *rule, + struct prefix *prefix, + route_map_object_t type, + void *object) +{ + uint8_t *instance = (uint8_t *)rule; + struct nh_rmap_obj *nh_data; + + if (type != RMAP_ZEBRA) + return RMAP_NOMATCH; + + nh_data = (struct nh_rmap_obj *)object; + if (!nh_data) + return RMAP_DENYMATCH; + + return (nh_data->instance == *instance) ? RMAP_MATCH : RMAP_NOMATCH; +} + +static void *route_match_source_instance_compile(const char *arg) +{ + uint8_t *instance; + int i; + + i = atoi(arg); + instance = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint8_t)); + + *instance = i; + + return instance; +} + +static void route_match_source_instance_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_match_source_instance_cmd = { + "source-instance", route_match_source_instance, + route_match_source_instance_compile, route_match_source_instance_free}; + /* `set src A.B.C.D' */ /* Set src. */ @@ -1252,7 +1322,7 @@ void zebra_route_map_write_delay_timer(struct vty *vty) } route_map_result_t zebra_route_map_check(int family, int rib_type, - struct prefix *p, + uint8_t instance, struct prefix *p, struct nexthop *nexthop, vrf_id_t vrf_id, route_tag_t tag) { @@ -1263,6 +1333,7 @@ route_map_result_t zebra_route_map_check(int family, int rib_type, nh_obj.nexthop = nexthop; nh_obj.vrf_id = vrf_id; nh_obj.source_protocol = rib_type; + nh_obj.instance = instance; nh_obj.metric = 0; nh_obj.tag = tag; @@ -1296,9 +1367,10 @@ void zebra_del_import_table_route_map(afi_t afi, uint32_t table) } route_map_result_t -zebra_import_table_route_map_check(int family, int re_type, struct prefix *p, - struct nexthop *nexthop, vrf_id_t vrf_id, - route_tag_t tag, const char *rmap_name) +zebra_import_table_route_map_check(int family, int re_type, uint8_t instance, + struct prefix *p, struct nexthop *nexthop, + vrf_id_t vrf_id, route_tag_t tag, + const char *rmap_name) { struct route_map *rmap = NULL; route_map_result_t ret = RMAP_DENYMATCH; @@ -1307,6 +1379,7 @@ zebra_import_table_route_map_check(int family, int re_type, struct prefix *p, nh_obj.nexthop = nexthop; nh_obj.vrf_id = vrf_id; nh_obj.source_protocol = re_type; + nh_obj.instance = instance; nh_obj.metric = 0; nh_obj.tag = tag; @@ -1331,6 +1404,7 @@ route_map_result_t zebra_nht_route_map_check(int family, int client_proto, nh_obj.nexthop = nexthop; nh_obj.vrf_id = nexthop->vrf_id; nh_obj.source_protocol = re->type; + nh_obj.instance = re->instance; nh_obj.metric = re->metric; nh_obj.tag = re->tag; @@ -1471,6 +1545,8 @@ void zebra_route_map_init() route_map_install_match(&route_match_ipv6_address_prefix_len_cmd); route_map_install_match(&route_match_ip_nexthop_prefix_len_cmd); route_map_install_match(&route_match_source_protocol_cmd); + route_map_install_match(&route_match_source_instance_cmd); + /* */ route_map_install_set(&route_set_src_cmd); /* */ @@ -1482,6 +1558,9 @@ void zebra_route_map_init() install_element(RMAP_NODE, &no_match_ip_address_prefix_len_cmd); install_element(RMAP_NODE, &match_source_protocol_cmd); install_element(RMAP_NODE, &no_match_source_protocol_cmd); + install_element(RMAP_NODE, &match_source_instance_cmd); + install_element(RMAP_NODE, &no_match_source_instance_cmd); + /* */ install_element(RMAP_NODE, &set_src_cmd); install_element(RMAP_NODE, &no_set_src_cmd); diff --git a/zebra/zebra_routemap.h b/zebra/zebra_routemap.h index 14c7c58848..20d425a2bc 100644 --- a/zebra/zebra_routemap.h +++ b/zebra/zebra_routemap.h @@ -34,14 +34,14 @@ extern void zebra_del_import_table_route_map(afi_t afi, uint32_t table); extern void zebra_route_map_write_delay_timer(struct vty *); extern route_map_result_t -zebra_import_table_route_map_check(int family, int rib_type, struct prefix *p, - struct nexthop *nexthop, vrf_id_t vrf_id, - route_tag_t tag, const char *rmap_name); -extern route_map_result_t zebra_route_map_check(int family, int rib_type, - struct prefix *p, - struct nexthop *nexthop, - vrf_id_t vrf_id, - route_tag_t tag); +zebra_import_table_route_map_check(int family, int rib_type, uint8_t instance, + struct prefix *p, struct nexthop *nexthop, + vrf_id_t vrf_id, route_tag_t tag, + const char *rmap_name); +extern route_map_result_t +zebra_route_map_check(int family, int rib_type, uint8_t instance, + struct prefix *p, struct nexthop *nexthop, + vrf_id_t vrf_id, route_tag_t tag); extern route_map_result_t zebra_nht_route_map_check(int family, int client_proto, struct prefix *p, struct route_entry *, struct nexthop *nexthop); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 3278c86b99..90604215e7 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -56,7 +56,6 @@ DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor"); /* definitions */ - /* static function declarations */ static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, uint16_t cmd); @@ -183,6 +182,47 @@ static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac, int uninstall); /* Private functions */ +static int host_rb_entry_compare(const struct host_rb_entry *hle1, + const struct host_rb_entry *hle2) +{ + if (hle1->p.family < hle2->p.family) + return -1; + + if (hle1->p.family > hle2->p.family) + return 1; + + if (hle1->p.prefixlen < hle2->p.prefixlen) + return -1; + + if (hle1->p.prefixlen > hle2->p.prefixlen) + return 1; + + if (hle1->p.family == AF_INET) { + if (hle1->p.u.prefix4.s_addr < hle2->p.u.prefix4.s_addr) + return -1; + + if (hle1->p.u.prefix4.s_addr > hle2->p.u.prefix4.s_addr) + return 1; + + return 0; + } else { + zlog_warn("%s: Unexpected family type: %d", __PRETTY_FUNCTION__, + hle1->p.family); + return 0; + } +} +RB_GENERATE(host_rb_tree_entry, host_rb_entry, hl_entry, host_rb_entry_compare); + +static uint32_t rb_host_count(struct host_rb_tree_entry *hrbe) +{ + struct host_rb_entry *hle; + uint32_t count = 0; + + RB_FOREACH (hle, host_rb_tree_entry, hrbe) + count++; + + return count; +} /* * Return number of valid MACs in a VNI's MAC hash table - all @@ -435,20 +475,20 @@ static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, { char buf1[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; - struct listnode *node = NULL; - struct prefix *p = NULL; json_object *json_hosts = NULL; + struct host_rb_entry *hle; if (!json) { vty_out(vty, "Ip: %s\n", ipaddr2str(&n->ip, buf2, sizeof(buf2))); vty_out(vty, " RMAC: %s\n", prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - vty_out(vty, " Refcount: %d\n", listcount(n->host_list)); + vty_out(vty, " Refcount: %d\n", + rb_host_count(&n->host_rb)); vty_out(vty, " Prefixes:\n"); - for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p)) + RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) vty_out(vty, " %s\n", - prefix2str(p, buf2, sizeof(buf2))); + prefix2str(&hle->p, buf2, sizeof(buf2))); } else { json_hosts = json_object_new_array(); json_object_string_add( @@ -456,11 +496,12 @@ static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, json_object_string_add( json, "routerMac", prefix_mac2str(&n->emac, buf2, sizeof(buf2))); - json_object_int_add(json, "refCount", listcount(n->host_list)); - for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p)) + json_object_int_add(json, "refCount", + rb_host_count(&n->host_rb)); + RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) json_object_array_add(json_hosts, json_object_new_string(prefix2str( - p, buf2, sizeof(buf2)))); + &hle->p, buf2, sizeof(buf2)))); json_object_object_add(json, "prefixList", json_hosts); } } @@ -471,20 +512,19 @@ static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, { char buf1[ETHER_ADDR_STRLEN]; char buf2[PREFIX_STRLEN]; - struct listnode *node = NULL; - struct prefix *p = NULL; json_object *json_hosts = NULL; + struct host_rb_entry *hle; if (!json) { vty_out(vty, "MAC: %s\n", prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1))); vty_out(vty, " Remote VTEP: %s\n", inet_ntoa(zrmac->fwd_info.r_vtep_ip)); - vty_out(vty, " Refcount: %d\n", listcount(zrmac->host_list)); + vty_out(vty, " Refcount: %d\n", rb_host_count(&zrmac->host_rb)); vty_out(vty, " Prefixes:\n"); - for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p)) + RB_FOREACH (hle, host_rb_tree_entry, &zrmac->host_rb) vty_out(vty, " %s\n", - prefix2str(p, buf2, sizeof(buf2))); + prefix2str(&hle->p, buf2, sizeof(buf2))); } else { json_hosts = json_object_new_array(); json_object_string_add( @@ -493,11 +533,12 @@ static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, json_object_string_add(json, "vtepIp", inet_ntoa(zrmac->fwd_info.r_vtep_ip)); json_object_int_add(json, "refCount", - listcount(zrmac->host_list)); - for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p)) - json_object_array_add(json_hosts, - json_object_new_string(prefix2str( - p, buf2, sizeof(buf2)))); + rb_host_count(&zrmac->host_rb)); + RB_FOREACH (hle, host_rb_tree_entry, &zrmac->host_rb) + json_object_array_add( + json_hosts, + json_object_new_string(prefix2str( + &hle->p, buf2, sizeof(buf2)))); json_object_object_add(json, "prefixList", json_hosts); } } @@ -3042,42 +3083,38 @@ static void zl3vni_cleanup_all(struct hash_backet *backet, void *args) zebra_vxlan_process_l3vni_oper_down(zl3vni); } -static int is_host_present_in_host_list(struct list *list, struct prefix *host) +static void rb_find_or_add_host(struct host_rb_tree_entry *hrbe, + struct prefix *host) { - struct listnode *node = NULL; - struct prefix *p = NULL; + struct host_rb_entry lookup; + struct host_rb_entry *hle; - for (ALL_LIST_ELEMENTS_RO(list, node, p)) { - if (prefix_same(p, host)) - return 1; - } - return 0; -} + memset(&lookup, 0, sizeof(lookup)); + memcpy(&lookup.p, host, sizeof(*host)); -static void host_list_add_host(struct list *list, struct prefix *host) -{ - struct prefix *p = NULL; + hle = RB_FIND(host_rb_tree_entry, hrbe, &lookup); + if (hle) + return; - p = XCALLOC(MTYPE_HOST_PREFIX, sizeof(struct prefix)); - memcpy(p, host, sizeof(struct prefix)); + hle = XCALLOC(MTYPE_HOST_PREFIX, sizeof(struct host_rb_entry)); + memcpy(hle, &lookup, sizeof(lookup)); - listnode_add_sort(list, p); + RB_INSERT(host_rb_tree_entry, hrbe, hle); } -static void host_list_delete_host(struct list *list, struct prefix *host) +static void rb_delete_host(struct host_rb_tree_entry *hrbe, struct prefix *host) { - struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL; - struct prefix *p = NULL; + struct host_rb_entry lookup; + struct host_rb_entry *hle; - for (ALL_LIST_ELEMENTS(list, node, nnode, p)) { - if (prefix_same(p, host)) { - XFREE(MTYPE_HOST_PREFIX, p); - node_to_del = node; - } - } + memset(&lookup, 0, sizeof(lookup)); + memcpy(&lookup.p, host, sizeof(*host)); + + hle = RB_FIND(host_rb_tree_entry, hrbe, &lookup); + if (hle) + RB_REMOVE(host_rb_tree_entry, hrbe, hle); - if (node_to_del) - list_delete_node(list, node_to_del); + return; } /* @@ -3123,8 +3160,7 @@ static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) zrmac = hash_get(zl3vni->rmac_table, &tmp_rmac, zl3vni_rmac_alloc); assert(zrmac); - zrmac->host_list = list_new(); - zrmac->host_list->cmp = (int (*)(void *, void *))prefix_cmp; + RB_INIT(host_rb_tree_entry, &zrmac->host_rb); SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE); SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC); @@ -3138,10 +3174,14 @@ static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) { zebra_mac_t *tmp_rmac; + struct host_rb_entry *hle; - if (zrmac->host_list) - list_delete_and_null(&zrmac->host_list); - zrmac->host_list = NULL; + while (!RB_EMPTY(host_rb_tree_entry, &zrmac->host_rb)) { + hle = RB_ROOT(host_rb_tree_entry, &zrmac->host_rb); + + RB_REMOVE(host_rb_tree_entry, &zrmac->host_rb, hle); + XFREE(MTYPE_HOST_PREFIX, hle); + } tmp_rmac = hash_release(zl3vni->rmac_table, zrmac); if (tmp_rmac) @@ -3231,8 +3271,8 @@ static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac, zl3vni_rmac_install(zl3vni, zrmac); } - if (!is_host_present_in_host_list(zrmac->host_list, host_prefix)) - host_list_add_host(zrmac->host_list, host_prefix); + rb_find_or_add_host(&zrmac->host_rb, host_prefix); + return 0; } @@ -3241,9 +3281,9 @@ static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni, struct ethaddr *rmac, static void zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac, struct prefix *host_prefix) { - host_list_delete_host(zrmac->host_list, host_prefix); - if (list_isempty(zrmac->host_list)) { + rb_delete_host(&zrmac->host_rb, host_prefix); + if (RB_EMPTY(host_rb_tree_entry, &zrmac->host_rb)) { /* uninstall from kernel */ zl3vni_rmac_uninstall(zl3vni, zrmac); @@ -3296,8 +3336,7 @@ static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *ip, n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc); assert(n); - n->host_list = list_new(); - n->host_list->cmp = (int (*)(void *, void *))prefix_cmp; + RB_INIT(host_rb_tree_entry, &n->host_rb); memcpy(&n->emac, mac, ETH_ALEN); SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); @@ -3312,10 +3351,14 @@ static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *ip, static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) { zebra_neigh_t *tmp_n; + struct host_rb_entry *hle; - if (n->host_list) - list_delete_and_null(&n->host_list); - n->host_list = NULL; + while (!RB_EMPTY(host_rb_tree_entry, &n->host_rb)) { + hle = RB_ROOT(host_rb_tree_entry, &n->host_rb); + + RB_REMOVE(host_rb_tree_entry, &n->host_rb, hle); + XFREE(MTYPE_HOST_PREFIX, hle); + } tmp_n = hash_release(zl3vni->nh_table, n); if (tmp_n) @@ -3380,8 +3423,7 @@ static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip, zl3vni_nh_install(zl3vni, nh); } - if (!is_host_present_in_host_list(nh->host_list, host_prefix)) - host_list_add_host(nh->host_list, host_prefix); + rb_find_or_add_host(&nh->host_rb, host_prefix); return 0; } @@ -3390,9 +3432,9 @@ static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni, struct ipaddr *vtep_ip, static void zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *nh, struct prefix *host_prefix) { - host_list_delete_host(nh->host_list, host_prefix); - if (list_isempty(nh->host_list)) { + rb_delete_host(&nh->host_rb, host_prefix); + if (RB_EMPTY(host_rb_tree_entry, &nh->host_rb)) { /* uninstall from kernel */ zl3vni_nh_uninstall(zl3vni, nh); diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index b4a8b5d480..fa7075f2de 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -215,6 +215,15 @@ static inline void zl3vni_get_rmac(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN); } +struct host_rb_entry { + RB_ENTRY(host_rb_entry) hl_entry; + + struct prefix p; +}; + +RB_HEAD(host_rb_tree_entry, host_rb_entry); +RB_PROTOTYPE(host_rb_tree_entry, host_rb_entry, hl_entry, + host_rb_entry_compare); /* * MAC hash table. * @@ -253,7 +262,7 @@ struct zebra_mac_t_ { struct list *neigh_list; /* list of hosts pointing to this remote RMAC */ - struct list *host_list; + struct host_rb_tree_entry host_rb; }; /* @@ -327,7 +336,7 @@ struct zebra_neigh_t_ { struct in_addr r_vtep_ip; /* list of hosts pointing to this remote NH entry */ - struct list *host_list; + struct host_rb_tree_entry host_rb; }; /* |
