diff options
141 files changed, 3231 insertions, 1805 deletions
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/alpine/APKBUILD.in b/alpine/APKBUILD.in index 1c579a8e72..f740a34583 100644 --- a/alpine/APKBUILD.in +++ b/alpine/APKBUILD.in @@ -17,7 +17,7 @@ makedepends="ncurses-dev net-snmp-dev gawk texinfo perl linux-headers lzip lzo m4 make mkinitfs mpc1 mpfr4 mtools musl-dev ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre perl pkgconf python2 python2-dev readline readline-dev sqlite-libs - squashfs-tools sudo tar texinfo xorriso xz-libs py-pip py-sphinx rtrlib + squashfs-tools sudo tar texinfo xorriso xz-libs py-pip rtrlib rtrlib-dev" checkdepends="pytest py-setuptools" install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall" @@ -34,6 +34,12 @@ _user=frr build() { cd "$builddir" + + _localpythondir=$PWD/.python + pip2 install --prefix $_localpythondir sphinx + export PATH=${_localpythondir}/bin:$PATH + export PYTHONPATH=${_localpythondir}/lib/python2.7/site-packages + ./configure \ --prefix=/usr \ --sbindir=$_sbindir \ diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index ab80aff81b..bdac2a8dcc 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -152,8 +152,9 @@ static bool cluster_hash_cmp(const void *p1, const void *p2) const struct cluster_list *cluster2 = p2; return (cluster1->length == cluster2->length - && memcmp(cluster1->list, cluster2->list, cluster1->length) - == 0); + && (cluster1->list == cluster2->list + || memcmp(cluster1->list, cluster2->list, cluster1->length) + == 0)); } static void cluster_free(struct cluster_list *cluster) @@ -421,14 +422,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; } } @@ -851,7 +853,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); @@ -2483,7 +2485,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. */ @@ -2506,7 +2509,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. */ @@ -2527,7 +2531,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 @@ -2584,7 +2589,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 = { @@ -2609,7 +2615,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. */ @@ -2687,32 +2693,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. */ @@ -2722,9 +2721,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; } } @@ -2735,9 +2733,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; } /* @@ -2756,16 +2754,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 @@ -2788,28 +2784,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. @@ -2817,21 +2795,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; } - /* Finally intern unknown attribute. */ + + 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); + } + + 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; } /* @@ -2890,7 +2906,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 13ed3e1ee3..40e87e190a 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -304,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 b8798a7ced..79a8fae530 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -5100,7 +5100,8 @@ int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, if (pnt + BGP_ADDPATH_ID_LEN > lim) return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; - addpath_id = ntohl(*((uint32_t *)pnt)); + memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN); + addpath_id = ntohl(addpath_id); pnt += BGP_ADDPATH_ID_LEN; } diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index d316a28dcb..3049a00ce3 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -5620,19 +5620,24 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (!bgp->evpn_info->advertise_pip) vty_out(vty, " no advertise-pip\n"); if (bgp->evpn_info->advertise_pip) { - if (bgp->evpn_info->pip_ip_static.s_addr != INADDR_ANY) + if (bgp->evpn_info->pip_ip_static.s_addr + != INADDR_ANY) { vty_out(vty, " advertise-pip ip %s", inet_ntop(AF_INET, &bgp->evpn_info->pip_ip_static, buf2, INET_ADDRSTRLEN)); - if (!is_zero_mac(&(bgp->evpn_info->pip_rmac_static))) { - char buf[ETHER_ADDR_STRLEN]; - - vty_out(vty, " mac %s", - prefix_mac2str(&bgp->evpn_info->pip_rmac, - buf, sizeof(buf))); + if (!is_zero_mac(&( + bgp->evpn_info->pip_rmac_static))) { + char buf[ETHER_ADDR_STRLEN]; + + vty_out(vty, " mac %s", + prefix_mac2str( + &bgp->evpn_info + ->pip_rmac, + buf, sizeof(buf))); + } + vty_out(vty, "\n"); } - vty_out(vty, "\n"); } } if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_RD_CFGD)) 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_label.c b/bgpd/bgp_label.c index 489ac6ea9f..ff1ab1a37d 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -368,7 +368,8 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, if (pnt + BGP_ADDPATH_ID_LEN > lim) return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; - addpath_id = ntohl(*((uint32_t *)pnt)); + memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN); + addpath_id = ntohl(addpath_id); pnt += BGP_ADDPATH_ID_LEN; } diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 59ed433e58..86c04b71f0 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -142,7 +142,8 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, if (pnt + BGP_ADDPATH_ID_LEN > lim) return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; - addpath_id = ntohl(*((uint32_t *)pnt)); + memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN); + addpath_id = ntohl(addpath_id); pnt += BGP_ADDPATH_ID_LEN; } 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_rd.c b/bgpd/bgp_rd.c index 571139a49a..be950dfa51 100644 --- a/bgpd/bgp_rd.c +++ b/bgpd/bgp_rd.c @@ -174,15 +174,16 @@ char *prefix_rd2str(struct prefix_rd *prd, char *buf, size_t size) if (type == RD_TYPE_AS) { decode_rd_as(pnt + 2, &rd_as); - snprintf(buf, size, "%u:%d", rd_as.as, rd_as.val); + snprintf(buf, size, "%u:%" PRIu32, rd_as.as, rd_as.val); return buf; } else if (type == RD_TYPE_AS4) { decode_rd_as4(pnt + 2, &rd_as); - snprintf(buf, size, "%u:%d", rd_as.as, rd_as.val); + snprintf(buf, size, "%u:%" PRIu32, rd_as.as, rd_as.val); return buf; } else if (type == RD_TYPE_IP) { decode_rd_ip(pnt + 2, &rd_ip); - snprintf(buf, size, "%s:%d", inet_ntoa(rd_ip.ip), rd_ip.val); + snprintf(buf, size, "%s:%" PRIu16, inet_ntoa(rd_ip.ip), + rd_ip.val); return buf; } #if ENABLE_BGP_VNC diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 3ef1ac39a5..5f4486b800 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -4357,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) @@ -4367,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) @@ -4509,7 +4515,8 @@ int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr, if (pnt + BGP_ADDPATH_ID_LEN >= lim) return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; - addpath_id = ntohl(*((uint32_t *)pnt)); + memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN); + addpath_id = ntohl(addpath_id); pnt += BGP_ADDPATH_ID_LEN; } @@ -7148,9 +7155,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; } @@ -7160,7 +7168,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; @@ -7172,7 +7180,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(); @@ -7190,9 +7198,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 @@ -7260,57 +7265,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)); } } @@ -7318,11 +7333,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"); @@ -7332,8 +7349,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); } @@ -7347,13 +7364,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", @@ -7366,14 +7385,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", @@ -7413,8 +7434,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, @@ -7430,8 +7451,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, @@ -8262,7 +8283,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; @@ -8287,7 +8308,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(); @@ -8340,8 +8361,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) { @@ -8429,32 +8448,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)); } @@ -8464,21 +8486,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)); @@ -8650,12 +8674,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"); @@ -9121,7 +9148,8 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, const char *prefix, afi_t afi, safi_t safi, enum bgp_show_type type); static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, - afi_t afi, safi_t safi, enum bgp_show_type type); + afi_t afi, safi_t safi, enum bgp_show_type type, + bool use_json); static int bgp_show_community(struct vty *vty, struct bgp *bgp, const char *comstr, int exact, afi_t afi, safi_t safi, bool use_json); @@ -9412,7 +9440,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, vty_out(vty, ",\"%s\": ", buf2); } vty_out(vty, "%s", - json_object_to_json_string(json_paths)); + json_object_to_json_string_ext( + json_paths, JSON_C_TO_STRING_PRETTY)); json_object_free(json_paths); json_paths = NULL; first = 0; @@ -10438,7 +10467,7 @@ DEFUN (show_ip_bgp_route, DEFUN (show_ip_bgp_regexp, show_ip_bgp_regexp_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] regexp REGEX...", + "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] regexp REGEX [json]", SHOW_STR IP_STR BGP_STR @@ -10446,11 +10475,14 @@ DEFUN (show_ip_bgp_regexp, BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR "Display routes matching the AS path regular expression\n" - "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") + "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n" + JSON_STR) { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; struct bgp *bgp = NULL; + bool uj = use_json(argc, argv); + char *regstr = NULL; int idx = 0; bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, @@ -10459,14 +10491,11 @@ DEFUN (show_ip_bgp_regexp, return CMD_WARNING; // get index of regex - argv_find(argv, argc, "regexp", &idx); - idx++; + if (argv_find(argv, argc, "REGEX", &idx)) + regstr = argv[idx]->arg; - char *regstr = argv_concat(argv, argc, idx); - int rc = bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi, - bgp_show_type_regexp); - XFREE(MTYPE_TMP, regstr); - return rc; + return bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi, + bgp_show_type_regexp, uj); } DEFUN (show_ip_bgp_instance_all, @@ -10499,13 +10528,14 @@ DEFUN (show_ip_bgp_instance_all, } static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, - afi_t afi, safi_t safi, enum bgp_show_type type) + afi_t afi, safi_t safi, enum bgp_show_type type, + bool use_json) { regex_t *regex; int rc; if (!config_bgp_aspath_validate(regstr)) { - vty_out(vty, "Invalid character in as-path access-list %s\n", + vty_out(vty, "Invalid character in REGEX %s\n", regstr); return CMD_WARNING_CONFIG_FAILED; } @@ -10516,7 +10546,7 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, return CMD_WARNING; } - rc = bgp_show(vty, bgp, afi, safi, type, regex, 0); + rc = bgp_show(vty, bgp, afi, safi, type, regex, use_json); bgp_regex_free(regex); return rc; } @@ -10921,56 +10951,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; } @@ -11004,6 +11054,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 diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 0ff64c527b..53d9732956 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -2691,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); @@ -2709,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); @@ -2843,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. */ @@ -7693,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"); @@ -9711,23 +9711,6 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, uptime -= p->uptime; epoch_tbuf = time(NULL) - uptime; -#if CONFDATE > 20200101 - CPP_NOTICE( - "bgpTimerUp should be deprecated and can be removed now"); -#endif - /* - * bgpTimerUp was miliseconds that was accurate - * up to 1 day, then the value returned - * became garbage. So in order to provide - * some level of backwards compatability, - * we still provde the data, but now - * we are returning the correct value - * and also adding a new bgpTimerUpMsec - * which will allow us to deprecate - * this eventually - */ - json_object_int_add(json_neigh, "bgpTimerUp", - uptime * 1000); json_object_int_add(json_neigh, "bgpTimerUpMsec", uptime * 1000); json_object_string_add(json_neigh, "bgpTimerUpString", 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 124312b1e8..0694e3ed2c 100755 --- a/configure.ac +++ b/configure.ac @@ -331,7 +331,14 @@ if test "$enable_memory_sanitizer" = "yes"; then AC_C_FLAG([-fsanitize=memory -fPIE -pie], [ AC_MSG_ERROR([$CC does not support Memory Sanitizer.]) ], [ - SAN_FLAGS="-fsanitize=memory -fPIE -pie" + SAN_FLAGS="$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="$SAN_FLAGS -fsanitize=undefined" ]) fi AC_SUBST([SAN_FLAGS]) @@ -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)])) @@ -1061,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*) @@ -1089,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]) @@ -1116,6 +1129,7 @@ case "$host_os" in ;; esac AM_CONDITIONAL([SOLARIS], [test "${SOLARIS}" = "solaris"]) +AM_CONDITIONAL([LINUX], [${is_linux}]) AC_SYS_LARGEFILE @@ -1154,6 +1168,7 @@ dnl ########################################################################## # # Logic for protobuf support. # +PROTO3=false if test "$enable_protobuf" = "yes"; then # Check for protoc & protoc-c @@ -1168,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 @@ -2266,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/README.Debian b/debian/README.Debian index 47a353310d..cbd70f82f6 100644 --- a/debian/README.Debian +++ b/debian/README.Debian @@ -61,7 +61,7 @@ OpenSSL now is not compatible with the GNU GENERAL PUBLIC LICENSE (GPL) licence that FRR is distributed under. For more explanation read: http://www.gnome.org/~markmc/openssl-and-the-gpl.html http://www.gnu.org/licenses/gpl-faq.html#GPLIncompatibleLibs -Updating the licence to explecitly allow linking against OpenSSL +Updating the licence to explicitly allow linking against OpenSSL would requite the affirmation of all people that ever contributed a significant part to Zebra / Quagga or FRR and thus are the collective "copyright holder". That's too much work. Using a shrinked down 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/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 40378f0219..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: @@ -452,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/bgp.rst b/doc/user/bgp.rst index 97ce036f52..2d4d0c4945 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1045,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/pim.rst b/doc/user/pim.rst index 4f9c573a24..6bda692607 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -11,6 +11,13 @@ vrf aware and can work within the context of vrf's in order to do S,G mrouting. Additionally PIM can be used in the EVPN underlay network for optimizing forwarding of overlay BUM traffic. +.. note:: + + On Linux for PIM-SM operation you *must* have kernel version 4.18 or greater. + To use PIM for EVPN BUM forwarding, kernels 5.0 or greater are required. + OpenBSD has no multicast support and FreeBSD, NetBSD and Solaris only + have support for SSM. + .. _starting-and-stopping-pimd: Starting and Stopping pimd @@ -197,8 +204,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 +218,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 +259,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/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/alpine/Dockerfile b/docker/alpine/Dockerfile index 88c8f88f81..ed6453e2b1 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -42,6 +42,7 @@ USER builder RUN cd /dist \ && abuild-keygen -a -n \ && abuild checksum \ + && git init \ && abuild -r -P /pkgs/apk # This stage installs frr from the apk diff --git a/docker/centos/Dockerfile b/docker/centos-7/Dockerfile index 088a3201bb..a8564bdd87 100644 --- a/docker/centos/Dockerfile +++ b/docker/centos-7/Dockerfile @@ -1,5 +1,5 @@ # This stage builds an rpm from the source -FROM centos:centos7 as centos-builder +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 \ @@ -35,9 +35,9 @@ 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-builder /rpmbuild/RPMS/ /pkgs/rpm/ +COPY --from=centos-7-builder /rpmbuild/RPMS/ /pkgs/rpm/ RUN yum install -y /pkgs/rpm/*/*.rpm \ && rm -rf /pkgs -COPY docker/centos/docker-start /usr/lib/frr/docker-start +COPY docker/centos-7/docker-start /usr/lib/frr/docker-start ENTRYPOINT [ "/usr/lib/frr/docker-start" ] diff --git a/docker/centos/build.sh b/docker/centos-7/build.sh index 9cd0f618e4..b3022d7c78 100755 --- a/docker/centos/build.sh +++ b/docker/centos-7/build.sh @@ -8,24 +8,24 @@ set -e GITREV="$(git rev-parse --short=10 HEAD)" PKGVER="$(printf '%u\n' 0x$GITREV)" -mkdir -p docker/centos/pkgs +mkdir -p docker/centos-7/pkgs docker build \ - --file=docker/centos/Dockerfile \ + --file=docker/centos-7/Dockerfile \ --build-arg="PKGVER=$PKGVER" \ - --tag="frr:centos-builder-$GITREV" \ - --target=centos-builder \ + --tag="frr:centos-7-builder-$GITREV" \ + --target=centos-7-builder \ . # Copy RPM package from container to host -CONTAINER_ID="$(docker create "frr:centos-builder-$GITREV")" -docker cp "${CONTAINER_ID}:/rpmbuild/RPMS/x86_64/" docker/centos/pkgs +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-builder-$GITREV" \ - --file=docker/centos/Dockerfile \ + --cache-from="frr:centos-7-builder-$GITREV" \ + --file=docker/centos-7/Dockerfile \ --build-arg="PKGVER=$PKGVER" \ - --tag="frr:centos-$GITREV" \ + --tag="frr:centos-7-$GITREV" \ . -docker rmi "frr:centos-builder-$GITREV" +docker rmi "frr:centos-7-builder-$GITREV" diff --git a/docker/centos/docker-start b/docker/centos-7/docker-start index a3913245b6..a3913245b6 100755 --- a/docker/centos/docker-start +++ b/docker/centos-7/docker-start 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..4a9918486c --- /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-8-builder-$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/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/isisd.c b/isisd/isisd.c index f15d7a9c7e..47d2e9faab 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -107,13 +107,10 @@ struct isis_area *isis_area_create(const char *area_tag) /* * Fabricd runs only as level-2. - * For IS-IS, the first instance is level-1-2 rest are level-1, - * unless otherwise configured + * For IS-IS, the default is level-1-2 */ - if (fabricd) { + if (fabricd) area->is_type = IS_LEVEL_2; - } else if (listcount(isis->area_list) == 0) - area->is_type = IS_LEVEL_1_AND_2; else area->is_type = yang_get_default_enum( "/frr-isisd:isis/instance/is-type"); 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_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/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 a23874a517..8ccdbfcbc1 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -38,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; @@ -491,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]; @@ -550,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(); @@ -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); @@ -137,7 +137,12 @@ static int if_cmp_func(const struct interface *ifp1, static int if_cmp_index_func(const struct interface *ifp1, const struct interface *ifp2) { - return ifp1->ifindex - ifp2->ifindex; + if (ifp1->ifindex == ifp2->ifindex) + return 0; + else if (ifp1->ifindex > ifp2->ifindex) + return 1; + else + return -1; } static void ifp_connected_free(void *arg) @@ -262,7 +267,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/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 ce8034846f..d2ab70e209 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -118,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: @@ -224,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. */ @@ -550,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 72a4acedb2..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); diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index b810d13a5a..0051cba625 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -42,6 +42,7 @@ struct nexthop_hold { union sockunion *addr; char *intf; char *labels; + uint32_t weight; }; struct nexthop_group_hooks { @@ -243,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; + struct nexthop *position, *prev; - /* Try to just append to the end first - * This trust it is already sorted - */ + /* Ensure this gets set */ + nexthop->next = NULL; - tail = nexthop_group_tail(nhg); - - if (tail && (nexthop_cmp(tail, nexthop) < 0)) { - tail->next = nexthop; - nexthop->prev = tail; - - return; - } - - 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; @@ -270,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; @@ -281,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. */ @@ -308,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) { @@ -526,8 +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 *labels) + const char *intf, const char *labels, + const uint32_t weight) { struct nexthop_hold *nh; @@ -542,23 +588,26 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc, if (labels) nh->labels = XSTRDUP(MTYPE_TMP, labels); + nh->weight = weight; + listnode_add_sort(nhgc->nhg_list, nh); } static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, const char *nhvrf_name, const union sockunion *addr, - const char *intf, - const char *labels) + 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 && - nhgc_cmp_helper(labels, nh->labels) == 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; } @@ -581,8 +630,8 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, static bool nexthop_group_parse_nexthop(struct nexthop *nhop, const union sockunion *addr, const char *intf, const char *name, - const char *labels, - int *lbl_ret) + const char *labels, int *lbl_ret, + uint32_t weight) { int ret = 0; struct vrf *vrf; @@ -639,6 +688,8 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, num, larray); } + nhop->weight = weight; + return true; } @@ -648,11 +699,9 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop, 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)); + return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf, + nhh->nhvrf_name, nhh->labels, NULL, + nhh->weight)); } DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, @@ -664,6 +713,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, [{ \ nexthop-vrf NAME$vrf_name \ |label WORD \ + |weight (1-255) \ }]", NO_STR "Specify one of the nexthops in this ECMP group\n" @@ -674,7 +724,9 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "If the nexthop is in a different vrf tell us\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") + "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; @@ -682,8 +734,8 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, int lbl_ret = 0; bool legal; - legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, - label, &lbl_ret); + 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)) { @@ -716,7 +768,8 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, nh = nexthop_exists(&nhgc->nhg, &nhop); if (no) { - nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label); + nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label, + weight); if (nh) { _nexthop_del(&nhgc->nhg, nh); @@ -734,7 +787,8 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, _nexthop_add(&nhgc->nhg.nexthop, nh); } - nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label); + nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label, + weight); if (legal && nhg_hooks.add_nexthop) nhg_hooks.add_nexthop(nhgc, nh); @@ -743,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 @@ -794,6 +848,9 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) vty_out(vty, " label %s", buf); } + if (nh->weight) + vty_out(vty, " weight %u", nh->weight); + vty_out(vty, "\n"); } @@ -816,6 +873,9 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty, if (nh->labels) vty_out(vty, " label %s", nh->labels); + if (nh->weight) + vty_out(vty, " weight %u", nh->weight); + vty_out(vty, "\n"); } diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index c90b21737a..73b020283a 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -44,6 +44,13 @@ 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); @@ -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/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/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 5ce1150b05..fd1b181e58 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: @@ -882,6 +888,9 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, } } + 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); @@ -920,6 +929,9 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, 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), @@ -1082,6 +1094,9 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, 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, @@ -2664,6 +2679,17 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl) } STREAM_GETW(s, zl->nexthop_num); + + if (zl->nexthop_num > MULTIPATH_NUM) { + flog_warn( + EC_LIB_ZAPI_ENCODE, + "%s: Prefix %pFX has %d nexthops, but we can only use the first %d", + __func__, &zl->route.prefix, zl->nexthop_num, + MULTIPATH_NUM); + } + + zl->nexthop_num = MIN(MULTIPATH_NUM, zl->nexthop_num); + for (int i = 0; i < zl->nexthop_num; i++) { znh = &zl->nexthops[i]; diff --git a/lib/zclient.h b/lib/zclient.h index 9c5c65ffc5..70304127a2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -323,6 +323,8 @@ struct zapi_nexthop { mpls_label_t labels[MPLS_MAX_LABELS]; struct ethaddr rmac; + + uint32_t weight; }; /* @@ -330,6 +332,7 @@ struct zapi_nexthop { */ #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 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_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/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index cfcffcdb3d..8efb32af37 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -906,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--; } 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/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 06ad0f40a4..ec0327d74f 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -279,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 7375e6d9b8..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; @@ -1994,7 +1989,7 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty, "\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]; @@ -2429,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; @@ -2444,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]; @@ -2716,7 +2710,6 @@ static void pim_show_join_desired_helper(struct pim_instance *pim, static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, bool uj) { - struct listnode *upnode; struct pim_upstream *up; json_object *json = NULL; @@ -2727,7 +2720,7 @@ static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, vty_out(vty, "Source Group EvalJD\n"); - for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) { + frr_each (rb_pim_upstream, &pim->upstream_head, up) { /* scan all interfaces */ pim_show_join_desired_helper(pim, vty, up, json, uj); @@ -2743,7 +2736,6 @@ static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, 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; @@ -2755,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]; @@ -2876,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; @@ -2893,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]; @@ -3933,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__); } } @@ -5420,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) @@ -5828,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; @@ -5863,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]; @@ -5968,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++; @@ -6915,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" @@ -6941,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), @@ -6957,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 @@ -6984,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); @@ -7854,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" @@ -7914,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; @@ -7941,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; } @@ -7959,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" @@ -8021,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; @@ -8048,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; } @@ -10528,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); 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_mroute.c b/pimd/pim_mroute.c index 1fe2289a8e..3459abbc19 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -590,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) { diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index 8a18594fd7..58ebc6ce67 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -565,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_nht.c b/pimd/pim_nht.c index bd8424b3e4..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; diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index 65c6bdd2bc..598988f88f 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -64,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)) @@ -86,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; + + while ((c_oil = rb_pim_oil_pop(&pim->channel_oil_head))) + pim_channel_oil_free(c_oil); - if (pim->channel_oil_hash) - hash_free(pim->channel_oil_hash); - pim->channel_oil_hash = NULL; + rb_pim_oil_fini(&pim->channel_oil_head); } void pim_channel_oil_free(struct channel_oil *c_oil) @@ -144,7 +115,7 @@ 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; } @@ -187,7 +158,6 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, 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 = MAXVIFS; c_oil->oil_ref_count = 1; @@ -195,7 +165,7 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, 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): c_oil %s add", @@ -224,8 +194,7 @@ struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, * 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; diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index de7fde05da..788ddaa16c 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -90,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; @@ -106,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); diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index bd295b0fef..2db39bac4b 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -446,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) @@ -554,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 */ @@ -650,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; @@ -723,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]; @@ -800,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 */ @@ -852,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 */ @@ -893,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) { @@ -942,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; diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index ce26d2b398..afd10bd3db 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -98,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)) @@ -108,7 +107,7 @@ 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)) { @@ -191,9 +190,8 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, 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); + 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); @@ -235,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); @@ -533,10 +530,9 @@ 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 @@ -639,9 +635,8 @@ void pim_upstream_update_use_rpt(struct pim_upstream *up, void pim_upstream_reeval_use_rpt(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; @@ -756,11 +751,9 @@ void pim_upstream_switch(struct pim_instance *pim, 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) { - 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; @@ -811,7 +804,7 @@ static struct pim_upstream *pim_upstream_new(struct pim_instance *pim, 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 */ @@ -825,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; @@ -884,14 +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; 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", @@ -909,7 +900,7 @@ 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; } @@ -1169,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]; @@ -1789,8 +1777,6 @@ 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; @@ -1798,7 +1784,7 @@ void pim_upstream_find_new_rpf(struct pim_instance *pim) /* * 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( @@ -1838,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); @@ -1992,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; @@ -2032,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; @@ -2042,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; @@ -2076,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 ab50820518..1eb2052bb3 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -169,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 @@ -198,6 +199,7 @@ 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*/ @@ -326,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); 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_zebra.c b/pimd/pim_zebra.c index 0417d0d063..06507b1f4c 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -392,16 +392,13 @@ static void pim_zebra_vxlan_replay(void) void pim_scan_oil(struct pim_instance *pim) { - struct listnode *node; - struct listnode *nextnode; struct channel_oil *c_oil; pim->scan_oil_last = pim_time_monotonic_sec(); ++pim->scan_oil_events; - for (ALL_LIST_ELEMENTS(pim->channel_oil_list, node, nextnode, c_oil)) { + 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) diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index b3f9ac7630..670bc6f4c9 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 @@ -428,7 +444,7 @@ zebra_spec_add_service () { # Add port /etc/services entry if it isn't already there if [ -f %{_sysconfdir}/services ] && \ - ! %__sed -e 's/#.*$//' %{_sysconfdir}/services | %__grep -wq $1 ; then + ! %__sed -e 's/#.*$//' %{_sysconfdir}/services 2>/dev/null | %__grep -wq $1 ; then echo "$1 $2 # $3" >> %{_sysconfdir}/services fi } @@ -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_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_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 797398c791..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; 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/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_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/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 46cd12d71c..03a08dd86b 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); @@ -528,8 +537,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 @@ -1051,6 +1061,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 @@ -1077,8 +1089,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) { @@ -1091,6 +1105,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 */ @@ -1726,7 +1761,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; @@ -2003,11 +2038,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 e8df08ef60..13413888bf 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -103,7 +103,7 @@ sub scan_file { $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/yang/frr-isisd.yang b/yang/frr-isisd.yang index faa880eff4..faab1e55b2 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -27,6 +27,10 @@ module frr-isisd { description "This module defines a model for managing FRR isisd daemon."; + revision 2019-12-17 { + description + "Changed default area is-type to level-1-2"; + } revision 2019-09-09 { description "Changed interface references to use @@ -748,7 +752,7 @@ module frr-isisd { leaf is-type { type level; - default "level-1"; + default "level-1-2"; description "Level of the IS-IS routing instance (OSI only)."; } diff --git a/zebra/debug.c b/zebra/debug.c index 8e5fb0ea10..681dfb8753 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -39,6 +39,7 @@ unsigned long zebra_debug_vxlan; unsigned long zebra_debug_pw; unsigned long zebra_debug_dplane; unsigned long zebra_debug_mlag; +unsigned long zebra_debug_nexthop; DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty)); @@ -103,6 +104,10 @@ DEFUN_NOSH (show_debugging_zebra, vty_out(vty, " Zebra dataplane debugging is on\n"); if (IS_ZEBRA_DEBUG_MLAG) vty_out(vty, " Zebra mlag debugging is on\n"); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + vty_out(vty, "Zebra detailed nexthop debugging is on\n"); + else if (IS_ZEBRA_DEBUG_NHG) + vty_out(vty, "Zebra nexthop debugging is on\n"); hook_call(zebra_debug_show_debugging, vty); return CMD_SUCCESS; @@ -443,6 +448,28 @@ DEFUN (no_debug_zebra_dplane, return CMD_SUCCESS; } +DEFPY (debug_zebra_nexthop, + debug_zebra_nexthop_cmd, + "[no$no] debug zebra nexthop [detail$detail]", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug zebra nexthop events\n" + "Detailed information\n") +{ + if (no) + zebra_debug_nexthop = 0; + else { + SET_FLAG(zebra_debug_nexthop, ZEBRA_DEBUG_NHG); + + if (detail) + SET_FLAG(zebra_debug_nexthop, + ZEBRA_DEBUG_NHG_DETAILED); + } + + return CMD_SUCCESS; +} + /* Debug node. */ struct cmd_node debug_node = {DEBUG_NODE, "", /* Debug node has no interface. */ 1}; @@ -546,6 +573,7 @@ void zebra_debug_init(void) zebra_debug_dplane = 0; zebra_debug_mlag = 0; zebra_debug_nht = 0; + zebra_debug_nexthop = 0; install_node(&debug_node, config_write_debug); @@ -563,6 +591,7 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &debug_zebra_fpm_cmd); install_element(ENABLE_NODE, &debug_zebra_dplane_cmd); install_element(ENABLE_NODE, &debug_zebra_mlag_cmd); + install_element(ENABLE_NODE, &debug_zebra_nexthop_cmd); install_element(ENABLE_NODE, &no_debug_zebra_events_cmd); install_element(ENABLE_NODE, &no_debug_zebra_nht_cmd); install_element(ENABLE_NODE, &no_debug_zebra_mpls_cmd); @@ -585,6 +614,7 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &debug_zebra_rib_cmd); install_element(CONFIG_NODE, &debug_zebra_fpm_cmd); install_element(CONFIG_NODE, &debug_zebra_dplane_cmd); + install_element(CONFIG_NODE, &debug_zebra_nexthop_cmd); install_element(CONFIG_NODE, &no_debug_zebra_events_cmd); install_element(CONFIG_NODE, &no_debug_zebra_nht_cmd); install_element(CONFIG_NODE, &no_debug_zebra_mpls_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index 176226f7ae..e513f8865d 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -59,6 +59,9 @@ extern "C" { #define ZEBRA_DEBUG_MLAG 0x01 +#define ZEBRA_DEBUG_NHG 0x01 +#define ZEBRA_DEBUG_NHG_DETAILED 0x02 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -92,6 +95,11 @@ extern "C" { #define IS_ZEBRA_DEBUG_MLAG (zebra_debug_mlag & ZEBRA_DEBUG_MLAG) +#define IS_ZEBRA_DEBUG_NHG (zebra_debug_nexthop & ZEBRA_DEBUG_NHG) + +#define IS_ZEBRA_DEBUG_NHG_DETAIL \ + (zebra_debug_nexthop & ZEBRA_DEBUG_NHG_DETAILED) + extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; extern unsigned long zebra_debug_kernel; @@ -103,6 +111,7 @@ extern unsigned long zebra_debug_vxlan; extern unsigned long zebra_debug_pw; extern unsigned long zebra_debug_dplane; extern unsigned long zebra_debug_mlag; +extern unsigned long zebra_debug_nexthop; extern void zebra_debug_init(void); diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index c09007bcb1..4731d1ed15 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -366,7 +366,7 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, } } -static int get_iflink_speed(struct interface *interface, int *error) +static uint32_t get_iflink_speed(struct interface *interface, int *error) { struct ifreq ifdata; struct ethtool_cmd ecmd; @@ -419,7 +419,7 @@ static int get_iflink_speed(struct interface *interface, int *error) close(sd); - return (ecmd.speed_hi << 16) | ecmd.speed; + return ((uint32_t)ecmd.speed_hi << 16) | ecmd.speed; } uint32_t kernel_get_speed(struct interface *ifp, int *error) @@ -1467,7 +1467,7 @@ int netlink_protodown(struct interface *ifp, bool down) req.ifa.ifi_index = ifp->ifindex; - addattr_l(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, 4); + addattr_l(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, sizeof(down)); addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifp->ifindex, 4); return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 23f1a3bf86..c3d5bf8428 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -290,6 +290,18 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return netlink_neigh_change(h, ns_id); case RTM_DELNEIGH: return netlink_neigh_change(h, ns_id); + case RTM_GETNEIGH: + /* + * Kernel in some situations when it expects + * user space to resolve arp entries, we will + * receive this notification. As we don't + * need this notification and as that + * we don't want to spam the log file with + * below messages, just ignore. + */ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Received RTM_GETNEIGH, ignoring"); + break; case RTM_NEWRULE: return netlink_rule_change(h, ns_id, startup); case RTM_DELRULE: @@ -1086,7 +1098,7 @@ int netlink_request(struct nlsock *nl, struct nlmsghdr *n) netlink_socket (). */ void kernel_init(struct zebra_ns *zns) { - unsigned long groups; + uint32_t groups; #if defined SOL_NETLINK int one, ret; #endif @@ -1107,9 +1119,9 @@ void kernel_init(struct zebra_ns *zns) RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_MROUTE | RTMGRP_NEIGH | - (1 << (RTNLGRP_IPV4_RULE - 1)) | - (1 << (RTNLGRP_IPV6_RULE - 1)) | - (1 << (RTNLGRP_NEXTHOP - 1)); + ((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) | + ((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) | + ((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1)); snprintf(zns->netlink.name, sizeof(zns->netlink.name), "netlink-listen (NS %u)", zns->ns_id); 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 f3a255fd29..29a341abbd 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -467,6 +467,8 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_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); @@ -785,34 +787,10 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, } else { if (!tb[RTA_MULTIPATH]) { struct nexthop nh; - size_t sz = (afi == AFI_IP) ? 4 : 16; - - memset(&nh, 0, sizeof(nh)); - if (bh_type == BLACKHOLE_UNSPEC) { - if (index && !gate) - nh.type = NEXTHOP_TYPE_IFINDEX; - else if (index && gate) - nh.type = - (afi == AFI_IP) - ? NEXTHOP_TYPE_IPV4_IFINDEX - : NEXTHOP_TYPE_IPV6_IFINDEX; - else if (!index && gate) - nh.type = - (afi == AFI_IP) - ? NEXTHOP_TYPE_IPV4 - : NEXTHOP_TYPE_IPV6; - else { - nh.type = - NEXTHOP_TYPE_BLACKHOLE; - nh.bh_type = BLACKHOLE_UNSPEC; - } - } else { - nh.type = NEXTHOP_TYPE_BLACKHOLE; - nh.bh_type = bh_type; - } - nh.ifindex = index; - if (gate) - memcpy(&nh.gate, gate, sz); + + nh = parse_nexthop_unicast( + ns_id, rtm, tb, bh_type, index, prefsrc, + gate, afi, vrf_id); rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, &src_p, &nh, 0, table, metric, distance, true); @@ -1419,6 +1397,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, @@ -1921,7 +1902,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) @@ -2039,6 +2020,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)); @@ -2347,7 +2331,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 5dd6012f62..e9a97d4b15 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -965,16 +965,25 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable) ifindex_t ifindex; struct interface *ifp; struct zebra_if *zif; - int ra_interval; + int ra_interval_rxd; s = msg; /* Get interface index and RA interval. */ STREAM_GETL(s, ifindex); - STREAM_GETL(s, ra_interval); + STREAM_GETL(s, ra_interval_rxd); + + if (ra_interval_rxd < 0) { + zlog_warn( + "Requested RA interval %d is garbage; ignoring request", + ra_interval_rxd); + return; + } + + unsigned int ra_interval = ra_interval_rxd; if (IS_ZEBRA_DEBUG_EVENT) - zlog_debug("%u: IF %u RA %s from client %s, interval %ds", + zlog_debug("%u: IF %u RA %s from client %s, interval %ums", zvrf_id(zvrf), ifindex, enable ? "enable" : "disable", zebra_route_string(client->proto), ra_interval); @@ -1001,7 +1010,7 @@ static void zebra_interface_radv_set(ZAPI_HANDLER_ARGS, int enable) SET_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED); ipv6_nd_suppress_ra_set(ifp, RA_ENABLE); if (ra_interval - && (ra_interval * 1000) < zif->rtadv.MaxRtrAdvInterval + && (ra_interval * 1000) < (unsigned int) zif->rtadv.MaxRtrAdvInterval && !CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED)) zif->rtadv.MaxRtrAdvInterval = ra_interval * 1000; 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 e1654a1a3c..5a63c1e4f6 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -567,6 +567,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, 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; @@ -1544,6 +1545,9 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) 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_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX @@ -2132,6 +2136,7 @@ static void zread_pseudowire(ZAPI_HANDLER_ARGS) /* Get data. */ STREAM_GET(ifname, s, IF_NAMESIZE); + ifname[IF_NAMESIZE - 1] = '\0'; STREAM_GETL(s, ifindex); STREAM_GETL(s, type); STREAM_GETL(s, af); @@ -2356,6 +2361,20 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) if (zpr.rule.filter.fwmark) zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK; + if (!(zpr.rule.filter.src_ip.family == AF_INET + || zpr.rule.filter.src_ip.family == AF_INET6)) { + zlog_warn("Unsupported PBR source IP family: %s\n", + family2str(zpr.rule.filter.src_ip.family)); + return; + } + if (!(zpr.rule.filter.dst_ip.family == AF_INET + || zpr.rule.filter.dst_ip.family == AF_INET6)) { + zlog_warn("Unsupported PBR dest IP family: %s\n", + family2str(zpr.rule.filter.dst_ip.family)); + return; + } + + zpr.vrf_id = zvrf->vrf->vrf_id; if (hdr->command == ZEBRA_RULE_ADD) zebra_pbr_add_rule(&zpr); @@ -2412,6 +2431,7 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS) zpi.sock = client->sock; STREAM_GETL(s, zpi.unique); STREAM_GET(&ipset.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); + ipset.ipset_name[ZEBRA_IPSET_NAME_SIZE - 1] = '\0'; STREAM_GETC(s, zpi.src.family); STREAM_GETC(s, zpi.src.prefixlen); STREAM_GET(&zpi.src.u.prefix, s, prefix_blen(&zpi.src)); @@ -2443,6 +2463,13 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS) /* calculate backpointer */ zpi.backpointer = zebra_pbr_lookup_ipset_pername(ipset.ipset_name); + + if (!zpi.backpointer) { + zlog_warn("ipset name specified: %s does not exist", + ipset.ipset_name); + goto stream_failure; + } + if (hdr->command == ZEBRA_IPSET_ENTRY_ADD) zebra_pbr_add_ipset_entry(&zpi); else @@ -2455,37 +2482,39 @@ stream_failure: static inline void zread_iptable(ZAPI_HANDLER_ARGS) { - struct zebra_pbr_iptable zpi; + struct zebra_pbr_iptable *zpi = + XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable)); struct stream *s; s = msg; - memset(&zpi, 0, sizeof(zpi)); - - zpi.interface_name_list = list_new(); - zpi.sock = client->sock; - zpi.vrf_id = zvrf->vrf->vrf_id; - STREAM_GETL(s, zpi.unique); - STREAM_GETL(s, zpi.type); - STREAM_GETL(s, zpi.filter_bm); - STREAM_GETL(s, zpi.action); - STREAM_GETL(s, zpi.fwmark); - STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); - STREAM_GETW(s, zpi.pkt_len_min); - STREAM_GETW(s, zpi.pkt_len_max); - STREAM_GETW(s, zpi.tcp_flags); - STREAM_GETW(s, zpi.tcp_mask_flags); - STREAM_GETC(s, zpi.dscp_value); - STREAM_GETC(s, zpi.fragment); - STREAM_GETC(s, zpi.protocol); - STREAM_GETL(s, zpi.nb_interface); - zebra_pbr_iptable_update_interfacelist(s, &zpi); + zpi->interface_name_list = list_new(); + zpi->sock = client->sock; + zpi->vrf_id = zvrf->vrf->vrf_id; + STREAM_GETL(s, zpi->unique); + STREAM_GETL(s, zpi->type); + STREAM_GETL(s, zpi->filter_bm); + STREAM_GETL(s, zpi->action); + STREAM_GETL(s, zpi->fwmark); + STREAM_GET(&zpi->ipset_name, s, ZEBRA_IPSET_NAME_SIZE); + STREAM_GETW(s, zpi->pkt_len_min); + STREAM_GETW(s, zpi->pkt_len_max); + STREAM_GETW(s, zpi->tcp_flags); + STREAM_GETW(s, zpi->tcp_mask_flags); + STREAM_GETC(s, zpi->dscp_value); + STREAM_GETC(s, zpi->fragment); + STREAM_GETC(s, zpi->protocol); + STREAM_GETL(s, zpi->nb_interface); + zebra_pbr_iptable_update_interfacelist(s, zpi); if (hdr->command == ZEBRA_IPTABLE_ADD) - zebra_pbr_add_iptable(&zpi); + zebra_pbr_add_iptable(zpi); else - zebra_pbr_del_iptable(&zpi); + zebra_pbr_del_iptable(zpi); + stream_failure: + zebra_pbr_iptable_free(zpi); + zpi = NULL; return; } @@ -2593,6 +2622,14 @@ void zserv_handle_commands(struct zserv *client, struct stream *msg) struct zmsghdr hdr; struct zebra_vrf *zvrf; + if (STREAM_READABLE(msg) > ZEBRA_MAX_PACKET_SIZ) { + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug( + "ZAPI message is %zu bytes long but the maximum packet size is %u; dropping", + STREAM_READABLE(msg), ZEBRA_MAX_PACKET_SIZ); + return; + } + zapi_parse_header(msg, &hdr); if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index ca72ea5227..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( @@ -2508,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; } 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_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_nhg.c b/zebra/zebra_nhg.c index 1a70250627..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 * @@ -593,7 +594,7 @@ zebra_nhg_find_nexthop(uint32_t id, struct nexthop *nh, afi_t afi, int type) 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; } @@ -1047,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; @@ -1790,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_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_pbr.c b/zebra/zebra_pbr.c index e24d2e2b42..0c3adcdfa1 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -345,11 +345,13 @@ void zebra_pbr_iptable_free(void *arg) iptable = (struct zebra_pbr_iptable *)arg; hook_call(zebra_pbr_iptable_update, 0, iptable); - for (ALL_LIST_ELEMENTS(iptable->interface_name_list, - node, nnode, name)) { - XFREE(MTYPE_PBR_IPTABLE_IFNAME, name); - list_delete_node(iptable->interface_name_list, - node); + if (iptable->interface_name_list) { + for (ALL_LIST_ELEMENTS(iptable->interface_name_list, node, + nnode, name)) { + XFREE(MTYPE_PBR_IPTABLE_IFNAME, name); + list_delete_node(iptable->interface_name_list, node); + } + list_delete(&iptable->interface_name_list); } XFREE(MTYPE_TMP, iptable); } @@ -688,6 +690,7 @@ void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable) list_delete_node(iptable->interface_name_list, node); } + list_delete(&iptable->interface_name_list); XFREE(MTYPE_TMP, lookup); } else zlog_debug("%s: IPTable being deleted we know nothing about", diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 46f1385520..b48756302a 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -1295,7 +1295,6 @@ static void zebra_ptm_send_bfdd(struct stream *msg) } stream_free(msgc); - stream_free(msg); } static void zebra_ptm_send_clients(struct stream *msg) @@ -1327,7 +1326,6 @@ static void zebra_ptm_send_clients(struct stream *msg) } stream_free(msgc); - stream_free(msg); } static int _zebra_ptm_bfd_client_deregister(struct zserv *zs) @@ -1402,37 +1400,25 @@ static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf, struct stream *msg, uint32_t command) { struct stream *msgc; - size_t zmsglen, zhdrlen; + char buf[ZEBRA_MAX_PACKET_SIZ]; pid_t ppid; - /* - * Don't modify message in the zebra API. In order to do that we - * need to allocate a new message stream and copy the message - * provided by zebra. - */ + /* Create BFD header */ msgc = stream_new(ZEBRA_MAX_PACKET_SIZ); - if (msgc == NULL) { - zlog_debug("%s: not enough memory", __func__); - return; - } - - /* Calculate our header size plus the message contents. */ - zhdrlen = ZEBRA_HEADER_SIZE + sizeof(uint32_t); - zmsglen = msg->endp - msg->getp; - memcpy(msgc->data + zhdrlen, msg->data + msg->getp, zmsglen); - - /* - * The message type will be BFD_DEST_REPLY so we can use only - * one callback at the `bfdd` side, however the real command - * number will be included right after the zebra header. - */ zclient_create_header(msgc, ZEBRA_BFD_DEST_REPLAY, zvrf->vrf->vrf_id); stream_putl(msgc, command); - /* Update the data pointers. */ - msgc->getp = 0; - msgc->endp = zhdrlen + zmsglen; - stream_putw_at(msgc, 0, stream_get_endp(msgc)); + if (STREAM_READABLE(msg) > STREAM_WRITEABLE(msgc)) { + zlog_warn("Cannot fit extended BFD header plus original message contents into ZAPI packet; dropping message"); + goto stream_failure; + } + + /* Copy original message, excluding header, into new message */ + stream_get_from(buf, msg, stream_get_getp(msg), STREAM_READABLE(msg)); + stream_put(msgc, buf, STREAM_READABLE(msg)); + + /* Update length field */ + stream_putw_at(msgc, 0, STREAM_READABLE(msgc)); zebra_ptm_send_bfdd(msgc); @@ -1443,6 +1429,7 @@ static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf, return; stream_failure: + stream_free(msgc); zlog_err("%s:%d failed to registrate client pid", __FILE__, __LINE__); } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index d525da26ee..051d7f5231 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -228,7 +228,7 @@ int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new) if (old) zebra_nhg_decrement_ref(old); - } else if (!re->nhe->nhg) + } else if (!re->nhe) /* This is the first time it's being attached */ route_entry_attach_ref(re, new); @@ -1369,6 +1369,14 @@ 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->nhe->nhg? @@ -1431,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 */ @@ -1788,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; } @@ -1808,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 @@ -1820,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; } /* @@ -1865,11 +1898,6 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) /* Redistribute, lsp, and nht update */ redistribute_update(dest_pfx, src_pfx, re, NULL); - zebra_rib_evaluate_rn_nexthops( - rn, zebra_router_get_next_sequence()); - - zebra_rib_evaluate_mpls(rn); - } else if (start_count > 0 && end_count == 0) { if (debug_p) zlog_debug("%u:%s un-installed transition from dplane notification", @@ -1888,12 +1916,13 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) /* Redistribute, lsp, and nht update */ redistribute_delete(dest_pfx, src_pfx, re, NULL); + } - zebra_rib_evaluate_rn_nexthops( - rn, zebra_router_get_next_sequence()); + /* Make any changes visible for lsp and nexthop-tracking processing */ + zebra_rib_evaluate_rn_nexthops( + rn, zebra_router_get_next_sequence()); - zebra_rib_evaluate_mpls(rn); - } + zebra_rib_evaluate_mpls(rn); done: if (rn) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index eca86d5fe2..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, @@ -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"); @@ -1240,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"); } @@ -1684,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 @@ -1693,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; @@ -1716,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; @@ -1732,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; @@ -1751,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 @@ -1775,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"); + } } /* @@ -1809,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; @@ -1820,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) { @@ -1849,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"); + } } /* diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 086b13d670..564573dcb3 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -7682,6 +7682,55 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp, return zvni_remote_neigh_update(zvni, ifp, ip, macaddr, state); } +static int32_t +zebra_vxlan_remote_macip_helper(bool add, struct stream *s, vni_t *vni, + struct ethaddr *macaddr, uint16_t *ipa_len, + struct ipaddr *ip, struct in_addr *vtep_ip, + uint8_t *flags, uint32_t *seq) +{ + uint16_t l = 0; + + /* + * Obtain each remote MACIP and process. + * Message contains VNI, followed by MAC followed by IP (if any) + * followed by remote VTEP IP. + */ + memset(ip, 0, sizeof(*ip)); + STREAM_GETL(s, *vni); + STREAM_GET(macaddr->octet, s, ETH_ALEN); + STREAM_GETL(s, *ipa_len); + + if (*ipa_len) { + if (*ipa_len == IPV4_MAX_BYTELEN) + ip->ipa_type = IPADDR_V4; + else if (*ipa_len == IPV6_MAX_BYTELEN) + ip->ipa_type = IPADDR_V6; + else { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "ipa_len *must* be %d or %d bytes in length not %d", + IPV4_MAX_BYTELEN, IPV6_MAX_BYTELEN, + *ipa_len); + goto stream_failure; + } + + STREAM_GET(&ip->ip.addr, s, *ipa_len); + } + l += 4 + ETH_ALEN + 4 + *ipa_len; + STREAM_GET(&vtep_ip->s_addr, s, IPV4_MAX_BYTELEN); + l += IPV4_MAX_BYTELEN; + + if (add) { + STREAM_GETC(s, *flags); + STREAM_GETL(s, *seq); + l += 5; + } + + return l; + +stream_failure: + return -1; +} /* * Handle message from client to delete a remote MACIP for a VNI. @@ -7704,23 +7753,14 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS) s = msg; while (l < hdr->length) { - /* Obtain each remote MACIP and process. */ - /* Message contains VNI, followed by MAC followed by IP (if any) - * followed by remote VTEP IP. - */ - memset(&ip, 0, sizeof(ip)); - STREAM_GETL(s, vni); - STREAM_GET(&macaddr.octet, s, ETH_ALEN); - STREAM_GETL(s, ipa_len); - if (ipa_len) { - ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 - : IPADDR_V6; - STREAM_GET(&ip.ip.addr, s, ipa_len); - } - l += 4 + ETH_ALEN + 4 + ipa_len; - STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); - l += IPV4_MAX_BYTELEN; + int res_length = zebra_vxlan_remote_macip_helper( + false, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip, NULL, + NULL); + if (res_length == -1) + goto stream_failure; + + l += res_length; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "Recv MACIP DEL VNI %u MAC %s%s%s Remote VTEP %s from %s", @@ -7769,29 +7809,14 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS) s = msg; while (l < hdr->length) { - /* Obtain each remote MACIP and process. */ - /* Message contains VNI, followed by MAC followed by IP (if any) - * followed by remote VTEP IP. - */ - memset(&ip, 0, sizeof(ip)); - STREAM_GETL(s, vni); - STREAM_GET(&macaddr.octet, s, ETH_ALEN); - STREAM_GETL(s, ipa_len); - if (ipa_len) { - ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 - : IPADDR_V6; - STREAM_GET(&ip.ip.addr, s, ipa_len); - } - l += 4 + ETH_ALEN + 4 + ipa_len; - STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN); - l += IPV4_MAX_BYTELEN; + int res_length = zebra_vxlan_remote_macip_helper( + true, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip, + &flags, &seq); - /* Get flags - sticky mac and/or gateway mac */ - STREAM_GETC(s, flags); - l++; - STREAM_GETL(s, seq); - l += 4; + if (res_length == -1) + goto stream_failure; + l += res_length; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "Recv MACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %s from %s", diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 989ea464e7..100bb0e093 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -162,8 +162,7 @@ static inline const char *zl3vni_rmac2str(zebra_l3vni_t *zl3vni, char *buf, char *ptr; if (!buf) - ptr = (char *)XMALLOC(MTYPE_TMP, - ETHER_ADDR_STRLEN * sizeof(char)); + ptr = XMALLOC(MTYPE_TMP, ETHER_ADDR_STRLEN * sizeof(char)); else { assert(size >= ETHER_ADDR_STRLEN); ptr = buf; @@ -200,8 +199,7 @@ static inline const char *zl3vni_sysmac2str(zebra_l3vni_t *zl3vni, char *buf, char *ptr; if (!buf) - ptr = (char *)XMALLOC(MTYPE_TMP, - ETHER_ADDR_STRLEN * sizeof(char)); + ptr = XMALLOC(MTYPE_TMP, ETHER_ADDR_STRLEN * sizeof(char)); else { assert(size >= ETHER_ADDR_STRLEN); ptr = buf; |
