diff options
238 files changed, 7768 insertions, 4253 deletions
diff --git a/.dockerignore b/.dockerignore index d613e18dfc..e6e1310d24 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,3 +6,4 @@ **/*.so **/.libs docker/alpine/pkgs +docker/centos/pkgs diff --git a/.gitignore b/.gitignore index 6cfe23e921..226dca09d0 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,10 @@ /Makefile /Makefile.in +/symalyzer_report.html +/jquery-3.4.1.min.js +/jquery-3.4.1.min.js.tmp + ### autoconf/automake subdir stuff .deps diff --git a/Makefile.am b/Makefile.am index 34f112bf01..6dc8e0d354 100644 --- a/Makefile.am +++ b/Makefile.am @@ -228,8 +228,6 @@ EXTRA_DIST += \ vrrpd/Makefile \ # end -noinst_HEADERS += defaults.h - clean-local: clean-python .PHONY: clean-python clean-python: diff --git a/bfdd/bfd.c b/bfdd/bfd.c index cc171f2ebf..920e3fdeea 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -1852,3 +1852,8 @@ void bfd_session_update_vrf_name(struct bfd_session *bs, struct vrf *vrf) strlcpy(bs->key.vrfname, vrf->name, sizeof(bs->key.vrfname)); hash_get(bfd_key_hash, bs, hash_alloc_intern); } + +unsigned long bfd_get_session_count(void) +{ + return bfd_key_hash->count; +} diff --git a/bfdd/bfd.h b/bfdd/bfd.h index ac413cafc3..97702aab54 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -567,6 +567,8 @@ typedef void (*hash_iter_func)(struct hash_bucket *hb, void *arg); void bfd_id_iterate(hash_iter_func hif, void *arg); void bfd_key_iterate(hash_iter_func hif, void *arg); +unsigned long bfd_get_session_count(void); + /* Export callback functions for `event.c`. */ extern struct thread_master *master; diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index c98ae5c7b3..e6307f78a4 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -142,8 +142,12 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) vty_out(vty, "\t\tDiagnostics: %s\n", diag2str(bs->local_diag)); vty_out(vty, "\t\tRemote diagnostics: %s\n", diag2str(bs->remote_diag)); + vty_out(vty, "\t\tPeer Type: %s\n", + BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) ? "configured" : "dynamic"); vty_out(vty, "\t\tLocal timers:\n"); + vty_out(vty, "\t\t\tDetect-multiplier: %" PRIu32 "\n", + bs->detect_mult); vty_out(vty, "\t\t\tReceive interval: %" PRIu32 "ms\n", bs->timers.required_min_rx / 1000); vty_out(vty, "\t\t\tTransmission interval: %" PRIu32 "ms\n", @@ -152,6 +156,8 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) bs->timers.required_min_echo / 1000); vty_out(vty, "\t\tRemote timers:\n"); + vty_out(vty, "\t\t\tDetect-multiplier: %" PRIu32 "\n", + bs->remote_detect_mult); vty_out(vty, "\t\t\tReceive interval: %" PRIu32 "ms\n", bs->remote_timers.required_min_rx / 1000); vty_out(vty, "\t\t\tTransmission interval: %" PRIu32 "ms\n", @@ -235,12 +241,16 @@ static struct json_object *__display_peer_json(struct bfd_session *bs) else json_object_int_add(jo, "echo-interval", 0); + json_object_int_add(jo, "detect-multiplier", bs->detect_mult); + json_object_int_add(jo, "remote-receive-interval", bs->remote_timers.required_min_rx / 1000); json_object_int_add(jo, "remote-transmit-interval", bs->remote_timers.desired_min_tx / 1000); json_object_int_add(jo, "remote-echo-interval", bs->remote_timers.required_min_echo / 1000); + json_object_int_add(jo, "remote-detect-multiplier", + bs->remote_detect_mult); return jo; } @@ -254,7 +264,7 @@ static void _display_peer_json(struct vty *vty, struct bfd_session *bs) } struct bfd_vrf_tuple { - char *vrfname; + const char *vrfname; struct vty *vty; struct json_object *jo; }; @@ -305,9 +315,8 @@ static void _display_peer_json_iter(struct hash_bucket *hb, void *arg) static void _display_all_peers(struct vty *vty, char *vrfname, bool use_json) { struct json_object *jo; - struct bfd_vrf_tuple bvt; + struct bfd_vrf_tuple bvt = {0}; - memset(&bvt, 0, sizeof(bvt)); bvt.vrfname = vrfname; if (!use_json) { @@ -416,9 +425,8 @@ static void _display_peer_counter_json_iter(struct hash_bucket *hb, void *arg) static void _display_peers_counter(struct vty *vty, char *vrfname, bool use_json) { struct json_object *jo; - struct bfd_vrf_tuple bvt; + struct bfd_vrf_tuple bvt = {0}; - memset(&bvt, 0, sizeof(struct bfd_vrf_tuple)); bvt.vrfname = vrfname; if (!use_json) { bvt.vty = vty; @@ -435,6 +443,90 @@ static void _display_peers_counter(struct vty *vty, char *vrfname, bool use_json json_object_free(jo); } +static void _clear_peer_counter(struct bfd_session *bs) +{ + /* Clear only pkt stats, intention is not to loose system + events counters */ + bs->stats.rx_ctrl_pkt = 0; + bs->stats.tx_ctrl_pkt = 0; + bs->stats.rx_echo_pkt = 0; + bs->stats.tx_echo_pkt = 0; +} + +static void _display_peer_brief(struct vty *vty, struct bfd_session *bs) +{ + char addr_buf[INET6_ADDRSTRLEN]; + + if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { + vty_out(vty, "%-10u", bs->discrs.my_discr); + inet_ntop(bs->key.family, &bs->key.local, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + vty_out(vty, "%-15s\n", state_list[bs->ses_state].str); + } else { + vty_out(vty, "%-10u", bs->discrs.my_discr); + vty_out(vty, " %-40s", satostr(&bs->local_address)); + inet_ntop(bs->key.family, &bs->key.peer, addr_buf, sizeof(addr_buf)); + vty_out(vty, " %-40s", addr_buf); + + vty_out(vty, "%-15s\n", state_list[bs->ses_state].str); + } +} + +static void _display_peer_brief_iter(struct hash_backet *hb, void *arg) +{ + struct bfd_vrf_tuple *bvt = arg; + struct vty *vty; + struct bfd_session *bs = hb->data; + + if (!bvt) + return; + vty = bvt->vty; + + if (bvt->vrfname) { + if (!bs->key.vrfname[0] || + !strmatch(bs->key.vrfname, bvt->vrfname)) + return; + } + + _display_peer_brief(vty, bs); +} + +static void _display_peers_brief(struct vty *vty, const char *vrfname, bool use_json) +{ + struct json_object *jo; + struct bfd_vrf_tuple bvt = {0}; + + bvt.vrfname = vrfname; + + if (use_json == false) { + bvt.vty = vty; + + vty_out(vty, "Session count: %lu\n", bfd_get_session_count()); + vty_out(vty, "%-10s", "SessionId"); + vty_out(vty, " %-40s", "LocalAddress"); + vty_out(vty, " %-40s", "PeerAddress"); + vty_out(vty, "%-15s\n", "Status"); + + vty_out(vty, "%-10s", "========="); + vty_out(vty, " %-40s", "============"); + vty_out(vty, " %-40s", "==========="); + vty_out(vty, "%-15s\n", "======"); + + bfd_id_iterate(_display_peer_brief_iter, &bvt); + return; + } + + jo = json_object_new_array(); + bvt.jo = jo; + + bfd_id_iterate(_display_peer_json_iter, &bvt); + + vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0)); + json_object_free(jo); +} + static struct bfd_session * _find_peer_or_error(struct vty *vty, int argc, struct cmd_token **argv, const char *label, const char *peer_str, @@ -596,6 +688,55 @@ DEFPY(bfd_show_peers_counters, bfd_show_peers_counters_cmd, return CMD_SUCCESS; } +DEFPY(bfd_clear_peer_counters, bfd_clear_peer_counters_cmd, + "clear bfd [vrf <NAME$vrfname>] peer <WORD$label|<A.B.C.D|X:X::X:X>$peer [{multihop|local-address <A.B.C.D|X:X::X:X>$local|interface IFNAME$ifname}]> counters", + SHOW_STR + "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR + "BFD peers status\n" + "Peer label\n" + PEER_IPV4_STR + PEER_IPV6_STR + MHOP_STR + LOCAL_STR + LOCAL_IPV4_STR + LOCAL_IPV6_STR + INTERFACE_STR + LOCAL_INTF_STR + "clear BFD peer counters information\n") +{ + struct bfd_session *bs; + + /* Look up the BFD peer. */ + bs = _find_peer_or_error(vty, argc, argv, label, peer_str, local_str, + ifname, vrfname); + if (bs == NULL) + return CMD_WARNING_CONFIG_FAILED; + + _clear_peer_counter(bs); + + return CMD_SUCCESS; +} + +DEFPY(bfd_show_peers_brief, bfd_show_peers_brief_cmd, + "show bfd [vrf <NAME$vrfname>] peers brief [json]", + SHOW_STR + "Bidirection Forwarding Detection\n" + VRF_CMD_HELP_STR + "BFD peers status\n" + "Show BFD peer information in tabular form\n" + JSON_STR) +{ + char *vrf_name = NULL; + int idx_vrf = 0; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf_name = argv[idx_vrf + 1]->arg; + + _display_peers_brief(vty, vrf_name, use_json(argc, argv)); + + return CMD_SUCCESS; +} /* * Function definitions. @@ -735,8 +876,10 @@ void bfdd_vty_init(void) { install_element(ENABLE_NODE, &bfd_show_peers_counters_cmd); install_element(ENABLE_NODE, &bfd_show_peer_counters_cmd); + install_element(ENABLE_NODE, &bfd_clear_peer_counters_cmd); install_element(ENABLE_NODE, &bfd_show_peers_cmd); install_element(ENABLE_NODE, &bfd_show_peer_cmd); + install_element(ENABLE_NODE, &bfd_show_peers_brief_cmd); install_element(ENABLE_NODE, &show_debugging_bfd_cmd); /* Install BFD node and commands. */ diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 5fa773c95b..a781e70d2f 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -416,6 +416,19 @@ unsigned int aspath_count_hops(const struct aspath *aspath) return count; } +/* Check if aspath has AS_SET or AS_CONFED_SET */ +bool aspath_check_as_sets(struct aspath *aspath) +{ + struct assegment *seg = aspath->segments; + + while (seg) { + if (seg->type == AS_SET || seg->type == AS_CONFED_SET) + return true; + seg = seg->next; + } + return false; +} + /* Estimate size aspath /might/ take if encoded into an * ASPATH attribute. * diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index 10f6ee2821..a4427714ba 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -120,6 +120,7 @@ extern int aspath_confed_check(struct aspath *); extern int aspath_left_confed_check(struct aspath *); extern unsigned long aspath_count(void); extern unsigned int aspath_count_hops(const struct aspath *); +extern bool aspath_check_as_sets(struct aspath *aspath); extern unsigned int aspath_count_confeds(struct aspath *); extern unsigned int aspath_size(struct aspath *); extern as_t aspath_highest(struct aspath *); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index bfa578085d..fe7a80ccf2 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -421,14 +421,15 @@ static struct transit *transit_intern(struct transit *transit) return find; } -void transit_unintern(struct transit *transit) +static void transit_unintern(struct transit **transit) { - if (transit->refcnt) - transit->refcnt--; + if ((*transit)->refcnt) + (*transit)->refcnt--; - if (transit->refcnt == 0) { - hash_release(transit_hash, transit); - transit_free(transit); + if ((*transit)->refcnt == 0) { + hash_release(transit_hash, *transit); + transit_free(*transit); + *transit = NULL; } } @@ -464,15 +465,6 @@ static void transit_finish(void) /* Attribute hash routines. */ static struct hash *attrhash; -/* Shallow copy of an attribute - * Though, not so shallow that it doesn't copy the contents - * of the attr_extra pointed to by 'extra' - */ -void bgp_attr_dup(struct attr *new, struct attr *orig) -{ - *new = *orig; -} - unsigned long int attr_count(void) { return attrhash->count; @@ -860,7 +852,7 @@ void bgp_attr_unintern_sub(struct attr *attr) UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)); if (attr->transit) - transit_unintern(attr->transit); + transit_unintern(&attr->transit); if (attr->encap_subtlvs) encap_unintern(&attr->encap_subtlvs, ENCAP_SUBTLV_TYPE); @@ -2492,7 +2484,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* Fetch attribute flag and type. */ @@ -2515,7 +2508,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* Check extended attribue length bit. */ @@ -2536,7 +2530,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* Set type to bitmap to check duplicate attribute. `type' is @@ -2593,7 +2588,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, ndata, ndl + lfl + 1); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } struct bgp_attr_parser_args attr_args = { @@ -2618,7 +2614,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, attr_args.total); if (ret == BGP_ATTR_PARSE_PROCEED) continue; - return ret; + goto done; } /* OK check attribute and store it's value. */ @@ -2696,32 +2692,25 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); ret = BGP_ATTR_PARSE_ERROR; + goto done; } if (ret == BGP_ATTR_PARSE_EOR) { - if (as4_path) - aspath_unintern(&as4_path); - return ret; + goto done; } - /* If hard error occurred immediately return to the caller. */ if (ret == BGP_ATTR_PARSE_ERROR) { flog_warn(EC_BGP_ATTRIBUTE_PARSE_ERROR, "%s: Attribute %s, parse error", peer->host, lookup_msg(attr_str, type, NULL)); - if (as4_path) - aspath_unintern(&as4_path); - return ret; + goto done; } if (ret == BGP_ATTR_PARSE_WITHDRAW) { - flog_warn( EC_BGP_ATTRIBUTE_PARSE_WITHDRAW, "%s: Attribute %s, parse error - treating as withdrawal", peer->host, lookup_msg(attr_str, type, NULL)); - if (as4_path) - aspath_unintern(&as4_path); - return ret; + goto done; } /* Check the fetched length. */ @@ -2731,9 +2720,8 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, peer->host, lookup_msg(attr_str, type, NULL)); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - if (as4_path) - aspath_unintern(&as4_path); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } } @@ -2744,9 +2732,9 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, lookup_msg(attr_str, type, NULL)); bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - if (as4_path) - aspath_unintern(&as4_path); - return BGP_ATTR_PARSE_ERROR; + + ret = BGP_ATTR_PARSE_ERROR; + goto done; } /* @@ -2765,16 +2753,14 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) { if (bgp_attr_nexthop_valid(peer, attr) < 0) { - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } } /* Check all mandatory well-known attributes are present */ - if ((ret = bgp_attr_check(peer, attr)) < 0) { - if (as4_path) - aspath_unintern(&as4_path); - return ret; - } + if ((ret = bgp_attr_check(peer, attr)) < 0) + goto done; /* * At this place we can see whether we got AS4_PATH and/or @@ -2797,28 +2783,10 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, &as4_aggregator_addr)) { bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MAL_ATTR); - if (as4_path) - aspath_unintern(&as4_path); - return BGP_ATTR_PARSE_ERROR; + ret = BGP_ATTR_PARSE_ERROR; + goto done; } - /* At this stage, we have done all fiddling with as4, and the - * resulting info is in attr->aggregator resp. attr->aspath - * so we can chuck as4_aggregator and as4_path alltogether in - * order to save memory - */ - if (as4_path) { - aspath_unintern(&as4_path); /* unintern - it is in the hash */ - /* The flag that we got this is still there, but that does not - * do any trouble - */ - } - /* - * The "rest" of the code does nothing with as4_aggregator. - * there is no memory attached specifically which is not part - * of the attr. - * so ignoring just means do nothing. - */ /* * Finally do the checks on the aspath we did not do yet * because we waited for a potentially synthesized aspath. @@ -2826,21 +2794,59 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH))) { ret = bgp_attr_aspath_check(peer, attr); if (ret != BGP_ATTR_PARSE_PROCEED) - return ret; + goto done; + } + + ret = BGP_ATTR_PARSE_PROCEED; +done: + + /* + * At this stage, we have done all fiddling with as4, and the + * resulting info is in attr->aggregator resp. attr->aspath so + * we can chuck as4_aggregator and as4_path alltogether in order + * to save memory + */ + if (as4_path) { + /* + * unintern - it is in the hash + * The flag that we got this is still there, but that + * does not do any trouble + */ + aspath_unintern(&as4_path); } - /* Finally intern unknown attribute. */ + + if (ret != BGP_ATTR_PARSE_ERROR) { + /* Finally intern unknown attribute. */ + if (attr->transit) + attr->transit = transit_intern(attr->transit); + if (attr->encap_subtlvs) + attr->encap_subtlvs = encap_intern(attr->encap_subtlvs, + ENCAP_SUBTLV_TYPE); +#if ENABLE_BGP_VNC + if (attr->vnc_subtlvs) + attr->vnc_subtlvs = encap_intern(attr->vnc_subtlvs, + VNC_SUBTLV_TYPE); +#endif + } else { + if (attr->transit) { + transit_free(attr->transit); + attr->transit = NULL; + } + + bgp_attr_flush_encap(attr); + }; + + /* Sanity checks */ if (attr->transit) - attr->transit = transit_intern(attr->transit); + assert(attr->transit->refcnt > 0); if (attr->encap_subtlvs) - attr->encap_subtlvs = - encap_intern(attr->encap_subtlvs, ENCAP_SUBTLV_TYPE); + assert(attr->encap_subtlvs->refcnt > 0); #if ENABLE_BGP_VNC if (attr->vnc_subtlvs) - attr->vnc_subtlvs = - encap_intern(attr->vnc_subtlvs, VNC_SUBTLV_TYPE); + assert(attr->vnc_subtlvs->refcnt > 0); #endif - return BGP_ATTR_PARSE_PROCEED; + return ret; } /* @@ -2899,7 +2905,9 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi, /* Nexthop AFI */ if (afi == AFI_IP - && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) + && (safi == SAFI_UNICAST || + safi == SAFI_LABELED_UNICAST || + safi == SAFI_MULTICAST)) nh_afi = peer_cap_enhe(peer, afi, safi) ? AFI_IP6 : AFI_IP; else nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len); diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 375a2272e1..40e87e190a 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -272,7 +272,6 @@ extern void bgp_attr_finish(void); extern bgp_attr_parse_ret_t bgp_attr_parse(struct peer *, struct attr *, bgp_size_t, struct bgp_nlri *, struct bgp_nlri *); -extern void bgp_attr_dup(struct attr *, struct attr *); extern void bgp_attr_undup(struct attr *new, struct attr *old); extern struct attr *bgp_attr_intern(struct attr *attr); extern void bgp_attr_unintern_sub(struct attr *); @@ -305,9 +304,6 @@ extern unsigned long int attr_unknown_count(void); extern int cluster_loop_check(struct cluster_list *, struct in_addr); extern void cluster_unintern(struct cluster_list *); -/* Transit attribute prototypes. */ -void transit_unintern(struct transit *); - /* Below exported for unit-test purposes only */ struct bgp_attr_parser_args { struct peer *peer; diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 9f1fe64813..24a9cab5d1 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -1662,18 +1662,23 @@ static void bmp_active_connect(struct bmp_active *ba) bmp_active_setup(ba); } -static void bmp_active_resolved(struct resolver_query *resq, int numaddrs, - union sockunion *addr) +static void bmp_active_resolved(struct resolver_query *resq, const char *errstr, + int numaddrs, union sockunion *addr) { struct bmp_active *ba = container_of(resq, struct bmp_active, resq); unsigned i; if (numaddrs <= 0) { - zlog_warn("bmp[%s]: hostname resolution failed", ba->hostname); + zlog_warn("bmp[%s]: hostname resolution failed: %s", + ba->hostname, errstr); + ba->last_err = errstr; ba->curretry += ba->curretry / 2; + ba->addrpos = 0; + ba->addrtotal = 0; bmp_active_setup(ba); return; } + if (numaddrs > (int)array_size(ba->addrs)) numaddrs = array_size(ba->addrs); @@ -1698,6 +1703,8 @@ static int bmp_active_thread(struct thread *t) THREAD_OFF(ba->t_read); THREAD_OFF(ba->t_write); + ba->last_err = NULL; + if (ba->socket == -1) { resolver_resolve(&ba->resq, AF_UNSPEC, ba->hostname, bmp_active_resolved); @@ -1710,8 +1717,9 @@ static int bmp_active_thread(struct thread *t) sockunion2str(&ba->addrs[ba->addrpos], buf, sizeof(buf)); if (ret < 0 || status != 0) { - zlog_warn("bmp[%s]: failed to connect to %s:%d", - ba->hostname, buf, ba->port); + ba->last_err = strerror(status); + zlog_warn("bmp[%s]: failed to connect to %s:%d: %s", + ba->hostname, buf, ba->port, ba->last_err); goto out_next; } @@ -2071,9 +2079,12 @@ DEFPY(show_bmp, struct bmp_bgp *bmpbgp; struct bmp_targets *bt; struct bmp_listener *bl; + struct bmp_active *ba; struct bmp *bmp; struct ttable *tt; char buf[SU_ADDRSTRLEN]; + char uptime[BGP_UPTIME_LEN]; + char *out; frr_each(bmp_bgph, &bmp_bgph, bmpbgp) { vty_out(vty, "BMP state for BGP %s:\n\n", @@ -2122,6 +2133,51 @@ DEFPY(show_bmp, sockunion2str(&bl->addr, buf, SU_ADDRSTRLEN), bl->port); + vty_out(vty, "\n Outbound connections:\n"); + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "remote|state||timer"); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + frr_each (bmp_actives, &bt->actives, ba) { + const char *state_str = "?"; + + if (ba->bmp) { + peer_uptime(ba->bmp->t_up.tv_sec, + uptime, sizeof(uptime), + false, NULL); + ttable_add_row(tt, "%s:%d|Up|%s|%s", + ba->hostname, ba->port, + ba->bmp->remote, uptime); + continue; + } + + uptime[0] = '\0'; + + if (ba->t_timer) { + long trem = thread_timer_remain_second( + ba->t_timer); + + peer_uptime(monotime(NULL) - trem, + uptime, sizeof(uptime), + false, NULL); + state_str = "RetryWait"; + } else if (ba->t_read) { + state_str = "Connecting"; + } else if (ba->resq.callback) { + state_str = "Resolving"; + } + + ttable_add_row(tt, "%s:%d|%s|%s|%s", + ba->hostname, ba->port, + state_str, + ba->last_err ? ba->last_err : "", + uptime); + continue; + } + out = ttable_dump(tt, "\n"); + vty_out(vty, "%s", out); + XFREE(MTYPE_TMP, out); + ttable_del(tt); + vty_out(vty, "\n %zu connected clients:\n", bmp_session_count(&bt->sessions)); tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); @@ -2134,14 +2190,17 @@ DEFPY(show_bmp, pullwr_stats(bmp->pullwr, &total, &q, &kq); - ttable_add_row(tt, "%s|-|%Lu|%Lu|%Lu|%Lu|%zu|%zu", - bmp->remote, + peer_uptime(bmp->t_up.tv_sec, uptime, + sizeof(uptime), false, NULL); + + ttable_add_row(tt, "%s|%s|%Lu|%Lu|%Lu|%Lu|%zu|%zu", + bmp->remote, uptime, bmp->cnt_update, bmp->cnt_mirror, bmp->cnt_mirror_overruns, total, q, kq); } - char *out = ttable_dump(tt, "\n"); + out = ttable_dump(tt, "\n"); vty_out(vty, "%s", out); XFREE(MTYPE_TMP, out); ttable_del(tt); diff --git a/bgpd/bgp_bmp.h b/bgpd/bgp_bmp.h index 9d270e808c..94a17f70da 100644 --- a/bgpd/bgp_bmp.h +++ b/bgpd/bgp_bmp.h @@ -182,6 +182,7 @@ struct bmp_active { unsigned addrpos, addrtotal; union sockunion addrs[8]; int socket; + const char *last_err; struct thread *t_timer, *t_read, *t_write; }; diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 48b714c7df..b8798a7ced 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -49,6 +49,7 @@ #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" +#include "bgpd/bgp_vty.h" /* * Definitions and external declarations. @@ -1571,7 +1572,7 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp, * present, else treat as locally originated. */ if (src_attr) - bgp_attr_dup(&attr, src_attr); + attr = *src_attr; else { memset(&attr, 0, sizeof(struct attr)); bgp_attr_default_set(&attr, BGP_ORIGIN_IGP); @@ -2661,7 +2662,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, * address for the rest of the code to flow through. In the case of IPv4, * make sure to set the flag for next hop attribute. */ - bgp_attr_dup(&attr, parent_pi->attr); + attr = *parent_pi->attr; if (afi == AFI_IP6) evpn_convert_nexthop_to_ipv6(&attr); else @@ -5709,9 +5710,10 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, int ret = 0; - ret = bgp_get(&bgp_vrf, &as, vrf_id_to_name(vrf_id), - vrf_id == VRF_DEFAULT ? BGP_INSTANCE_TYPE_DEFAULT - : BGP_INSTANCE_TYPE_VRF); + ret = bgp_get_vty(&bgp_vrf, &as, vrf_id_to_name(vrf_id), + vrf_id == VRF_DEFAULT + ? BGP_INSTANCE_TYPE_DEFAULT + : BGP_INSTANCE_TYPE_VRF); switch (ret) { case BGP_ERR_AS_MISMATCH: flog_err(EC_BGP_EVPN_AS_MISMATCH, diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 6460ff76fe..3667dae83d 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -164,6 +164,14 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) bgp_writes_off(from_peer); bgp_reads_off(from_peer); + /* + * Before exchanging FD remove doppelganger from + * keepalive peer hash. It could be possible conf peer + * fd is set to -1. If blocked on lock then keepalive + * thread can access peer pointer with fd -1. + */ + bgp_keepalives_off(from_peer); + BGP_TIMER_OFF(peer->t_routeadv); BGP_TIMER_OFF(peer->t_connect); BGP_TIMER_OFF(peer->t_connect_check_r); @@ -1879,7 +1887,7 @@ static const struct { {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */ {bgp_fsm_event_error, Idle}, /* Receive_KEEPALIVE_message */ {bgp_fsm_event_error, Idle}, /* Receive_UPDATE_message */ - {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_event_error, Idle}, /* Receive_NOTIFICATION_message */ {bgp_fsm_exeption, Idle}, /* Clearing_Completed */ }, { diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 08c5d3468d..9cb3957a86 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -27,7 +27,6 @@ #include "thread.h" #include <lib/version.h> #include "memory.h" -#include "memory_vty.h" #include "prefix.h" #include "log.h" #include "privs.h" diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index d37bf54734..77448ec15d 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -720,7 +720,7 @@ void bgp_path_info_mpath_aggregate_update(struct bgp_path_info *new_best, return; } - bgp_attr_dup(&attr, new_best->attr); + attr = *new_best->attr; if (new_best->peer && bgp_flag_check(new_best->peer->bgp, BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 1d15361416..59ed433e58 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -699,7 +699,8 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ return; } - bgp_attr_dup(&static_attr, path_vrf->attr); /* shallow copy */ + /* shallow copy */ + static_attr = *path_vrf->attr; /* * route map handling @@ -1082,7 +1083,8 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ buf_prefix, bgp_vrf->name_pretty); } - bgp_attr_dup(&static_attr, path_vpn->attr); /* shallow copy */ + /* shallow copy */ + static_attr = *path_vpn->attr; /* * Nexthop: stash and clear diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 0969c8e77e..a50fc7d697 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -421,7 +421,8 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) if (peer && !peer->ifp && CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE) - && nhr.prefix.family == AF_INET6) { + && nhr.prefix.family == AF_INET6 + && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { struct interface *ifp; ifp = if_lookup_by_index(nexthop->ifindex, diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 5296246b31..b7e9af002b 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1658,10 +1658,11 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) if (peer->nsf[afi][safi]) bgp_clear_stale_route(peer, afi, safi); - zlog_info("%%NOTIFICATION: rcvd End-of-RIB for %s from %s in vrf %s", - get_afi_safi_str(afi, safi, false), peer->host, - vrf ? vrf->name : VRF_DEFAULT_NAME); - } + zlog_info( + "%s: rcvd End-of-RIB for %s from %s in vrf %s", + __func__, get_afi_safi_str(afi, safi, false), + peer->host, vrf ? vrf->name : VRF_DEFAULT_NAME); + } } /* Everything is done. We unintern temporary structures which diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index bd920740c0..64d7ce9e9f 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1695,7 +1695,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, } /* For modify attribute, copy it to temporary structure. */ - bgp_attr_dup(attr, piattr); + *attr = *piattr; /* If local-preference is not set. */ if ((peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) @@ -1815,7 +1815,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, if ((from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP) && !bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { - bgp_attr_dup(&dummy_attr, attr); + dummy_attr = *attr; rmap_path.attr = &dummy_attr; } @@ -1854,6 +1854,16 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi, if (!bgp_outbound_policy_exists(peer, filter)) return 0; + /* draft-ietf-idr-deprecate-as-set-confed-set + * Filter routes having AS_SET or AS_CONFED_SET in the path. + * Eventually, This document (if approved) updates RFC 4271 + * and RFC 5065 by eliminating AS_SET and AS_CONFED_SET types, + * and obsoletes RFC 6472. + */ + if (peer->bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED) + if (aspath_check_as_sets(attr->aspath)) + return 0; + if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) { if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) { @@ -3155,7 +3165,20 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id, goto filtered; } - bgp_attr_dup(&new_attr, attr); + /* draft-ietf-idr-deprecate-as-set-confed-set + * Filter routes having AS_SET or AS_CONFED_SET in the path. + * Eventually, This document (if approved) updates RFC 4271 + * and RFC 5065 by eliminating AS_SET and AS_CONFED_SET types, + * and obsoletes RFC 6472. + */ + if (peer->bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED) + if (aspath_check_as_sets(attr->aspath)) { + reason = + "as-path contains AS_SET or AS_CONFED_SET type;"; + goto filtered; + } + + new_attr = *attr; /* Apply incoming route-map. * NB: new_attr may now contain newly allocated values from route-map @@ -4334,6 +4357,9 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi) int bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter) { + if (peer->sort == BGP_PEER_IBGP) + return 1; + if (peer->sort == BGP_PEER_EBGP && (ROUTE_MAP_OUT_NAME(filter) || PREFIX_LIST_OUT_NAME(filter) || FILTER_LIST_OUT_NAME(filter) @@ -4344,6 +4370,9 @@ int bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter) int bgp_inbound_policy_exists(struct peer *peer, struct bgp_filter *filter) { + if (peer->sort == BGP_PEER_IBGP) + return 1; + if (peer->sort == BGP_PEER_EBGP && (ROUTE_MAP_IN_NAME(filter) || PREFIX_LIST_IN_NAME(filter) || FILTER_LIST_IN_NAME(filter) @@ -6392,6 +6421,7 @@ void bgp_aggregate_decrement(struct bgp *bgp, struct prefix *p, /* Aggregate route attribute. */ #define AGGREGATE_SUMMARY_ONLY 1 #define AGGREGATE_AS_SET 1 +#define AGGREGATE_AS_UNSET 0 static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str, afi_t afi, safi_t safi) @@ -6494,6 +6524,7 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, struct prefix p; struct bgp_node *rn; struct bgp_aggregate *aggregate; + uint8_t as_set_new = as_set; /* Convert string to prefix structure. */ ret = str2prefix(prefix_str, &p); @@ -6528,7 +6559,27 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, /* Make aggregate address structure. */ aggregate = bgp_aggregate_new(); aggregate->summary_only = summary_only; - aggregate->as_set = as_set; + + /* Network operators MUST NOT locally generate any new + * announcements containing AS_SET or AS_CONFED_SET. If they have + * announced routes with AS_SET or AS_CONFED_SET in them, then they + * SHOULD withdraw those routes and re-announce routes for the + * aggregate or component prefixes (i.e., the more-specific routes + * subsumed by the previously aggregated route) without AS_SET + * or AS_CONFED_SET in the updates. + */ + if (bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED) { + if (as_set == AGGREGATE_AS_SET) { + as_set_new = AGGREGATE_AS_UNSET; + zlog_warn( + "%s: Ignoring as-set because `bgp reject-as-sets` is enabled.\n", + __func__); + vty_out(vty, + "Ignoring as-set because `bgp reject-as-sets` is enabled.\n"); + } + } + + aggregate->as_set = as_set_new; aggregate->safi = safi; if (rmap) { @@ -6563,8 +6614,8 @@ DEFUN (aggregate_address, argv_find(argv, argc, "A.B.C.D/M", &idx); char *prefix = argv[idx]->arg; char *rmap = NULL; - int as_set = - argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET : 0; + int as_set = argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET + : AGGREGATE_AS_UNSET; idx = 0; int summary_only = argv_find(argv, argc, "summary-only", &idx) ? AGGREGATE_SUMMARY_ONLY @@ -6598,8 +6649,8 @@ DEFUN (aggregate_address_mask, char *mask = argv[idx + 1]->arg; bool rmap_found; char *rmap = NULL; - int as_set = - argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET : 0; + int as_set = argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET + : AGGREGATE_AS_UNSET; idx = 0; int summary_only = argv_find(argv, argc, "summary-only", &idx) ? AGGREGATE_SUMMARY_ONLY @@ -6687,8 +6738,8 @@ DEFUN (ipv6_aggregate_address, char *prefix = argv[idx]->arg; char *rmap = NULL; bool rmap_found; - int as_set = - argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET : 0; + int as_set = argv_find(argv, argc, "as-set", &idx) ? AGGREGATE_AS_SET + : AGGREGATE_AS_UNSET; idx = 0; int sum_only = argv_find(argv, argc, "summary-only", &idx) @@ -6784,7 +6835,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, struct attr attr_new; /* Copy attribute for modification. */ - bgp_attr_dup(&attr_new, &attr); + attr_new = attr; if (red->redist_metric_flag) attr_new.med = red->redist_metric; @@ -7103,9 +7154,10 @@ static void route_vty_short_status_out(struct vty *vty, vty_out(vty, " "); } -static char *bgp_nexthop_fqdn(struct peer *peer) +static char *bgp_nexthop_hostname(struct peer *peer, struct attr *attr) { - if (peer->hostname && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME)) + if (peer->hostname && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME) + && !(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))) return peer->hostname; return NULL; } @@ -7115,7 +7167,7 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_path_info *path, int display, safi_t safi, json_object *json_paths) { - struct attr *attr; + struct attr *attr = path->attr; json_object *json_path = NULL; json_object *json_nexthops = NULL; json_object *json_nexthop_global = NULL; @@ -7127,7 +7179,7 @@ void route_vty_out(struct vty *vty, struct prefix *p, bool nexthop_othervrf = false; vrf_id_t nexthop_vrfid = VRF_DEFAULT; const char *nexthop_vrfname = VRF_DEFAULT_NAME; - char *nexthop_fqdn = bgp_nexthop_fqdn(path->peer); + char *nexthop_hostname = bgp_nexthop_hostname(path->peer, attr); if (json_paths) json_path = json_object_new_object(); @@ -7145,9 +7197,6 @@ void route_vty_out(struct vty *vty, struct prefix *p, route_vty_out_route(p, vty, json_path); } - /* Print attribute */ - attr = path->attr; - /* * If vrf id of nexthop is different from that of prefix, * set up printable string to append @@ -7215,57 +7264,67 @@ void route_vty_out(struct vty *vty, struct prefix *p, if (json_paths) { json_nexthop_global = json_object_new_object(); - json_object_string_add( - json_nexthop_global, "afi", - nexthop_fqdn ? "fqdn" - : (af == AF_INET) ? "ip" : "ipv6"); - json_object_string_add( - json_nexthop_global, - nexthop_fqdn ? "fqdn" - : (af == AF_INET) ? "ip" : "ipv6", - nexthop_fqdn ? nexthop_fqdn : nexthop); + json_object_string_add(json_nexthop_global, "ip", + nexthop); + + if (nexthop_hostname) + json_object_string_add(json_nexthop_global, + "hostname", + nexthop_hostname); + + json_object_string_add(json_nexthop_global, "afi", + (af == AF_INET) ? "ipv4" + : "ipv6"); json_object_boolean_true_add(json_nexthop_global, "used"); } else vty_out(vty, "%s%s", - nexthop_fqdn ? nexthop_fqdn : nexthop, + nexthop_hostname ? nexthop_hostname : nexthop, vrf_id_str); } else if (safi == SAFI_EVPN) { if (json_paths) { json_nexthop_global = json_object_new_object(); - json_object_string_add( - json_nexthop_global, - nexthop_fqdn ? "fqdn" : "ip", - nexthop_fqdn ? nexthop_fqdn - : inet_ntoa(attr->nexthop)); + json_object_string_add(json_nexthop_global, "ip", + inet_ntoa(attr->nexthop)); + + if (nexthop_hostname) + json_object_string_add(json_nexthop_global, + "hostname", + nexthop_hostname); + json_object_string_add(json_nexthop_global, "afi", "ipv4"); json_object_boolean_true_add(json_nexthop_global, "used"); } else vty_out(vty, "%-16s%s", - nexthop_fqdn ?: inet_ntoa(attr->nexthop), + nexthop_hostname ? nexthop_hostname + : inet_ntoa(attr->nexthop), vrf_id_str); } else if (safi == SAFI_FLOWSPEC) { if (attr->nexthop.s_addr != 0) { if (json_paths) { json_nexthop_global = json_object_new_object(); - json_object_string_add( - json_nexthop_global, - nexthop_fqdn ? "fqdn" : "ip", - nexthop_fqdn - ? nexthop_fqdn - : inet_ntoa(attr->nexthop)); + json_object_string_add(json_nexthop_global, "afi", "ipv4"); + json_object_string_add( + json_nexthop_global, "ip", + inet_ntoa(attr->nexthop)); + + if (nexthop_hostname) + json_object_string_add( + json_nexthop_global, "hostname", + nexthop_hostname); + json_object_boolean_true_add( json_nexthop_global, "used"); } else { vty_out(vty, "%-16s", - nexthop_fqdn - ? nexthop_fqdn + nexthop_hostname + ? nexthop_hostname : inet_ntoa(attr->nexthop)); } } @@ -7273,11 +7332,13 @@ void route_vty_out(struct vty *vty, struct prefix *p, if (json_paths) { json_nexthop_global = json_object_new_object(); - json_object_string_add(json_nexthop_global, - nexthop_fqdn ? "fqdn" : "ip", - nexthop_fqdn - ? nexthop_fqdn - : inet_ntoa(attr->nexthop)); + json_object_string_add(json_nexthop_global, "ip", + inet_ntoa(attr->nexthop)); + + if (nexthop_hostname) + json_object_string_add(json_nexthop_global, + "hostname", + nexthop_hostname); json_object_string_add(json_nexthop_global, "afi", "ipv4"); @@ -7287,8 +7348,8 @@ void route_vty_out(struct vty *vty, struct prefix *p, char buf[BUFSIZ]; snprintf(buf, sizeof(buf), "%s%s", - nexthop_fqdn ? nexthop_fqdn - : inet_ntoa(attr->nexthop), + nexthop_hostname ? nexthop_hostname + : inet_ntoa(attr->nexthop), vrf_id_str); vty_out(vty, "%-16s", buf); } @@ -7302,13 +7363,15 @@ void route_vty_out(struct vty *vty, struct prefix *p, if (json_paths) { json_nexthop_global = json_object_new_object(); json_object_string_add( - json_nexthop_global, - nexthop_fqdn ? "fqdn" : "ip", - nexthop_fqdn - ? nexthop_fqdn - : inet_ntop(AF_INET6, - &attr->mp_nexthop_global, - buf, BUFSIZ)); + json_nexthop_global, "ip", + inet_ntop(AF_INET6, &attr->mp_nexthop_global, + buf, BUFSIZ)); + + if (nexthop_hostname) + json_object_string_add(json_nexthop_global, + "hostname", + nexthop_hostname); + json_object_string_add(json_nexthop_global, "afi", "ipv6"); json_object_string_add(json_nexthop_global, "scope", @@ -7321,14 +7384,16 @@ void route_vty_out(struct vty *vty, struct prefix *p, || (path->peer->conf_if)) { json_nexthop_ll = json_object_new_object(); json_object_string_add( - json_nexthop_ll, - nexthop_fqdn ? "fqdn" : "ip", - nexthop_fqdn - ? nexthop_fqdn - : inet_ntop( - AF_INET6, - &attr->mp_nexthop_local, - buf, BUFSIZ)); + json_nexthop_ll, "ip", + inet_ntop(AF_INET6, + &attr->mp_nexthop_local, buf, + BUFSIZ)); + + if (nexthop_hostname) + json_object_string_add( + json_nexthop_ll, "hostname", + nexthop_hostname); + json_object_string_add(json_nexthop_ll, "afi", "ipv6"); json_object_string_add(json_nexthop_ll, "scope", @@ -7368,8 +7433,8 @@ void route_vty_out(struct vty *vty, struct prefix *p, } else { len = vty_out( vty, "%s%s", - nexthop_fqdn - ? nexthop_fqdn + nexthop_hostname + ? nexthop_hostname : inet_ntop( AF_INET6, &attr->mp_nexthop_local, @@ -7385,8 +7450,8 @@ void route_vty_out(struct vty *vty, struct prefix *p, } else { len = vty_out( vty, "%s%s", - nexthop_fqdn - ? nexthop_fqdn + nexthop_hostname + ? nexthop_hostname : inet_ntop( AF_INET6, &attr->mp_nexthop_global, @@ -8217,7 +8282,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, char buf[INET6_ADDRSTRLEN]; char buf1[BUFSIZ]; char buf2[EVPN_ROUTE_STRLEN]; - struct attr *attr; + struct attr *attr = path->attr; int sockunion_vty_out(struct vty *, union sockunion *); time_t tbuf; json_object *json_bestpath = NULL; @@ -8242,7 +8307,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, bool nexthop_self = CHECK_FLAG(path->flags, BGP_PATH_ANNC_NH_SELF) ? true : false; int i; - char *nexthop_fqdn = bgp_nexthop_fqdn(path->peer); + char *nexthop_hostname = bgp_nexthop_hostname(path->peer, attr); if (json_paths) { json_path = json_object_new_object(); @@ -8295,8 +8360,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, } } - attr = path->attr; - /* Line1 display AS-path, Aggregator */ if (attr->aspath) { if (json_paths) { @@ -8384,32 +8447,35 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, || !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) { if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) { - if (json_paths) + if (json_paths) { json_object_string_add( - json_nexthop_global, - nexthop_fqdn ? "fqdn" : "ip", - nexthop_fqdn - ? nexthop_fqdn - : inet_ntoa( - attr->mp_nexthop_global_in)); - else + json_nexthop_global, "ip", + inet_ntoa(attr->mp_nexthop_global_in)); + + if (nexthop_hostname) + json_object_string_add( + json_nexthop_global, "hostname", + nexthop_hostname); + } else vty_out(vty, " %s", - nexthop_fqdn - ? nexthop_fqdn + nexthop_hostname + ? nexthop_hostname : inet_ntoa( - attr->mp_nexthop_global_in)); + attr->mp_nexthop_global_in)); } else { - if (json_paths) + if (json_paths) { json_object_string_add( - json_nexthop_global, - nexthop_fqdn ? "fqdn" : "ip", - nexthop_fqdn - ? nexthop_fqdn - : inet_ntoa(attr->nexthop)); - else + json_nexthop_global, "ip", + inet_ntoa(attr->nexthop)); + + if (nexthop_hostname) + json_object_string_add( + json_nexthop_global, "hostname", + nexthop_hostname); + } else vty_out(vty, " %s", - nexthop_fqdn - ? nexthop_fqdn + nexthop_hostname + ? nexthop_hostname : inet_ntoa(attr->nexthop)); } @@ -8419,21 +8485,23 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, } else { if (json_paths) { json_object_string_add( - json_nexthop_global, - nexthop_fqdn ? "fqdn" : "ip", - nexthop_fqdn - ? nexthop_fqdn - : inet_ntop(AF_INET6, - &attr->mp_nexthop_global, - buf, INET6_ADDRSTRLEN)); + json_nexthop_global, "ip", + inet_ntop(AF_INET6, &attr->mp_nexthop_global, + buf, INET6_ADDRSTRLEN)); + + if (nexthop_hostname) + json_object_string_add(json_nexthop_global, + "hostname", + nexthop_hostname); + json_object_string_add(json_nexthop_global, "afi", "ipv6"); json_object_string_add(json_nexthop_global, "scope", "global"); } else { vty_out(vty, " %s", - nexthop_fqdn - ? nexthop_fqdn + nexthop_hostname + ? nexthop_hostname : inet_ntop(AF_INET6, &attr->mp_nexthop_global, buf, INET6_ADDRSTRLEN)); @@ -8605,12 +8673,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, if (json_paths) { json_nexthop_ll = json_object_new_object(); json_object_string_add( - json_nexthop_ll, nexthop_fqdn ? "fqdn" : "ip", - nexthop_fqdn - ? nexthop_fqdn - : inet_ntop(AF_INET6, - &attr->mp_nexthop_local, - buf, INET6_ADDRSTRLEN)); + json_nexthop_ll, "ip", + inet_ntop(AF_INET6, &attr->mp_nexthop_local, + buf, INET6_ADDRSTRLEN)); + + if (nexthop_hostname) + json_object_string_add(json_nexthop_ll, + "hostname", + nexthop_hostname); + json_object_string_add(json_nexthop_ll, "afi", "ipv6"); json_object_string_add(json_nexthop_ll, "scope", "link-local"); @@ -9173,7 +9244,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, struct attr dummy_attr; route_map_result_t ret; - bgp_attr_dup(&dummy_attr, pi->attr); + dummy_attr = *pi->attr; path.peer = pi->peer; path.attr = &dummy_attr; @@ -10876,56 +10947,76 @@ struct peer_pcounts { unsigned int count[PCOUNT_MAX]; const struct peer *peer; const struct bgp_table *table; + safi_t safi; }; -static int bgp_peer_count_walker(struct thread *t) +static void bgp_peer_count_proc(struct bgp_node *rn, + struct peer_pcounts *pc) { - struct bgp_node *rn; - struct peer_pcounts *pc = THREAD_ARG(t); + const struct bgp_adj_in *ain; + const struct bgp_path_info *pi; const struct peer *peer = pc->peer; - for (rn = bgp_table_top(pc->table); rn; rn = bgp_route_next(rn)) { - struct bgp_adj_in *ain; - struct bgp_path_info *pi; - - for (ain = rn->adj_in; ain; ain = ain->next) - if (ain->peer == peer) - pc->count[PCOUNT_ADJ_IN]++; + for (ain = rn->adj_in; ain; ain = ain->next) + if (ain->peer == peer) + pc->count[PCOUNT_ADJ_IN]++; - for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { + for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) { - if (pi->peer != peer) - continue; + if (pi->peer != peer) + continue; - pc->count[PCOUNT_ALL]++; + pc->count[PCOUNT_ALL]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_DAMPED)) - pc->count[PCOUNT_DAMPED]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) - pc->count[PCOUNT_HISTORY]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) - pc->count[PCOUNT_REMOVED]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) - pc->count[PCOUNT_STALE]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_VALID)) - pc->count[PCOUNT_VALID]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_DAMPED)) + pc->count[PCOUNT_DAMPED]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) + pc->count[PCOUNT_HISTORY]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) + pc->count[PCOUNT_REMOVED]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) + pc->count[PCOUNT_STALE]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_VALID)) + pc->count[PCOUNT_VALID]++; + if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) + pc->count[PCOUNT_PFCNT]++; + + if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) { + pc->count[PCOUNT_COUNTED]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) + flog_err( + EC_LIB_DEVELOPMENT, + "Attempting to count but flags say it is unusable"); + } else { if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) - pc->count[PCOUNT_PFCNT]++; - - if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) { - pc->count[PCOUNT_COUNTED]++; - if (CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) - flog_err( - EC_LIB_DEVELOPMENT, - "Attempting to count but flags say it is unusable"); - } else { - if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) - flog_err( - EC_LIB_DEVELOPMENT, - "Not counted but flags say we should"); - } + flog_err( + EC_LIB_DEVELOPMENT, + "Not counted but flags say we should"); } } +} + +static int bgp_peer_count_walker(struct thread *t) +{ + struct bgp_node *rn, *rm; + const struct bgp_table *table; + struct peer_pcounts *pc = THREAD_ARG(t); + + if (pc->safi == SAFI_MPLS_VPN || pc->safi == SAFI_ENCAP + || pc->safi == SAFI_EVPN) { + /* Special handling for 2-level routing tables. */ + for (rn = bgp_table_top(pc->table); rn; + rn = bgp_route_next(rn)) { + table = bgp_node_get_bgp_table_info(rn); + if (table != NULL) + for (rm = bgp_table_top(table); rm; + rm = bgp_route_next(rm)) + bgp_peer_count_proc(rm, pc); + } + } else + for (rn = bgp_table_top(pc->table); rn; rn = bgp_route_next(rn)) + bgp_peer_count_proc(rn, pc); + return 0; } @@ -10959,6 +11050,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi, memset(&pcounts, 0, sizeof(pcounts)); pcounts.peer = peer; pcounts.table = peer->bgp->rib[afi][safi]; + pcounts.safi = safi; /* in-place call via thread subsystem so as to record execution time * stats for the thread-walk (i.e. ensure this can't be blamed on @@ -11316,7 +11408,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, header2 = 0; } - bgp_attr_dup(&attr, ain->attr); + attr = *ain->attr; route_filtered = false; /* Filter prefix using distribute list, @@ -11418,7 +11510,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, header2 = 0; } - bgp_attr_dup(&attr, adj->attr); + attr = *adj->attr; ret = bgp_output_modifier( peer, &rn->p, &attr, afi, safi, rmap_name); diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 5c1483a768..49e87adc3c 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -764,7 +764,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) /* Provide dummy so the route-map can't modify * the attributes */ - bgp_attr_dup(&dummy_attr, ri->attr); + dummy_attr = *ri->attr; tmp_info.peer = ri->peer; tmp_info.attr = &dummy_attr; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index fc73ef520e..04e01411a5 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -32,7 +32,7 @@ #include "thread.h" #include "log.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "hash.h" #include "queue.h" #include "filter.h" @@ -64,8 +64,46 @@ #include "bgpd/bgp_bfd.h" #include "bgpd/bgp_io.h" #include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_evpn_vty.h" #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" +#include "bgpd/bgp_flowspec.h" +#if ENABLE_BGP_VNC +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#endif + +FRR_CFG_DEFAULT_BOOL(BGP_IMPORT_CHECK, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) +FRR_CFG_DEFAULT_BOOL(BGP_SHOW_HOSTNAME, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) +FRR_CFG_DEFAULT_BOOL(BGP_LOG_NEIGHBOR_CHANGES, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) +FRR_CFG_DEFAULT_BOOL(BGP_DETERMINISTIC_MED, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) +FRR_CFG_DEFAULT_ULONG(BGP_CONNECT_RETRY, + { .val_ulong = 10, .match_profile = "datacenter", }, + { .val_ulong = 120 }, +) +FRR_CFG_DEFAULT_ULONG(BGP_HOLDTIME, + { .val_ulong = 9, .match_profile = "datacenter", }, + { .val_ulong = 180 }, +) +FRR_CFG_DEFAULT_ULONG(BGP_KEEPALIVE, + { .val_ulong = 3, .match_profile = "datacenter", }, + { .val_ulong = 60 }, +) + +DEFINE_HOOK(bgp_inst_config_write, + (struct bgp *bgp, struct vty *vty), + (bgp, vty)) static struct peer_group *listen_range_exists(struct bgp *bgp, struct prefix *range, int exact); @@ -347,6 +385,29 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, return ret; } +int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, + enum bgp_instance_type inst_type) +{ + int ret = bgp_get(bgp, as, name, inst_type); + + if (ret == BGP_CREATED) { + bgp_timers_set(*bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME, + DFLT_BGP_CONNECT_RETRY); + + if (DFLT_BGP_IMPORT_CHECK) + bgp_flag_set(*bgp, BGP_FLAG_IMPORT_CHECK); + if (DFLT_BGP_SHOW_HOSTNAME) + bgp_flag_set(*bgp, BGP_FLAG_SHOW_HOSTNAME); + if (DFLT_BGP_LOG_NEIGHBOR_CHANGES) + bgp_flag_set(*bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); + if (DFLT_BGP_DETERMINISTIC_MED) + bgp_flag_set(*bgp, BGP_FLAG_DETERMINISTIC_MED); + + ret = BGP_SUCCESS; + } + return ret; +} + /* * bgp_vty_find_and_parse_afi_safi_bgp * @@ -1068,7 +1129,7 @@ DEFUN_NOSH (router_bgp, if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) is_new_bgp = (bgp_lookup(as, name) == NULL); - ret = bgp_get(&bgp, &as, name, inst_type); + ret = bgp_get_vty(&bgp, &as, name, inst_type); switch (ret) { case BGP_ERR_AS_MISMATCH: vty_out(vty, "BGP is already running; AS is %u\n", as); @@ -1777,8 +1838,8 @@ ALIAS_HIDDEN(no_bgp_maxpaths_ibgp, no_bgp_maxpaths_ibgp_hidden_cmd, "Number of paths\n" "Match the cluster length\n") -void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) +static void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi) { if (bgp->maxpaths[afi][safi].maxpaths_ebgp != MULTIPATH_NUM) { vty_out(vty, " maximum-paths %d\n", @@ -1821,7 +1882,7 @@ DEFUN (bgp_timers, return CMD_WARNING_CONFIG_FAILED; } - bgp_timers_set(bgp, keepalive, holdtime); + bgp_timers_set(bgp, keepalive, holdtime, DFLT_BGP_CONNECT_RETRY); return CMD_SUCCESS; } @@ -1836,7 +1897,8 @@ DEFUN (no_bgp_timers, "Holdtime\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); - bgp_timers_unset(bgp); + bgp_timers_set(bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME, + DFLT_BGP_CONNECT_RETRY); return CMD_SUCCESS; } @@ -1921,6 +1983,56 @@ DEFUN(no_bgp_ebgp_requires_policy, no_bgp_ebgp_requires_policy_cmd, return CMD_SUCCESS; } +DEFUN(bgp_reject_as_sets, bgp_reject_as_sets_cmd, + "bgp reject-as-sets", + "BGP specific commands\n" + "Reject routes with AS_SET or AS_CONFED_SET flag\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct listnode *node, *nnode; + struct peer *peer; + + bgp->reject_as_sets = BGP_REJECT_AS_SETS_ENABLED; + + /* Reset existing BGP sessions to reject routes + * with aspath containing AS_SET or AS_CONFED_SET. + */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { + peer->last_reset = PEER_DOWN_AS_SETS_REJECT; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + + return CMD_SUCCESS; +} + +DEFUN(no_bgp_reject_as_sets, no_bgp_reject_as_sets_cmd, + "no bgp reject-as-sets", + NO_STR + "BGP specific commands\n" + "Reject routes with AS_SET or AS_CONFED_SET flag\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct listnode *node, *nnode; + struct peer *peer; + + bgp->reject_as_sets = BGP_REJECT_AS_SETS_DISABLED; + + /* Reset existing BGP sessions to reject routes + * with aspath containing AS_SET or AS_CONFED_SET. + */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { + peer->last_reset = PEER_DOWN_AS_SETS_REJECT; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + + return CMD_SUCCESS; +} /* "bgp deterministic-med" configuration. */ DEFUN (bgp_deterministic_med, @@ -2579,8 +2691,8 @@ DEFUN (bgp_listen_limit, bgp_listen_limit_cmd, "bgp listen limit (1-5000)", "BGP specific commands\n" - "Configure BGP defaults\n" - "maximum number of BGP Dynamic Neighbors that can be created\n" + "BGP Dynamic Neighbors listen commands\n" + "Maximum number of BGP Dynamic Neighbors that can be created\n" "Configure Dynamic Neighbors listen limit value\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2597,10 +2709,10 @@ DEFUN (bgp_listen_limit, DEFUN (no_bgp_listen_limit, no_bgp_listen_limit_cmd, "no bgp listen limit [(1-5000)]", + NO_STR "BGP specific commands\n" - "Configure BGP defaults\n" - "unset maximum number of BGP Dynamic Neighbors that can be created\n" - "Configure Dynamic Neighbors listen limit value to default\n" + "BGP Dynamic Neighbors listen commands\n" + "Maximum number of BGP Dynamic Neighbors that can be created\n" "Configure Dynamic Neighbors listen limit value\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); @@ -2731,7 +2843,7 @@ DEFUN (no_bgp_listen_range, argv_find(argv, argc, "A.B.C.D/M", &idx); argv_find(argv, argc, "X:X::X:X/M", &idx); char *prefix = argv[idx]->arg; - argv_find(argv, argc, "WORD", &idx); + argv_find(argv, argc, "PGNAME", &idx); char *peergroup = argv[idx]->arg; /* Convert IP prefix string to struct prefix. */ @@ -6906,8 +7018,8 @@ DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd, as_t as = bgp->as; /* Auto-create assuming the same AS */ - ret = bgp_get(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT); + ret = bgp_get_vty(&bgp_default, &as, NULL, + BGP_INSTANCE_TYPE_DEFAULT); if (ret) { vty_out(vty, @@ -6992,8 +7104,8 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, bgp_default = bgp_get_default(); if (!bgp_default) { /* Auto-create assuming the same AS */ - ret = bgp_get(&bgp_default, &as, NULL, - BGP_INSTANCE_TYPE_DEFAULT); + ret = bgp_get_vty(&bgp_default, &as, NULL, + BGP_INSTANCE_TYPE_DEFAULT); if (ret) { vty_out(vty, @@ -7008,7 +7120,7 @@ DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd, vrf_bgp = bgp_default; else /* Auto-create assuming the same AS */ - ret = bgp_get(&vrf_bgp, &as, import_name, bgp_type); + ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type); if (ret) { vty_out(vty, @@ -7581,7 +7693,7 @@ DEFUN (show_bgp_vrfs, if (!uj && count == 1) { vty_out(vty, "%4s %-5s %-16s %9s %10s %-37s\n", - "Type", "Id", "routerId", "#PeersVfg", + "Type", "Id", "routerId", "#PeersCfg", "#PeersEstb", "Name"); vty_out(vty, "%11s %-16s %-21s %-6s\n", " ", "L3-VNI", "RouterMAC", "Interface"); @@ -9675,9 +9787,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_neigh, "bgpTimerConfiguredKeepAliveIntervalMsecs", p->keepalive * 1000); - } else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) - || (bgp->default_keepalive - != BGP_DEFAULT_KEEPALIVE)) { + } else if ((bgp->default_holdtime != SAVE_BGP_HOLDTIME) + || (bgp->default_keepalive != SAVE_BGP_KEEPALIVE)) { json_object_int_add(json_neigh, "bgpTimerConfiguredHoldTimeMsecs", bgp->default_holdtime); @@ -9739,9 +9850,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, p->holdtime); vty_out(vty, ", keepalive interval is %d seconds\n", p->keepalive); - } else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) - || (bgp->default_keepalive - != BGP_DEFAULT_KEEPALIVE)) { + } else if ((bgp->default_holdtime != SAVE_BGP_HOLDTIME) + || (bgp->default_keepalive != SAVE_BGP_KEEPALIVE)) { vty_out(vty, " Configured hold time is %d", bgp->default_holdtime); vty_out(vty, ", keepalive interval is %d seconds\n", @@ -12759,8 +12869,8 @@ DEFUN (no_bgp_redistribute_ipv6, return bgp_redistribute_unset(bgp, AFI_IP6, type, 0); } -void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) +static void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi) { int i; @@ -12797,8 +12907,86 @@ void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, } } +/* peer-group helpers for config-write */ + +static bool peergroup_flag_check(struct peer *peer, uint32_t flag) +{ + if (!peer_group_active(peer)) { + if (CHECK_FLAG(peer->flags_invert, flag)) + return !CHECK_FLAG(peer->flags, flag); + else + return !!CHECK_FLAG(peer->flags, flag); + } + + return !!CHECK_FLAG(peer->flags_override, flag); +} + +static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, + uint32_t flag) +{ + if (!peer_group_active(peer)) { + if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag)) + return !peer_af_flag_check(peer, afi, safi, flag); + else + return !!peer_af_flag_check(peer, afi, safi, flag); + } + + return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag); +} + +static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi, + uint8_t type, int direct) +{ + struct bgp_filter *filter; + + if (peer_group_active(peer)) + return !!CHECK_FLAG(peer->filter_override[afi][safi][direct], + type); + + filter = &peer->filter[afi][safi]; + switch (type) { + case PEER_FT_DISTRIBUTE_LIST: + return !!(filter->dlist[direct].name); + case PEER_FT_FILTER_LIST: + return !!(filter->aslist[direct].name); + case PEER_FT_PREFIX_LIST: + return !!(filter->plist[direct].name); + case PEER_FT_ROUTE_MAP: + return !!(filter->map[direct].name); + case PEER_FT_UNSUPPRESS_MAP: + return !!(filter->usmap.name); + default: + return false; + } +} + +/* Return true if the addpath type is set for peer and different from + * peer-group. + */ +static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi) +{ + enum bgp_addpath_strat type, g_type; + + type = peer->addpath_type[afi][safi]; + + if (type != BGP_ADDPATH_NONE) { + if (peer_group_active(peer)) { + g_type = peer->group->conf->addpath_type[afi][safi]; + + if (type != g_type) + return 1; + else + return 0; + } + + return 1; + } + + return 0; +} + /* This is part of the address-family block (unicast only) */ -void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, +static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, afi_t afi) { int indent = 2; @@ -12898,6 +13086,970 @@ void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, } } +static void bgp_config_write_filter(struct vty *vty, struct peer *peer, + afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + char *addr; + + addr = peer->host; + filter = &peer->filter[afi][safi]; + + /* distribute-list. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s distribute-list %s in\n", addr, + filter->dlist[FILTER_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, + FILTER_OUT)) + vty_out(vty, " neighbor %s distribute-list %s out\n", addr, + filter->dlist[FILTER_OUT].name); + + /* prefix-list. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s prefix-list %s in\n", addr, + filter->plist[FILTER_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, + FILTER_OUT)) + vty_out(vty, " neighbor %s prefix-list %s out\n", addr, + filter->plist[FILTER_OUT].name); + + /* route-map. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN)) + vty_out(vty, " neighbor %s route-map %s in\n", addr, + filter->map[RMAP_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, + RMAP_OUT)) + vty_out(vty, " neighbor %s route-map %s out\n", addr, + filter->map[RMAP_OUT].name); + + /* unsuppress-map */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0)) + vty_out(vty, " neighbor %s unsuppress-map %s\n", addr, + filter->usmap.name); + + /* filter-list. */ + if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s filter-list %s in\n", addr, + filter->aslist[FILTER_IN].name); + + if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, + FILTER_OUT)) + vty_out(vty, " neighbor %s filter-list %s out\n", addr, + filter->aslist[FILTER_OUT].name); +} + +/* BGP peer configuration display function. */ +static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, + struct peer *peer) +{ + struct peer *g_peer = NULL; + char buf[SU_ADDRSTRLEN]; + char *addr; + int if_pg_printed = false; + int if_ras_printed = false; + + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor(peer)) + return; + + if (peer->conf_if) + addr = peer->conf_if; + else + addr = peer->host; + + /************************************ + ****** Global to the neighbor ****** + ************************************/ + if (peer->conf_if) { + if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY)) + vty_out(vty, " neighbor %s interface v6only", addr); + else + vty_out(vty, " neighbor %s interface", addr); + + if (peer_group_active(peer)) { + vty_out(vty, " peer-group %s", peer->group->name); + if_pg_printed = true; + } else if (peer->as_type == AS_SPECIFIED) { + vty_out(vty, " remote-as %u", peer->as); + if_ras_printed = true; + } else if (peer->as_type == AS_INTERNAL) { + vty_out(vty, " remote-as internal"); + if_ras_printed = true; + } else if (peer->as_type == AS_EXTERNAL) { + vty_out(vty, " remote-as external"); + if_ras_printed = true; + } + + vty_out(vty, "\n"); + } + + /* remote-as and peer-group */ + /* peer is a member of a peer-group */ + if (peer_group_active(peer)) { + g_peer = peer->group->conf; + + if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) { + if (peer->as_type == AS_SPECIFIED) { + vty_out(vty, " neighbor %s remote-as %u\n", + addr, peer->as); + } else if (peer->as_type == AS_INTERNAL) { + vty_out(vty, + " neighbor %s remote-as internal\n", + addr); + } else if (peer->as_type == AS_EXTERNAL) { + vty_out(vty, + " neighbor %s remote-as external\n", + addr); + } + } + + /* For swpX peers we displayed the peer-group + * via 'neighbor swpX interface peer-group PGNAME' */ + if (!if_pg_printed) + vty_out(vty, " neighbor %s peer-group %s\n", addr, + peer->group->name); + } + + /* peer is NOT a member of a peer-group */ + else { + /* peer is a peer-group, declare the peer-group */ + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + vty_out(vty, " neighbor %s peer-group\n", addr); + } + + if (!if_ras_printed) { + if (peer->as_type == AS_SPECIFIED) { + vty_out(vty, " neighbor %s remote-as %u\n", + addr, peer->as); + } else if (peer->as_type == AS_INTERNAL) { + vty_out(vty, + " neighbor %s remote-as internal\n", + addr); + } else if (peer->as_type == AS_EXTERNAL) { + vty_out(vty, + " neighbor %s remote-as external\n", + addr); + } + } + } + + /* local-as */ + if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) { + vty_out(vty, " neighbor %s local-as %u", addr, + peer->change_local_as); + if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND)) + vty_out(vty, " no-prepend"); + if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS)) + vty_out(vty, " replace-as"); + vty_out(vty, "\n"); + } + + /* description */ + if (peer->desc) { + vty_out(vty, " neighbor %s description %s\n", addr, peer->desc); + } + + /* shutdown */ + if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) { + if (peer->tx_shutdown_message) + vty_out(vty, " neighbor %s shutdown message %s\n", addr, + peer->tx_shutdown_message); + else + vty_out(vty, " neighbor %s shutdown\n", addr); + } + + /* bfd */ + if (peer->bfd_info) { + if (!peer_group_active(peer) || !g_peer->bfd_info) { + bgp_bfd_peer_config_write(vty, peer, addr); + } + } + + /* password */ + if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD)) + vty_out(vty, " neighbor %s password %s\n", addr, + peer->password); + + /* neighbor solo */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) { + if (!peer_group_active(peer)) { + vty_out(vty, " neighbor %s solo\n", addr); + } + } + + /* BGP port */ + if (peer->port != BGP_PORT_DEFAULT) { + vty_out(vty, " neighbor %s port %d\n", addr, peer->port); + } + + /* Local interface name */ + if (peer->ifname) { + vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname); + } + + /* passive */ + if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE)) + vty_out(vty, " neighbor %s passive\n", addr); + + /* ebgp-multihop */ + if (peer->sort != BGP_PEER_IBGP && peer->ttl != BGP_DEFAULT_TTL + && !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL)) { + if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) { + vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr, + peer->ttl); + } + } + + /* ttl-security hops */ + if (peer->gtsm_hops != 0) { + if (!peer_group_active(peer) + || g_peer->gtsm_hops != peer->gtsm_hops) { + vty_out(vty, " neighbor %s ttl-security hops %d\n", + addr, peer->gtsm_hops); + } + } + + /* disable-connected-check */ + if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK)) + vty_out(vty, " neighbor %s disable-connected-check\n", addr); + + /* enforce-first-as */ + if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS)) + vty_out(vty, " neighbor %s enforce-first-as\n", addr); + + /* update-source */ + if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) { + if (peer->update_source) + vty_out(vty, " neighbor %s update-source %s\n", addr, + sockunion2str(peer->update_source, buf, + SU_ADDRSTRLEN)); + else if (peer->update_if) + vty_out(vty, " neighbor %s update-source %s\n", addr, + peer->update_if); + } + + /* advertisement-interval */ + if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV)) + vty_out(vty, " neighbor %s advertisement-interval %u\n", addr, + peer->routeadv); + + /* timers */ + if (peergroup_flag_check(peer, PEER_FLAG_TIMER)) + vty_out(vty, " neighbor %s timers %u %u\n", addr, + peer->keepalive, peer->holdtime); + + /* timers connect */ + if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT)) + vty_out(vty, " neighbor %s timers connect %u\n", addr, + peer->connect); + /* need special-case handling for changed default values due to + * config profile / version (because there is no "timers bgp connect" + * command, we need to save this per-peer :/) + */ + else if (!peer_group_active(peer) && !peer->connect && + peer->bgp->default_connect_retry != SAVE_BGP_CONNECT_RETRY) + vty_out(vty, " neighbor %s timers connect %u\n", addr, + peer->bgp->default_connect_retry); + + /* capability dynamic */ + if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY)) + vty_out(vty, " neighbor %s capability dynamic\n", addr); + + /* capability extended-nexthop */ + if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) { + if (!peer->conf_if) { + if (CHECK_FLAG(peer->flags_invert, + PEER_FLAG_CAPABILITY_ENHE)) + vty_out(vty, + " no neighbor %s capability extended-nexthop\n", + addr); + else + vty_out(vty, + " neighbor %s capability extended-nexthop\n", + addr); + } + } + + /* dont-capability-negotiation */ + if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY)) + vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr); + + /* override-capability */ + if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY)) + vty_out(vty, " neighbor %s override-capability\n", addr); + + /* strict-capability-match */ + if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH)) + vty_out(vty, " neighbor %s strict-capability-match\n", addr); + + /* Sender side AS path loop detection. */ + if (peer->as_path_loop_detection) + vty_out(vty, " neighbor %s sender-as-path-loop-detection\n", + addr); +} + +/* BGP peer configuration display function. */ +static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, + struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer *g_peer = NULL; + char *addr; + bool flag_scomm, flag_secomm, flag_slcomm; + + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor(peer)) + return; + + if (peer->conf_if) + addr = peer->conf_if; + else + addr = peer->host; + + /************************************ + ****** Per AF to the neighbor ****** + ************************************/ + if (peer_group_active(peer)) { + g_peer = peer->group->conf; + + /* If the peer-group is active but peer is not, print a 'no + * activate' */ + if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) { + vty_out(vty, " no neighbor %s activate\n", addr); + } + + /* If the peer-group is not active but peer is, print an + 'activate' */ + else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) { + vty_out(vty, " neighbor %s activate\n", addr); + } + } else { + if (peer->afc[afi][safi]) { + if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { + if (bgp_flag_check(bgp, + BGP_FLAG_NO_DEFAULT_IPV4)) { + vty_out(vty, " neighbor %s activate\n", + addr); + } + } else + vty_out(vty, " neighbor %s activate\n", addr); + } else { + if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { + if (!bgp_flag_check(bgp, + BGP_FLAG_NO_DEFAULT_IPV4)) { + vty_out(vty, + " no neighbor %s activate\n", + addr); + } + } + } + } + + /* addpath TX knobs */ + if (peergroup_af_addpath_check(peer, afi, safi)) { + switch (peer->addpath_type[afi][safi]) { + case BGP_ADDPATH_ALL: + vty_out(vty, " neighbor %s addpath-tx-all-paths\n", + addr); + break; + case BGP_ADDPATH_BEST_PER_AS: + vty_out(vty, + " neighbor %s addpath-tx-bestpath-per-AS\n", + addr); + break; + case BGP_ADDPATH_MAX: + case BGP_ADDPATH_NONE: + break; + } + } + + /* ORF capability. */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_RM)) { + vty_out(vty, " neighbor %s capability orf prefix-list", addr); + + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_SM) + && peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_RM)) + vty_out(vty, " both"); + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_ORF_PREFIX_SM)) + vty_out(vty, " send"); + else + vty_out(vty, " receive"); + vty_out(vty, "\n"); + } + + /* BGP flag dampening. */ + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_DAMPENING)) + bgp_config_write_damp(vty, afi, safi); + + /* Route reflector client. */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REFLECTOR_CLIENT)) { + vty_out(vty, " neighbor %s route-reflector-client\n", addr); + } + + /* next-hop-self force */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_FORCE_NEXTHOP_SELF)) { + vty_out(vty, " neighbor %s next-hop-self force\n", addr); + } + + /* next-hop-self */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) { + vty_out(vty, " neighbor %s next-hop-self\n", addr); + } + + /* remove-private-AS */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) { + vty_out(vty, " neighbor %s remove-private-AS all replace-AS\n", + addr); + } + + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) { + vty_out(vty, " neighbor %s remove-private-AS replace-AS\n", + addr); + } + + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) { + vty_out(vty, " neighbor %s remove-private-AS all\n", addr); + } + + else if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_REMOVE_PRIVATE_AS)) { + vty_out(vty, " neighbor %s remove-private-AS\n", addr); + } + + /* as-override */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) { + vty_out(vty, " neighbor %s as-override\n", addr); + } + + /* send-community print. */ + flag_scomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_COMMUNITY); + flag_secomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY); + flag_slcomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_LARGE_COMMUNITY); + + if (flag_scomm && flag_secomm && flag_slcomm) { + vty_out(vty, " no neighbor %s send-community all\n", addr); + } else { + if (flag_scomm) + vty_out(vty, " no neighbor %s send-community\n", addr); + if (flag_secomm) + vty_out(vty, + " no neighbor %s send-community extended\n", + addr); + + if (flag_slcomm) + vty_out(vty, " no neighbor %s send-community large\n", + addr); + } + + /* Default information */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_DEFAULT_ORIGINATE)) { + vty_out(vty, " neighbor %s default-originate", addr); + + if (peer->default_rmap[afi][safi].name) + vty_out(vty, " route-map %s", + peer->default_rmap[afi][safi].name); + + vty_out(vty, "\n"); + } + + /* Soft reconfiguration inbound. */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) { + vty_out(vty, " neighbor %s soft-reconfiguration inbound\n", + addr); + } + + /* maximum-prefix. */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) { + vty_out(vty, " neighbor %s maximum-prefix %" PRIu32, addr, + peer->pmax[afi][safi]); + + if (peer->pmax_threshold[afi][safi] + != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) + vty_out(vty, " %u", peer->pmax_threshold[afi][safi]); + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_MAX_PREFIX_WARNING)) + vty_out(vty, " warning-only"); + if (peer->pmax_restart[afi][safi]) + vty_out(vty, " restart %u", + peer->pmax_restart[afi][safi]); + + vty_out(vty, "\n"); + } + + /* Route server client. */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_RSERVER_CLIENT)) { + vty_out(vty, " neighbor %s route-server-client\n", addr); + } + + /* Nexthop-local unchanged. */ + if (peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) { + vty_out(vty, " neighbor %s nexthop-local unchanged\n", addr); + } + + /* allowas-in <1-10> */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) { + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_ALLOWAS_IN_ORIGIN)) { + vty_out(vty, " neighbor %s allowas-in origin\n", addr); + } else if (peer->allowas_in[afi][safi] == 3) { + vty_out(vty, " neighbor %s allowas-in\n", addr); + } else { + vty_out(vty, " neighbor %s allowas-in %d\n", addr, + peer->allowas_in[afi][safi]); + } + } + + /* weight */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT)) + vty_out(vty, " neighbor %s weight %lu\n", addr, + peer->weight[afi][safi]); + + /* Filter. */ + bgp_config_write_filter(vty, peer, afi, safi); + + /* atribute-unchanged. */ + if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED) + || (safi != SAFI_EVPN + && peer_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED)) + || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) { + + if (!peer_group_active(peer) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED) + || peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_MED_UNCHANGED)) { + + vty_out(vty, + " neighbor %s attribute-unchanged%s%s%s\n", + addr, + peer_af_flag_check(peer, afi, safi, + PEER_FLAG_AS_PATH_UNCHANGED) + ? " as-path" + : "", + peer_af_flag_check(peer, afi, safi, + PEER_FLAG_NEXTHOP_UNCHANGED) + ? " next-hop" + : "", + peer_af_flag_check(peer, afi, safi, + PEER_FLAG_MED_UNCHANGED) + ? " med" + : ""); + } + } +} + +/* Address family based peer configuration display. */ +static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) +{ + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; + + + vty_frame(vty, " !\n address-family "); + if (afi == AFI_IP) { + if (safi == SAFI_UNICAST) + vty_frame(vty, "ipv4 unicast"); + else if (safi == SAFI_LABELED_UNICAST) + vty_frame(vty, "ipv4 labeled-unicast"); + else if (safi == SAFI_MULTICAST) + vty_frame(vty, "ipv4 multicast"); + else if (safi == SAFI_MPLS_VPN) + vty_frame(vty, "ipv4 vpn"); + else if (safi == SAFI_ENCAP) + vty_frame(vty, "ipv4 encap"); + else if (safi == SAFI_FLOWSPEC) + vty_frame(vty, "ipv4 flowspec"); + } else if (afi == AFI_IP6) { + if (safi == SAFI_UNICAST) + vty_frame(vty, "ipv6 unicast"); + else if (safi == SAFI_LABELED_UNICAST) + vty_frame(vty, "ipv6 labeled-unicast"); + else if (safi == SAFI_MULTICAST) + vty_frame(vty, "ipv6 multicast"); + else if (safi == SAFI_MPLS_VPN) + vty_frame(vty, "ipv6 vpn"); + else if (safi == SAFI_ENCAP) + vty_frame(vty, "ipv6 encap"); + else if (safi == SAFI_FLOWSPEC) + vty_frame(vty, "ipv6 flowspec"); + } else if (afi == AFI_L2VPN) { + if (safi == SAFI_EVPN) + vty_frame(vty, "l2vpn evpn"); + } + vty_frame(vty, "\n"); + + bgp_config_write_distance(vty, bgp, afi, safi); + + bgp_config_write_network(vty, bgp, afi, safi); + + bgp_config_write_redistribute(vty, bgp, afi, safi); + + for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) + bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi); + + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor(peer)) + continue; + + /* Do not display doppelganger peers */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + bgp_config_write_peer_af(vty, bgp, peer, afi, safi); + } + + bgp_config_write_maxpaths(vty, bgp, afi, safi); + bgp_config_write_table_map(vty, bgp, afi, safi); + + if (safi == SAFI_EVPN) + bgp_config_write_evpn_info(vty, bgp, afi, safi); + + if (safi == SAFI_FLOWSPEC) + bgp_fs_config_write_pbr(vty, bgp, afi, safi); + + if (safi == SAFI_UNICAST) { + bgp_vpn_policy_config_write_afi(vty, bgp, afi); + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { + + vty_out(vty, " export vpn\n"); + } + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) { + + vty_out(vty, " import vpn\n"); + } + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_VRF_IMPORT)) { + char *name; + + for (ALL_LIST_ELEMENTS_RO( + bgp->vpn_policy[afi].import_vrf, node, + name)) + vty_out(vty, " import vrf %s\n", name); + } + } + + vty_endframe(vty, " exit-address-family\n"); +} + +int bgp_config_write(struct vty *vty) +{ + struct bgp *bgp; + struct peer_group *group; + struct peer *peer; + struct listnode *node, *nnode; + struct listnode *mnode, *mnnode; + + if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER) + vty_out(vty, "bgp route-map delay-timer %u\n", + bm->rmap_update_timer); + + /* BGP configuration. */ + for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { + + /* skip all auto created vrf as they dont have user config */ + if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) + continue; + + /* Router bgp ASN */ + vty_out(vty, "router bgp %u", bgp->as); + + if (bgp->name) + vty_out(vty, " %s %s", + (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) + ? "view" : "vrf", bgp->name); + vty_out(vty, "\n"); + + /* BGP fast-external-failover. */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) + vty_out(vty, " no bgp fast-external-failover\n"); + + /* BGP router ID. */ + if (bgp->router_id_static.s_addr != 0) + vty_out(vty, " bgp router-id %s\n", + inet_ntoa(bgp->router_id_static)); + + /* BGP log-neighbor-changes. */ + if (!!bgp_flag_check(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES) + != SAVE_BGP_LOG_NEIGHBOR_CHANGES) + vty_out(vty, " %sbgp log-neighbor-changes\n", + bgp_flag_check(bgp, + BGP_FLAG_LOG_NEIGHBOR_CHANGES) + ? "" + : "no "); + + /* BGP configuration. */ + if (bgp_flag_check(bgp, BGP_FLAG_ALWAYS_COMPARE_MED)) + vty_out(vty, " bgp always-compare-med\n"); + + /* RFC8212 default eBGP policy. */ + if (bgp->ebgp_requires_policy + == DEFAULT_EBGP_POLICY_ENABLED) + vty_out(vty, " bgp ebgp-requires-policy\n"); + + /* draft-ietf-idr-deprecate-as-set-confed-set */ + if (bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED) + vty_out(vty, " bgp reject-as-sets\n"); + + /* BGP default ipv4-unicast. */ + if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4)) + vty_out(vty, " no bgp default ipv4-unicast\n"); + + /* BGP default local-preference. */ + if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF) + vty_out(vty, " bgp default local-preference %u\n", + bgp->default_local_pref); + + /* BGP default show-hostname */ + if (!!bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME) + != SAVE_BGP_SHOW_HOSTNAME) + vty_out(vty, " %sbgp default show-hostname\n", + bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME) + ? "" + : "no "); + + /* BGP default subgroup-pkt-queue-max. */ + if (bgp->default_subgroup_pkt_queue_max + != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX) + vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n", + bgp->default_subgroup_pkt_queue_max); + + /* BGP client-to-client reflection. */ + if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT)) + vty_out(vty, " no bgp client-to-client reflection\n"); + + /* BGP cluster ID. */ + if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID)) + vty_out(vty, " bgp cluster-id %s\n", + inet_ntoa(bgp->cluster_id)); + + /* Disable ebgp connected nexthop check */ + if (bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) + vty_out(vty, + " bgp disable-ebgp-connected-route-check\n"); + + /* Confederation identifier*/ + if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) + vty_out(vty, " bgp confederation identifier %u\n", + bgp->confed_id); + + /* Confederation peer */ + if (bgp->confed_peers_cnt > 0) { + int i; + + vty_out(vty, " bgp confederation peers"); + + for (i = 0; i < bgp->confed_peers_cnt; i++) + vty_out(vty, " %u", bgp->confed_peers[i]); + + vty_out(vty, "\n"); + } + + /* BGP deterministic-med. */ + if (!!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) + != SAVE_BGP_DETERMINISTIC_MED) + vty_out(vty, " %sbgp deterministic-med\n", + bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) + ? "" + : "no "); + + /* BGP update-delay. */ + bgp_config_write_update_delay(vty, bgp); + + if (bgp->v_maxmed_onstartup + != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) { + vty_out(vty, " bgp max-med on-startup %u", + bgp->v_maxmed_onstartup); + if (bgp->maxmed_onstartup_value + != BGP_MAXMED_VALUE_DEFAULT) + vty_out(vty, " %u", + bgp->maxmed_onstartup_value); + vty_out(vty, "\n"); + } + if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) { + vty_out(vty, " bgp max-med administrative"); + if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT) + vty_out(vty, " %u", bgp->maxmed_admin_value); + vty_out(vty, "\n"); + } + + /* write quanta */ + bgp_config_write_wpkt_quanta(vty, bgp); + /* read quanta */ + bgp_config_write_rpkt_quanta(vty, bgp); + + /* coalesce time */ + bgp_config_write_coalesce_time(vty, bgp); + + /* BGP graceful-restart. */ + if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME) + vty_out(vty, + " bgp graceful-restart stalepath-time %u\n", + bgp->stalepath_time); + if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME) + vty_out(vty, " bgp graceful-restart restart-time %u\n", + bgp->restart_time); + if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) + vty_out(vty, " bgp graceful-restart\n"); + + /* BGP graceful-shutdown */ + if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) + vty_out(vty, " bgp graceful-shutdown\n"); + + /* BGP graceful-restart Preserve State F bit. */ + if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD)) + vty_out(vty, + " bgp graceful-restart preserve-fw-state\n"); + + /* BGP bestpath method. */ + if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE)) + vty_out(vty, " bgp bestpath as-path ignore\n"); + if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED)) + vty_out(vty, " bgp bestpath as-path confed\n"); + + if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { + if (bgp_flag_check(bgp, + BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { + vty_out(vty, + " bgp bestpath as-path multipath-relax as-set\n"); + } else { + vty_out(vty, + " bgp bestpath as-path multipath-relax\n"); + } + } + + if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { + vty_out(vty, + " bgp route-reflector allow-outbound-policy\n"); + } + if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID)) + vty_out(vty, " bgp bestpath compare-routerid\n"); + if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED) + || bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) { + vty_out(vty, " bgp bestpath med"); + if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)) + vty_out(vty, " confed"); + if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) + vty_out(vty, " missing-as-worst"); + vty_out(vty, "\n"); + } + + /* BGP network import check. */ + if (!!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) + != SAVE_BGP_IMPORT_CHECK) + vty_out(vty, " %sbgp network import-check\n", + bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) + ? "" + : "no "); + + /* BGP timers configuration. */ + if (bgp->default_keepalive != SAVE_BGP_KEEPALIVE + && bgp->default_holdtime != SAVE_BGP_HOLDTIME) + vty_out(vty, " timers bgp %u %u\n", + bgp->default_keepalive, bgp->default_holdtime); + + /* peer-group */ + for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) { + bgp_config_write_peer_global(vty, bgp, group->conf); + } + + /* Normal neighbor configuration. */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + bgp_config_write_peer_global(vty, bgp, peer); + } + + /* listen range and limit for dynamic BGP neighbors */ + bgp_config_write_listen(vty, bgp); + + /* + * BGP default autoshutdown neighbors + * + * This must be placed after any peer and peer-group + * configuration, to avoid setting all peers to shutdown after + * a daemon restart, which is undesired behavior. (see #2286) + */ + if (bgp->autoshutdown) + vty_out(vty, " bgp default shutdown\n"); + + /* IPv4 unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); + + /* IPv4 multicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST); + + /* IPv4 labeled-unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST); + + /* IPv4 VPN configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN); + + /* ENCAPv4 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP); + + /* FLOWSPEC v4 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC); + + /* IPv6 unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST); + + /* IPv6 multicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST); + + /* IPv6 labeled-unicast configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, + SAFI_LABELED_UNICAST); + + /* IPv6 VPN configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN); + + /* ENCAPv6 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP); + + /* FLOWSPEC v6 configuration. */ + bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC); + + /* EVPN configuration. */ + bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN); + + hook_call(bgp_inst_config_write, bgp, vty); + +#if ENABLE_BGP_VNC + bgp_rfapi_cfg_write(vty, bgp); +#endif + + vty_out(vty, "!\n"); + } + return 0; +} + /* BGP node structure. */ static struct cmd_node bgp_node = { @@ -13136,6 +14288,10 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_ebgp_requires_policy_cmd); install_element(BGP_NODE, &no_bgp_ebgp_requires_policy_cmd); + /* bgp reject-as-sets */ + install_element(BGP_NODE, &bgp_reject_as_sets_cmd); + install_element(BGP_NODE, &no_bgp_reject_as_sets_cmd); + /* "bgp deterministic-med" commands */ install_element(BGP_NODE, &bgp_deterministic_med_cmd); install_element(BGP_NODE, &no_bgp_deterministic_med_cmd); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 27b5ea47b2..5f3ce9cd8e 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -21,6 +21,8 @@ #ifndef _QUAGGA_BGP_VTY_H #define _QUAGGA_BGP_VTY_H +#include "bgpd/bgpd.h" + struct bgp; #define BGP_INSTANCE_HELP_STR "BGP view\nBGP VRF\nView/VRF name\n" @@ -46,6 +48,8 @@ struct bgp; extern void bgp_vty_init(void); extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json); +extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, + enum bgp_instance_type inst_type); extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp); @@ -72,6 +76,5 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, bool use_json); extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, safi_t safi, bool show_failed, bool use_json); -extern void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, - afi_t afi); + #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index e886733ced..069c53d7df 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1072,7 +1072,7 @@ static int update_ipv4nh_for_route_install(int nh_othervrf, */ if (is_evpn) { api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; - api_nh->onlink = true; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; } else if (nh_othervrf && api_nh->gate.ipv4.s_addr == INADDR_ANY) { @@ -1098,7 +1098,7 @@ update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp, if (is_evpn) { api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; - api_nh->onlink = true; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); api_nh->ifindex = nh_bgp->l3vni_svi_ifindex; } else if (nh_othervrf) { if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) { @@ -1347,6 +1347,8 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, has_valid_label = 1; label = label_pton(&mpinfo->extra->label[0]); + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL); + api_nh->label_num = 1; api_nh->labels[0] = label; } @@ -1355,11 +1357,6 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, valid_nh_count++; } - - /* if this is a evpn route we don't have to include the label */ - if (has_valid_label && !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))) - SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); - /* * When we create an aggregate route we must also * install a Null0 route in the RIB, so overwrite @@ -1693,7 +1690,7 @@ int bgp_redistribute_metric_set(struct bgp *bgp, struct bgp_redist *red, struct attr *old_attr; struct attr new_attr; - bgp_attr_dup(&new_attr, pi->attr); + new_attr = *pi->attr; new_attr.med = red->redist_metric; old_attr = pi->attr; pi->attr = bgp_attr_intern(&new_attr); diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index b912870b80..62c311cc1d 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -32,10 +32,6 @@ extern void bgp_zebra_destroy(void); extern int bgp_zebra_get_table_range(uint32_t chunk_size, uint32_t *start, uint32_t *end); extern int bgp_if_update_all(void); -extern void bgp_config_write_maxpaths(struct vty *, struct bgp *, afi_t, - safi_t); -extern void bgp_config_write_redistribute(struct vty *, struct bgp *, afi_t, - safi_t); extern void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, struct bgp_path_info *path, struct bgp *bgp, afi_t afi, safi_t safi); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index a74923c302..9b0e81491a 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -95,9 +95,6 @@ DEFINE_QOBJ_TYPE(bgp_master) DEFINE_QOBJ_TYPE(bgp) DEFINE_QOBJ_TYPE(peer) DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp)) -DEFINE_HOOK(bgp_inst_config_write, - (struct bgp *bgp, struct vty *vty), - (bgp, vty)) /* BGP process wide configuration. */ static struct bgp_master bgp_master; @@ -407,19 +404,23 @@ time_t bgp_clock(void) } /* BGP timer configuration. */ -int bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime) +int bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime, + uint32_t connect_retry) { bgp->default_keepalive = (keepalive < holdtime / 3 ? keepalive : holdtime / 3); bgp->default_holdtime = holdtime; + bgp->default_connect_retry = connect_retry; return 0; } +/* mostly for completeness - CLI uses its own defaults */ int bgp_timers_unset(struct bgp *bgp) { bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; + bgp->default_connect_retry = BGP_DEFAULT_CONNECT_RETRY; return 0; } @@ -883,82 +884,6 @@ void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi, COND_FLAG(peer->af_flags[afi][safi], flag, group_val); } -static bool peergroup_flag_check(struct peer *peer, uint32_t flag) -{ - if (!peer_group_active(peer)) { - if (CHECK_FLAG(peer->flags_invert, flag)) - return !CHECK_FLAG(peer->flags, flag); - else - return !!CHECK_FLAG(peer->flags, flag); - } - - return !!CHECK_FLAG(peer->flags_override, flag); -} - -static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, - uint32_t flag) -{ - if (!peer_group_active(peer)) { - if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag)) - return !peer_af_flag_check(peer, afi, safi, flag); - else - return !!peer_af_flag_check(peer, afi, safi, flag); - } - - return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag); -} - -static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi, - uint8_t type, int direct) -{ - struct bgp_filter *filter; - - if (peer_group_active(peer)) - return !!CHECK_FLAG(peer->filter_override[afi][safi][direct], - type); - - filter = &peer->filter[afi][safi]; - switch (type) { - case PEER_FT_DISTRIBUTE_LIST: - return !!(filter->dlist[direct].name); - case PEER_FT_FILTER_LIST: - return !!(filter->aslist[direct].name); - case PEER_FT_PREFIX_LIST: - return !!(filter->plist[direct].name); - case PEER_FT_ROUTE_MAP: - return !!(filter->map[direct].name); - case PEER_FT_UNSUPPRESS_MAP: - return !!(filter->usmap.name); - default: - return false; - } -} - -/* Return true if the addpath type is set for peer and different from - * peer-group. - */ -static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi) -{ - enum bgp_addpath_strat type, g_type; - - type = peer->addpath_type[afi][safi]; - - if (type != BGP_ADDPATH_NONE) { - if (peer_group_active(peer)) { - g_type = peer->group->conf->addpath_type[afi][safi]; - - if (type != g_type) - return 1; - else - return 0; - } - - return 1; - } - - return 0; -} - /* Check peer's AS number and determines if this peer is IBGP or EBGP */ static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer) { @@ -1197,7 +1122,7 @@ struct peer *peer_new(struct bgp *bgp) /* Set default value. */ peer->fd = -1; peer->v_start = BGP_INIT_START_TIMER; - peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + peer->v_connect = bgp->default_connect_retry; peer->status = Idle; peer->ostatus = Idle; peer->cur_event = peer->last_event = peer->last_major_event = 0; @@ -2496,7 +2421,7 @@ static void peer_group2peer_config_copy(struct peer_group *group, if (CHECK_FLAG(conf->flags, PEER_FLAG_TIMER_CONNECT)) peer->v_connect = conf->connect; else - peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + peer->v_connect = peer->bgp->default_connect_retry; } /* advertisement-interval apply */ @@ -2983,25 +2908,13 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF; bgp->default_subgroup_pkt_queue_max = BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX; - bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; - bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; + bgp_timers_unset(bgp); bgp->restart_time = BGP_DEFAULT_RESTART_TIME; bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT; bgp->dynamic_neighbors_count = 0; bgp->ebgp_requires_policy = DEFAULT_EBGP_POLICY_DISABLED; -#if DFLT_BGP_IMPORT_CHECK - bgp_flag_set(bgp, BGP_FLAG_IMPORT_CHECK); -#endif -#if DFLT_BGP_SHOW_HOSTNAME - bgp_flag_set(bgp, BGP_FLAG_SHOW_HOSTNAME); -#endif -#if DFLT_BGP_LOG_NEIGHBOR_CHANGES - bgp_flag_set(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); -#endif -#if DFLT_BGP_DETERMINISTIC_MED - bgp_flag_set(bgp, BGP_FLAG_DETERMINISTIC_MED); -#endif + bgp->reject_as_sets = BGP_REJECT_AS_SETS_DISABLED; bgp_addpath_init_bgp_data(&bgp->tx_addpath); bgp->as = *as; @@ -3254,7 +3167,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, bgp_zebra_instance_register(bgp); } - return BGP_SUCCESS; + return BGP_CREATED; } /* @@ -5058,7 +4971,7 @@ int peer_timers_connect_unset(struct peer *peer) if (peer->connect) peer->v_connect = peer->connect; else - peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + peer->v_connect = peer->bgp->default_connect_retry; /* Skip peer-group mechanics for regular peers. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) @@ -5076,7 +4989,7 @@ int peer_timers_connect_unset(struct peer *peer) /* Remove flag and configuration on peer-group member. */ UNSET_FLAG(member->flags, PEER_FLAG_TIMER_CONNECT); member->connect = 0; - member->v_connect = BGP_DEFAULT_CONNECT_RETRY; + member->v_connect = peer->bgp->default_connect_retry; } return 0; @@ -6898,958 +6811,6 @@ char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json, return buf; } -static void bgp_config_write_filter(struct vty *vty, struct peer *peer, - afi_t afi, safi_t safi) -{ - struct bgp_filter *filter; - char *addr; - - addr = peer->host; - filter = &peer->filter[afi][safi]; - - /* distribute-list. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, - FILTER_IN)) - vty_out(vty, " neighbor %s distribute-list %s in\n", addr, - filter->dlist[FILTER_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, - FILTER_OUT)) - vty_out(vty, " neighbor %s distribute-list %s out\n", addr, - filter->dlist[FILTER_OUT].name); - - /* prefix-list. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, - FILTER_IN)) - vty_out(vty, " neighbor %s prefix-list %s in\n", addr, - filter->plist[FILTER_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, - FILTER_OUT)) - vty_out(vty, " neighbor %s prefix-list %s out\n", addr, - filter->plist[FILTER_OUT].name); - - /* route-map. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN)) - vty_out(vty, " neighbor %s route-map %s in\n", addr, - filter->map[RMAP_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, - RMAP_OUT)) - vty_out(vty, " neighbor %s route-map %s out\n", addr, - filter->map[RMAP_OUT].name); - - /* unsuppress-map */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0)) - vty_out(vty, " neighbor %s unsuppress-map %s\n", addr, - filter->usmap.name); - - /* filter-list. */ - if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, - FILTER_IN)) - vty_out(vty, " neighbor %s filter-list %s in\n", addr, - filter->aslist[FILTER_IN].name); - - if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, - FILTER_OUT)) - vty_out(vty, " neighbor %s filter-list %s out\n", addr, - filter->aslist[FILTER_OUT].name); -} - -/* BGP peer configuration display function. */ -static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, - struct peer *peer) -{ - struct peer *g_peer = NULL; - char buf[SU_ADDRSTRLEN]; - char *addr; - int if_pg_printed = false; - int if_ras_printed = false; - - /* Skip dynamic neighbors. */ - if (peer_dynamic_neighbor(peer)) - return; - - if (peer->conf_if) - addr = peer->conf_if; - else - addr = peer->host; - - /************************************ - ****** Global to the neighbor ****** - ************************************/ - if (peer->conf_if) { - if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY)) - vty_out(vty, " neighbor %s interface v6only", addr); - else - vty_out(vty, " neighbor %s interface", addr); - - if (peer_group_active(peer)) { - vty_out(vty, " peer-group %s", peer->group->name); - if_pg_printed = true; - } else if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " remote-as %u", peer->as); - if_ras_printed = true; - } else if (peer->as_type == AS_INTERNAL) { - vty_out(vty, " remote-as internal"); - if_ras_printed = true; - } else if (peer->as_type == AS_EXTERNAL) { - vty_out(vty, " remote-as external"); - if_ras_printed = true; - } - - vty_out(vty, "\n"); - } - - /* remote-as and peer-group */ - /* peer is a member of a peer-group */ - if (peer_group_active(peer)) { - g_peer = peer->group->conf; - - if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) { - if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " neighbor %s remote-as %u\n", - addr, peer->as); - } else if (peer->as_type == AS_INTERNAL) { - vty_out(vty, - " neighbor %s remote-as internal\n", - addr); - } else if (peer->as_type == AS_EXTERNAL) { - vty_out(vty, - " neighbor %s remote-as external\n", - addr); - } - } - - /* For swpX peers we displayed the peer-group - * via 'neighbor swpX interface peer-group PGNAME' */ - if (!if_pg_printed) - vty_out(vty, " neighbor %s peer-group %s\n", addr, - peer->group->name); - } - - /* peer is NOT a member of a peer-group */ - else { - /* peer is a peer-group, declare the peer-group */ - if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - vty_out(vty, " neighbor %s peer-group\n", addr); - } - - if (!if_ras_printed) { - if (peer->as_type == AS_SPECIFIED) { - vty_out(vty, " neighbor %s remote-as %u\n", - addr, peer->as); - } else if (peer->as_type == AS_INTERNAL) { - vty_out(vty, - " neighbor %s remote-as internal\n", - addr); - } else if (peer->as_type == AS_EXTERNAL) { - vty_out(vty, - " neighbor %s remote-as external\n", - addr); - } - } - } - - /* local-as */ - if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) { - vty_out(vty, " neighbor %s local-as %u", addr, - peer->change_local_as); - if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND)) - vty_out(vty, " no-prepend"); - if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS)) - vty_out(vty, " replace-as"); - vty_out(vty, "\n"); - } - - /* description */ - if (peer->desc) { - vty_out(vty, " neighbor %s description %s\n", addr, peer->desc); - } - - /* shutdown */ - if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) { - if (peer->tx_shutdown_message) - vty_out(vty, " neighbor %s shutdown message %s\n", addr, - peer->tx_shutdown_message); - else - vty_out(vty, " neighbor %s shutdown\n", addr); - } - - /* bfd */ - if (peer->bfd_info) { - if (!peer_group_active(peer) || !g_peer->bfd_info) { - bgp_bfd_peer_config_write(vty, peer, addr); - } - } - - /* password */ - if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD)) - vty_out(vty, " neighbor %s password %s\n", addr, - peer->password); - - /* neighbor solo */ - if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) { - if (!peer_group_active(peer)) { - vty_out(vty, " neighbor %s solo\n", addr); - } - } - - /* BGP port */ - if (peer->port != BGP_PORT_DEFAULT) { - vty_out(vty, " neighbor %s port %d\n", addr, peer->port); - } - - /* Local interface name */ - if (peer->ifname) { - vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname); - } - - /* passive */ - if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE)) - vty_out(vty, " neighbor %s passive\n", addr); - - /* ebgp-multihop */ - if (peer->sort != BGP_PEER_IBGP && peer->ttl != BGP_DEFAULT_TTL - && !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL)) { - if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) { - vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr, - peer->ttl); - } - } - - /* ttl-security hops */ - if (peer->gtsm_hops != 0) { - if (!peer_group_active(peer) - || g_peer->gtsm_hops != peer->gtsm_hops) { - vty_out(vty, " neighbor %s ttl-security hops %d\n", - addr, peer->gtsm_hops); - } - } - - /* disable-connected-check */ - if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK)) - vty_out(vty, " neighbor %s disable-connected-check\n", addr); - - /* enforce-first-as */ - if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS)) - vty_out(vty, " neighbor %s enforce-first-as\n", addr); - - /* update-source */ - if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) { - if (peer->update_source) - vty_out(vty, " neighbor %s update-source %s\n", addr, - sockunion2str(peer->update_source, buf, - SU_ADDRSTRLEN)); - else if (peer->update_if) - vty_out(vty, " neighbor %s update-source %s\n", addr, - peer->update_if); - } - - /* advertisement-interval */ - if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV)) - vty_out(vty, " neighbor %s advertisement-interval %u\n", addr, - peer->routeadv); - - /* timers */ - if (peergroup_flag_check(peer, PEER_FLAG_TIMER)) - vty_out(vty, " neighbor %s timers %u %u\n", addr, - peer->keepalive, peer->holdtime); - - /* timers connect */ - if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT)) - vty_out(vty, " neighbor %s timers connect %u\n", addr, - peer->connect); - - /* capability dynamic */ - if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY)) - vty_out(vty, " neighbor %s capability dynamic\n", addr); - - /* capability extended-nexthop */ - if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) { - if (!peer->conf_if) { - if (CHECK_FLAG(peer->flags_invert, - PEER_FLAG_CAPABILITY_ENHE)) - vty_out(vty, - " no neighbor %s capability extended-nexthop\n", - addr); - else - vty_out(vty, - " neighbor %s capability extended-nexthop\n", - addr); - } - } - - /* dont-capability-negotiation */ - if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY)) - vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr); - - /* override-capability */ - if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY)) - vty_out(vty, " neighbor %s override-capability\n", addr); - - /* strict-capability-match */ - if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH)) - vty_out(vty, " neighbor %s strict-capability-match\n", addr); - - /* Sender side AS path loop detection. */ - if (peer->as_path_loop_detection) - vty_out(vty, " neighbor %s sender-as-path-loop-detection\n", - addr); -} - -/* BGP peer configuration display function. */ -static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, - struct peer *peer, afi_t afi, safi_t safi) -{ - struct peer *g_peer = NULL; - char *addr; - bool flag_scomm, flag_secomm, flag_slcomm; - - /* Skip dynamic neighbors. */ - if (peer_dynamic_neighbor(peer)) - return; - - if (peer->conf_if) - addr = peer->conf_if; - else - addr = peer->host; - - /************************************ - ****** Per AF to the neighbor ****** - ************************************/ - if (peer_group_active(peer)) { - g_peer = peer->group->conf; - - /* If the peer-group is active but peer is not, print a 'no - * activate' */ - if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) { - vty_out(vty, " no neighbor %s activate\n", addr); - } - - /* If the peer-group is not active but peer is, print an - 'activate' */ - else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) { - vty_out(vty, " neighbor %s activate\n", addr); - } - } else { - if (peer->afc[afi][safi]) { - if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { - if (bgp_flag_check(bgp, - BGP_FLAG_NO_DEFAULT_IPV4)) { - vty_out(vty, " neighbor %s activate\n", - addr); - } - } else - vty_out(vty, " neighbor %s activate\n", addr); - } else { - if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { - if (!bgp_flag_check(bgp, - BGP_FLAG_NO_DEFAULT_IPV4)) { - vty_out(vty, - " no neighbor %s activate\n", - addr); - } - } - } - } - - /* addpath TX knobs */ - if (peergroup_af_addpath_check(peer, afi, safi)) { - switch (peer->addpath_type[afi][safi]) { - case BGP_ADDPATH_ALL: - vty_out(vty, " neighbor %s addpath-tx-all-paths\n", - addr); - break; - case BGP_ADDPATH_BEST_PER_AS: - vty_out(vty, - " neighbor %s addpath-tx-bestpath-per-AS\n", - addr); - break; - case BGP_ADDPATH_MAX: - case BGP_ADDPATH_NONE: - break; - } - } - - /* ORF capability. */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_RM)) { - vty_out(vty, " neighbor %s capability orf prefix-list", addr); - - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_SM) - && peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_RM)) - vty_out(vty, " both"); - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_ORF_PREFIX_SM)) - vty_out(vty, " send"); - else - vty_out(vty, " receive"); - vty_out(vty, "\n"); - } - - /* BGP flag dampening. */ - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_DAMPENING)) - bgp_config_write_damp(vty, afi, safi); - - /* Route reflector client. */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REFLECTOR_CLIENT)) { - vty_out(vty, " neighbor %s route-reflector-client\n", addr); - } - - /* next-hop-self force */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_FORCE_NEXTHOP_SELF)) { - vty_out(vty, " neighbor %s next-hop-self force\n", addr); - } - - /* next-hop-self */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) { - vty_out(vty, " neighbor %s next-hop-self\n", addr); - } - - /* remove-private-AS */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) { - vty_out(vty, " neighbor %s remove-private-AS all replace-AS\n", - addr); - } - - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) { - vty_out(vty, " neighbor %s remove-private-AS replace-AS\n", - addr); - } - - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) { - vty_out(vty, " neighbor %s remove-private-AS all\n", addr); - } - - else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_REMOVE_PRIVATE_AS)) { - vty_out(vty, " neighbor %s remove-private-AS\n", addr); - } - - /* as-override */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) { - vty_out(vty, " neighbor %s as-override\n", addr); - } - - /* send-community print. */ - flag_scomm = peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_COMMUNITY); - flag_secomm = peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_EXT_COMMUNITY); - flag_slcomm = peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_LARGE_COMMUNITY); - - if (flag_scomm && flag_secomm && flag_slcomm) { - vty_out(vty, " no neighbor %s send-community all\n", addr); - } else { - if (flag_scomm) - vty_out(vty, " no neighbor %s send-community\n", addr); - if (flag_secomm) - vty_out(vty, - " no neighbor %s send-community extended\n", - addr); - - if (flag_slcomm) - vty_out(vty, " no neighbor %s send-community large\n", - addr); - } - - /* Default information */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_DEFAULT_ORIGINATE)) { - vty_out(vty, " neighbor %s default-originate", addr); - - if (peer->default_rmap[afi][safi].name) - vty_out(vty, " route-map %s", - peer->default_rmap[afi][safi].name); - - vty_out(vty, "\n"); - } - - /* Soft reconfiguration inbound. */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) { - vty_out(vty, " neighbor %s soft-reconfiguration inbound\n", - addr); - } - - /* maximum-prefix. */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) { - vty_out(vty, " neighbor %s maximum-prefix %" PRIu32, addr, - peer->pmax[afi][safi]); - - if (peer->pmax_threshold[afi][safi] - != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) - vty_out(vty, " %u", peer->pmax_threshold[afi][safi]); - if (peer_af_flag_check(peer, afi, safi, - PEER_FLAG_MAX_PREFIX_WARNING)) - vty_out(vty, " warning-only"); - if (peer->pmax_restart[afi][safi]) - vty_out(vty, " restart %u", - peer->pmax_restart[afi][safi]); - - vty_out(vty, "\n"); - } - - /* Route server client. */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_RSERVER_CLIENT)) { - vty_out(vty, " neighbor %s route-server-client\n", addr); - } - - /* Nexthop-local unchanged. */ - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) { - vty_out(vty, " neighbor %s nexthop-local unchanged\n", addr); - } - - /* allowas-in <1-10> */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) { - if (peer_af_flag_check(peer, afi, safi, - PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - vty_out(vty, " neighbor %s allowas-in origin\n", addr); - } else if (peer->allowas_in[afi][safi] == 3) { - vty_out(vty, " neighbor %s allowas-in\n", addr); - } else { - vty_out(vty, " neighbor %s allowas-in %d\n", addr, - peer->allowas_in[afi][safi]); - } - } - - /* weight */ - if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT)) - vty_out(vty, " neighbor %s weight %lu\n", addr, - peer->weight[afi][safi]); - - /* Filter. */ - bgp_config_write_filter(vty, peer, afi, safi); - - /* atribute-unchanged. */ - if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED) - || (safi != SAFI_EVPN - && peer_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED)) - || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) { - - if (!peer_group_active(peer) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_AS_PATH_UNCHANGED) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED) - || peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_MED_UNCHANGED)) { - - vty_out(vty, - " neighbor %s attribute-unchanged%s%s%s\n", - addr, - peer_af_flag_check(peer, afi, safi, - PEER_FLAG_AS_PATH_UNCHANGED) - ? " as-path" - : "", - peer_af_flag_check(peer, afi, safi, - PEER_FLAG_NEXTHOP_UNCHANGED) - ? " next-hop" - : "", - peer_af_flag_check(peer, afi, safi, - PEER_FLAG_MED_UNCHANGED) - ? " med" - : ""); - } - } -} - -/* Address family based peer configuration display. */ -static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, - safi_t safi) -{ - struct peer *peer; - struct peer_group *group; - struct listnode *node, *nnode; - - - vty_frame(vty, " !\n address-family "); - if (afi == AFI_IP) { - if (safi == SAFI_UNICAST) - vty_frame(vty, "ipv4 unicast"); - else if (safi == SAFI_LABELED_UNICAST) - vty_frame(vty, "ipv4 labeled-unicast"); - else if (safi == SAFI_MULTICAST) - vty_frame(vty, "ipv4 multicast"); - else if (safi == SAFI_MPLS_VPN) - vty_frame(vty, "ipv4 vpn"); - else if (safi == SAFI_ENCAP) - vty_frame(vty, "ipv4 encap"); - else if (safi == SAFI_FLOWSPEC) - vty_frame(vty, "ipv4 flowspec"); - } else if (afi == AFI_IP6) { - if (safi == SAFI_UNICAST) - vty_frame(vty, "ipv6 unicast"); - else if (safi == SAFI_LABELED_UNICAST) - vty_frame(vty, "ipv6 labeled-unicast"); - else if (safi == SAFI_MULTICAST) - vty_frame(vty, "ipv6 multicast"); - else if (safi == SAFI_MPLS_VPN) - vty_frame(vty, "ipv6 vpn"); - else if (safi == SAFI_ENCAP) - vty_frame(vty, "ipv6 encap"); - else if (safi == SAFI_FLOWSPEC) - vty_frame(vty, "ipv6 flowspec"); - } else if (afi == AFI_L2VPN) { - if (safi == SAFI_EVPN) - vty_frame(vty, "l2vpn evpn"); - } - vty_frame(vty, "\n"); - - bgp_config_write_distance(vty, bgp, afi, safi); - - bgp_config_write_network(vty, bgp, afi, safi); - - bgp_config_write_redistribute(vty, bgp, afi, safi); - - for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) - bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi); - - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - /* Skip dynamic neighbors. */ - if (peer_dynamic_neighbor(peer)) - continue; - - /* Do not display doppelganger peers */ - if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) - bgp_config_write_peer_af(vty, bgp, peer, afi, safi); - } - - bgp_config_write_maxpaths(vty, bgp, afi, safi); - bgp_config_write_table_map(vty, bgp, afi, safi); - - if (safi == SAFI_EVPN) - bgp_config_write_evpn_info(vty, bgp, afi, safi); - - if (safi == SAFI_FLOWSPEC) - bgp_fs_config_write_pbr(vty, bgp, afi, safi); - - if (safi == SAFI_UNICAST) { - bgp_vpn_policy_config_write_afi(vty, bgp, afi); - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { - - vty_out(vty, " export vpn\n"); - } - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) { - - vty_out(vty, " import vpn\n"); - } - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_VRF_TO_VRF_IMPORT)) { - char *name; - - for (ALL_LIST_ELEMENTS_RO( - bgp->vpn_policy[afi].import_vrf, node, - name)) - vty_out(vty, " import vrf %s\n", name); - } - } - - vty_endframe(vty, " exit-address-family\n"); -} - -int bgp_config_write(struct vty *vty) -{ - struct bgp *bgp; - struct peer_group *group; - struct peer *peer; - struct listnode *node, *nnode; - struct listnode *mnode, *mnnode; - - if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER) - vty_out(vty, "bgp route-map delay-timer %u\n", - bm->rmap_update_timer); - - /* BGP configuration. */ - for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { - - /* skip all auto created vrf as they dont have user config */ - if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO)) - continue; - - /* Router bgp ASN */ - vty_out(vty, "router bgp %u", bgp->as); - - if (bgp->name) - vty_out(vty, " %s %s", - (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) - ? "view" : "vrf", bgp->name); - vty_out(vty, "\n"); - - /* BGP fast-external-failover. */ - if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) - vty_out(vty, " no bgp fast-external-failover\n"); - - /* BGP router ID. */ - if (bgp->router_id_static.s_addr != 0) - vty_out(vty, " bgp router-id %s\n", - inet_ntoa(bgp->router_id_static)); - - /* BGP log-neighbor-changes. */ - if (!!bgp_flag_check(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES) - != DFLT_BGP_LOG_NEIGHBOR_CHANGES) - vty_out(vty, " %sbgp log-neighbor-changes\n", - bgp_flag_check(bgp, - BGP_FLAG_LOG_NEIGHBOR_CHANGES) - ? "" - : "no "); - - /* BGP configuration. */ - if (bgp_flag_check(bgp, BGP_FLAG_ALWAYS_COMPARE_MED)) - vty_out(vty, " bgp always-compare-med\n"); - - /* RFC8212 default eBGP policy. */ - if (bgp->ebgp_requires_policy - == DEFAULT_EBGP_POLICY_ENABLED) - vty_out(vty, " bgp ebgp-requires-policy\n"); - - /* BGP default ipv4-unicast. */ - if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4)) - vty_out(vty, " no bgp default ipv4-unicast\n"); - - /* BGP default local-preference. */ - if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF) - vty_out(vty, " bgp default local-preference %u\n", - bgp->default_local_pref); - - /* BGP default show-hostname */ - if (!!bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME) - != DFLT_BGP_SHOW_HOSTNAME) - vty_out(vty, " %sbgp default show-hostname\n", - bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME) - ? "" - : "no "); - - /* BGP default subgroup-pkt-queue-max. */ - if (bgp->default_subgroup_pkt_queue_max - != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX) - vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n", - bgp->default_subgroup_pkt_queue_max); - - /* BGP client-to-client reflection. */ - if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT)) - vty_out(vty, " no bgp client-to-client reflection\n"); - - /* BGP cluster ID. */ - if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID)) - vty_out(vty, " bgp cluster-id %s\n", - inet_ntoa(bgp->cluster_id)); - - /* Disable ebgp connected nexthop check */ - if (bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) - vty_out(vty, - " bgp disable-ebgp-connected-route-check\n"); - - /* Confederation identifier*/ - if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) - vty_out(vty, " bgp confederation identifier %u\n", - bgp->confed_id); - - /* Confederation peer */ - if (bgp->confed_peers_cnt > 0) { - int i; - - vty_out(vty, " bgp confederation peers"); - - for (i = 0; i < bgp->confed_peers_cnt; i++) - vty_out(vty, " %u", bgp->confed_peers[i]); - - vty_out(vty, "\n"); - } - - /* BGP deterministic-med. */ - if (!!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) - != DFLT_BGP_DETERMINISTIC_MED) - vty_out(vty, " %sbgp deterministic-med\n", - bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED) - ? "" - : "no "); - - /* BGP update-delay. */ - bgp_config_write_update_delay(vty, bgp); - - if (bgp->v_maxmed_onstartup - != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) { - vty_out(vty, " bgp max-med on-startup %u", - bgp->v_maxmed_onstartup); - if (bgp->maxmed_onstartup_value - != BGP_MAXMED_VALUE_DEFAULT) - vty_out(vty, " %u", - bgp->maxmed_onstartup_value); - vty_out(vty, "\n"); - } - if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) { - vty_out(vty, " bgp max-med administrative"); - if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT) - vty_out(vty, " %u", bgp->maxmed_admin_value); - vty_out(vty, "\n"); - } - - /* write quanta */ - bgp_config_write_wpkt_quanta(vty, bgp); - /* read quanta */ - bgp_config_write_rpkt_quanta(vty, bgp); - - /* coalesce time */ - bgp_config_write_coalesce_time(vty, bgp); - - /* BGP graceful-restart. */ - if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME) - vty_out(vty, - " bgp graceful-restart stalepath-time %u\n", - bgp->stalepath_time); - if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME) - vty_out(vty, " bgp graceful-restart restart-time %u\n", - bgp->restart_time); - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART)) - vty_out(vty, " bgp graceful-restart\n"); - - /* BGP graceful-shutdown */ - if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) - vty_out(vty, " bgp graceful-shutdown\n"); - - /* BGP graceful-restart Preserve State F bit. */ - if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD)) - vty_out(vty, - " bgp graceful-restart preserve-fw-state\n"); - - /* BGP bestpath method. */ - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE)) - vty_out(vty, " bgp bestpath as-path ignore\n"); - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED)) - vty_out(vty, " bgp bestpath as-path confed\n"); - - if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { - if (bgp_flag_check(bgp, - BGP_FLAG_MULTIPATH_RELAX_AS_SET)) { - vty_out(vty, - " bgp bestpath as-path multipath-relax as-set\n"); - } else { - vty_out(vty, - " bgp bestpath as-path multipath-relax\n"); - } - } - - if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { - vty_out(vty, - " bgp route-reflector allow-outbound-policy\n"); - } - if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID)) - vty_out(vty, " bgp bestpath compare-routerid\n"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED) - || bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) { - vty_out(vty, " bgp bestpath med"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)) - vty_out(vty, " confed"); - if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) - vty_out(vty, " missing-as-worst"); - vty_out(vty, "\n"); - } - - /* BGP network import check. */ - if (!!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) - != DFLT_BGP_IMPORT_CHECK) - vty_out(vty, " %sbgp network import-check\n", - bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK) - ? "" - : "no "); - - /* BGP timers configuration. */ - if (bgp->default_keepalive != BGP_DEFAULT_KEEPALIVE - && bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) - vty_out(vty, " timers bgp %u %u\n", - bgp->default_keepalive, bgp->default_holdtime); - - /* peer-group */ - for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) { - bgp_config_write_peer_global(vty, bgp, group->conf); - } - - /* Normal neighbor configuration. */ - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) - bgp_config_write_peer_global(vty, bgp, peer); - } - - /* listen range and limit for dynamic BGP neighbors */ - bgp_config_write_listen(vty, bgp); - - /* - * BGP default autoshutdown neighbors - * - * This must be placed after any peer and peer-group - * configuration, to avoid setting all peers to shutdown after - * a daemon restart, which is undesired behavior. (see #2286) - */ - if (bgp->autoshutdown) - vty_out(vty, " bgp default shutdown\n"); - - /* IPv4 unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); - - /* IPv4 multicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST); - - /* IPv4 labeled-unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST); - - /* IPv4 VPN configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN); - - /* ENCAPv4 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP); - - /* FLOWSPEC v4 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC); - - /* IPv6 unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST); - - /* IPv6 multicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST); - - /* IPv6 labeled-unicast configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, - SAFI_LABELED_UNICAST); - - /* IPv6 VPN configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN); - - /* ENCAPv6 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP); - - /* FLOWSPEC v6 configuration. */ - bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC); - - /* EVPN configuration. */ - bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN); - - hook_call(bgp_inst_config_write, bgp, vty); - -#if ENABLE_BGP_VNC - bgp_rfapi_cfg_write(vty, bgp); -#endif - - vty_out(vty, "!\n"); - } - return 0; -} - void bgp_master_init(struct thread_master *master, const int buffer_size) { qobj_init(); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index e27018407f..7d81579009 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -457,6 +457,7 @@ struct bgp { /* BGP default timer. */ uint32_t default_holdtime; uint32_t default_keepalive; + uint32_t default_connect_retry; /* BGP graceful restart */ uint32_t restart_time; @@ -504,6 +505,13 @@ struct bgp { #define DEFAULT_EBGP_POLICY_DISABLED 0 #define DEFAULT_EBGP_POLICY_ENABLED 1 + /* draft-ietf-idr-deprecate-as-set-confed-set + * Reject aspaths with AS_SET and/or AS_CONFED_SET. + */ + bool reject_as_sets; +#define BGP_REJECT_AS_SETS_DISABLED 0 +#define BGP_REJECT_AS_SETS_ENABLED 1 + struct bgp_evpn_info *evpn_info; /* EVPN - use RFC 8365 to auto-derive RT */ @@ -1203,6 +1211,7 @@ struct peer { #define PEER_DOWN_NBR_ADDR 28 /* Waiting for peer IPv6 IP Addr */ #define PEER_DOWN_VRF_UNINIT 29 /* Associated VRF is not init yet */ #define PEER_DOWN_NOAFI_ACTIVATED 30 /* No AFI/SAFI activated for peer */ +#define PEER_DOWN_AS_SETS_REJECT 31 /* Reject routes with AS_SET */ size_t last_reset_cause_size; uint8_t last_reset_cause[BGP_MAX_PACKET_SIZE]; @@ -1421,13 +1430,17 @@ struct bgp_nlri { #define BGP_EVENTS_MAX 15 /* BGP timers default value. */ -/* note: the DFLT_ ones depend on compile-time "defaults" selection */ #define BGP_INIT_START_TIMER 1 -#define BGP_DEFAULT_HOLDTIME DFLT_BGP_HOLDTIME -#define BGP_DEFAULT_KEEPALIVE DFLT_BGP_KEEPALIVE +/* The following 3 are RFC defaults that are overridden in bgp_vty.c with + * version-/profile-specific values. The values here do not matter, they only + * exist to provide a clear layering separation between core and CLI. + */ +#define BGP_DEFAULT_HOLDTIME 180 +#define BGP_DEFAULT_KEEPALIVE 60 +#define BGP_DEFAULT_CONNECT_RETRY 120 + #define BGP_DEFAULT_EBGP_ROUTEADV 0 #define BGP_DEFAULT_IBGP_ROUTEADV 0 -#define BGP_DEFAULT_CONNECT_RETRY DFLT_BGP_TIMERS_CONNECT /* BGP default local preference. */ #define BGP_DEFAULT_LOCAL_PREF 100 @@ -1472,6 +1485,7 @@ enum bgp_clear_type { /* BGP error codes. */ #define BGP_SUCCESS 0 +#define BGP_CREATED 1 #define BGP_ERR_INVALID_VALUE -1 #define BGP_ERR_INVALID_FLAG -2 #define BGP_ERR_INVALID_AS -3 @@ -1618,7 +1632,8 @@ extern int bgp_confederation_peers_check(struct bgp *, as_t); extern int bgp_confederation_peers_add(struct bgp *, as_t); extern int bgp_confederation_peers_remove(struct bgp *, as_t); -extern int bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime); +extern int bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime, + uint32_t connect_retry); extern int bgp_timers_unset(struct bgp *); extern int bgp_default_local_preference_set(struct bgp *, uint32_t); diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index b97c8c3030..352f5e8328 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -78,7 +78,7 @@ static void encap_attr_export_ce(struct attr *new, struct attr *orig, * Make "new" a ghost attr copy of "orig" */ memset(new, 0, sizeof(struct attr)); - bgp_attr_dup(new, orig); + *new = *orig; /* * Set nexthop @@ -616,7 +616,7 @@ encap_attr_export(struct attr *new, struct attr *orig, * Make "new" a ghost attr copy of "orig" */ memset(new, 0, sizeof(struct attr)); - bgp_attr_dup(new, orig); + *new = *orig; /* * Set nexthop diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 6e8969ad18..ba6ef14257 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -356,7 +356,8 @@ static int process_unicast_route(struct bgp *bgp, /* in */ * all of the possible returns above. */ memset(&hattr, 0, sizeof(struct attr)); - bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */ + /* hattr becomes a ghost attr */ + hattr = *attr; if (rmap) { struct bgp_path_info info; @@ -798,7 +799,8 @@ static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, * all of the possible returns above. */ memset(&hattr, 0, sizeof(struct attr)); - bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */ + /* hattr becomes a ghost attr */ + hattr = *attr; if (rmap) { struct bgp_path_info info; @@ -1000,7 +1002,8 @@ vnc_import_bgp_add_route_mode_nvegroup(struct bgp *bgp, struct prefix *prefix, * all of the possible returns above. */ memset(&hattr, 0, sizeof(struct attr)); - bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */ + /* hattr becomes a ghost attr */ + hattr = *attr; if (rmap) { struct bgp_path_info path; @@ -1779,7 +1782,7 @@ static void vnc_import_bgp_exterior_add_route_it( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { new_attr.local_pref = @@ -2104,7 +2107,7 @@ void vnc_import_bgp_exterior_add_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { @@ -2240,7 +2243,7 @@ void vnc_import_bgp_exterior_add_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { @@ -2360,7 +2363,7 @@ void vnc_import_bgp_exterior_add_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi_interior->attr); + new_attr = *bpi_interior->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { @@ -2550,7 +2553,7 @@ void vnc_import_bgp_exterior_del_route_interior( /* use local_pref from unicast route */ memset(&new_attr, 0, sizeof(struct attr)); - bgp_attr_dup(&new_attr, bpi->attr); + new_attr = *bpi->attr; if (bpi_exterior && (bpi_exterior->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { diff --git a/bgpd/subdir.am b/bgpd/subdir.am index b338fd4f3d..203cf779b9 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -199,14 +199,6 @@ bgpd_bgp_btoa_SOURCES = bgpd/bgp_btoa.c bgpd_bgpd_CFLAGS = $(AM_CFLAGS) bgpd_bgp_btoa_CFLAGS = $(AM_CFLAGS) -if ENABLE_BGP_VNC -bgpd_bgpd_SOURCES += bgpd/rfapi/rfapi_descriptor_rfp_utils.c -bgpd_bgpd_CFLAGS += -Irfapi -I@top_srcdir@/$(RFPINC) - -bgpd_bgp_btoa_SOURCES += bgpd/rfapi/rfapi_descriptor_rfp_utils.c -bgpd_bgp_btoa_CFLAGS += -Irfapi -I@top_srcdir@/$(RFPINC) -endif - # RFPLDADD is set in bgpd/rfp-example/librfp/subdir.am bgpd_bgpd_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBCAP) $(LIBM) bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBCAP) $(LIBM) diff --git a/configure.ac b/configure.ac index 6147ebf0d8..a9784842e4 100755 --- a/configure.ac +++ b/configure.ac @@ -334,6 +334,13 @@ if test "$enable_memory_sanitizer" = "yes"; then SAN_FLAGS="-fsanitize=memory -fPIE -pie" ]) fi +if test "$enable_undefined_sanitizer" = "yes"; then + AC_C_FLAG([-fsanitize=undefined], [ + AC_MSG_ERROR([$CC does not support UndefinedBehaviorSanitizer.]) + ], [ + SAN_FLAGS="-fsanitize=undefined" + ]) +fi AC_SUBST([SAN_FLAGS]) dnl ---------- @@ -576,6 +583,8 @@ AC_ARG_ENABLE([thread-sanitizer], AS_HELP_STRING([--enable-thread-sanitizer], [enable ThreadSanitizer support for detecting data races])) AC_ARG_ENABLE([memory-sanitizer], AS_HELP_STRING([--enable-memory-sanitizer], [enable MemorySanitizer support for detecting uninitialized memory reads])) +AC_ARG_ENABLE([undefined-sanitizer], + AS_HELP_STRING([--undefined-sanitizer], [enable UndefinedBehaviorSanitizer support for detecting undefined behavior])) AC_ARG_WITH([crypto], AS_HELP_STRING([--with-crypto=<internal|openssl>], [choose between different implementations of cryptographic functions(default value is --with-crypto=internal)])) @@ -643,6 +652,7 @@ fi if test "${enable_datacenter}" = "yes" ; then AC_DEFINE([HAVE_DATACENTER], [1], [Compile extensions for a DataCenter]) + AC_MSG_WARN([The --enable-datacenter compile time option is deprecated. Please modify the init script to pass -F datacenter to the daemons instead.]) DFLT_NAME="datacenter" else DFLT_NAME="traditional" @@ -1060,6 +1070,8 @@ FRR_INCLUDES dnl V6 headers are checked below, after we check for v6 +is_linux=false + AC_MSG_CHECKING([which operating system interface to use]) case "$host_os" in sunos* | solaris2*) @@ -1088,6 +1100,8 @@ case "$host_os" in dnl how to fix it but no real progress on implementation dnl when they fix it, remove this AC_DEFINE([IPV6_MINHOPCOUNT], [73], [Linux ipv6 Min Hop Count]) + + is_linux=true ;; openbsd*) AC_MSG_RESULT([OpenBSD]) @@ -1115,6 +1129,7 @@ case "$host_os" in ;; esac AM_CONDITIONAL([SOLARIS], [test "${SOLARIS}" = "solaris"]) +AM_CONDITIONAL([LINUX], [${is_linux}]) AC_SYS_LARGEFILE @@ -1153,6 +1168,7 @@ dnl ########################################################################## # # Logic for protobuf support. # +PROTO3=false if test "$enable_protobuf" = "yes"; then # Check for protoc & protoc-c @@ -1167,9 +1183,15 @@ if test "$enable_protobuf" = "yes"; then PKG_CHECK_MODULES([PROTOBUF_C], [libprotobuf-c >= 0.14],, [ AC_MSG_FAILURE([protobuf requested but libprotobuf-c not found. Install protobuf-c.]) ]) - AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], [], [ - AC_MSG_FAILURE([protobuf requested but protobuf-c.h not found. Install protobuf-c.]) - ]) + + PROTO3=true + AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], + [AC_CHECK_DECLS(PROTOBUF_C_LABEL_NONE, + AC_DEFINE([HAVE_PROTOBUF_VERSION_3], + [1], [Have Protobuf version 3]), + [PROTO3=false], + [#include <google/protobuf-c/protobuf-c.h>])], + [PROTO3=false && AC_MSG_FAILURE([protobuf requested but protobuf-c.h not found. Install protobuf-c.])]) AC_DEFINE([HAVE_PROTOBUF], [1], [protobuf]) fi @@ -2265,6 +2287,7 @@ AM_CONDITIONAL([SNMP], [test "x$SNMP_METHOD" = "xagentx"]) AM_CONDITIONAL([IRDP], [$IRDP]) AM_CONDITIONAL([FPM], [test "x$enable_fpm" = "xyes"]) AM_CONDITIONAL([HAVE_PROTOBUF], [test "x$enable_protobuf" = "xyes"]) +AM_CONDITIONAL([HAVE_PROTOBUF3], [$PROTO3]) dnl daemons AM_CONDITIONAL([VTYSH], [test "x$VTYSH" = "xvtysh"]) AM_CONDITIONAL([ZEBRA], [test "${enable_zebra}" != "no"]) diff --git a/debian/frr.install b/debian/frr.install index 09bddf0fc6..5917c0da84 100644 --- a/debian/frr.install +++ b/debian/frr.install @@ -8,6 +8,7 @@ usr/lib/frr/*.sh usr/lib/frr/*d usr/lib/frr/watchfrr usr/lib/frr/zebra +usr/lib/*/frr/modules/zebra_cumulus_mlag.so usr/lib/*/frr/modules/zebra_irdp.so usr/lib/*/frr/modules/zebra_fpm.so usr/lib/*/frr/modules/bgpd_bmp.so diff --git a/defaults.h b/defaults.h deleted file mode 100644 index fd38aaed70..0000000000 --- a/defaults.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * FRR switchable defaults. - * Copyright (C) 2017 David Lamparter for NetDEF, Inc. - * - * This file is part of FRRouting (FRR). - * - * FRR is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2, or (at your option) any later version. - * - * FRR is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _FRR_DEFAULTS_H -#define _FRR_DEFAULTS_H - -#include "config.h" - -#ifdef HAVE_DATACENTER - -#define DFLT_BGP_IMPORT_CHECK 1 -#define DFLT_BGP_TIMERS_CONNECT 10 -#define DFLT_BGP_HOLDTIME 9 -#define DFLT_BGP_KEEPALIVE 3 -#define DFLT_BGP_LOG_NEIGHBOR_CHANGES 1 -#define DFLT_BGP_SHOW_HOSTNAME 1 -#define DFLT_BGP_DETERMINISTIC_MED 1 - -#define DFLT_OSPF_LOG_ADJACENCY_CHANGES 1 -#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES 1 - -#else /* !HAVE_DATACENTER */ - -#define DFLT_BGP_IMPORT_CHECK 0 -#define DFLT_BGP_TIMERS_CONNECT 120 -#define DFLT_BGP_HOLDTIME 180 -#define DFLT_BGP_KEEPALIVE 60 -#define DFLT_BGP_LOG_NEIGHBOR_CHANGES 0 -#define DFLT_BGP_SHOW_HOSTNAME 0 -#define DFLT_BGP_DETERMINISTIC_MED 0 - -#define DFLT_OSPF_LOG_ADJACENCY_CHANGES 0 -#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES 0 - -#endif /* !HAVE_DATACENTER */ - -#endif /* _FRR_DEFAULTS_H */ diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst new file mode 100644 index 0000000000..7751482b19 --- /dev/null +++ b/doc/developer/building-frr-for-centos8.rst @@ -0,0 +1,156 @@ +CentOS 8 +======== + +This document describes installation from source. If you want to build an RPM, +see :ref:`packaging-redhat`. + +Install required packages +------------------------- + +Add packages: + +:: + + sudo dnf install --enablerepo=PowerTools git autoconf pcre-devel \ + automake libtool make readline-devel texinfo net-snmp-devel pkgconfig \ + groff pkgconfig json-c-devel pam-devel bison flex python2-pytest \ + c-ares-devel python2-devel systemd-devel libcap-devel + +.. include:: building-libyang.rst + +Get FRR, compile it and install it (from Git) +--------------------------------------------- + +**This assumes you want to build and install FRR from source and not +using any packages** + +Add frr groups and user +^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo groupadd -g 92 frr + sudo groupadd -r -g 85 frrvty + sudo useradd -u 92 -g 92 -M -r -G frrvty -s /sbin/nologin \ + -c "FRR FRRouting suite" -d /var/run/frr frr + +Download Source, configure and compile it +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +(You may prefer different options on configure statement. These are just +an example.) + +:: + + git clone https://github.com/frrouting/frr.git frr + cd frr + ./bootstrap.sh + ./configure \ + --bindir=/usr/bin \ + --sbindir=/usr/lib/frr \ + --sysconfdir=/etc/frr \ + --libdir=/usr/lib/frr \ + --libexecdir=/usr/lib/frr \ + --localstatedir=/var/run/frr \ + --with-moduledir=/usr/lib/frr/modules \ + --enable-snmp=agentx \ + --enable-multipath=64 \ + --enable-user=frr \ + --enable-group=frr \ + --enable-vty-group=frrvty \ + --enable-systemd=yes \ + --disable-exampledir \ + --disable-ldpd \ + --enable-fpm \ + --with-pkg-git-version \ + --with-pkg-extra-version=-MyOwnFRRVersion \ + SPHINXBUILD=/usr/bin/sphinx-build + make + make check + sudo make install + +Create empty FRR configuration files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo mkdir /var/log/frr + sudo mkdir /etc/frr + sudo touch /etc/frr/zebra.conf + sudo touch /etc/frr/bgpd.conf + sudo touch /etc/frr/ospfd.conf + sudo touch /etc/frr/ospf6d.conf + sudo touch /etc/frr/isisd.conf + sudo touch /etc/frr/ripd.conf + sudo touch /etc/frr/ripngd.conf + sudo touch /etc/frr/pimd.conf + sudo touch /etc/frr/nhrpd.conf + sudo touch /etc/frr/eigrpd.conf + sudo touch /etc/frr/babeld.conf + sudo chown -R frr:frr /etc/frr/ + sudo touch /etc/frr/vtysh.conf + sudo chown frr:frrvty /etc/frr/vtysh.conf + sudo chmod 640 /etc/frr/*.conf + +Install daemon config file +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo install -p -m 644 redhat/daemons /etc/frr/ + sudo chown frr:frr /etc/frr/daemons + +Edit /etc/frr/daemons as needed to select the required daemons +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Look for the section with ``watchfrr_enable=...`` and ``zebra=...`` etc. +Enable the daemons as required by changing the value to ``yes`` + +Enable IP & IPv6 forwarding +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Create a new file ``/etc/sysctl.d/90-routing-sysctl.conf`` with the +following content: + +:: + + # Sysctl for routing + # + # Routing: We need to forward packets + net.ipv4.conf.all.forwarding=1 + net.ipv6.conf.all.forwarding=1 + +Load the modified sysctl's on the system: + +:: + + sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf + +Install frr Service and redhat init files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo install -p -m 644 redhat/frr.service /usr/lib/systemd/system/frr.service + sudo install -p -m 755 redhat/frr.init /usr/lib/frr/frr + +Register the systemd files +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo systemctl preset frr.service + +Enable required frr at startup +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo systemctl enable frr + +Reboot or start FRR manually +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sudo systemctl start frr diff --git a/doc/developer/building.rst b/doc/developer/building.rst index c13fb10ffc..b99667124f 100644 --- a/doc/developer/building.rst +++ b/doc/developer/building.rst @@ -10,6 +10,7 @@ Building FRR building-frr-for-alpine building-frr-for-centos6 building-frr-for-centos7 + building-frr-for-centos8 building-frr-for-debian8 building-frr-for-debian9 building-frr-for-fedora diff --git a/doc/developer/packaging-redhat.rst b/doc/developer/packaging-redhat.rst index d344046148..d9c4c5bf0c 100644 --- a/doc/developer/packaging-redhat.rst +++ b/doc/developer/packaging-redhat.rst @@ -3,7 +3,7 @@ Packaging Red Hat ================= -Tested on CentOS 6, CentOS 7 and Fedora 24. +Tested on CentOS 6, CentOS 7, CentOS 8 and Fedora 24. 1. On CentOS 6, refer to :ref:`building-centos6` for details on installing sufficiently up-to-date package versions to enable building FRR. @@ -22,13 +22,31 @@ Tested on CentOS 6, CentOS 7 and Fedora 24. yum install systemd-devel + .. note:: + + For CentOS 8 you need to install ``platform-python-devel`` package + to provide ``/usr/bin/pathfix.py``:: + + yum install platform-python-devel + + .. warning:: + + ``python2-sphinx`` is not shipped for CentOS 8. + Development reached the end of life for Python 2. + We need to install it using ```pip``:: + + pip2 install sphinx + If ``yum`` is not present on your system, use ``dnf`` instead. -3. Checkout FRR:: + You should enable ``PowerTools`` repo if using CentOS 8 which + is disabled by default. + +4. Checkout FRR:: git clone https://github.com/frrouting/frr.git frr -4. Run Bootstrap and make distribution tar.gz:: +5. Run Bootstrap and make distribution tar.gz:: cd frr ./bootstrap.sh @@ -40,7 +58,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24. The only ``configure`` option respected when building RPMs is ``--with-pkg-extra-version``. -5. Create RPM directory structure and populate with sources:: +6. Create RPM directory structure and populate with sources:: mkdir rpmbuild mkdir rpmbuild/SOURCES @@ -48,7 +66,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24. cp redhat/*.spec rpmbuild/SPECS/ cp frr*.tar.gz rpmbuild/SOURCES/ -6. Edit :file:`rpm/SPECS/frr.spec` with configuration as needed. +7. Edit :file:`rpm/SPECS/frr.spec` with configuration as needed. Look at the beginning of the file and adjust the following parameters to enable or disable features as required:: @@ -73,7 +91,7 @@ Tested on CentOS 6, CentOS 7 and Fedora 24. %{!?with_pimd: %global with_pimd 1 } %{!?with_rpki: %global with_rpki 0 } -7. Build the RPM:: +8. Build the RPM:: rpmbuild --define "_topdir `pwd`/rpmbuild" -ba rpmbuild/SPECS/frr.spec diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index 6eef7532b3..8ce3bdeeb2 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -283,7 +283,10 @@ Pre-submission Checklist - ``make test`` - In the case of a major new feature or other significant change, document - plans for continued maintenance of the feature + plans for continued maintenance of the feature. In addition it is a + requirement that automated testing must be written that exercises + the new feature within our existing CI infrastructure. Also the + addition of automated testing to cover any pull request is encouraged. .. _signing-off: @@ -293,6 +296,11 @@ Code submitted to FRR must be signed off. We have the same requirements for using the signed-off-by process as the Linux kernel. In short, you must include a ``Signed-off-by`` tag in every patch. +An easy way to do this is to use ``git commit -s`` where ``-s`` will automatically +append a signed-off line to the end of your commit message. Also, if you commit +and forgot to add the line you can use ``git commit --amend -s`` to add the +signed-off line to the last commit. + ``Signed-off-by`` is a developer's certification that they have the right to submit the patch for inclusion into the project. It is an agreement to the :ref:`Developer's Certificate of Origin <developers-certificate-of-origin>`. @@ -447,6 +455,10 @@ Guidelines for code review may originate with a reviewer or document agreement reached on Slack, the Development mailing list, or the weekly technical meeting. +- Reviewers may ask for new automated testing if they feel that the + code change is large enough/significant enough to warrant such + a requirement. + Coding Practices & Style ======================== diff --git a/doc/manpages/bgpd.rst b/doc/manpages/bgpd.rst index 079aad8c48..f7e20265e5 100644 --- a/doc/manpages/bgpd.rst +++ b/doc/manpages/bgpd.rst @@ -39,11 +39,6 @@ OPTIONS available for the |DAEMON| command: processes in the same namespace. This option is different than the --no_zebra option in that a ZAPI connection is made. -.. option:: -S, --skip_runas - - Skip the normal process of checking capabilities and changing user and group - information. - .. option:: -e, --ecmp Run BGP with a limited ecmp capability, that is different than what BGP diff --git a/doc/user/basic.rst b/doc/user/basic.rst index f946c37a73..edcfce45ad 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -286,6 +286,9 @@ Below is a sample configuration file for the zebra daemon. ! ! Zebra configuration file ! + frr version 6.0 + frr defaults traditional + ! hostname Router password zebra enable password zebra @@ -307,6 +310,137 @@ If a comment character is not the first character of the word, it's a normal character. So in the above example ``!`` will not be regarded as a comment and the password is set to ``zebra!password``. + +Configuration versioning, profiles and upgrade behavior +------------------------------------------------------- + +All |PACKAGE_NAME| daemons share a mechanism to specify a configuration profile +and version for loading and saving configuration. Specific configuration +settings take different default values depending on the selected profile and +version. + +While the profile can be selected by user configuration and will remain over +upgrades, |PACKAGE_NAME| will always write configurations using its current +version. This means that, after upgrading, a ``write file`` may write out a +slightly different configuration than what was read in. + +Since the previous configuration is loaded with its version's defaults, but +the new configuration is written with the new defaults, any default that +changed between versions will result in an appropriate configuration entry +being written out. **FRRouting configuration is sticky, staying consistent +over upgrades.** Changed defaults will only affect new configuration. + +Note that the loaded version persists into interactive configuration +sessions. Commands executed in an interactive configuration session are +no different from configuration loaded at startup. This means that when, +say, you configure a new BGP peer, the defaults used for configuration +are the ones selected by the last ``frr version`` command. + +.. warning:: + + Saving the configuration does not bump the daemons forward to use the new + version for their defaults, but restarting them will, since they will then + apply the new ``frr version`` command that was written out. Manually + execute the ``frr version`` command in ``show running-config`` to avoid + this intermediate state. + +This is visible in ``show running-config``: + +.. code-block:: frr + + Current configuration: + ! + ! loaded from 6.0 + frr version 6.1-dev + frr defaults traditional + ! + +If you save and then restart with this configuration, the old defaults will +no longer apply. Similarly, you could execute ``frr version 6.1-dev``, causing +the new defaults to apply and the ``loaded from 6.0`` comment to disappear. + + +Profiles +^^^^^^^^ + +|PACKAGE_NAME| provides configuration profiles to adapt its default settings +to various usage scenarios. Currently, the following profiles are +implemented: + +* ``traditional`` - reflects defaults adhering mostly to IETF standards or + common practices in wide-area internet routing. +* ``datacenter`` - reflects a single administrative domain with intradomain + links using aggressive timers. + +Your distribution/installation may pre-set a profile through the ``-F`` command +line option on all daemons. All daemons must be configured for the same +profile. The value specified on the command line is only a pre-set and any +``frr defaults`` statement in the configuration will take precedence. + +.. note:: + + The profile must be the same across all daemons. Mismatches may result + in undefined behavior. + +You can freely switch between profiles without causing any interruption or +configuration changes. All settings remain at their previous values, and +``show running-configuration`` output will have new output listing the previous +default values as explicit configuration. New configuration, e.g. adding a +BGP peer, will use the new defaults. To apply the new defaults for existing +configuration, the previously-invisible old defaults that are now shown must +be removed from the configuration. + + +Upgrade practices for interactive configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you configure |PACKAGE_NAME| interactively and use the configuration +writing functionality to make changes persistent, the following +recommendations apply in regards to upgrades: + +1. Skipping major versions should generally work but is still inadvisable. + To avoid unneeded issue, upgrade one major version at a time and write + out the configuration after each update. + +2. After installing a new |PACKAGE_NAME| version, check the configuration + for differences against your old configuration. If any defaults changed + that affect your setup, lines may appear or disappear. If a new line + appears, it was previously the default (or not supported) and is now + neccessary to retain previous behavior. If a line disappears, it + previously wasn't the default, but now is, so it is no longer necessary. + +3. Check the log files for deprecation warnings by using ``grep -i deprecat``. + +4. After completing each upgrade, save the configuration and either restart + |PACKAGE_NAME| or execute ``frr version <CURRENT>`` to ensure defaults of + the new version are fully applied. + + +Upgrade practices for autogenerated configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When using |PACKAGE_NAME| with generated configurations (e.g. Ansible, +Puppet, etc.), upgrade considerations differ somewhat: + +1. Always write out a ``frr version`` statement in the configurations you + generate. This ensures that defaults are applied consistently. + +2. Try to not run more distinct versions of |PACKAGE_NAME| than necessary. + Each version may need to be checked individually. If running a mix of + older and newer installations, use the oldest version for the + ``frr version`` statement. + +3. When rolling out upgrades, generate a configuration as usual with the old + version identifier and load it. Check for any differences or deprecation + warnings. If there are differences in the configuration, propagate these + back to the configuration generator to minimize relying on actual default + values. + +4. After the last installation of an old version is removed, change the + configuration generation to a newer ``frr version`` as appropriate. Perform + the same checks as when rolling out upgrades. + + .. _terminal-mode-commands: Terminal Mode Commands diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 525a31d486..b3fc7f15a6 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -94,6 +94,10 @@ BFDd Commands Show status for a specific BFD peer. +.. index:: show bfd [vrf NAME] peers brief [json] +.. clicmd:: show bfd [vrf NAME] peers brief [json] + + Show all configured BFD peers information and current status in brief. .. _bfd-peer-config: @@ -340,11 +344,14 @@ You can inspect the current BFD peer status with the following commands: Uptime: 1 minute(s), 51 second(s) Diagnostics: ok Remote diagnostics: ok + Peer Type: dynamic Local timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: disabled Remote timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: 50ms @@ -357,11 +364,14 @@ You can inspect the current BFD peer status with the following commands: Uptime: 1 minute(s), 53 second(s) Diagnostics: ok Remote diagnostics: ok + Peer Type: configured Local timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: disabled Remote timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: 50ms @@ -376,17 +386,31 @@ You can inspect the current BFD peer status with the following commands: Uptime: 3 minute(s), 4 second(s) Diagnostics: ok Remote diagnostics: ok + Peer Type: dynamic Local timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: disabled Remote timers: + Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms Echo transmission interval: 50ms frr# show bfd peer 192.168.0.1 json - {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50} + {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"detect-multiplier":3,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50,"remote-detect-multiplier":3,"peer-type":"dynamic"} + + +You can inspect the current BFD peer status in brief with the following commands: + +:: + + frr# show bfd peers brief + Session count: 1 + SessionId LocalAddress PeerAddress Status + ========= ============ =========== ====== + 1 192.168.0.1 192.168.0.2 up You can also inspect peer session counters with the following commands: @@ -425,3 +449,30 @@ You can also inspect peer session counters with the following commands: frr# show bfd peer 192.168.0.1 counters json {"multihop":false,"peer":"192.168.0.1","control-packet-input":348,"control-packet-output":685,"echo-packet-input":6815,"echo-packet-output":6816,"session-up":1,"session-down":0,"zebra-notifications":4} + +You can also clear packet counters per session with the following commands, only the packet counters will be reset: + +:: + + frr# clear bfd peers counters + + frr# show bfd peers counters + BFD Peers: + peer 192.168.2.1 interface r2-eth2 + Control packet input: 0 packets + Control packet output: 0 packets + Echo packet input: 0 packets + Echo packet output: 0 packets + Session up events: 1 + Session down events: 0 + Zebra notifications: 2 + + peer 192.168.0.1 + Control packet input: 0 packets + Control packet output: 0 packets + Echo packet input: 0 packets + Echo packet output: 0 packets + Session up events: 1 + Session down events: 0 + Zebra notifications: 4 + diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 0741b1d619..2d4d0c4945 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -416,6 +416,14 @@ Require policy on EBGP This command requires incoming and outgoing filters to be applied for eBGP sessions. Without the incoming filter, no routes will be accepted. Without the outgoing filter, no routes will be announced. +Reject routes with AS_SET or AS_CONFED_SET types +------------------------------------------------ + +.. index:: [no] bgp reject-as-sets +.. clicmd:: [no] bgp reject-as-sets + + This command enables rejection of incoming and outgoing routes having AS_SET or AS_CONFED_SET type. + .. _bgp-route-flap-dampening: Route Flap Dampening @@ -1037,6 +1045,15 @@ Configuring Peers .. index:: neighbor PEER port PORT .. clicmd:: neighbor PEER port PORT +.. index:: [no] neighbor PEER password PASSWORD +.. clicmd:: [no] neighbor PEER password PASSWORD + + Set a MD5 password to be used with the tcp socket that is being used + to connect to the remote peer. Please note if you are using this + command with a large number of peers on linux you should consider + modifying the `net.core.optmem_max` sysctl to a larger value to + avoid out of memory errors from the linux kernel. + .. index:: neighbor PEER send-community .. clicmd:: neighbor PEER send-community diff --git a/doc/user/nhrpd.rst b/doc/user/nhrpd.rst index ec6bc56e76..95ef9cb7ee 100644 --- a/doc/user/nhrpd.rst +++ b/doc/user/nhrpd.rst @@ -4,8 +4,8 @@ NHRP **** -*nhrpd* is an implementation of the :abbr:NHRP `(Next Hop Routing Protocol)`. -NHRP is described in :rfc`2332`. +*nhrpd* is an implementation of the :abbr:`NHRP (Next Hop Routing Protocol)`. +NHRP is described in :rfc:`2332`. NHRP is used to improve the efficiency of routing computer network traffic over :abbr:`NBMA (Non-Broadcast, Multiple Access)` networks. NHRP provides an @@ -13,6 +13,13 @@ ARP-like solution that allows a system to dynamically learn the NBMA address of the other systems that are part of that network, allowing these systems to directly communicate without requiring traffic to use an intermediate hop. +NHRP is a client-server protocol. The server side is called the :abbr:`NHS +(Next Hop Server)` or the hub, while a client is referred to as the :abbr:`NHC +(Next Hop Client)` or the spoke. When a node is configured as an NHC, it +registers its address with the NHS which keeps track of all registered spokes. +An NHC client can then query the addresses of other clients from NHS allowing +all spokes to communicate directly with each other. + Cisco Dynamic Multipoint VPN (DMVPN) is based on NHRP, and |PACKAGE_NAME| nhrpd implements this scenario. @@ -31,7 +38,9 @@ This is similar to Cisco FlexVPN; but in contrast to opennhrp which uses a generic subnet route. To create NBMA GRE tunnel you might use the following (Linux terminal -commands)::: +commands): + +.. code-block:: console ip tunnel add gre1 mode gre key 42 ttl 64 ip addr add 10.255.255.2/32 dev gre1 @@ -68,7 +77,58 @@ command defines the GRE subnet): Configuring NHRP ================ -FIXME +.. index:: ip nhrp holdtime (1-65000) +.. clicmd:: ip nhrp holdtime (1-65000) + + Holdtime is the number of seconds that have to pass before stopping to + advertise an NHRP NBMA address as valid. It also controls how often NHRP + registration requests are sent. By default registrations are sent every one + third of the holdtime. + +.. index:: ip nhrp map A.B.C.D|X:X::X:X A.B.C.D|local +.. clicmd:: ip nhrp map A.B.C.D|X:X::X:X A.B.C.D|local + + Map an IP address of a station to the station's NBMA address. + +.. index:: ip nhrp network-id (1-4294967295) +.. clicmd:: ip nhrp network-id (1-4294967295) + + Enable NHRP on this interface and set the interface's network ID. The + network ID is used to allow creating multiple nhrp domains on a router when + multiple interfaces are configured on the router. Interfaces configured + with the same ID are part of the same logical NBMA network. The ID is a + local only parameter and is not sent to other NHRP nodes and so IDs on + different nodes do not need to match. When NHRP packets are received on an + interface they are assigned to the local NHRP domain for that interface. + +.. index:: ip nhrp nhs A.B.C.D nbma A.B.C.D|FQDN +.. clicmd:: ip nhrp nhs A.B.C.D nbma A.B.C.D|FQDN + + Configure the Next Hop Server address and its NBMA address. + +.. index:: ip nhrp nhs dynamic nbma A.B.C.D +.. clicmd:: ip nhrp nhs dynamic nbma A.B.C.D + + Configure the Next Hop Server to have a dynamic address and set its NBMA + address. + +.. index:: ip nhrp registration no-unique +.. clicmd:: ip nhrp registration no-unique + + Allow the client to not set the unique flag in the NHRP packets. This is + useful when a station has a dynamic IP address that could change over time. + +.. index:: ip nhrp shortcut +.. clicmd:: ip nhrp shortcut + + Enable shortcut (spoke-to-spoke) tunnels to allow NHC to talk to each others + directly after establishing a connection without going through the hub. + +.. index:: ip nhrp mtu +.. clicmd:: ip nhrp mtu + + Configure NHRP advertised MTU. + .. _hub-functionality: @@ -92,25 +152,25 @@ This can be achieved with the following iptables rule. --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128 -You can fine tune the src/dstmask according to the prefix lengths you -announce internal, add additional IP range matches, or rate limitation -if needed. However, the above should be good in most cases. +You can fine tune the src/dstmask according to the prefix lengths you announce +internal, add additional IP range matches, or rate limitation if needed. +However, the above should be good in most cases. This kernel NFLOG target's nflog-group is configured in global nhrp config with: -.. code-block:: frr - - nhrp nflog-group 1 +.. index:: nhrp nflog-group (1-65535) +.. clicmd:: nhrp nflog-group (1-65535) To start sending these traffic notices out from hubs, use the nhrp per-interface directive: -.. code-block:: frr - - interface gre1 - ip nhrp redirect +.. index:: ip nhrp redirect +.. clicmd:: ip nhrp redirect +This enable redirect replies on the NHS similar to ICMP redirects except this +is managed by the nhrp protocol. This setting allows spokes to communicate with +each others directly. .. _integration-with-ike: @@ -134,7 +194,10 @@ git repositories for the patches. NHRP Events =========== -FIXME +.. index:: nhrp event socket SOCKET +.. clicmd:: nhrp event socket SOCKET + + Configure the Unix path for the event socket. Configuration Example ===================== diff --git a/doc/user/overview.rst b/doc/user/overview.rst index 03d7299bf7..262c0117df 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -4,65 +4,23 @@ Overview ******** -`FRR`_ is a routing software package that provides TCP/IP based routing -services with routing protocols support such as BGP, RIP, OSPF, IS-IS and more -(see :ref:`supported-protocols`). FRR also supports -special BGP Route Reflector and Route Server behavior. In addition to -traditional IPv4 routing protocols, FRR also supports IPv6 routing protocols. -With an SNMP daemon that supports the AgentX protocol, FRR provides routing -protocol MIB read-only access (:ref:`snmp-support`). - -FRR uses an advanced software architecture to provide you with a high quality, -multi server routing engine. FRR has an interactive user interface for each -routing protocol and supports common client commands. Due to this design, you -can add new protocol daemons to FRR easily. You can use FRR library as your -program's client user interface. - -FRR is distributed under the GNU General Public License. +`FRR`_ is a fully featured, high performance, free software IP routing suite. -FRR is a fork of `Quagga <http://www.quagga.net/>`_. +FRR implements all standard routing protocols such as BGP, RIP, OSPF, IS-IS and +more (see :ref:`feature-matrix`), as well as many of their extensions. -.. _about-frr: +FRR is a high performance suite written primarily in C. It can easily handle +full Internet routing tables and is suitable for use on hardware ranging from +cheap SBCs to commercial grade routers. It is actively used in production by +hundreds of companies, universities, research labs and governments. -About FRR -========= +FRR is distributed under GPLv2, with development modeled after the Linux +kernel. Anyone may contribute features, bug fixes, tools, documentation +updates, or anything else. -Today, TCP/IP networks are covering all of the world. The Internet has been -deployed in many countries, companies, and to the home. When you connect to -the Internet your packet will pass many routers which have TCP/IP routing -functionality. - -A system with FRR installed acts as a dedicated router. With FRR, your machine -exchanges routing information with other routers using routing protocols. FRR -uses this information to update the kernel routing table so that the right data -goes to the right place. You can dynamically change the configuration and you -may view routing table information from the FRR terminal interface. - -Adding to routing protocol support, FRR can setup interface's flags, -interface's address, static routes and so on. If you have a small network, or -a stub network, or xDSL connection, configuring the FRR routing software is -very easy. The only thing you have to do is to set up the interfaces and put a -few commands about static routes and/or default routes. If the network is -rather large, or if the network structure changes frequently, you will want to -take advantage of FRR's dynamic routing protocol support for protocols such as -RIP, OSPF, IS-IS or BGP. - -Traditionally, UNIX based router configuration is done by *ifconfig* and -*route* commands. Status of routing table is displayed by *netstat* utility. -Almost of these commands work only if the user has root privileges. FRR has a -different system administration method. There are two user modes in FRR. One -is normal mode, the other is enable mode. Normal mode user can only view -system status, enable mode user can change system configuration. This UNIX -account independent feature will be great help to the router administrator. - -Currently, FRR supports common unicast routing protocols, that is BGP, OSPF, -RIP and IS-IS. Upcoming for MPLS support, an implementation of LDP is -currently being prepared for merging. Implementations of BFD and PIM-SSM -(IPv4) also exist, but are not actively being worked on. - -The ultimate goal of the FRR project is making a production-grade, high -quality, featureful and free IP routing software suite. +FRR is a fork of `Quagga <http://www.quagga.net/>`_. +.. _how-to-get-frr: How to get FRR ============== @@ -79,25 +37,46 @@ For instructions on installing from source, refer to the `developer documentation <http://docs.frrouting.org/projects/dev-guide/en/latest/>`_. +.. _about-frr: + +About FRR +========= + +FRR provides IP routing services. Its role in a networking stack is to exchange +routing information with other routers, make routing and policy decisions, and +inform other layers of these decisions. In the most common scenario, FRR +installs routing decisions into the OS kernel, allowing the kernel networking +stack to make the corresponding forwarding decisions. + +In addition to dynamic routing FRR supports the full range of L3 configuration, +including static routes, addresses, router advertisements etc. It has some +light L2 functionality as well, but this is mostly left to the platform. This +makes it suitable for deployments ranging from small home networks with static +routes to Internet exchanges running full Internet tables. + +FRR runs on all modern \*NIX operating systems, including Linux and the BSDs. +Feature support varies by platform; see the :ref:`feature-matrix`. + + System Architecture -=================== +------------------- .. index:: System architecture - .. index:: Software architecture - .. index:: Software internals Traditional routing software is made as a one process program which provides all of the routing protocol functionalities. FRR takes a different approach. -FRR is a suite of daemons that work together to build the routing table. There -is a daemon for each major supported protocol as well as a middleman daemon -(*Zebra*) which serves as the broker between these daemons and the kernel. +FRR is a suite of daemons that work together to build the routing table. Each +major protocol is implemented in its own daemon, and these daemons talk to a +middleman daemon (*zebra*), which is responsible for coordinating routing +decisions and talking to the dataplane. This architecture allows for high resiliency, since an error, crash or exploit -in one protocol daemon will generally not affect the others. It is also +in one protocol daemon will generally not affect the others. It is also flexible and extensible since the modularity makes it easy to implement new -protocols and tie them into the suite. +protocols and tie them into the suite. Additionally, each daemon implements a +plugin system allowing new functionality to be loaded at runtime. An illustration of the large scale architecture is given below. @@ -121,17 +100,23 @@ An illustration of the large scale architecture is given below. +-------------+ +------------------+ +-------------+ -The multi-process architecture brings extensibility, modularity and -maintainability. All of the FRR daemons can be managed through a single -integrated user interface shell called *vtysh*. *vtysh* connects to each -daemon through a UNIX domain socket and then works as a proxy for user input. -In addition to a unified frontend, *vtysh* also provides the ability to -configure all the daemons using a single configuration file through the -integrated configuration mode avoiding the problem of having to maintain a -separate configuration file for each daemon. +All of the FRR daemons can be managed through a single integrated user +interface shell called *vtysh*. *vtysh* connects to each daemon through a UNIX +domain socket and then works as a proxy for user input. In addition to a +unified frontend, *vtysh* also provides the ability to configure all the +daemons using a single configuration file through the integrated configuration +mode. This avoids the overhead of maintaining a separate configuration file for +each daemon. + +FRR is currently currently implementing a new internal configuration system +based on YANG data models. When this work is completed, FRR will be a fully +programmable routing stack. + + +.. _supported-platforms: Supported Platforms -=================== +------------------- .. index:: Supported platforms .. index:: FRR on other systems @@ -150,7 +135,7 @@ us know if you can get FRR to run on a platform which is not listed below: Versions of these platforms that are older than around 2 years from the point of their original release (in case of GNU/Linux, this is since the kernel's -release on https://kernel.org/) may need some work. Similarly, the following +release on https://kernel.org/) may need some work. Similarly, the following platforms may work with some effort: - Solaris @@ -162,14 +147,15 @@ Recent versions of the following compilers are well tested: - LLVM's Clang - Intel's ICC -.. _supported-protocols: +.. _feature-matrix: -Supported Protocols vs. Platform -================================ +Feature Matrix +^^^^^^^^^^^^^^ -The following table lists all protocols cross-refrenced to all operating -systems that have at least CI build tests. Note that for features, only -features with system dependencies are included here. +The following table lists all protocols cross-referenced to all operating +systems that have at least CI build tests. Note that for features, only +features with system dependencies are included here; if you don't see the +feature you're interested in, it should be supported on your platform. .. role:: mark @@ -276,78 +262,132 @@ FRR implements the following RFCs: .. note:: This list is incomplete. -- :rfc:`1058` - :t:`Routing Information Protocol. C.L. Hedrick. Jun-01-1988.` -- :rfc:`2082` - :t:`RIP-2 MD5 Authentication. F. Baker, R. Atkinson. January 1997.` -- :rfc:`2453` - :t:`RIP Version 2. G. Malkin. November 1998.` -- :rfc:`2080` - :t:`RIPng for IPv6. G. Malkin, R. Minnear. January 1997.` -- :rfc:`2328` - :t:`OSPF Version 2. J. Moy. April 1998.` -- :rfc:`2370` - :t:`The OSPF Opaque LSA Option R. Coltun. July 1998.` -- :rfc:`3101` - :t:`The OSPF Not-So-Stubby Area (NSSA) Option P. Murphy. January 2003.` -- :rfc:`2740` - :t:`OSPF for IPv6. R. Coltun, D. Ferguson, J. Moy. December 1999.` +BGP +---- + - :rfc:`1771` :t:`A Border Gateway Protocol 4 (BGP-4). Y. Rekhter & T. Li. March 1995.` - :rfc:`1965` :t:`Autonomous System Confederations for BGP. P. Traina. June 1996.` - :rfc:`1997` :t:`BGP Communities Attribute. R. Chandra, P. Traina & T. Li. August 1996.` +- :rfc:`2439` + :t:`BGP Route Flap Damping. C. Villamizar, R. Chandra, R. Govindan. November 1998.` - :rfc:`2545` :t:`Use of BGP-4 Multiprotocol Extensions for IPv6 Inter-Domain Routing. P. Marques, F. Dupont. March 1999.` - :rfc:`2796` - :t:`BGP Route Reflection An alternative to full mesh IBGP. T. Bates & R. - Chandrasekeran. June 1996.` -- :rfc:`2858` - :t:`Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D. - Katz. June 2000.` + :t:`BGP Route Reflection An alternative to full mesh IBGP. T. Bates & R. Chandrasekeran. June 1996.` - :rfc:`2842` :t:`Capabilities Advertisement with BGP-4. R. Chandra, J. Scudder. May 2000.` +- :rfc:`2858` + :t:`Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D.` +- :rfc:`3107` + :t:`Carrying Label Information in BGP-4. Y. Rekhter & E. Rosen. May 2001.` +- :rfc:`3765` + :t:`NOPEER Community for Border Gateway Protocol (BGP) Route Scope Control. G.Huston, April 2001.` +- :rfc:`4271` + :t:`A Border Gateway Protocol 4 (BGP-4). Updates RFC1771. Y. Rekhter, T. Li & S. Hares. January 2006.` +- :rfc:`4364` + :t:`BGP/MPLS IP Virtual Private Networks (VPNs). Y. Rekhter. Feb 2006.` +- :rfc:`4659` + :t:`BGP-MPLS IP Virtual Private Network (VPN) Extension for IPv6 VPN. J. De Clercq, D. Ooms, M. Carugi, F. Le Faucheur. September 2006.` +- :rfc:`5004` + :t:`Avoid BGP Best Path Transitions from One External to Another. E. Chen & S. Sangli. September 2007 (Partial support).` +- :rfc:`5082` + :t:`The Generalized TTL Security Mechanism (GTSM). V. Gill, J. Heasley, D. Meyer, P. Savola, C. Pingnataro. October 2007.` +- :rfc:`5575` + :t:`Dissemination of Flow Specification Rules. P. Marques, N. Sheth, R. Raszuk, B. Greene, J. Mauch, D. McPherson. August 2009` +- :rfc:`6810` + :t:`The Resource Public Key Infrastructure (RPKI) to Router Protocol. R. Bush, R. Austein. January 2013.` +- :rfc:`6811` + :t:`BGP Prefix Origin Validation. P. Mohapatra, J. Scudder, D. Ward, R. Bush, R. Austein. January 2013.` +- :rfc:`7611` + :t:`BGP ACCEPT_OWN Community Attribute. J. Uttaro, P. Mohapatra, D. Smith, R. Raszuk, J. Scudder. August 2015.` +- :rfc:`7999` + :t:`BLACKHOLE Community. T. King, C. Dietzel, J. Snijders, G. Doering, G. Hankins. Oct 2016.` +- :rfc:`8092` + :t:`BGP Large Communities Attribute. J. Heitz, Ed., J. Snijders, Ed, K. Patel, I. Bagdonas, N. Hilliard. February 2017` +- :rfc:`8195` + :t:`Use of BGP Large Communities. J. Snijders, J. Heasley, M. Schmidt, June 2017` +- :rfc:`8212` + :t:`Default External BGP (EBGP) Route Propagation Behavior without Policies. J. Mauch, J. Snijders, G. Hankins. July 2017` +- :rfc:`8277` + :t:`Using BGP to Bind MPLS Labels to Address Prefixes. E. Rosen. October 2017` + + +OSPF +---- + +- :rfc:`2328` + :t:`OSPF Version 2. J. Moy. April 1998.` +- :rfc:`2370` + :t:`The OSPF Opaque LSA Option R. Coltun. July 1998.` +- :rfc:`3101` + :t:`The OSPF Not-So-Stubby Area (NSSA) Option P. Murphy. January 2003.` +- :rfc:`2740` + :t:`OSPF for IPv6. R. Coltun, D. Ferguson, J. Moy. December 1999.` - :rfc:`3137` - :t:`OSPF Stub Router Advertisement, A. Retana, L. Nguyen, R. White, A. Zinin, - D. McPherson. June 2001` + :t:`OSPF Stub Router Advertisement, A. Retana, L. Nguyen, R. White, A. Zinin, D. McPherson. June 2001` + +ISIS +---- + +RIP +---- + +- :rfc:`1058` + :t:`Routing Information Protocol. C.L. Hedrick. Jun-01-1988.` +- :rfc:`2082` + :t:`RIP-2 MD5 Authentication. F. Baker, R. Atkinson. January 1997.` +- :rfc:`2453` + :t:`RIP Version 2. G. Malkin. November 1998.` +- :rfc:`2080` + :t:`RIPng for IPv6. G. Malkin, R. Minnear. January 1997.` + +PIM +---- + +BFD +---- +- :rfc:`5880` + :t:`Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010` +- :rfc:`5881` + :t:`Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop), D. Katz, D. Ward. June 2010` +- :rfc:`5883` + :t:`Bidirectional Forwarding Detection (BFD) for Multihop Paths, D. Katz, D. Ward. June 2010` + +MPLS +---- + +- :rfc:`2858` + :t:`Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D. Katz. June 2000.` +- :rfc:`4364` + :t:`BGP/MPLS IP Virtual Private Networks (VPNs). Y. Rekhter. Feb 2006.` - :rfc:`4447` - :t:`Pseudowire Setup and Maintenance Using the Label Distribution Protocol - (LDP), L. Martini, E. Rosen, N. El-Aawar, T. Smith, and G. Heron. April - 2006.` + :t:`Pseudowire Setup and Maintenance Using the Label Distribution Protocol (LDP), L. Martini, E. Rosen, N. El-Aawar, T. Smith, and G. Heron. April 2006.` +- :rfc:`4659` + :t:`BGP-MPLS IP Virtual Private Network (VPN) Extension for IPv6 VPN. J. De Clercq, D. Ooms, M. Carugi, F. Le Faucheur. September 2006` - :rfc:`4762` - :t:`Virtual Private LAN Service (VPLS) Using Label Distribution Protocol - (LDP) Signaling, M. Lasserre and V. Kompella. January 2007.` + :t:`Virtual Private LAN Service (VPLS) Using Label Distribution Protocol (LDP) Signaling, M. Lasserre and V. Kompella. January 2007.` - :rfc:`5036` :t:`LDP Specification, L. Andersson, I. Minei, and B. Thomas. October 2007.` - :rfc:`5561` - :t:`LDP Capabilities, B. Thomas, K. Raza, S. Aggarwal, R. Aggarwal, and - JL. Le Roux. July 2009.` + :t:`LDP Capabilities, B. Thomas, K. Raza, S. Aggarwal, R. Aggarwal, and JL. Le Roux. July 2009.` - :rfc:`5918` - :t:`Label Distribution Protocol (LDP) 'Typed Wildcard' Forward Equivalence - Class (FEC), R. Asati, I. Minei, and B. Thomas. August 2010.` + :t:`Label Distribution Protocol (LDP) 'Typed Wildcard' Forward Equivalence Class (FEC), R. Asati, I. Minei, and B. Thomas. August 2010.` - :rfc:`5919` - :t:`Signaling LDP Label Advertisement Completion, R. Asati, P. Mohapatra, - E. Chen, and B. Thomas. August 2010.` + :t:`Signaling LDP Label Advertisement Completion, R. Asati, P. Mohapatra, E. Chen, and B. Thomas. August 2010.` - :rfc:`6667` - :t:`LDP 'Typed Wildcard' Forwarding Equivalence Class (FEC) for PWid and - Generalized PWid FEC Elements, K. Raza, S. Boutros, and C. Pignataro. July - 2012.` + :t:`LDP 'Typed Wildcard' Forwarding Equivalence Class (FEC) for PWid and Generalized PWid FEC Elements, K. Raza, S. Boutros, and C. Pignataro. July 2012.` - :rfc:`6720` - :t:`The Generalized TTL Security Mechanism (GTSM) for the Label Distribution - Protocol (LDP), C. Pignataro and R. Asati. August 2012.` + :t:`The Generalized TTL Security Mechanism (GTSM) for the Label Distribution Protocol (LDP), C. Pignataro and R. Asati. August 2012.` - :rfc:`7552` - :t:`Updates to LDP for IPv6, R. Asati, C. Pignataro, K. Raza, V. Manral, - and R. Papneja. June 2015.` -- :rfc:`5880` - :t:`Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010` -- :rfc:`5881` - :t:`Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop), - D. Katz, D. Ward. June 2010` -- :rfc:`5883` - :t:`Bidirectional Forwarding Detection (BFD) for Multihop Paths, D. Katz, - D. Ward. June 2010` + :t:`Updates to LDP for IPv6, R. Asati, C. Pignataro, K. Raza, V. Manral, and R. Papneja. June 2015.` + + +SNMP +---- **When SNMP support is enabled, the following RFCs are also supported:** diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 68e460748c..68659fbf50 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -39,7 +39,7 @@ listing of ECMP nexthops used to forward packets for when a pbr-map is matched. sub-mode where you can specify individual nexthops. To exit this mode type exit or end as per normal conventions for leaving a sub-mode. -.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] +.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] [label LABELS] Create a v4 or v6 nexthop. All normal rules for creating nexthops that you are used to are allowed here. The syntax was intentionally kept the same as @@ -115,6 +115,12 @@ end destination. Not supported with NETNS VRF backend. +.. clicmd:: show pbr map [NAME] [detail] + + Display pbr maps either all or by ``NAME``. If ``detail`` is set, it will + give information about the rules unique ID used internally and some extra + debugging information about install state for the nexthop/nexthop group. + .. _pbr-policy: PBR Policy diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 4f9c573a24..9267095b3e 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -197,8 +197,8 @@ is in a vrf, enter the interface command with the vrf keyword at the end. Set the pim hello and hold interval for a interface. -.. index:: ip pim sm -.. clicmd:: ip pim sm +.. index:: ip pim +.. clicmd:: ip pim Tell pim that we would like to use this interface to form pim neighbors over. Please note that this command does not enable the reception of IGMP @@ -211,10 +211,10 @@ is in a vrf, enter the interface command with the vrf keyword at the end. Tell pim to receive IGMP reports and Query on this interface. The default version is v3. This command is useful on a LHR. -.. index:: ip igmp join A.B.C.D A.B.C.D -.. clicmd:: ip igmp join A.B.C.D A.B.C.D +.. index:: ip igmp join A.B.C.D [A.B.C.D] +.. clicmd:: ip igmp join A.B.C.D [A.B.C.D] - Join multicast source-group on an interface. + Join multicast group or source-group on an interface. .. index:: ip igmp query-interval (1-1800) .. clicmd:: ip igmp query-interval (1-1800) @@ -252,6 +252,13 @@ is in a vrf, enter the interface command with the vrf keyword at the end. 10 deciseconds. 'no' form of this command is used to to configure back to the default value. +.. index:: ip mroute INTERFACE A.B.C.D [A.B.C.D] +.. clicmd:: ip mroute INTERFACE A.B.C.D [A.B.C.D] + + Set a static multicast route for a traffic coming on the current interface to + be forwarded on the given interface if the traffic matches the group address + and optionally the source address. + .. _pim-multicast-rib-insertion: PIM Multicast RIB insertion: diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 5c2b41b63c..435639c291 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -7,6 +7,9 @@ Route Maps Route maps provide a means to both filter and/or apply actions to route, hence allowing policy to be applied to routes. +For a route reflector to apply a ``route-map`` to reflected routes, be sure to +include ``bgp route-reflector allow-outbound-policy`` in ``router bgp`` mode. + Route maps are an ordered list of route map entries. Each entry may specify up to four distinct sets of clauses: diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 2099dfdd62..23a062ab02 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -916,3 +916,22 @@ zebra Terminal Mode Commands Display nexthop groups created by zebra. + +Router-id +========= + +Many routing protocols require a router-id to be configured. To have a +consistent router-id across all daemons, the following commands are available +to configure and display the router-id: + +.. index:: [no] router-id A.B.C.D [vrf NAME] +.. clicmd:: [no] router-id A.B.C.D [vrf NAME] + + Configure the router-id of this router. + +.. index:: show router-id [vrf NAME] +.. clicmd:: show router-id [vrf NAME] + + Display the user configured router-id. + + diff --git a/docker/centos-7/Dockerfile b/docker/centos-7/Dockerfile new file mode 100644 index 0000000000..a8564bdd87 --- /dev/null +++ b/docker/centos-7/Dockerfile @@ -0,0 +1,43 @@ +# This stage builds an rpm from the source +FROM centos:centos7 as centos-7-builder + +RUN yum install -y rpm-build autoconf automake libtool make \ + readline-devel texinfo net-snmp-devel groff pkgconfig \ + json-c-devel pam-devel bison flex pytest c-ares-devel \ + python-devel systemd-devel python-sphinx libcap-devel \ + https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-devel-0.16.111-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm + +COPY . /src +ARG PKGVER + +RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \ + && cd /src \ + && ./bootstrap.sh \ + && ./configure \ + --enable-rpki \ + --enable-numeric-version \ + --with-pkg-extra-version="_git$PKGVER" \ + && make dist \ + && cd / \ + && mkdir -p /rpmbuild/{SOURCES,SPECS} \ + && cp /src/frr*.tar.gz /rpmbuild/SOURCES \ + && cp /src/redhat/frr.spec /rpmbuild/SPECS \ + && rpmbuild \ + --define "_topdir /rpmbuild" \ + -ba /rpmbuild/SPECS/frr.spec + +# This stage installs frr from the rpm +FROM centos:centos7 +RUN mkdir -p /pkgs/rpm \ + && yum install -y https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm + +COPY --from=centos-7-builder /rpmbuild/RPMS/ /pkgs/rpm/ + +RUN yum install -y /pkgs/rpm/*/*.rpm \ + && rm -rf /pkgs +COPY docker/centos-7/docker-start /usr/lib/frr/docker-start +ENTRYPOINT [ "/usr/lib/frr/docker-start" ] diff --git a/docker/centos-7/build.sh b/docker/centos-7/build.sh new file mode 100755 index 0000000000..b3022d7c78 --- /dev/null +++ b/docker/centos-7/build.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +set -e + +## +# Package version needs to be decimal +## +GITREV="$(git rev-parse --short=10 HEAD)" +PKGVER="$(printf '%u\n' 0x$GITREV)" + +mkdir -p docker/centos-7/pkgs +docker build \ + --file=docker/centos-7/Dockerfile \ + --build-arg="PKGVER=$PKGVER" \ + --tag="frr:centos-7-builder-$GITREV" \ + --target=centos-7-builder \ + . + +# Copy RPM package from container to host +CONTAINER_ID="$(docker create "frr:centos-7-builder-$GITREV")" +docker cp "${CONTAINER_ID}:/rpmbuild/RPMS/x86_64/" docker/centos-7/pkgs +docker rm "${CONTAINER_ID}" + +docker build \ + --cache-from="frr:centos-7-builder-$GITREV" \ + --file=docker/centos-7/Dockerfile \ + --build-arg="PKGVER=$PKGVER" \ + --tag="frr:centos-7-$GITREV" \ + . + +docker rmi "frr:centos-7-builder-$GITREV" diff --git a/docker/centos-7/docker-start b/docker/centos-7/docker-start new file mode 100755 index 0000000000..a3913245b6 --- /dev/null +++ b/docker/centos-7/docker-start @@ -0,0 +1,12 @@ +#!/bin/sh + +set -e + +## +# Change owner for docker volume mount +## +chown -R frr:frr /etc/frr +/usr/lib/frr/frrinit.sh start + +# Sleep forever +exec tail -f /dev/null diff --git a/docker/centos-8/Dockerfile b/docker/centos-8/Dockerfile new file mode 100644 index 0000000000..6c1f873589 --- /dev/null +++ b/docker/centos-8/Dockerfile @@ -0,0 +1,46 @@ +# This stage builds an rpm from the source +FROM centos:centos8 as centos-8-builder + +RUN dnf install --enablerepo=PowerTools -y rpm-build git autoconf pcre-devel \ + automake libtool make readline-devel texinfo net-snmp-devel pkgconfig \ + groff pkgconfig json-c-devel pam-devel bison flex python2-pytest \ + c-ares-devel python2-devel systemd-devel libcap-devel platform-python-devel \ + https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-devel-0.16.111-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm + +RUN pip2 install sphinx + +COPY . /src + +ARG PKGVER + +RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \ + && cd /src \ + && ./bootstrap.sh \ + && ./configure \ + --enable-rpki \ + --enable-numeric-version \ + --with-pkg-extra-version="_git$PKGVER" \ + && make dist \ + && cd / \ + && mkdir -p /rpmbuild/{SOURCES,SPECS} \ + && cp /src/frr*.tar.gz /rpmbuild/SOURCES \ + && cp /src/redhat/frr.spec /rpmbuild/SPECS \ + && rpmbuild \ + --define "_topdir /rpmbuild" \ + -ba /rpmbuild/SPECS/frr.spec + +# This stage installs frr from the rpm +FROM centos:centos8 +RUN mkdir -p /pkgs/rpm \ + && yum install -y https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \ + https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm + +COPY --from=centos-8-builder /rpmbuild/RPMS/ /pkgs/rpm/ + +RUN yum install -y /pkgs/rpm/*/*.rpm \ + && rm -rf /pkgs +COPY docker/centos-8/docker-start /usr/lib/frr/docker-start +ENTRYPOINT [ "/usr/lib/frr/docker-start" ] diff --git a/docker/centos-8/build.sh b/docker/centos-8/build.sh new file mode 100755 index 0000000000..968d5fe6c4 --- /dev/null +++ b/docker/centos-8/build.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +set -e + +## +# Package version needs to be decimal +## +GITREV="$(git rev-parse --short=10 HEAD)" +PKGVER="$(printf '%u\n' 0x$GITREV)" + +mkdir -p docker/centos-8/pkgs +docker build \ + --file=docker/centos-8/Dockerfile \ + --build-arg="PKGVER=$PKGVER" \ + --tag="frr:centos-8-builder-$GITREV" \ + --target=centos-8-builder \ + . + +# Copy RPM package from container to host +CONTAINER_ID="$(docker create "frr:centos-builder-8-$GITREV")" +docker cp "${CONTAINER_ID}:/rpmbuild/RPMS/x86_64/" docker/centos-8/pkgs +docker rm "${CONTAINER_ID}" + +docker build \ + --cache-from="frr:centos-8-builder-$GITREV" \ + --file=docker/centos-8/Dockerfile \ + --build-arg="PKGVER=$PKGVER" \ + --tag="frr:centos-8-$GITREV" \ + . + +docker rmi "frr:centos-8-builder-$GITREV" diff --git a/docker/centos-8/docker-start b/docker/centos-8/docker-start new file mode 100755 index 0000000000..935b22209e --- /dev/null +++ b/docker/centos-8/docker-start @@ -0,0 +1,9 @@ +#!/bin/sh + +set -e + +chown -R frr:frr /etc/frr +/usr/lib/frr/frrinit.sh start + +# Sleep forever +exec tail -f /dev/null diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c index 6294c0dd0f..ece0b4b0c4 100644 --- a/eigrpd/eigrp_interface.c +++ b/eigrpd/eigrp_interface.c @@ -99,6 +99,9 @@ struct eigrp_interface *eigrp_if_new(struct eigrp *eigrp, struct interface *ifp, ei->params.auth_type = EIGRP_AUTH_TYPE_NONE; ei->params.auth_keychain = NULL; + ei->curr_bandwidth = ifp->bandwidth; + ei->curr_mtu = ifp->mtu; + return ei; } @@ -139,45 +142,40 @@ static int eigrp_ifp_create(struct interface *ifp) static int eigrp_ifp_up(struct interface *ifp) { - /* Interface is already up. */ - if (if_is_operative(ifp)) { - /* Temporarily keep ifp values. */ - struct interface if_tmp; - memcpy(&if_tmp, ifp, sizeof(struct interface)); + struct eigrp_interface *ei = ifp->info; - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: Interface[%s] state update.", - ifp->name); + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to up.", + ifp->name); - if (if_tmp.bandwidth != ifp->bandwidth) { - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: Interface[%s] bandwidth change %d -> %d.", - ifp->name, if_tmp.bandwidth, - ifp->bandwidth); + if (!ei) + return 0; - // eigrp_if_recalculate_output_cost (ifp); - } + if (ei->curr_bandwidth != ifp->bandwidth) { + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: Interface[%s] bandwidth change %d -> %d.", + ifp->name, ei->curr_bandwidth, + ifp->bandwidth); - if (if_tmp.mtu != ifp->mtu) { - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: Interface[%s] MTU change %u -> %u.", - ifp->name, if_tmp.mtu, ifp->mtu); + ei->curr_bandwidth = ifp->bandwidth; + // eigrp_if_recalculate_output_cost (ifp); + } - /* Must reset the interface (simulate down/up) when MTU - * changes. */ - eigrp_if_reset(ifp); - } + if (ei->curr_mtu != ifp->mtu) { + if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: Interface[%s] MTU change %u -> %u.", + ifp->name, ei->curr_mtu, ifp->mtu); + + ei->curr_mtu = ifp->mtu; + /* Must reset the interface (simulate down/up) when MTU + * changes. */ + eigrp_if_reset(ifp); return 0; } - if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: Interface[%s] state change to up.", - ifp->name); - - if (ifp->info) - eigrp_if_up(ifp->info); + eigrp_if_up(ifp->info); return 0; } diff --git a/eigrpd/eigrp_structs.h b/eigrpd/eigrp_structs.h index e50858f071..82bddaaae3 100644 --- a/eigrpd/eigrp_structs.h +++ b/eigrpd/eigrp_structs.h @@ -176,6 +176,8 @@ struct eigrp_interface { /* To which multicast groups do we currently belong? */ + uint32_t curr_bandwidth; + uint32_t curr_mtu; uint8_t multicast_memberships; diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 7e79fdea15..364441f79d 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -29,7 +29,6 @@ #include "command.h" #include "vty.h" #include "memory.h" -#include "memory_vty.h" #include "stream.h" #include "if.h" #include "privs.h" diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 820cfaa426..d14704b4ee 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -1550,9 +1550,13 @@ int lib_interface_isis_destroy(enum nb_event event, circuit = nb_running_unset_entry(dnode); if (!circuit) return NB_ERR_INCONSISTENCY; - if (circuit->state == C_STATE_UP || circuit->state == C_STATE_CONF) - isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area); + /* disable both AFs for this circuit. this will also update the + * CSM state by sending an ISIS_DISABLED signal. If there is no + * area associated to the circuit there is nothing to do + */ + if (circuit->area) + isis_circuit_af_set(circuit, false, false); return NB_OK; } diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 71249cf658..cc22aa5ffd 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -1054,6 +1054,8 @@ dontcheckadj: circuit->rcv_stream, circuit->area, level, lsp_confusion); + if (lsp_confusion) + isis_free_tlvs(tlvs); tlvs = NULL; /* ii */ lsp_flood_or_update(lsp, NULL, diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index b4c699ccbb..630264768f 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -184,7 +184,7 @@ void isis_zebra_route_add_route(struct prefix *prefix, break; api_nh = &api.nexthops[count]; if (fabricd) - api_nh->onlink = true; + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); api_nh->vrf_id = VRF_DEFAULT; switch (nexthop->family) { diff --git a/lib/agg_table.c b/lib/agg_table.c index dad6a13d67..22b981e284 100644 --- a/lib/agg_table.c +++ b/lib/agg_table.c @@ -41,7 +41,7 @@ static void agg_node_destroy(route_table_delegate_t *delegate, XFREE(MTYPE_TMP, anode); } -route_table_delegate_t agg_table_delegate = { +static route_table_delegate_t agg_table_delegate = { .create_node = agg_node_create, .destroy_node = agg_node_destroy, }; diff --git a/lib/buffer.c b/lib/buffer.c index bb2cdb7e54..766b9791a5 100644 --- a/lib/buffer.c +++ b/lib/buffer.c @@ -114,12 +114,6 @@ char *buffer_getstr(struct buffer *b) return s; } -/* Return 1 if buffer is empty. */ -int buffer_empty(struct buffer *b) -{ - return (b->head == NULL); -} - /* Clear and free all allocated data. */ void buffer_reset(struct buffer *b) { diff --git a/lib/command.c b/lib/command.c index 9238ae412a..d2145d9f5a 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1575,18 +1575,6 @@ DEFUN (show_version, return CMD_SUCCESS; } -/* "Set" version ... ignore version tags */ -DEFUN (frr_version_defaults, - frr_version_defaults_cmd, - "frr <version|defaults> LINE...", - "FRRouting global parameters\n" - "version configuration was written by\n" - "set of configuration defaults used\n" - "version string\n") -{ - return CMD_SUCCESS; -} - /* Help display function for all node. */ DEFUN (config_help, config_help_cmd, @@ -1721,8 +1709,10 @@ static int vty_write_config(struct vty *vty) vty_out(vty, "!\n"); } + if (strcmp(frr_defaults_version(), FRR_VER_SHORT)) + vty_out(vty, "! loaded from %s\n", frr_defaults_version()); vty_out(vty, "frr version %s\n", FRR_VER_SHORT); - vty_out(vty, "frr defaults %s\n", DFLT_NAME); + vty_out(vty, "frr defaults %s\n", frr_defaults_profile()); vty_out(vty, "!\n"); for (i = 0; i < vector_active(cmdvec); i++) @@ -2941,7 +2931,6 @@ void cmd_init(int terminal) install_element(CONFIG_NODE, &no_hostname_cmd); install_element(CONFIG_NODE, &domainname_cmd); install_element(CONFIG_NODE, &no_domainname_cmd); - install_element(CONFIG_NODE, &frr_version_defaults_cmd); if (terminal > 0) { install_element(CONFIG_NODE, &debug_memstats_cmd); diff --git a/lib/command_lex.l b/lib/command_lex.l index f361db78e9..0556605d63 100644 --- a/lib/command_lex.l +++ b/lib/command_lex.l @@ -85,7 +85,7 @@ RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\) . {return yytext[0];} %% -YY_BUFFER_STATE buffer; +static YY_BUFFER_STATE buffer; void set_lexer_string (yyscan_t *scn, const char *string) { diff --git a/lib/defaults.c b/lib/defaults.c new file mode 100644 index 0000000000..71ccc73cc6 --- /dev/null +++ b/lib/defaults.c @@ -0,0 +1,229 @@ +/* + * FRR switchable defaults. + * Copyright (c) 2017-2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <zebra.h> + +#include "defaults.h" +#include "version.h" + +static char df_version[128] = FRR_VER_SHORT, df_profile[128] = DFLT_NAME; +static struct frr_default *dflt_first = NULL, **dflt_next = &dflt_first; + +/* these are global for all FRR daemons. they have to be, since we write an + * integrated config with the same value for all daemons. + */ +const char *frr_defaults_profiles[] = { + "traditional", + "datacenter", + NULL, +}; + +static int version_value(int ch) +{ + /* non-ASCII shouldn't happen */ + if (ch < 0 || ch >= 128) + return 2; + + /* ~foo sorts older than nothing */ + if (ch == '~') + return 0; + if (ch == '\0') + return 1; + if (isalpha(ch)) + return 0x100 + tolower(ch); + + /* punctuation and digits (and everything else) */ + return 0x200 + ch; +} + +int frr_version_cmp(const char *aa, const char *bb) +{ + const char *apos = aa, *bpos = bb; + + /* || is correct, we won't scan past the end of a string since that + * doesn't compare equal to anything else */ + while (apos[0] || bpos[0]) { + if (isdigit((unsigned char)apos[0]) && + isdigit((unsigned char)bpos[0])) { + unsigned long av, bv; + char *aend = NULL, *bend = NULL; + + av = strtoul(apos, &aend, 10); + bv = strtoul(bpos, &bend, 10); + if (av < bv) + return -1; + if (av > bv) + return 1; + + apos = aend; + bpos = bend; + continue; + } + + int a = version_value(*apos++); + int b = version_value(*bpos++); + + if (a < b) + return -1; + if (a > b) + return 1; + } + return 0; +} + +static void frr_default_apply_one(struct frr_default *dflt, bool check); + +void frr_default_add(struct frr_default *dflt) +{ + dflt->next = NULL; + *dflt_next = dflt; + dflt_next = &dflt->next; + + frr_default_apply_one(dflt, true); +} + +static bool frr_match_version(const char *name, const char *vspec, + const char *version, bool check) +{ + int cmp; + static struct spec { + const char *str; + bool dir, eq; + } *s, specs[] = { + {"<=", -1, 1}, + {">=", 1, 1}, + {"==", 0, 1}, + {"<", -1, 0}, + {">", 1, 0}, + {"=", 0, 1}, + {NULL, 0, 0}, + }; + + if (!vspec) + /* NULL = all versions */ + return true; + + for (s = specs; s->str; s++) + if (!strncmp(s->str, vspec, strlen(s->str))) + break; + if (!s->str) { + if (check) + fprintf(stderr, "invalid version specifier for %s: %s", + name, vspec); + /* invalid version spec, never matches */ + return false; + } + + vspec += strlen(s->str); + while (isspace((unsigned char)*vspec)) + vspec++; + + cmp = frr_version_cmp(version, vspec); + if (cmp == s->dir || (s->eq && cmp == 0)) + return true; + + return false; +} + +static void frr_default_apply_one(struct frr_default *dflt, bool check) +{ + struct frr_default_entry *entry = dflt->entries; + struct frr_default_entry *dfltentry = NULL, *saveentry = NULL; + + for (; entry->match_version || entry->match_profile; entry++) { + if (entry->match_profile + && strcmp(entry->match_profile, df_profile)) + continue; + + if (!dfltentry && frr_match_version(dflt->name, + entry->match_version, df_version, check)) + dfltentry = entry; + if (!saveentry && frr_match_version(dflt->name, + entry->match_version, FRR_VER_SHORT, check)) + saveentry = entry; + + if (dfltentry && saveentry && !check) + break; + } + /* found default or arrived at last entry that has NULL,NULL spec */ + + if (!dfltentry) + dfltentry = entry; + if (!saveentry) + saveentry = entry; + + if (dflt->dflt_bool) + *dflt->dflt_bool = dfltentry->val_bool; + if (dflt->dflt_str) + *dflt->dflt_str = dfltentry->val_str; + if (dflt->dflt_long) + *dflt->dflt_long = dfltentry->val_long; + if (dflt->dflt_ulong) + *dflt->dflt_ulong = dfltentry->val_ulong; + if (dflt->dflt_float) + *dflt->dflt_float = dfltentry->val_float; + if (dflt->save_bool) + *dflt->save_bool = saveentry->val_bool; + if (dflt->save_str) + *dflt->save_str = saveentry->val_str; + if (dflt->save_long) + *dflt->save_long = saveentry->val_long; + if (dflt->save_ulong) + *dflt->save_ulong = saveentry->val_ulong; + if (dflt->save_float) + *dflt->save_float = saveentry->val_float; +} + +void frr_defaults_apply(void) +{ + struct frr_default *dflt; + + for (dflt = dflt_first; dflt; dflt = dflt->next) + frr_default_apply_one(dflt, false); +} + +bool frr_defaults_profile_valid(const char *profile) +{ + const char **p; + + for (p = frr_defaults_profiles; *p; p++) + if (!strcmp(profile, *p)) + return true; + return false; +} + +const char *frr_defaults_version(void) +{ + return df_version; +} + +const char *frr_defaults_profile(void) +{ + return df_profile; +} + +void frr_defaults_version_set(const char *version) +{ + strlcpy(df_version, version, sizeof(df_version)); + frr_defaults_apply(); +} + +void frr_defaults_profile_set(const char *profile) +{ + strlcpy(df_profile, profile, sizeof(df_profile)); + frr_defaults_apply(); +} diff --git a/lib/defaults.h b/lib/defaults.h new file mode 100644 index 0000000000..7cdd18120e --- /dev/null +++ b/lib/defaults.h @@ -0,0 +1,138 @@ +/* + * FRR switchable defaults. + * Copyright (C) 2017-2019 David Lamparter for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_DEFAULTS_H +#define _FRR_DEFAULTS_H + +#include <stdbool.h> + +#include "compiler.h" + +/* frr_default wraps information about a default that has different + * values depending on FRR version or default-set + * + * frr_default_entry describes one match rule and the resulting value; + * entries are evaluated in order and the first matching is used. + * + * If both match_version and match_profile are specified, they must both + * match. A NULL value matches everything. + */ +struct frr_default_entry { + /* syntax: "(<|<=|==|>=|>) [whitespace] version", e.g. + * ">= 6.1-dev" "<6.0" + */ + const char *match_version; + /* exact profile string to compare against */ + const char *match_profile; + + /* value to use */ + bool val_bool; + const char *val_str; + long val_long; + unsigned long val_ulong; + float val_float; +}; + +/* one struct frr_default exists for each malleable default value */ +struct frr_default { + struct frr_default *next; + + /* for UI/debug use */ + const char *name; + + /* the following two sets of variables differ because the written + * config always targets the *current* FRR version + * + * e.g. if you load a config that has "frr version 5.0" on 6.0 + * *dflt_long => set to the default value in 5.0 + * *save_long => set to the default value in 6.0 + * config save will write "frr version 6.0" with 6.0 defaults + */ + + /* variable holding the default value for reading/use */ + bool *dflt_bool; + const char **dflt_str; + long *dflt_long; + unsigned long *dflt_ulong; + float *dflt_float; + + /* variable to use when comparing for config save */ + bool *save_bool; + const char **save_str; + long *save_long; + unsigned long *save_ulong; + float *save_float; + + struct frr_default_entry entries[]; +}; + +#define _FRR_CFG_DEFAULT(type, typname, varname, ...) \ + static type DFLT_##varname; \ + static type SAVE_##varname; \ + static struct frr_default _dflt_##varname = { \ + .name = #varname, \ + .dflt_##typname = &DFLT_##varname, \ + .save_##typname = &SAVE_##varname, \ + .entries = { __VA_ARGS__ }, \ + }; \ + static void _dfltinit_##varname(void) \ + __attribute__((_CONSTRUCTOR(1000))); \ + static void _dfltinit_##varname(void) \ + { \ + frr_default_add(&_dflt_##varname); \ + } + +/* use: + * FRR_CFG_DEFAULT_LONG(SHARP_BLUNTNESS, + * { .val_long = 2, .match_version = ">= 10.0" }, + * { .val_long = 1, .match_profile = "datacenter" }, + * { .val_long = 0 }, + * ) + * + * This will create DFLT_SHARP_BLUNTNESS and SAVE_SHARP_BLUNTNESS variables. + * + * Note: preprocessor defines cannot be used as variable names because they + * will be expanded and blow up with a compile error. Use an enum or add an + * extra _ at the beginning (e.g. _SHARP_BLUNTNESS => DFLT__SHARP_BLUNTNESS) + */ +#define FRR_CFG_DEFAULT_BOOL(varname, ...) \ + _FRR_CFG_DEFAULT(bool, bool, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_LONG(varname, ...) \ + _FRR_CFG_DEFAULT(long, long, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_ULONG(varname, ...) \ + _FRR_CFG_DEFAULT(unsigned long, ulong, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_FLOAT(varname, ...) \ + _FRR_CFG_DEFAULT(float, float, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_STR(varname, ...) \ + _FRR_CFG_DEFAULT(const char *, str, varname, ## __VA_ARGS__) + + +/* daemons don't need to call any of these, libfrr handles that */ +extern void frr_default_add(struct frr_default *dflt); +extern void frr_defaults_version_set(const char *version); +extern void frr_defaults_profile_set(const char *profile); +extern const char *frr_defaults_version(void); +extern const char *frr_defaults_profile(void); +extern void frr_defaults_apply(void); + +extern const char *frr_defaults_profiles[]; +extern bool frr_defaults_profile_valid(const char *profile); + +/* like strcmp(), but with version ordering */ +extern int frr_version_cmp(const char *aa, const char *bb); + +#endif /* _FRR_DEFAULTS_H */ diff --git a/lib/filter.c b/lib/filter.c index ed3ffe9c67..31e25d6001 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -2498,8 +2498,8 @@ DEFUN (no_ipv6_access_list_remark_comment, return no_ipv6_access_list_remark(self, vty, argc, argv); } -void config_write_access_zebra(struct vty *, struct filter *); -void config_write_access_cisco(struct vty *, struct filter *); +static void config_write_access_zebra(struct vty *, struct filter *); +static void config_write_access_cisco(struct vty *, struct filter *); /* show access-list command. */ static int filter_show(struct vty *vty, const char *name, afi_t afi) @@ -2685,7 +2685,7 @@ DEFUN (show_ipv6_access_list_name, return filter_show(vty, argv[idx_word]->arg, AFI_IP6); } -void config_write_access_cisco(struct vty *vty, struct filter *mfilter) +static void config_write_access_cisco(struct vty *vty, struct filter *mfilter) { struct filter_cisco *filter; @@ -2724,7 +2724,7 @@ void config_write_access_cisco(struct vty *vty, struct filter *mfilter) } } -void config_write_access_zebra(struct vty *vty, struct filter *mfilter) +static void config_write_access_zebra(struct vty *vty, struct filter *mfilter) { struct filter_zebra *filter; struct prefix *p; diff --git a/lib/frrcu.c b/lib/frrcu.c index d65a4a98bf..7e6475b648 100644 --- a/lib/frrcu.c +++ b/lib/frrcu.c @@ -206,7 +206,7 @@ void rcu_thread_unprepare(struct rcu_thread *rt) rcu_bump(); if (rt != &rcu_thread_main) /* this free() happens after seqlock_release() below */ - rcu_free_internal(&_mt_RCU_THREAD, rt, rcu_head); + rcu_free_internal(MTYPE_RCU_THREAD, rt, rcu_head); rcu_threads_del(&rcu_threads, rt); seqlock_release(&rt->rcu); @@ -269,7 +269,7 @@ static void rcu_bump(void) * "last item is being deleted - start over" case, and then we may end * up accessing old RCU queue items that are already free'd. */ - rcu_free_internal(&_mt_RCU_NEXT, rn, head_free); + rcu_free_internal(MTYPE_RCU_NEXT, rn, head_free); /* Only allow the RCU sweeper to run after these 2 items are queued. * diff --git a/lib/frrcu.h b/lib/frrcu.h index 8f789303cc..06d87c39f1 100644 --- a/lib/frrcu.h +++ b/lib/frrcu.h @@ -139,6 +139,8 @@ extern void rcu_enqueue(struct rcu_head *head, const struct rcu_action *action); #define rcu_free(mtype, ptr, field) \ do { \ typeof(ptr) _ptr = (ptr); \ + if (!_ptr) \ + break; \ struct rcu_head *_rcu_head = &_ptr->field; \ static const struct rcu_action _rcu_action = { \ .type = RCUA_FREE, \ diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index c6fd3c04ad..8ccdbfcbc1 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -28,7 +28,6 @@ #endif #include "command.h" -#include "memory_vty.h" #include "graph.h" #include "linklist.h" #include "command_match.h" @@ -39,9 +38,9 @@ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc") /** headers **/ void grammar_sandbox_init(void); -void pretty_print_graph(struct vty *vty, struct graph_node *, int, int, - struct graph_node **, size_t); -void init_cmdgraph(struct vty *, struct graph **); +static void pretty_print_graph(struct vty *vty, struct graph_node *, int, int, + struct graph_node **, size_t); +static void init_cmdgraph(struct vty *, struct graph **); /** shim interface commands **/ static struct graph *nodegraph = NULL, *nodegraph_free = NULL; @@ -492,8 +491,9 @@ void grammar_sandbox_init(void) * @param start the node to take as the root * @param level indent level for recursive calls, always pass 0 */ -void pretty_print_graph(struct vty *vty, struct graph_node *start, int level, - int desc, struct graph_node **stack, size_t stackpos) +static void pretty_print_graph(struct vty *vty, struct graph_node *start, + int level, int desc, struct graph_node **stack, + size_t stackpos) { // print this node char tokennum[32]; @@ -551,7 +551,7 @@ void pretty_print_graph(struct vty *vty, struct graph_node *start, int level, } /** stuff that should go in command.c + command.h */ -void init_cmdgraph(struct vty *vty, struct graph **graph) +static void init_cmdgraph(struct vty *vty, struct graph **graph) { // initialize graph, add start noe *graph = graph_new(); diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index 6d28a667b3..4bd8f5138a 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -28,7 +28,7 @@ #endif #include "command.h" -#include "memory_vty.h" +#include "lib_vty.h" static void vty_do_exit(int isexit) { @@ -57,7 +57,7 @@ int main(int argc, char **argv) host.domainname = strdup("testdomainname"); vty_init(master, true); - memory_init(); + lib_cmd_init(); yang_init(); nb_init(master, NULL, 0); @@ -58,7 +58,7 @@ DEFINE_QOBJ_TYPE(interface) DEFINE_HOOK(if_add, (struct interface * ifp), (ifp)) DEFINE_KOOH(if_del, (struct interface * ifp), (ifp)) -struct interface_master{ +static struct interface_master{ int (*create_hook)(struct interface *ifp); int (*up_hook)(struct interface *ifp); int (*down_hook)(struct interface *ifp); @@ -262,7 +262,9 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id) "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf", ifp->name, old_vrf->name); if (if_dnode) { + nb_running_unset_entry(if_dnode->parent); yang_dnode_change_leaf(if_dnode, vrf->name); + nb_running_set_entry(if_dnode->parent, ifp); running_config->version++; } } diff --git a/lib/memory_vty.c b/lib/lib_vty.c index 1adc0d7b74..787da08e28 100644 --- a/lib/memory_vty.c +++ b/lib/lib_vty.c @@ -1,5 +1,5 @@ /* - * Memory and dynamic module VTY routine + * Assorted library VTY commands * * Copyright (C) 1998 Kunihiro Ishiguro * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc. @@ -35,7 +35,8 @@ #include "log.h" #include "memory.h" #include "module.h" -#include "memory_vty.h" +#include "defaults.h" +#include "lib_vty.h" /* Looking up memory status from vty interface. */ #include "vector.h" @@ -120,11 +121,11 @@ static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt) } -DEFUN (show_memory, - show_memory_cmd, - "show memory", - "Show running system information\n" - "Memory statistics\n") +DEFUN_NOSH (show_memory, + show_memory_cmd, + "show memory", + "Show running system information\n" + "Memory statistics\n") { #ifdef HAVE_MALLINFO show_memory_mallinfo(vty); @@ -134,11 +135,11 @@ DEFUN (show_memory, return CMD_SUCCESS; } -DEFUN (show_modules, - show_modules_cmd, - "show modules", - "Show running system information\n" - "Loaded modules\n") +DEFUN_NOSH (show_modules, + show_modules_cmd, + "show modules", + "Show running system information\n" + "Loaded modules\n") { struct frrmod_runtime *plug = frrmod_list; @@ -177,8 +178,60 @@ DEFUN (show_modules, return CMD_SUCCESS; } -void memory_init(void) +DEFUN (frr_defaults, + frr_defaults_cmd, + "frr defaults PROFILE...", + "FRRouting global parameters\n" + "set of configuration defaults used\n" + "profile string\n") { + char *profile = argv_concat(argv, argc, 2); + int rv = CMD_SUCCESS; + + if (!frr_defaults_profile_valid(profile)) { + vty_out(vty, "%% WARNING: profile %s is not known in this version\n", + profile); + rv = CMD_WARNING; + } + frr_defaults_profile_set(profile); + XFREE(MTYPE_TMP, profile); + return rv; +} + +DEFUN (frr_version, + frr_version_cmd, + "frr version VERSION...", + "FRRouting global parameters\n" + "version configuration was written by\n" + "version string\n") +{ + char *version = argv_concat(argv, argc, 2); + + frr_defaults_version_set(version); + XFREE(MTYPE_TMP, version); + return CMD_SUCCESS; +} + +static void defaults_autocomplete(vector comps, struct cmd_token *token) +{ + const char **p; + + for (p = frr_defaults_profiles; *p; p++) + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, *p)); +} + +static const struct cmd_variable_handler default_var_handlers[] = { + {.tokenname = "PROFILE", .completions = defaults_autocomplete}, + {.completions = NULL}, +}; + +void lib_cmd_init(void) +{ + cmd_variable_handler_register(default_var_handlers); + + install_element(CONFIG_NODE, &frr_defaults_cmd); + install_element(CONFIG_NODE, &frr_version_cmd); + install_element(VIEW_NODE, &show_memory_cmd); install_element(VIEW_NODE, &show_modules_cmd); } diff --git a/lib/memory_vty.h b/lib/lib_vty.h index 941255be1d..48e409ec52 100644 --- a/lib/memory_vty.h +++ b/lib/lib_vty.h @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _ZEBRA_MEMORY_VTY_H -#define _ZEBRA_MEMORY_VTY_H +#ifndef _ZEBRA_LIB_VTY_H +#define _ZEBRA_LIB_VTY_H #include "memory.h" @@ -27,7 +27,7 @@ extern "C" { #endif -extern void memory_init(void); +extern void lib_cmd_init(void); /* Human friendly string for given byte count */ #define MTYPE_MEMSTR_LEN 20 @@ -37,4 +37,4 @@ extern const char *mtype_memstr(char *, size_t, unsigned long); } #endif -#endif /* _ZEBRA_MEMORY_VTY_H */ +#endif /* _ZEBRA_LIB_VTY_H */ diff --git a/lib/libfrr.c b/lib/libfrr.c index 8ef32eaa8a..4fb43edff2 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -30,7 +30,7 @@ #include "vty.h" #include "command.h" #include "version.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "log_vty.h" #include "zclient.h" #include "log_int.h" @@ -43,6 +43,7 @@ #include "debug.h" #include "frrcu.h" #include "frr_pthread.h" +#include "defaults.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) DEFINE_KOOH(frr_early_fini, (), ()) @@ -104,6 +105,7 @@ static const struct option lo_always[] = { {"version", no_argument, NULL, 'v'}, {"daemon", no_argument, NULL, 'd'}, {"module", no_argument, NULL, 'M'}, + {"profile", required_argument, NULL, 'F'}, {"vty_socket", required_argument, NULL, OPTION_VTYSOCK}, {"moduledir", required_argument, NULL, OPTION_MODULEDIR}, {"log", required_argument, NULL, OPTION_LOG}, @@ -112,11 +114,12 @@ static const struct option lo_always[] = { {"command-log-always", no_argument, NULL, OPTION_LOGGING}, {NULL}}; static const struct optspec os_always = { - "hvdM:", + "hvdM:F:", " -h, --help Display this help and exit\n" " -v, --version Print program version\n" " -d, --daemon Runs in daemon mode\n" " -M, --module Load specified module\n" + " -F, --profile Use specified configuration profile\n" " --vty_socket Override vty socket path\n" " --moduledir Override modules directory\n" " --log Set Logging to stdout, syslog, or file:<name>\n" @@ -175,7 +178,6 @@ static const struct optspec os_user = {"u:g:", " -g, --group Group to run as\n", lo_user}; - bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, const char *path) { @@ -390,6 +392,32 @@ static int frr_opt(int opt) *modnext = oc; modnext = &oc->next; break; + case 'F': + if (!frr_defaults_profile_valid(optarg)) { + const char **p; + FILE *ofd = stderr; + + if (!strcmp(optarg, "help")) + ofd = stdout; + else + fprintf(stderr, + "The \"%s\" configuration profile is not valid for this FRR version.\n", + optarg); + + fprintf(ofd, "Available profiles are:\n"); + for (p = frr_defaults_profiles; *p; p++) + fprintf(ofd, "%s%s\n", + strcmp(*p, DFLT_NAME) ? " " : " * ", + *p); + + if (ofd == stdout) + exit(0); + fprintf(ofd, "\n"); + errors++; + break; + } + frr_defaults_profile_set(optarg); + break; case 'i': if (di->flags & FRR_NO_CFG_PID_DRY) return 1; @@ -608,6 +636,7 @@ struct thread_master *frr_init(void) dir = di->module_path ? di->module_path : frr_moduledir; srandom(time(NULL)); + frr_defaults_apply(); if (di->instance) { snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]", @@ -679,7 +708,7 @@ struct thread_master *frr_init(void) cmd_init(1); vty_init(master, di->log_always); - memory_init(); + lib_cmd_init(); log_filter_cmd_init(); frr_pthread_init(); @@ -1077,7 +1106,6 @@ void frr_fini(void) hook_call(frr_fini); - /* memory_init -> nothing needed */ vty_terminate(); cmd_terminate(); nb_terminate(); diff --git a/lib/linklist.c b/lib/linklist.c index 0d1efdf3aa..272e153276 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -339,29 +339,6 @@ void list_delete_node(struct list *list, struct listnode *node) listnode_free(node); } -void list_add_list(struct list *list, struct list *add) -{ - struct listnode *n; - - 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; diff --git a/lib/linklist.h b/lib/linklist.h index ef914b965f..00cb9f8714 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -208,17 +208,6 @@ extern struct listnode *listnode_lookup(struct list *list, const void *data); 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 @@ -296,19 +285,6 @@ extern void list_delete_all_node(struct list *list); extern void list_delete_node(struct list *list, struct listnode *node); /* - * 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); - -/* * Delete all nodes which satisfy a condition from a list. * Deletes the node if cond function returns true for the node. * If function ptr passed is NULL, it deletes all nodes diff --git a/lib/memory.h b/lib/memory.h index 8de5c4c2bf..44ea19b557 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -102,45 +102,42 @@ struct memgroup { } #define DECLARE_MTYPE(name) \ - extern struct memtype _mt_##name; \ - extern struct memtype *const MTYPE_##name; \ + extern struct memtype MTYPE_##name[1]; \ /* end */ #define DEFINE_MTYPE_ATTR(group, mname, attr, desc) \ - attr struct memtype _mt_##mname \ - __attribute__((section(".data.mtypes"))) = { \ + attr struct memtype MTYPE_##mname[1] \ + __attribute__((section(".data.mtypes"))) = { { \ .name = desc, \ .next = NULL, \ .n_alloc = 0, \ .size = 0, \ .ref = NULL, \ - }; \ + } }; \ static void _mtinit_##mname(void) __attribute__((_CONSTRUCTOR(1001))); \ static void _mtinit_##mname(void) \ { \ if (_mg_##group.insert == NULL) \ _mg_##group.insert = &_mg_##group.types; \ - _mt_##mname.ref = _mg_##group.insert; \ - *_mg_##group.insert = &_mt_##mname; \ - _mg_##group.insert = &_mt_##mname.next; \ + MTYPE_##mname->ref = _mg_##group.insert; \ + *_mg_##group.insert = MTYPE_##mname; \ + _mg_##group.insert = &MTYPE_##mname->next; \ } \ static void _mtfini_##mname(void) __attribute__((_DESTRUCTOR(1001))); \ static void _mtfini_##mname(void) \ { \ - if (_mt_##mname.next) \ - _mt_##mname.next->ref = _mt_##mname.ref; \ - *_mt_##mname.ref = _mt_##mname.next; \ + if (MTYPE_##mname->next) \ + MTYPE_##mname->next->ref = MTYPE_##mname->ref; \ + *MTYPE_##mname->ref = MTYPE_##mname->next; \ } \ /* end */ #define DEFINE_MTYPE(group, name, desc) \ DEFINE_MTYPE_ATTR(group, name, , desc) \ - struct memtype *const MTYPE_##name = &_mt_##name; \ /* end */ #define DEFINE_MTYPE_STATIC(group, name, desc) \ DEFINE_MTYPE_ATTR(group, name, static, desc) \ - static struct memtype *const MTYPE_##name = &_mt_##name; \ /* end */ DECLARE_MGROUP(LIB) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 55c66fdc3d..d1a31ae35f 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -51,7 +51,7 @@ static struct ns *ns_lookup_name_internal(const char *name); RB_GENERATE(ns_head, ns, entry, ns_compare) -struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); static struct ns *default_ns; static int ns_current_ns_fd; @@ -74,7 +74,8 @@ static inline int ns_map_compare(const struct ns_map_nsid *a, RB_HEAD(ns_map_nsid_head, ns_map_nsid); RB_PROTOTYPE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare); RB_GENERATE(ns_map_nsid_head, ns_map_nsid, id_entry, ns_map_compare); -struct ns_map_nsid_head ns_map_nsid_list = RB_INITIALIZER(&ns_map_nsid_list); +static struct ns_map_nsid_head ns_map_nsid_list = + RB_INITIALIZER(&ns_map_nsid_list); static ns_id_t ns_id_external_numbering; @@ -123,7 +124,7 @@ static int have_netns(void) } /* Holding NS hooks */ -struct ns_master { +static struct ns_master { int (*ns_new_hook)(struct ns *ns); int (*ns_delete_hook)(struct ns *ns); int (*ns_enable_hook)(struct ns *ns); diff --git a/lib/netns_other.c b/lib/netns_other.c index b0aae4f8df..740d2b621e 100644 --- a/lib/netns_other.c +++ b/lib/netns_other.c @@ -34,7 +34,7 @@ static inline int ns_compare(const struct ns *ns, const struct ns *ns2); RB_GENERATE(ns_head, ns, entry, ns_compare) -struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +static struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); static inline int ns_compare(const struct ns *a, const struct ns *b) { diff --git a/lib/nexthop.c b/lib/nexthop.c index d05fa8e6d7..d2ab70e209 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -33,6 +33,7 @@ #include "mpls.h" #include "jhash.h" #include "printfrr.h" +#include "vrf.h" DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop") DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label") @@ -117,6 +118,12 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1, if (next1->type > next2->type) return 1; + if (next1->weight < next2->weight) + return -1; + + if (next1->weight > next2->weight) + return 1; + switch (next1->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV6: @@ -223,7 +230,23 @@ bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2) struct nexthop *nexthop_new(void) { - return XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop)); + struct nexthop *nh; + + nh = XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop)); + + /* + * Default the weight to 1 here for all nexthops. + * The linux kernel does some weird stuff with adding +1 to + * all nexthop weights it gets over netlink. + * To handle this, just default everything to 1 right from + * from the beggining so we don't have to special case + * default weights in the linux netlink code. + * + * 1 should be a valid on all platforms anyway. + */ + nh->weight = 1; + + return nh; } /* Free nexthop. */ @@ -281,6 +304,93 @@ bool nexthop_same_no_labels(const struct nexthop *nh1, return true; } +/* + * Allocate a new nexthop object and initialize it from various args. + */ +struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->type = NEXTHOP_TYPE_IFINDEX; + nexthop->ifindex = ifindex; + nexthop->vrf_id = vrf_id; + + return nexthop; +} + +struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4, + const struct in_addr *src, + vrf_id_t vrf_id) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->type = NEXTHOP_TYPE_IPV4; + nexthop->vrf_id = vrf_id; + nexthop->gate.ipv4 = *ipv4; + if (src) + nexthop->src.ipv4 = *src; + + return nexthop; +} + +struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4, + const struct in_addr *src, + ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + nexthop->vrf_id = vrf_id; + nexthop->gate.ipv4 = *ipv4; + if (src) + nexthop->src.ipv4 = *src; + nexthop->ifindex = ifindex; + + return nexthop; +} + +struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6, + vrf_id_t vrf_id) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->vrf_id = vrf_id; + nexthop->type = NEXTHOP_TYPE_IPV6; + nexthop->gate.ipv6 = *ipv6; + + return nexthop; +} + +struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6, + ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->vrf_id = vrf_id; + nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + nexthop->gate.ipv6 = *ipv6; + nexthop->ifindex = ifindex; + + return nexthop; +} + +struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->vrf_id = VRF_DEFAULT; + nexthop->type = NEXTHOP_TYPE_BLACKHOLE; + nexthop->bh_type = bh_type; + + return nexthop; +} + /* Update nexthop with label information. */ void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type, uint8_t num_labels, mpls_label_t *label) @@ -288,6 +398,9 @@ void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type, struct mpls_label_stack *nh_label; int i; + if (num_labels == 0) + return; + nexthop->nh_label_type = type; nh_label = XCALLOC(MTYPE_NH_LABEL, sizeof(struct mpls_label_stack) @@ -459,6 +572,7 @@ void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop, copy->ifindex = nexthop->ifindex; copy->type = nexthop->type; copy->flags = nexthop->flags; + copy->weight = nexthop->weight; memcpy(©->gate, &nexthop->gate, sizeof(nexthop->gate)); memcpy(©->src, &nexthop->src, sizeof(nexthop->src)); memcpy(©->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src)); diff --git a/lib/nexthop.h b/lib/nexthop.h index fe029f1867..040b643a84 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -110,6 +110,9 @@ struct nexthop { /* Label(s) associated with this nexthop. */ struct mpls_label_stack *nh_label; + + /* Weight of the nexthop ( for unequal cost ECMP ) */ + uint8_t weight; }; struct nexthop *nexthop_new(void); @@ -122,6 +125,22 @@ void nexthop_add_labels(struct nexthop *, enum lsp_types_t, uint8_t, void nexthop_del_labels(struct nexthop *); /* + * Allocate a new nexthop object and initialize it from various args. + */ +struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id); +struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4, + const struct in_addr *src, + vrf_id_t vrf_id); +struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4, + const struct in_addr *src, + ifindex_t ifindex, vrf_id_t vrf_id); +struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6, + vrf_id_t vrf_id); +struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6, + ifindex_t ifindex, vrf_id_t vrf_id); +struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type); + +/* * Hash a nexthop. Suitable for use with hash tables. * * This function uses the following values when computing the hash: diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 9552f89568..0051cba625 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -34,6 +34,17 @@ DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group") +/* + * Internal struct used to hold nhg config strings + */ +struct nexthop_hold { + char *nhvrf_name; + union sockunion *addr; + char *intf; + char *labels; + uint32_t weight; +}; + struct nexthop_group_hooks { void (*new)(const char *name); void (*add_nexthop)(const struct nexthop_group_cmd *nhg, @@ -233,25 +244,16 @@ void _nexthop_add(struct nexthop **target, struct nexthop *nexthop) nexthop->prev = last; } -void _nexthop_group_add_sorted(struct nexthop_group *nhg, - struct nexthop *nexthop) +/* Add nexthop to sorted list of nexthops */ +static void _nexthop_add_sorted(struct nexthop **head, + struct nexthop *nexthop) { - struct nexthop *position, *prev, *tail; - - /* Try to just append to the end first - * This trust it is already sorted - */ - - tail = nexthop_group_tail(nhg); - - if (tail && (nexthop_cmp(tail, nexthop) < 0)) { - tail->next = nexthop; - nexthop->prev = tail; + struct nexthop *position, *prev; - return; - } + /* Ensure this gets set */ + nexthop->next = NULL; - for (position = nhg->nexthop, prev = NULL; position; + for (position = *head, prev = NULL; position; prev = position, position = position->next) { if (nexthop_cmp(position, nexthop) > 0) { nexthop->next = position; @@ -260,7 +262,7 @@ void _nexthop_group_add_sorted(struct nexthop_group *nhg, if (nexthop->prev) nexthop->prev->next = nexthop; else - nhg->nexthop = nexthop; + *head = nexthop; position->prev = nexthop; return; @@ -271,7 +273,27 @@ void _nexthop_group_add_sorted(struct nexthop_group *nhg, if (prev) prev->next = nexthop; else - nhg->nexthop = nexthop; + *head = nexthop; +} + +void nexthop_group_add_sorted(struct nexthop_group *nhg, + struct nexthop *nexthop) +{ + struct nexthop *tail; + + /* Try to just append to the end first; + * trust the list is already sorted + */ + tail = nexthop_group_tail(nhg); + + if (tail && (nexthop_cmp(tail, nexthop) < 0)) { + tail->next = nexthop; + nexthop->prev = tail; + + return; + } + + _nexthop_add_sorted(&nhg->nexthop, nexthop); } /* Delete nexthop from a nexthop list. */ @@ -298,6 +320,40 @@ void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh) nh->next = NULL; } +/* + * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order + */ +void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg, + const struct nexthop *nh) +{ + struct nexthop *nexthop, *tail; + const struct nexthop *nh1; + + /* We'll try to append to the end of the new list; + * if the original list in nh is already sorted, this eliminates + * lots of comparison operations. + */ + tail = nexthop_group_tail(nhg); + + for (nh1 = nh; nh1; nh1 = nh1->next) { + nexthop = nexthop_dup(nh1, NULL); + + if (tail && (nexthop_cmp(tail, nexthop) < 0)) { + tail->next = nexthop; + nexthop->prev = tail; + + tail = nexthop; + continue; + } + + _nexthop_add_sorted(&nhg->nexthop, nexthop); + + if (tail == NULL) + tail = nexthop; + } +} + +/* Copy a list of nexthops, no effort made to sort or order them. */ void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh, struct nexthop *rparent) { @@ -425,7 +481,11 @@ static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2) if (ret) return ret; - return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name); + ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name); + if (ret) + return ret; + + return nhgc_cmp_helper(nh1->labels, nh2->labels); } static void nhgl_delete(struct nexthop_hold *nh) @@ -437,6 +497,8 @@ static void nhgl_delete(struct nexthop_hold *nh) if (nh->addr) sockunion_free(nh->addr); + XFREE(MTYPE_TMP, nh->labels); + XFREE(MTYPE_TMP, nh); } @@ -510,7 +572,8 @@ DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME", static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, const char *nhvrf_name, const union sockunion *addr, - const char *intf) + const char *intf, const char *labels, + const uint32_t weight) { struct nexthop_hold *nh; @@ -522,6 +585,10 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, nh->intf = XSTRDUP(MTYPE_TMP, intf); if (addr) nh->addr = sockunion_dup(addr); + if (labels) + nh->labels = XSTRDUP(MTYPE_TMP, labels); + + nh->weight = weight; listnode_add_sort(nhgc->nhg_list, nh); } @@ -529,15 +596,18 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, const char *nhvrf_name, const union sockunion *addr, - const char *intf) + const char *intf, const char *labels, + const uint32_t weight) { struct nexthop_hold *nh; struct listnode *node; for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) { - if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 && - nhgc_addr_cmp_helper(addr, nh->addr) == 0 && - nhgc_cmp_helper(intf, nh->intf) == 0) + if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 + && nhgc_addr_cmp_helper(addr, nh->addr) == 0 + && nhgc_cmp_helper(intf, nh->intf) == 0 + && nhgc_cmp_helper(labels, nh->labels) == 0 + && weight == nh->weight) break; } @@ -551,10 +621,19 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, nhgl_delete(nh); } +/* + * Parse the config strings we support for a single nexthop. This gets used + * in a couple of different ways, and we distinguish between transient + * failures - such as a still-unprocessed interface - and fatal errors + * from label-string parsing. + */ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, const union sockunion *addr, - const char *intf, const char *name) + const char *intf, const char *name, + const char *labels, int *lbl_ret, + uint32_t weight) { + int ret = 0; struct vrf *vrf; memset(nhop, 0, sizeof(*nhop)); @@ -592,16 +671,50 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, } else nhop->type = NEXTHOP_TYPE_IFINDEX; + if (labels) { + uint8_t num = 0; + mpls_label_t larray[MPLS_MAX_LABELS]; + + ret = mpls_str2label(labels, &num, larray); + + /* Return label parse result */ + if (lbl_ret) + *lbl_ret = ret; + + if (ret < 0) + return false; + else if (num > 0) + nexthop_add_labels(nhop, ZEBRA_LSP_NONE, + num, larray); + } + + nhop->weight = weight; + return true; } +/* + * Wrapper to parse the strings in a 'nexthop_hold' + */ +static bool nexthop_group_parse_nhh(struct nexthop *nhop, + const struct nexthop_hold *nhh) +{ + return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf, + nhh->nhvrf_name, nhh->labels, NULL, + nhh->weight)); +} + DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "[no] nexthop\ <\ <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\ |INTERFACE$intf\ >\ - [nexthop-vrf NAME$vrf_name]", + [{ \ + nexthop-vrf NAME$vrf_name \ + |label WORD \ + |weight (1-255) \ + }]", NO_STR "Specify one of the nexthops in this ECMP group\n" "v4 Address\n" @@ -609,14 +722,20 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "Interface to use\n" "Interface to use\n" "If the nexthop is in a different vrf tell us\n" - "The nexthop-vrf Name\n") + "The nexthop-vrf Name\n" + "Specify label(s) for this nexthop\n" + "One or more labels in the range (16-1048575) separated by '/'\n" + "Weight to be used by the nexthop for purposes of ECMP\n" + "Weight value to be used\n") { VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); struct nexthop nhop; struct nexthop *nh; + int lbl_ret = 0; bool legal; - legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name); + legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label, + &lbl_ret, weight); if (nhop.type == NEXTHOP_TYPE_IPV6 && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { @@ -625,10 +744,32 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, return CMD_WARNING_CONFIG_FAILED; } + /* Handle label-string errors */ + if (!legal && lbl_ret < 0) { + switch (lbl_ret) { + case -1: + vty_out(vty, "%% Malformed label(s)\n"); + break; + case -2: + vty_out(vty, + "%% Cannot use reserved label(s) (%d-%d)\n", + MPLS_LABEL_RESERVED_MIN, + MPLS_LABEL_RESERVED_MAX); + break; + case -3: + vty_out(vty, + "%% Too many labels. Enter %d or fewer\n", + MPLS_MAX_LABELS); + break; + } + return CMD_WARNING_CONFIG_FAILED; + } + nh = nexthop_exists(&nhgc->nhg, &nhop); if (no) { - nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf); + nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label, + weight); if (nh) { _nexthop_del(&nhgc->nhg, nh); @@ -646,7 +787,8 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, _nexthop_add(&nhgc->nhg.nexthop, nh); } - nexthop_group_save_nhop(nhgc, vrf_name, addr, intf); + nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label, + weight); if (legal && nhg_hooks.add_nexthop) nhg_hooks.add_nexthop(nhgc, nh); @@ -655,7 +797,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, return CMD_SUCCESS; } -struct cmd_node nexthop_group_node = { +static struct cmd_node nexthop_group_node = { NH_GROUP_NODE, "%s(config-nh-group)# ", 1 @@ -696,6 +838,19 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) vrf = vrf_lookup_by_id(nh->vrf_id); vty_out(vty, " nexthop-vrf %s", vrf->name); } + + if (nh->nh_label && nh->nh_label->num_labels > 0) { + char buf[200]; + + mpls_label2str(nh->nh_label->num_labels, + nh->nh_label->label, + buf, sizeof(buf), 0); + vty_out(vty, " label %s", buf); + } + + if (nh->weight) + vty_out(vty, " weight %u", nh->weight); + vty_out(vty, "\n"); } @@ -715,6 +870,12 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty, if (nh->nhvrf_name) vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name); + if (nh->labels) + vty_out(vty, " label %s", nh->labels); + + if (nh->weight) + vty_out(vty, " weight %u", nh->weight); + vty_out(vty, "\n"); } @@ -751,9 +912,7 @@ void nexthop_group_enable_vrf(struct vrf *vrf) struct nexthop nhop; struct nexthop *nh; - if (!nexthop_group_parse_nexthop(&nhop, nhh->addr, - nhh->intf, - nhh->nhvrf_name)) + if (!nexthop_group_parse_nhh(&nhop, nhh)) continue; nh = nexthop_exists(&nhgc->nhg, &nhop); @@ -787,9 +946,7 @@ void nexthop_group_disable_vrf(struct vrf *vrf) struct nexthop nhop; struct nexthop *nh; - if (!nexthop_group_parse_nexthop(&nhop, nhh->addr, - nhh->intf, - nhh->nhvrf_name)) + if (!nexthop_group_parse_nhh(&nhop, nhh)) continue; nh = nexthop_exists(&nhgc->nhg, &nhop); @@ -824,9 +981,7 @@ void nexthop_group_interface_state_change(struct interface *ifp, for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { struct nexthop nhop; - if (!nexthop_group_parse_nexthop( - &nhop, nhh->addr, nhh->intf, - nhh->nhvrf_name)) + if (!nexthop_group_parse_nhh(&nhop, nhh)) continue; switch (nhop.type) { diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 391775c69c..73b020283a 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -44,12 +44,21 @@ void nexthop_group_delete(struct nexthop_group **nhg); void nexthop_group_copy(struct nexthop_group *to, struct nexthop_group *from); + +/* + * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order + */ +void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg, + const struct nexthop *nh); + void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh, struct nexthop *rparent); uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg); uint32_t nexthop_group_hash(const struct nexthop_group *nhg); void nexthop_group_mark_duplicates(struct nexthop_group *nhg); +void nexthop_group_add_sorted(struct nexthop_group *nhg, + struct nexthop *nexthop); /* The following for loop allows to iterate over the nexthop * structure of routes. @@ -70,12 +79,6 @@ void nexthop_group_mark_duplicates(struct nexthop_group *nhg); (nhop) = nexthop_next(nhop) -struct nexthop_hold { - char *nhvrf_name; - union sockunion *addr; - char *intf; -}; - struct nexthop_group_cmd { RB_ENTRY(nexthop_group_cmd) nhgc_entry; diff --git a/lib/nexthop_group_private.h b/lib/nexthop_group_private.h index cdd0df0ab3..4abda624ae 100644 --- a/lib/nexthop_group_private.h +++ b/lib/nexthop_group_private.h @@ -35,8 +35,6 @@ extern "C" { void _nexthop_add(struct nexthop **target, struct nexthop *nexthop); void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nexthop); -void _nexthop_group_add_sorted(struct nexthop_group *nhg, - struct nexthop *nexthop); #ifdef __cplusplus } diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 009e5bd824..17dc256281 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -21,6 +21,7 @@ #include "libfrr.h" #include "version.h" +#include "defaults.h" #include "log.h" #include "lib_errors.h" #include "command.h" @@ -486,7 +487,7 @@ static void nb_cli_show_config_cmds(struct vty *vty, struct nb_config *config, vty_out(vty, "Configuration:\n"); vty_out(vty, "!\n"); vty_out(vty, "frr version %s\n", FRR_VER_SHORT); - vty_out(vty, "frr defaults %s\n", DFLT_NAME); + vty_out(vty, "frr defaults %s\n", frr_defaults_profile()); LY_TREE_FOR (config->dnode, root) nb_cli_show_dnode_cmds(vty, root, with_defaults); @@ -71,8 +71,6 @@ struct ns { RB_HEAD(ns_head, ns); RB_PROTOTYPE(ns_head, ns, entry, ns_compare) -extern struct ns_head ns_tree; - /* * API for managing NETNS. eg from zebra daemon * one want to manage the list of NETNS, etc... diff --git a/lib/plist.c b/lib/plist.c index 64571a05b7..a0976cd6bd 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -1891,6 +1891,8 @@ int prefix_bgp_orf_set(char *name, afi_t afi, struct orf_prefix *orfp, if (!plist) return CMD_WARNING_CONFIG_FAILED; + apply_mask(&orfp->p); + if (set) { pentry = prefix_list_entry_make( &orfp->p, (permit ? PREFIX_PERMIT : PREFIX_DENY), diff --git a/lib/prefix.c b/lib/prefix.c index e2bf3b949c..219f798dcc 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -37,394 +37,6 @@ DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec") static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; -static const struct in6_addr maskbytes6[] = { - /* /0 */ {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /1 */ - {{{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /2 */ - {{{0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /3 */ - {{{0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /4 */ - {{{0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /5 */ - {{{0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /6 */ - {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /7 */ - {{{0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /8 */ - {{{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /9 */ - {{{0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /10 */ - {{{0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /11 */ - {{{0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /12 */ - {{{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /13 */ - {{{0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /14 */ - {{{0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /15 */ - {{{0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /16 */ - {{{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /17 */ - {{{0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /18 */ - {{{0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /19 */ - {{{0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /20 */ - {{{0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /21 */ - {{{0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /22 */ - {{{0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /23 */ - {{{0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /24 */ - {{{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /25 */ - {{{0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /26 */ - {{{0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /27 */ - {{{0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /28 */ - {{{0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /29 */ - {{{0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /30 */ - {{{0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /31 */ - {{{0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /32 */ - {{{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /33 */ - {{{0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /34 */ - {{{0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /35 */ - {{{0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /36 */ - {{{0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /37 */ - {{{0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /38 */ - {{{0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /39 */ - {{{0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /40 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /41 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /42 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /43 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /44 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /45 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /46 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /47 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /48 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /49 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /50 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /51 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /52 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /53 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /54 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /55 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /56 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /57 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /58 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /59 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /60 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /61 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /62 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /63 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /64 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /65 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /66 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /67 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /68 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /69 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /70 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /71 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /72 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /73 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /74 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /75 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /76 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /77 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /78 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /79 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /80 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /81 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /82 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /83 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /84 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /85 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /86 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /87 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /88 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00}}}, - /* /89 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x80, 0x00, 0x00, 0x00, 0x00}}}, - /* /90 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xc0, 0x00, 0x00, 0x00, 0x00}}}, - /* /91 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xe0, 0x00, 0x00, 0x00, 0x00}}}, - /* /92 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf0, 0x00, 0x00, 0x00, 0x00}}}, - /* /93 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf8, 0x00, 0x00, 0x00, 0x00}}}, - /* /94 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfc, 0x00, 0x00, 0x00, 0x00}}}, - /* /95 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0x00, 0x00, 0x00, 0x00}}}, - /* /96 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x00, 0x00, 0x00}}}, - /* /97 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x80, 0x00, 0x00, 0x00}}}, - /* /98 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xc0, 0x00, 0x00, 0x00}}}, - /* /99 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xe0, 0x00, 0x00, 0x00}}}, - /* /100 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf0, 0x00, 0x00, 0x00}}}, - /* /101 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf8, 0x00, 0x00, 0x00}}}, - /* /102 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfc, 0x00, 0x00, 0x00}}}, - /* /103 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0x00, 0x00, 0x00}}}, - /* /104 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x00, 0x00, 0x00}}}, - /* /105 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x80, 0x00, 0x00}}}, - /* /106 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xc0, 0x00, 0x00}}}, - /* /107 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xe0, 0x00, 0x00}}}, - /* /108 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xf0, 0x00, 0x00}}}, - /* /109 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xf8, 0x00, 0x00}}}, - /* /110 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfc, 0x00, 0x00}}}, - /* /111 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x00, 0x00}}}, - /* /112 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x00, 0x00}}}, - /* /113 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x80, 0x00}}}, - /* /114 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xc0, 0x00}}}, - /* /115 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xe0, 0x00}}}, - /* /116 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf0, 0x00}}}, - /* /117 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf8, 0x00}}}, - /* /118 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfc, 0x00}}}, - /* /119 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0x00}}}, - /* /120 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x00}}}, - /* /121 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x80}}}, - /* /122 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xc0}}}, - /* /123 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xe0}}}, - /* /124 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf0}}}, - /* /125 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf8}}}, - /* /126 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfc}}}, - /* /127 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe}}}, - /* /128 */ - {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff}}}}; - /* Number of bits in prefix type. */ #ifndef PNBBY #define PNBBY 8 @@ -432,15 +44,6 @@ static const struct in6_addr maskbytes6[] = { #define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff) -void prefix_hexdump(const struct prefix *p) -{ - char buf[PREFIX_STRLEN]; - - zlog_debug("prefix: %s", - prefix2str(p, buf, sizeof(buf))); - zlog_hexdump(p, sizeof(struct prefix)); -} - int is_zero_mac(const struct ethaddr *mac) { int i = 0; @@ -461,11 +64,6 @@ unsigned int prefix_bit(const uint8_t *prefix, const uint16_t prefixlen) return (prefix[offset] >> shift) & 1; } -unsigned int prefix6_bit(const struct in6_addr *prefix, const uint16_t prefixlen) -{ - return prefix_bit((const uint8_t *)&prefix->s6_addr, prefixlen); -} - int str2family(const char *string) { if (!strcmp("ipv4", string)) @@ -1121,31 +719,46 @@ int str2prefix_ipv6(const char *str, struct prefix_ipv6 *p) * FIXME return uint8_t as ip_maskleni() does. */ int ip6_masklen(struct in6_addr netmask) { - int len = 0; - unsigned char val; - unsigned char *pnt; - - pnt = (unsigned char *)&netmask; - - while ((*pnt == 0xff) && len < IPV6_MAX_BITLEN) { - len += 8; - pnt++; - } - - if (len < IPV6_MAX_BITLEN) { - val = *pnt; - while (val) { - len++; - val <<= 1; - } - } - return len; + if (netmask.s6_addr32[0] != 0xffffffffU) + return __builtin_clz(~ntohl(netmask.s6_addr32[0])); + if (netmask.s6_addr32[1] != 0xffffffffU) + return __builtin_clz(~ntohl(netmask.s6_addr32[1])) + 32; + if (netmask.s6_addr32[2] != 0xffffffffU) + return __builtin_clz(~ntohl(netmask.s6_addr32[2])) + 64; + if (netmask.s6_addr32[3] != 0xffffffffU) + return __builtin_clz(~ntohl(netmask.s6_addr32[3])) + 96; + /* note __builtin_clz(0) is undefined */ + return 128; } void masklen2ip6(const int masklen, struct in6_addr *netmask) { assert(masklen >= 0 && masklen <= IPV6_MAX_BITLEN); - memcpy(netmask, maskbytes6 + masklen, sizeof(struct in6_addr)); + + if (masklen == 0) { + /* note << 32 is undefined */ + memset(netmask, 0, sizeof(*netmask)); + } else if (masklen <= 32) { + netmask->s6_addr32[0] = htonl(0xffffffffU << (32 - masklen)); + netmask->s6_addr32[1] = 0; + netmask->s6_addr32[2] = 0; + netmask->s6_addr32[3] = 0; + } else if (masklen <= 64) { + netmask->s6_addr32[0] = 0xffffffffU; + netmask->s6_addr32[1] = htonl(0xffffffffU << (64 - masklen)); + netmask->s6_addr32[2] = 0; + netmask->s6_addr32[3] = 0; + } else if (masklen <= 96) { + netmask->s6_addr32[0] = 0xffffffffU; + netmask->s6_addr32[1] = 0xffffffffU; + netmask->s6_addr32[2] = htonl(0xffffffffU << (96 - masklen)); + netmask->s6_addr32[3] = 0; + } else { + netmask->s6_addr32[0] = 0xffffffffU; + netmask->s6_addr32[1] = 0xffffffffU; + netmask->s6_addr32[2] = 0xffffffffU; + netmask->s6_addr32[3] = htonl(0xffffffffU << (128 - masklen)); + } } void apply_mask_ipv6(struct prefix_ipv6 *p) @@ -1183,33 +796,6 @@ void apply_mask(struct prefix *p) return; } -/* Utility function of convert between struct prefix <=> union sockunion. - * FIXME This function isn't used anywhere. */ -struct prefix *sockunion2prefix(const union sockunion *dest, - const union sockunion *mask) -{ - if (dest->sa.sa_family == AF_INET) { - struct prefix_ipv4 *p; - - p = prefix_ipv4_new(); - p->family = AF_INET; - p->prefix = dest->sin.sin_addr; - p->prefixlen = ip_masklen(mask->sin.sin_addr); - return (struct prefix *)p; - } - if (dest->sa.sa_family == AF_INET6) { - struct prefix_ipv6 *p; - - p = prefix_ipv6_new(); - p->family = AF_INET6; - p->prefixlen = ip6_masklen(mask->sin6.sin6_addr); - memcpy(&p->prefix, &dest->sin6.sin6_addr, - sizeof(struct in6_addr)); - return (struct prefix *)p; - } - return NULL; -} - /* Utility function of convert between struct prefix <=> union sockunion. */ struct prefix *sockunion2hostprefix(const union sockunion *su, struct prefix *prefix) @@ -1521,14 +1107,6 @@ void apply_classful_mask_ipv4(struct prefix_ipv4 *p) } } -in_addr_t ipv4_network_addr(in_addr_t hostaddr, int masklen) -{ - struct in_addr mask; - - masklen2ip(masklen, &mask); - return hostaddr & mask.s_addr; -} - in_addr_t ipv4_broadcast_addr(in_addr_t hostaddr, int masklen) { struct in_addr mask; diff --git a/lib/prefix.h b/lib/prefix.h index 7a93c766a3..667627ddfe 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -392,8 +392,6 @@ extern const char *afi2str(afi_t afi); /* Check bit of the prefix. */ extern unsigned int prefix_bit(const uint8_t *prefix, const uint16_t prefixlen); -extern unsigned int prefix6_bit(const struct in6_addr *prefix, - const uint16_t prefixlen); extern struct prefix *prefix_new(void); extern void prefix_free(struct prefix **p); @@ -430,8 +428,6 @@ extern void apply_mask(struct prefix *); #define prefix_copy(a, b) ({ memset(a, 0, sizeof(*a)); prefix_copy(a, b); }) #endif -extern struct prefix *sockunion2prefix(const union sockunion *dest, - const union sockunion *mask); extern struct prefix *sockunion2hostprefix(const union sockunion *, struct prefix *p); extern void prefix2sockunion(const struct prefix *, union sockunion *); @@ -453,8 +449,6 @@ extern void apply_classful_mask_ipv4(struct prefix_ipv4 *); extern uint8_t ip_masklen(struct in_addr); extern void masklen2ip(const int, struct in_addr *); -/* returns the network portion of the host address */ -extern in_addr_t ipv4_network_addr(in_addr_t hostaddr, int masklen); /* given the address of a host on a network and the network mask length, * calculate the broadcast address for that network; * special treatment for /31: returns the address of the other host @@ -484,7 +478,6 @@ extern unsigned prefix_hash_key(const void *pp); extern int str_to_esi(const char *str, esi_t *esi); extern char *esi_to_str(const esi_t *esi, char *buf, int size); -extern void prefix_hexdump(const struct prefix *p); extern void prefix_evpn_hexdump(const struct prefix_evpn *p); static inline int ipv6_martian(struct in6_addr *addr) diff --git a/lib/resolver.c b/lib/resolver.c index fb8aeed92f..1be47bd6e1 100644 --- a/lib/resolver.c +++ b/lib/resolver.c @@ -145,7 +145,8 @@ static void ares_address_cb(void *arg, int status, int timeouts, { struct resolver_query *query = (struct resolver_query *)arg; union sockunion addr[16]; - void (*callback)(struct resolver_query *, int, union sockunion *); + void (*callback)(struct resolver_query *, const char *, int, + union sockunion *); size_t i; callback = query->callback; @@ -153,9 +154,10 @@ static void ares_address_cb(void *arg, int status, int timeouts, if (status != ARES_SUCCESS) { if (resolver_debug) - zlog_debug("[%p] Resolving failed", query); + zlog_debug("[%p] Resolving failed (%s)", + query, ares_strerror(status)); - callback(query, -1, NULL); + callback(query, ares_strerror(status), -1, NULL); return; } @@ -177,14 +179,29 @@ static void ares_address_cb(void *arg, int status, int timeouts, if (resolver_debug) zlog_debug("[%p] Resolved with %d results", query, (int)i); - callback(query, i, &addr[0]); + callback(query, NULL, i, &addr[0]); +} + +static int resolver_cb_literal(struct thread *t) +{ + struct resolver_query *query = THREAD_ARG(t); + void (*callback)(struct resolver_query *, const char *, int, + union sockunion *); + + callback = query->callback; + query->callback = NULL; + + callback(query, ARES_SUCCESS, 1, &query->literal_addr); + return 0; } void resolver_resolve(struct resolver_query *query, int af, const char *hostname, - void (*callback)(struct resolver_query *, int, - union sockunion *)) + void (*callback)(struct resolver_query *, const char *, + int, union sockunion *)) { + int ret; + if (query->callback != NULL) { flog_err( EC_LIB_RESOLVER, @@ -193,10 +210,26 @@ void resolver_resolve(struct resolver_query *query, int af, return; } + query->callback = callback; + query->literal_cb = NULL; + + ret = str2sockunion(hostname, &query->literal_addr); + if (ret == 0) { + if (resolver_debug) + zlog_debug("[%p] Resolving '%s' (IP literal)", + query, hostname); + + /* for consistency with proper name lookup, don't call the + * callback immediately; defer to thread loop + */ + thread_add_timer_msec(state.master, resolver_cb_literal, + query, 0, &query->literal_cb); + return; + } + if (resolver_debug) zlog_debug("[%p] Resolving '%s'", query, hostname); - query->callback = callback; ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query); resolver_update_timeouts(&state); } diff --git a/lib/resolver.h b/lib/resolver.h index bc6326edaa..59bf0d0f55 100644 --- a/lib/resolver.h +++ b/lib/resolver.h @@ -14,12 +14,18 @@ #include "sockunion.h" struct resolver_query { - void (*callback)(struct resolver_query *, int n, union sockunion *); + void (*callback)(struct resolver_query *, const char *errstr, int n, + union sockunion *); + + /* used to immediate provide the result if IP literal is passed in */ + union sockunion literal_addr; + struct thread *literal_cb; }; void resolver_init(struct thread_master *tm); void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*cb)(struct resolver_query *, - int, union sockunion *)); + const char *, int, + union sockunion *)); #endif /* _FRR_RESOLVER_H */ diff --git a/lib/routemap.c b/lib/routemap.c index c0e01488b2..14fec0283c 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -221,7 +221,7 @@ struct route_map_match_set_hooks { const char *command, const char *arg); }; -struct route_map_match_set_hooks rmap_match_set_hook; +static struct route_map_match_set_hooks rmap_match_set_hook; /* match interface */ void route_map_match_interface_hook(int (*func)( @@ -623,7 +623,7 @@ struct route_map_list { /* Master list of route map. */ static struct route_map_list route_map_master = {NULL, NULL, NULL, NULL, NULL}; -struct hash *route_map_master_hash = NULL; +static struct hash *route_map_master_hash = NULL; static unsigned int route_map_hash_key_make(const void *p) { @@ -683,7 +683,7 @@ struct route_map_dep_data { }; /* Hashes maintaining dependency between various sublists used by route maps */ -struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX]; +static struct hash *route_map_dep_hash[ROUTE_MAP_DEP_MAX]; static unsigned int route_map_dep_hash_make_key(const void *p); static void route_map_clear_all_references(char *rmap_name); diff --git a/lib/skiplist.c b/lib/skiplist.c index dda442580a..6efa2c362d 100644 --- a/lib/skiplist.c +++ b/lib/skiplist.c @@ -608,7 +608,7 @@ void skiplist_test(struct vty *vty) struct skiplist *l; register int i, k; void *keys[sampleSize]; - void *v; + void *v = NULL; zlog_debug("%s: entry", __func__); diff --git a/lib/sockopt.c b/lib/sockopt.c index 7726d74ff7..3b12d16cbc 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -122,21 +122,6 @@ int setsockopt_ipv6_pktinfo(int sock, int val) } /* Set multicast hops val to the socket. */ -int setsockopt_ipv6_checksum(int sock, int val) -{ - int ret; - -#ifdef GNU_LINUX - ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); -#else - ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); -#endif /* GNU_LINUX */ - if (ret < 0) - flog_err(EC_LIB_SOCKET, "can't setsockopt IPV6_CHECKSUM"); - return ret; -} - -/* Set multicast hops val to the socket. */ int setsockopt_ipv6_multicast_hops(int sock, int val) { int ret; diff --git a/lib/sockopt.h b/lib/sockopt.h index f6b57b8e07..59d8a65964 100644 --- a/lib/sockopt.h +++ b/lib/sockopt.h @@ -33,7 +33,6 @@ extern int getsockopt_so_sendbuf(const int sock); extern int getsockopt_so_recvbuf(const int sock); extern int setsockopt_ipv6_pktinfo(int, int); -extern int setsockopt_ipv6_checksum(int, int); extern int setsockopt_ipv6_multicast_hops(int, int); extern int setsockopt_ipv6_unicast_hops(int, int); extern int setsockopt_ipv6_hoplimit(int, int); diff --git a/lib/stream.c b/lib/stream.c index 2e1a0193a2..dd4d5bd96d 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -812,7 +812,7 @@ int stream_put_ipv4(struct stream *s, uint32_t l) } /* Put long word to the stream. */ -int stream_put_in_addr(struct stream *s, struct in_addr *addr) +int stream_put_in_addr(struct stream *s, const struct in_addr *addr) { STREAM_VERIFY_SANE(s); @@ -828,7 +828,8 @@ int stream_put_in_addr(struct stream *s, struct in_addr *addr) } /* Put in_addr at location in the stream. */ -int stream_put_in_addr_at(struct stream *s, size_t putp, struct in_addr *addr) +int stream_put_in_addr_at(struct stream *s, size_t putp, + const struct in_addr *addr) { STREAM_VERIFY_SANE(s); @@ -842,7 +843,8 @@ int stream_put_in_addr_at(struct stream *s, size_t putp, struct in_addr *addr) } /* Put in6_addr at location in the stream. */ -int stream_put_in6_addr_at(struct stream *s, size_t putp, struct in6_addr *addr) +int stream_put_in6_addr_at(struct stream *s, size_t putp, + const struct in6_addr *addr) { STREAM_VERIFY_SANE(s); @@ -856,7 +858,7 @@ int stream_put_in6_addr_at(struct stream *s, size_t putp, struct in6_addr *addr) } /* Put prefix by nlri type format. */ -int stream_put_prefix_addpath(struct stream *s, struct prefix *p, +int stream_put_prefix_addpath(struct stream *s, const struct prefix *p, int addpath_encode, uint32_t addpath_tx_id) { size_t psize; @@ -890,7 +892,7 @@ int stream_put_prefix_addpath(struct stream *s, struct prefix *p, return psize; } -int stream_put_prefix(struct stream *s, struct prefix *p) +int stream_put_prefix(struct stream *s, const struct prefix *p) { return stream_put_prefix_addpath(s, p, 0, 0); } diff --git a/lib/stream.h b/lib/stream.h index 1144e43ef0..c0d25e0579 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -186,13 +186,16 @@ extern int stream_putl_at(struct stream *, size_t, uint32_t); extern int stream_putq(struct stream *, uint64_t); extern int stream_putq_at(struct stream *, size_t, uint64_t); extern int stream_put_ipv4(struct stream *, uint32_t); -extern int stream_put_in_addr(struct stream *, struct in_addr *); -extern int stream_put_in_addr_at(struct stream *, size_t, struct in_addr *); -extern int stream_put_in6_addr_at(struct stream *, size_t, struct in6_addr *); -extern int stream_put_prefix_addpath(struct stream *, struct prefix *, +extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr); +extern int stream_put_in_addr_at(struct stream *s, size_t putp, + const struct in_addr *addr); +extern int stream_put_in6_addr_at(struct stream *s, size_t putp, + const struct in6_addr *addr); +extern int stream_put_prefix_addpath(struct stream *s, + const struct prefix *p, int addpath_encode, uint32_t addpath_tx_id); -extern int stream_put_prefix(struct stream *, struct prefix *); +extern int stream_put_prefix(struct stream *s, const struct prefix *p); extern int stream_put_labeled_prefix(struct stream *, struct prefix *, mpls_label_t *, int addpath_encode, uint32_t addpath_tx_id); diff --git a/lib/subdir.am b/lib/subdir.am index 23b1950384..cb6fa7a3b8 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -18,6 +18,7 @@ lib_libfrr_la_SOURCES = \ lib/command_parse.y \ lib/csv.c \ lib/debug.c \ + lib/defaults.c \ lib/distribute.c \ lib/ferr.c \ lib/filter.c \ @@ -40,13 +41,13 @@ lib_libfrr_la_SOURCES = \ lib/json.c \ lib/keychain.c \ lib/lib_errors.c \ + lib/lib_vty.c \ lib/libfrr.c \ lib/linklist.c \ lib/log.c \ lib/log_vty.c \ lib/md5.c \ lib/memory.c \ - lib/memory_vty.c \ lib/mlag.c \ lib/module.c \ lib/mpls.c \ @@ -114,6 +115,7 @@ vtysh_scan += \ $(top_srcdir)/lib/if.c \ $(top_srcdir)/lib/if_rmap.c \ $(top_srcdir)/lib/keychain.c \ + $(top_srcdir)/lib/lib_vty.c \ $(top_srcdir)/lib/nexthop_group.c \ $(top_srcdir)/lib/plist.c \ $(top_srcdir)/lib/routemap.c \ @@ -156,6 +158,7 @@ pkginclude_HEADERS += \ lib/csv.h \ lib/db.h \ lib/debug.h \ + lib/defaults.h \ lib/distribute.h \ lib/ferr.h \ lib/filter.h \ @@ -179,6 +182,7 @@ pkginclude_HEADERS += \ lib/json.h \ lib/keychain.h \ lib/lib_errors.h \ + lib/lib_vty.h \ lib/libfrr.h \ lib/libospf.h \ lib/linklist.h \ @@ -186,7 +190,6 @@ pkginclude_HEADERS += \ lib/log_vty.h \ lib/md5.h \ lib/memory.h \ - lib/memory_vty.h \ lib/module.h \ lib/monotime.h \ lib/mpls.h \ diff --git a/lib/systemd.c b/lib/systemd.c index 44db48d006..81b0400ab9 100644 --- a/lib/systemd.c +++ b/lib/systemd.c @@ -32,7 +32,7 @@ * Wrapper this silliness if we * don't have systemd */ -void systemd_send_information(const char *info) +static void systemd_send_information(const char *info) { #if defined HAVE_SYSTEMD sd_notify(0, info); @@ -93,8 +93,8 @@ void systemd_send_stopping(void) /* * How many seconds should we wait between watchdog sends */ -int wsecs = 0; -struct thread_master *systemd_master = NULL; +static int wsecs = 0; +static struct thread_master *systemd_master = NULL; static int systemd_send_watchdog(struct thread *t) { diff --git a/lib/systemd.h b/lib/systemd.h index 1f730720ce..d9885c5d9c 100644 --- a/lib/systemd.h +++ b/lib/systemd.h @@ -32,7 +32,6 @@ extern "C" { * To turn on systemd compilation, use --enable-systemd on * configure run. */ -void systemd_send_information(const char *info); void systemd_send_stopping(void); /* @@ -67,7 +67,7 @@ static char vrf_default_name[VRF_NAMSIZ] = VRF_DEFAULT_NAME_INTERNAL; static int debug_vrf = 0; /* Holding VRF hooks */ -struct vrf_master { +static struct vrf_master { int (*vrf_new_hook)(struct vrf *); int (*vrf_delete_hook)(struct vrf *); int (*vrf_enable_hook)(struct vrf *); @@ -555,7 +555,6 @@ void vrf_terminate(void) } } -/* Create a socket for the VRF. */ int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, const char *interfacename) { @@ -752,7 +751,7 @@ DEFUN (no_vrf, } -struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1}; +static struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1}; DEFUN_NOSH (vrf_netns, vrf_netns_cmd, @@ -218,13 +218,36 @@ extern void vrf_terminate(void); * or call network operations */ -/* Create a socket serving for the given VRF */ +/* + * Create a new socket associated with a VRF. + * + * This is a wrapper that ensures correct behavior when using namespace VRFs. + * In the namespace case, the socket is created within the namespace. In the + * non-namespace case, this is equivalent to socket(). + * + * If name is provided, this is provided to vrf_bind() to bind the socket to + * the VRF. This is only relevant when using VRF-lite. + * + * Summary: + * - Namespace: pass vrf_id but not name + * - VRF-lite: pass vrf_id and name of VRF device to bind to + * - VRF-lite, no binding: pass vrf_id but not name, or just use socket() + */ extern int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, const char *name); extern int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id, const char *name); +/* + * Binds a socket to a VRF device. + * + * If name is null, the socket is not bound, irrespective of any other + * arguments. + * + * name should be the name of the VRF device. vrf_id should be the + * corresponding vrf_id (the ifindex of the device). + */ extern int vrf_bind(vrf_id_t vrf_id, int fd, const char *name); /* VRF ioctl operations */ @@ -89,7 +89,7 @@ static char *vty_ipv6_accesslist_name = NULL; static vector Vvty_serv_thread; /* Current directory. */ -char vty_cwd[MAXPATHLEN]; +static char vty_cwd[MAXPATHLEN]; /* Login password check. */ static int no_password_check = 0; diff --git a/lib/zclient.c b/lib/zclient.c index 7a62e408ea..6982d287a2 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -802,6 +802,12 @@ static int zapi_nexthop_cmp_no_labels(const struct zapi_nexthop *next1, if (next1->type > next2->type) return 1; + if (next1->weight < next2->weight) + return -1; + + if (next1->weight > next2->weight) + return 1; + switch (next1->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV6: @@ -859,6 +865,82 @@ static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp, &zapi_nexthop_cmp); } +/* + * Encode a single zapi nexthop + */ +int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, + uint32_t api_flags) +{ + int ret = 0; + int nh_flags = api_nh->flags; + + stream_putl(s, api_nh->vrf_id); + stream_putc(s, api_nh->type); + + /* If needed, set 'labelled nexthop' flag */ + if (api_nh->label_num > 0) { + SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_LABEL); + + /* Validate label count */ + if (api_nh->label_num > MPLS_MAX_LABELS) { + ret = -1; + goto done; + } + } + + if (api_nh->weight) + SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_WEIGHT); + + /* Note that we're only encoding a single octet */ + stream_putc(s, nh_flags); + + switch (api_nh->type) { + case NEXTHOP_TYPE_BLACKHOLE: + stream_putc(s, api_nh->bh_type); + break; + case NEXTHOP_TYPE_IPV4: + stream_put_in_addr(s, &api_nh->gate.ipv4); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + stream_put_in_addr(s, &api_nh->gate.ipv4); + stream_putl(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IFINDEX: + stream_putl(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IPV6: + stream_write(s, (uint8_t *)&api_nh->gate.ipv6, + 16); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + stream_write(s, (uint8_t *)&api_nh->gate.ipv6, + 16); + stream_putl(s, api_nh->ifindex); + break; + } + + /* We only encode labels if we have >0 - we use + * the per-nexthop flag above to signal that the count + * is present in the payload. + */ + if (api_nh->label_num > 0) { + stream_putc(s, api_nh->label_num); + stream_put(s, &api_nh->labels[0], + api_nh->label_num * sizeof(mpls_label_t)); + } + + if (api_nh->weight) + stream_putl(s, api_nh->weight); + + /* Router MAC for EVPN routes. */ + if (CHECK_FLAG(api_flags, ZEBRA_FLAG_EVPN_ROUTE)) + stream_put(s, &(api_nh->rmac), + sizeof(struct ethaddr)); + +done: + return ret; +} + int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) { struct zapi_nexthop *api_nh; @@ -921,59 +1003,22 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) for (i = 0; i < api->nexthop_num; i++) { api_nh = &api->nexthops[i]; - stream_putl(s, api_nh->vrf_id); - stream_putc(s, api_nh->type); - stream_putc(s, api_nh->onlink); - switch (api_nh->type) { - case NEXTHOP_TYPE_BLACKHOLE: - stream_putc(s, api_nh->bh_type); - break; - case NEXTHOP_TYPE_IPV4: - stream_put_in_addr(s, &api_nh->gate.ipv4); - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: - stream_put_in_addr(s, &api_nh->gate.ipv4); - stream_putl(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - stream_putl(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IPV6: - stream_write(s, (uint8_t *)&api_nh->gate.ipv6, - 16); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - stream_write(s, (uint8_t *)&api_nh->gate.ipv6, - 16); - stream_putl(s, api_nh->ifindex); - break; - } - /* MPLS labels for BGP-LU or Segment Routing */ - if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) { - if (api_nh->label_num > MPLS_MAX_LABELS) { - char buf[PREFIX2STR_BUFFER]; - prefix2str(&api->prefix, buf, - sizeof(buf)); - flog_err(EC_LIB_ZAPI_ENCODE, - "%s: prefix %s: can't encode " - "%u labels (maximum is %u)", - __func__, buf, - api_nh->label_num, - MPLS_MAX_LABELS); - return -1; - } - - stream_putc(s, api_nh->label_num); - stream_put(s, &api_nh->labels[0], - api_nh->label_num - * sizeof(mpls_label_t)); + if (api_nh->label_num > MPLS_MAX_LABELS) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&api->prefix, buf, sizeof(buf)); + + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: prefix %s: can't encode %u labels (maximum is %u)", + __func__, buf, + api_nh->label_num, + MPLS_MAX_LABELS); + return -1; } - /* Router MAC for EVPN routes. */ - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) - stream_put(s, &(api_nh->rmac), - sizeof(struct ethaddr)); + if (zapi_nexthop_encode(s, api_nh, api->flags) != 0) + return -1; } } @@ -995,6 +1040,76 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return 0; } +/* + * Decode a single zapi nexthop object + */ +static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, + uint32_t api_flags) +{ + int ret = -1; + + STREAM_GETL(s, api_nh->vrf_id); + STREAM_GETC(s, api_nh->type); + + /* Note that we're only using a single octet of flags */ + STREAM_GETC(s, api_nh->flags); + + switch (api_nh->type) { + case NEXTHOP_TYPE_BLACKHOLE: + STREAM_GETC(s, api_nh->bh_type); + break; + case NEXTHOP_TYPE_IPV4: + STREAM_GET(&api_nh->gate.ipv4.s_addr, s, + IPV4_MAX_BYTELEN); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + STREAM_GET(&api_nh->gate.ipv4.s_addr, s, + IPV4_MAX_BYTELEN); + STREAM_GETL(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IFINDEX: + STREAM_GETL(s, api_nh->ifindex); + break; + case NEXTHOP_TYPE_IPV6: + STREAM_GET(&api_nh->gate.ipv6, s, 16); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + STREAM_GET(&api_nh->gate.ipv6, s, 16); + STREAM_GETL(s, api_nh->ifindex); + break; + } + + /* MPLS labels for BGP-LU or Segment Routing */ + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)) { + STREAM_GETC(s, api_nh->label_num); + if (api_nh->label_num > MPLS_MAX_LABELS) { + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: invalid number of MPLS labels (%u)", + __func__, api_nh->label_num); + return -1; + } + + STREAM_GET(&api_nh->labels[0], s, + api_nh->label_num * sizeof(mpls_label_t)); + } + + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT)) + STREAM_GETL(s, api_nh->weight); + + /* Router MAC for EVPN routes. */ + if (CHECK_FLAG(api_flags, ZEBRA_FLAG_EVPN_ROUTE)) + STREAM_GET(&(api_nh->rmac), s, + sizeof(struct ethaddr)); + + /* Success */ + ret = 0; + +stream_failure: + + return ret; +} + int zapi_route_decode(struct stream *s, struct zapi_route *api) { struct zapi_nexthop *api_nh; @@ -1088,55 +1203,8 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) for (i = 0; i < api->nexthop_num; i++) { api_nh = &api->nexthops[i]; - STREAM_GETL(s, api_nh->vrf_id); - STREAM_GETC(s, api_nh->type); - STREAM_GETC(s, api_nh->onlink); - switch (api_nh->type) { - case NEXTHOP_TYPE_BLACKHOLE: - STREAM_GETC(s, api_nh->bh_type); - break; - case NEXTHOP_TYPE_IPV4: - STREAM_GET(&api_nh->gate.ipv4.s_addr, s, - IPV4_MAX_BYTELEN); - break; - case NEXTHOP_TYPE_IPV4_IFINDEX: - STREAM_GET(&api_nh->gate.ipv4.s_addr, s, - IPV4_MAX_BYTELEN); - STREAM_GETL(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - STREAM_GETL(s, api_nh->ifindex); - break; - case NEXTHOP_TYPE_IPV6: - STREAM_GET(&api_nh->gate.ipv6, s, 16); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - STREAM_GET(&api_nh->gate.ipv6, s, 16); - STREAM_GETL(s, api_nh->ifindex); - break; - } - - /* MPLS labels for BGP-LU or Segment Routing */ - if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) { - STREAM_GETC(s, api_nh->label_num); - - if (api_nh->label_num > MPLS_MAX_LABELS) { - flog_err( - EC_LIB_ZAPI_ENCODE, - "%s: invalid number of MPLS labels (%u)", - __func__, api_nh->label_num); - return -1; - } - - STREAM_GET(&api_nh->labels[0], s, - api_nh->label_num - * sizeof(mpls_label_t)); - } - - /* Router MAC for EVPN routes. */ - if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) - stream_get(&(api_nh->rmac), s, - sizeof(struct ethaddr)); + if (zapi_nexthop_decode(s, api_nh, api->flags) != 0) + return -1; } } @@ -1335,6 +1403,35 @@ struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh) return n; } +/* + * Convert nexthop to zapi nexthop + */ +int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, + const struct nexthop *nh) +{ + int i; + + memset(znh, 0, sizeof(*znh)); + + znh->type = nh->type; + znh->vrf_id = nh->vrf_id; + znh->ifindex = nh->ifindex; + znh->gate = nh->gate; + + if (nh->nh_label && (nh->nh_label->num_labels > 0)) { + for (i = 0; i < nh->nh_label->num_labels; i++) + znh->labels[i] = nh->nh_label->label[i]; + + znh->label_num = i; + SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_LABEL); + } + + return 0; +} + +/* + * Decode the nexthop-tracking update message + */ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) { uint32_t i; @@ -1361,38 +1458,8 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) STREAM_GETC(s, nhr->nexthop_num); for (i = 0; i < nhr->nexthop_num; i++) { - STREAM_GETL(s, nhr->nexthops[i].vrf_id); - STREAM_GETC(s, nhr->nexthops[i].type); - switch (nhr->nexthops[i].type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - STREAM_GET(&nhr->nexthops[i].gate.ipv4.s_addr, s, - IPV4_MAX_BYTELEN); - STREAM_GETL(s, nhr->nexthops[i].ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - STREAM_GETL(s, nhr->nexthops[i].ifindex); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - STREAM_GET(&nhr->nexthops[i].gate.ipv6, s, - IPV6_MAX_BYTELEN); - STREAM_GETL(s, nhr->nexthops[i].ifindex); - break; - case NEXTHOP_TYPE_BLACKHOLE: - break; - } - STREAM_GETC(s, nhr->nexthops[i].label_num); - if (nhr->nexthops[i].label_num > MPLS_MAX_LABELS) { - flog_err(EC_LIB_ZAPI_ENCODE, - "%s: invalid number of MPLS labels (%u)", - __func__, nhr->nexthops[i].label_num); - return false; - } - if (nhr->nexthops[i].label_num) - STREAM_GET(&nhr->nexthops[i].labels[0], s, - nhr->nexthops[i].label_num - * sizeof(mpls_label_t)); + if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0) != 0) + return -1; } return true; diff --git a/lib/zclient.h b/lib/zclient.h index 7adb294a31..70304127a2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -289,7 +289,6 @@ struct zclient { #define ZAPI_MESSAGE_TAG 0x08 #define ZAPI_MESSAGE_MTU 0x10 #define ZAPI_MESSAGE_SRCPFX 0x20 -#define ZAPI_MESSAGE_LABEL 0x40 /* * This should only be used by a DAEMON that needs to communicate * the table being used is not in the VRF. You must pass the @@ -313,7 +312,7 @@ struct zapi_nexthop { enum nexthop_types_t type; vrf_id_t vrf_id; ifindex_t ifindex; - bool onlink; + uint8_t flags; union { union g_addr gate; enum blackhole_type bh_type; @@ -324,9 +323,18 @@ struct zapi_nexthop { mpls_label_t labels[MPLS_MAX_LABELS]; struct ethaddr rmac; + + uint32_t weight; }; /* + * ZAPI nexthop flags values + */ +#define ZAPI_NEXTHOP_FLAG_ONLINK 0x01 +#define ZAPI_NEXTHOP_FLAG_LABEL 0x02 +#define ZAPI_NEXTHOP_FLAG_WEIGHT 0x04 + +/* * Some of these data structures do not map easily to * a actual data structure size giving different compilers * and systems. For those data structures we need @@ -485,6 +493,29 @@ enum zapi_iptable_notify_owner { ZAPI_IPTABLE_FAIL_REMOVE, }; +static inline const char * +zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note) +{ + const char *ret = "UNKNOWN"; + + switch (note) { + case ZAPI_RULE_FAIL_INSTALL: + ret = "ZAPI_RULE_FAIL_INSTALL"; + break; + case ZAPI_RULE_INSTALLED: + ret = "ZAPI_RULE_INSTALLED"; + break; + case ZAPI_RULE_FAIL_REMOVE: + ret = "ZAPI_RULE_FAIL_REMOVE"; + break; + case ZAPI_RULE_REMOVED: + ret = "ZAPI_RULE_REMOVED"; + break; + } + + return ret; +} + /* Zebra MAC types */ #define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/ #define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/ @@ -666,6 +697,8 @@ extern int zclient_route_send(uint8_t, struct zclient *, struct zapi_route *); extern int zclient_send_rnh(struct zclient *zclient, int command, struct prefix *p, bool exact_match, vrf_id_t vrf_id); +int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, + uint32_t api_flags); extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *); extern int zapi_route_decode(struct stream *, struct zapi_route *); bool zapi_route_notify_decode(struct stream *s, struct prefix *p, @@ -690,6 +723,8 @@ bool zapi_iptable_notify_decode(struct stream *s, enum zapi_iptable_notify_owner *note); extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh); +int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, + const struct nexthop *nh); extern bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr); diff --git a/mlag/subdir.am b/mlag/subdir.am index 9fab662860..49d1761505 100644 --- a/mlag/subdir.am +++ b/mlag/subdir.am @@ -1,4 +1,4 @@ -if HAVE_PROTOBUF +if HAVE_PROTOBUF3 lib_LTLIBRARIES += mlag/libmlag_pb.la endif diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index a781122b16..b58fe776ab 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -21,7 +21,6 @@ #include "version.h" #include "log.h" #include "memory.h" -#include "memory_vty.h" #include "command.h" #include "libfrr.h" diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c index 360972c327..bec6c014a0 100644 --- a/nhrpd/nhrp_nhs.c +++ b/nhrpd/nhrp_nhs.c @@ -238,8 +238,8 @@ nhrp_reg_by_nbma(struct nhrp_nhs *nhs, const union sockunion *nbma_addr) return NULL; } -static void nhrp_nhs_resolve_cb(struct resolver_query *q, int n, - union sockunion *addrs) +static void nhrp_nhs_resolve_cb(struct resolver_query *q, const char *errstr, + int n, union sockunion *addrs) { struct nhrp_nhs *nhs = container_of(q, struct nhrp_nhs, dns_resolve); struct nhrp_interface *nifp = nhs->ifp->info; diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 0fa5585b80..0aaefeb3c2 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -28,7 +28,6 @@ #include "command.h" #include "vty.h" #include "memory.h" -#include "memory_vty.h" #include "if.h" #include "filter.h" #include "prefix.h" diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 95dafff84e..95537eb86e 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -51,6 +51,11 @@ DEFINE_QOBJ_TYPE(ospf6) +FRR_CFG_DEFAULT_BOOL(OSPF6_LOG_ADJACENCY_CHANGES, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) + /* global ospf6d variable */ struct ospf6 *ospf6; static struct ospf6_master ospf6_master; @@ -179,11 +184,6 @@ static struct ospf6 *ospf6_create(vrf_id_t vrf_id) o->distance_table = route_table_init(); -/* Enable "log-adjacency-changes" */ -#if DFLT_OSPF6_LOG_ADJACENCY_CHANGES - SET_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); -#endif - QOBJ_REG(o, ospf6); return o; @@ -327,6 +327,9 @@ DEFUN_NOSH (router_ospf6, { if (ospf6 == NULL) { ospf6 = ospf6_create(VRF_DEFAULT); + if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES) + SET_FLAG(ospf6->config_flags, + OSPF6_LOG_ADJACENCY_CHANGES); if (ospf6->router_id == 0) ospf6_router_id_update(); } @@ -1079,9 +1082,9 @@ static int config_write_ospf6(struct vty *vty) if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) { if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL)) vty_out(vty, " log-adjacency-changes detail\n"); - else if (!DFLT_OSPF6_LOG_ADJACENCY_CHANGES) + else if (!SAVE_OSPF6_LOG_ADJACENCY_CHANGES) vty_out(vty, " log-adjacency-changes\n"); - } else if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES) { + } else if (SAVE_OSPF6_LOG_ADJACENCY_CHANGES) { vty_out(vty, " no log-adjacency-changes\n"); } diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index ba41fca65b..18c0697025 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -29,6 +29,12 @@ struct ospf6_master { uint32_t zebra_router_id; }; +/* ospf6->config_flags */ +enum { + OSPF6_LOG_ADJACENCY_CHANGES = (1 << 0), + OSPF6_LOG_ADJACENCY_DETAIL = (1 << 1), +}; + /* OSPFv3 top level data structure */ struct ospf6 { /* The relevant vrf_id */ @@ -68,10 +74,8 @@ struct ospf6 { uint8_t flag; - /* Configured flags */ + /* Configuration bitmask, refer to enum above */ uint8_t config_flags; -#define OSPF6_LOG_ADJACENCY_CHANGES (1 << 0) -#define OSPF6_LOG_ADJACENCY_DETAIL (1 << 1) /* LSA timer parameters */ unsigned int lsa_minarrival; /* LSA minimum arrival in milliseconds. */ diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index bd703bc89d..49730063b9 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -2325,7 +2325,7 @@ void ospf_apiserver_clients_notify_ism_change(struct ospf_interface *oi) void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr) { struct msg *msg; - struct in_addr ifaddr = {.s_addr = 0L}; + struct in_addr ifaddr; struct in_addr nbraddr; assert(nbr); diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 7ddffbcdbd..8efb32af37 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -636,9 +636,13 @@ void ospf_if_update_params(struct interface *ifp, struct in_addr addr) int ospf_if_new_hook(struct interface *ifp) { int rc = 0; + struct ospf_if_info *oii; ifp->info = XCALLOC(MTYPE_OSPF_IF_INFO, sizeof(struct ospf_if_info)); + oii = ifp->info; + oii->curr_mtu = ifp->mtu; + IF_OIFS(ifp) = route_table_init(); IF_OIFS_PARAMS(ifp) = route_table_init(); @@ -902,10 +906,12 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf, static void ospf_vl_if_delete(struct ospf_vl_data *vl_data) { + struct interface *ifp = vl_data->vl_oi->ifp; + vl_data->vl_oi->address->u.prefix4.s_addr = 0; vl_data->vl_oi->address->prefixlen = 0; ospf_if_free(vl_data->vl_oi); - if_delete(&vl_data->vl_oi->ifp); + if_delete(&ifp); vlink_count--; } @@ -1261,31 +1267,21 @@ static int ospf_ifp_up(struct interface *ifp) { struct ospf_interface *oi; struct route_node *rn; + struct ospf_if_info *oii = ifp->info; - /* Interface is already up. */ - if (if_is_operative(ifp)) { - /* Temporarily keep ifp values. */ - struct interface if_tmp; - memcpy(&if_tmp, ifp, sizeof(struct interface)); + ospf_if_recalculate_output_cost(ifp); + if (oii && oii->curr_mtu != ifp->mtu) { if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) zlog_debug( - "Zebra: Interface[%s] state update speed %u -> %u, bw %d -> %d", - ifp->name, if_tmp.speed, ifp->speed, - if_tmp.bandwidth, ifp->bandwidth); + "Zebra: Interface[%s] MTU change %u -> %u.", + ifp->name, oii->curr_mtu, ifp->mtu); - ospf_if_recalculate_output_cost(ifp); + oii->curr_mtu = ifp->mtu; + /* Must reset the interface (simulate down/up) when MTU + * changes. */ + ospf_if_reset(ifp); - if (if_tmp.mtu != ifp->mtu) { - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug( - "Zebra: Interface[%s] MTU change %u -> %u.", - ifp->name, if_tmp.mtu, ifp->mtu); - - /* Must reset the interface (simulate down/up) when MTU - * changes. */ - ospf_if_reset(ifp); - } return 0; } diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index cde52dbb9e..4b3dbcc5c2 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -117,6 +117,8 @@ struct ospf_if_info { struct route_table *oifs; unsigned int membership_counts[MEMBER_MAX]; /* multicast group refcnts */ + + uint32_t curr_mtu; }; struct ospf_interface; diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index a542b4da83..d02ffe0448 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -35,7 +35,6 @@ #include "stream.h" #include "log.h" #include "memory.h" -#include "memory_vty.h" #include "privs.h" #include "sigevent.h" #include "zclient.h" diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 80ffc3f361..95fb694925 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -2318,8 +2318,7 @@ static struct stream *ospf_recv_packet(struct ospf *ospf, int fd, safe_strerror(errno)); return NULL; } - if ((unsigned int)ret < sizeof(iph)) /* ret must be > 0 now */ - { + if ((unsigned int)ret < sizeof(struct ip)) { flog_warn( EC_OSPF_PACKET, "ospf_recv_packet: discarding runt packet of length %d " @@ -3001,11 +3000,23 @@ static enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf) return OSPF_READ_CONTINUE; } - /* - * Advance from IP header to OSPF header (iph->ip_hl has - * been verified by ospf_recv_packet() to be correct). - */ - stream_forward_getp(ibuf, iph->ip_hl * 4); + /* Check that we have enough for an IP header */ + if ((unsigned int)(iph->ip_hl << 2) >= STREAM_READABLE(ibuf)) { + if ((unsigned int)(iph->ip_hl << 2) == STREAM_READABLE(ibuf)) { + flog_warn( + EC_OSPF_PACKET, + "Rx'd IP packet with OSPF protocol number but no payload"); + } else { + flog_warn( + EC_OSPF_PACKET, + "IP header length field claims header is %u bytes, but we only have %zu", + (unsigned int)(iph->ip_hl << 2), + STREAM_READABLE(ibuf)); + } + + return OSPF_READ_ERROR; + } + stream_forward_getp(ibuf, iph->ip_hl << 2); ospfh = (struct ospf_header *)stream_pnt(ibuf); if (MSG_OK diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 4c97615ed1..152a7e83b7 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -53,6 +53,11 @@ #include "ospfd/ospf_dump.h" #include "ospfd/ospf_bfd.h" +FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES, + { .val_long = true, .match_profile = "datacenter", }, + { .val_long = false }, +) + static const char *const ospf_network_type_str[] = { "Null", "POINTOPOINT", "BROADCAST", "NBMA", "POINTOMULTIPOINT", "VIRTUALLINK", "LOOPBACK"}; @@ -138,6 +143,7 @@ static struct ospf *ospf_cmd_lookup_ospf(struct vty *vty, struct ospf *ospf = NULL; int idx_vrf = 0, idx_inst = 0; const char *vrf_name = NULL; + bool created = false; *instance = 0; if (argv_find(argv, argc, "(1-65535)", &idx_inst)) @@ -149,18 +155,23 @@ static struct ospf *ospf_cmd_lookup_ospf(struct vty *vty, vrf_name = NULL; if (enable) { /* Allocate VRF aware instance */ - ospf = ospf_get(*instance, vrf_name); + ospf = ospf_get(*instance, vrf_name, &created); } else { ospf = ospf_lookup_by_inst_name(*instance, vrf_name); } } else { if (enable) { - ospf = ospf_get(*instance, NULL); + ospf = ospf_get(*instance, NULL, &created); } else { ospf = ospf_lookup_instance(*instance); } } + if (created) { + if (DFLT_OSPF_LOG_ADJACENCY_CHANGES) + SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES); + } + return ospf; } @@ -4154,7 +4165,7 @@ DEFUN (show_ip_ospf_interface_traffic, static void show_ip_ospf_neighbour_header(struct vty *vty) { - vty_out(vty, "\n%-15s %3s %-15s %9s %-15s %-20s %5s %5s %5s\n", + vty_out(vty, "\n%-15s %3s %-15s %9s %-15s %-32s %5s %5s %5s\n", "Neighbor ID", "Pri", "State", "Dead Time", "Address", "Interface", "RXmtL", "RqstL", "DBsmL"); } @@ -4260,7 +4271,7 @@ static void show_ip_ospf_neighbor_sub(struct vty *vty, timebuf, sizeof(timebuf))); vty_out(vty, "%-15s ", inet_ntoa(nbr->src)); - vty_out(vty, "%-20s %5ld %5ld %5d\n", + vty_out(vty, "%-32s %5ld %5ld %5d\n", IF_NAME(oi), ospf_ls_retransmit_count(nbr), ospf_ls_request_count(nbr), @@ -4524,7 +4535,7 @@ static int show_ip_ospf_neighbor_all_common(struct vty *vty, struct ospf *ospf, "-", nbr_nbma->priority, "Down", "-"); vty_out(vty, - "%-15s %-20s %5d %5d %5d\n", + "%-32s %-20s %5d %5d %5d\n", inet_ntoa(nbr_nbma->addr), IF_NAME(oi), 0, 0, 0); } @@ -10358,9 +10369,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES)) { if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) vty_out(vty, " log-adjacency-changes detail\n"); - else if (!DFLT_OSPF_LOG_ADJACENCY_CHANGES) + else if (!SAVE_OSPF_LOG_ADJACENCY_CHANGES) vty_out(vty, " log-adjacency-changes\n"); - } else if (DFLT_OSPF_LOG_ADJACENCY_CHANGES) { + } else if (SAVE_OSPF_LOG_ADJACENCY_CHANGES) { vty_out(vty, " no log-adjacency-changes\n"); } diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 5058886f36..6a4e63372a 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -293,11 +293,6 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) new->oi_write_q = list_new(); new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT; -/* Enable "log-adjacency-changes" */ -#if DFLT_OSPF_LOG_ADJACENCY_CHANGES - SET_FLAG(new->config, OSPF_LOG_ADJACENCY_CHANGES); -#endif - QOBJ_REG(new, ospf); new->fd = -1; @@ -368,7 +363,7 @@ struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name) return NULL; } -struct ospf *ospf_get(unsigned short instance, const char *name) +struct ospf *ospf_get(unsigned short instance, const char *name, bool *created) { struct ospf *ospf; @@ -379,6 +374,7 @@ struct ospf *ospf_get(unsigned short instance, const char *name) else ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + *created = (ospf == NULL); if (ospf == NULL) { ospf = ospf_new(instance, name); ospf_add(ospf); @@ -392,27 +388,18 @@ struct ospf *ospf_get(unsigned short instance, const char *name) return ospf; } -struct ospf *ospf_get_instance(unsigned short instance) +struct ospf *ospf_get_instance(unsigned short instance, bool *created) { struct ospf *ospf; ospf = ospf_lookup_instance(instance); + *created = (ospf == NULL); if (ospf == NULL) { ospf = ospf_new(instance, NULL /* VRF_DEFAULT*/); ospf_add(ospf); - if (ospf->router_id_static.s_addr == 0) { - if (vrf_lookup_by_id(ospf->vrf_id)) - ospf_router_id_update(ospf); - else { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "%s: ospf VRF (id %d) is not active yet, skip router id update", - __PRETTY_FUNCTION__, - ospf->vrf_id); - } + if (ospf->router_id_static.s_addr == 0) ospf_router_id_update(ospf); - } ospf_opaque_type11_lsa_init(ospf); } @@ -584,7 +571,6 @@ static void ospf_finish_final(struct ospf *ospf) struct ospf_vl_data *vl_data; struct listnode *node, *nnode; int i; - unsigned short instance = 0; QOBJ_UNREG(ospf); @@ -755,9 +741,6 @@ static void ospf_finish_final(struct ospf *ospf) ospf_distance_reset(ospf); route_table_finish(ospf->distance_table); - if (!CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN)) - instance = ospf->instance; - list_delete(&ospf->areas); list_delete(&ospf->oi_write_q); @@ -778,9 +761,6 @@ static void ospf_finish_final(struct ospf *ospf) } XFREE(MTYPE_OSPF_TOP, ospf); - - if (!CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN)) - ospf_get_instance(instance); } diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 937d363b4c..5e91e6f8e6 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -117,6 +117,14 @@ struct ospf_redist { #define ROUTEMAP(R) (R->route_map.map) }; +/* ospf->config */ +enum { + OSPF_RFC1583_COMPATIBLE = (1 << 0), + OSPF_OPAQUE_CAPABLE = (1 << 2), + OSPF_LOG_ADJACENCY_CHANGES = (1 << 3), + OSPF_LOG_ADJACENCY_DETAIL = (1 << 4), +}; + /* OSPF instance structure. */ struct ospf { /* OSPF's running state based on the '[no] router ospf [<instance>]' @@ -151,12 +159,8 @@ struct ospf { /* NSSA ABR */ uint8_t anyNSSA; /* Bump for every NSSA attached. */ - /* Configured variables. */ + /* Configuration bitmask, refer to enum above */ uint8_t config; -#define OSPF_RFC1583_COMPATIBLE (1 << 0) -#define OSPF_OPAQUE_CAPABLE (1 << 2) -#define OSPF_LOG_ADJACENCY_CHANGES (1 << 3) -#define OSPF_LOG_ADJACENCY_DETAIL (1 << 4) /* Opaque-LSA administrative flags. */ uint8_t opaque; @@ -500,8 +504,9 @@ extern struct zebra_privs_t ospfd_privs; /* Prototypes. */ extern const char *ospf_redist_string(unsigned int route_type); extern struct ospf *ospf_lookup_instance(unsigned short); -extern struct ospf *ospf_get(unsigned short instance, const char *name); -extern struct ospf *ospf_get_instance(unsigned short); +extern struct ospf *ospf_get(unsigned short instance, const char *name, + bool *created); +extern struct ospf *ospf_get_instance(unsigned short, bool *created); extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name); extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id); diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 7e34066b47..37b6be6949 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -100,6 +100,37 @@ static void pbr_map_interface_list_delete(struct pbr_map_interface *pmi) } } +static bool pbrms_is_installed(const struct pbr_map_sequence *pbrms, + const struct pbr_map_interface *pmi) +{ + uint64_t is_installed = (uint64_t)1 << pmi->install_bit; + + is_installed &= pbrms->installed; + + if (is_installed) + return true; + + return false; +} + +/* If any sequence is installed on the interface, assume installed */ +static bool +pbr_map_interface_is_installed(const struct pbr_map *pbrm, + const struct pbr_map_interface *check_pmi) +{ + + struct pbr_map_sequence *pbrms; + struct pbr_map_interface *pmi; + struct listnode *node, *inode; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + if (pmi == check_pmi && pbrms_is_installed(pbrms, pmi)) + return true; + + return false; +} + static bool pbr_map_interface_is_valid(const struct pbr_map_interface *pmi) { /* Don't install rules without a real ifindex on the incoming interface. @@ -171,7 +202,7 @@ void pbr_map_reason_string(unsigned int reason, char *buf, int size) void pbr_map_final_interface_deletion(struct pbr_map *pbrm, struct pbr_map_interface *pmi) { - if (pmi->delete == true) { + if (pmi->delete == true && !pbr_map_interface_is_installed(pbrm, pmi)) { listnode_delete(pbrm->incoming, pmi); pmi->pbrm = NULL; diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index bc4aa947a9..c1922d8728 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -127,11 +127,16 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, pbrms->family = prefix->family; if (!no) { - if (prefix_same(pbrms->src, prefix)) - return CMD_SUCCESS; + if (pbrms->src) { + if (prefix_same(pbrms->src, prefix)) + return CMD_SUCCESS; + + vty_out(vty, + "A `match src-ip XX` command already exists, please remove that first\n"); + return CMD_WARNING_CONFIG_FAILED; + } - if (!pbrms->src) - pbrms->src = prefix_new(); + pbrms->src = prefix_new(); prefix_copy(pbrms->src, prefix); } else prefix_free(&pbrms->src); @@ -145,7 +150,7 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, "[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix", NO_STR "Match the rest of the command\n" - "Choose the src ip or ipv6 prefix to use\n" + "Choose the dst ip or ipv6 prefix to use\n" "v4 Prefix\n" "v6 Prefix\n") { @@ -154,11 +159,16 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, pbrms->family = prefix->family; if (!no) { - if (prefix_same(pbrms->dst, prefix)) - return CMD_SUCCESS; + if (pbrms->dst) { + if (prefix_same(pbrms->dst, prefix)) + return CMD_SUCCESS; - if (!pbrms->dst) - pbrms->dst = prefix_new(); + vty_out(vty, + "A `match dst-ip XX` command already exists, please remove that first\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + pbrms->dst = prefix_new(); prefix_copy(pbrms->dst, prefix); } else prefix_free(&pbrms->dst); @@ -183,12 +193,18 @@ DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd, #endif if (!no) { - if (pbrms->mark == (uint32_t) mark) - return CMD_SUCCESS; - pbrms->mark = (uint32_t) mark; - } else { + if (pbrms->mark) { + if (pbrms->mark == (uint32_t)mark) + return CMD_SUCCESS; + + vty_out(vty, + "A `match mark XX` command already exists, please remove that first\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + pbrms->mark = (uint32_t)mark; + } else pbrms->mark = 0; - } pbr_map_check(pbrms); @@ -232,7 +248,7 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, pbr_map_delete_nexthops(pbrms); else { vty_out(vty, - "Nexthop Group specified: %s does not exist to remove", + "Nexthop Group specified: %s does not exist to remove\n", name); return CMD_WARNING_CONFIG_FAILED; } @@ -240,7 +256,7 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, if (pbrms->nhgrp_name) { if (strcmp(name, pbrms->nhgrp_name) != 0) { vty_out(vty, - "Please delete current nexthop group before modifying current one"); + "Please delete current nexthop group before modifying current one\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -277,7 +293,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (pbrms->nhgrp_name) { vty_out(vty, - "Please unconfigure the nexthop group before adding an individual nexthop"); + "Please unconfigure the nexthop group before adding an individual nexthop\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -359,7 +375,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (pbrms->nhg->nexthop) { vty_out(vty, - "If you would like more than one nexthop please use nexthop-groups"); + "If you would like more than one nexthop please use nexthop-groups\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -508,6 +524,88 @@ DEFPY (show_pbr, return CMD_SUCCESS; } +static void vty_show_pbrms(struct vty *vty, + const struct pbr_map_sequence *pbrms, bool detail) +{ + char buf[PREFIX_STRLEN]; + char rbuf[64]; + + if (pbrms->reason) + pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf)); + + vty_out(vty, " Seq: %u rule: %u\n", pbrms->seqno, pbrms->ruleno); + + if (detail) + vty_out(vty, " Installed: %" PRIu64 "(%u) Reason: %s\n", + pbrms->installed, pbrms->unique, + pbrms->reason ? rbuf : "Valid"); + else + vty_out(vty, " Installed: %s Reason: %s\n", + pbrms->installed ? "yes" : "no", + pbrms->reason ? rbuf : "Valid"); + + if (pbrms->src) + vty_out(vty, " SRC Match: %s\n", + prefix2str(pbrms->src, buf, sizeof(buf))); + if (pbrms->dst) + vty_out(vty, " DST Match: %s\n", + prefix2str(pbrms->dst, buf, sizeof(buf))); + if (pbrms->mark) + vty_out(vty, " MARK Match: %u\n", pbrms->mark); + + if (pbrms->nhgrp_name) { + vty_out(vty, " Nexthop-Group: %s\n", pbrms->nhgrp_name); + + if (detail) + vty_out(vty, + " Installed: %u(%d) Tableid: %d\n", + pbrms->nhs_installed, + pbr_nht_get_installed(pbrms->nhgrp_name), + pbr_nht_get_table(pbrms->nhgrp_name)); + else + vty_out(vty, " Installed: %s Tableid: %d\n", + pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes" + : "no", + pbr_nht_get_table(pbrms->nhgrp_name)); + + } else if (pbrms->nhg) { + vty_out(vty, " "); + nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop); + if (detail) + vty_out(vty, + " Installed: %u(%d) Tableid: %d\n", + pbrms->nhs_installed, + pbr_nht_get_installed(pbrms->internal_nhg_name), + pbr_nht_get_table(pbrms->internal_nhg_name)); + else + vty_out(vty, " Installed: %s Tableid: %d\n", + pbr_nht_get_installed(pbrms->internal_nhg_name) + ? "yes" + : "no", + pbr_nht_get_table(pbrms->internal_nhg_name)); + + } else if (pbrms->vrf_unchanged) { + vty_out(vty, " VRF Unchanged (use interface vrf)\n"); + } else if (pbrms->vrf_lookup) { + vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name); + } else { + vty_out(vty, " Nexthop-Group: Unknown Installed: no\n"); + } +} + +static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm, + bool detail) +{ + struct pbr_map_sequence *pbrms; + struct listnode *node; + + vty_out(vty, " pbr-map %s valid: %s\n", pbrm->name, + pbrm->valid ? "yes" : "no"); + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + vty_show_pbrms(vty, pbrms, detail); +} + DEFPY (show_pbr_map, show_pbr_map_cmd, "show pbr map [NAME$name] [detail$detail]", @@ -517,69 +615,13 @@ DEFPY (show_pbr_map, "PBR Map Name\n" "Detailed information\n") { - struct pbr_map_sequence *pbrms; struct pbr_map *pbrm; - struct listnode *node; - char buf[PREFIX_STRLEN]; - char rbuf[64]; RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { if (name && strcmp(name, pbrm->name) != 0) continue; - vty_out(vty, " pbr-map %s valid: %d\n", pbrm->name, - pbrm->valid); - - for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { - if (pbrms->reason) - pbr_map_reason_string(pbrms->reason, rbuf, - sizeof(rbuf)); - vty_out(vty, - " Seq: %u rule: %u Installed: %" PRIu64 "(%u) Reason: %s\n", - pbrms->seqno, pbrms->ruleno, pbrms->installed, - pbrms->unique, pbrms->reason ? rbuf : "Valid"); - - if (pbrms->src) - vty_out(vty, "\tSRC Match: %s\n", - prefix2str(pbrms->src, buf, - sizeof(buf))); - if (pbrms->dst) - vty_out(vty, "\tDST Match: %s\n", - prefix2str(pbrms->dst, buf, - sizeof(buf))); - if (pbrms->mark) - vty_out(vty, "\tMARK Match: %u\n", pbrms->mark); - - if (pbrms->nhgrp_name) { - vty_out(vty, - "\tNexthop-Group: %s(%u) Installed: %u(%d)\n", - pbrms->nhgrp_name, - pbr_nht_get_table(pbrms->nhgrp_name), - pbrms->nhs_installed, - pbr_nht_get_installed( - pbrms->nhgrp_name)); - } else if (pbrms->nhg) { - vty_out(vty, " "); - nexthop_group_write_nexthop( - vty, pbrms->nhg->nexthop); - vty_out(vty, - "\tInstalled: %u(%d) Tableid: %d\n", - pbrms->nhs_installed, - pbr_nht_get_installed( - pbrms->internal_nhg_name), - pbr_nht_get_table( - pbrms->internal_nhg_name)); - } else if (pbrms->vrf_unchanged) { - vty_out(vty, - "\tVRF Unchanged (use interface vrf)\n"); - } else if (pbrms->vrf_lookup) { - vty_out(vty, "\tVRF Lookup: %s\n", - pbrms->vrf_name); - } else { - vty_out(vty, - "\tNexthop-Group: Unknown Installed: 0(0)\n"); - } - } + vty_show_pbr_map(vty, pbrm, detail); } return CMD_SUCCESS; } diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index b0a689a7e4..ec0327d74f 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -234,23 +234,21 @@ static int rule_notify_owner(ZAPI_CALLBACK_ARGS) switch (note) { case ZAPI_RULE_FAIL_INSTALL: pbrms->installed &= ~installed; - DEBUGD(&pbr_dbg_zebra, - "%s: Received RULE_FAIL_INSTALL: %" PRIu64, - __PRETTY_FUNCTION__, pbrms->installed); break; case ZAPI_RULE_INSTALLED: pbrms->installed |= installed; - DEBUGD(&pbr_dbg_zebra, "%s: Received RULE_INSTALLED: %" PRIu64, - __PRETTY_FUNCTION__, pbrms->installed); break; case ZAPI_RULE_FAIL_REMOVE: + /* Don't change state on rule removal failure */ + break; case ZAPI_RULE_REMOVED: pbrms->installed &= ~installed; - DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED: %" PRIu64, - __PRETTY_FUNCTION__, pbrms->installed); break; } + DEBUGD(&pbr_dbg_zebra, "%s: Received %s: %" PRIu64, __func__, + zapi_rule_notify_owner2str(note), pbrms->installed); + pbr_map_final_interface_deletion(pbrms->parent, pmi); return 0; @@ -281,6 +279,7 @@ static void route_add_helper(struct zapi_route *api, struct nexthop_group nhg, api_nh = &api->nexthops[i]; api_nh->vrf_id = nhop->vrf_id; api_nh->type = nhop->type; + api_nh->weight = nhop->weight; switch (nhop->type) { case NEXTHOP_TYPE_IPV4: api_nh->gate.ipv4 = nhop->gate.ipv4; diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index ca86017f10..01bebebd29 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -914,7 +914,6 @@ static void pim_show_interfaces_single(struct pim_instance *pim, struct in_addr ifaddr; struct interface *ifp; struct listnode *neighnode; - struct listnode *upnode; struct pim_interface *pim_ifp; struct pim_neighbor *neigh; struct pim_upstream *up; @@ -1052,8 +1051,7 @@ static void pim_show_interfaces_single(struct pim_instance *pim, pim_ifp->pim_dr_election_changes); // FHR - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, - up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (ifp != up->rpf.source_nexthop.interface) continue; @@ -1215,8 +1213,7 @@ static void pim_show_interfaces_single(struct pim_instance *pim, // FHR print_header = 1; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, - up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (!up->rpf.source_nexthop.interface) continue; @@ -1386,7 +1383,6 @@ static void pim_show_interfaces(struct pim_instance *pim, struct vty *vty, bool uj) { struct interface *ifp; - struct listnode *upnode; struct pim_interface *pim_ifp; struct pim_upstream *up; int fhr = 0; @@ -1408,7 +1404,7 @@ static void pim_show_interfaces(struct pim_instance *pim, struct vty *vty, pim_ifchannels = pim_if_ifchannel_count(pim_ifp); fhr = 0; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) + frr_each (rb_pim_upstream, &pim->upstream_head, up) if (ifp == up->rpf.source_nexthop.interface) if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR) fhr++; @@ -1975,7 +1971,6 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, const char *src_or_group, const char *group, bool uj) { struct channel_oil *c_oil; - struct listnode *node; json_object *json = NULL; json_object *json_group = NULL; json_object *json_ifp_in = NULL; @@ -1989,20 +1984,28 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, json = json_object_new_object(); } else { vty_out(vty, - "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN"); + "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted"); vty_out(vty, - "\nInstalled Source Group IIF OIL\n"); + "\nActive Source Group RPT IIF OIL\n"); } - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) { char grp_str[INET_ADDRSTRLEN]; char src_str[INET_ADDRSTRLEN]; char in_ifname[INTERFACE_NAMSIZ + 1]; char out_ifname[INTERFACE_NAMSIZ + 1]; int oif_vif_index; struct interface *ifp_in; + bool isRpt; first_oif = 1; + if ((c_oil->up && + PIM_UPSTREAM_FLAG_TEST_USE_RPT(c_oil->up->flags)) || + c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) + isRpt = true; + else + isRpt = false; + pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, grp_str, sizeof(grp_str)); pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, src_str, @@ -2056,6 +2059,12 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, json_ifp_in); json_object_int_add(json_source, "Installed", c_oil->installed); + if (isRpt) + json_object_boolean_true_add( + json_source, "isRpt"); + else + json_object_boolean_false_add( + json_source, "isRpt"); json_object_int_add(json_source, "RefCount", c_oil->oil_ref_count); json_object_int_add(json_source, "OilListSize", @@ -2074,8 +2083,9 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, c_oil->cc.wrong_if); } } else { - vty_out(vty, "%-9d %-15s %-15s %-16s ", - c_oil->installed, src_str, grp_str, in_ifname); + vty_out(vty, "%-6d %-15s %-15s %-3s %-16s ", + c_oil->installed, src_str, grp_str, + isRpt ? "y" : "n", in_ifname); } for (oif_vif_index = 0; oif_vif_index < MAXVIFS; @@ -2118,7 +2128,8 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, } else { if (first_oif) { first_oif = 0; - vty_out(vty, "%s(%c%c%c%c)", out_ifname, + vty_out(vty, "%s(%c%c%c%c%c)", + out_ifname, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) ? 'I' @@ -2134,9 +2145,13 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) ? '*' + : ' ', + (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_MUTE) + ? 'M' : ' '); } else - vty_out(vty, ", %s(%c%c%c%c)", + vty_out(vty, ", %s(%c%c%c%c%c)", out_ifname, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) @@ -2153,6 +2168,10 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_STAR) ? '*' + : ' ', + (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_MUTE) + ? 'M' : ' '); } } @@ -2405,7 +2424,6 @@ static const char *pim_reg_state2brief_str(enum pim_reg_state reg_state, static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, struct prefix_sg *sg, bool uj) { - struct listnode *upnode; struct pim_upstream *up; time_t now; json_object *json = NULL; @@ -2420,7 +2438,7 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, vty_out(vty, "Iif Source Group State Uptime JoinTimer RSTimer KATimer RefCnt\n"); - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { char src_str[INET_ADDRSTRLEN]; char grp_str[INET_ADDRSTRLEN]; char uptime[10]; @@ -2554,7 +2572,7 @@ static void pim_show_upstream(struct pim_instance *pim, struct vty *vty, } } -static void pim_show_join_desired_helper(struct pim_instance *pim, +static void pim_show_channel_helper(struct pim_instance *pim, struct vty *vty, struct pim_interface *pim_ifp, struct pim_ifchannel *ch, @@ -2613,7 +2631,7 @@ static void pim_show_join_desired_helper(struct pim_instance *pim, } } -static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, +static void pim_show_channel(struct pim_instance *pim, struct vty *vty, bool uj) { struct pim_interface *pim_ifp; @@ -2637,7 +2655,7 @@ static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { /* scan all interfaces */ - pim_show_join_desired_helper(pim, vty, pim_ifp, ch, + pim_show_channel_helper(pim, vty, pim_ifp, ch, json, uj); } } @@ -2649,10 +2667,75 @@ static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, } } +static void pim_show_join_desired_helper(struct pim_instance *pim, + struct vty *vty, + struct pim_upstream *up, + json_object *json, bool uj) +{ + json_object *json_group = NULL; + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + json_object *json_row = NULL; + + pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str)); + + if (uj) { + json_object_object_get_ex(json, grp_str, &json_group); + + if (!json_group) { + json_group = json_object_new_object(); + json_object_object_add(json, grp_str, json_group); + } + + json_row = json_object_new_object(); + json_object_pim_upstream_add(json_row, up); + json_object_string_add(json_row, "source", src_str); + json_object_string_add(json_row, "group", grp_str); + + if (pim_upstream_evaluate_join_desired(pim, up)) + json_object_boolean_true_add(json_row, + "evaluateJoinDesired"); + + json_object_object_add(json_group, src_str, json_row); + + } else { + vty_out(vty, "%-15s %-15s %-6s\n", + src_str, grp_str, + pim_upstream_evaluate_join_desired(pim, up) ? "yes" + : "no"); + } +} + +static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, + bool uj) +{ + struct pim_upstream *up; + + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + else + vty_out(vty, + "Source Group EvalJD\n"); + + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + /* scan all interfaces */ + pim_show_join_desired_helper(pim, vty, up, + json, uj); + } + + if (uj) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + static void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty, bool uj) { - struct listnode *upnode; struct pim_upstream *up; json_object *json = NULL; json_object *json_group = NULL; @@ -2664,7 +2747,7 @@ static void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty, vty_out(vty, "Source Group RpfIface RibNextHop RpfAddress \n"); - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { char src_str[INET_ADDRSTRLEN]; char grp_str[INET_ADDRSTRLEN]; char rpf_nexthop_str[PREFIX_STRLEN]; @@ -2785,7 +2868,6 @@ static void show_scan_oil_stats(struct pim_instance *pim, struct vty *vty, static void pim_show_rpf(struct pim_instance *pim, struct vty *vty, bool uj) { - struct listnode *up_node; struct pim_upstream *up; time_t now = pim_time_monotonic_sec(); json_object *json = NULL; @@ -2802,7 +2884,7 @@ static void pim_show_rpf(struct pim_instance *pim, struct vty *vty, bool uj) "Source Group RpfIface RpfAddress RibNextHop Metric Pref\n"); } - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, up_node, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { char src_str[INET_ADDRSTRLEN]; char grp_str[INET_ADDRSTRLEN]; char rpf_addr_str[PREFIX_STRLEN]; @@ -3842,11 +3924,8 @@ static void clear_mroute(struct pim_instance *pim) } /* clean up all upstreams*/ - if (pim->upstream_list) { - while (pim->upstream_list->count) { - up = listnode_head(pim->upstream_list); - pim_upstream_del(pim, up, __PRETTY_FUNCTION__); - } + while ((up = rb_pim_upstream_first(&pim->upstream_head))) { + pim_upstream_del(pim, up, __PRETTY_FUNCTION__); } } @@ -4438,6 +4517,90 @@ DEFUN (show_ip_pim_join_vrf_all, return CMD_WARNING; } +static void pim_show_jp_agg_helper(struct vty *vty, + struct interface *ifp, + struct pim_neighbor *neigh, + struct pim_upstream *up, + int is_join) +{ + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + char rpf_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str)); + /* pius->address.s_addr */ + pim_inet4_dump("<rpf?>", neigh->source_addr, rpf_str, sizeof(rpf_str)); + + vty_out(vty, "%-16s %-15s %-15s %-15s %5s\n", + ifp->name, rpf_str, src_str, + grp_str, is_join?"J":"P"); +} + +static void pim_show_jp_agg_list(struct pim_instance *pim, struct vty *vty) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + struct listnode *n_node; + struct pim_neighbor *neigh; + struct listnode *jag_node; + struct pim_jp_agg_group *jag; + struct listnode *js_node; + struct pim_jp_sources *js; + + vty_out(vty, + "Interface RPF Nbr Source Group State\n"); + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, + n_node, neigh)) { + for (ALL_LIST_ELEMENTS_RO(neigh->upstream_jp_agg, + jag_node, jag)) { + for (ALL_LIST_ELEMENTS_RO(jag->sources, + js_node, js)) { + pim_show_jp_agg_helper(vty, + ifp, neigh, js->up, + js->is_join); + } + } + } + } +} + +DEFPY (show_ip_pim_jp_agg, + show_ip_pim_jp_agg_cmd, + "show ip pim [vrf NAME] jp-agg", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "join prune aggregation list\n") +{ + struct vrf *v; + struct pim_instance *pim; + + v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME); + + if (!v) { + vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf); + return CMD_WARNING; + } + pim = pim_get_pim_instance(v->vrf_id); + + if (!pim) { + vty_out(vty, "%% Unable to find pim instance\n"); + return CMD_WARNING; + } + + pim_show_jp_agg_list(pim, vty); + + return CMD_SUCCESS; +} + DEFUN (show_ip_pim_local_membership, show_ip_pim_local_membership_cmd, "show ip pim [vrf NAME] local-membership [json]", @@ -4702,6 +4865,28 @@ DEFUN (show_ip_pim_upstream_vrf_all, return CMD_SUCCESS; } +DEFUN (show_ip_pim_channel, + show_ip_pim_channel_cmd, + "show ip pim [vrf NAME] channel [json]", + SHOW_STR + IP_STR + PIM_STR + VRF_CMD_HELP_STR + "PIM downstream channel info\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + bool uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + pim_show_channel(vrf->info, vty, uj); + + return CMD_SUCCESS; +} + DEFUN (show_ip_pim_upstream_join_desired, show_ip_pim_upstream_join_desired_cmd, "show ip pim [vrf NAME] upstream-join-desired [json]", @@ -5223,7 +5408,7 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, now = pim_time_monotonic_sec(); /* print list of PIM and IGMP routes */ - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) { found_oif = 0; first = 1; if (!c_oil->installed && !uj) @@ -5293,6 +5478,16 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty, if (ttl < 1) continue; + /* do not display muted OIFs */ + if (c_oil->oif_flags[oif_vif_index] + & PIM_OIF_FLAG_MUTE) + continue; + + if (c_oil->oil.mfcc_parent == oif_vif_index && + !pim_mroute_allow_iif_in_oil(c_oil, + oif_vif_index)) + continue; + ifp_out = pim_if_find_by_vif_index(pim, oif_vif_index); pim_time_uptime( mroute_uptime, sizeof(mroute_uptime), @@ -5621,7 +5816,7 @@ DEFUN (clear_ip_mroute_count, return CMD_WARNING; pim = vrf->info; - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + frr_each(rb_pim_oil, &pim->channel_oil_head, c_oil) { if (!c_oil->installed) continue; @@ -5656,7 +5851,7 @@ static void show_mroute_count(struct pim_instance *pim, struct vty *vty) "Source Group LastUsed Packets Bytes WrongIf \n"); /* Print PIM and IGMP route counts */ - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -5761,7 +5956,7 @@ static void show_mroute_summary(struct pim_instance *pim, struct vty *vty) vty_out(vty, "Mroute Type Installed/Total\n"); - for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) { + frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) { if (!c_oil->installed) { if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) starg_sw_mroute_cnt++; @@ -6708,7 +6903,7 @@ DEFUN (interface_no_ip_igmp, DEFUN (interface_ip_igmp_join, interface_ip_igmp_join_cmd, - "ip igmp join A.B.C.D A.B.C.D", + "ip igmp join A.B.C.D [A.B.C.D]", IP_STR IFACE_IGMP_STR "IGMP join multicast group\n" @@ -6734,12 +6929,21 @@ DEFUN (interface_ip_igmp_join, } /* Source address */ - source_str = argv[idx_ipv4_2]->arg; - result = inet_pton(AF_INET, source_str, &source_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s\n", - source_str, errno, safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; + if (argc == (idx_ipv4_2 + 1)) { + source_str = argv[idx_ipv4_2]->arg; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s\n", + source_str, errno, safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + /* Reject 0.0.0.0. Reserved for any source. */ + if (source_addr.s_addr == INADDR_ANY) { + vty_out(vty, "Bad source address %s\n", source_str); + return CMD_WARNING_CONFIG_FAILED; + } + } else { + source_addr.s_addr = INADDR_ANY; } CMD_FERR_RETURN(pim_if_igmp_join_add(ifp, group_addr, source_addr), @@ -6750,7 +6954,7 @@ DEFUN (interface_ip_igmp_join, DEFUN (interface_no_ip_igmp_join, interface_no_ip_igmp_join_cmd, - "no ip igmp join A.B.C.D A.B.C.D", + "no ip igmp join A.B.C.D [A.B.C.D]", NO_STR IP_STR IFACE_IGMP_STR @@ -6777,12 +6981,22 @@ DEFUN (interface_no_ip_igmp_join, } /* Source address */ - source_str = argv[idx_ipv4_2]->arg; - result = inet_pton(AF_INET, source_str, &source_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s\n", - source_str, errno, safe_strerror(errno)); - return CMD_WARNING_CONFIG_FAILED; + if (argc == (idx_ipv4_2 + 1)) { + source_str = argv[idx_ipv4_2]->arg; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s\n", + source_str, errno, safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + /* Reject 0.0.0.0. Reserved for any source. */ + if (source_addr.s_addr == INADDR_ANY) { + vty_out(vty, "Bad source address %s\n", source_str); + return CMD_WARNING_CONFIG_FAILED; + } + } else { + source_str = "*"; + source_addr.s_addr = INADDR_ANY; } result = pim_if_igmp_join_del(ifp, group_addr, source_addr); @@ -7647,55 +7861,7 @@ DEFUN(interface_no_ip_pim_boundary_oil, DEFUN (interface_ip_mroute, interface_ip_mroute_cmd, - "ip mroute INTERFACE A.B.C.D", - IP_STR - "Add multicast route\n" - "Outgoing interface name\n" - "Group address\n") -{ - VTY_DECLVAR_CONTEXT(interface, iif); - struct pim_interface *pim_ifp; - struct pim_instance *pim; - int idx_interface = 2; - int idx_ipv4 = 3; - struct interface *oif; - const char *oifname; - const char *grp_str; - struct in_addr grp_addr; - struct in_addr src_addr; - int result; - - PIM_GET_PIM_INTERFACE(pim_ifp, iif); - pim = pim_ifp->pim; - - oifname = argv[idx_interface]->arg; - oif = if_lookup_by_name(oifname, pim->vrf_id); - if (!oif) { - vty_out(vty, "No such interface name %s\n", oifname); - return CMD_WARNING; - } - - grp_str = argv[idx_ipv4]->arg; - result = inet_pton(AF_INET, grp_str, &grp_addr); - if (result <= 0) { - vty_out(vty, "Bad group address %s: errno=%d: %s\n", grp_str, - errno, safe_strerror(errno)); - return CMD_WARNING; - } - - src_addr.s_addr = INADDR_ANY; - - if (pim_static_add(pim, iif, oif, grp_addr, src_addr)) { - vty_out(vty, "Failed to add route\n"); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN (interface_ip_mroute_source, - interface_ip_mroute_source_cmd, - "ip mroute INTERFACE A.B.C.D A.B.C.D", + "ip mroute INTERFACE A.B.C.D [A.B.C.D]", IP_STR "Add multicast route\n" "Outgoing interface name\n" @@ -7707,7 +7873,6 @@ DEFUN (interface_ip_mroute_source, struct pim_instance *pim; int idx_interface = 2; int idx_ipv4 = 3; - int idx_ipv4_2 = 4; struct interface *oif; const char *oifname; const char *grp_str; @@ -7734,16 +7899,21 @@ DEFUN (interface_ip_mroute_source, return CMD_WARNING; } - src_str = argv[idx_ipv4_2]->arg; - result = inet_pton(AF_INET, src_str, &src_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str, - errno, safe_strerror(errno)); - return CMD_WARNING; - } + if (argc == (idx_ipv4 + 1)) { + src_addr.s_addr = INADDR_ANY; + } + else { + src_str = argv[idx_ipv4 + 1]->arg; + result = inet_pton(AF_INET, src_str, &src_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str, + errno, safe_strerror(errno)); + return CMD_WARNING; + } + } if (pim_static_add(pim, iif, oif, grp_addr, src_addr)) { - vty_out(vty, "Failed to add route\n"); + vty_out(vty, "Failed to add static mroute\n"); return CMD_WARNING; } @@ -7752,56 +7922,7 @@ DEFUN (interface_ip_mroute_source, DEFUN (interface_no_ip_mroute, interface_no_ip_mroute_cmd, - "no ip mroute INTERFACE A.B.C.D", - NO_STR - IP_STR - "Add multicast route\n" - "Outgoing interface name\n" - "Group Address\n") -{ - VTY_DECLVAR_CONTEXT(interface, iif); - struct pim_interface *pim_ifp; - struct pim_instance *pim; - int idx_interface = 3; - int idx_ipv4 = 4; - struct interface *oif; - const char *oifname; - const char *grp_str; - struct in_addr grp_addr; - struct in_addr src_addr; - int result; - - PIM_GET_PIM_INTERFACE(pim_ifp, iif); - pim = pim_ifp->pim; - - oifname = argv[idx_interface]->arg; - oif = if_lookup_by_name(oifname, pim->vrf_id); - if (!oif) { - vty_out(vty, "No such interface name %s\n", oifname); - return CMD_WARNING; - } - - grp_str = argv[idx_ipv4]->arg; - result = inet_pton(AF_INET, grp_str, &grp_addr); - if (result <= 0) { - vty_out(vty, "Bad group address %s: errno=%d: %s\n", grp_str, - errno, safe_strerror(errno)); - return CMD_WARNING; - } - - src_addr.s_addr = INADDR_ANY; - - if (pim_static_del(pim, iif, oif, grp_addr, src_addr)) { - vty_out(vty, "Failed to remove route\n"); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN (interface_no_ip_mroute_source, - interface_no_ip_mroute_source_cmd, - "no ip mroute INTERFACE A.B.C.D A.B.C.D", + "no ip mroute INTERFACE A.B.C.D [A.B.C.D]", NO_STR IP_STR "Add multicast route\n" @@ -7814,7 +7935,6 @@ DEFUN (interface_no_ip_mroute_source, struct pim_instance *pim; int idx_interface = 3; int idx_ipv4 = 4; - int idx_ipv4_2 = 5; struct interface *oif; const char *oifname; const char *grp_str; @@ -7841,16 +7961,21 @@ DEFUN (interface_no_ip_mroute_source, return CMD_WARNING; } - src_str = argv[idx_ipv4_2]->arg; - result = inet_pton(AF_INET, src_str, &src_addr); - if (result <= 0) { - vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str, - errno, safe_strerror(errno)); - return CMD_WARNING; - } + if (argc == (idx_ipv4 + 1)) { + src_addr.s_addr = INADDR_ANY; + } + else { + src_str = argv[idx_ipv4 + 1]->arg; + result = inet_pton(AF_INET, src_str, &src_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s\n", src_str, + errno, safe_strerror(errno)); + return CMD_WARNING; + } + } if (pim_static_del(pim, iif, oif, grp_addr, src_addr)) { - vty_out(vty, "Failed to remove route\n"); + vty_out(vty, "Failed to remove static mroute\n"); return CMD_WARNING; } @@ -10321,9 +10446,7 @@ void pim_cmd_init(void) // Static mroutes NEB install_element(INTERFACE_NODE, &interface_ip_mroute_cmd); - install_element(INTERFACE_NODE, &interface_ip_mroute_source_cmd); install_element(INTERFACE_NODE, &interface_no_ip_mroute_cmd); - install_element(INTERFACE_NODE, &interface_no_ip_mroute_source_cmd); install_element(VIEW_NODE, &show_ip_igmp_interface_cmd); install_element(VIEW_NODE, &show_ip_igmp_interface_vrf_all_cmd); @@ -10344,6 +10467,7 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_interface_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_join_cmd); install_element(VIEW_NODE, &show_ip_pim_join_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_pim_jp_agg_cmd); install_element(VIEW_NODE, &show_ip_pim_local_membership_cmd); install_element(VIEW_NODE, &show_ip_pim_neighbor_cmd); install_element(VIEW_NODE, &show_ip_pim_neighbor_vrf_all_cmd); @@ -10354,6 +10478,7 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_pim_state_vrf_all_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_vrf_all_cmd); + install_element(VIEW_NODE, &show_ip_pim_channel_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd); install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd); install_element(VIEW_NODE, &show_ip_pim_rp_cmd); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 3ee9caebcf..c615540149 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -1481,6 +1481,13 @@ void pim_if_create_pimreg(struct pim_instance *pim) pim_if_new(pim->regiface, false, false, true, false /*vxlan_term*/); + /* + * On vrf moves we delete the interface if there + * is nothing going on with it. We cannot have + * the pimregiface deleted. + */ + pim->regiface->configured = true; + } } @@ -1581,6 +1588,7 @@ int pim_ifp_create(struct interface *ifp) int pim_ifp_up(struct interface *ifp) { + struct pim_interface *pim_ifp; struct pim_instance *pim; uint32_t table_id; @@ -1593,24 +1601,21 @@ int pim_ifp_up(struct interface *ifp) } pim = pim_get_pim_instance(ifp->vrf_id); - if (if_is_operative(ifp)) { - struct pim_interface *pim_ifp; - pim_ifp = ifp->info; - /* - * If we have a pim_ifp already and this is an if_add - * that means that we probably have a vrf move event - * If that is the case, set the proper vrfness. - */ - if (pim_ifp) - pim_ifp->pim = pim; + pim_ifp = ifp->info; + /* + * If we have a pim_ifp already and this is an if_add + * that means that we probably have a vrf move event + * If that is the case, set the proper vrfness. + */ + if (pim_ifp) + pim_ifp->pim = pim; - /* - pim_if_addr_add_all() suffices for bringing up both IGMP and - PIM - */ - pim_if_addr_add_all(ifp); - } + /* + pim_if_addr_add_all() suffices for bringing up both IGMP and + PIM + */ + pim_if_addr_add_all(ifp); /* * If we have a pimreg device callback and it's for a specific diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 48bd031f1e..3a7eb45f27 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -147,10 +147,11 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) ch->upstream, ch, ch->parent)) pim_channel_add_oif(ch->upstream->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + PIM_OIF_FLAG_PROTO_STAR, + __func__); pim_channel_del_oif(ch->upstream->channel_oil, - ch->interface, mask); + ch->interface, mask, __func__); /* * Do we have any S,G's that are inheriting? * Nuke from on high too. @@ -161,9 +162,10 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) for (ALL_LIST_ELEMENTS_RO(ch->upstream->sources, up_node, child)) - pim_channel_del_oif(child->channel_oil, - ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + pim_channel_del_inherited_oif( + child->channel_oil, + ch->interface, + __func__); } } @@ -179,9 +181,7 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) listnode_delete(ch->upstream->ifchannels, ch); - if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) { - pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream); - } + pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream); /* upstream is common across ifchannels, check if upstream's ifchannel list is empty before deleting upstream_del @@ -250,6 +250,7 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, { enum pim_ifjoin_state old_state = ch->ifjoin_state; struct pim_interface *pim_ifp = ch->interface->info; + struct pim_ifchannel *child_ch; if (PIM_DEBUG_PIM_EVENTS) zlog_debug( @@ -293,28 +294,18 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, if (!c_oil) continue; - if (!pim_upstream_evaluate_join_desired( - pim_ifp->pim, child)) { - pim_channel_del_oif( - c_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); - pim_upstream_update_join_desired( - pim_ifp->pim, child); - } - /* * If the S,G has no if channel and the * c_oil still * has output here then the *,G was * supplying the implied * if channel. So remove it. - * I think this is dead code now. is it? */ if (c_oil->oil.mfcc_ttls [pim_ifp->mroute_vif_index]) - pim_channel_del_oif( + pim_channel_del_inherited_oif( c_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + __func__); } } if (ch->ifjoin_state == PIM_IFJOIN_JOIN) { @@ -328,12 +319,19 @@ void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch, child->sg_str, up->sg_str); - if (pim_upstream_evaluate_join_desired( - pim_ifp->pim, child)) { + /* check if the channel can be + * inherited into the SG's OIL + */ + child_ch = pim_ifchannel_find( + ch->interface, + &child->sg); + if (pim_upstream_eval_inherit_if( + child, child_ch, ch)) { pim_channel_add_oif( child->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + PIM_OIF_FLAG_PROTO_STAR, + __func__); pim_upstream_update_join_desired( pim_ifp->pim, child); } @@ -783,6 +781,28 @@ static int nonlocal_upstream(int is_join, struct interface *recv_ifp, return 1; /* non-local */ } +static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel *ch, + struct pim_interface *pim_ifp) +{ + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, + PIM_IFJOIN_JOIN); + PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags); + /* check if the interface qualifies as an immediate + * OIF + */ + if (pim_upstream_evaluate_join_desired_interface( + ch->upstream, ch, + NULL /*starch*/)) { + pim_channel_add_oif(ch->upstream->channel_oil, + ch->interface, + PIM_OIF_FLAG_PROTO_PIM, + __func__); + pim_upstream_update_join_desired(pim_ifp->pim, + ch->upstream); + } +} + + void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, struct in_addr upstream, struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime) @@ -890,25 +910,8 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, if (source_flags & PIM_ENCODE_RPT_BIT) pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); - else { - /* - * We have received a S,G join and we are in - * S,G RPT Prune state. Which means we need - * to transition to Join state and setup - * state as appropriate. - */ - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, - PIM_IFJOIN_JOIN); - PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags); - if (pim_upstream_evaluate_join_desired(pim_ifp->pim, - ch->upstream)) { - pim_channel_add_oif(ch->upstream->channel_oil, - ch->interface, - PIM_OIF_FLAG_PROTO_PIM); - pim_upstream_update_join_desired(pim_ifp->pim, - ch->upstream); - } - } + else + pim_ifchannel_ifjoin_handler(ch, pim_ifp); break; case PIM_IFJOIN_PRUNE_PENDING: THREAD_OFF(ch->t_ifjoin_prune_pending_timer); @@ -916,9 +919,9 @@ void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, THREAD_OFF(ch->t_ifjoin_expiry_timer); pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); - } else - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, - PIM_IFJOIN_JOIN); + } else { + pim_ifchannel_ifjoin_handler(ch, pim_ifp); + } break; case PIM_IFJOIN_PRUNE_TMP: break; @@ -1105,9 +1108,9 @@ int pim_ifchannel_local_membership_add(struct interface *ifp, if (pim_upstream_evaluate_join_desired_interface( child, ch, starch)) { pim_channel_add_oif(child->channel_oil, ifp, - PIM_OIF_FLAG_PROTO_STAR); - pim_upstream_switch(pim, child, - PIM_UPSTREAM_JOINED); + PIM_OIF_FLAG_PROTO_STAR, + __func__); + pim_upstream_update_join_desired(pim, child); } } @@ -1124,12 +1127,14 @@ int pim_ifchannel_local_membership_add(struct interface *ifp, == PREFIX_DENY) { pim_channel_add_oif( up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); } } } else pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); } return 1; @@ -1174,22 +1179,19 @@ void pim_ifchannel_local_membership_del(struct interface *ifp, child->sg_str); ch = pim_ifchannel_find(ifp, &child->sg); - if (c_oil - && !pim_upstream_evaluate_join_desired_interface( - child, ch, starch)) - pim_channel_del_oif(c_oil, ifp, - PIM_OIF_FLAG_PROTO_STAR); - /* * If the S,G has no if channel and the c_oil still * has output here then the *,G was supplying the * implied * if channel. So remove it. */ - if (!chchannel && c_oil - && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]) - pim_channel_del_oif(c_oil, ifp, - PIM_OIF_FLAG_PROTO_STAR); + if (!pim_upstream_evaluate_join_desired_interface( + child, ch, starch) || + (!chchannel && + c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])) { + pim_channel_del_inherited_oif(c_oil, ifp, + __func__); + } /* Child node removal/ref count-- will happen as part of * parent' delete_no_info */ @@ -1408,12 +1410,10 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom, child->upstream))) { pim_channel_add_oif( child->upstream->channel_oil, - ch->interface, PIM_OIF_FLAG_PROTO_STAR); - pim_upstream_switch(pim, child->upstream, - PIM_UPSTREAM_JOINED); - pim_jp_agg_single_upstream_send( - &child->upstream->rpf, child->upstream, - true); + ch->interface, PIM_OIF_FLAG_PROTO_STAR, + __func__); + pim_upstream_update_join_desired(pim, + child->upstream); } send_upstream_starg = true; diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 3602d98a3e..39ef706f79 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -478,10 +478,24 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ + if (ip_hlen > len) { + zlog_warn( + "IGMP packet header claims size %zu, but we only have %zu bytes", + ip_hlen, len); + return -1; + } + igmp_msg = buf + ip_hlen; - msg_type = *igmp_msg; igmp_msg_len = len - ip_hlen; + if (igmp_msg_len < PIM_IGMP_MIN_LEN) { + zlog_warn("IGMP message size=%d shorter than minimum=%d", + igmp_msg_len, PIM_IGMP_MIN_LEN); + return -1; + } + + msg_type = *igmp_msg; + if (PIM_DEBUG_IGMP_PACKETS) { zlog_debug( "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d", @@ -489,12 +503,6 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) msg_type, igmp_msg_len); } - if (igmp_msg_len < PIM_IGMP_MIN_LEN) { - zlog_warn("IGMP message size=%d shorter than minimum=%d", - igmp_msg_len, PIM_IGMP_MIN_LEN); - return -1; - } - switch (msg_type) { case PIM_IGMP_MEMBERSHIP_QUERY: { int max_resp_code = igmp_msg[1]; @@ -1105,8 +1113,10 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, } if (pim_is_group_224_0_0_0_24(group_addr)) { - zlog_warn("%s: Group specified is part of 224.0.0.0/24", - __PRETTY_FUNCTION__); + if (PIM_DEBUG_IGMP_TRACE) + zlog_debug( + "%s: Group specified %s is part of 224.0.0.0/24", + __PRETTY_FUNCTION__, inet_ntoa(group_addr)); return NULL; } /* diff --git a/pimd/pim_igmp_join.h b/pimd/pim_igmp_join.h index 88385bffba..c323902764 100644 --- a/pimd/pim_igmp_join.h +++ b/pimd/pim_igmp_join.h @@ -26,6 +26,10 @@ #define SOL_IP IPPROTO_IP #endif +#ifndef MCAST_JOIN_GROUP +#define MCAST_JOIN_GROUP 42 +#endif + #ifndef MCAST_JOIN_SOURCE_GROUP #define MCAST_JOIN_SOURCE_GROUP 46 struct group_source_req { @@ -58,8 +62,12 @@ static int pim_igmp_join_source(int fd, ifindex_t ifindex, req.gsr_interface = ifindex; - return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &req, - sizeof(req)); + if (source_addr.s_addr == INADDR_ANY) + return setsockopt(fd, SOL_IP, MCAST_JOIN_GROUP, &req, + sizeof(req)); + else + return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &req, + sizeof(req)); } #endif /* PIM_IGMP_JOIN_H */ diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index 0758e2f784..695d04c7c2 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -864,6 +864,16 @@ int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr, pim_ifp = ifp->info; pim = pim_ifp->pim; + if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) { + if (PIM_DEBUG_MTRACE) + zlog_warn( + "Recv mtrace packet from %s on %s: too short," + " len=%d, min=%zu", + from_str, ifp->name, igmp_msg_len, + sizeof(struct igmp_mtrace)); + return -1; + } + mtracep = (struct igmp_mtrace *)igmp_msg; recv_checksum = mtracep->checksum; diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index dd3ac8fcb0..da0c75decb 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -28,6 +28,8 @@ #include "pim_assert.h" #include "pim_bsm.h" #include "pim_vxlan_instance.h" +#include "pim_oil.h" +#include "pim_upstream.h" #if defined(HAVE_LINUX_MROUTE_H) #include <linux/mroute.h> @@ -107,8 +109,7 @@ struct pim_instance { struct list *static_routes; // Upstream vrf specific information - struct list *upstream_list; - struct hash *upstream_hash; + struct rb_pim_upstream_head upstream_head; struct timer_wheel *upstream_sg_wheel; /* @@ -119,8 +120,7 @@ struct pim_instance { int iface_vif_index[MAXVIFS]; - struct list *channel_oil_list; - struct hash *channel_oil_hash; + struct rb_pim_oil_head channel_oil_head; struct pim_msdp msdp; struct pim_vxlan_instance vxlan; diff --git a/pimd/pim_join.c b/pimd/pim_join.c index 89be42842e..fbb547c80e 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -327,7 +327,6 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, recv_prune(ifp, neigh, msg_holdtime, msg_upstream_addr.u.prefix4, &sg, msg_source_flags); - /* * So if we are receiving a S,G,RPT prune * before we have any data for that S,G @@ -348,10 +347,10 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, "%s: SGRpt flag is set, del inherit oif from up %s", __PRETTY_FUNCTION__, up->sg_str); - pim_channel_del_oif( + pim_channel_del_inherited_oif( up->channel_oil, starg_ch->interface, - PIM_OIF_FLAG_PROTO_STAR); + __func__); } } } diff --git a/pimd/pim_jp_agg.c b/pimd/pim_jp_agg.c index 418f66df05..2662c9eae6 100644 --- a/pimd/pim_jp_agg.c +++ b/pimd/pim_jp_agg.c @@ -117,10 +117,16 @@ void pim_jp_agg_clear_group(struct list *group) static struct pim_iface_upstream_switch * pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf) { - struct pim_interface *pim_ifp = rpf->source_nexthop.interface->info; + struct interface *ifp = rpf->source_nexthop.interface; + struct pim_interface *pim_ifp; struct pim_iface_upstream_switch *pius; struct listnode *node, *nnode; + if (!ifp) + return NULL; + + pim_ifp = ifp->info; + /* Old interface is pim disabled */ if (!pim_ifp) return NULL; @@ -142,7 +148,8 @@ pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf) return pius; } -void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up) +void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up, + struct pim_neighbor *nbr) { struct listnode *node, *nnode; struct pim_jp_agg_group *jag = NULL; @@ -161,6 +168,20 @@ void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up) break; } + if (nbr) { + if (PIM_DEBUG_TRACE) { + char src_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("<src?>", nbr->source_addr, src_str, + sizeof(src_str)); + zlog_debug( + "up %s remove from nbr %s/%s jp-agg-list", + up->sg_str, + nbr->interface->name, + src_str); + } + } + if (js) { js->up = NULL; listnode_delete(jag->sources, js); @@ -248,7 +269,7 @@ void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore) } void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, - bool is_join) + bool is_join, struct pim_neighbor *nbr) { struct listnode *node, *nnode; struct pim_jp_agg_group *jag = NULL; @@ -274,6 +295,20 @@ void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, break; } + if (nbr) { + if (PIM_DEBUG_TRACE) { + char src_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("<src?>", nbr->source_addr, src_str, + sizeof(src_str)); + zlog_debug( + "up %s add to nbr %s/%s jp-agg-list", + up->sg_str, + up->rpf.source_nexthop.interface->name, + src_str); + } + } + if (!js) { js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE, sizeof(struct pim_jp_sources)); @@ -314,10 +349,11 @@ void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf, /* send Prune(S,G) to the old upstream neighbor */ if (opius) - pim_jp_agg_add_group(opius->us, up, false); + pim_jp_agg_add_group(opius->us, up, false, NULL); /* send Join(S,G) to the current upstream neighbor */ - pim_jp_agg_add_group(npius->us, up, true); + if (npius) + pim_jp_agg_add_group(npius->us, up, true, NULL); } @@ -331,10 +367,10 @@ void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf, static bool first = true; /* skip JP upstream messages if source is directly connected */ - if (!up || !rpf->source_nexthop.interface || pim_if_connected_to_source( - rpf->source_nexthop - .interface, - up->sg.src)) + if (!up || !rpf->source_nexthop.interface || + pim_if_connected_to_source(rpf->source_nexthop.interface, + up->sg.src) || + if_is_loopback_or_vrf(rpf->source_nexthop.interface)) return; if (first) { diff --git a/pimd/pim_jp_agg.h b/pimd/pim_jp_agg.h index aa21aa9816..d88ff8892b 100644 --- a/pimd/pim_jp_agg.h +++ b/pimd/pim_jp_agg.h @@ -37,10 +37,11 @@ void pim_jp_agg_group_list_free(struct pim_jp_agg_group *jag); int pim_jp_agg_group_list_cmp(void *arg1, void *arg2); void pim_jp_agg_clear_group(struct list *group); -void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up); +void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up, + struct pim_neighbor *nbr); void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up, - bool is_join); + bool is_join, struct pim_neighbor *nbr); void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf, struct pim_upstream *up); diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 3f79b304e5..f0ca367258 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -29,7 +29,6 @@ #include "memory.h" #include "vrf.h" -#include "memory_vty.h" #include "filter.h" #include "vty.h" #include "sigevent.h" diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 2859d26eba..3459abbc19 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -207,7 +207,7 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp, up = pim_upstream_find_or_add( &sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, __PRETTY_FUNCTION__); - pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); return 0; } @@ -228,16 +228,10 @@ static int pim_mroute_msg_nocache(int fd, struct interface *ifp, pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time); up->channel_oil->cc.pktcnt++; - PIM_UPSTREAM_FLAG_SET_FHR(up->flags); // resolve mfcc_parent prior to mroute_add in channel_add_oif if (up->rpf.source_nexthop.interface && up->channel_oil->oil.mfcc_parent >= MAXVIFS) { - int vif_index = 0; - vif_index = pim_if_find_vifindex_by_ifindex( - pim_ifp->pim, - up->rpf.source_nexthop.interface->ifindex); - pim_channel_oil_change_iif(pim_ifp->pim, up->channel_oil, - vif_index, __PRETTY_FUNCTION__); + pim_upstream_mroute_iif_update(up->channel_oil, __func__); } pim_register_join(up); @@ -283,8 +277,7 @@ static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, pim_upstream_keep_alive_timer_start( up, pim_ifp->pim->keep_alive_time); pim_upstream_inherited_olist(pim_ifp->pim, up); - pim_upstream_switch(pim_ifp->pim, up, - PIM_UPSTREAM_JOINED); + pim_upstream_update_join_desired(pim_ifp->pim, up); if (PIM_DEBUG_MROUTE) zlog_debug("%s: Creating %s upstream on LHR", @@ -330,6 +323,15 @@ static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, pim_str_sg_dump(&sg)); return 0; } + + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) { + if (PIM_DEBUG_PIM_REG) + zlog_debug( + "%s register forward skipped, not FHR", + up->sg_str); + return 0; + } + pim_register_send((uint8_t *)buf + sizeof(struct ip), ntohs(ip_hdr->ip_len) - sizeof(struct ip), pim_ifp->primary_address, rpg, 0, up); @@ -518,7 +520,7 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, pim_upstream_inherited_olist(pim_ifp->pim, up); if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, + pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); } else { if (I_am_RP(pim_ifp->pim, up->sg.grp)) { @@ -557,6 +559,8 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, up->channel_oil->cc.pktcnt++; pim_register_join(up); pim_upstream_inherited_olist(pim_ifp->pim, up); + if (!up->channel_oil->installed) + pim_upstream_mroute_add(up->channel_oil, __func__); // Send the packet to the RP pim_mroute_msg_wholepkt(fd, ifp, buf); @@ -565,7 +569,8 @@ static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, __PRETTY_FUNCTION__, NULL); if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, + __PRETTY_FUNCTION__); } return 0; @@ -585,6 +590,9 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, struct in_addr ifaddr; struct igmp_sock *igmp; + if (buf_size < (int)sizeof(struct ip)) + return 0; + ip_hdr = (const struct ip *)buf; if (ip_hdr->ip_p == IPPROTO_IGMP) { @@ -882,72 +890,92 @@ int pim_mroute_del_vif(struct interface *ifp) return 0; } -int pim_mroute_add(struct channel_oil *c_oil, const char *name) +/* + * Prevent creating MFC entry with OIF=IIF. + * + * This is a protection against implementation mistakes. + * + * PIM protocol implicitely ensures loopfree multicast topology. + * + * IGMP must be protected against adding looped MFC entries created + * by both source and receiver attached to the same interface. See + * TODO T22. + * We shall allow igmp to create upstream when it is DR for the intf. + * Assume RP reachable via non DR. + */ +bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil, + int oif_index) +{ +#ifdef PIM_ENFORCE_LOOPFREE_MFC + struct interface *ifp_out; + struct pim_interface *pim_ifp; + + if (c_oil->up && + PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags)) + return true; + + ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index); + if (!ifp_out) + return false; + pim_ifp = ifp_out->info; + if (!pim_ifp) + return false; + if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_IGMP) && + PIM_I_am_DR(pim_ifp)) + return true; + + return false; +#else + return true; +#endif +} + +static inline void pim_mroute_copy(struct mfcctl *oil, + struct channel_oil *c_oil) +{ + int i; + + oil->mfcc_origin = c_oil->oil.mfcc_origin; + oil->mfcc_mcastgrp = c_oil->oil.mfcc_mcastgrp; + oil->mfcc_parent = c_oil->oil.mfcc_parent; + + for (i = 0; i < MAXVIFS; ++i) { + if ((oil->mfcc_parent == i) && + !pim_mroute_allow_iif_in_oil(c_oil, i)) { + oil->mfcc_ttls[i] = 0; + continue; + } + + if (c_oil->oif_flags[i] & PIM_OIF_FLAG_MUTE) + oil->mfcc_ttls[i] = 0; + else + oil->mfcc_ttls[i] = c_oil->oil.mfcc_ttls[i]; + } +} + +/* This function must not be called directly 0 + * use pim_upstream_mroute_add or pim_static_mroute_add instead + */ +static int pim_mroute_add(struct channel_oil *c_oil, const char *name) { struct pim_instance *pim = c_oil->pim; + struct mfcctl tmp_oil; int err; - int orig = 0; - int orig_iif_vif = 0; - struct pim_interface *pim_reg_ifp = NULL; - int orig_pimreg_ttl = 0; - bool pimreg_ttl_reset = false; - struct pim_interface *vxlan_ifp = NULL; - int orig_term_ttl = 0; - bool orig_term_ttl_reset = false; pim->mroute_add_last = pim_time_monotonic_sec(); ++pim->mroute_add_events; - /* Do not install route if incoming interface is undefined. */ - if (c_oil->oil.mfcc_parent >= MAXVIFS) { - if (PIM_DEBUG_MROUTE) { - char buf[1000]; - zlog_debug( - "%s(%s) %s Attempting to add vifi that is invalid to mroute table", - __PRETTY_FUNCTION__, name, - pim_channel_oil_dump(c_oil, buf, sizeof(buf))); - } - return -2; - } + /* Copy the oil to a temporary structure to fixup (without need to + * later restore) before sending the mroute add to the dataplane + */ + pim_mroute_copy(&tmp_oil, c_oil); /* The linux kernel *expects* the incoming * vif to be part of the outgoing list * in the case of a (*,G). */ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) { - orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent]; - c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1; - } - - if (c_oil->up) { - /* suppress pimreg in the OIL if the mroute is not supposed to - * trigger register encapsulated data - */ - if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) { - pim_reg_ifp = pim->regiface->info; - orig_pimreg_ttl = - c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index]; - c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = 0; - /* remember to flip it back after MFC programming */ - pimreg_ttl_reset = true; - } - - vxlan_ifp = pim_vxlan_get_term_ifp(pim); - /* 1. vxlan termination device must never be added to the - * origination mroute (and that can actually happen because - * of XG inheritance from the termination mroute) otherwise - * traffic will end up looping. - * 2. vxlan termination device should be removed from the non-DF - * to prevent duplicates to the overlay rxer - */ - if (vxlan_ifp && - (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) || - PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags))) { - orig_term_ttl_reset = true; - orig_term_ttl = - c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index]; - c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = 0; - } + tmp_oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1; } /* @@ -959,33 +987,19 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) */ if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY && c_oil->oil.mfcc_parent != 0) { - orig_iif_vif = c_oil->oil.mfcc_parent; - c_oil->oil.mfcc_parent = 0; + tmp_oil.mfcc_parent = 0; } err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC, - &c_oil->oil, sizeof(c_oil->oil)); + &tmp_oil, sizeof(tmp_oil)); if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY - && orig_iif_vif != 0) { - c_oil->oil.mfcc_parent = orig_iif_vif; + && c_oil->oil.mfcc_parent != 0) { + tmp_oil.mfcc_parent = c_oil->oil.mfcc_parent; err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC, - &c_oil->oil, sizeof(c_oil->oil)); - } - - if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) - c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig; - - if (pimreg_ttl_reset) { - assert(pim_reg_ifp); - c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = - orig_pimreg_ttl; + &tmp_oil, sizeof(tmp_oil)); } - if (orig_term_ttl_reset) - c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = - orig_term_ttl; - if (err) { zlog_warn( "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s", @@ -1007,6 +1021,142 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) return 0; } +static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil, + const char *name) +{ + vifi_t iif = MAXVIFS; + struct interface *ifp = NULL; + struct pim_interface *pim_ifp; + struct pim_upstream *up = c_oil->up; + + if (up) { + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) { + if (up->parent) + ifp = up->parent->rpf.source_nexthop.interface; + } else { + ifp = up->rpf.source_nexthop.interface; + } + if (ifp) { + pim_ifp = (struct pim_interface *)ifp->info; + if (pim_ifp) + iif = pim_ifp->mroute_vif_index; + } + } + return iif; +} + +static int pim_upstream_mroute_update(struct channel_oil *c_oil, + const char *name) +{ + char buf[1000]; + + if (c_oil->oil.mfcc_parent >= MAXVIFS) { + /* the c_oil cannot be installed as a mroute yet */ + if (PIM_DEBUG_MROUTE) + zlog_debug( + "%s(%s) %s mroute not ready to be installed; %s", + __func__, name, + pim_channel_oil_dump(c_oil, buf, + sizeof(buf)), + c_oil->installed ? + "uninstall" : "skip"); + /* if already installed flush it out as we are going to stop + * updates to it leaving it in a stale state + */ + if (c_oil->installed) + pim_mroute_del(c_oil, name); + /* return success (skipped) */ + return 0; + } + + return pim_mroute_add(c_oil, name); +} + +/* IIF associated with SGrpt entries are re-evaluated when the parent + * (*,G) entries IIF changes + */ +static void pim_upstream_all_sources_iif_update(struct pim_upstream *up) +{ + struct listnode *listnode; + struct pim_upstream *child; + + for (ALL_LIST_ELEMENTS_RO(up->sources, listnode, + child)) { + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) + pim_upstream_mroute_iif_update(child->channel_oil, + __func__); + } +} + +/* In the case of "PIM state machine" added mroutes an upstream entry + * must be present to decide on the SPT-forwarding vs. RPT-forwarding. + */ +int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name) +{ + vifi_t iif; + + iif = pim_upstream_get_mroute_iif(c_oil, name); + + if (c_oil->oil.mfcc_parent != iif) { + c_oil->oil.mfcc_parent = iif; + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY && + c_oil->up) + pim_upstream_all_sources_iif_update(c_oil->up); + } else { + c_oil->oil.mfcc_parent = iif; + } + + return pim_upstream_mroute_update(c_oil, name); +} + +/* Look for IIF changes and update the dateplane entry only if the IIF + * has changed. + */ +int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name) +{ + vifi_t iif; + char buf[1000]; + + iif = pim_upstream_get_mroute_iif(c_oil, name); + if (c_oil->oil.mfcc_parent == iif) { + /* no change */ + return 0; + } + c_oil->oil.mfcc_parent = iif; + + if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY && + c_oil->up) + pim_upstream_all_sources_iif_update(c_oil->up); + + if (PIM_DEBUG_MROUTE_DETAIL) + zlog_debug("%s(%s) %s mroute iif update %d", + __func__, name, + pim_channel_oil_dump(c_oil, buf, + sizeof(buf)), iif); + /* XXX: is this hack needed? */ + c_oil->oil_inherited_rescan = 1; + return pim_upstream_mroute_update(c_oil, name); +} + +int pim_static_mroute_add(struct channel_oil *c_oil, const char *name) +{ + return pim_mroute_add(c_oil, name); +} + +void pim_static_mroute_iif_update(struct channel_oil *c_oil, + int input_vif_index, + const char *name) +{ + if (c_oil->oil.mfcc_parent == input_vif_index) + return; + + c_oil->oil.mfcc_parent = input_vif_index; + if (input_vif_index == MAXVIFS) + pim_mroute_del(c_oil, name); + else + pim_static_mroute_add(c_oil, name); +} + int pim_mroute_del(struct channel_oil *c_oil, const char *name) { struct pim_instance *pim = c_oil->pim; diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h index bd71acbf82..2d8e1b01fb 100644 --- a/pimd/pim_mroute.h +++ b/pimd/pim_mroute.h @@ -174,8 +174,15 @@ int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags); int pim_mroute_del_vif(struct interface *ifp); -int pim_mroute_add(struct channel_oil *c_oil, const char *name); +int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name); +int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name); +int pim_static_mroute_add(struct channel_oil *c_oil, const char *name); +void pim_static_mroute_iif_update(struct channel_oil *c_oil, + int input_vif_index, + const char *name); int pim_mroute_del(struct channel_oil *c_oil, const char *name); void pim_mroute_update_counters(struct channel_oil *c_oil); +bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil, + int oif_index); #endif /* PIM_MROUTE_H */ diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 74a3a9836b..58ebc6ce67 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -126,7 +126,12 @@ static void pim_msdp_sa_upstream_del(struct pim_msdp_sa *sa) if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) { PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags); sa->flags |= PIM_MSDP_SAF_UP_DEL_IN_PROG; - pim_upstream_del(sa->pim, up, __PRETTY_FUNCTION__); + up = pim_upstream_del(sa->pim, up, __PRETTY_FUNCTION__); + /* re-eval joinDesired; clearing peer-msdp-sa flag can + * cause JD to change + */ + if (up) + pim_upstream_update_join_desired(sa->pim, up); sa->flags &= ~PIM_MSDP_SAF_UP_DEL_IN_PROG; } @@ -445,10 +450,9 @@ static bool pim_msdp_sa_local_add_ok(struct pim_upstream *up) return false; } - if (!up->t_ka_timer) { + if (!pim_upstream_is_kat_running(up)) /* stream is not active */ return false; - } if (!I_am_RP(pim, up->sg.grp)) { /* we are not RP for the group */ @@ -561,11 +565,9 @@ void pim_msdp_sa_local_update(struct pim_upstream *up) static void pim_msdp_sa_local_setup(struct pim_instance *pim) { struct pim_upstream *up; - struct listnode *up_node; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, up_node, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) pim_msdp_sa_local_update(up); - } } /* whenever the RP changes we need to re-evaluate the "local" SA-cache */ diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 2e467502b1..fba76d84dd 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -126,8 +126,17 @@ size_t pim_msg_get_jp_group_size(struct list *sources) __PRETTY_FUNCTION__, up->sg_str); for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) { - if (child->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) { - if (!pim_rpf_is_same(&up->rpf, &child->rpf)) { + if (!PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) { + /* If we are using SPT and the SPT and RPT IIFs + * are different we can prune the source off + * of the RPT. + * If RPF_interface(S) is not resolved hold + * decision to prune as SPT may end up on the + * same IIF as RPF_interface(RP). + */ + if (child->rpf.source_nexthop.interface && + !pim_rpf_is_same(&up->rpf, + &child->rpf)) { size += sizeof( struct pim_encoded_source_ipv4); PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( @@ -143,37 +152,25 @@ size_t pim_msg_get_jp_group_size(struct list *sources) "%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)", __PRETTY_FUNCTION__, up->sg_str, child->sg_str); - } else if (pim_upstream_is_sg_rpt(child)) { - if (pim_upstream_empty_inherited_olist(child)) { - size += sizeof( + } else if (pim_upstream_empty_inherited_olist(child)) { + /* S is supposed to be forwarded along the RPT + * but it's inherited OIL is empty. So just + * prune it off. + */ + size += sizeof( struct pim_encoded_source_ipv4); - PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( + PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( child->flags); - if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( + if (PIM_DEBUG_PIM_PACKETS) + zlog_debug( "%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message", __PRETTY_FUNCTION__, child->sg_str); - } else if (!pim_rpf_is_same(&up->rpf, - &child->rpf)) { - size += sizeof( - struct pim_encoded_source_ipv4); - PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE( - child->flags); - if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: RPF'(%s) != RPF'(%s,rpt), Add Prune to compound message", - __PRETTY_FUNCTION__, - up->sg_str, - child->sg_str); - } else if (PIM_DEBUG_PIM_PACKETS) - zlog_debug( - "%s: RPF'(%s) == RPF'(%s,rpt), Do not add Prune to compound message", - __PRETTY_FUNCTION__, up->sg_str, - child->sg_str); } else if (PIM_DEBUG_PIM_PACKETS) - zlog_debug("%s: SPT bit is not set for (%s)", - __PRETTY_FUNCTION__, child->sg_str); + zlog_debug( + "%s: Do not add Prune %s to compound message %s", + __PRETTY_FUNCTION__, child->sg_str, + up->sg_str); } } return size; diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 7900e39231..5cb9492ec3 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -177,7 +177,6 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, struct pim_nexthop_cache *pnc = NULL; struct pim_nexthop_cache lookup; struct zclient *zclient = NULL; - struct listnode *upnode = NULL; struct pim_upstream *upstream = NULL; zclient = pim_zebra_zclient_get(); @@ -190,8 +189,8 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, /* Release the (*, G)upstream from pnc->upstream_hash, * whose Group belongs to the RP getting deleted */ - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, - upstream)) { + frr_each (rb_pim_upstream, &pim->upstream_head, + upstream) { struct prefix grp; struct rp_info *trp_info; @@ -441,37 +440,22 @@ static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg) { struct pim_instance *pim = (struct pim_instance *)arg; struct pim_upstream *up = (struct pim_upstream *)bucket->data; - int vif_index = 0; enum pim_rpf_result rpf_result; struct pim_rpf old; old.source_nexthop.interface = up->rpf.source_nexthop.interface; - rpf_result = pim_rpf_update(pim, up, &old); - if (rpf_result == PIM_RPF_FAILURE) { - pim_upstream_rpf_clear(pim, up); - return HASHWALK_CONTINUE; - } - - /* update kernel multicast forwarding cache (MFC) */ - if (up->rpf.source_nexthop.interface) { - ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex; + rpf_result = pim_rpf_update(pim, up, &old, __func__); - vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex); - /* Pass Current selected NH vif index to mroute download - */ - if (vif_index) - pim_scan_individual_oil(up->channel_oil, vif_index); - else { - if (PIM_DEBUG_PIM_NHT) - zlog_debug( - "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid", - __PRETTY_FUNCTION__, up->sg_str, - up->rpf.source_nexthop.interface->name); - } - } + /* update kernel multicast forwarding cache (MFC); if the + * RPF nbr is now unreachable the MFC has already been updated + * by pim_rpf_clear + */ + if (rpf_result != PIM_RPF_FAILURE) + pim_upstream_mroute_iif_update(up->channel_oil, __func__); - if (rpf_result == PIM_RPF_CHANGED) + if (rpf_result == PIM_RPF_CHANGED || + (rpf_result == PIM_RPF_FAILURE && old.source_nexthop.interface)) pim_zebra_upstream_rpf_changed(pim, up, &old); @@ -480,7 +464,8 @@ static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg) __PRETTY_FUNCTION__, up->sg_str, pim->vrf->name, old.source_nexthop.interface ? old.source_nexthop.interface->name : "Unknown", - up->rpf.source_nexthop.interface->name); + up->rpf.source_nexthop.interface + ? up->rpf.source_nexthop.interface->name : "Unknown"); } return HASHWALK_CONTINUE; diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index d142934916..598988f88f 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -31,10 +31,13 @@ #include "pim_str.h" #include "pim_iface.h" #include "pim_time.h" +#include "pim_vxlan.h" // struct list *pim_channel_oil_list = NULL; // struct hash *pim_channel_oil_hash = NULL; +static void pim_channel_update_mute(struct channel_oil *c_oil); + char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size) { char *out; @@ -61,8 +64,8 @@ char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size) return buf; } -static int pim_channel_oil_compare(struct channel_oil *c1, - struct channel_oil *c2) +int pim_channel_oil_compare(const struct channel_oil *c1, + const struct channel_oil *c2) { if (ntohl(c1->oil.mfcc_mcastgrp.s_addr) < ntohl(c2->oil.mfcc_mcastgrp.s_addr)) @@ -83,48 +86,19 @@ static int pim_channel_oil_compare(struct channel_oil *c1, return 0; } -static bool pim_oil_equal(const void *arg1, const void *arg2) -{ - const struct channel_oil *c1 = (const struct channel_oil *)arg1; - const struct channel_oil *c2 = (const struct channel_oil *)arg2; - - if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr) - && (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr)) - return true; - - return false; -} - -static unsigned int pim_oil_hash_key(const void *arg) -{ - const struct channel_oil *oil = arg; - - return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr, - oil->oil.mfcc_origin.s_addr, 0); -} - void pim_oil_init(struct pim_instance *pim) { - char hash_name[64]; - - snprintf(hash_name, 64, "PIM %s Oil Hash", pim->vrf->name); - pim->channel_oil_hash = hash_create_size(8192, pim_oil_hash_key, - pim_oil_equal, hash_name); - - pim->channel_oil_list = list_new(); - pim->channel_oil_list->del = (void (*)(void *))pim_channel_oil_free; - pim->channel_oil_list->cmp = - (int (*)(void *, void *))pim_channel_oil_compare; + rb_pim_oil_init(&pim->channel_oil_head); } void pim_oil_terminate(struct pim_instance *pim) { - if (pim->channel_oil_list) - list_delete(&pim->channel_oil_list); + struct channel_oil *c_oil; - if (pim->channel_oil_hash) - hash_free(pim->channel_oil_hash); - pim->channel_oil_hash = NULL; + while ((c_oil = rb_pim_oil_pop(&pim->channel_oil_head))) + pim_channel_oil_free(c_oil); + + rb_pim_oil_fini(&pim->channel_oil_head); } void pim_channel_oil_free(struct channel_oil *c_oil) @@ -141,71 +115,36 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, lookup.oil.mfcc_mcastgrp = sg->grp; lookup.oil.mfcc_origin = sg->src; - c_oil = hash_lookup(pim->channel_oil_hash, &lookup); + c_oil = rb_pim_oil_find(&pim->channel_oil_head, &lookup); return c_oil; } -void pim_channel_oil_change_iif(struct pim_instance *pim, - struct channel_oil *c_oil, - int input_vif_index, - const char *name) -{ - int old_vif_index = c_oil->oil.mfcc_parent; - struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp, - .grp = c_oil->oil.mfcc_origin}; - - if (c_oil->oil.mfcc_parent == input_vif_index) { - if (PIM_DEBUG_MROUTE_DETAIL) - zlog_debug("%s(%s): Existing channel oil %pSG4 already using %d as IIF", - __PRETTY_FUNCTION__, name, &sg, - input_vif_index); - - return; - } - - if (PIM_DEBUG_MROUTE_DETAIL) - zlog_debug("%s(%s): Changing channel oil %pSG4 IIF from %d to %d installed: %d", - __PRETTY_FUNCTION__, name, &sg, - c_oil->oil.mfcc_parent, input_vif_index, - c_oil->installed); - - c_oil->oil.mfcc_parent = input_vif_index; - if (c_oil->installed) { - if (input_vif_index == MAXVIFS) - pim_mroute_del(c_oil, name); - else - pim_mroute_add(c_oil, name); - } else - if (old_vif_index == MAXVIFS) - pim_mroute_add(c_oil, name); - - return; -} - struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, struct prefix_sg *sg, - int input_vif_index, const char *name) + const char *name) { struct channel_oil *c_oil; - struct interface *ifp; c_oil = pim_find_channel_oil(pim, sg); if (c_oil) { - if (c_oil->oil.mfcc_parent != input_vif_index) { - c_oil->oil_inherited_rescan = 1; - if (PIM_DEBUG_MROUTE_DETAIL) - zlog_debug( - "%s: Existing channel oil %pSG4 points to %d, modifying to point at %d", - __PRETTY_FUNCTION__, sg, - c_oil->oil.mfcc_parent, - input_vif_index); - } - pim_channel_oil_change_iif(pim, c_oil, input_vif_index, - name); ++c_oil->oil_ref_count; - /* channel might be present prior to upstream */ - c_oil->up = pim_upstream_find(pim, sg); + + if (!c_oil->up) { + /* channel might be present prior to upstream */ + c_oil->up = pim_upstream_find( + pim, sg); + /* if the upstream entry is being anchored to an + * already existing channel OIL we need to re-evaluate + * the "Mute" state on AA OIFs + */ + pim_channel_update_mute(c_oil); + } + + /* check if the IIF has changed + * XXX - is this really needed + */ + pim_upstream_mroute_iif_update(c_oil, __func__); if (PIM_DEBUG_MROUTE) zlog_debug( @@ -215,38 +154,28 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, return c_oil; } - if (input_vif_index != MAXVIFS) { - ifp = pim_if_find_by_vif_index(pim, input_vif_index); - if (!ifp) { - /* warning only */ - zlog_warn( - "%s:%s (S,G)=%pSG4 could not find input interface for input_vif_index=%d", - __PRETTY_FUNCTION__, name, sg, input_vif_index); - } - } - c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil)); c_oil->oil.mfcc_mcastgrp = sg->grp; c_oil->oil.mfcc_origin = sg->src; - c_oil = hash_get(pim->channel_oil_hash, c_oil, hash_alloc_intern); - c_oil->oil.mfcc_parent = input_vif_index; + c_oil->oil.mfcc_parent = MAXVIFS; c_oil->oil_ref_count = 1; c_oil->installed = 0; c_oil->up = pim_upstream_find(pim, sg); c_oil->pim = pim; - listnode_add_sort(pim->channel_oil_list, c_oil); + rb_pim_oil_add(&pim->channel_oil_head, c_oil); if (PIM_DEBUG_MROUTE) - zlog_debug( - "%s(%s): New oil for %pSG4 vif_index: %d Ref Count: 1 (Post Increment)", - __PRETTY_FUNCTION__, name, sg, input_vif_index); + zlog_debug("%s(%s): c_oil %s add", + __func__, name, pim_str_sg_dump(sg)); + return c_oil; } -void pim_channel_oil_del(struct channel_oil *c_oil, const char *name) +struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, + const char *name) { if (PIM_DEBUG_MROUTE) { struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp, @@ -265,15 +194,32 @@ void pim_channel_oil_del(struct channel_oil *c_oil, const char *name) * called by list_delete_all_node() */ c_oil->up = NULL; - listnode_delete(c_oil->pim->channel_oil_list, c_oil); - hash_release(c_oil->pim->channel_oil_hash, c_oil); + rb_pim_oil_del(&c_oil->pim->channel_oil_head, c_oil); pim_channel_oil_free(c_oil); + return NULL; + } + + return c_oil; +} + +void pim_channel_oil_upstream_deref(struct channel_oil *c_oil) +{ + /* The upstream entry associated with a channel_oil is abt to be + * deleted. If the channel_oil is kept around because of other + * references we need to remove upstream based states out of it. + */ + c_oil = pim_channel_oil_del(c_oil, __func__); + if (c_oil) { + /* note: here we assume that c_oil->up has already been + * cleared + */ + pim_channel_update_mute(c_oil); } } int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, - uint32_t proto_mask) + uint32_t proto_mask, const char *caller) { struct pim_interface *pim_ifp; @@ -311,7 +257,8 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask; - if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) { + if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & + PIM_OIF_FLAG_PROTO_ANY) { if (PIM_DEBUG_MROUTE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -333,8 +280,10 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, } channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0; + /* clear mute; will be re-evaluated when the OIF becomes valid again */ + channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE; - if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { + if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { if (PIM_DEBUG_MROUTE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -363,8 +312,8 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug( - "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, source_str, group_str, + "%s(%s): (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d", + __PRETTY_FUNCTION__, caller, source_str, group_str, proto_mask, channel_oil->oil.mfcc_parent, oif->name, pim_ifp->mroute_vif_index); } @@ -372,13 +321,114 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, return 0; } +void pim_channel_del_inherited_oif(struct channel_oil *c_oil, + struct interface *oif, const char *caller) +{ + struct pim_upstream *up = c_oil->up; + + pim_channel_del_oif(c_oil, oif, PIM_OIF_FLAG_PROTO_STAR, + caller); + + /* if an inherited OIF is being removed join-desired can change + * if the inherited OIL is now empty and KAT is running + */ + if (up && up->sg.src.s_addr != INADDR_ANY && + pim_upstream_empty_inherited_olist(up)) + pim_upstream_update_join_desired(up->pim, up); +} + +static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil, + struct pim_interface *pim_ifp) +{ + struct pim_interface *pim_reg_ifp; + struct pim_interface *vxlan_ifp; + bool do_mute = false; + struct pim_instance *pim = c_oil->pim; + + if (!c_oil->up) + return do_mute; + + pim_reg_ifp = pim->regiface->info; + if (pim_ifp == pim_reg_ifp) { + /* suppress pimreg in the OIL if the mroute is not supposed to + * trigger register encapsulated data + */ + if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) + do_mute = true; + + return do_mute; + } + + vxlan_ifp = pim_vxlan_get_term_ifp(pim); + if (pim_ifp == vxlan_ifp) { + /* 1. vxlan termination device must never be added to the + * origination mroute (and that can actually happen because + * of XG inheritance from the termination mroute) otherwise + * traffic will end up looping. + * PS: This check has also been extended to non-orig mroutes + * that have a local SIP as such mroutes can move back and + * forth between orig<=>non-orig type. + * 2. vxlan termination device should be removed from the non-DF + * to prevent duplicates to the overlay rxer + */ + if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) || + PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) || + pim_vxlan_is_local_sip(c_oil->up)) + do_mute = true; + + return do_mute; + } + + return do_mute; +} + +void pim_channel_update_oif_mute(struct channel_oil *c_oil, + struct pim_interface *pim_ifp) +{ + bool old_mute; + bool new_mute; + + /* If pim_ifp is not a part of the OIL there is nothing to do */ + if (!c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]) + return; + + old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] & + PIM_OIF_FLAG_MUTE); + new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp); + if (old_mute == new_mute) + return; + + if (new_mute) + c_oil->oif_flags[pim_ifp->mroute_vif_index] |= + PIM_OIF_FLAG_MUTE; + else + c_oil->oif_flags[pim_ifp->mroute_vif_index] &= + ~PIM_OIF_FLAG_MUTE; + + pim_upstream_mroute_add(c_oil, __PRETTY_FUNCTION__); +} + +/* pim_upstream has been set or cleared on the c_oil. re-eval mute state + * on all existing OIFs + */ +static void pim_channel_update_mute(struct channel_oil *c_oil) +{ + struct pim_interface *pim_reg_ifp; + struct pim_interface *vxlan_ifp; + + pim_reg_ifp = c_oil->pim->regiface->info; + if (pim_reg_ifp) + pim_channel_update_oif_mute(c_oil, pim_reg_ifp); + vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim); + if (vxlan_ifp) + pim_channel_update_oif_mute(c_oil, vxlan_ifp); +} int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, - uint32_t proto_mask) + uint32_t proto_mask, const char *caller) { struct pim_interface *pim_ifp; int old_ttl; - bool allow_iif_in_oil = false; /* * If we've gotten here we've gone bad, but let's @@ -391,48 +441,6 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, pim_ifp = oif->info; -#ifdef PIM_ENFORCE_LOOPFREE_MFC - /* - Prevent creating MFC entry with OIF=IIF. - - This is a protection against implementation mistakes. - - PIM protocol implicitely ensures loopfree multicast topology. - - IGMP must be protected against adding looped MFC entries created - by both source and receiver attached to the same interface. See - TODO T22. - We shall allow igmp to create upstream when it is DR for the intf. - Assume RP reachable via non DR. - */ - if ((channel_oil->up && - PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(channel_oil->up->flags)) || - ((proto_mask == PIM_OIF_FLAG_PROTO_IGMP) && PIM_I_am_DR(pim_ifp))) { - allow_iif_in_oil = true; - } - - if (!allow_iif_in_oil && - pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) { - channel_oil->oil_inherited_rescan = 1; - if (PIM_DEBUG_MROUTE) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<group?>", - channel_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - pim_inet4_dump("<source?>", - channel_oil->oil.mfcc_origin, source_str, - sizeof(source_str)); - zlog_debug( - "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, proto_mask, - oif->name, pim_ifp->mroute_vif_index, - source_str, group_str); - } - return -2; - } -#endif - /* Prevent single protocol from subscribing same interface to channel (S,G) multiple times */ if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) { @@ -517,11 +525,23 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL; + /* Some OIFs are held in a muted state i.e. the PIM state machine + * decided to include the OIF but additional status check such as + * MLAG DF role prevent it from being activated for traffic + * forwarding. + */ + if (pim_channel_eval_oif_mute(channel_oil, pim_ifp)) + channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= + PIM_OIF_FLAG_MUTE; + else + channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= + ~PIM_OIF_FLAG_MUTE; + /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not * valid to get installed in kernel. */ if (channel_oil->oil.mfcc_parent != MAXVIFS) { - if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { + if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) { if (PIM_DEBUG_MROUTE) { char group_str[INET_ADDRSTRLEN]; char source_str[INET_ADDRSTRLEN]; @@ -557,8 +577,8 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); zlog_debug( - "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", - __FILE__, __PRETTY_FUNCTION__, source_str, group_str, + "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", + __PRETTY_FUNCTION__, caller, source_str, group_str, proto_mask, oif->name, pim_ifp->mroute_vif_index); } @@ -567,19 +587,15 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, int pim_channel_oil_empty(struct channel_oil *c_oil) { - static uint32_t zero[MAXVIFS]; - static int inited = 0; + static struct mfcctl null_oil; if (!c_oil) return 1; - /* - * Not sure that this is necessary, but I would rather ensure - * that this works. - */ - if (!inited) { - memset(&zero, 0, sizeof(uint32_t) * MAXVIFS); - inited = 1; - } - return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t)); + /* exclude pimreg from the OIL when checking if the inherited_oil is + * non-NULL. + * pimreg device (in all vrfs) uses a vifi of + * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */ + return !memcmp(&c_oil->oil.mfcc_ttls[1], &null_oil.mfcc_ttls[1], + sizeof(null_oil.mfcc_ttls) - sizeof(null_oil.mfcc_ttls[0])); } diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index 319a1c91a3..788ddaa16c 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -21,6 +21,7 @@ #define PIM_OIL_H #include "pim_mroute.h" +#include "pim_iface.h" /* * Where did we get this (S,G) from? @@ -38,6 +39,8 @@ (PIM_OIF_FLAG_PROTO_IGMP | PIM_OIF_FLAG_PROTO_PIM \ | PIM_OIF_FLAG_PROTO_STAR | PIM_OIF_FLAG_PROTO_VXLAN) +/* OIF is present in the OIL but must not be used for forwarding traffic */ +#define PIM_OIF_FLAG_MUTE (1 << 4) /* * We need a pimreg vif id from the kernel. * Since ifindex == vif id for most cases and the number @@ -87,10 +90,13 @@ struct channel_counts { installed: indicate if this entry is installed in the kernel. */ +PREDECL_RBTREE_UNIQ(rb_pim_oil) struct channel_oil { struct pim_instance *pim; + struct rb_pim_oil_item oil_rb; + struct mfcctl oil; int installed; int oil_inherited_rescan; @@ -103,6 +109,12 @@ struct channel_oil { time_t mroute_creation; }; +extern int pim_channel_oil_compare(const struct channel_oil *c1, + const struct channel_oil *c2); +DECLARE_RBTREE_UNIQ(rb_pim_oil, struct channel_oil, oil_rb, + pim_channel_oil_compare) + + extern struct list *pim_channel_oil_list; void pim_oil_init(struct pim_instance *pim); @@ -113,18 +125,27 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, struct prefix_sg *sg); struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, struct prefix_sg *sg, - int input_vif_index, const char *name); + const char *name); void pim_channel_oil_change_iif(struct pim_instance *pim, struct channel_oil *c_oil, int input_vif_index, const char *name); -void pim_channel_oil_del(struct channel_oil *c_oil, const char *name); +struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, + const char *name); int pim_channel_add_oif(struct channel_oil *c_oil, struct interface *oif, - uint32_t proto_mask); + uint32_t proto_mask, const char *caller); int pim_channel_del_oif(struct channel_oil *c_oil, struct interface *oif, - uint32_t proto_mask); + uint32_t proto_mask, const char *caller); int pim_channel_oil_empty(struct channel_oil *c_oil); char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size); + +void pim_channel_update_oif_mute(struct channel_oil *c_oil, + struct pim_interface *pim_ifp); + +void pim_channel_oil_upstream_deref(struct channel_oil *c_oil); +void pim_channel_del_inherited_oif(struct channel_oil *c_oil, + struct interface *oif, const char *caller); + #endif /* PIM_OIL_H */ diff --git a/pimd/pim_register.c b/pimd/pim_register.c index aff3fbed6c..013f04bb00 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -59,7 +59,7 @@ void pim_register_join(struct pim_upstream *up) } pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); up->reg_state = PIM_REG_JOIN; pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/); } @@ -145,7 +145,7 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size) case PIM_REG_JOIN: upstream->reg_state = PIM_REG_PRUNE; pim_channel_del_oif(upstream->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); pim_upstream_start_register_stop_timer(upstream, 0); pim_vxlan_update_sg_reg_state(pim, upstream, false/*reg_join*/); @@ -452,10 +452,9 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, } if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) - || ((SwitchToSptDesired(pim_ifp->pim, &sg)) + || ((SwitchToSptDesiredOnRp(pim_ifp->pim, &sg)) && pim_upstream_inherited_olist(pim_ifp->pim, upstream) == 0)) { - // pim_scan_individual_oil (upstream->channel_oil); pim_register_stop_send(ifp, &sg, dest_addr, src_addr); sentRegisterStop = 1; } else { @@ -464,7 +463,7 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, upstream->sptbit); } if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) - || (SwitchToSptDesired(pim_ifp->pim, &sg))) { + || (SwitchToSptDesiredOnRp(pim_ifp->pim, &sg))) { if (sentRegisterStop) { pim_upstream_keep_alive_timer_start( upstream, diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 09529055e0..2db39bac4b 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -274,6 +274,7 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim, static void pim_rp_refresh_group_to_rp_mapping(struct pim_instance *pim) { pim_msdp_i_am_rp_changed(pim); + pim_upstream_reeval_use_rpt(pim); } void pim_rp_prefix_list_update(struct pim_instance *pim, @@ -381,27 +382,17 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) old_rpf.source_nexthop.interface = up->rpf.source_nexthop.interface; - rpf_result = pim_rpf_update(pim, up, &old_rpf); + rpf_result = pim_rpf_update(pim, up, &old_rpf, __func__); if (rpf_result == PIM_RPF_FAILURE) pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__); /* update kernel multicast forwarding cache (MFC) */ - if (up->rpf.source_nexthop.interface && up->channel_oil) { - ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex; - int vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex); - /* Pass Current selected NH vif index to mroute download */ - if (vif_index) - pim_scan_individual_oil(up->channel_oil, vif_index); - else { - if (PIM_DEBUG_PIM_NHT) - zlog_debug( - "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid", - __PRETTY_FUNCTION__, up->sg_str, - up->rpf.source_nexthop.interface->name); - } - } + if (up->rpf.source_nexthop.interface && up->channel_oil) + pim_upstream_mroute_iif_update(up->channel_oil, __func__); - if (rpf_result == PIM_RPF_CHANGED) + if (rpf_result == PIM_RPF_CHANGED || + (rpf_result == PIM_RPF_FAILURE && + old_rpf.source_nexthop.interface)) pim_zebra_upstream_rpf_changed(pim, up, &old_rpf); pim_zebra_update_all_interfaces(pim); @@ -455,7 +446,6 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix nht_p; struct route_node *rn; struct pim_upstream *up; - struct listnode *upnode; if (rp_addr.s_addr == INADDR_ANY || rp_addr.s_addr == INADDR_NONE) @@ -563,8 +553,7 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, __PRETTY_FUNCTION__, buf, buf1); } - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, - up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { /* Find (*, G) upstream whose RP is not * configured yet */ @@ -659,7 +648,7 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, rn->lock); } - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (up->sg.src.s_addr == INADDR_ANY) { struct prefix grp; struct rp_info *trp_info; @@ -732,7 +721,6 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, bool was_plist = false; struct rp_info *trp_info; struct pim_upstream *up; - struct listnode *upnode; struct bsgrp_node *bsgrp = NULL; struct bsm_rpinfo *bsrp = NULL; char grp_str[PREFIX2STR_BUFFER]; @@ -809,7 +797,7 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, rp_all = pim_rp_find_match_group(pim, &g_all); if (rp_all == rp_info) { - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { /* Find the upstream (*, G) whose upstream address is * same as the deleted RP */ @@ -861,7 +849,7 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, pim_rp_refresh_group_to_rp_mapping(pim); - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { /* Find the upstream (*, G) whose upstream address is same as * the deleted RP */ @@ -902,7 +890,6 @@ int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, int result = 0; struct rp_info *rp_info = NULL; struct pim_upstream *up; - struct listnode *upnode; rn = route_node_lookup(pim->rp_table, &group); if (!rn) { @@ -951,7 +938,7 @@ int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, listnode_add_sort(pim->rp_list, rp_info); - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (up->sg.src.s_addr == INADDR_ANY) { struct prefix grp; struct rp_info *trp_info; @@ -1057,6 +1044,7 @@ void pim_rp_check_on_if_add(struct pim_interface *pim_ifp) if (i_am_rp_changed) { pim_msdp_i_am_rp_changed(pim); + pim_upstream_reeval_use_rpt(pim); } } @@ -1099,6 +1087,7 @@ void pim_i_am_rp_re_evaluate(struct pim_instance *pim) if (i_am_rp_changed) { pim_msdp_i_am_rp_changed(pim); + pim_upstream_reeval_use_rpt(pim); } } diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index d383ef5249..1eb5006b94 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -195,7 +195,8 @@ static int nexthop_mismatch(const struct pim_nexthop *nh1, } enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, - struct pim_upstream *up, struct pim_rpf *old) + struct pim_upstream *up, struct pim_rpf *old, + const char *caller) { struct pim_rpf *rpf = &up->rpf; struct pim_rpf saved; @@ -207,13 +208,17 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, return PIM_RPF_OK; if (up->upstream_addr.s_addr == INADDR_ANY) { - zlog_debug("%s: RP is not configured yet for %s", - __PRETTY_FUNCTION__, up->sg_str); + zlog_debug("%s(%s): RP is not configured yet for %s", + __func__, caller, up->sg_str); return PIM_RPF_OK; } saved.source_nexthop = rpf->source_nexthop; saved.rpf_addr = rpf->rpf_addr; + if (old) { + old->source_nexthop = saved.source_nexthop; + old->rpf_addr = saved.rpf_addr; + } nht_p.family = AF_INET; nht_p.prefixlen = IPV4_MAX_BITLEN; @@ -238,8 +243,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, rpf->rpf_addr.u.prefix4 = pim_rpf_find_rpf_addr(up); if (pim_rpf_addr_is_inaddr_any(rpf) && PIM_DEBUG_ZEBRA) { /* RPF'(S,G) not found */ - zlog_debug("%s %s: RPF'%s not found: won't send join upstream", - __FILE__, __PRETTY_FUNCTION__, up->sg_str); + zlog_debug("%s(%s): RPF'%s not found: won't send join upstream", + __func__, caller, up->sg_str); /* warning only */ } @@ -251,8 +256,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, pim_addr_dump("<addr?>", &rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str)); - zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d", - __FILE__, __PRETTY_FUNCTION__, + zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d", + __func__, caller, up->sg_str, rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>", nhaddr_str, @@ -269,8 +274,8 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, if (saved.source_nexthop.interface != rpf->source_nexthop.interface) { if (PIM_DEBUG_ZEBRA) { - zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s", - __FILE__, __PRETTY_FUNCTION__, + zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s", + __func__, caller, up->sg_str, saved.source_nexthop.interface ? saved.source_nexthop.interface->name : "<oldif?>", rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>"); @@ -286,11 +291,6 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, || saved.source_nexthop .interface != rpf->source_nexthop.interface) { - /* return old rpf to caller ? */ - if (old) { - old->source_nexthop = saved.source_nexthop; - old->rpf_addr = saved.rpf_addr; - } return PIM_RPF_CHANGED; } @@ -307,12 +307,6 @@ void pim_upstream_rpf_clear(struct pim_instance *pim, struct pim_upstream *up) { if (up->rpf.source_nexthop.interface) { - if (up->channel_oil) - pim_channel_oil_change_iif(pim, up->channel_oil, - MAXVIFS, - __PRETTY_FUNCTION__); - - pim_upstream_switch(pim, up, PIM_UPSTREAM_NOTJOINED); up->rpf.source_nexthop.interface = NULL; up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY; @@ -321,6 +315,7 @@ void pim_upstream_rpf_clear(struct pim_instance *pim, up->rpf.source_nexthop.mrib_route_metric = router->infinite_assert_metric.route_metric; up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY; + pim_upstream_mroute_iif_update(up->channel_oil, __func__); } } diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h index df7b249289..f006519b71 100644 --- a/pimd/pim_rpf.h +++ b/pimd/pim_rpf.h @@ -63,7 +63,7 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, struct in_addr addr, int neighbor_needed); enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, struct pim_upstream *up, - struct pim_rpf *old); + struct pim_rpf *old, const char *caller); void pim_upstream_rpf_clear(struct pim_instance *pim, struct pim_upstream *up); int pim_rpf_addr_is_inaddr_none(struct pim_rpf *rpf); diff --git a/pimd/pim_static.c b/pimd/pim_static.c index e3138360c8..7b121c9136 100644 --- a/pimd/pim_static.c +++ b/pimd/pim_static.c @@ -138,7 +138,7 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, } else { /* input interface changed */ s_route->iif = iif_index; - pim_channel_oil_change_iif(pim, &s_route->c_oil, + pim_static_mroute_iif_update(&s_route->c_oil, iif_index, __PRETTY_FUNCTION__); @@ -179,7 +179,7 @@ int pim_static_add(struct pim_instance *pim, struct interface *iif, s_route->c_oil.pim = pim; - if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { + if (pim_static_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { char gifaddr_str[INET_ADDRSTRLEN]; char sifaddr_str[INET_ADDRSTRLEN]; pim_inet4_dump("<ifaddr?>", group, gifaddr_str, @@ -264,7 +264,7 @@ int pim_static_del(struct pim_instance *pim, struct interface *iif, if (s_route->c_oil.oil_ref_count <= 0 ? pim_mroute_del(&s_route->c_oil, __PRETTY_FUNCTION__) - : pim_mroute_add(&s_route->c_oil, + : pim_static_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { char gifaddr_str[INET_ADDRSTRLEN]; char sifaddr_str[INET_ADDRSTRLEN]; diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index 40b7e3d236..afd10bd3db 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -78,8 +78,13 @@ static void pim_upstream_remove_children(struct pim_instance *pim, child = pim_upstream_del(pim, child, __PRETTY_FUNCTION__); } - if (child) + if (child) { child->parent = NULL; + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) + pim_upstream_mroute_iif_update( + child->channel_oil, + __func__); + } } list_delete(&up->sources); } @@ -93,7 +98,6 @@ static void pim_upstream_find_new_children(struct pim_instance *pim, struct pim_upstream *up) { struct pim_upstream *child; - struct listnode *ch_node; if ((up->sg.src.s_addr != INADDR_ANY) && (up->sg.grp.s_addr != INADDR_ANY)) @@ -103,12 +107,16 @@ static void pim_upstream_find_new_children(struct pim_instance *pim, && (up->sg.grp.s_addr == INADDR_ANY)) return; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, ch_node, child)) { + frr_each (rb_pim_upstream, &pim->upstream_head, child) { if ((up->sg.grp.s_addr != INADDR_ANY) && (child->sg.grp.s_addr == up->sg.grp.s_addr) && (child != up)) { child->parent = up; listnode_add_sort(up->sources, child); + if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) + pim_upstream_mroute_iif_update( + child->channel_oil, + __func__); } } } @@ -141,14 +149,22 @@ static struct pim_upstream *pim_upstream_find_parent(struct pim_instance *pim, static void upstream_channel_oil_detach(struct pim_upstream *up) { - if (up->channel_oil) { + struct channel_oil *channel_oil = up->channel_oil; + + if (channel_oil) { /* Detaching from channel_oil, channel_oil may exist post del, but upstream would not keep reference of it */ - up->channel_oil->up = NULL; - pim_channel_oil_del(up->channel_oil, __PRETTY_FUNCTION__); + channel_oil->up = NULL; up->channel_oil = NULL; + + /* attempt to delete channel_oil; if channel_oil is being held + * because of other references cleanup info such as "Mute" + * inferred from the parent upstream + */ + pim_channel_oil_upstream_deref(channel_oil); } + } struct pim_upstream *pim_upstream_del(struct pim_instance *pim, @@ -173,6 +189,10 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, if (up->ref_count >= 1) return up; + if (PIM_DEBUG_TRACE) + zlog_debug("pim_upstream free vrf:%s %s flags 0x%x", + pim->vrf->name, up->sg_str, up->flags); + THREAD_OFF(up->t_ka_timer); THREAD_OFF(up->t_rs_timer); THREAD_OFF(up->t_msdp_reg_timer); @@ -213,8 +233,7 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, listnode_delete(up->parent->sources, up); up->parent = NULL; - listnode_delete(pim->upstream_list, up); - hash_release(pim->upstream_hash, up); + rb_pim_upstream_del(&pim->upstream_head, up); if (notify_msdp) { pim_msdp_up_del(pim, &up->sg); @@ -316,7 +335,7 @@ static void join_timer_stop(struct pim_upstream *up) up->rpf.rpf_addr.u.prefix4); if (nbr) - pim_jp_agg_remove_group(nbr->upstream_jp_agg, up); + pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr); pim_jp_agg_upstream_verification(up, false); } @@ -338,7 +357,7 @@ void join_timer_start(struct pim_upstream *up) } if (nbr) - pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1); + pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1, nbr); else { THREAD_OFF(up->t_join_timer); thread_add_timer(router->master, on_join_timer, up, @@ -511,15 +530,15 @@ static int pim_upstream_could_register(struct pim_upstream *up) * we re-revaluate register setup for existing upstream entries */ void pim_upstream_register_reevaluate(struct pim_instance *pim) { - struct listnode *upnode; struct pim_upstream *up; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { /* If FHR is set CouldRegister is True. Also check if the flow * is actually active; if it is not kat setup will trigger * source * registration whenever the flow becomes active. */ - if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || !up->t_ka_timer) + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || + !pim_upstream_is_kat_running(up)) continue; if (pim_is_grp_ssm(pim, up->sg.grp)) { @@ -532,7 +551,8 @@ void pim_upstream_register_reevaluate(struct pim_instance *pim) /* remove regiface from the OIL if it is there*/ pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, + __func__); up->reg_state = PIM_REG_NOINFO; } } else { @@ -544,13 +564,86 @@ void pim_upstream_register_reevaluate(struct pim_instance *pim) up->sg_str); pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, + __func__); up->reg_state = PIM_REG_JOIN; } } } } +/* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should + * forward a S - + * 1. along the SPT if SPTbit is set + * 2. and along the RPT if SPTbit is not set + * If forwarding is hw accelerated i.e. control and dataplane components + * are separate you may not be able to reliably set SPT bit on intermediate + * routers while still fowarding on the (S,G,rpt). + * + * This macro is a slight deviation on the RFC and uses "traffic-agnostic" + * criteria to decide between using the RPT vs. SPT for forwarding. + */ +void pim_upstream_update_use_rpt(struct pim_upstream *up, + bool update_mroute) +{ + bool old_use_rpt; + bool new_use_rpt; + + if (up->sg.src.s_addr == INADDR_ANY) + return; + + old_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + + /* We will use the SPT (IIF=RPF_interface(S) if - + * 1. We have decided to join the SPT + * 2. We are FHR + * 3. Source is directly connected + * 4. We are RP (parent's IIF is lo or vrf-device) + * In all other cases the source will stay along the RPT and + * IIF=RPF_interface(RP). + */ + if (up->join_state == PIM_UPSTREAM_JOINED || + PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || + pim_if_connected_to_source( + up->rpf.source_nexthop.interface, + up->sg.src) || + /* XXX - need to switch this to a more efficient + * lookup API + */ + I_am_RP(up->pim, up->sg.grp)) + /* use SPT */ + PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up->flags); + else + /* use RPT */ + PIM_UPSTREAM_FLAG_SET_USE_RPT(up->flags); + + new_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + if (old_use_rpt != new_use_rpt) { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug("%s switched from %s to %s", + up->sg_str, + old_use_rpt?"RPT":"SPT", + new_use_rpt?"RPT":"SPT"); + if (update_mroute) + pim_upstream_mroute_add(up->channel_oil, __func__); + } +} + +/* some events like RP change require re-evaluation of SGrpt across + * all groups + */ +void pim_upstream_reeval_use_rpt(struct pim_instance *pim) +{ + struct pim_upstream *up; + + frr_each (rb_pim_upstream, &pim->upstream_head, up) { + if (up->sg.src.s_addr == INADDR_ANY) + continue; + + pim_upstream_update_use_rpt(up, true /*update_mroute*/); + } +} + void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, enum pim_upstream_state new_state) { @@ -603,21 +696,47 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, join_timer_start(up); } } + if (old_state != new_state) + pim_upstream_update_use_rpt(up, true /*update_mroute*/); } else { + bool old_use_rpt; + bool new_use_rpt; + bool send_xg_jp = false; forward_off(up); if (old_state == PIM_UPSTREAM_JOINED) pim_msdp_up_join_state_changed(pim, up); + if (old_state != new_state) { + old_use_rpt = + !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + pim_upstream_update_use_rpt(up, true /*update_mroute*/); + new_use_rpt = + !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags); + if (new_use_rpt && + (new_use_rpt != old_use_rpt) && + up->parent) + /* we have decided to switch from the SPT back + * to the RPT which means we need to cancel + * any previously sent SGrpt prunes immediately + */ + send_xg_jp = true; + } + /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards RP. If I am RP for G then send S,G prune to its IIF. */ - if (pim_upstream_is_sg_rpt(up) && up->parent - && !I_am_RP(pim, up->sg.grp)) { + if (pim_upstream_is_sg_rpt(up) && up->parent && + !I_am_RP(pim, up->sg.grp)) + send_xg_jp = true; + else + pim_jp_agg_single_upstream_send(&up->rpf, up, + 0 /* prune */); + + if (send_xg_jp) { if (PIM_DEBUG_PIM_TRACE_DETAIL) zlog_debug( - "%s: *,G IIF %s S,G IIF %s ", - __PRETTY_FUNCTION__, + "re-join RPT; *,G IIF %s S,G IIF %s ", up->parent->rpf.source_nexthop.interface ? up->parent->rpf.source_nexthop.interface->name : "Unknown", @@ -627,18 +746,14 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up, pim_jp_agg_single_upstream_send(&up->parent->rpf, up->parent, 1 /* (W,G) Join */); - } else - pim_jp_agg_single_upstream_send(&up->rpf, up, - 0 /* prune */); + } join_timer_stop(up); } } -int pim_upstream_compare(void *arg1, void *arg2) +int pim_upstream_compare(const struct pim_upstream *up1, + const struct pim_upstream *up2) { - const struct pim_upstream *up1 = (const struct pim_upstream *)arg1; - const struct pim_upstream *up2 = (const struct pim_upstream *)arg2; - if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr)) return -1; @@ -683,12 +798,13 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up)); + up->pim = pim; up->sg = *sg; pim_str_sg_set(sg, up->sg_str); if (ch) ch->upstream = up; - up = hash_get(pim->upstream_hash, up, hash_alloc_intern); + rb_pim_upstream_add(&pim->upstream_head, up); /* Set up->upstream_addr as INADDR_ANY, if RP is not * configured and retain the upstream data structure */ @@ -702,7 +818,8 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, up->parent = pim_upstream_find_parent(pim, up); if (up->sg.src.s_addr == INADDR_ANY) { up->sources = list_new(); - up->sources->cmp = pim_upstream_compare; + up->sources->cmp = + (int (*)(void *, void *))pim_upstream_compare; } else up->sources = NULL; @@ -717,7 +834,7 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, up->reg_state = PIM_REG_NOINFO; up->state_transition = pim_time_monotonic_sec(); up->channel_oil = - pim_channel_oil_add(pim, &up->sg, MAXVIFS, __PRETTY_FUNCTION__); + pim_channel_oil_add(pim, &up->sg, __PRETTY_FUNCTION__); up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE; up->rpf.source_nexthop.interface = NULL; @@ -742,15 +859,17 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, pim_upstream_fill_static_iif(up, incoming); pim_ifp = up->rpf.source_nexthop.interface->info; assert(pim_ifp); - pim_channel_oil_change_iif(pim, up->channel_oil, - pim_ifp->mroute_vif_index, - __PRETTY_FUNCTION__); + pim_upstream_update_use_rpt(up, + false /*update_mroute*/); + pim_upstream_mroute_iif_update(up->channel_oil, __func__); if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags)) pim_upstream_keep_alive_timer_start( up, pim->keep_alive_time); } else if (up->upstream_addr.s_addr != INADDR_ANY) { - rpf_result = pim_rpf_update(pim, up, NULL); + pim_upstream_update_use_rpt(up, + false /*update_mroute*/); + rpf_result = pim_rpf_update(pim, up, NULL, __func__); if (rpf_result == PIM_RPF_FAILURE) { if (PIM_DEBUG_PIM_TRACE) zlog_debug( @@ -759,17 +878,11 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, } if (up->rpf.source_nexthop.interface) { - pim_ifp = up->rpf.source_nexthop.interface->info; - if (pim_ifp) - pim_channel_oil_change_iif( - pim, up->channel_oil, - pim_ifp->mroute_vif_index, - __PRETTY_FUNCTION__); + pim_upstream_mroute_iif_update(up->channel_oil, + __func__); } } - listnode_add_sort(pim->upstream_list, up); - if (PIM_DEBUG_PIM_TRACE) { zlog_debug( "%s: Created Upstream %s upstream_addr %s ref count %d increment", @@ -787,40 +900,40 @@ struct pim_upstream *pim_upstream_find(struct pim_instance *pim, struct pim_upstream *up = NULL; lookup.sg = *sg; - up = hash_lookup(pim->upstream_hash, &lookup); + up = rb_pim_upstream_find(&pim->upstream_head, &lookup); return up; } struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg, - struct interface *incoming, - int flags, const char *name) + struct interface *incoming, + int flags, const char *name) { - struct pim_upstream *up; - struct pim_interface *pim_ifp; - - pim_ifp = incoming->info; - - up = pim_upstream_find(pim_ifp->pim, sg); - - if (up) { - if (!(up->flags & flags)) { - up->flags |= flags; - up->ref_count++; - if (PIM_DEBUG_PIM_TRACE) - zlog_debug( - "%s(%s): upstream %s ref count %d increment", - __PRETTY_FUNCTION__, name, up->sg_str, - up->ref_count); - } - } else - up = pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name, - NULL); + struct pim_interface *pim_ifp = incoming->info; - return up; + return (pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name, + NULL)); } void pim_upstream_ref(struct pim_upstream *up, int flags, const char *name) { + /* when we go from non-FHR to FHR we need to re-eval traffic + * forwarding path + */ + if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) && + PIM_UPSTREAM_FLAG_TEST_FHR(flags)) { + PIM_UPSTREAM_FLAG_SET_FHR(up->flags); + pim_upstream_update_use_rpt(up, true /*update_mroute*/); + } + + /* re-eval joinDesired; clearing peer-msdp-sa flag can + * cause JD to change + */ + if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags) && + PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags)) { + PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up->flags); + pim_upstream_update_join_desired(up->pim, up); + } + up->flags |= flags; ++up->ref_count; if (PIM_DEBUG_PIM_TRACE) @@ -867,6 +980,36 @@ struct pim_upstream *pim_upstream_add(struct pim_instance *pim, /* * Passed in up must be the upstream for ch. starch is NULL if no * information + * This function is copied over from + * pim_upstream_evaluate_join_desired_interface but limited to + * parent (*,G)'s includes/joins. + */ +int pim_upstream_eval_inherit_if(struct pim_upstream *up, + struct pim_ifchannel *ch, + struct pim_ifchannel *starch) +{ + /* if there is an explicit prune for this interface we cannot + * add it to the OIL + */ + if (ch) { + if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) + return 0; + } + + /* Check if the OIF can be inherited fron the (*,G) entry + */ + if (starch) { + if (!pim_macro_ch_lost_assert(starch) + && pim_macro_chisin_joins_or_include(starch)) + return 1; + } + + return 0; +} + +/* + * Passed in up must be the upstream for ch. starch is NULL if no + * information */ int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, struct pim_ifchannel *ch, @@ -885,8 +1028,14 @@ int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, * joins (*,G) */ if (starch) { + /* XXX: check on this with donald + * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in + * upstream flags? + */ +#if 0 if (PIM_IF_FLAG_TEST_S_G_RPT(starch->upstream->flags)) return 0; +#endif if (!pim_macro_ch_lost_assert(starch) && pim_macro_chisin_joins_or_include(starch)) @@ -896,56 +1045,77 @@ int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, return 0; } -/* - Evaluate JoinDesired(S,G): - - JoinDesired(S,G) is true if there is a downstream (S,G) interface I - in the set: - - inherited_olist(S,G) = - joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) - - JoinDesired(S,G) may be affected by changes in the following: - - pim_ifp->primary_address - pim_ifp->pim_dr_addr - ch->ifassert_winner_metric - ch->ifassert_winner - ch->local_ifmembership - ch->ifjoin_state - ch->upstream->rpf.source_nexthop.mrib_metric_preference - ch->upstream->rpf.source_nexthop.mrib_route_metric - ch->upstream->rpf.source_nexthop.interface - - See also pim_upstream_update_join_desired() below. +/* Returns true if immediate OIL is empty and is used to evaluate + * JoinDesired. See pim_upstream_evaluate_join_desired. */ -int pim_upstream_evaluate_join_desired(struct pim_instance *pim, +static bool pim_upstream_empty_immediate_olist(struct pim_instance *pim, struct pim_upstream *up) { struct interface *ifp; - struct pim_ifchannel *ch, *starch; - struct pim_upstream *starup = up->parent; - int ret = 0; + struct pim_ifchannel *ch; FOR_ALL_INTERFACES (pim->vrf, ifp) { if (!ifp->info) continue; ch = pim_ifchannel_find(ifp, &up->sg); - - if (starup) - starch = pim_ifchannel_find(ifp, &starup->sg); - else - starch = NULL; - - if (!ch && !starch) + if (!ch) continue; - ret += pim_upstream_evaluate_join_desired_interface(up, ch, - starch); + /* If we have even one immediate OIF we can return with + * not-empty + */ + if (pim_upstream_evaluate_join_desired_interface(up, ch, + NULL /* starch */)) + return false; } /* scan iface channel list */ - return ret; /* false */ + /* immediate_oil is empty */ + return true; +} + + +static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream *up) +{ + return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags); +} + +/* + * bool JoinDesired(*,G) { + * if (immediate_olist(*,G) != NULL) + * return TRUE + * else + * return FALSE + * } + * + * bool JoinDesired(S,G) { + * return( immediate_olist(S,G) != NULL + * OR ( KeepaliveTimer(S,G) is running + * AND inherited_olist(S,G) != NULL ) ) + * } + */ +int pim_upstream_evaluate_join_desired(struct pim_instance *pim, + struct pim_upstream *up) +{ + bool empty_imm_oil; + bool empty_inh_oil; + + empty_imm_oil = pim_upstream_empty_immediate_olist(pim, up); + + /* (*,G) */ + if (up->sg.src.s_addr == INADDR_ANY) + return !empty_imm_oil; + + /* (S,G) */ + if (!empty_imm_oil) + return true; + empty_inh_oil = pim_upstream_empty_inherited_olist(up); + if (!empty_inh_oil && + (pim_upstream_is_kat_running(up) || + pim_upstream_is_msdp_peer_sa(up))) + return true; + + return false; } /* @@ -990,15 +1160,12 @@ void pim_upstream_update_join_desired(struct pim_instance *pim, void pim_upstream_rpf_genid_changed(struct pim_instance *pim, struct in_addr neigh_addr) { - struct listnode *up_node; - struct listnode *up_nextnode; struct pim_upstream *up; /* * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr */ - for (ALL_LIST_ELEMENTS(pim->upstream_list, up_node, up_nextnode, up)) { - + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (PIM_DEBUG_PIM_TRACE) { char neigh_str[INET_ADDRSTRLEN]; char rpf_addr_str[PREFIX_STRLEN]; @@ -1116,7 +1283,7 @@ static void pim_upstream_fhr_kat_expiry(struct pim_instance *pim, THREAD_OFF(up->t_rs_timer); /* remove regiface from the OIL if it is there*/ pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); /* clear the register state */ up->reg_state = PIM_REG_NOINFO; PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags); @@ -1136,6 +1303,7 @@ static void pim_upstream_fhr_kat_start(struct pim_upstream *up) PIM_UPSTREAM_FLAG_SET_FHR(up->flags); if (up->reg_state == PIM_REG_NOINFO) pim_register_join(up); + pim_upstream_update_use_rpt(up, true /*update_mroute*/); } } @@ -1171,6 +1339,9 @@ struct pim_upstream *pim_upstream_keep_alive_timer_proc( /* source is no longer active - pull the SA from MSDP's cache */ pim_msdp_sa_local_del(pim, &up->sg); + /* JoinDesired can change when KAT is started or stopped */ + pim_upstream_update_join_desired(pim, up); + /* if entry was created because of activity we need to deref it */ if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { pim_upstream_fhr_kat_expiry(pim, up); @@ -1233,6 +1404,8 @@ void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time) /* any time keepalive is started against a SG we will have to * re-evaluate our active source database */ pim_msdp_sa_local_update(up); + /* JoinDesired can change when KAT is started or stopped */ + pim_upstream_update_join_desired(up->pim, up); } /* MSDP on RP needs to know if a source is registerable to this RP */ @@ -1282,7 +1455,7 @@ void pim_upstream_msdp_reg_timer_start(struct pim_upstream *up) * SwitchToSptDesired(S,G) return true once a single packet has been * received for the source and group. */ -int pim_upstream_switch_to_spt_desired(struct pim_instance *pim, +int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim, struct prefix_sg *sg) { if (I_am_RP(pim, sg->grp)) @@ -1445,7 +1618,8 @@ static int pim_upstream_register_stop_timer(struct thread *t) case PIM_REG_JOIN_PENDING: up->reg_state = PIM_REG_JOIN; pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, + __func__); pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/); break; case PIM_REG_JOIN: @@ -1546,7 +1720,8 @@ int pim_upstream_inherited_olist_decide(struct pim_instance *pim, if (!ch) flag = PIM_OIF_FLAG_PROTO_STAR; - pim_channel_add_oif(up->channel_oil, ifp, flag); + pim_channel_add_oif(up->channel_oil, ifp, flag, + __func__); output_intf++; } } @@ -1581,9 +1756,9 @@ int pim_upstream_inherited_olist(struct pim_instance *pim, * switch on a stick so turn on forwarding to just accept the * incoming packets so we don't bother the other stuff! */ - if (output_intf) - pim_upstream_switch(pim, up, PIM_UPSTREAM_JOINED); - else + pim_upstream_update_join_desired(pim, up); + + if (!output_intf) forward_on(up); return output_intf; @@ -1602,14 +1777,14 @@ int pim_upstream_empty_inherited_olist(struct pim_upstream *up) */ void pim_upstream_find_new_rpf(struct pim_instance *pim) { - struct listnode *up_node; - struct listnode *up_nextnode; struct pim_upstream *up; + struct pim_rpf old; + enum pim_rpf_result rpf_result; /* * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr */ - for (ALL_LIST_ELEMENTS(pim->upstream_list, up_node, up_nextnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (up->upstream_addr.s_addr == INADDR_ANY) { if (PIM_DEBUG_PIM_TRACE) zlog_debug( @@ -1623,9 +1798,19 @@ void pim_upstream_find_new_rpf(struct pim_instance *pim) zlog_debug( "%s: Upstream %s without a path to send join, checking", __PRETTY_FUNCTION__, up->sg_str); - pim_rpf_update(pim, up, NULL); + old.source_nexthop.interface = + up->rpf.source_nexthop.interface; + rpf_result = pim_rpf_update(pim, up, &old, __func__); + if (rpf_result == PIM_RPF_CHANGED || + (rpf_result == PIM_RPF_FAILURE && + old.source_nexthop.interface)) + pim_zebra_upstream_rpf_changed(pim, up, &old); + /* update kernel multicast forwarding cache (MFC) */ + pim_upstream_mroute_iif_update(up->channel_oil, + __func__); } } + pim_zebra_update_all_interfaces(pim); } unsigned int pim_upstream_hash_key(const void *arg) @@ -1639,18 +1824,11 @@ void pim_upstream_terminate(struct pim_instance *pim) { struct pim_upstream *up; - if (pim->upstream_list) { - while (pim->upstream_list->count) { - up = listnode_head(pim->upstream_list); - pim_upstream_del(pim, up, __PRETTY_FUNCTION__); - } - - list_delete(&pim->upstream_list); + while ((up = rb_pim_upstream_first(&pim->upstream_head))) { + pim_upstream_del(pim, up, __PRETTY_FUNCTION__); } - if (pim->upstream_hash) - hash_free(pim->upstream_hash); - pim->upstream_hash = NULL; + rb_pim_upstream_fini(&pim->upstream_head); if (pim->upstream_sg_wheel) wheel_delete(pim->upstream_sg_wheel); @@ -1685,30 +1863,30 @@ bool pim_upstream_equal(const void *arg1, const void *arg2) */ static bool pim_upstream_kat_start_ok(struct pim_upstream *up) { - struct pim_instance *pim = up->channel_oil->pim; + struct channel_oil *c_oil = up->channel_oil; + struct interface *ifp = up->rpf.source_nexthop.interface; + struct pim_interface *pim_ifp; + + /* "iif == RPF_interface(S)" check is not easy to do as the info + * we get from the kernel/ASIC is really a "lookup/key hit". + * So we will do an approximate check here to avoid starting KAT + * because of (S,G,rpt) forwarding on a non-LHR. + */ + if (!ifp) + return false; - /* "iif == RPF_interface(S)" check has to be done by the kernel or hw - * so we will skip that here */ - if (up->rpf.source_nexthop.interface && - pim_if_connected_to_source(up->rpf.source_nexthop.interface, + pim_ifp = ifp->info; + if (pim_ifp->mroute_vif_index != c_oil->oil.mfcc_parent) + return false; + + if (pim_if_connected_to_source(up->rpf.source_nexthop.interface, up->sg.src)) { return true; } if ((up->join_state == PIM_UPSTREAM_JOINED) - && !pim_upstream_empty_inherited_olist(up)) { - /* XXX: I have added this RP check just for 3.2 and it's a - * digression from - * what rfc-4601 says. Till now we were only running KAT on FHR - * and RP and - * there is some angst around making the change to run it all - * routers that - * maintain the (S, G) state. This is tracked via CM-13601 and - * MUST be - * removed to handle spt turn-arounds correctly in a 3-tier clos - */ - if (I_am_RP(pim, up->sg.grp)) - return true; + && !pim_upstream_empty_inherited_olist(up)) { + return true; } return false; @@ -1793,9 +1971,8 @@ static void pim_upstream_sg_running(void *arg) void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim) { struct pim_upstream *up; - struct listnode *node; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, node, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (up->sg.src.s_addr != INADDR_ANY) continue; @@ -1803,7 +1980,7 @@ void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim) continue; pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); } } @@ -1833,7 +2010,6 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim, const char *nlist) { struct pim_upstream *up; - struct listnode *node; struct prefix_list *np; struct prefix g; enum prefix_list_type apply_new; @@ -1843,7 +2019,7 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim, g.family = AF_INET; g.prefixlen = IPV4_MAX_PREFIXLEN; - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, node, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { if (up->sg.src.s_addr != INADDR_ANY) continue; @@ -1852,17 +2028,18 @@ void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim, if (!nlist) { pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); continue; } g.u.prefix4 = up->sg.grp; apply_new = prefix_list_apply(np, &g); if (apply_new == PREFIX_DENY) pim_channel_add_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); else pim_channel_del_oif(up->channel_oil, pim->regiface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); } } @@ -1876,11 +2053,5 @@ void pim_upstream_init(struct pim_instance *pim) wheel_init(router->master, 31000, 100, pim_upstream_hash_key, pim_upstream_sg_running, name); - snprintf(name, 64, "PIM %s Upstream Hash", - pim->vrf->name); - pim->upstream_hash = hash_create_size(8192, pim_upstream_hash_key, - pim_upstream_equal, name); - - pim->upstream_list = list_new(); - pim->upstream_list->cmp = pim_upstream_compare; + rb_pim_upstream_init(&pim->upstream_head); } diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index c6c9291eed..1eb2052bb3 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -80,6 +80,12 @@ * associated with an upstream */ #define PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE (1 << 19) +/* By default as SG entry will use the SPT for forwarding traffic + * unless it was setup as a result of a Prune(S,G,rpt) from a + * downstream router and has JoinDesired(S,G) as False. + * This flag is only relevant for (S,G) entries. + */ +#define PIM_UPSTREAM_FLAG_MASK_USE_RPT (1 << 20) #define PIM_UPSTREAM_FLAG_ALL 0xFFFFFFFF @@ -103,6 +109,7 @@ #define PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) #define PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) #define PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(flags) ((flags) &PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE) +#define PIM_UPSTREAM_FLAG_TEST_USE_RPT(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_USE_RPT) #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) @@ -122,6 +129,7 @@ #define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) #define PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) #define PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) +#define PIM_UPSTREAM_FLAG_SET_USE_RPT(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_USE_RPT) #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) #define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) @@ -142,6 +150,7 @@ #define PIM_UPSTREAM_FLAG_UNSET_MLAG_VXLAN(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN) #define PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF) #define PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE) +#define PIM_UPSTREAM_FLAG_UNSET_USE_RPT(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_USE_RPT) enum pim_upstream_state { PIM_UPSTREAM_NOTJOINED, @@ -160,6 +169,7 @@ enum pim_upstream_sptbit { PIM_UPSTREAM_SPTBIT_TRUE }; +PREDECL_RBTREE_UNIQ(rb_pim_upstream); /* Upstream (S,G) channel in Joined state (S,G) in the "Not Joined" state is not represented @@ -188,6 +198,8 @@ enum pim_upstream_sptbit { */ struct pim_upstream { + struct pim_instance *pim; + struct rb_pim_upstream_item upstream_rb; struct pim_upstream *parent; struct in_addr upstream_addr; /* Who we are talking to */ struct in_addr upstream_register; /*Who we received a register from*/ @@ -232,6 +244,11 @@ struct pim_upstream { int64_t state_transition; /* Record current state uptime */ }; +static inline bool pim_upstream_is_kat_running(struct pim_upstream *up) +{ + return (up->t_ka_timer != NULL); +} + struct pim_upstream *pim_upstream_find(struct pim_instance *pim, struct prefix_sg *sg); struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg, @@ -252,6 +269,9 @@ int pim_upstream_evaluate_join_desired(struct pim_instance *pim, int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up, struct pim_ifchannel *ch, struct pim_ifchannel *starch); +int pim_upstream_eval_inherit_if(struct pim_upstream *up, + struct pim_ifchannel *ch, + struct pim_ifchannel *starch); void pim_upstream_update_join_desired(struct pim_instance *pim, struct pim_upstream *up); @@ -274,9 +294,9 @@ void pim_upstream_update_my_assert_metric(struct pim_upstream *up); void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time); -int pim_upstream_switch_to_spt_desired(struct pim_instance *pim, +int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim, struct prefix_sg *sg); -#define SwitchToSptDesired(pim, sg) pim_upstream_switch_to_spt_desired (pim, sg) +#define SwitchToSptDesiredOnRp(pim, sg) pim_upstream_switch_to_spt_desired_on_rp (pim, sg) int pim_upstream_is_sg_rpt(struct pim_upstream *up); void pim_upstream_set_sptbit(struct pim_upstream *up, @@ -308,7 +328,11 @@ void pim_upstream_init(struct pim_instance *pim); void pim_upstream_terminate(struct pim_instance *pim); void join_timer_start(struct pim_upstream *up); -int pim_upstream_compare(void *arg1, void *arg2); +int pim_upstream_compare(const struct pim_upstream *up1, + const struct pim_upstream *up2); +DECLARE_RBTREE_UNIQ(rb_pim_upstream, struct pim_upstream, upstream_rb, + pim_upstream_compare) + void pim_upstream_register_reevaluate(struct pim_instance *pim); void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim); @@ -324,4 +348,7 @@ struct pim_upstream *pim_upstream_keep_alive_timer_proc( struct pim_upstream *up); void pim_upstream_fill_static_iif(struct pim_upstream *up, struct interface *incoming); +void pim_upstream_update_use_rpt(struct pim_upstream *up, + bool update_mroute); +void pim_upstream_reeval_use_rpt(struct pim_instance *pim); #endif /* PIM_UPSTREAM_H */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 1c4ecf299f..c48ec373f8 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -379,13 +379,19 @@ int pim_interface_config_write(struct vty *vty) ij->group_addr, group_str, sizeof(group_str)); - inet_ntop(AF_INET, - &ij->source_addr, - source_str, - sizeof(source_str)); - vty_out(vty, - " ip igmp join %s %s\n", - group_str, source_str); + if (ij->source_addr.s_addr == INADDR_ANY) { + vty_out(vty, + " ip igmp join %s\n", + group_str); + } else { + inet_ntop(AF_INET, + &ij->source_addr, + source_str, + sizeof(source_str)); + vty_out(vty, + " ip igmp join %s %s\n", + group_str, source_str); + } ++writes; } } diff --git a/pimd/pim_vxlan.c b/pimd/pim_vxlan.c index 33e3b2340c..fc34f3f600 100644 --- a/pimd/pim_vxlan.c +++ b/pimd/pim_vxlan.c @@ -245,26 +245,20 @@ static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg *vxlan_sg) * for nht */ if (up) - pim_rpf_update(vxlan_sg->pim, up, NULL); + pim_rpf_update(vxlan_sg->pim, up, NULL, __func__); } } static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg *vxlan_sg) { - int vif_index; - /* update MFC with the new IIF */ pim_upstream_fill_static_iif(vxlan_sg->up, vxlan_sg->iif); - vif_index = pim_if_find_vifindex_by_ifindex(vxlan_sg->pim, - vxlan_sg->iif->ifindex); - if (vif_index > 0) - pim_scan_individual_oil(vxlan_sg->up->channel_oil, - vif_index); + pim_upstream_mroute_iif_update(vxlan_sg->up->channel_oil, __func__); if (PIM_DEBUG_VXLAN) - zlog_debug("vxlan SG %s orig mroute-up updated with iif %s vifi %d", + zlog_debug("vxlan SG %s orig mroute-up updated with iif %s", vxlan_sg->sg_str, - vxlan_sg->iif?vxlan_sg->iif->name:"-", vif_index); + vxlan_sg->iif?vxlan_sg->iif->name:"-"); } @@ -292,6 +286,7 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) struct pim_upstream *up; int flags = 0; struct prefix nht_p; + struct pim_instance *pim = vxlan_sg->pim; if (vxlan_sg->up) { /* nothing to do */ @@ -346,9 +341,15 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) pim_delete_tracked_nexthop(vxlan_sg->pim, &nht_p, up, NULL, false); } + /* We are acting FHR; clear out use_rpt setting if any */ + pim_upstream_update_use_rpt(up, false /*update_mroute*/); pim_upstream_ref(up, flags, __PRETTY_FUNCTION__); vxlan_sg->up = up; pim_vxlan_orig_mr_up_iif_update(vxlan_sg); + /* mute pimreg on origination mroutes */ + if (pim->regiface) + pim_channel_update_oif_mute(up->channel_oil, + pim->regiface->info); } else { up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg, vxlan_sg->iif, flags, @@ -373,6 +374,8 @@ static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg) /* update the inherited OIL */ pim_upstream_inherited_olist(vxlan_sg->pim, up); + if (!up->channel_oil->installed) + pim_upstream_mroute_add(up->channel_oil, __func__); } static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg) @@ -386,7 +389,8 @@ static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg) vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED; pim_channel_add_oif(vxlan_sg->up->channel_oil, - vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN); + vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN, + __func__); } static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg) @@ -405,7 +409,7 @@ static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg) vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED; pim_channel_del_oif(vxlan_sg->up->channel_oil, - orig_oif, PIM_OIF_FLAG_PROTO_VXLAN); + orig_oif, PIM_OIF_FLAG_PROTO_VXLAN, __func__); } static inline struct interface *pim_vxlan_orig_mr_oif_get( diff --git a/pimd/pim_vxlan.h b/pimd/pim_vxlan.h index f0a66e6b77..c6507a474c 100644 --- a/pimd/pim_vxlan.h +++ b/pimd/pim_vxlan.h @@ -115,6 +115,13 @@ static inline bool pim_vxlan_is_orig_mroute(struct pim_vxlan_sg *vxlan_sg) return (vxlan_sg->sg.src.s_addr != 0); } +static inline bool pim_vxlan_is_local_sip(struct pim_upstream *up) +{ + return (up->sg.src.s_addr != INADDR_ANY) && + up->rpf.source_nexthop.interface && + if_is_loopback_or_vrf(up->rpf.source_nexthop.interface); +} + extern struct pim_vxlan *pim_vxlan_p; extern struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim, struct prefix_sg *sg); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index b999188a9b..06507b1f4c 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -272,7 +272,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, nbr = pim_neighbor_find(old->source_nexthop.interface, old->rpf_addr.u.prefix4); if (nbr) - pim_jp_agg_remove_group(nbr->upstream_jp_agg, up); + pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr); /* * We have detected a case where we might need @@ -290,7 +290,7 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, * so install it. */ if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, + pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); /* @@ -325,8 +325,12 @@ void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, up->channel_oil->oil_inherited_rescan = 0; } + if (up->join_state == PIM_UPSTREAM_JOINED) + pim_jp_agg_switch_interface(old, &up->rpf, up); + if (!up->channel_oil->installed) - pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, + __PRETTY_FUNCTION__); } /* FIXME can join_desired actually be changed by pim_rpf_update() @@ -386,138 +390,15 @@ static void pim_zebra_vxlan_replay(void) zclient_send_message(zclient); } -void pim_scan_individual_oil(struct channel_oil *c_oil, int in_vif_index) -{ - struct in_addr vif_source; - int input_iface_vif_index; - - pim_rp_set_upstream_addr(c_oil->pim, &vif_source, - c_oil->oil.mfcc_origin, - c_oil->oil.mfcc_mcastgrp); - - if (in_vif_index) - input_iface_vif_index = in_vif_index; - else { - struct prefix src, grp; - - src.family = AF_INET; - src.prefixlen = IPV4_MAX_BITLEN; - src.u.prefix4 = vif_source; - grp.family = AF_INET; - grp.prefixlen = IPV4_MAX_BITLEN; - grp.u.prefix4 = c_oil->oil.mfcc_mcastgrp; - - if (PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_debug( - "%s: channel_oil (%s,%s) upstream info is not present.", - __PRETTY_FUNCTION__, source_str, group_str); - } - input_iface_vif_index = pim_ecmp_fib_lookup_if_vif_index( - c_oil->pim, &src, &grp); - } - - if (input_iface_vif_index < 1) { - if (PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_debug( - "%s %s: could not find input interface(%d) for (S,G)=(%s,%s)", - __FILE__, __PRETTY_FUNCTION__, - c_oil->oil.mfcc_parent, source_str, group_str); - } - pim_mroute_del(c_oil, __PRETTY_FUNCTION__); - return; - } - - if (input_iface_vif_index == c_oil->oil.mfcc_parent) { - if (!c_oil->installed) - pim_mroute_add(c_oil, __PRETTY_FUNCTION__); - - /* RPF unchanged */ - return; - } - - if (PIM_DEBUG_ZEBRA) { - struct interface *old_iif = pim_if_find_by_vif_index( - c_oil->pim, c_oil->oil.mfcc_parent); - struct interface *new_iif = pim_if_find_by_vif_index( - c_oil->pim, input_iface_vif_index); - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, - sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, - sizeof(group_str)); - zlog_debug( - "%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, source_str, group_str, - (old_iif) ? old_iif->name : "<old_iif?>", - c_oil->oil.mfcc_parent, - (new_iif) ? new_iif->name : "<new_iif?>", - input_iface_vif_index); - } - - /* new iif loops to existing oif ? */ - if (c_oil->oil.mfcc_ttls[input_iface_vif_index]) { - struct interface *new_iif = pim_if_find_by_vif_index( - c_oil->pim, input_iface_vif_index); - - if (PIM_DEBUG_ZEBRA) { - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, - source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, - group_str, sizeof(group_str)); - zlog_debug( - "%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d", - __FILE__, __PRETTY_FUNCTION__, source_str, - group_str, - (new_iif) ? new_iif->name : "<new_iif?>", - input_iface_vif_index); - } - } - - /* update iif vif_index */ - pim_channel_oil_change_iif(c_oil->pim, c_oil, input_iface_vif_index, - __PRETTY_FUNCTION__); - pim_mroute_add(c_oil, __PRETTY_FUNCTION__); -} - void pim_scan_oil(struct pim_instance *pim) { - struct listnode *node; - struct listnode *nextnode; struct channel_oil *c_oil; - ifindex_t ifindex; - int vif_index = 0; pim->scan_oil_last = pim_time_monotonic_sec(); ++pim->scan_oil_events; - for (ALL_LIST_ELEMENTS(pim->channel_oil_list, node, nextnode, c_oil)) { - if (c_oil->up && c_oil->up->rpf.source_nexthop.interface) { - ifindex = c_oil->up->rpf.source_nexthop - .interface->ifindex; - vif_index = - pim_if_find_vifindex_by_ifindex(pim, ifindex); - /* Pass Current selected NH vif index to mroute - * download */ - if (vif_index) - pim_scan_individual_oil(c_oil, vif_index); - } else - pim_scan_individual_oil(c_oil, 0); - } + frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) + pim_upstream_mroute_iif_update(c_oil, __func__); } static int on_rpf_cache_refresh(struct thread *t) @@ -758,7 +639,7 @@ void igmp_source_forward_start(struct pim_instance *pim, source->source_addr, sg.grp)) { /*Create a dummy channel oil */ source->source_channel_oil = pim_channel_oil_add( - pim, &sg, MAXVIFS, __PRETTY_FUNCTION__); + pim, &sg, __PRETTY_FUNCTION__); } else { @@ -809,7 +690,7 @@ void igmp_source_forward_start(struct pim_instance *pim, } source->source_channel_oil = pim_channel_oil_add( - pim, &sg, MAXVIFS, + pim, &sg, __PRETTY_FUNCTION__); } @@ -843,7 +724,7 @@ void igmp_source_forward_start(struct pim_instance *pim, source->source_channel_oil = pim_channel_oil_add( - pim, &sg, input_iface_vif_index, + pim, &sg, __PRETTY_FUNCTION__); if (!source->source_channel_oil) { if (PIM_DEBUG_IGMP_TRACE) { @@ -862,7 +743,7 @@ void igmp_source_forward_start(struct pim_instance *pim, if (PIM_I_am_DR(pim_oif)) { result = pim_channel_add_oif(source->source_channel_oil, group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); if (result) { if (PIM_DEBUG_MROUTE) { zlog_warn("%s: add_oif() failed with return=%d", @@ -891,7 +772,7 @@ void igmp_source_forward_start(struct pim_instance *pim, pim_channel_del_oif(source->source_channel_oil, group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, __func__); return; } @@ -942,7 +823,8 @@ void igmp_source_forward_stop(struct igmp_source *source) */ result = pim_channel_del_oif(source->source_channel_oil, group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); + PIM_OIF_FLAG_PROTO_IGMP, + __func__); if (result) { if (PIM_DEBUG_IGMP_TRACE) zlog_debug( @@ -985,7 +867,8 @@ void pim_forward_start(struct pim_ifchannel *ch) if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) mask = PIM_OIF_FLAG_PROTO_IGMP; - pim_channel_add_oif(up->channel_oil, ch->interface, mask); + pim_channel_add_oif(up->channel_oil, ch->interface, + mask, __func__); } void pim_forward_stop(struct pim_ifchannel *ch, bool install_it) @@ -1004,13 +887,13 @@ void pim_forward_stop(struct pim_ifchannel *ch, bool install_it) */ if (pim_upstream_evaluate_join_desired_interface(up, ch, ch->parent)) pim_channel_add_oif(up->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); else pim_channel_del_oif(up->channel_oil, ch->interface, - PIM_OIF_FLAG_PROTO_PIM); + PIM_OIF_FLAG_PROTO_PIM, __func__); if (install_it && !up->channel_oil->installed) - pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); + pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__); } void pim_zebra_zclient_update(struct vty *vty) diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index b3f9ac7630..be3b83bf80 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -169,9 +169,15 @@ BuildRequires: libyang-devel >= 0.16.74 BuildRequires: python27-devel BuildRequires: python27-sphinx %else +%if 0%{?rhel} && 0%{?rhel} > 7 +BuildRequires: python2-devel +#platform-python-devel is needed for /usr/bin/pathfix.py +BuildRequires: platform-python-devel +%else BuildRequires: python-devel >= 2.7 BuildRequires: python-sphinx %endif +%endif Requires: initscripts %if %{with_pam} BuildRequires: pam-devel @@ -217,8 +223,13 @@ Contributed/3rd party tools which may be of use with frr. %package pythontools Summary: python tools for frr +%if 0%{?rhel} && 0%{?rhel} > 7 +BuildRequires: python2 +Requires: python2-ipaddress +%else BuildRequires: python Requires: python-ipaddress +%endif Group: System Environment/Daemons %description pythontools @@ -397,6 +408,11 @@ install -m644 %{zeb_rh_src}/frr.pam %{buildroot}%{_sysconfdir}/pam.d/frr install -m644 %{zeb_rh_src}/frr.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/frr install -d -m750 %{buildroot}%{rundir} +%if 0%{?rhel} && 0%{?rhel} > 7 +# avoid `ERROR: ambiguous python shebang in` errors +pathfix.py -pni "%{__python2} %{py2_shbang_opts}" %{buildroot}/usr/lib/frr/*.py +%py_byte_compile %{__python2} %{buildroot}/usr/lib/frr/*.py +%endif %pre # add vty_group @@ -633,6 +649,7 @@ fi %if %{with_rpki} %{_libdir}/frr/modules/bgpd_rpki.so %endif +%{_libdir}/frr/modules/zebra_cumulus_mlag.so %{_libdir}/frr/modules/zebra_irdp.so %{_libdir}/frr/modules/bgpd_bmp.so %{_bindir}/* @@ -681,7 +698,10 @@ fi %changelog -* Sun May 28 2018 Rafael Zalamena <rzalamena@opensourcerouting.org> - %{version} +* Fri Dec 27 2019 Donatas Abraitis <donatas.abraitis@gmail.com> +- Add CentOS 8 support + +* Mon May 28 2018 Rafael Zalamena <rzalamena@opensourcerouting.org> - %{version} - Add BFDd support * Sun May 20 2018 Martin Winter <mwinter@opensourcerouting.org> diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c index 5c26c0cef7..7e2394f473 100644 --- a/ripd/rip_cli.c +++ b/ripd/rip_cli.c @@ -1001,6 +1001,7 @@ DEFPY (clear_ip_rip, VRF_CMD_HELP_STR) { struct list *input; + int ret; input = list_new(); if (vrf) { @@ -1011,7 +1012,11 @@ DEFPY (clear_ip_rip, listnode_add(input, yang_vrf); } - return nb_cli_rpc("/frr-ripd:clear-rip-route", input, NULL); + ret = nb_cli_rpc("/frr-ripd:clear-rip-route", input, NULL); + + list_delete(&input); + + return ret; } void rip_cli_init(void) diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 26504a857d..060bb76585 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -25,7 +25,6 @@ #include "thread.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" #include "prefix.h" #include "filter.h" #include "keychain.h" diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 90ee667f05..e07d218860 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -151,8 +151,8 @@ static int rip_zebra_read_route(ZAPI_CALLBACK_ARGS) void rip_redistribute_conf_update(struct rip *rip, int type) { - zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, - 0, rip->vrf->vrf_id); + zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, + type, 0, rip->vrf->vrf_id); } void rip_redistribute_conf_delete(struct rip *rip, int type) diff --git a/ripngd/ripng_cli.c b/ripngd/ripng_cli.c index 2d9930e357..b3d92fb0d9 100644 --- a/ripngd/ripng_cli.c +++ b/ripngd/ripng_cli.c @@ -485,6 +485,7 @@ DEFPY (clear_ipv6_rip, VRF_CMD_HELP_STR) { struct list *input; + int ret; input = list_new(); if (vrf) { @@ -495,7 +496,11 @@ DEFPY (clear_ipv6_rip, listnode_add(input, yang_vrf); } - return nb_cli_rpc("/frr-ripngd:clear-ripng-route", input, NULL); + ret = nb_cli_rpc("/frr-ripngd:clear-ripng-route", input, NULL); + + list_delete(&input); + + return ret; } void ripng_cli_init(void) diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index 0024345500..9daeeb9580 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -27,7 +27,6 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" #include "thread.h" #include "log.h" #include "prefix.h" diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index fa61d69caa..f9bd56d1df 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -152,8 +152,8 @@ static int ripng_zebra_read_route(ZAPI_CALLBACK_ARGS) void ripng_redistribute_conf_update(struct ripng *ripng, int type) { - zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, type, 0, - ripng->vrf->vrf_id); + zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, + type, 0, ripng->vrf->vrf_id); } void ripng_redistribute_conf_delete(struct ripng *ripng, int type) diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index cd577e9051..4fc8f40ae1 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -243,6 +243,8 @@ void route_add(struct prefix *p, vrf_id_t vrf_id, api_nh = &api.nexthops[i]; api_nh->vrf_id = nh->vrf_id; api_nh->type = nh->type; + api_nh->weight = nh->weight; + switch (nh->type) { case NEXTHOP_TYPE_IPV4: api_nh->gate = nh->gate; @@ -265,6 +267,17 @@ void route_add(struct prefix *p, vrf_id_t vrf_id, api_nh->bh_type = nh->bh_type; break; } + + if (nh->nh_label && nh->nh_label->num_labels > 0) { + int j; + + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL); + + api_nh->label_num = nh->nh_label->num_labels; + for (j = 0; j < nh->nh_label->num_labels; j++) + api_nh->labels[j] = nh->nh_label->label[j]; + } + i++; } api.nexthop_num = i; diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index a474613b4d..42646d15bc 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -388,7 +388,8 @@ extern void static_zebra_route_add(struct route_node *rn, continue; api_nh->vrf_id = si->nh_vrf_id; - api_nh->onlink = si->onlink; + if (si->onlink) + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); si->state = STATIC_SENT_TO_ZEBRA; @@ -441,7 +442,7 @@ extern void static_zebra_route_add(struct route_node *rn, if (si->snh_label.num_labels) { int i; - SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL); api_nh->label_num = si->snh_label.num_labels; for (i = 0; i < api_nh->label_num; i++) api_nh->labels[i] = si->snh_label.label[i]; diff --git a/tests/.gitignore b/tests/.gitignore index 6252cea823..5414cb8cc9 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -45,6 +45,7 @@ /lib/test_timer_performance /lib/test_ttable /lib/test_typelist +/lib/test_versioncmp /lib/test_zlog /lib/test_zmq /ospf6d/test_lsdb diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index 96e398512b..1b3f90434b 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -923,7 +923,7 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT)) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index fbf2a9fed2..af9e5791b7 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -1087,7 +1087,7 @@ int main(void) if (fileno(stdout) >= 0) tty = isatty(fileno(stdout)); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT)) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c index 7a038fb02e..d2c093fbea 100644 --- a/tests/bgpd/test_packet.c +++ b/tests/bgpd/test_packet.c @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) vrf_init(NULL, NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); - if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT)) + if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0) return -1; peer = peer_create_accept(bgp); diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index b1dcfcf707..2de29cbdbc 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -24,7 +24,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" extern void test_init(void); @@ -154,7 +154,7 @@ int main(int argc, char **argv) /* Library inits. */ cmd_init(1); vty_init(master, false); - memory_init(); + lib_cmd_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 2071ae08cd..e091372ab8 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -25,7 +25,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "log.h" #include "common_cli.h" @@ -83,7 +83,7 @@ int main(int argc, char **argv) cmd_domainname_set("test.domain"); vty_init(master, false); - memory_init(); + lib_cmd_init(); yang_init(); nb_init(master, NULL, 0); diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c index 88126e84bc..391ccd9268 100644 --- a/tests/lib/cxxcompat.c +++ b/tests/lib/cxxcompat.c @@ -51,13 +51,13 @@ #include "lib/json.h" #include "lib/keychain.h" #include "lib/lib_errors.h" +#include "lib/lib_vty.h" #include "lib/libfrr.h" #include "lib/libospf.h" #include "lib/linklist.h" #include "lib/log.h" #include "lib/md5.h" #include "lib/memory.h" -#include "lib/memory_vty.h" #include "lib/mlag.h" #include "lib/module.h" #include "lib/monotime.h" diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index a28830e088..18d3180889 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -23,7 +23,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" #include "log.h" #include "northbound.h" @@ -412,7 +412,7 @@ int main(int argc, char **argv) cmd_init(1); cmd_hostname_set("test"); vty_init(master, false); - memory_init(); + lib_cmd_init(); yang_init(); nb_init(master, modules, array_size(modules)); diff --git a/tests/lib/test_buffer.c b/tests/lib/test_buffer.c index b56cc30cf3..7fb9a769d3 100644 --- a/tests/lib/test_buffer.c +++ b/tests/lib/test_buffer.c @@ -20,7 +20,7 @@ #include <zebra.h> #include <memory.h> -#include <memory_vty.h> +#include <lib_vty.h> #include <buffer.h> struct thread_master *master; @@ -32,7 +32,7 @@ int main(int argc, char **argv) char junk[3]; char c = 'a'; - memory_init(); + lib_cmd_init(); if ((argc != 2) || (sscanf(argv[1], "%d%1s", &n, junk) != 1)) { fprintf(stderr, "Usage: %s <number of chars to simulate>\n", diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c index de638bc67a..c06ebbeb38 100644 --- a/tests/lib/test_privs.c +++ b/tests/lib/test_privs.c @@ -22,7 +22,7 @@ #include "getopt.h" #include "privs.h" #include "memory.h" -#include "memory_vty.h" +#include "lib_vty.h" zebra_capabilities_t _caps_p[] = { ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_DAC_OVERRIDE, @@ -105,7 +105,7 @@ int main(int argc, char **argv) } /* Library inits. */ - memory_init(); + lib_cmd_init(); zprivs_preinit(&test_privs); zprivs_init(&test_privs); diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h index f20bbc52d9..9039fa8a46 100644 --- a/tests/lib/test_typelist.h +++ b/tests/lib/test_typelist.h @@ -98,12 +98,13 @@ static void ts_hash(const char *text, const char *expect) unsigned i = 0; uint8_t hash[32]; char hashtext[65]; - uint32_t count; + uint32_t swap_count, count; - count = htonl(list_count(&head)); + count = list_count(&head); + swap_count = htonl(count); SHA256_Init(&ctx); - SHA256_Update(&ctx, &count, sizeof(count)); + SHA256_Update(&ctx, &swap_count, sizeof(swap_count)); frr_each (list, &head, item) { struct { @@ -115,7 +116,7 @@ static void ts_hash(const char *text, const char *expect) }; SHA256_Update(&ctx, &hashitem, sizeof(hashitem)); i++; - assert(i < count); + assert(i <= count); } SHA256_Final(hash, &ctx); diff --git a/tests/lib/test_versioncmp.c b/tests/lib/test_versioncmp.c new file mode 100644 index 0000000000..bb819e36f5 --- /dev/null +++ b/tests/lib/test_versioncmp.c @@ -0,0 +1,66 @@ +/* + * frr_version_cmp() tests + * Copyright (C) 2018 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include <defaults.h> + +static const char *rel(int x) +{ + if (x < 0) + return "<"; + if (x > 0) + return ">"; + return "=="; +} + +static int fail; + +static void compare(const char *a, const char *b, int expect) +{ + int result = frr_version_cmp(a, b); + + if (expect == result) + printf("\"%s\" %s \"%s\"\n", a, rel(result), b); + else { + printf("\"%s\" %s \"%s\", expected %s!\n", a, rel(result), b, + rel(expect)); + fail = 1; + } +} + +int main(int argc, char **argv) +{ + compare("", "", 0); + compare("1", "1", 0); + compare("1.0", "1.00", 0); + compare("10.0", "1", 1); + compare("10.0", "2", 1); + compare("2.1", "10.0", -1); + compare("1.1.1", "1.1.0", 1); + compare("1.0a", "1.0", 1); + compare("1.0a", "1.0b", -1); + compare("1.0a10", "1.0a2", 1); + compare("1.00a2", "1.0a2", 0); + compare("1.00a2", "1.0a3", -1); + compare("1.0-dev", "1.0", 1); + compare("1.0~foo", "1.0", -1); + compare("1.0~1", "1.0~0", 1); + compare("1.00~1", "1.0~0", 1); + printf("final tally: %s\n", fail ? "FAILED" : "ok"); + return fail; +} diff --git a/tests/lib/test_versioncmp.py b/tests/lib/test_versioncmp.py new file mode 100644 index 0000000000..0990757000 --- /dev/null +++ b/tests/lib/test_versioncmp.py @@ -0,0 +1,6 @@ +import frrtest + +class TestVersionCmp(frrtest.TestMultiOut): + program = './test_versioncmp' + +TestVersionCmp.exit_cleanly() diff --git a/tests/subdir.am b/tests/subdir.am index 270c0811b4..d87d348949 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -69,6 +69,7 @@ check_PROGRAMS = \ tests/lib/test_timer_performance \ tests/lib/test_ttable \ tests/lib/test_typelist \ + tests/lib/test_versioncmp \ tests/lib/test_zlog \ tests/lib/test_graph \ tests/lib/cli/test_cli \ @@ -293,6 +294,10 @@ tests_lib_test_typelist_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_typelist_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_typelist_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_typelist_SOURCES = tests/lib/test_typelist.c tests/helpers/c/prng.c +tests_lib_test_versioncmp_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_versioncmp_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_versioncmp_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_versioncmp_SOURCES = tests/lib/test_versioncmp.c tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD) @@ -344,6 +349,7 @@ EXTRA_DIST += \ tests/lib/test_ttable.py \ tests/lib/test_ttable.refout \ tests/lib/test_typelist.py \ + tests/lib/test_versioncmp.py \ tests/lib/test_zlog.py \ tests/lib/test_graph.py \ tests/lib/test_graph.refout \ diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf new file mode 100644 index 0000000000..99e6b6818d --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65000 + neighbor 192.168.255.2 remote-as 65000 + address-family ipv4 unicast + redistribute connected +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf new file mode 100644 index 0000000000..7ef77a6015 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r5-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf new file mode 100644 index 0000000000..164f975cb7 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf @@ -0,0 +1,3 @@ +router bgp 65000 + bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65000 diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf new file mode 100644 index 0000000000..1c617c4272 --- /dev/null +++ b/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf @@ -0,0 +1,6 @@ +! +interface r6-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py index eecacfd00c..6660b4e866 100644 --- a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py +++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py @@ -5,7 +5,7 @@ # Part of NetDEF Topology Tests # # Copyright (c) 2019 by -# Network Device Education Foundation, Inc. ("NetDEF") +# Donatas Abraitis <donatas.abraitis@gmail.com> # # Permission to use, copy, modify, and/or distribute this software # for any purpose with or without fee is hereby granted, provided @@ -34,6 +34,7 @@ import sys import json import time import pytest +import functools CWD = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(CWD, '../')) @@ -48,7 +49,7 @@ class TemplateTopo(Topo): def build(self, *_args, **_opts): tgen = get_topogen(self) - for routern in range(1, 5): + for routern in range(1, 7): tgen.add_router('r{}'.format(routern)) switch = tgen.add_switch('s1') @@ -59,6 +60,10 @@ class TemplateTopo(Topo): switch.add_link(tgen.gears['r3']) switch.add_link(tgen.gears['r4']) + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r5']) + switch.add_link(tgen.gears['r6']) + def setup_module(mod): tgen = Topogen(TemplateTopo, mod.__name__) tgen.start_topology() @@ -81,32 +86,57 @@ def teardown_module(mod): tgen = get_topogen() tgen.stop_topology() -def test_bgp_remove_private_as(): +def test_ebgp_requires_policy(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) def _bgp_converge(router): - while True: - cmd = "show ip bgp neighbor 192.168.255.1 json" - output = json.loads(tgen.gears[router].vtysh_cmd(cmd)) - if output['192.168.255.1']['bgpState'] == 'Established': - time.sleep(3) - return True - - def _bgp_ebgp_requires_policy(router): - cmd = "show ip bgp 172.16.255.254/32 json" - output = json.loads(tgen.gears[router].vtysh_cmd(cmd)) - if 'prefix' in output: - return True - return False - - if _bgp_converge('r2'): - assert _bgp_ebgp_requires_policy('r2') == True - - if _bgp_converge('r4'): - assert _bgp_ebgp_requires_policy('r4') == False + output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + '192.168.255.1': { + 'bgpState': 'Established' + } + } + return topotest.json_cmp(output, expected) + + def _bgp_has_routes(router): + output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 routes json")) + expected = { + 'routes': { + '172.16.255.254/32': [ + { + 'valid': True + } + ] + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, 'r2') + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success is True, 'Failed bgp convergence (r2) in "{}"'.format(router) + + test_func = functools.partial(_bgp_has_routes, 'r2') + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success is True, 'eBGP policy is not working (r2) in "{}"'.format(router) + + test_func = functools.partial(_bgp_converge, 'r4') + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success is True, 'Failed bgp convergence (r4) in "{}"'.format(router) + + test_func = functools.partial(_bgp_has_routes, 'r4') + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success is False, 'eBGP policy is not working (r4) in "{}"'.format(router) + + test_func = functools.partial(_bgp_converge, 'r6') + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success is True, 'Failed bgp convergence (r6) in "{}"'.format(router) + + test_func = functools.partial(_bgp_has_routes, 'r6') + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert success is True, 'eBGP policy is not working (r6) in "{}"'.format(router) if __name__ == '__main__': args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py index 02fba97316..e62d139a0c 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -153,8 +153,10 @@ def ltemplatePreRouterStartHook(): if topotest.version_cmp(krel, '4.15') >= 0 and \ topotest.version_cmp(krel, '4.18') <= 0: l3mdev_accept = 1 - else: - l3mdev_accept = 0 + + if topotest.version_cmp(krel, '5.0') >= 0: + l3mdev_accept = 1 + logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) #check for mpls if tgen.hasmpls != True: diff --git a/tests/topotests/bgp_reject_as_sets/__init__.py b/tests/topotests/bgp_reject_as_sets/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/__init__.py diff --git a/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf new file mode 100644 index 0000000000..7b24c4bbf9 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf @@ -0,0 +1,8 @@ +! exit1 +router bgp 65001 + neighbor 192.168.255.1 remote-as 65002 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_reject_as_sets/r1/zebra.conf b/tests/topotests/bgp_reject_as_sets/r1/zebra.conf new file mode 100644 index 0000000000..9904bb4e16 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r1/zebra.conf @@ -0,0 +1,9 @@ +! exit1 +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf new file mode 100644 index 0000000000..c991b5bcd8 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf @@ -0,0 +1,10 @@ +! spine +router bgp 65002 + bgp reject-as-sets + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.254.2 remote-as 65003 + address-family ipv4 unicast + aggregate-address 172.16.0.0/16 as-set summary-only + exit-address-family + ! +! diff --git a/tests/topotests/bgp_reject_as_sets/r2/zebra.conf b/tests/topotests/bgp_reject_as_sets/r2/zebra.conf new file mode 100644 index 0000000000..f0d357c5ff --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r2/zebra.conf @@ -0,0 +1,9 @@ +! spine +interface r2-eth0 + ip address 192.168.255.1/30 +! +interface r2-eth1 + ip address 192.168.254.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf new file mode 100644 index 0000000000..bee518c84b --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf @@ -0,0 +1,9 @@ +! exit2 +router bgp 65003 + neighbor 192.168.254.1 remote-as 65002 + address-family ipv4 unicast + neighbor 192.168.254.1 allowas-in + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_reject_as_sets/r3/zebra.conf b/tests/topotests/bgp_reject_as_sets/r3/zebra.conf new file mode 100644 index 0000000000..f490d97afe --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/r3/zebra.conf @@ -0,0 +1,9 @@ +! exit2 +interface lo + ip address 172.16.254.254/32 +! +interface r3-eth0 + ip address 192.168.254.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py new file mode 100644 index 0000000000..f307edc678 --- /dev/null +++ b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python + +# +# test_bgp_reject_as_sets.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if an aggregated route with AS_SET is not sent to peers. +Addressing draft-ietf-idr-deprecate-as-set-confed-set recommendations. + +BGP speakers conforming to this document (i.e., conformant BGP + speakers) MUST NOT locally generate BGP UPDATE messages containing + AS_SET or AS_CONFED_SET. Conformant BGP speakers SHOULD NOT send BGP + UPDATE messages containing AS_SET or AS_CONFED_SET. Upon receipt of + such messages, conformant BGP speakers SHOULD use the "Treat-as- + withdraw" error handling behavior as per [RFC7606]. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 4): + tgen.add_router('r{}'.format(routern)) + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r2']) + + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r2']) + switch.add_link(tgen.gears['r3']) + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.iteritems(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, '{}/bgpd.conf'.format(rname)) + ) + + tgen.start_router() + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + +def test_bgp_reject_as_sets(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears['r2'] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")) + expected = { + '192.168.255.2': { + 'bgpState': 'Established', + 'addressFamilyInfo': { + 'ipv4Unicast': { + 'acceptedPrefixCounter': 2 + } + } + } + } + return topotest.json_cmp(output, expected) + + def _bgp_has_aggregated_route_with_stripped_as_set(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.0.0/16 json")) + expected = { + 'paths': [ + { + 'aspath': { + 'string': 'Local', + 'segments': [ + ], + 'length': 0 + } + } + ] + } + return topotest.json_cmp(output, expected) + + def _bgp_announce_route_without_as_sets(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.254.2 advertised-routes json")) + expected = { + 'advertisedRoutes': { + '172.16.0.0/16': { + 'asPath': '' + }, + '192.168.254.0/30': { + 'asPath': '65003' + }, + '192.168.255.0/30': { + 'asPath': '65001' + } + }, + 'totalPrefixCounter': 3 + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(router) + + test_func = functools.partial(_bgp_has_aggregated_route_with_stripped_as_set, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed to see an aggregated route in "{}"'.format(router) + + test_func = functools.partial(_bgp_announce_route_without_as_sets, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Route 172.16.0.0/16 should be sent without AS_SET to r3 "{}"'.format(router) + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py b/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py index 59ffd36ef3..f5119468e0 100644 --- a/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py +++ b/tests/topotests/bgp_show_ip_bgp_fqdn/test_bgp_show_ip_bgp_fqdn.py @@ -5,7 +5,7 @@ # Part of NetDEF Topology Tests # # Copyright (c) 2019 by -# Network Device Education Foundation, Inc. ("NetDEF") +# Donatas Abraitis <donatas.abraitis@gmail.com> # # Permission to use, copy, modify, and/or distribute this software # for any purpose with or without fee is hereby granted, provided @@ -33,6 +33,7 @@ import sys import json import time import pytest +import functools CWD = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(CWD, '../')) @@ -76,33 +77,40 @@ def teardown_module(mod): tgen = get_topogen() tgen.stop_topology() -def test_bgp_maximum_prefix_invalid(): +def test_bgp_show_ip_bgp_hostname(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - def _bgp_converge(router, neighbor): - cmd = "show ip bgp neighbor {0} json".format(neighbor) - while True: - output = json.loads(tgen.gears[router].vtysh_cmd(cmd)) - if output[neighbor]['bgpState'] == 'Established': - time.sleep(3) + router = tgen.gears['r2'] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + '192.168.255.1': { + 'bgpState': 'Established', + 'addressFamilyInfo': { + 'ipv4Unicast': { + 'acceptedPrefixCounter': 2 + } + } + } + } + return topotest.json_cmp(output, expected) + + def _bgp_show_nexthop_hostname_and_ip(router): + output = json.loads(router.vtysh_cmd("show ip bgp json")) + for nh in output['routes']['172.16.255.253/32'][0]['nexthops']: + if 'hostname' in nh and 'ip' in nh: return True + return False - def _bgp_show_nexthop(router, prefix): - cmd = "show ip bgp json" - output = json.loads(tgen.gears[router].vtysh_cmd(cmd)) - for nh in output['routes'][prefix][0]['nexthops']: - if 'fqdn' in nh: - return 'fqdn' - return 'ip' + test_func = functools.partial(_bgp_converge, router) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) - if _bgp_converge('r2', '192.168.255.1'): - assert _bgp_show_nexthop('r2', '172.16.255.254/32') == 'fqdn' - - if _bgp_converge('r1', '192.168.255.2'): - assert _bgp_show_nexthop('r1', '172.16.255.253/32') == 'ip' + assert result is None, 'Failed bgp convergence in "{}"'.format(router) + assert _bgp_show_nexthop_hostname_and_ip(router) == True if __name__ == '__main__': args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py index 6b4df78c69..2944b3729c 100644 --- a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py @@ -31,6 +31,7 @@ import sys import json from functools import partial import pytest +import platform # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) @@ -68,6 +69,19 @@ def setup_module(mod): router_list = tgen.routers() logger.info('Testing with VRF Lite support') + krel = platform.release() + + # May need to adjust handling of vrf traffic depending on kernel version + l3mdev_accept = 0 + if topotest.version_cmp(krel, '4.15') >= 0 and \ + topotest.version_cmp(krel, '4.18') <= 0: + l3mdev_accept = 1 + + if topotest.version_cmp(krel, '5.0') >= 0: + l3mdev_accept = 1 + + logger.info('krel \'{0}\' setting net.ipv4.tcp_l3mdev_accept={1}'.format( + krel, l3mdev_accept)) cmds = ['ip link add {0}-cust1 type vrf table 1001', 'ip link add loop1 type dummy', @@ -78,6 +92,15 @@ def setup_module(mod): for cmd in cmds: output = tgen.net[rname].cmd(cmd.format(rname)) + output = tgen.net[rname].cmd('sysctl -n net.ipv4.tcp_l3mdev_accept') + logger.info( + 'router {0}: existing tcp_l3mdev_accept was {1}'.format( + rname, output)) + + if l3mdev_accept: + output = tgen.net[rname].cmd( + 'sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept)) + for rname, router in router_list.iteritems(): router.load_config( TopoRouter.RD_ZEBRA, diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 9f2fef52ea..fc7581b1f2 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -28,6 +28,7 @@ from subprocess import PIPE as SUB_PIPE from subprocess import Popen from functools import wraps from re import search as re_search +from tempfile import mkdtemp import StringIO import os @@ -276,11 +277,19 @@ def reset_config_on_routers(tgen, routerName=None): run_cfg_file = "{}/{}/frr.sav".format(TMPDIR, rname) init_cfg_file = "{}/{}/frr_json_initial.conf".format(TMPDIR, rname) - command = "/usr/lib/frr/frr-reload.py --input {} --test {} > {}". \ - format(run_cfg_file, init_cfg_file, dname) + + tempdir = mkdtemp() + with open(os.path.join(tempdir, 'vtysh.conf'), 'w') as fd: + pass + + command = "/usr/lib/frr/frr-reload.py --confdir {} --input {} --test {} > {}". \ + format(tempdir, run_cfg_file, init_cfg_file, dname) result = call(command, shell=True, stderr=SUB_STDOUT, stdout=SUB_PIPE) + os.unlink(os.path.join(tempdir, 'vtysh.conf')) + os.rmdir(tempdir) + # Assert if command fail if result > 0: logger.error("Delta file creation failed. Command executed %s", diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons index 79c52d30d1..8bec3c5bb6 100644 --- a/tools/etc/frr/daemons +++ b/tools/etc/frr/daemons @@ -56,6 +56,11 @@ bfdd_options=" -A 127.0.0.1" fabricd_options="-A 127.0.0.1" vrrpd_options=" -A 127.0.0.1" +# configuration profile +# +#frr_profile="traditional" +#frr_profile="datacenter" + # # This is the maximum number of FD's that will be available. # Upon startup this is read by the control files and ulimit diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 412cde0910..3e97635dfe 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -155,7 +155,7 @@ class Config(object): try: config_text = subprocess.check_output( bindir + "/vtysh --config_dir " + confdir + " -c 'show run " + daemon + "' | /usr/bin/tail -n +4 | " + bindir + "/vtysh --config_dir " + confdir + " -m -f -", - shell=True, stderr=subprocess.STDOUT) + shell=True) except subprocess.CalledProcessError as e: ve = VtyshMarkException(e) ve.output = e.output @@ -1105,7 +1105,7 @@ def vtysh_config_available(bindir, confdir): try: cmd = [str(bindir + '/vtysh'), '--config_dir', confdir, '-c', 'conf t'] - output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip() + output = subprocess.check_output(cmd).strip() if 'VTY configuration is locked by other VTY' in output.decode('utf-8'): print(output) @@ -1363,7 +1363,7 @@ if __name__ == '__main__': while True: try: - _ = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + _ = subprocess.check_output(cmd) except subprocess.CalledProcessError: @@ -1408,7 +1408,7 @@ if __name__ == '__main__': fh.write(line + '\n') try: - subprocess.check_output([str(args.bindir + '/vtysh'), '--config_dir', args.confdir, '-f', filename], stderr=subprocess.STDOUT) + subprocess.check_output([str(args.bindir + '/vtysh'), '--config_dir', args.confdir, '-f', filename]) except subprocess.CalledProcessError as e: log.warning("frr-reload.py failed due to\n%s" % e.output) reload_ok = False diff --git a/tools/frr.in b/tools/frr.in index 50f7ecaa9f..d9816c2568 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -21,6 +21,7 @@ VTYSH="@vtysh_bin@" # /usr/bin/vtysh FRR_USER="@enable_user@" # frr FRR_GROUP="@enable_group@" # frr FRR_VTY_GROUP="@enable_vty_group@" # frrvty +FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter # Local Daemon selection may be done by using /etc/frr/daemons. # See /usr/share/doc/frr/README.Debian.gz for further information. @@ -151,7 +152,7 @@ start() --pidfile=`pidfile $dmn-$inst` \ --exec "$D_PATH/$dmn" \ -- \ - `eval echo "$""$dmn""_options"` -n "$inst" + `eval echo "$""$dmn""_options"` $frr_global_options -n "$inst" else if ! check_daemon $dmn; then echo -n " (binary does not exist)" @@ -164,14 +165,14 @@ start() --pidfile=`pidfile $dmn` \ --exec "$valgrind" \ -- --trace-children=no --leak-check=full --log-file=/var/log/frr/$dmn-valgrind.log $D_PATH/$dmn \ - `eval echo "$""$dmn""_options"` + `eval echo "$""$dmn""_options"` $frr_global_options else ${SSD} \ --start \ --pidfile=`pidfile $dmn` \ --exec "$D_PATH/$dmn" \ -- \ - `eval echo "$""$dmn""_options"` + `eval echo "$""$dmn""_options"` $frr_global_options fi fi @@ -188,7 +189,7 @@ start() --pidfile=`pidfile staticd` \ --exec "$D_PATH/staticd" \ -- \ - `eval echo "$"staticd"_options"` + `eval echo "$"staticd"_options"` $frr_global_options fi } @@ -511,6 +512,18 @@ fi # Read configuration variable file if it is present [ -r /etc/default/frr ] && . /etc/default/frr +if test -z "$frr_profile"; then + # try to autodetect config profile + if test -d /etc/cumulus; then + frr_profile=datacenter + # elif test ...; then + # -- add your distro/system here + elif test -n "$FRR_DEFAULT_PROFILE"; then + frr_profile="$FRR_DEFAULT_PROFILE" + fi +fi +test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile" + MAX_INSTANCES=${MAX_INSTANCES:=5} # Set priority of un-startable daemons to 'no' and substitute 'yes' to '0' @@ -561,7 +574,7 @@ case "$1" in stop_prio 0 $dmn fi - if [ [ -n "$dmn" ] && [ "$dmn" != "zebra" ] ]; then + if [ -n "$dmn" -a "$dmn" != "zebra" ]; then [ -n "$dmn" ] && eval "${dmn/-/_}=0" start_watchfrr fi diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index 3fc38d4bed..0dfdfd0efa 100644 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -24,6 +24,7 @@ VTYSH="@vtysh_bin@" # /usr/bin/vtysh FRR_USER="@enable_user@" # frr FRR_GROUP="@enable_group@" # frr FRR_VTY_GROUP="@enable_vty_group@" # frrvty +FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter # ORDER MATTERS FOR $DAEMONS! # - keep zebra first @@ -151,7 +152,7 @@ daemon_start() { instopt="${inst:+-n $inst}" eval args="\$${daemon}_options" - if eval "$all_wrap $wrap $bin -d $instopt $args"; then + if eval "$all_wrap $wrap $bin -d $frr_global_options $instopt $args"; then log_success_msg "Started $dmninst" vtysh_b "$daemon" else @@ -298,6 +299,18 @@ if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a' unset watchfrr_options fi +if test -z "$frr_profile"; then + # try to autodetect config profile + if test -d /etc/cumulus; then + frr_profile=datacenter + # elif test ...; then + # -- add your distro/system here + elif test -n "$FRR_DEFAULT_PROFILE"; then + frr_profile="$FRR_DEFAULT_PROFILE" + fi +fi +test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile" + # # other defaults and dispatch # diff --git a/tools/symalyzer.html b/tools/symalyzer.html new file mode 100644 index 0000000000..eefeee3b05 --- /dev/null +++ b/tools/symalyzer.html @@ -0,0 +1,347 @@ +<html> +<!-- + - 2019 by David Lamparter, placed in public domain + --> + <head> + <title>Symalyzer report</title> + <style type="text/css"> +html { + margin:auto; + max-width:70em; + font-family:Fira Sans, sans-serif; +} +dl { + display:grid; + grid-template-columns: 1.4em 1.4em 1fr 1fr; + grid-auto-rows: auto; +} +dt.dir { + background-color:#ff8; + color:#000; + border:1px solid #000; + border-bottom:2px solid #000; + font-size:14pt; + padding:2pt 15pt; + margin:0pt; + margin-top:10pt; + grid-column:1 / -1; +} +dt.file { + background-color:#ffa; + color:#000; + border-bottom:1px solid #000; + font-size:12pt; + padding:2pt 15pt; + margin:5pt 0pt; + grid-column:1 / -1; +} +dt.file.filehidden { + background-color:#ffc; + font-size:10pt; + padding:0.5pt 15pt; + margin-bottom:-5pt; +} +dd { + display:inline-block; + vertical-align:middle; + margin:0; +} +dd.symtype { + grid-column:1; + + border:1px solid #666; + text-align:center; +} +dd.symklass { + grid-column:2; + + border:1px solid #666; + text-align:center; +} +dd.symname { + grid-column:3; + + font-family:monospace; + padding:0 0.5em; + padding-top:2px; + border-bottom:1px dashed #ccc; +} +dd.symloc { + grid-column:4; + + padding:0 0.5em; + border-bottom:1px dashed #ccc; +} +.symloc-unknown { + font-style:italic; + color:#aaa; +} + +.symtype.sym-static { + background-color:#cf4; + color:#000; +} +.symtype.sym-extrastatic { + background-color:#fe8; + color:#000; +} +.symtype.sym-liblocal { + background-color:#fc6; + color:#000; +} + +.symklass.symk-T { + background-color:#ddd; + color:#000; +} +.symklass.symk-B, +.symklass.symk-C, +.symklass.symk-D { + background-color:#faa; + color:#000; +} +.symklass.symk-R { + background-color:#fd8; + color:#000; +} + +.symtype.sym-api { + background-color:#d9f; + color:#000; +} +.symname.sym-api, +.symloc.sym-api { + background-color:#f8e8ff; +} + +dt.file.dirhidden, +dd.dirhidden { + display:none; +} +dd.filehidden { + display:none; +} +dd.symhidden { + display:none; +} + +ul { + font-size:10pt; +} +li { + margin-bottom:6pt; + text-indent:-2.5em; + margin-left:2.5em; +} +code { + background-color:#eee; + color:#060; + text-decoration:underline; +} +b.symtype, +b.symklass { + display:inline-block; + text-align:center; + border:1px solid #666; + width:1.4em; + text-indent:0; +} + </style> + <script src="jquery-3.4.1.min.js"></script> + <script> + +function dirtoggle(elem, visible) { + if (visible) { + elem.removeClass("dirhidden"); + } else { + elem.addClass("dirhidden"); + } + + var next = elem.next(); + while (next.is("dd") || next.is("dt.file")) { + if (visible) { + next.removeClass("dirhidden"); + } else { + next.addClass("dirhidden"); + } + next = next.next(); + } +} + +function filetoggle(elem, visible) { + if (visible) { + elem.removeClass("filehidden"); + } else { + elem.addClass("filehidden"); + } + + var next = elem.next(); + while (next.is("dd")) { + if (visible) { + next.removeClass("filehidden"); + } else { + next.addClass("filehidden"); + } + next = next.next(); + } +} + +function symtoggle(elem, visible) { + if (visible) { + elem.removeClass("symhidden"); + } else { + elem.addClass("symhidden"); + } + + var next = elem.next(); + while (next.is(".symklass") || next.is(".symname") || next.is(".symloc")) { + if (visible) { + next.removeClass("symhidden"); + } else { + next.addClass("symhidden"); + } + next = next.next(); + } +} + + +$(document).ready(function(){ + $("dt.dir").each(function(){ + var elem = $(this); + + elem.click(function(){ + dirtoggle(elem, elem.is(".dirhidden")); + }); + + dirtoggle(elem, false); + }); + + $("dt.file").each(function(){ + var elem = $(this); + + elem.click(function(){ + filetoggle(elem, elem.is(".filehidden")); + }); + + /* filetoggle(elem, false); */ + }); + + $("#f_hide_all").click(function(){ + $("dt.file").each(function(){ + filetoggle($(this), false); + }); + }); + $("#f_show_all").click(function(){ + $("dt.file").each(function(){ + filetoggle($(this), true); + }); + }); + + $("#s_show_all").click(function(){ + $("dd.symtype").each(function(){ + symtoggle($(this), true); + }); + }); + $("#s_hide_all").click(function(){ + $("dd.symtype").each(function(){ + symtoggle($(this), false); + }); + }); + $("#s_show_vars").click(function(){ + $("dd.symtype").each(function(){ + var elem_type = $(this); + if (elem_type.text() === "A") { + return; + } + + var elem_klass = elem_type.next(); + + if ("BbCDdGgnRrSs".indexOf(elem_klass.text()) >= 0) { + symtoggle(elem_type, true); + } + }); + }); + $("#s_show_funcs").click(function(){ + $("dd.symtype").each(function(){ + var elem_type = $(this); + if (elem_type.text() === "A") { + return; + } + + var elem_klass = elem_type.next(); + + if ("Tt".indexOf(elem_klass.text()) >= 0) { + symtoggle(elem_type, true); + } + }); + }); + $("#s_show_api").click(function(){ + $("dd.sym-api").each(function(){ + symtoggle($(this), true); + }); + }); + + $("#jsbuttons").show(); +}); + </script> + </head> + <body> + <table style="display:none" id="jsbuttons"> + <tr><td>Files</td><td> + <button type="button" id="f_hide_all">Hide all</button> + <button type="button" id="f_show_all">Show all</button> + </td></tr> + <tr><td>Symbols</td><td> + <button type="button" id="s_hide_all">Hide all</button> + <button type="button" id="s_show_all">Show all</button><br> + <button type="button" id="s_show_vars">Show variables</button> + <button type="button" id="s_show_funcs">Show functions</button> + <button type="button" id="s_show_api">Show module/API usage</button> + </td></tr> + </table> + <div style="display:grid;grid-template-columns:1fr 1fr;"> + <ul> + <li><b class="symtype sym-static">S</b> means the symbol is not used outside its own file. + It could either be completely unused or used locally. It might be appropriate to make it + <code>static</code>.</li> + <li><b class="symtype sym-extrastatic">Z</b> means the symbol is not used outside its own file, + and it's not visible to the outside of the library or daemon (i.e. ELF hidden linkage.) + It could still be completely unused, or used within the library. It might be appropriate to make it + <code>static</code>.</li> + <li><b class="symtype sym-liblocal">L</b> means the symbol is used from other files in the library, + but not from outside. It might be appropriate to make it <code>DSO_LOCAL</code>.</li> + <li><b class="symtype sym-api">A</b> means the symbol is used from some other file, most likely a + loadable module. Note this is only flagged for symbols in executable files, not libraries.</li> + </ul> + <ul> + <li><b class="symklass symk-T">T</b> are normal functions ("program <u>T</u>ext")</li> + <li style="text-indent:0;margin-left:0"> + <b class="symklass symk-B">B</b> (<u>B</u>SS),<br> + <b class="symklass symk-C">C</b> (<u>C</u>ommon),<br> + <b class="symklass symk-D">D</b> (<u>D</u>ata)<br> + are various types of writable global variables</li> + <li><b class="symklass symk-R">R</b> are read-only global variables ("<u>R</u>odata")</li> + </ul> + </div> + <dl> + {%- for subdir, subreport in dirgroups.items()|sort %} + <dt class="dir">{{ subdir }}</dt> + {%- for obj, reports in subreport.items()|sort %} + <dt class="file">{{ obj }}</dt> + {%- for report in reports|sort %} + {#- <dd class="{{ report.idlong }}"> #} + <dd class="sym-{{ report.idlong }} symtype" title="{{ report.title }}">{{ report.idshort }}</dd> + <dd class="sym-{{ report.idlong }} symk-{{ report.sym.klass }} symklass" title="{{ klasses.get(report.sym.klass, '???') }}">{{ report.sym.klass }}</dd> + <dd class="sym-{{ report.idlong }} symname">{{ report.sym.name }}</dd> + {% if report.sym.loc %} + <dd class="sym-{{ report.idlong }} symloc">{{ report.sym.loc }}</dd> + {% else %} + <dd class="sym-{{ report.idlong }} symloc symloc-unknown">unknown</dd> + {% endif %} + {#- </dd> #} + {%- endfor %} + {%- endfor %} + {%- endfor %} + </dl> + </body> +</html> diff --git a/tools/symalyzer.py b/tools/symalyzer.py new file mode 100755 index 0000000000..b3b5c4e567 --- /dev/null +++ b/tools/symalyzer.py @@ -0,0 +1,383 @@ +#!/usr/bin/python3 +# +# 2019 by David Lamparter, placed in public domain +# +# This tool generates a report of possibly unused symbols in the build. It's +# particularly useful for libfrr to find bitrotting functions that aren't even +# used anywhere anymore. +# +# Note that the tool can't distinguish between "a symbol is completely unused" +# and "a symbol is used only in its file" since file-internal references are +# invisible in nm output. However, the compiler will warn you if a static +# symbol is unused. +# +# This tool is only tested on Linux, it probably needs `nm` from GNU binutils +# (as opposed to BSD `nm`). Could use pyelftools instead but that's a lot of +# extra work. +# +# This is a developer tool, please don't put it in any packages :) + +import sys, os, subprocess +import re +from collections import namedtuple + +class MakeVars(object): + ''' + makevars['FOO_CFLAGS'] gets you "FOO_CFLAGS" from Makefile + ''' + def __init__(self): + self._data = dict() + + def getvars(self, varlist): + ''' + get a batch list of variables from make. faster than individual calls. + ''' + rdfd, wrfd = os.pipe() + + shvars = ['shvar-%s' % s for s in varlist] + make = subprocess.Popen(['make', '-s', 'VARFD=%d' % wrfd] + shvars, pass_fds = [wrfd]) + os.close(wrfd) + data = b'' + + rdf = os.fdopen(rdfd, 'rb') + while True: + rdata = rdf.read() + if len(rdata) == 0: + break + data += rdata + + del rdf + make.wait() + + data = data.decode('US-ASCII').strip().split('\n') + for row in data: + k, v = row.split('=', 1) + v = v[1:-1] + self._data[k] = v + + def __getitem__(self, k): + if k not in self._data: + self.getvars([k]) + return self._data[k] + + def get(self, k, defval = None): + if k not in self._data: + self.getvars([k]) + return self._data[k] or defval + +SymRowBase = namedtuple('SymRow', ['target', 'object', 'name', 'address', 'klass', 'typ', 'size', 'line', 'section', 'loc']) +class SymRow(SymRowBase): + ''' + wrapper around a line of `nm` output + ''' + lib_re = re.compile(r'/lib[^/]+\.(so|la)$') + def is_global(self): + return self.klass.isupper() or self.klass in 'uvw' + def scope(self): + if self.lib_re.search(self.target) is None: + return self.target + # "global" + return None + + def is_export(self): + ''' + FRR-specific list of symbols which are considered "externally used" + + e.g. hooks are by design APIs for external use, same for qobj_t_* + frr_inet_ntop is here because it's used through an ELF alias to + "inet_ntop()" + ''' + if self.name in ['main', 'frr_inet_ntop', '_libfrr_version']: + return True + if self.name.startswith('_hook_'): + return True + if self.name.startswith('qobj_t_'): + return True + return False + +class Symbols(dict): + ''' + dict of all symbols in all libs & executables + ''' + + from_re = re.compile(r'^Symbols from (.*?):$') + lt_re = re.compile(r'^(.*/)([^/]+)\.l[oa]$') + + def __init__(self): + super().__init__() + + class ReportSym(object): + def __init__(self, sym): + self.sym = sym + def __repr__(self): + return '<%-25s %-40s [%s]>' % (self.__class__.__name__ + ':', self.sym.name, self.sym.loc) + def __lt__(self, other): + return self.sym.name.__lt__(other.sym.name) + + class ReportSymCouldBeStaticAlreadyLocal(ReportSym): + idshort = 'Z' + idlong = 'extrastatic' + title = "symbol is local to library, but only used in its source file (make static?)" + class ReportSymCouldBeStatic(ReportSym): + idshort = 'S' + idlong = 'static' + title = "symbol is only used in its source file (make static?)" + class ReportSymCouldBeLibLocal(ReportSym): + idshort = 'L' + idlong = 'liblocal' + title = "symbol is only used inside of library" + class ReportSymModuleAPI(ReportSym): + idshort = 'A' + idlong = 'api' + title = "symbol (in executable) is referenced externally from a module" + + class Symbol(object): + def __init__(self, name): + super().__init__() + self.name = name + self.defs = {} + self.refs = [] + + def process(self, row): + scope = row.scope() + if row.section == '*UND*': + self.refs.append(row) + else: + self.defs.setdefault(scope, []).append(row) + + def evaluate(self, out): + ''' + generate output report + + invoked after all object files have been read in, so it can look + at inter-object-file relationships + ''' + if len(self.defs) == 0: + out.extsyms.add(self.name) + return + + for scopename, symdefs in self.defs.items(): + common_defs = [symdef for symdef in symdefs if symdef.section == '*COM*'] + proper_defs = [symdef for symdef in symdefs if symdef.section != '*COM*'] + + if len(proper_defs) > 1: + print(self.name, ' DUPLICATE') + print('\tD: %s %s' % (scopename, '\n\t\t'.join([repr(s) for s in symdefs]))) + for syms in self.refs: + print('\tR: %s' % (syms, )) + return + + if len(proper_defs): + primary_def = proper_defs[0] + elif len(common_defs): + # "common" = global variables without initializer; + # they can occur in multiple .o files and the linker will + # merge them into one variable/storage location. + primary_def = common_defs[0] + else: + # undefined symbol, e.g. libc + continue + + if scopename is not None and len(self.refs) > 0: + for ref in self.refs: + if ref.target != primary_def.target and ref.target.endswith('.la'): + outobj = out.report.setdefault(primary_def.object, []) + outobj.append(out.ReportSymModuleAPI(primary_def)) + break + + if len(self.refs) == 0: + if primary_def.is_export(): + continue + outobj = out.report.setdefault(primary_def.object, []) + if primary_def.visible: + outobj.append(out.ReportSymCouldBeStatic(primary_def)) + else: + outobj.append(out.ReportSymCouldBeStaticAlreadyLocal(primary_def)) + continue + + if scopename is None and primary_def.visible: + # lib symbol + for ref in self.refs: + if ref.target != primary_def.target: + break + else: + outobj = out.report.setdefault(primary_def.object, []) + outobj.append(out.ReportSymCouldBeLibLocal(primary_def)) + + + def evaluate(self): + self.extsyms = set() + self.report = {} + + for sym in self.values(): + sym.evaluate(self) + + def load(self, target, files): + def libtoolmustdie(fn): + m = self.lt_re.match(fn) + if m is None: + return fn + return m.group(1) + '.libs/' + m.group(2) + '.o' + + def libtooltargetmustdie(fn): + m = self.lt_re.match(fn) + if m is None: + a, b = fn.rsplit('/', 1) + return '%s/.libs/%s' % (a, b) + return m.group(1) + '.libs/' + m.group(2) + '.so' + + files = list(set([libtoolmustdie(fn) for fn in files])) + + def parse_nm_output(text): + filename = None + path_rel_to = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + for line in text.split('\n'): + if line.strip() == '': + continue + m = self.from_re.match(line) + if m is not None: + filename = m.group(1) + continue + if line.startswith('Name'): + continue + + items = [i.strip() for i in line.split('|')] + loc = None + if '\t' in items[-1]: + items[-1], loc = items[-1].split('\t', 1) + fn, lno = loc.rsplit(':', 1) + fn = os.path.relpath(fn, path_rel_to) + loc = '%s:%s' % (fn, lno) + + items[1] = int(items[1] if items[1] != '' else '0', 16) + items[4] = int(items[4] if items[4] != '' else '0', 16) + items.append(loc) + row = SymRow(target, filename, *items) + + if row.section == '.group' or row.name == '_GLOBAL_OFFSET_TABLE_': + continue + if not row.is_global(): + continue + + yield row + + visible_syms = set() + + # the actual symbol report uses output from the individual object files + # (e.g. lib/.libs/foo.o), but we also read the linked binary (e.g. + # lib/.libs/libfrr.so) to determine which symbols are actually visible + # in the linked result (this covers ELF "hidden"/"internal" linkage) + + libfile = libtooltargetmustdie(target) + nmlib = subprocess.Popen(['nm', '-l', '-g', '--defined-only', '-f', 'sysv', libfile], stdout = subprocess.PIPE) + out = nmlib.communicate()[0].decode('US-ASCII') + + for row in parse_nm_output(out): + visible_syms.add(row.name) + + nm = subprocess.Popen(['nm', '-l', '-f', 'sysv'] + files, stdout = subprocess.PIPE) + out = nm.communicate()[0].decode('US-ASCII') + + for row in parse_nm_output(out): + row.visible = row.name in visible_syms + sym = self.setdefault(row.name, self.Symbol(row.name)) + sym.process(row) + + +def write_html_report(syms): + try: + import jinja2 + except ImportError: + sys.stderr.write('jinja2 could not be imported, not writing HTML report!\n') + return + + self_path = os.path.dirname(os.path.abspath(__file__)) + jenv = jinja2.Environment(loader=jinja2.FileSystemLoader(self_path)) + template = jenv.get_template('symalyzer.html') + + dirgroups = {} + for fn, reports in syms.report.items(): + dirname, filename = fn.replace('.libs/', '').rsplit('/', 1) + dirgroups.setdefault(dirname, {})[fn] = reports + + klasses = { + 'T': 'code / plain old regular function (Text)', + 'D': 'global variable, read-write, with nonzero initializer (Data)', + 'B': 'global variable, read-write, with zero initializer (BSS)', + 'C': 'global variable, read-write, with zero initializer (Common)', + 'R': 'global variable, read-only (Rodata)', + } + + with open('symalyzer_report.html.tmp', 'w') as fd: + fd.write(template.render(dirgroups = dirgroups, klasses = klasses)) + os.rename('symalyzer_report.html.tmp', 'symalyzer_report.html') + + if not os.path.exists('jquery-3.4.1.min.js'): + url = 'https://code.jquery.com/jquery-3.4.1.min.js' + sys.stderr.write( + 'trying to grab a copy of jquery from %s\nif this fails, please get it manually (the HTML output is done.)\n' % (url)) + import requests + r = requests.get('https://code.jquery.com/jquery-3.4.1.min.js') + if r.status_code != 200: + sys.stderr.write('failed -- please download jquery-3.4.1.min.js and put it next to the HTML report\n') + else: + with open('jquery-3.4.1.min.js.tmp', 'w') as fd: + fd.write(r.text) + os.rename('jquery-3.4.1.min.js.tmp', 'jquery-3.4.1.min.js.tmp') + sys.stderr.write('done.\n') + +def automake_escape(s): + return s.replace('.', '_').replace('/', '_') + +if __name__ == '__main__': + mv = MakeVars() + + if not (os.path.exists('config.version') and os.path.exists('lib/.libs/libfrr.so')): + sys.stderr.write('please execute this script in the root directory of an FRR build tree\n') + sys.stderr.write('./configure && make need to have completed successfully\n') + sys.exit(1) + + amtargets = ['bin_PROGRAMS', 'sbin_PROGRAMS', 'lib_LTLIBRARIES', 'module_LTLIBRARIES'] + targets = [] + + mv.getvars(amtargets) + for amtarget in amtargets: + targets.extend([item for item in mv[amtarget].strip().split() if item != 'tools/ssd']) + + mv.getvars(['%s_LDADD' % automake_escape(t) for t in targets]) + ldobjs = targets[:] + for t in targets: + ldadd = mv['%s_LDADD' % automake_escape(t)].strip().split() + for item in ldadd: + if item.startswith('-'): + continue + if item.endswith('.a'): + ldobjs.append(item) + + mv.getvars(['%s_OBJECTS' % automake_escape(o) for o in ldobjs]) + + syms = Symbols() + + for t in targets: + objs = mv['%s_OBJECTS' % automake_escape(t)].strip().split() + ldadd = mv['%s_LDADD' % automake_escape(t)].strip().split() + for item in ldadd: + if item.startswith('-'): + continue + if item.endswith('.a'): + objs.extend(mv['%s_OBJECTS' % automake_escape(item)].strip().split()) + + sys.stderr.write('processing %s...\n' % t) + sys.stderr.flush() + #print(t, '\n\t', objs) + syms.load(t, objs) + + syms.evaluate() + + for obj, reports in sorted(syms.report.items()): + print('%s:' % obj) + for report in reports: + print('\t%r' % report) + + write_html_report(syms) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 42bb154f98..2acf985893 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -211,7 +211,16 @@ static struct vrrp_vrouter *vrrp_lookup_by_if_mvl(struct interface *mvl_ifp) return NULL; } - p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT); + p = if_lookup_by_index(mvl_ifp->link_ifindex, mvl_ifp->vrf_id); + + if (!p) { + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX + "Tried to lookup interface %d, parent of %s, but it doesn't exist", + mvl_ifp->link_ifindex, mvl_ifp->name); + return NULL; + } + uint8_t vrid = mvl_ifp->hw_addr[5]; return vrrp_lookup(p, vrid); @@ -525,8 +534,9 @@ static bool vrrp_attach_interface(struct vrrp_router *r) /* Search for existing interface with computed MAC address */ struct interface **ifps; - size_t ifps_cnt = if_lookup_by_hwaddr( - r->vmac.octet, sizeof(r->vmac.octet), &ifps, VRF_DEFAULT); + size_t ifps_cnt = + if_lookup_by_hwaddr(r->vmac.octet, sizeof(r->vmac.octet), &ifps, + r->vr->ifp->vrf_id); /* * Filter to only those macvlan interfaces whose parent is the base @@ -1048,6 +1058,8 @@ done: * * This function: * - Creates two sockets, one for Tx, one for Rx + * - Binds the Tx socket to the macvlan device, if necessary (VRF case) + * - Binds the Rx socket to the base interface * - Joins the Rx socket to the appropriate VRRP multicast group * - Sets the Tx socket to set the TTL (v4) or Hop Limit (v6) field to 255 for * all transmitted IPvX packets @@ -1074,8 +1086,10 @@ static int vrrp_socket(struct vrrp_router *r) bool failed = false; frr_with_privs(&vrrp_privs) { - r->sock_rx = socket(r->family, SOCK_RAW, IPPROTO_VRRP); - r->sock_tx = socket(r->family, SOCK_RAW, IPPROTO_VRRP); + r->sock_rx = vrf_socket(r->family, SOCK_RAW, IPPROTO_VRRP, + r->vr->ifp->vrf_id, NULL); + r->sock_tx = vrf_socket(r->family, SOCK_RAW, IPPROTO_VRRP, + r->vr->ifp->vrf_id, NULL); } if (r->sock_rx < 0 || r->sock_tx < 0) { @@ -1088,6 +1102,27 @@ static int vrrp_socket(struct vrrp_router *r) goto done; } + /* + * Bind Tx socket to macvlan device - necessary for VRF support, + * otherwise the kernel will select the vrf device + */ + if (r->vr->ifp->vrf_id != VRF_DEFAULT) { + frr_with_privs (&vrrp_privs) { + ret = setsockopt(r->sock_tx, SOL_SOCKET, + SO_BINDTODEVICE, r->mvl_ifp->name, + strlen(r->mvl_ifp->name)); + } + + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Tx socket to macvlan device '%s'", + r->vr->vrid, family2str(r->family), + r->mvl_ifp->name); + failed = true; + goto done; + } + } /* Configure sockets */ if (r->family == AF_INET) { /* Set Tx socket to always Tx with TTL set to 255 */ @@ -1722,7 +1757,7 @@ vrrp_autoconfig_autocreate(struct interface *mvl_ifp) struct interface *p; struct vrrp_vrouter *vr; - p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT); + p = if_lookup_by_index(mvl_ifp->link_ifindex, mvl_ifp->vrf_id); if (!p) return NULL; @@ -1999,11 +2034,13 @@ int vrrp_autoconfig(void) if (!vrrp_autoconfig_is_on) return 0; - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf; struct interface *ifp; - FOR_ALL_INTERFACES (vrf, ifp) - vrrp_autoconfig_if_add(ifp); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) + vrrp_autoconfig_if_add(ifp); + } return 0; } diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index a6c575f8da..000672a080 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -175,7 +175,7 @@ void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable) "Requesting Zebra to turn router advertisements %s for %s", r->vr->vrid, enable ? "on" : "off", r->mvl_ifp->name); - zclient_send_interface_radv_req(zclient, VRF_DEFAULT, r->mvl_ifp, + zclient_send_interface_radv_req(zclient, r->mvl_ifp->vrf_id, r->mvl_ifp, enable, VRRP_RADV_INT); } @@ -185,7 +185,7 @@ int vrrp_zclient_send_interface_protodown(struct interface *ifp, bool down) VRRP_LOGPFX "Requesting Zebra to set %s protodown %s", ifp->name, down ? "on" : "off"); - return zclient_send_interface_protodown(zclient, VRF_DEFAULT, ifp, + return zclient_send_interface_protodown(zclient, ifp->vrf_id, ifp, down); } diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index d0b0c701a7..13413888bf 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -96,14 +96,14 @@ sub scan_file { elsif ($file =~ /lib\/if\.c$/) { $protocol = "VTYSH_INTERFACE"; } - elsif ($file =~ /lib\/filter\.c$/) { + elsif ($file =~ /lib\/(filter|lib_vty)\.c$/) { $protocol = "VTYSH_ALL"; } elsif ($file =~ /lib\/agentx\.c$/) { $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA"; } elsif ($file =~ /lib\/nexthop_group\.c$/) { - $protocol = "VTYSH_PBRD | VTYSH_SHARPD"; + $protocol = "VTYSH_NH_GROUP"; } elsif ($file =~ /lib\/plist\.c$/) { if ($defun_array[1] =~ m/ipv6/) { diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 5c4e8a313b..b7d35caa39 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2163,7 +2163,8 @@ DEFUNSH(VTYSH_ZEBRA, vtysh_pseudowire, vtysh_pseudowire_cmd, return CMD_SUCCESS; } -DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_nexthop_group, vtysh_nexthop_group_cmd, +DEFUNSH(VTYSH_NH_GROUP, + vtysh_nexthop_group, vtysh_nexthop_group_cmd, "nexthop-group NHGNAME", "Nexthop Group configuration\n" "Name of the Nexthop Group\n") @@ -2172,7 +2173,7 @@ DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_nexthop_group, vtysh_nexthop_group_cmd, return CMD_SUCCESS; } -DEFSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_no_nexthop_group_cmd, +DEFSH(VTYSH_NH_GROUP, vtysh_no_nexthop_group_cmd, "no nexthop-group NHGNAME", NO_STR "Nexthop Group Configuration\n" @@ -2209,13 +2210,15 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit", return vtysh_exit_vrf(self, vty, argc, argv); } -DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, +DEFUNSH(VTYSH_NH_GROUP, + vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); } -DEFUNSH(VTYSH_PBRD | VTYSH_SHARPD, vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd, +DEFUNSH(VTYSH_NH_GROUP, + vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd, "quit", "Exit current mode and down to previous mode\n") { return vtysh_exit_nexthop_group(self, vty, argc, argv); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index b16761b41a..d0edbb2710 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -56,6 +56,8 @@ DECLARE_MGROUP(MVTYSH) #define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD #define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD #define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD +/* Daemons who can process nexthop-group configs */ +#define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD enum vtysh_write_integrated { WRITE_INTEGRATED_UNSPECIFIED, diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index e56c6fbf4e..27f4b0834d 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -262,6 +262,8 @@ void vtysh_config_parse_line(void *arg, const char *line) || !strncmp(line, " no vrrp", strlen(" no vrrp"))) { config_add_line(config->line, line); + } else if (!strncmp(line, " ip mroute", strlen(" ip mroute"))) { + config_add_line_uniq_end(config->line, line); } else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE || config->index == VTY_NODE diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index c7e1d252c7..0ba1b9d9c8 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -45,7 +45,6 @@ #include "command.h" #include "memory.h" #include "linklist.h" -#include "memory_vty.h" #include "libfrr.h" #include "ferr.h" #include "lib_errors.h" diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index 4e8502107a..ed9616963d 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -25,7 +25,6 @@ #include <sigevent.h> #include <lib/version.h> #include "command.h" -#include "memory_vty.h" #include "libfrr.h" #include "lib_errors.h" diff --git a/zebra/interface.c b/zebra/interface.c index 20b05dfb32..bcb833b8d8 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1977,6 +1977,8 @@ DEFUN (shutdown_if, struct zebra_if *if_data; if (ifp->ifindex != IFINDEX_INTERNAL) { + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv_stop_ra(ifp); ret = if_unset_flags(ifp, IFF_UP); if (ret < 0) { vty_out(vty, "Can't shutdown interface\n"); diff --git a/zebra/main.c b/zebra/main.c index 731c4e1614..75f825e507 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -27,7 +27,6 @@ #include "filter.h" #include "memory.h" #include "zebra_memory.h" -#include "memory_vty.h" #include "prefix.h" #include "log.h" #include "plist.h" @@ -145,6 +144,9 @@ static void sigint(void) atomic_store_explicit(&zrouter.in_shutdown, true, memory_order_relaxed); + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv_stop_ra_all(); + frr_early_fini(); zebra_dplane_pre_finish(); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 98603c9693..6aa52bcb61 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -637,13 +637,15 @@ int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, struct route_entry *newre; struct route_entry *same; struct prefix p; + struct nexthop_group *ng; route_map_result_t ret = RMAP_PERMITMATCH; afi_t afi; afi = family2afi(rn->p.family); if (rmap_name) ret = zebra_import_table_route_map_check( - afi, re->type, re->instance, &rn->p, re->ng->nexthop, + afi, re->type, re->instance, &rn->p, + re->nhe->nhg->nexthop, zvrf->vrf->vrf_id, re->tag, rmap_name); if (ret != RMAP_PERMITMATCH) { @@ -678,10 +680,11 @@ int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, newre->table = zvrf->table_id; newre->uptime = monotime(NULL); newre->instance = re->table; - newre->ng = nexthop_group_new(); - route_entry_copy_nexthops(newre, re->ng->nexthop); - rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre); + ng = nexthop_group_new(); + copy_nexthops(&ng->nexthop, re->nhe->nhg->nexthop, NULL); + + rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre, ng); return 0; } @@ -696,8 +699,9 @@ int zebra_del_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, prefix_copy(&p, &rn->p); rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_TABLE, - re->table, re->flags, &p, NULL, re->ng->nexthop, re->nhe_id, - zvrf->table_id, re->metric, re->distance, false); + re->table, re->flags, &p, NULL, re->nhe->nhg->nexthop, + re->nhe_id, zvrf->table_id, re->metric, re->distance, + false); return 0; } diff --git a/zebra/rib.h b/zebra/rib.h index 35aa011c0d..927675e3d9 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -35,6 +35,7 @@ #include "if.h" #include "mpls.h" #include "srcdest_table.h" +#include "zebra/zebra_nhg.h" #ifdef __cplusplus extern "C" { @@ -87,10 +88,14 @@ struct route_entry { /* Link list. */ struct re_list_item next; - /* Nexthop structure (from RIB) */ - struct nexthop_group *ng; + /* Nexthop group, shared/refcounted, based on the nexthop(s) + * provided by the owner of the route + */ + struct nhg_hash_entry *nhe; - /* Nexthop group from FIB (optional) */ + /* Nexthop group from FIB (optional), reflecting what is actually + * installed in the FIB if that differs. + */ struct nexthop_group fib_ng; /* Nexthop group hash entry ID */ @@ -307,33 +312,9 @@ typedef enum { RIB_UPDATE_MAX } rib_update_event_t; -extern struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *re, - ifindex_t ifindex, - vrf_id_t nh_vrf_id); -extern struct nexthop * -route_entry_nexthop_blackhole_add(struct route_entry *re, - enum blackhole_type bh_type); -extern struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *re, - struct in_addr *ipv4, - struct in_addr *src, - vrf_id_t nh_vrf_id); -extern struct nexthop * -route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re, - struct in_addr *ipv4, struct in_addr *src, - ifindex_t ifindex, vrf_id_t nh_vrf_id); -extern void route_entry_nexthop_delete(struct route_entry *re, - struct nexthop *nexthop); -extern struct nexthop *route_entry_nexthop_ipv6_add(struct route_entry *re, - struct in6_addr *ipv6, - vrf_id_t nh_vrf_id); -extern struct nexthop * -route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re, - struct in6_addr *ipv6, ifindex_t ifindex, - vrf_id_t nh_vrf_id); -extern void route_entry_nexthop_add(struct route_entry *re, - struct nexthop *nexthop); extern void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh); +int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new); #define route_entry_dump(prefix, src, re) _route_entry_dump(__func__, prefix, src, re) extern void _route_entry_dump(const char *func, union prefixconstptr pp, @@ -368,7 +349,8 @@ extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, uint32_t mtu, uint8_t distance, route_tag_t tag); extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, - struct prefix_ipv6 *src_p, struct route_entry *re); + struct prefix_ipv6 *src_p, struct route_entry *re, + struct nexthop_group *ng); extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, @@ -533,7 +515,7 @@ static inline struct nexthop_group *rib_active_nhg(struct route_entry *re) if (re->fib_ng.nexthop) return &(re->fib_ng); else - return re->ng; + return re->nhe->nhg; } extern void zebra_vty_init(void); diff --git a/zebra/router-id.c b/zebra/router-id.c index 569ffbab41..b37d4aea70 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -253,6 +253,36 @@ DEFUN (no_router_id, return CMD_SUCCESS; } +DEFUN (show_router_id, + show_router_id_cmd, + "show router-id [vrf NAME]", + SHOW_STR + "Show the configured router-id\n" + VRF_CMD_HELP_STR) +{ + int idx_name = 3; + + vrf_id_t vrf_id = VRF_DEFAULT; + struct zebra_vrf *zvrf; + + if (argc > 2) + VRF_GET_ID(vrf_id, argv[idx_name]->arg, false); + + zvrf = vrf_info_get(vrf_id); + + if ((zvrf != NULL) && (zvrf->rid_user_assigned.u.prefix4.s_addr)) { + vty_out(vty, "zebra:\n"); + if (vrf_id == VRF_DEFAULT) + vty_out(vty, " router-id %s vrf default\n", + inet_ntoa(zvrf->rid_user_assigned.u.prefix4)); + else + vty_out(vty, " router-id %s vrf %s\n", + inet_ntoa(zvrf->rid_user_assigned.u.prefix4), + argv[idx_name]->arg); + } + + return CMD_SUCCESS; +} static int router_id_cmp(void *a, void *b) { @@ -267,6 +297,7 @@ void router_id_cmd_init(void) { install_element(CONFIG_NODE, &router_id_cmd); install_element(CONFIG_NODE, &no_router_id_cmd); + install_element(VIEW_NODE, &show_router_id_cmd); } void router_id_init(struct zebra_vrf *zvrf) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 5e328b827f..fff569c092 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -390,7 +390,7 @@ parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb, } static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, - struct route_entry *re, + struct nexthop_group *ng, struct rtmsg *rtm, struct rtnexthop *rtnh, struct rtattr **tb, @@ -407,8 +407,6 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, int len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); vrf_id_t nh_vrf_id = vrf_id; - re->ng = nexthop_group_new(); - for (;;) { struct nexthop *nh = NULL; @@ -454,29 +452,32 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, if (gate && rtm->rtm_family == AF_INET) { if (index) - nh = route_entry_nexthop_ipv4_ifindex_add( - re, gate, prefsrc, index, nh_vrf_id); + nh = nexthop_from_ipv4_ifindex( + gate, prefsrc, index, nh_vrf_id); else - nh = route_entry_nexthop_ipv4_add( - re, gate, prefsrc, nh_vrf_id); + nh = nexthop_from_ipv4(gate, prefsrc, + nh_vrf_id); } else if (gate && rtm->rtm_family == AF_INET6) { if (index) - nh = route_entry_nexthop_ipv6_ifindex_add( - re, gate, index, nh_vrf_id); + nh = nexthop_from_ipv6_ifindex( + gate, index, nh_vrf_id); else - nh = route_entry_nexthop_ipv6_add(re, gate, - nh_vrf_id); + nh = nexthop_from_ipv6(gate, nh_vrf_id); } else - nh = route_entry_nexthop_ifindex_add(re, index, - nh_vrf_id); + nh = nexthop_from_ifindex(index, nh_vrf_id); if (nh) { + nh->weight = rtnh->rtnh_hops + 1; + if (num_labels) nexthop_add_labels(nh, ZEBRA_LSP_STATIC, num_labels, labels); if (rtnh->rtnh_flags & RTNH_F_ONLINK) SET_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK); + + /* Add to temporary list */ + nexthop_group_add_sorted(ng, nh); } if (rtnh->rtnh_len == 0) @@ -486,10 +487,7 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, rtnh = RTNH_NEXT(rtnh); } - uint8_t nhop_num = nexthop_group_nexthop_num(re->ng); - - if (!nhop_num) - nexthop_group_delete(&re->ng); + uint8_t nhop_num = nexthop_group_nexthop_num(ng); return nhop_num; } @@ -737,6 +735,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, } else { /* This is a multipath route */ struct route_entry *re; + struct nexthop_group *ng = NULL; struct rtnexthop *rtnh = (struct rtnexthop *)RTA_DATA(tb[RTA_MULTIPATH]); @@ -753,19 +752,30 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, re->nhe_id = nhe_id; if (!nhe_id) { - uint8_t nhop_num = + uint8_t nhop_num; + + /* Use temporary list of nexthops; parse + * message payload's nexthops. + */ + ng = nexthop_group_new(); + nhop_num = parse_multipath_nexthops_unicast( - ns_id, re, rtm, rtnh, tb, + ns_id, ng, rtm, rtnh, tb, prefsrc, vrf_id); zserv_nexthop_num_warn( __func__, (const struct prefix *)&p, nhop_num); + + if (nhop_num == 0) { + nexthop_group_delete(&ng); + ng = NULL; + } } - if (nhe_id || re->ng) + if (nhe_id || ng) rib_add_multipath(afi, SAFI_UNICAST, &p, - &src_p, re); + &src_p, re, ng); else XFREE(MTYPE_RE, re); } @@ -1411,6 +1421,9 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen, "nexthop via if %u", routedesc, nexthop->ifindex); } + + if (nexthop->weight) + rtnh->rtnh_hops = nexthop->weight - 1; } static inline void _netlink_mpls_build_singlepath(const char *routedesc, @@ -1913,7 +1926,7 @@ static void _netlink_nexthop_build_group(struct nlmsghdr *n, size_t req_size, if (count) { for (int i = 0; i < count; i++) { grp[i].id = z_grp[i].id; - grp[i].weight = z_grp[i].weight; + grp[i].weight = z_grp[i].weight - 1; if (IS_ZEBRA_DEBUG_KERNEL) { if (i == 0) @@ -2031,6 +2044,9 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx) addattr32(&req.n, req_size, NHA_OIF, nh->ifindex); + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK)) + req.nhm.nh_flags |= RTNH_F_ONLINK; + num_labels = build_label_stack(nh->nh_label, out_lse, label_buf, sizeof(label_buf)); @@ -2339,7 +2355,7 @@ static int netlink_nexthop_process_group(struct rtattr **tb, for (int i = 0; ((i < count) && (i < z_grp_size)); i++) { z_grp[i].id = n_grp[i].id; - z_grp[i].weight = n_grp[i].weight; + z_grp[i].weight = n_grp[i].weight + 1; } return count; } diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 0adf654aaf..5dd6012f62 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -166,7 +166,8 @@ static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf, #define RTADV_MSG_SIZE 4096 /* Send router advertisement packet. */ -static void rtadv_send_packet(int sock, struct interface *ifp) +static void rtadv_send_packet(int sock, struct interface *ifp, + ipv6_nd_suppress_ra_status stop) { struct msghdr msg; struct iovec iov; @@ -252,7 +253,10 @@ static void rtadv_send_packet(int sock, struct interface *ifp) zif->rtadv.AdvDefaultLifetime != -1 ? zif->rtadv.AdvDefaultLifetime : MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval); - rtadv->nd_ra_router_lifetime = htons(pkt_RouterLifetime); + + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv->nd_ra_router_lifetime = + (stop == RA_SUPPRESS) ? htons(0) : htons(pkt_RouterLifetime); rtadv->nd_ra_reachable = htonl(zif->rtadv.AdvReachableTime); rtadv->nd_ra_retransmit = htonl(0); @@ -512,7 +516,7 @@ static int rtadv_timer(struct thread *thread) ifp->name); rtadv_send_packet(rtadv_get_socket(zvrf), - ifp); + ifp, RA_ENABLE); } else { zif->rtadv.AdvIntervalTimer -= period; if (zif->rtadv.AdvIntervalTimer <= 0) { @@ -526,7 +530,7 @@ static int rtadv_timer(struct thread *thread) .MaxRtrAdvInterval; rtadv_send_packet( rtadv_get_socket(zvrf), - ifp); + ifp, RA_ENABLE); } } } @@ -556,7 +560,7 @@ static void rtadv_process_solicit(struct interface *ifp) if ((zif->rtadv.UseFastRexmit) || (zif->rtadv.AdvIntervalTimer <= (zif->rtadv.MaxRtrAdvInterval - MIN_DELAY_BETWEEN_RAS))) { - rtadv_send_packet(rtadv_get_socket(zvrf), ifp); + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_ENABLE); zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; } else zif->rtadv.AdvIntervalTimer = MIN_DELAY_BETWEEN_RAS; @@ -911,6 +915,8 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, if (status == RA_SUPPRESS) { /* RA is currently enabled */ if (zif->rtadv.AdvSendAdvertisements) { + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, + RA_SUPPRESS); zif->rtadv.AdvSendAdvertisements = 0; zif->rtadv.AdvIntervalTimer = 0; zvrf->rtadv.adv_if_count--; @@ -1012,6 +1018,38 @@ stream_failure: return; } +/* + * send router lifetime value of zero in RAs on this interface since we're + * ceasing to advertise and want to let our neighbors know. + * RFC 4861 secion 6.2.5 + */ +void rtadv_stop_ra(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vrf *zvrf; + + zif = ifp->info; + zvrf = vrf_info_lookup(ifp->vrf_id); + + if (zif->rtadv.AdvSendAdvertisements) + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_SUPPRESS); +} + +/* + * send router lifetime value of zero in RAs on all interfaces since we're + * ceasing to advertise globally and want to let all of our neighbors know + * RFC 4861 secion 6.2.5 + */ +void rtadv_stop_ra_all(void) +{ + struct vrf *vrf; + struct interface *ifp; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) + FOR_ALL_INTERFACES (vrf, ifp) + rtadv_stop_ra(ifp); +} + void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS) { zebra_interface_radv_set(client, hdr, msg, zvrf, 0); diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 409959d08d..63cec94434 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -140,6 +140,8 @@ typedef enum { extern void rtadv_init(struct zebra_vrf *zvrf); extern void rtadv_terminate(struct zebra_vrf *zvrf); +extern void rtadv_stop_ra(struct interface *ifp); +extern void rtadv_stop_ra_all(void); extern void rtadv_cmd_init(void); extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS); extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS); diff --git a/zebra/subdir.am b/zebra/subdir.am index d0f32d6a14..916f6cb5d1 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -10,7 +10,7 @@ vtysh_scan += \ $(top_srcdir)/zebra/interface.c \ $(top_srcdir)/zebra/router-id.c \ $(top_srcdir)/zebra/rtadv.c \ - $(top_srcdir)/zebra/zebra_mlag.c \ + $(top_srcdir)/zebra/zebra_mlag_vty.c \ $(top_srcdir)/zebra/zebra_mpls_vty.c \ $(top_srcdir)/zebra/zebra_ptm.c \ $(top_srcdir)/zebra/zebra_pw.c \ @@ -32,13 +32,16 @@ endif if FPM module_LTLIBRARIES += zebra/zebra_fpm.la endif +if LINUX +module_LTLIBRARIES += zebra/zebra_cumulus_mlag.la +endif man8 += $(MANBUILD)/zebra.8 ## endif ZEBRA endif zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP) -if HAVE_PROTOBUF +if HAVE_PROTOBUF3 zebra_zebra_LDADD += mlag/libmlag_pb.la $(PROTOBUF_C_LIBS) endif zebra_zebra_SOURCES = \ @@ -69,7 +72,7 @@ zebra_zebra_SOURCES = \ zebra/rule_netlink.c \ zebra/rule_socket.c \ zebra/zebra_mlag.c \ - zebra/zebra_mlag_private.c \ + zebra/zebra_mlag_vty.c \ zebra/zebra_l2.c \ zebra/zebra_memory.c \ zebra/zebra_dplane.c \ @@ -103,8 +106,8 @@ zebra_zebra_SOURCES = \ zebra/debug_clippy.c: $(CLIPPY_DEPS) zebra/debug.$(OBJEXT): zebra/debug_clippy.c -zebra/zebra_mlag_clippy.c: $(CLIPPY_DEPS) -zebra/zebra_mlag.$(OBJEXT): zebra/zebra_mlag_clippy.c +zebra/zebra_mlag_vty_clippy.c: $(CLIPPY_DEPS) +zebra/zebra_mlag_vty.$(OBJEXT): zebra/zebra_mlag_vty_clippy.c zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) zebra/interface_clippy.c: $(CLIPPY_DEPS) @@ -134,7 +137,7 @@ noinst_HEADERS += \ zebra/rtadv.h \ zebra/rule_netlink.h \ zebra/zebra_mlag.h \ - zebra/zebra_mlag_private.h \ + zebra/zebra_mlag_vty.h \ zebra/zebra_fpm_private.h \ zebra/zebra_l2.h \ zebra/zebra_dplane.h \ @@ -185,3 +188,6 @@ if DEV_BUILD zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_dt.c endif endif + +zebra_zebra_cumulus_mlag_la_SOURCES = zebra/zebra_mlag_private.c +zebra_zebra_cumulus_mlag_la_LDFLAGS = -avoid-version -module -shared -export-dynamic diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index e6a06ff95d..1dbe41f462 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -559,13 +559,15 @@ int zsend_redistribute_route(int cmd, struct zserv *client, memcpy(&api.src_prefix, src_p, sizeof(api.src_prefix)); } - for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->nhe->nhg->nexthop; + nexthop; nexthop = nexthop->next) { if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) continue; api_nh = &api.nexthops[count]; api_nh->vrf_id = nexthop->vrf_id; api_nh->type = nexthop->type; + api_nh->weight = nexthop->weight; switch (nexthop->type) { case NEXTHOP_TYPE_BLACKHOLE: api_nh->bh_type = nexthop->bh_type; @@ -666,7 +668,7 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client, * nexthop we are looking up. Therefore, we will just iterate * over the top chain of nexthops. */ - for (nexthop = re->ng->nexthop; nexthop; + for (nexthop = re->nhe->nhg->nexthop; nexthop; nexthop = nexthop->next) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) num += zserv_encode_nexthop(s, nexthop); @@ -1393,6 +1395,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) struct prefix_ipv6 *src_p = NULL; struct route_entry *re; struct nexthop *nexthop = NULL; + struct nexthop_group *ng = NULL; int i, ret; vrf_id_t vrf_id = 0; struct ipaddr vtep_ip; @@ -1409,11 +1412,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) char buf_prefix[PREFIX_STRLEN]; prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: p=%s, ZAPI_MESSAGE_LABEL: %sset, flags=0x%x", - __func__, buf_prefix, - (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL) ? "" - : "un"), - api.flags); + zlog_debug("%s: p=%s, flags=0x%x", + __func__, buf_prefix, api.flags); } /* Allocate new route. */ @@ -1424,7 +1424,6 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) re->flags = api.flags; re->uptime = monotime(NULL); re->vrf_id = vrf_id; - re->ng = nexthop_group_new(); if (api.tableid) re->table = api.tableid; @@ -1438,11 +1437,13 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) __func__, &api.prefix, zebra_route_string(client->proto)); - nexthop_group_delete(&re->ng); XFREE(MTYPE_RE, re); return; } + /* Use temporary list of nexthops */ + ng = nexthop_group_new(); + /* * TBD should _all_ of the nexthop add operations use * api_nh->vrf_id instead of re->vrf_id ? I only changed @@ -1457,8 +1458,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) switch (api_nh->type) { case NEXTHOP_TYPE_IFINDEX: - nexthop = route_entry_nexthop_ifindex_add( - re, api_nh->ifindex, api_nh->vrf_id); + nexthop = nexthop_from_ifindex(api_nh->ifindex, + api_nh->vrf_id); break; case NEXTHOP_TYPE_IPV4: if (IS_ZEBRA_DEBUG_RECV) { @@ -1469,8 +1470,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) zlog_debug("%s: nh=%s, vrf_id=%d", __func__, nhbuf, api_nh->vrf_id); } - nexthop = route_entry_nexthop_ipv4_add( - re, &api_nh->gate.ipv4, NULL, api_nh->vrf_id); + nexthop = nexthop_from_ipv4(&api_nh->gate.ipv4, + NULL, api_nh->vrf_id); break; case NEXTHOP_TYPE_IPV4_IFINDEX: @@ -1486,8 +1487,8 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) __func__, nhbuf, api_nh->vrf_id, re->vrf_id, ifindex); } - nexthop = route_entry_nexthop_ipv4_ifindex_add( - re, &api_nh->gate.ipv4, NULL, ifindex, + nexthop = nexthop_from_ipv4_ifindex( + &api_nh->gate.ipv4, NULL, ifindex, api_nh->vrf_id); /* Special handling for IPv4 routes sourced from EVPN: @@ -1504,15 +1505,15 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) } break; case NEXTHOP_TYPE_IPV6: - nexthop = route_entry_nexthop_ipv6_add( - re, &api_nh->gate.ipv6, api_nh->vrf_id); + nexthop = nexthop_from_ipv6(&api_nh->gate.ipv6, + api_nh->vrf_id); break; case NEXTHOP_TYPE_IPV6_IFINDEX: memset(&vtep_ip, 0, sizeof(struct ipaddr)); ifindex = api_nh->ifindex; - nexthop = route_entry_nexthop_ipv6_ifindex_add( - re, &api_nh->gate.ipv6, ifindex, - api_nh->vrf_id); + nexthop = nexthop_from_ipv6_ifindex(&api_nh->gate.ipv6, + ifindex, + api_nh->vrf_id); /* Special handling for IPv6 routes sourced from EVPN: * the nexthop and associated MAC need to be installed. @@ -1527,8 +1528,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) } break; case NEXTHOP_TYPE_BLACKHOLE: - nexthop = route_entry_nexthop_blackhole_add( - re, api_nh->bh_type); + nexthop = nexthop_from_blackhole(api_nh->bh_type); break; } @@ -1537,15 +1537,19 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) EC_ZEBRA_NEXTHOP_CREATION_FAILED, "%s: Nexthops Specified: %d but we failed to properly create one", __PRETTY_FUNCTION__, api.nexthop_num); - nexthop_group_delete(&re->ng); + nexthop_group_delete(&ng); XFREE(MTYPE_RE, re); return; } - if (api_nh->onlink) + + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT)) + nexthop->weight = api_nh->weight; + /* MPLS labels for BGP-LU or Segment Routing */ - if (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL) + if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX && api_nh->type != NEXTHOP_TYPE_BLACKHOLE) { enum lsp_types_t label_type; @@ -1563,6 +1567,9 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) api_nh->label_num, &api_nh->labels[0]); } + + /* Add new nexthop to temporary list */ + nexthop_group_add_sorted(ng, nexthop); } if (CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE)) @@ -1579,14 +1586,14 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) flog_warn(EC_ZEBRA_RX_SRCDEST_WRONG_AFI, "%s: Received SRC Prefix but afi is not v6", __PRETTY_FUNCTION__); - nexthop_group_delete(&re->ng); + nexthop_group_delete(&ng); XFREE(MTYPE_RE, re); return; } if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) src_p = &api.src_prefix; - ret = rib_add_multipath(afi, api.safi, &api.prefix, src_p, re); + ret = rib_add_multipath(afi, api.safi, &api.prefix, src_p, re, ng); /* Stats */ switch (api.prefix.family) { diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index aecadfadf8..bf1ba522a3 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -1023,6 +1023,11 @@ uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx) return ctx->u.rinfo.zd_old_distance; } +/* + * Set the nexthops associated with a context: note that processing code + * may well expect that nexthops are in canonical (sorted) order, so we + * will enforce that here. + */ void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh) { DPLANE_CTX_VALID(ctx); @@ -1031,7 +1036,7 @@ void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh) nexthops_free(ctx->u.rinfo.zd_ng.nexthop); ctx->u.rinfo.zd_ng.nexthop = NULL; } - copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), nh, NULL); + nexthop_group_copy_nh_sorted(&(ctx->u.rinfo.zd_ng), nh); } const struct nexthop_group *dplane_ctx_get_ng( @@ -1508,7 +1513,8 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, ctx->u.rinfo.zd_safi = info->safi; /* Copy nexthops; recursive info is included too */ - copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->ng->nexthop, NULL); + copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), + re->nhe->nhg->nexthop, NULL); /* Ensure that the dplane's nexthops flags are clear. */ for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop)) @@ -1748,7 +1754,7 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, if (re) copy_nexthops(&(ctx->u.pw.nhg.nexthop), - re->ng->nexthop, NULL); + re->nhe->nhg->nexthop, NULL); route_unlock_node(rn); } @@ -1844,7 +1850,7 @@ dplane_route_update_internal(struct route_node *rn, * We'll need these to do per-nexthop deletes. */ copy_nexthops(&(ctx->u.rinfo.zd_old_ng.nexthop), - old_re->ng->nexthop, NULL); + old_re->nhe->nhg->nexthop, NULL); #endif /* !HAVE_NETLINK */ } @@ -2507,7 +2513,7 @@ enum zebra_dplane_result dplane_neigh_add(const struct interface *ifp, enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, - ifp, mac, ip, flags, 0); + ifp, mac, ip, flags, DPLANE_NUD_NOARP); return result; } @@ -2813,6 +2819,7 @@ int dplane_provider_register(const char *name, TAILQ_INIT(&(p->dp_ctx_in_q)); TAILQ_INIT(&(p->dp_ctx_out_q)); + p->dp_flags = flags; p->dp_priority = prio; p->dp_fp = fp; p->dp_start = start_fp; diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c index 5a0905d591..ef792d14c2 100644 --- a/zebra/zebra_errors.c +++ b/zebra/zebra_errors.c @@ -316,6 +316,12 @@ static struct log_ref ferr_zebra_err[] = { .description = "Zebra attempted to look up a interface for a particular vrf_id and interface index, but didn't find anything.", .suggestion = "If you entered a command to trigger this error, make sure you entered the arguments correctly. Check your config file for any potential errors. If these look correct, seek help.", }, + { + .code = EC_ZEBRA_NS_NO_DEFAULT, + .title = "Zebra NameSpace failed to find Default", + .description = "Zebra NameSpace subsystem failed to find a Default namespace during initialization.", + .suggestion = "Open an Issue with all relevant log files and restart FRR", + }, /* Warnings */ { .code = EC_ZEBRAING_LM_PROTO_MISMATCH, diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h index f9ccc2db28..4625a03ae6 100644 --- a/zebra/zebra_errors.h +++ b/zebra/zebra_errors.h @@ -76,6 +76,7 @@ enum zebra_log_refs { EC_ZEBRA_NHG_SYNC, EC_ZEBRA_NHG_FIB_UPDATE, EC_ZEBRA_IF_LOOKUP_FAILED, + EC_ZEBRA_NS_NO_DEFAULT, /* warnings */ EC_ZEBRA_NS_NOTIFY_READ, EC_ZEBRAING_LM_PROTO_MISMATCH, diff --git a/zebra/zebra_fpm_dt.c b/zebra/zebra_fpm_dt.c index debcf60ee5..389781d4f7 100644 --- a/zebra/zebra_fpm_dt.c +++ b/zebra/zebra_fpm_dt.c @@ -90,7 +90,7 @@ static int zfpm_dt_find_route(rib_dest_t **dest_p, struct route_entry **re_p) if (!re) continue; - if (nexthop_group_active_nexthop_num(re->ng) == 0) + if (nexthop_group_active_nexthop_num(re->nhe->nhg) == 0) continue; *dest_p = dest; diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index b54d8fbc12..7786dc246c 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -314,7 +314,7 @@ static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd, ri->rtm_type = RTN_UNICAST; ri->metric = &re->metric; - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) { if (ri->num_nhs >= zrouter.multipath_num) break; diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c index a11517ab8b..c09fa1c65d 100644 --- a/zebra/zebra_fpm_protobuf.c +++ b/zebra/zebra_fpm_protobuf.c @@ -173,7 +173,7 @@ static Fpm__AddRoute *create_add_route_message(qpb_allocator_t *allocator, * Figure out the set of nexthops to be added to the message. */ num_nhs = 0; - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) { if (num_nhs >= zrouter.multipath_num) break; diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index 1a911e429f..f6bd5f4586 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -27,15 +27,18 @@ #include "mlag.h" #include "zebra/zebra_mlag.h" -#include "zebra/zebra_mlag_private.h" +#include "zebra/zebra_mlag_vty.h" #include "zebra/zebra_router.h" #include "zebra/zebra_memory.h" #include "zebra/zapi_msg.h" #include "zebra/debug.h" -#ifndef VTYSH_EXTRACT_PL -#include "zebra/zebra_mlag_clippy.c" -#endif +DEFINE_HOOK(zebra_mlag_private_write_data, + (uint8_t *data, uint32_t len), (data, len)) +DEFINE_HOOK(zebra_mlag_private_monitor_state, (), ()) +DEFINE_HOOK(zebra_mlag_private_open_channel, (), ()) +DEFINE_HOOK(zebra_mlag_private_close_channel, (), ()) +DEFINE_HOOK(zebra_mlag_private_cleanup_data, (), ()) #define ZEBRA_MLAG_METADATA_LEN 4 #define ZEBRA_MLAG_MSG_BCAST 0xFFFFFFFF @@ -175,7 +178,8 @@ static int zebra_mlag_client_msg_handler(struct thread *event) * write to MCLAGD */ if (len > 0) { - zebra_mlag_private_write_data(mlag_wr_buffer, len); + hook_call(zebra_mlag_private_write_data, + mlag_wr_buffer, len); /* * If message type is De-register, send a signal to main @@ -220,7 +224,7 @@ void zebra_mlag_handle_process_state(enum zebra_mlag_state state) } else if (state == MLAG_DOWN) { zrouter.mlag_info.connected = false; zebra_mlag_publish_process_state(NULL, ZEBRA_MLAG_PROCESS_DOWN); - zebra_mlag_private_monitor_state(); + hook_call(zebra_mlag_private_monitor_state); } } @@ -412,7 +416,7 @@ static int zebra_mlag_terminate_pthread(struct thread *event) /* * Send Notification to clean private data */ - zebra_mlag_private_cleanup_data(); + hook_call(zebra_mlag_private_cleanup_data); return 0; } @@ -470,7 +474,7 @@ void zebra_mlag_client_register(ZAPI_HANDLER_ARGS) "First client, opening the channel with MLAG"); zebra_mlag_spawn_pthread(); - rc = zebra_mlag_private_open_channel(); + rc = hook_call(zebra_mlag_private_open_channel); if (rc < 0) { /* * For some reason, zebra not able to open the @@ -530,7 +534,7 @@ void zebra_mlag_client_unregister(ZAPI_HANDLER_ARGS) * signal back to main thread to do the thread cleanup * this was mainly to make sure De-register is posted to MCLAGD. */ - zebra_mlag_private_close_channel(); + hook_call(zebra_mlag_private_close_channel); } if (IS_ZEBRA_DEBUG_MLAG) @@ -579,29 +583,8 @@ enum mlag_role zebra_mlag_get_role(void) return zrouter.mlag_info.role; } -DEFUN_HIDDEN (show_mlag, - show_mlag_cmd, - "show zebra mlag", - SHOW_STR - ZEBRA_STR - "The mlag role on this machine\n") -{ - char buf[MLAG_ROLE_STRSIZE]; - - vty_out(vty, "MLag is configured to: %s\n", - mlag_role2str(zrouter.mlag_info.role, buf, sizeof(buf))); - - return CMD_SUCCESS; -} - -DEFPY_HIDDEN(test_mlag, test_mlag_cmd, - "test zebra mlag <none$none|primary$primary|secondary$secondary>", - "Test code\n" - ZEBRA_STR - "Modify the Mlag state\n" - "Mlag is not setup on the machine\n" - "Mlag is setup to be primary\n" - "Mlag is setup to be the secondary\n") +int32_t zebra_mlag_test_mlag_internal(const char *none, const char *primary, + const char *secondary) { enum mlag_role orig = zrouter.mlag_info.role; char buf1[MLAG_ROLE_STRSIZE], buf2[MLAG_ROLE_STRSIZE]; @@ -627,13 +610,13 @@ DEFPY_HIDDEN(test_mlag, test_mlag_cmd, zebra_mlag_spawn_pthread(); zrouter.mlag_info.clients_interested_cnt++; test_mlag_in_progress = true; - zebra_mlag_private_open_channel(); + hook_call(zebra_mlag_private_open_channel); } } else { if (test_mlag_in_progress == true) { test_mlag_in_progress = false; zrouter.mlag_info.clients_interested_cnt--; - zebra_mlag_private_close_channel(); + hook_call(zebra_mlag_private_close_channel); } } } @@ -643,8 +626,7 @@ DEFPY_HIDDEN(test_mlag, test_mlag_cmd, void zebra_mlag_init(void) { - install_element(VIEW_NODE, &show_mlag_cmd); - install_element(ENABLE_NODE, &test_mlag_cmd); + zebra_mlag_vty_init(); /* * Intialiaze the MLAG Global variables @@ -672,7 +654,7 @@ void zebra_mlag_terminate(void) * ProtoBuf Encoding APIs */ -#ifdef HAVE_PROTOBUF +#ifdef HAVE_PROTOBUF_VERSION_3 DEFINE_MTYPE_STATIC(ZEBRA, MLAG_PBUF, "ZEBRA MLAG PROTOBUF") diff --git a/zebra/zebra_mlag.h b/zebra/zebra_mlag.h index 6f7ef8319f..c35fa15561 100644 --- a/zebra/zebra_mlag.h +++ b/zebra/zebra_mlag.h @@ -26,13 +26,20 @@ #include "zclient.h" #include "zebra/zserv.h" -#ifdef HAVE_PROTOBUF +#ifdef HAVE_PROTOBUF_VERSION_3 #include "mlag/mlag.pb-c.h" #endif #define ZEBRA_MLAG_BUF_LIMIT 2048 #define ZEBRA_MLAG_LEN_SIZE 4 +DECLARE_HOOK(zebra_mlag_private_write_data, + (uint8_t *data, uint32_t len), (data, len)) +DECLARE_HOOK(zebra_mlag_private_monitor_state, (), ()) +DECLARE_HOOK(zebra_mlag_private_open_channel, (), ()) +DECLARE_HOOK(zebra_mlag_private_close_channel, (), ()) +DECLARE_HOOK(zebra_mlag_private_cleanup_data, (), ()) + extern uint8_t mlag_wr_buffer[ZEBRA_MLAG_BUF_LIMIT]; extern uint8_t mlag_rd_buffer[ZEBRA_MLAG_BUF_LIMIT]; extern uint32_t mlag_rd_buf_offset; @@ -57,6 +64,7 @@ void zebra_mlag_send_register(void); void zebra_mlag_send_deregister(void); void zebra_mlag_handle_process_state(enum zebra_mlag_state state); void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len); + /* * ProtoBuffer Api's */ diff --git a/zebra/zebra_mlag_private.c b/zebra/zebra_mlag_private.c index 4df7b6dd11..3024407ada 100644 --- a/zebra/zebra_mlag_private.c +++ b/zebra/zebra_mlag_private.c @@ -36,7 +36,6 @@ #include "zebra/debug.h" #include "zebra/zebra_router.h" #include "zebra/zebra_mlag.h" -#include "zebra/zebra_mlag_private.h" #include <sys/un.h> @@ -46,8 +45,6 @@ * */ -#ifdef HAVE_CUMULUS - static struct thread_master *zmlag_master; static int mlag_socket; @@ -57,7 +54,7 @@ static int zebra_mlag_read(struct thread *thread); /* * Write the data to MLAGD */ -int zebra_mlag_private_write_data(uint8_t *data, uint32_t len) +static int zebra_mlag_private_write_data(uint8_t *data, uint32_t len) { int rc = 0; @@ -103,7 +100,7 @@ static int zebra_mlag_read(struct thread *thread) return -1; } mlag_rd_buf_offset += data_len; - if (data_len != (ssize_t)ZEBRA_MLAG_LEN_SIZE - curr_len) { + if (data_len != (ssize_t)(ZEBRA_MLAG_LEN_SIZE - curr_len)) { /* Try again later */ zebra_mlag_sched_read(); return 0; @@ -132,7 +129,7 @@ static int zebra_mlag_read(struct thread *thread) return -1; } mlag_rd_buf_offset += data_len; - if (data_len != (ssize_t)tot_len - curr_len) { + if (data_len != (ssize_t)(tot_len - curr_len)) { /* Try again later */ zebra_mlag_sched_read(); return 0; @@ -207,13 +204,14 @@ static int zebra_mlag_connect(struct thread *thread) /* * Currently we are doing polling later we will look for better options */ -void zebra_mlag_private_monitor_state(void) +static int zebra_mlag_private_monitor_state(void) { thread_add_event(zmlag_master, zebra_mlag_connect, NULL, 0, &zrouter.mlag_info.t_read); + return 0; } -int zebra_mlag_private_open_channel(void) +static int zebra_mlag_private_open_channel(void) { zmlag_master = zrouter.mlag_info.th_master; @@ -242,7 +240,7 @@ int zebra_mlag_private_open_channel(void) return 0; } -int zebra_mlag_private_close_channel(void) +static int zebra_mlag_private_close_channel(void) { if (zmlag_master == NULL) return -1; @@ -263,37 +261,34 @@ int zebra_mlag_private_close_channel(void) return 0; } -void zebra_mlag_private_cleanup_data(void) +static int zebra_mlag_private_cleanup_data(void) { zmlag_master = NULL; zrouter.mlag_info.connected = false; zrouter.mlag_info.timer_running = false; close(mlag_socket); -} - -#else /*HAVE_CUMULUS */ - -int zebra_mlag_private_write_data(uint8_t *data, uint32_t len) -{ return 0; } -void zebra_mlag_private_monitor_state(void) -{ -} - -int zebra_mlag_private_open_channel(void) +static int zebra_mlag_module_init(void) { + hook_register(zebra_mlag_private_write_data, + zebra_mlag_private_write_data); + hook_register(zebra_mlag_private_monitor_state, + zebra_mlag_private_monitor_state); + hook_register(zebra_mlag_private_open_channel, + zebra_mlag_private_open_channel); + hook_register(zebra_mlag_private_close_channel, + zebra_mlag_private_close_channel); + hook_register(zebra_mlag_private_cleanup_data, + zebra_mlag_private_cleanup_data); return 0; } -int zebra_mlag_private_close_channel(void) -{ - return 0; -} - -void zebra_mlag_private_cleanup_data(void) -{ -} -#endif /*HAVE_CUMULUS*/ +FRR_MODULE_SETUP( + .name = "zebra_cumulus_mlag", + .version = FRR_VERSION, + .description = "zebra Cumulus MLAG interface", + .init = zebra_mlag_module_init, +) diff --git a/zebra/zebra_mlag_private.h b/zebra/zebra_mlag_private.h deleted file mode 100644 index f7b68e9ba0..0000000000 --- a/zebra/zebra_mlag_private.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This is an implementation of MLAG Functionality - * - * Module name: Zebra MLAG - * - * Author: sathesh Kumar karra <sathk@cumulusnetworks.com> - * - * Copyright (C) 2019 Cumulus Networks http://www.cumulusnetworks.com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __ZEBRA_MLAG_PRIVATE_H__ -#define __ZEBRA_MLAG_PRIVATE_H__ - - -/* - * all the platform specific API's - */ - -int zebra_mlag_private_open_channel(void); -int zebra_mlag_private_close_channel(void); -void zebra_mlag_private_monitor_state(void); -int zebra_mlag_private_write_data(uint8_t *data, uint32_t len); -void zebra_mlag_private_cleanup_data(void); -#endif diff --git a/zebra/zebra_mlag_vty.c b/zebra/zebra_mlag_vty.c new file mode 100644 index 0000000000..ebaaf03dab --- /dev/null +++ b/zebra/zebra_mlag_vty.c @@ -0,0 +1,67 @@ +/* Zebra Mlag vty Code. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include <zebra.h> + +#include "vty.h" +#include "command.h" + +#include "zebra_router.h" +#include "zebra_mlag_vty.h" +#include "debug.h" +#include "zapi_msg.h" + +#ifndef VTYSH_EXTRACT_PL +#include "zebra/zebra_mlag_vty_clippy.c" +#endif + +DEFUN_HIDDEN (show_mlag, + show_mlag_cmd, + "show zebra mlag", + SHOW_STR + ZEBRA_STR + "The mlag role on this machine\n") +{ + char buf[MLAG_ROLE_STRSIZE]; + + vty_out(vty, "MLag is configured to: %s\n", + mlag_role2str(zrouter.mlag_info.role, buf, sizeof(buf))); + + return CMD_SUCCESS; +} + +DEFPY_HIDDEN(test_mlag, test_mlag_cmd, + "test zebra mlag <none$none|primary$primary|secondary$secondary>", + "Test code\n" + ZEBRA_STR + "Modify the Mlag state\n" + "Mlag is not setup on the machine\n" + "Mlag is setup to be primary\n" + "Mlag is setup to be the secondary\n") +{ + return zebra_mlag_test_mlag_internal(none, primary, secondary); +} + +void zebra_mlag_vty_init(void) +{ + install_element(VIEW_NODE, &show_mlag_cmd); + install_element(ENABLE_NODE, &test_mlag_cmd); +} diff --git a/zebra/zebra_mlag_vty.h b/zebra/zebra_mlag_vty.h new file mode 100644 index 0000000000..c3dfdf74e0 --- /dev/null +++ b/zebra/zebra_mlag_vty.h @@ -0,0 +1,31 @@ +/* Zebra Mlag vty Code. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef __ZEBRA_MLAG_VTY_CODE__ +#define __ZEBRA_MLAG_VTY_CODE__ + +extern int32_t zebra_mlag_test_mlag_internal(const char *none, + const char *primary, + const char *secondary); + +extern void zebra_mlag_vty_init(void); + +#endif diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 5146311c6a..d5d424732a 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -186,7 +186,8 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, * the label advertised by the recursive nexthop (plus we don't have the * logic yet to push multiple labels). */ - for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next) { + for (nexthop = re->nhe->nhg->nexthop; + nexthop; nexthop = nexthop->next) { /* Skip inactive and recursive entries. */ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) continue; @@ -637,7 +638,7 @@ static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe, || !CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED)) continue; - for (match_nh = match->ng->nexthop; match_nh; + for (match_nh = match->nhe->nhg->nexthop; match_nh; match_nh = match_nh->next) { if (match->type == ZEBRA_ROUTE_CONNECT || nexthop->ifindex == match_nh->ifindex) { @@ -688,10 +689,10 @@ static int nhlfe_nexthop_active_ipv6(zebra_nhlfe_t *nhlfe, break; } - if (!match || !match->ng->nexthop) + if (!match || !match->nhe->nhg->nexthop) return 0; - nexthop->ifindex = match->ng->nexthop->ifindex; + nexthop->ifindex = match->nhe->nhg->nexthop->ifindex; return 1; } @@ -2577,7 +2578,7 @@ static void mpls_zebra_nhg_update(struct route_entry *re, afi_t afi, nhe = zebra_nhg_rib_find(0, new_grp, afi); - zebra_nhg_re_update_ref(re, nhe); + route_entry_update_nhe(re, nhe); } static bool mpls_ftn_update_nexthop(int add, struct nexthop *nexthop, @@ -2631,7 +2632,7 @@ int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type, * We can't just change the values here since we are hashing * on labels. We need to create a whole new group */ - nexthop_group_copy(&new_grp, re->ng); + nexthop_group_copy(&new_grp, re->nhe->nhg); found = false; for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) { @@ -2712,7 +2713,7 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, if (re == NULL) return -1; - nexthop_group_copy(&new_grp, re->ng); + nexthop_group_copy(&new_grp, re->nhe->nhg); for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) nexthop_del_labels(nexthop); @@ -2949,7 +2950,7 @@ static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf, RNODE_FOREACH_RE (rn, re) { struct nexthop_group new_grp = {}; - nexthop_group_copy(&new_grp, re->ng); + nexthop_group_copy(&new_grp, re->nhe->nhg); for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) { diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index f06ff44f20..4f41406a5c 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -49,7 +49,8 @@ DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context"); /* id counter to keep in sync with kernel */ uint32_t id_counter; -static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi); +static struct nhg_hash_entry *depends_find(const struct nexthop *nh, + afi_t afi); static void depends_add(struct nhg_connected_tree_head *head, struct nhg_hash_entry *depend); static struct nhg_hash_entry * @@ -299,13 +300,22 @@ zebra_nhg_connect_depends(struct nhg_hash_entry *nhe, } } -static struct nhg_hash_entry *zebra_nhg_copy(struct nhg_hash_entry *copy, - uint32_t id) +struct nhg_hash_entry *zebra_nhg_alloc(void) { struct nhg_hash_entry *nhe; nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry)); + return nhe; +} + +static struct nhg_hash_entry *zebra_nhg_copy(const struct nhg_hash_entry *copy, + uint32_t id) +{ + struct nhg_hash_entry *nhe; + + nhe = zebra_nhg_alloc(); + nhe->id = id; nhe->nhg = nexthop_group_new(); @@ -468,7 +478,7 @@ static void handle_recursive_depend(struct nhg_connected_tree_head *nhg_depends, struct nhg_hash_entry *depend = NULL; struct nexthop_group resolved_ng = {}; - _nexthop_group_add_sorted(&resolved_ng, nh); + nexthop_group_add_sorted(&resolved_ng, nh); depend = zebra_nhg_rib_find(0, &resolved_ng, afi); depends_add(nhg_depends, depend); @@ -582,9 +592,9 @@ zebra_nhg_find_nexthop(uint32_t id, struct nexthop *nh, afi_t afi, int type) struct nhg_hash_entry *nhe = NULL; struct nexthop_group nhg = {}; - _nexthop_group_add_sorted(&nhg, nh); + nexthop_group_add_sorted(&nhg, nh); - zebra_nhg_find(&nhe, id, &nhg, NULL, nh->vrf_id, afi, 0); + zebra_nhg_find(&nhe, id, &nhg, NULL, nh->vrf_id, afi, type); return nhe; } @@ -1038,25 +1048,24 @@ int zebra_nhg_kernel_del(uint32_t id) } /* Some dependency helper functions */ -static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi) +static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi) { - struct nexthop *lookup = NULL; + struct nexthop lookup; struct nhg_hash_entry *nhe = NULL; if (!nh) goto done; - copy_nexthops(&lookup, nh, NULL); - - /* Clear it, in case its a group */ - nexthops_free(lookup->next); - nexthops_free(lookup->prev); - lookup->next = NULL; - lookup->prev = NULL; + /* Capture a snapshot of this single nh; it might be part of a list, + * so we need to make a standalone copy. + */ + memset(&lookup, 0, sizeof(lookup)); + nexthop_copy(&lookup, nh, NULL); - nhe = zebra_nhg_find_nexthop(0, lookup, afi, 0); + nhe = zebra_nhg_find_nexthop(0, &lookup, afi, 0); - nexthops_free(lookup); + /* The copy may have allocated labels; free them if necessary. */ + nexthop_del_labels(&lookup); done: return nhe; @@ -1128,12 +1137,8 @@ static void zebra_nhg_free_members(struct nhg_hash_entry *nhe) nhg_connected_tree_free(&nhe->nhg_dependents); } -void zebra_nhg_free(void *arg) +void zebra_nhg_free(struct nhg_hash_entry *nhe) { - struct nhg_hash_entry *nhe = NULL; - - nhe = (struct nhg_hash_entry *)arg; - if (nhe->refcnt) zlog_debug("nhe_id=%u hash refcnt=%d", nhe->id, nhe->refcnt); @@ -1142,6 +1147,11 @@ void zebra_nhg_free(void *arg) XFREE(MTYPE_NHG, nhe); } +void zebra_nhg_hash_free(void *p) +{ + zebra_nhg_free((struct nhg_hash_entry *)p); +} + void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe) { nhe->refcnt--; @@ -1447,7 +1457,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, if (match->type == ZEBRA_ROUTE_CONNECT) { /* Directly point connected route. */ - newhop = match->ng->nexthop; + newhop = match->nhe->nhg->nexthop; if (newhop) { if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV6) @@ -1456,7 +1466,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return 1; } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { resolved = 0; - for (ALL_NEXTHOPS_PTR(match->ng, newhop)) { + for (ALL_NEXTHOPS_PTR(match->nhe->nhg, newhop)) { if (!CHECK_FLAG(match->status, ROUTE_ENTRY_INSTALLED)) continue; @@ -1477,7 +1487,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return resolved; } else if (re->type == ZEBRA_ROUTE_STATIC) { resolved = 0; - for (ALL_NEXTHOPS_PTR(match->ng, newhop)) { + for (ALL_NEXTHOPS_PTR(match->nhe->nhg, newhop)) { if (!CHECK_FLAG(match->status, ROUTE_ENTRY_INSTALLED)) continue; @@ -1668,7 +1678,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); /* Copy over the nexthops in current state */ - nexthop_group_copy(&new_grp, re->ng); + nexthop_group_copy(&new_grp, re->nhe->nhg); for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) { @@ -1718,7 +1728,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) new_nhe = zebra_nhg_rib_find(0, &new_grp, rt_afi); - zebra_nhg_re_update_ref(re, new_nhe); + route_entry_update_nhe(re, new_nhe); } if (curr_active) { @@ -1744,40 +1754,6 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) return curr_active; } -static void zebra_nhg_re_attach_ref(struct route_entry *re, - struct nhg_hash_entry *new) -{ - re->ng = new->nhg; - re->nhe_id = new->id; - - zebra_nhg_increment_ref(new); -} - -int zebra_nhg_re_update_ref(struct route_entry *re, struct nhg_hash_entry *new) -{ - struct nhg_hash_entry *old = NULL; - int ret = 0; - - if (new == NULL) { - re->ng = NULL; - goto done; - } - - if (re->nhe_id != new->id) { - old = zebra_nhg_lookup_id(re->nhe_id); - - zebra_nhg_re_attach_ref(re, new); - - if (old) - zebra_nhg_decrement_ref(old); - } else if (!re->ng) - /* This is the first time it's being attached */ - zebra_nhg_re_attach_ref(re, new); - -done: - return ret; -} - /* Convert a nhe into a group array */ uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe, int max_num) @@ -1814,7 +1790,7 @@ uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe, if (!duplicate) { grp[i].id = depend->id; /* We aren't using weights for anything right now */ - grp[i].weight = 0; + grp[i].weight = depend->nhg->nexthop->weight; i++; } diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 1f695433c9..522ec1e9dd 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -23,11 +23,9 @@ #ifndef __ZEBRA_NHG_H__ #define __ZEBRA_NHG_H__ -#include "zebra/rib.h" +#include "lib/nexthop.h" #include "lib/nexthop_group.h" -#include "zebra/zebra_dplane.h" - /* This struct is used exclusively for dataplane * interaction via a dataplane context. * @@ -160,6 +158,11 @@ struct nhg_ctx { * NHE abstracted tree functions. * Use these where possible instead of the direct ones access ones. */ +struct nhg_hash_entry *zebra_nhg_alloc(void); +void zebra_nhg_free(struct nhg_hash_entry *nhe); +/* In order to clear a generic hash, we need a generic api, sigh. */ +void zebra_nhg_hash_free(void *p); + extern struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe); extern unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe); @@ -201,8 +204,6 @@ zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi); /* Reference counter functions */ extern void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe); extern void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe); -extern int zebra_nhg_re_update_ref(struct route_entry *re, - struct nhg_hash_entry *nhe); /* Check validity of nhe, if invalid will update dependents as well */ extern void zebra_nhg_check_valid(struct nhg_hash_entry *nhe); @@ -224,5 +225,6 @@ extern void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx); extern void zebra_nhg_sweep_table(struct hash *hash); /* Nexthop resolution processing */ +struct route_entry; /* Forward ref to avoid circular includes */ extern int nexthop_active_update(struct route_node *rn, struct route_entry *re); #endif diff --git a/zebra/zebra_nhg_private.h b/zebra/zebra_nhg_private.h index 170e2357e3..79107b047c 100644 --- a/zebra/zebra_nhg_private.h +++ b/zebra/zebra_nhg_private.h @@ -57,6 +57,4 @@ extern void nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head, extern void nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head, struct nhg_hash_entry *nhe); -extern void zebra_nhg_free(void *arg); - #endif /* __ZEBRA_NHG_PRIVATE_H__ */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 37f53bf911..3287176ef5 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -37,6 +37,7 @@ #include "zebra_pbr.h" #include "rib.h" #include "table_manager.h" +#include "zebra_errors.h" extern struct zebra_privs_t zserv_privs; @@ -64,6 +65,9 @@ static int zebra_ns_new(struct ns *ns) { struct zebra_ns *zns; + if (!ns) + return -1; + if (IS_ZEBRA_DEBUG_EVENT) zlog_info("ZNS %s with id %u (created)", ns->name, ns->ns_id); @@ -86,7 +90,7 @@ static int zebra_ns_delete(struct ns *ns) zlog_info("ZNS %s with id %u (deleted)", ns->name, ns->ns_id); if (!zns) return 0; - XFREE(MTYPE_ZEBRA_NS, zns); + XFREE(MTYPE_ZEBRA_NS, ns->info); return 0; } @@ -175,19 +179,26 @@ int zebra_ns_final_shutdown(struct ns *ns) int zebra_ns_init(const char *optional_default_name) { + struct ns *default_ns; ns_id_t ns_id; ns_id_t ns_id_external; - dzns = zebra_ns_alloc(); - frr_with_privs(&zserv_privs) { ns_id = zebra_ns_id_get_default(); } ns_id_external = ns_map_nsid_with_external(ns_id, true); ns_init_management(ns_id_external, ns_id); + default_ns = ns_lookup(ns_get_default_id()); + if (!default_ns) { + flog_err(EC_ZEBRA_NS_NO_DEFAULT, + "%s: failed to find default ns", __func__); + exit(EXIT_FAILURE); /* This is non-recoverable */ + } + /* Do any needed per-NS data structure allocation. */ - dzns->if_table = route_table_init(); + zebra_ns_new(default_ns); + dzns = default_ns->info; /* Register zebra VRF callbacks, create and activate default VRF. */ zebra_vrf_init(); diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 3f1567a95b..618a232408 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -259,7 +259,7 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw) * Need to ensure that there's a label binding for all nexthops. * Otherwise, ECMP for this route could render the pseudowire unusable. */ - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) { if (!nexthop->nh_label) { if (IS_ZEBRA_DEBUG_PW) zlog_debug("%s: unlabeled route for %s", diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 605311769d..309b0f4301 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -193,144 +193,47 @@ int zebra_check_addr(const struct prefix *p) return 1; } -/* Add nexthop to the end of a rib node's nexthop list */ -void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop) -{ - _nexthop_group_add_sorted(re->ng, nexthop); -} - - /** * copy_nexthop - copy a nexthop to the rib structure. */ void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh) { - assert(!re->ng->nexthop); - copy_nexthops(&re->ng->nexthop, nh, NULL); + assert(!re->nhe->nhg->nexthop); + copy_nexthops(&re->nhe->nhg->nexthop, nh, NULL); } -/* Delete specified nexthop from the list. */ -void route_entry_nexthop_delete(struct route_entry *re, struct nexthop *nexthop) -{ - if (nexthop->next) - nexthop->next->prev = nexthop->prev; - if (nexthop->prev) - nexthop->prev->next = nexthop->next; - else - re->ng->nexthop = nexthop->next; -} - - -struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *re, - ifindex_t ifindex, - vrf_id_t nh_vrf_id) +static void route_entry_attach_ref(struct route_entry *re, + struct nhg_hash_entry *new) { - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->type = NEXTHOP_TYPE_IFINDEX; - nexthop->ifindex = ifindex; - nexthop->vrf_id = nh_vrf_id; - - route_entry_nexthop_add(re, nexthop); + re->nhe = new; + re->nhe_id = new->id; - return nexthop; + zebra_nhg_increment_ref(new); } -struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *re, - struct in_addr *ipv4, - struct in_addr *src, - vrf_id_t nh_vrf_id) +int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new) { - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->type = NEXTHOP_TYPE_IPV4; - nexthop->vrf_id = nh_vrf_id; - nexthop->gate.ipv4 = *ipv4; - if (src) - nexthop->src.ipv4 = *src; - - route_entry_nexthop_add(re, nexthop); - - return nexthop; -} - -struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re, - struct in_addr *ipv4, - struct in_addr *src, - ifindex_t ifindex, - vrf_id_t nh_vrf_id) -{ - struct nexthop *nexthop; - struct interface *ifp; - - nexthop = nexthop_new(); - nexthop->vrf_id = nh_vrf_id; - nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX; - nexthop->gate.ipv4 = *ipv4; - if (src) - nexthop->src.ipv4 = *src; - nexthop->ifindex = ifindex; - ifp = if_lookup_by_index(nexthop->ifindex, nh_vrf_id); - /*Pending: need to think if null ifp here is ok during bootup? - There was a crash because ifp here was coming to be NULL */ - if (ifp) - if (connected_is_unnumbered(ifp)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); - - route_entry_nexthop_add(re, nexthop); - - return nexthop; -} - -struct nexthop *route_entry_nexthop_ipv6_add(struct route_entry *re, - struct in6_addr *ipv6, - vrf_id_t nh_vrf_id) -{ - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->vrf_id = nh_vrf_id; - nexthop->type = NEXTHOP_TYPE_IPV6; - nexthop->gate.ipv6 = *ipv6; - - route_entry_nexthop_add(re, nexthop); - - return nexthop; -} - -struct nexthop *route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re, - struct in6_addr *ipv6, - ifindex_t ifindex, - vrf_id_t nh_vrf_id) -{ - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->vrf_id = nh_vrf_id; - nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX; - nexthop->gate.ipv6 = *ipv6; - nexthop->ifindex = ifindex; + struct nhg_hash_entry *old = NULL; + int ret = 0; - route_entry_nexthop_add(re, nexthop); + if (new == NULL) { + re->nhe->nhg = NULL; + goto done; + } - return nexthop; -} + if (re->nhe_id != new->id) { + old = zebra_nhg_lookup_id(re->nhe_id); -struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *re, - enum blackhole_type bh_type) -{ - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->vrf_id = VRF_DEFAULT; - nexthop->type = NEXTHOP_TYPE_BLACKHOLE; - nexthop->bh_type = bh_type; + route_entry_attach_ref(re, new); - route_entry_nexthop_add(re, nexthop); + if (old) + zebra_nhg_decrement_ref(old); + } else if (!re->nhe) + /* This is the first time it's being attached */ + route_entry_attach_ref(re, new); - return nexthop; +done: + return ret; } struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, @@ -501,7 +404,7 @@ int zebra_rib_labeled_unicast(struct route_entry *re) if (re->type != ZEBRA_ROUTE_BGP) return 0; - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) if (!nexthop->nh_label || !nexthop->nh_label->num_labels) return 0; @@ -525,7 +428,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, srcdest_rnode_prefixes(rn, &p, &src_p); if (info->safi != SAFI_UNICAST) { - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); return; } @@ -603,7 +506,7 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) if (info->safi != SAFI_UNICAST) { UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); return; } @@ -663,7 +566,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) re->fib_ng.nexthop = NULL; } - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } @@ -839,7 +742,7 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, /* Update real nexthop. This may actually determine if nexthop is active * or not. */ - if (!nexthop_group_active_nexthop_num(new->ng)) { + if (!nexthop_group_active_nexthop_num(new->nhe->nhg)) { UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED); return; } @@ -908,7 +811,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, /* Update the nexthop; we could determine here that nexthop is * inactive. */ - if (nexthop_group_active_nexthop_num(new->ng)) + if (nexthop_group_active_nexthop_num(new->nhe->nhg)) nh_active = 1; /* If nexthop is active, install the selected route, if @@ -1026,7 +929,7 @@ static struct route_entry *rib_choose_best(struct route_entry *current, /* both are connected. are either loop or vrf? */ struct nexthop *nexthop = NULL; - for (ALL_NEXTHOPS_PTR(alternate->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(alternate->nhe->nhg, nexthop)) { struct interface *ifp = if_lookup_by_index( nexthop->ifindex, alternate->vrf_id); @@ -1034,7 +937,7 @@ static struct route_entry *rib_choose_best(struct route_entry *current, return alternate; } - for (ALL_NEXTHOPS_PTR(current->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(current->nhe->nhg, nexthop)) { struct interface *ifp = if_lookup_by_index( nexthop->ifindex, current->vrf_id); @@ -1365,7 +1268,7 @@ static void zebra_rib_fixup_system(struct route_node *rn) SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED); - for (ALL_NEXTHOPS_PTR(re->ng, nhop)) { + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nhop)) { if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; @@ -1466,15 +1369,23 @@ static bool rib_update_re_from_ctx(struct route_entry *re, ctx_nexthop = dplane_ctx_get_ng(ctx)->nexthop; + /* Nothing installed - we can skip some of the checking/comparison + * of nexthops. + */ + if (ctx_nexthop == NULL) { + changed_p = true; + goto no_nexthops; + } + /* Get the first `installed` one to check against. * If the dataplane doesn't set these to be what was actually installed, - * it will just be whatever was in re->ng? + * it will just be whatever was in re->nhe->nhg? */ if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE) || !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE)) ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; @@ -1528,6 +1439,8 @@ static bool rib_update_re_from_ctx(struct route_entry *re, goto done; } +no_nexthops: + /* FIB nexthop set differs from the RIB set: * create a fib-specific nexthop-group */ @@ -1885,18 +1798,40 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) /* Ensure we clear the QUEUED flag */ UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED); - /* Is this a notification that ... matters? We only really care about - * the route that is currently selected for installation. + /* Is this a notification that ... matters? We mostly care about + * the route that is currently selected for installation; we may also + * get an un-install notification, and handle that too. */ if (re != dest->selected_fib) { - /* TODO -- don't skip processing entirely? We might like to - * at least report on the event. + /* + * If we need to, clean up after a delete that was part of + * an update operation. */ - if (debug_p) - zlog_debug("%u:%s dplane notif, but type %s not selected_fib", - dplane_ctx_get_vrf(ctx), dest_str, - zebra_route_string( - dplane_ctx_get_type(ctx))); + end_count = 0; + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + end_count++; + } + + /* If no nexthops or none installed, ensure that this re + * gets its 'installed' flag cleared. + */ + if (end_count == 0) { + if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) + UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); + if (debug_p) + zlog_debug("%u:%s dplane notif, uninstalled type %s route", + dplane_ctx_get_vrf(ctx), dest_str, + zebra_route_string( + dplane_ctx_get_type(ctx))); + } else { + /* At least report on the event. */ + if (debug_p) + zlog_debug("%u:%s dplane notif, but type %s not selected_fib", + dplane_ctx_get_vrf(ctx), dest_str, + zebra_route_string( + dplane_ctx_get_type(ctx))); + } goto done; } @@ -1905,9 +1840,12 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) * and then again if there's been a change. */ start_count = 0; - for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - start_count++; + + if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) { + for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + start_count++; + } } /* Update zebra's nexthop FIB flags based on the context struct's @@ -1917,10 +1855,8 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) if (!fib_changed) { if (debug_p) - zlog_debug("%u:%s No change from dplane notification", + zlog_debug("%u:%s dplane notification: rib_update returns FALSE", dplane_ctx_get_vrf(ctx), dest_str); - - goto done; } /* @@ -2421,8 +2357,8 @@ void rib_unlink(struct route_node *rn, struct route_entry *re) nhe = zebra_nhg_lookup_id(re->nhe_id); if (nhe) zebra_nhg_decrement_ref(nhe); - } else if (re->ng) - nexthop_group_delete(&re->ng); + } else if (re->nhe->nhg) + nexthop_group_delete(&re->nhe->nhg); nexthops_free(re->fib_ng.nexthop); @@ -2489,10 +2425,10 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, "%s: metric == %u, mtu == %u, distance == %u, flags == %u, status == %u", straddr, re->metric, re->mtu, re->distance, re->flags, re->status); zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", straddr, - nexthop_group_nexthop_num(re->ng), - nexthop_group_active_nexthop_num(re->ng)); + nexthop_group_nexthop_num(re->nhe->nhg), + nexthop_group_active_nexthop_num(re->nhe->nhg)); - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) { struct interface *ifp; struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); @@ -2641,7 +2577,8 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) } int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, - struct prefix_ipv6 *src_p, struct route_entry *re) + struct prefix_ipv6 *src_p, struct route_entry *re, + struct nexthop_group *ng) { struct nhg_hash_entry *nhe = NULL; struct route_table *table; @@ -2658,8 +2595,8 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, table = zebra_vrf_get_table_with_table_id(afi, safi, re->vrf_id, re->table); if (!table) { - if (re->ng) - nexthop_group_delete(&re->ng); + if (ng) + nexthop_group_delete(&ng); XFREE(MTYPE_RE, re); return 0; } @@ -2676,13 +2613,13 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, return -1; } } else { - nhe = zebra_nhg_rib_find(0, re->ng, afi); + nhe = zebra_nhg_rib_find(0, ng, afi); /* * The nexthops got copied over into an nhe, * so free them now. */ - nexthop_group_delete(&re->ng); + nexthop_group_delete(&ng); if (!nhe) { char buf[PREFIX_STRLEN] = ""; @@ -2708,7 +2645,7 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, * level protocols, as the refcnt might be wrong, since it checks * if old_id != new_id. */ - zebra_nhg_re_update_ref(re, nhe); + route_entry_update_nhe(re, nhe); /* Make it sure prefixlen is applied to the prefix. */ apply_mask(p); @@ -2851,7 +2788,8 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != metric) continue; - if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->ng->nexthop) + if (re->type == ZEBRA_ROUTE_CONNECT && + (rtnh = re->nhe->nhg->nexthop) && rtnh->type == NEXTHOP_TYPE_IFINDEX && nh) { if (rtnh->ifindex != nh->ifindex) continue; @@ -2869,7 +2807,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, same = re; break; } - for (ALL_NEXTHOPS_PTR(re->ng, rtnh)) { + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, rtnh)) { /* * No guarantee all kernel send nh with labels * on delete. @@ -2911,7 +2849,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (allow_delete) { UNSET_FLAG(fib->status, ROUTE_ENTRY_INSTALLED); /* Unset flags. */ - for (rtnh = fib->ng->nexthop; rtnh; + for (rtnh = fib->nhe->nhg->nexthop; rtnh; rtnh = rtnh->next) UNSET_FLAG(rtnh->flags, NEXTHOP_FLAG_FIB); @@ -2967,7 +2905,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) { struct nexthop *tmp_nh; - for (ALL_NEXTHOPS_PTR(re->ng, tmp_nh)) { + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, tmp_nh)) { struct ipaddr vtep_ip; memset(&vtep_ip, 0, sizeof(struct ipaddr)); @@ -3007,6 +2945,7 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, { struct route_entry *re = NULL; struct nexthop *nexthop = NULL; + struct nexthop_group *ng = NULL; /* Allocate new route_entry structure. */ re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); @@ -3022,16 +2961,19 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, re->tag = tag; re->nhe_id = nhe_id; + /* If the owner of the route supplies a shared nexthop-group id, + * we'll use that. Otherwise, pass the nexthop along directly. + */ if (!nhe_id) { - re->ng = nexthop_group_new(); + ng = nexthop_group_new(); /* Add nexthop. */ nexthop = nexthop_new(); *nexthop = *nh; - route_entry_nexthop_add(re, nexthop); + nexthop_group_add_sorted(ng, nexthop); } - return rib_add_multipath(afi, safi, p, src_p, re); + return rib_add_multipath(afi, safi, p, src_p, re, ng); } static const char *rib_update_event2str(rib_update_event_t event) @@ -3289,7 +3231,7 @@ void rib_sweep_table(struct route_table *table) * this decision needs to be revisited */ SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); rib_uninstall_kernel(rn, re); diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 60e23cc4d4..2d9c83becb 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -384,7 +384,7 @@ static void zebra_rnh_clear_nexthop_rnh_filters(struct route_entry *re) struct nexthop *nexthop; if (re) { - for (nexthop = re->ng->nexthop; nexthop; + for (nexthop = re->nhe->nhg->nexthop; nexthop; nexthop = nexthop->next) { UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RNH_FILTERED); } @@ -403,7 +403,7 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf, route_map_result_t ret; if (prn && re) { - for (nexthop = re->ng->nexthop; nexthop; + for (nexthop = re->nhe->nhg->nexthop; nexthop; nexthop = nexthop->next) { ret = zebra_nht_route_map_check( afi, proto, &prn->p, zvrf, re, nexthop); @@ -688,7 +688,7 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, /* Just being SELECTED isn't quite enough - must * have an installed nexthop to be useful. */ - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) { if (rnh_nexthop_valid(re, nexthop)) break; } @@ -707,7 +707,8 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi, break; if (re->type == ZEBRA_ROUTE_NHRP) { - for (nexthop = re->ng->nexthop; nexthop; + for (nexthop = re->nhe->nhg->nexthop; + nexthop; nexthop = nexthop->next) if (nexthop->type == NEXTHOP_TYPE_IFINDEX) @@ -940,7 +941,7 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re, return; /* free RE and nexthops */ - nexthop_group_delete(&re->ng); + zebra_nhg_free(re->nhe); XFREE(MTYPE_RE, re); } @@ -963,9 +964,11 @@ static void copy_state(struct rnh *rnh, struct route_entry *re, state->metric = re->metric; state->vrf_id = re->vrf_id; state->status = re->status; - state->ng = nexthop_group_new(); - route_entry_copy_nexthops(state, re->ng->nexthop); + state->nhe = zebra_nhg_alloc(); + state->nhe->nhg = nexthop_group_new(); + + nexthop_group_copy(state->nhe->nhg, re->nhe->nhg); rnh->state = state; } @@ -983,11 +986,12 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2) if (r1->metric != r2->metric) return 1; - if (nexthop_group_nexthop_num(r1->ng) - != nexthop_group_nexthop_num(r2->ng)) + if (nexthop_group_nexthop_num(r1->nhe->nhg) + != nexthop_group_nexthop_num(r2->nhe->nhg)) return 1; - if (nexthop_group_hash(r1->ng) != nexthop_group_hash(r2->ng)) + if (nexthop_group_hash(r1->nhe->nhg) != + nexthop_group_hash(r2->nhe->nhg)) return 1; return 0; @@ -1030,6 +1034,8 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, break; } if (re) { + struct zapi_nexthop znh; + stream_putc(s, re->type); stream_putw(s, re->instance); stream_putc(s, re->distance); @@ -1037,39 +1043,10 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type, num = 0; nump = stream_get_endp(s); stream_putc(s, 0); - for (ALL_NEXTHOPS_PTR(re->ng, nh)) + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nh)) if (rnh_nexthop_valid(re, nh)) { - stream_putl(s, nh->vrf_id); - stream_putc(s, nh->type); - switch (nh->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - stream_put_in_addr(s, &nh->gate.ipv4); - stream_putl(s, nh->ifindex); - break; - case NEXTHOP_TYPE_IFINDEX: - stream_putl(s, nh->ifindex); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - stream_put(s, &nh->gate.ipv6, 16); - stream_putl(s, nh->ifindex); - break; - default: - /* do nothing */ - break; - } - if (nh->nh_label) { - stream_putc(s, - nh->nh_label->num_labels); - if (nh->nh_label->num_labels) - stream_put( - s, - &nh->nh_label->label[0], - nh->nh_label->num_labels - * sizeof(mpls_label_t)); - } else - stream_putc(s, 0); + zapi_nexthop_from_nexthop(&znh, nh); + zapi_nexthop_encode(s, &znh, 0 /* flags */); num++; } stream_putc_at(s, nump, num); @@ -1137,7 +1114,7 @@ static void print_rnh(struct route_node *rn, struct vty *vty) if (rnh->state) { vty_out(vty, " resolved via %s\n", zebra_route_string(rnh->state->type)); - for (nexthop = rnh->state->ng->nexthop; nexthop; + for (nexthop = rnh->state->nhe->nhg->nexthop; nexthop; nexthop = nexthop->next) print_nh(nexthop, vty); } else diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index e573093b70..a891ffb76a 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -29,7 +29,7 @@ #include "zebra_pbr.h" #include "zebra_vxlan.h" #include "zebra_mlag.h" -#include "zebra_nhg_private.h" +#include "zebra_nhg.h" #include "debug.h" DEFINE_MTYPE_STATIC(ZEBRA, RIB_TABLE_INFO, "RIB table info") @@ -223,7 +223,7 @@ void zebra_router_terminate(void) zebra_vxlan_disable(); zebra_mlag_terminate(); - hash_clean(zrouter.nhgs, zebra_nhg_free); + hash_clean(zrouter.nhgs, zebra_nhg_hash_free); hash_free(zrouter.nhgs); hash_clean(zrouter.nhgs_id, NULL); hash_free(zrouter.nhgs_id); diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index 56c7664328..4c52651981 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -285,8 +285,8 @@ static void check_replace(struct route_node *np2, struct route_entry *re2, return; } - if (in_addr_cmp((uint8_t *)&(*re)->ng->nexthop->gate.ipv4, - (uint8_t *)&re2->ng->nexthop->gate.ipv4) + if (in_addr_cmp((uint8_t *)&(*re)->nhe->nhg->nexthop->gate.ipv4, + (uint8_t *)&re2->nhe->nhg->nexthop->gate.ipv4) <= 0) return; @@ -371,9 +371,9 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], if (!in_addr_cmp(&(*np)->p.u.prefix, (uint8_t *)&dest)) { RNODE_FOREACH_RE (*np, *re) { - if (!in_addr_cmp((uint8_t *)&(*re) - ->ng->nexthop - ->gate.ipv4, + if (!in_addr_cmp((uint8_t *)&(*re)->nhe + ->nhg->nexthop + ->gate.ipv4, (uint8_t *)&nexthop)) if (proto == proto_trans((*re)->type)) @@ -406,8 +406,8 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], || ((policy == policy2) && (proto < proto2)) || ((policy == policy2) && (proto == proto2) && (in_addr_cmp( - (uint8_t *)&re2->ng->nexthop - ->gate.ipv4, + (uint8_t *)&re2->nhe + ->nhg->nexthop->gate.ipv4, (uint8_t *)&nexthop) >= 0))) check_replace(np2, re2, np, re); @@ -432,7 +432,7 @@ static void get_fwtable_route_node(struct variable *v, oid objid[], { struct nexthop *nexthop; - nexthop = (*re)->ng->nexthop; + nexthop = (*re)->nhe->nhg->nexthop; if (nexthop) { pnt = (uint8_t *)&nexthop->gate.ipv4; for (i = 0; i < 4; i++) @@ -462,7 +462,7 @@ static uint8_t *ipFwTable(struct variable *v, oid objid[], size_t *objid_len, if (!np) return NULL; - nexthop = re->ng->nexthop; + nexthop = re->nhe->nhg->nexthop; if (!nexthop) return NULL; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 8a7c7e359f..b85bf83923 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -66,9 +66,10 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi, static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, int mcast, bool use_fib, bool show_ng); static void vty_show_ip_route_summary(struct vty *vty, - struct route_table *table); + struct route_table *table, bool use_json); static void vty_show_ip_route_summary_prefix(struct vty *vty, - struct route_table *table); + struct route_table *table, + bool use_json); DEFUN (ip_multicast_mode, ip_multicast_mode_cmd, @@ -263,7 +264,7 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, if (show_ng) vty_out(vty, " Nexthop Group ID: %u\n", re->nhe_id); - for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) { + for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) { char addrstr[32]; vty_out(vty, " %c%s", @@ -380,6 +381,9 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, sizeof buf, 1)); } + if (nexthop->weight) + vty_out(vty, ", weight %u", nexthop->weight); + vty_out(vty, "\n"); } vty_out(vty, "\n"); @@ -413,7 +417,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, if (is_fib) nhg = rib_active_nhg(re); else - nhg = re->ng; + nhg = re->nhe->nhg; if (json) { json_route = json_object_new_object(); @@ -466,9 +470,10 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_int_add(json_route, "internalFlags", re->flags); json_object_int_add(json_route, "internalNextHopNum", - nexthop_group_nexthop_num(re->ng)); + nexthop_group_nexthop_num(re->nhe->nhg)); json_object_int_add(json_route, "internalNextHopActiveNum", - nexthop_group_active_nexthop_num(re->ng)); + nexthop_group_active_nexthop_num( + re->nhe->nhg)); if (uptime < ONE_DAY_SECOND) sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); @@ -1239,6 +1244,9 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) sizeof(buf), 1)); } + if (nexthop->weight) + vty_out(vty, ", weight %u", nexthop->weight); + vty_out(vty, "\n"); } @@ -1683,7 +1691,7 @@ DEFPY (show_route_detail, DEFPY (show_route_summary, show_route_summary_cmd, "show <ip$ipv4|ipv6$ipv6> route [vrf <NAME$vrf_name|all$vrf_all>] \ - summary [table (1-4294967295)$table_id] [prefix$prefix]", + summary [table (1-4294967295)$table_id] [prefix$prefix] [json]", SHOW_STR IP_STR IP6_STR @@ -1692,10 +1700,12 @@ DEFPY (show_route_summary, "Summary of all routes\n" "Table to display summary for\n" "The table number\n" - "Prefix routes\n") + "Prefix routes\n" + JSON_STR) { afi_t afi = ipv4 ? AFI_IP : AFI_IP6; struct route_table *table; + bool uj = use_json(argc, argv); if (table_id == 0) table_id = RT_TABLE_MAIN; @@ -1715,9 +1725,10 @@ DEFPY (show_route_summary, continue; if (prefix) - vty_show_ip_route_summary_prefix(vty, table); + vty_show_ip_route_summary_prefix(vty, table, + uj); else - vty_show_ip_route_summary(vty, table); + vty_show_ip_route_summary(vty, table, uj); } } else { vrf_id_t vrf_id = VRF_DEFAULT; @@ -1731,16 +1742,16 @@ DEFPY (show_route_summary, return CMD_SUCCESS; if (prefix) - vty_show_ip_route_summary_prefix(vty, table); + vty_show_ip_route_summary_prefix(vty, table, uj); else - vty_show_ip_route_summary(vty, table); + vty_show_ip_route_summary(vty, table, uj); } return CMD_SUCCESS; } static void vty_show_ip_route_summary(struct vty *vty, - struct route_table *table) + struct route_table *table, bool use_json) { struct route_node *rn; struct route_entry *re; @@ -1750,9 +1761,19 @@ static void vty_show_ip_route_summary(struct vty *vty, uint32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1]; uint32_t i; uint32_t is_ibgp; + json_object *json_route_summary = NULL; + json_object *json_route_routes = NULL; memset(&rib_cnt, 0, sizeof(rib_cnt)); memset(&fib_cnt, 0, sizeof(fib_cnt)); + + if (use_json) { + json_route_summary = json_object_new_object(); + json_route_routes = json_object_new_array(); + json_object_object_add(json_route_summary, "routes", + json_route_routes); + } + for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) RNODE_FOREACH_RE (rn, re) { is_ibgp = (re->type == ZEBRA_ROUTE_BGP @@ -1774,30 +1795,93 @@ static void vty_show_ip_route_summary(struct vty *vty, } } - vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source", "Routes", - "FIB", zvrf_name(((rib_table_info_t *)route_table_get_info(table))->zvrf)); + if (!use_json) + vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source", + "Routes", "FIB", + zvrf_name(((rib_table_info_t *)route_table_get_info( + table)) + ->zvrf)); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { if ((rib_cnt[i] > 0) || (i == ZEBRA_ROUTE_BGP && rib_cnt[ZEBRA_ROUTE_IBGP] > 0)) { if (i == ZEBRA_ROUTE_BGP) { - vty_out(vty, "%-20s %-20d %-20d \n", "ebgp", - rib_cnt[ZEBRA_ROUTE_BGP], - fib_cnt[ZEBRA_ROUTE_BGP]); - vty_out(vty, "%-20s %-20d %-20d \n", "ibgp", - rib_cnt[ZEBRA_ROUTE_IBGP], - fib_cnt[ZEBRA_ROUTE_IBGP]); - } else - vty_out(vty, "%-20s %-20d %-20d \n", - zebra_route_string(i), rib_cnt[i], - fib_cnt[i]); + if (use_json) { + json_object *json_route_ebgp = + json_object_new_object(); + + json_object_int_add( + json_route_ebgp, "fib", + fib_cnt[ZEBRA_ROUTE_BGP]); + json_object_int_add( + json_route_ebgp, "rib", + rib_cnt[ZEBRA_ROUTE_BGP]); + json_object_string_add(json_route_ebgp, + "type", "ebgp"); + json_object_array_add(json_route_routes, + json_route_ebgp); + + json_object *json_route_ibgp = + json_object_new_object(); + + json_object_int_add( + json_route_ibgp, "fib", + fib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_int_add( + json_route_ibgp, "rib", + rib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_string_add(json_route_ibgp, + "type", "ibgp"); + json_object_array_add(json_route_routes, + json_route_ibgp); + } else { + vty_out(vty, "%-20s %-20d %-20d \n", + "ebgp", + rib_cnt[ZEBRA_ROUTE_BGP], + fib_cnt[ZEBRA_ROUTE_BGP]); + vty_out(vty, "%-20s %-20d %-20d \n", + "ibgp", + rib_cnt[ZEBRA_ROUTE_IBGP], + fib_cnt[ZEBRA_ROUTE_IBGP]); + } + } else { + if (use_json) { + json_object *json_route_type = + json_object_new_object(); + + json_object_int_add(json_route_type, + "fib", fib_cnt[i]); + json_object_int_add(json_route_type, + "rib", rib_cnt[i]); + json_object_string_add( + json_route_type, "type", + zebra_route_string(i)); + json_object_array_add(json_route_routes, + json_route_type); + } else + vty_out(vty, "%-20s %-20d %-20d \n", + zebra_route_string(i), + rib_cnt[i], fib_cnt[i]); + } } } - vty_out(vty, "------\n"); - vty_out(vty, "%-20s %-20d %-20d \n", "Totals", - rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]); - vty_out(vty, "\n"); + if (use_json) { + json_object_int_add(json_route_summary, "routesTotal", + rib_cnt[ZEBRA_ROUTE_TOTAL]); + json_object_int_add(json_route_summary, "routesTotalFib", + fib_cnt[ZEBRA_ROUTE_TOTAL]); + + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_route_summary, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_route_summary); + } else { + vty_out(vty, "------\n"); + vty_out(vty, "%-20s %-20d %-20d \n", "Totals", + rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]); + vty_out(vty, "\n"); + } } /* @@ -1808,7 +1892,8 @@ static void vty_show_ip_route_summary(struct vty *vty, * */ static void vty_show_ip_route_summary_prefix(struct vty *vty, - struct route_table *table) + struct route_table *table, + bool use_json) { struct route_node *rn; struct route_entry *re; @@ -1819,9 +1904,19 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, uint32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1]; uint32_t i; int cnt; + json_object *json_route_summary = NULL; + json_object *json_route_routes = NULL; memset(&rib_cnt, 0, sizeof(rib_cnt)); memset(&fib_cnt, 0, sizeof(fib_cnt)); + + if (use_json) { + json_route_summary = json_object_new_object(); + json_route_routes = json_object_new_array(); + json_object_object_add(json_route_summary, "prefixRoutes", + json_route_routes); + } + for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) RNODE_FOREACH_RE (rn, re) { @@ -1833,7 +1928,7 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, fib_cnt[ZEBRA_ROUTE_TOTAL]++; fib_cnt[re->type]++; } - for (nexthop = re->ng->nexthop; (!cnt && nexthop); + for (nexthop = re->nhe->nhg->nexthop; (!cnt && nexthop); nexthop = nexthop->next) { cnt++; rib_cnt[ZEBRA_ROUTE_TOTAL]++; @@ -1848,32 +1943,96 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, } } - vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source", - "Prefix Routes", "FIB", - zvrf_name(((rib_table_info_t *)route_table_get_info(table))->zvrf)); + if (!use_json) + vty_out(vty, "%-20s %-20s %s (vrf %s)\n", "Route Source", + "Prefix Routes", "FIB", + zvrf_name(((rib_table_info_t *)route_table_get_info( + table)) + ->zvrf)); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { if (rib_cnt[i] > 0) { if (i == ZEBRA_ROUTE_BGP) { - vty_out(vty, "%-20s %-20d %-20d \n", "ebgp", - rib_cnt[ZEBRA_ROUTE_BGP] - - rib_cnt[ZEBRA_ROUTE_IBGP], - fib_cnt[ZEBRA_ROUTE_BGP] - - fib_cnt[ZEBRA_ROUTE_IBGP]); - vty_out(vty, "%-20s %-20d %-20d \n", "ibgp", - rib_cnt[ZEBRA_ROUTE_IBGP], - fib_cnt[ZEBRA_ROUTE_IBGP]); - } else - vty_out(vty, "%-20s %-20d %-20d \n", - zebra_route_string(i), rib_cnt[i], - fib_cnt[i]); + if (use_json) { + json_object *json_route_ebgp = + json_object_new_object(); + + json_object_int_add( + json_route_ebgp, "fib", + fib_cnt[ZEBRA_ROUTE_BGP] + - fib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_int_add( + json_route_ebgp, "rib", + rib_cnt[ZEBRA_ROUTE_BGP] + - rib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_string_add(json_route_ebgp, + "type", "ebgp"); + json_object_array_add(json_route_routes, + json_route_ebgp); + + json_object *json_route_ibgp = + json_object_new_object(); + + json_object_int_add( + json_route_ibgp, "fib", + fib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_int_add( + json_route_ibgp, "rib", + rib_cnt[ZEBRA_ROUTE_IBGP]); + json_object_string_add(json_route_ibgp, + "type", "ibgp"); + json_object_array_add(json_route_routes, + json_route_ibgp); + } else { + vty_out(vty, "%-20s %-20d %-20d \n", + "ebgp", + rib_cnt[ZEBRA_ROUTE_BGP] + - rib_cnt[ZEBRA_ROUTE_IBGP], + fib_cnt[ZEBRA_ROUTE_BGP] + - fib_cnt[ZEBRA_ROUTE_IBGP]); + vty_out(vty, "%-20s %-20d %-20d \n", + "ibgp", + rib_cnt[ZEBRA_ROUTE_IBGP], + fib_cnt[ZEBRA_ROUTE_IBGP]); + } + } else { + if (use_json) { + json_object *json_route_type = + json_object_new_object(); + + json_object_int_add(json_route_type, + "fib", fib_cnt[i]); + json_object_int_add(json_route_type, + "rib", rib_cnt[i]); + json_object_string_add( + json_route_type, "type", + zebra_route_string(i)); + json_object_array_add(json_route_routes, + json_route_type); + } else + vty_out(vty, "%-20s %-20d %-20d \n", + zebra_route_string(i), + rib_cnt[i], fib_cnt[i]); + } } } - vty_out(vty, "------\n"); - vty_out(vty, "%-20s %-20d %-20d \n", "Totals", - rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]); - vty_out(vty, "\n"); + if (use_json) { + json_object_int_add(json_route_summary, "prefixRoutesTotal", + rib_cnt[ZEBRA_ROUTE_TOTAL]); + json_object_int_add(json_route_summary, "prefixRoutesTotalFib", + fib_cnt[ZEBRA_ROUTE_TOTAL]); + + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_route_summary, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_route_summary); + } else { + vty_out(vty, "------\n"); + vty_out(vty, "%-20s %-20d %-20d \n", "Totals", + rib_cnt[ZEBRA_ROUTE_TOTAL], fib_cnt[ZEBRA_ROUTE_TOTAL]); + vty_out(vty, "\n"); + } } /* |
