ospf6d: ospf6_flood.c: self-originated MaxAge LSAs to install and refresh.
/Makefile
/Makefile.in
+/symalyzer_report.html
+/jquery-3.4.1.min.js
+/jquery-3.4.1.min.js.tmp
+
### autoconf/automake subdir stuff
.deps
vrrpd/Makefile \
# end
-noinst_HEADERS += defaults.h
-
clean-local: clean-python
.PHONY: clean-python
clean-python:
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"
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 \
dist_examples_DATA += bfdd/bfdd.conf.sample
vtysh_scan += $(top_srcdir)/bfdd/bfdd_vty.c
vtysh_scan += $(top_srcdir)/bfdd/bfdd_cli.c
-man8 += $(MANBUILD)/bfdd.8
+man8 += $(MANBUILD)/frr-bfdd.8
endif
bfdd_libbfd_a_SOURCES = \
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)
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;
}
}
/* Attribute hash routines. */
static struct hash *attrhash;
-/* Shallow copy of an attribute
- * Though, not so shallow that it doesn't copy the contents
- * of the attr_extra pointed to by 'extra'
- */
-void bgp_attr_dup(struct attr *new, struct attr *orig)
-{
- *new = *orig;
-}
-
unsigned long int attr_count(void)
{
return attrhash->count;
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);
* Read an individual SID value returning how much data we have read
* Returns 0 if there was an error that needs to be passed up the stack
*/
-static bgp_attr_parse_ret_t bgp_attr_psid_sub(int32_t type,
- int32_t length,
+static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
struct bgp_attr_parser_args *args,
struct bgp_nlri *mp_update)
{
int srgb_count;
if (type == BGP_PREFIX_SID_LABEL_INDEX) {
- if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
- flog_err(
- EC_BGP_ATTR_LEN,
- "Prefix SID label index length is %d instead of %d",
- length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
+ if (STREAM_READABLE(peer->curr) < length
+ || length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "Prefix SID label index length is %" PRIu16
+ " instead of %u",
+ length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
return bgp_attr_malformed(args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
args->total);
/* Placeholder code for the IPv6 SID type */
else if (type == BGP_PREFIX_SID_IPV6) {
- if (length != BGP_PREFIX_SID_IPV6_LENGTH) {
+ if (STREAM_READABLE(peer->curr) < length
+ || length != BGP_PREFIX_SID_IPV6_LENGTH) {
flog_err(EC_BGP_ATTR_LEN,
- "Prefix SID IPv6 length is %d instead of %d",
+ "Prefix SID IPv6 length is %" PRIu16
+ " instead of %u",
length, BGP_PREFIX_SID_IPV6_LENGTH);
return bgp_attr_malformed(args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
/* Placeholder code for the Originator SRGB type */
else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) {
- /* Ignore flags */
- stream_getw(peer->curr);
+ /*
+ * ietf-idr-bgp-prefix-sid-05:
+ * Length is the total length of the value portion of the
+ * TLV: 2 + multiple of 6.
+ *
+ * peer->curr stream readp should be at the beginning of the 16
+ * bit flag field at this point in the code.
+ */
- length -= 2;
+ /*
+ * Check that the TLV length field is sane: at least 2 bytes of
+ * flag, and at least 1 SRGB (these are 6 bytes each)
+ */
+ if (length < (2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH)) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Prefix SID Originator SRGB length field claims length of %" PRIu16 " bytes, but the minimum for this TLV type is %u",
+ length,
+ 2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH);
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ /*
+ * Check that we actually have at least as much data as
+ * specified by the length field
+ */
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "Prefix SID Originator SRGB specifies length %" PRIu16 ", but only %zu bytes remain",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+ /*
+ * Check that the portion of the TLV containing the sequence of
+ * SRGBs corresponds to a multiple of the SRGB size; to get
+ * that length, we skip the 16 bit flags field
+ */
+ stream_getw(peer->curr);
+ length -= 2;
if (length % BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH) {
flog_err(
EC_BGP_ATTR_LEN,
- "Prefix SID Originator SRGB length is %d, it must be a multiple of %d ",
+ "Prefix SID Originator SRGB length field claims attribute SRGB sequence section is %" PRIu16 "bytes, but it must be a multiple of %u",
length, BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH);
return bgp_attr_malformed(
args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
*/
else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE
|| type == BGP_PREFIX_SID_SRV6_L2_SERVICE) {
+
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Prefix SID SRv6 length is %" PRIu16
+ " - too long, only %zu remaining in this UPDATE",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
if (bgp_debug_update(peer, NULL, NULL, 1))
zlog_debug(
"%s attr Prefix-SID sub-type=%u is not supported, skipped",
peer->host, type);
- for (int i = 0; i < length; i++)
- stream_getc(peer->curr);
+
+ stream_forward_getp(peer->curr, length);
}
return BGP_ATTR_PARSE_PROCEED;
/* Prefix SID attribute
* draft-ietf-idr-bgp-prefix-sid-05
*/
-bgp_attr_parse_ret_t
-bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args,
- struct bgp_nlri *mp_update)
+bgp_attr_parse_ret_t bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
+ struct bgp_nlri *mp_update)
{
struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID);
- while (tlength) {
- int32_t type, length;
+ uint8_t type;
+ uint16_t length;
+ size_t headersz = sizeof(type) + sizeof(length);
- type = stream_getc(peer->curr);
- length = stream_getw(peer->curr);
+ while (STREAM_READABLE(peer->curr) > 0) {
- ret = bgp_attr_psid_sub(type, length, args, mp_update);
+ if (STREAM_READABLE(peer->curr) < headersz) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed Prefix SID attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)",
+ headersz, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
- if (ret != BGP_ATTR_PARSE_PROCEED)
- return ret;
- /*
- * Subtract length + the T and the L
- * since length is the Vector portion
- */
- tlength -= length + 3;
+ type = stream_getc(peer->curr);
+ length = stream_getw(peer->curr);
- if (tlength < 0) {
+ if (STREAM_READABLE(peer->curr) < length) {
flog_err(
EC_BGP_ATTR_LEN,
- "Prefix SID internal length %d causes us to read beyond the total Prefix SID length",
- length);
+ "Malformed Prefix SID attribute - insufficient data (need %" PRIu8
+ " for attribute body, have %zu remaining in UPDATE)",
+ length, STREAM_READABLE(peer->curr));
return bgp_attr_malformed(args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
args->total);
}
+
+ ret = bgp_attr_psid_sub(type, length, args, mp_update);
+
+ if (ret != BGP_ATTR_PARSE_PROCEED)
+ return ret;
}
return BGP_ATTR_PARSE_PROCEED;
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. */
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. */
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
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 = {
attr_args.total);
if (ret == BGP_ATTR_PARSE_PROCEED)
continue;
- return ret;
+ goto done;
}
/* OK check attribute and store it's value. */
startp);
break;
case BGP_ATTR_PREFIX_SID:
- ret = bgp_attr_prefix_sid(length,
- &attr_args, mp_update);
+ ret = bgp_attr_prefix_sid(&attr_args, mp_update);
break;
case BGP_ATTR_PMSI_TUNNEL:
ret = bgp_attr_pmsi_tunnel(&attr_args);
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. */
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;
}
}
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;
}
/*
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
&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.
if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH))) {
ret = bgp_attr_aspath_check(peer, attr);
if (ret != BGP_ATTR_PARSE_PROCEED)
- return ret;
+ goto done;
+ }
+
+ ret = BGP_ATTR_PARSE_PROCEED;
+done:
+
+ /*
+ * At this stage, we have done all fiddling with as4, and the
+ * resulting info is in attr->aggregator resp. attr->aspath so
+ * we can chuck as4_aggregator and as4_path alltogether in order
+ * to save memory
+ */
+ if (as4_path) {
+ /*
+ * unintern - it is in the hash
+ * The flag that we got this is still there, but that
+ * does not do any trouble
+ */
+ aspath_unintern(&as4_path);
}
- /* Finally intern unknown attribute. */
+
+ if (ret != BGP_ATTR_PARSE_ERROR) {
+ /* Finally intern unknown attribute. */
+ if (attr->transit)
+ attr->transit = transit_intern(attr->transit);
+ if (attr->encap_subtlvs)
+ attr->encap_subtlvs = encap_intern(attr->encap_subtlvs,
+ ENCAP_SUBTLV_TYPE);
+#if ENABLE_BGP_VNC
+ if (attr->vnc_subtlvs)
+ attr->vnc_subtlvs = encap_intern(attr->vnc_subtlvs,
+ VNC_SUBTLV_TYPE);
+#endif
+ } else {
+ if (attr->transit) {
+ transit_free(attr->transit);
+ attr->transit = NULL;
+ }
+
+ bgp_attr_flush_encap(attr);
+ };
+
+ /* Sanity checks */
if (attr->transit)
- attr->transit = transit_intern(attr->transit);
+ assert(attr->transit->refcnt > 0);
if (attr->encap_subtlvs)
- attr->encap_subtlvs =
- encap_intern(attr->encap_subtlvs, ENCAP_SUBTLV_TYPE);
+ assert(attr->encap_subtlvs->refcnt > 0);
#if ENABLE_BGP_VNC
if (attr->vnc_subtlvs)
- attr->vnc_subtlvs =
- encap_intern(attr->vnc_subtlvs, VNC_SUBTLV_TYPE);
+ assert(attr->vnc_subtlvs->refcnt > 0);
#endif
- return BGP_ATTR_PARSE_PROCEED;
+ return ret;
}
/*
/* 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);
extern bgp_attr_parse_ret_t bgp_attr_parse(struct peer *, struct attr *,
bgp_size_t, struct bgp_nlri *,
struct bgp_nlri *);
-extern void bgp_attr_dup(struct attr *, struct attr *);
extern void bgp_attr_undup(struct attr *new, struct attr *old);
extern struct attr *bgp_attr_intern(struct attr *attr);
extern void bgp_attr_unintern_sub(struct attr *);
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;
extern int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args,
struct bgp_nlri *);
extern bgp_attr_parse_ret_t
-bgp_attr_prefix_sid(int32_t tlength, struct bgp_attr_parser_args *args,
+bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
struct bgp_nlri *mp_update);
extern struct bgp_attr_encap_subtlv *
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);
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);
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;
}
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",
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]);
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);
unsigned addrpos, addrtotal;
union sockunion addrs[8];
int socket;
+ const char *last_err;
struct thread *t_timer, *t_read, *t_write;
};
#include "bgpd/bgp_nexthop.h"
#include "bgpd/bgp_addpath.h"
#include "bgpd/bgp_mac.h"
+#include "bgpd/bgp_vty.h"
/*
* Definitions and external declarations.
* present, else treat as locally originated.
*/
if (src_attr)
- bgp_attr_dup(&attr, src_attr);
+ attr = *src_attr;
else {
memset(&attr, 0, sizeof(struct attr));
bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
* address for the rest of the code to flow through. In the case of IPv4,
* make sure to set the flag for next hop attribute.
*/
- bgp_attr_dup(&attr, parent_pi->attr);
+ attr = *parent_pi->attr;
if (afi == AFI_IP6)
evpn_convert_nexthop_to_ipv6(&attr);
else
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;
}
int ret = 0;
- ret = bgp_get(&bgp_vrf, &as, vrf_id_to_name(vrf_id),
- vrf_id == VRF_DEFAULT ? BGP_INSTANCE_TYPE_DEFAULT
- : BGP_INSTANCE_TYPE_VRF);
+ ret = bgp_get_vty(&bgp_vrf, &as, vrf_id_to_name(vrf_id),
+ vrf_id == VRF_DEFAULT
+ ? BGP_INSTANCE_TYPE_DEFAULT
+ : BGP_INSTANCE_TYPE_VRF);
switch (ret) {
case BGP_ERR_AS_MISMATCH:
flog_err(EC_BGP_EVPN_AS_MISMATCH,
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))
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);
{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 */
},
{
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;
}
}
if (bsm->ifp_list->count == 0) {
+ struct ethaddr mac = *macaddr;
+
hash_release(bm->self_mac_hash, bsm);
list_delete(&bsm->ifp_list);
XFREE(MTYPE_BSM, bsm);
- bgp_mac_rescan_all_evpn_tables(macaddr);
+ bgp_mac_rescan_all_evpn_tables(&mac);
}
}
#include "thread.h"
#include <lib/version.h>
#include "memory.h"
-#include "memory_vty.h"
#include "prefix.h"
#include "log.h"
#include "privs.h"
return;
}
- bgp_attr_dup(&attr, new_best->attr);
+ attr = *new_best->attr;
if (new_best->peer && bgp_flag_check(new_best->peer->bgp,
BGP_FLAG_MULTIPATH_RELAX_AS_SET)) {
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;
}
return;
}
- bgp_attr_dup(&static_attr, path_vrf->attr); /* shallow copy */
+ /* shallow copy */
+ static_attr = *path_vrf->attr;
/*
* route map handling
buf_prefix, bgp_vrf->name_pretty);
}
- bgp_attr_dup(&static_attr, path_vpn->attr); /* shallow copy */
+ /* shallow copy */
+ static_attr = *path_vpn->attr;
/*
* Nexthop: stash and clear
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,
if (len) {
str[len] = '\0';
+
+ if (peer->domainname != NULL) {
+ XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+ peer->domainname = NULL;
+ }
+
peer->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, str);
}
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
temp.type = IPSET_NET_NET;
}
if (bpf->vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
- temp.vrf_id = 0;
+ temp.vrf_id = VRF_DEFAULT;
else
temp.vrf_id = bpf->vrf_id;
bpme = &temp2;
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
}
/* For modify attribute, copy it to temporary structure. */
- bgp_attr_dup(attr, piattr);
+ *attr = *piattr;
/* If local-preference is not set. */
if ((peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED)
if ((from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP)
&& !bgp_flag_check(bgp,
BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
- bgp_attr_dup(&dummy_attr, attr);
+ dummy_attr = *attr;
rmap_path.attr = &dummy_attr;
}
goto filtered;
}
- bgp_attr_dup(&new_attr, attr);
+ new_attr = *attr;
/* Apply incoming route-map.
* NB: new_attr may now contain newly allocated values from route-map
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)
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)
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;
}
struct attr attr_new;
/* Copy attribute for modification. */
- bgp_attr_dup(&attr_new, &attr);
+ attr_new = attr;
if (red->redist_metric_flag)
attr_new.med = red->redist_metric;
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;
}
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;
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();
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
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));
}
}
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");
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);
}
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",
|| (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",
} else {
len = vty_out(
vty, "%s%s",
- nexthop_fqdn
- ? nexthop_fqdn
+ nexthop_hostname
+ ? nexthop_hostname
: inet_ntop(
AF_INET6,
&attr->mp_nexthop_local,
} else {
len = vty_out(
vty, "%s%s",
- nexthop_fqdn
- ? nexthop_fqdn
+ nexthop_hostname
+ ? nexthop_hostname
: inet_ntop(
AF_INET6,
&attr->mp_nexthop_global,
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;
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();
}
}
- attr = path->attr;
-
/* Line1 display AS-path, Aggregator */
if (attr->aspath) {
if (json_paths) {
|| !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));
}
} 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));
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");
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);
struct attr dummy_attr;
route_map_result_t ret;
- bgp_attr_dup(&dummy_attr, pi->attr);
+ dummy_attr = *pi->attr;
path.peer = pi->peer;
path.attr = &dummy_attr;
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;
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
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,
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,
}
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;
}
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;
}
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;
}
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
header2 = 0;
}
- bgp_attr_dup(&attr, ain->attr);
+ attr = *ain->attr;
route_filtered = false;
/* Filter prefix using distribute list,
header2 = 0;
}
- bgp_attr_dup(&attr, adj->attr);
+ attr = *adj->attr;
ret = bgp_output_modifier(
peer, &rn->p, &attr, afi, safi,
rmap_name);
dst->v_routeadv = src->v_routeadv;
dst->flags = src->flags;
dst->af_flags[afi][safi] = src->af_flags[afi][safi];
+ dst->pmax_out[afi][safi] = src->pmax_out[afi][safi];
XFREE(MTYPE_BGP_PEER_HOST, dst->host);
dst->host = XSTRDUP(MTYPE_BGP_PEER_HOST, src->host);
struct bgp_synchronize *sync;
/* send prefix count */
- unsigned long scount;
+ uint32_t scount;
/* announcement attribute hash */
struct hash *hash;
if (o1->subgroup > o2->subgroup)
return 1;
+ if (o1->addpath_tx_id < o2->addpath_tx_id)
+ return -1;
+
+ if (o1->addpath_tx_id > o2->addpath_tx_id)
+ return 1;
+
return 0;
}
RB_GENERATE(bgp_adj_out_rb, bgp_adj_out, adj_entry, bgp_adj_out_compare);
struct update_subgroup *subgrp,
uint32_t addpath_tx_id)
{
- struct bgp_adj_out *adj, lookup;
- struct peer *peer;
- afi_t afi;
- safi_t safi;
- int addpath_capable;
+ struct bgp_adj_out lookup;
if (!rn || !subgrp)
return NULL;
- peer = SUBGRP_PEER(subgrp);
- afi = SUBGRP_AFI(subgrp);
- safi = SUBGRP_SAFI(subgrp);
- addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
-
/* update-groups that do not support addpath will pass 0 for
- * addpath_tx_id so do not both matching against it */
+ * addpath_tx_id. */
lookup.subgroup = subgrp;
- adj = RB_FIND(bgp_adj_out_rb, &rn->adj_out, &lookup);
- if (adj) {
- if (addpath_capable) {
- if (adj->addpath_tx_id == addpath_tx_id)
- return adj;
- } else
- return adj;
- }
- return NULL;
+ lookup.addpath_tx_id = addpath_tx_id;
+
+ return RB_FIND(bgp_adj_out_rb, &rn->adj_out, &lookup);
}
static void adj_free(struct bgp_adj_out *adj)
adj = XCALLOC(MTYPE_BGP_ADJ_OUT, sizeof(struct bgp_adj_out));
adj->subgroup = subgrp;
+ adj->addpath_tx_id = addpath_tx_id;
+
if (rn) {
RB_INSERT(bgp_adj_out_rb, &rn->adj_out, adj);
bgp_lock_node(rn);
adj->rn = rn;
}
- adj->addpath_tx_id = addpath_tx_id;
TAILQ_INSERT_TAIL(&(subgrp->adjq), adj, subgrp_adj_train);
SUBGRP_INCR_STAT(subgrp, adj_count);
return adj;
{
struct bgp *bgp;
struct attr attr;
+ struct attr *new_attr = &attr;
struct aspath *aspath;
- struct bgp_path_info tmp_info;
struct prefix p;
struct peer *from;
struct bgp_node *rn;
- struct bgp_path_info *ri;
struct peer *peer;
route_map_result_t ret = RMAP_DENYMATCH;
afi_t afi;
}
if (peer->default_rmap[afi][safi].name) {
+ struct attr attr_tmp = attr;
+ struct bgp_path_info bpi_rmap = {0};
+
+ bpi_rmap.peer = bgp->peer_self;
+ bpi_rmap.attr = &attr_tmp;
+
SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT);
+
+ /* Iterate over the RIB to see if we can announce
+ * the default route. We announce the default
+ * route only if route-map has a match.
+ */
for (rn = bgp_table_top(bgp->rib[afi][safi]); rn;
rn = bgp_route_next(rn)) {
- for (ri = bgp_node_get_bgp_path_info(rn);
- ri; ri = ri->next) {
- struct attr dummy_attr;
-
- /* Provide dummy so the route-map can't modify
- * the attributes */
- bgp_attr_dup(&dummy_attr, ri->attr);
- tmp_info.peer = ri->peer;
- tmp_info.attr = &dummy_attr;
-
- ret = route_map_apply(
- peer->default_rmap[afi][safi].map,
- &rn->p, RMAP_BGP, &tmp_info);
-
- /* The route map might have set attributes. If
- * we don't flush them
- * here, they will be leaked. */
- bgp_attr_flush(&dummy_attr);
- if (ret != RMAP_DENYMATCH)
- break;
- }
+ ret = route_map_apply(peer->default_rmap[afi][safi].map,
+ &rn->p, RMAP_BGP, &bpi_rmap);
+
if (ret != RMAP_DENYMATCH)
break;
}
bgp->peer_self->rmap_type = 0;
+ new_attr = bgp_attr_intern(&attr_tmp);
- if (ret == RMAP_DENYMATCH)
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&attr_tmp);
withdraw = 1;
+ }
}
if (withdraw) {
SUBGRP_STATUS_DEFAULT_ORIGINATE)) {
if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
- bgp_attr_add_gshut_community(&attr);
+ bgp_attr_add_gshut_community(new_attr);
}
SET_FLAG(subgrp->sflags,
SUBGRP_STATUS_DEFAULT_ORIGINATE);
- subgroup_default_update_packet(subgrp, &attr, from);
+ subgroup_default_update_packet(subgrp, new_attr, from);
/* The 'neighbor x.x.x.x default-originate' default will
* act as an
addpath_tx_id = adj->addpath_tx_id;
path = adv->pathi;
+ /* Check if we need to add a prefix to the packet if
+ * maximum-prefix-out is set for the peer.
+ */
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT)
+ && subgrp->scount >= peer->pmax_out[afi][safi]) {
+ if (BGP_DEBUG(update, UPDATE_OUT)
+ || BGP_DEBUG(update, UPDATE_PREFIX)) {
+ zlog_debug(
+ "%s reached maximum prefix to be send (%" PRIu32
+ ")",
+ peer->host, peer->pmax_out[afi][safi]);
+ }
+ goto next;
+ }
+
space_remaining = STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s))
- BGP_MAX_PACKET_SIZE_OVERFLOW;
space_needed =
subgrp->scount++;
adj->attr = bgp_attr_intern(adv->baa->attr);
-
+next:
adv = bgp_advertise_clean_subgroup(subgrp, adj);
}
#include "thread.h"
#include "log.h"
#include "memory.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
#include "hash.h"
#include "queue.h"
#include "filter.h"
#include "bgpd/bgp_bfd.h"
#include "bgpd/bgp_io.h"
#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_addpath.h"
#include "bgpd/bgp_mac.h"
+#include "bgpd/bgp_flowspec.h"
+#if ENABLE_BGP_VNC
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#endif
+
+FRR_CFG_DEFAULT_BOOL(BGP_IMPORT_CHECK,
+ { .val_long = true, .match_profile = "datacenter", },
+ { .val_long = false },
+)
+FRR_CFG_DEFAULT_BOOL(BGP_SHOW_HOSTNAME,
+ { .val_long = true, .match_profile = "datacenter", },
+ { .val_long = false },
+)
+FRR_CFG_DEFAULT_BOOL(BGP_LOG_NEIGHBOR_CHANGES,
+ { .val_long = true, .match_profile = "datacenter", },
+ { .val_long = false },
+)
+FRR_CFG_DEFAULT_BOOL(BGP_DETERMINISTIC_MED,
+ { .val_long = true, .match_profile = "datacenter", },
+ { .val_long = false },
+)
+FRR_CFG_DEFAULT_ULONG(BGP_CONNECT_RETRY,
+ { .val_ulong = 10, .match_profile = "datacenter", },
+ { .val_ulong = 120 },
+)
+FRR_CFG_DEFAULT_ULONG(BGP_HOLDTIME,
+ { .val_ulong = 9, .match_profile = "datacenter", },
+ { .val_ulong = 180 },
+)
+FRR_CFG_DEFAULT_ULONG(BGP_KEEPALIVE,
+ { .val_ulong = 3, .match_profile = "datacenter", },
+ { .val_ulong = 60 },
+)
+
+DEFINE_HOOK(bgp_inst_config_write,
+ (struct bgp *bgp, struct vty *vty),
+ (bgp, vty))
static struct peer_group *listen_range_exists(struct bgp *bgp,
struct prefix *range, int exact);
return ret;
}
+int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
+ enum bgp_instance_type inst_type)
+{
+ int ret = bgp_get(bgp, as, name, inst_type);
+
+ if (ret == BGP_CREATED) {
+ bgp_timers_set(*bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME,
+ DFLT_BGP_CONNECT_RETRY);
+
+ if (DFLT_BGP_IMPORT_CHECK)
+ bgp_flag_set(*bgp, BGP_FLAG_IMPORT_CHECK);
+ if (DFLT_BGP_SHOW_HOSTNAME)
+ bgp_flag_set(*bgp, BGP_FLAG_SHOW_HOSTNAME);
+ if (DFLT_BGP_LOG_NEIGHBOR_CHANGES)
+ bgp_flag_set(*bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
+ if (DFLT_BGP_DETERMINISTIC_MED)
+ bgp_flag_set(*bgp, BGP_FLAG_DETERMINISTIC_MED);
+
+ ret = BGP_SUCCESS;
+ }
+ return ret;
+}
+
/*
* bgp_vty_find_and_parse_afi_safi_bgp
*
if (inst_type == BGP_INSTANCE_TYPE_DEFAULT)
is_new_bgp = (bgp_lookup(as, name) == NULL);
- ret = bgp_get(&bgp, &as, name, inst_type);
+ ret = bgp_get_vty(&bgp, &as, name, inst_type);
switch (ret) {
case BGP_ERR_AS_MISMATCH:
vty_out(vty, "BGP is already running; AS is %u\n", as);
"Number of paths\n"
"Match the cluster length\n")
-void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp, afi_t afi,
- safi_t safi)
+static void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp,
+ afi_t afi, safi_t safi)
{
if (bgp->maxpaths[afi][safi].maxpaths_ebgp != MULTIPATH_NUM) {
vty_out(vty, " maximum-paths %d\n",
return CMD_WARNING_CONFIG_FAILED;
}
- bgp_timers_set(bgp, keepalive, holdtime);
+ bgp_timers_set(bgp, keepalive, holdtime, DFLT_BGP_CONNECT_RETRY);
return CMD_SUCCESS;
}
"Holdtime\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
- bgp_timers_unset(bgp);
+ bgp_timers_set(bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME,
+ DFLT_BGP_CONNECT_RETRY);
return CMD_SUCCESS;
}
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);
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);
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. */
return bgp_vty_return(vty, ret);
}
+/* Maximum number of prefix to be sent to the neighbor. */
+DEFUN(neighbor_maximum_prefix_out,
+ neighbor_maximum_prefix_out_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix-out (1-4294967295)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefixes to be sent to this peer\n"
+ "Maximum no. of prefix limit\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ struct peer *peer;
+ uint32_t max;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ max = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT);
+ peer->pmax_out[afi][safi] = max;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_neighbor_maximum_prefix_out,
+ no_neighbor_maximum_prefix_out_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix-out",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefixes to be sent to this peer\n")
+{
+ int idx_peer = 2;
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ peer->pmax_out[afi][safi] = 0;
+
+ return CMD_SUCCESS;
+}
+
/* Maximum number of prefix configuration. prefix count is different
for each peer configuration. So this configuration can be set for
each peer configuration. */
as_t as = bgp->as;
/* Auto-create assuming the same AS */
- ret = bgp_get(&bgp_default, &as, NULL,
- BGP_INSTANCE_TYPE_DEFAULT);
+ ret = bgp_get_vty(&bgp_default, &as, NULL,
+ BGP_INSTANCE_TYPE_DEFAULT);
if (ret) {
vty_out(vty,
bgp_default = bgp_get_default();
if (!bgp_default) {
/* Auto-create assuming the same AS */
- ret = bgp_get(&bgp_default, &as, NULL,
- BGP_INSTANCE_TYPE_DEFAULT);
+ ret = bgp_get_vty(&bgp_default, &as, NULL,
+ BGP_INSTANCE_TYPE_DEFAULT);
if (ret) {
vty_out(vty,
vrf_bgp = bgp_default;
else
/* Auto-create assuming the same AS */
- ret = bgp_get(&vrf_bgp, &as, import_name, bgp_type);
+ ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type);
if (ret) {
vty_out(vty,
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");
json_object_int_add(json_addr, "sentPrefixCounter",
(PAF_SUBGRP(paf))->scount);
+ /* Maximum prefix */
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT))
+ json_object_int_add(json_addr, "prefixOutAllowedMax",
+ p->pmax_out[afi][safi]);
+
/* Maximum prefix */
if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) {
json_object_int_add(json_addr, "prefixAllowedMax",
vty_out(vty, " %" PRIu32 " accepted prefixes\n",
p->pcount[afi][safi]);
+ /* maximum-prefix-out */
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT))
+ vty_out(vty,
+ " Maximum allowed prefixes sent %" PRIu32 "\n",
+ p->pmax_out[afi][safi]);
+
/* Maximum prefix */
if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) {
vty_out(vty,
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",
json_neigh,
"bgpTimerConfiguredKeepAliveIntervalMsecs",
p->keepalive * 1000);
- } else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
- || (bgp->default_keepalive
- != BGP_DEFAULT_KEEPALIVE)) {
+ } else if ((bgp->default_holdtime != SAVE_BGP_HOLDTIME)
+ || (bgp->default_keepalive != SAVE_BGP_KEEPALIVE)) {
json_object_int_add(json_neigh,
"bgpTimerConfiguredHoldTimeMsecs",
bgp->default_holdtime);
p->holdtime);
vty_out(vty, ", keepalive interval is %d seconds\n",
p->keepalive);
- } else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
- || (bgp->default_keepalive
- != BGP_DEFAULT_KEEPALIVE)) {
+ } else if ((bgp->default_holdtime != SAVE_BGP_HOLDTIME)
+ || (bgp->default_keepalive != SAVE_BGP_KEEPALIVE)) {
vty_out(vty, " Configured hold time is %d",
bgp->default_holdtime);
vty_out(vty, ", keepalive interval is %d seconds\n",
return bgp_redistribute_unset(bgp, AFI_IP6, type, 0);
}
-void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi,
- safi_t safi)
+static void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp,
+ afi_t afi, safi_t safi)
{
int i;
}
}
+/* peer-group helpers for config-write */
+
+static bool peergroup_flag_check(struct peer *peer, uint32_t flag)
+{
+ if (!peer_group_active(peer)) {
+ if (CHECK_FLAG(peer->flags_invert, flag))
+ return !CHECK_FLAG(peer->flags, flag);
+ else
+ return !!CHECK_FLAG(peer->flags, flag);
+ }
+
+ return !!CHECK_FLAG(peer->flags_override, flag);
+}
+
+static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi,
+ uint32_t flag)
+{
+ if (!peer_group_active(peer)) {
+ if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag))
+ return !peer_af_flag_check(peer, afi, safi, flag);
+ else
+ return !!peer_af_flag_check(peer, afi, safi, flag);
+ }
+
+ return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag);
+}
+
+static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi,
+ uint8_t type, int direct)
+{
+ struct bgp_filter *filter;
+
+ if (peer_group_active(peer))
+ return !!CHECK_FLAG(peer->filter_override[afi][safi][direct],
+ type);
+
+ filter = &peer->filter[afi][safi];
+ switch (type) {
+ case PEER_FT_DISTRIBUTE_LIST:
+ return !!(filter->dlist[direct].name);
+ case PEER_FT_FILTER_LIST:
+ return !!(filter->aslist[direct].name);
+ case PEER_FT_PREFIX_LIST:
+ return !!(filter->plist[direct].name);
+ case PEER_FT_ROUTE_MAP:
+ return !!(filter->map[direct].name);
+ case PEER_FT_UNSUPPRESS_MAP:
+ return !!(filter->usmap.name);
+ default:
+ return false;
+ }
+}
+
+/* Return true if the addpath type is set for peer and different from
+ * peer-group.
+ */
+static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi)
+{
+ enum bgp_addpath_strat type, g_type;
+
+ type = peer->addpath_type[afi][safi];
+
+ if (type != BGP_ADDPATH_NONE) {
+ if (peer_group_active(peer)) {
+ g_type = peer->group->conf->addpath_type[afi][safi];
+
+ if (type != g_type)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
/* This is part of the address-family block (unicast only) */
-void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
+static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
afi_t afi)
{
int indent = 2;
}
}
+static void bgp_config_write_filter(struct vty *vty, struct peer *peer,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_filter *filter;
+ char *addr;
+
+ addr = peer->host;
+ filter = &peer->filter[afi][safi];
+
+ /* distribute-list. */
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST,
+ FILTER_IN))
+ vty_out(vty, " neighbor %s distribute-list %s in\n", addr,
+ filter->dlist[FILTER_IN].name);
+
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST,
+ FILTER_OUT))
+ vty_out(vty, " neighbor %s distribute-list %s out\n", addr,
+ filter->dlist[FILTER_OUT].name);
+
+ /* prefix-list. */
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST,
+ FILTER_IN))
+ vty_out(vty, " neighbor %s prefix-list %s in\n", addr,
+ filter->plist[FILTER_IN].name);
+
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST,
+ FILTER_OUT))
+ vty_out(vty, " neighbor %s prefix-list %s out\n", addr,
+ filter->plist[FILTER_OUT].name);
+
+ /* route-map. */
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN))
+ vty_out(vty, " neighbor %s route-map %s in\n", addr,
+ filter->map[RMAP_IN].name);
+
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP,
+ RMAP_OUT))
+ vty_out(vty, " neighbor %s route-map %s out\n", addr,
+ filter->map[RMAP_OUT].name);
+
+ /* unsuppress-map */
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0))
+ vty_out(vty, " neighbor %s unsuppress-map %s\n", addr,
+ filter->usmap.name);
+
+ /* filter-list. */
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
+ FILTER_IN))
+ vty_out(vty, " neighbor %s filter-list %s in\n", addr,
+ filter->aslist[FILTER_IN].name);
+
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
+ FILTER_OUT))
+ vty_out(vty, " neighbor %s filter-list %s out\n", addr,
+ filter->aslist[FILTER_OUT].name);
+}
+
+/* BGP peer configuration display function. */
+static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
+ struct peer *peer)
+{
+ struct peer *g_peer = NULL;
+ char buf[SU_ADDRSTRLEN];
+ char *addr;
+ int if_pg_printed = false;
+ int if_ras_printed = false;
+
+ /* Skip dynamic neighbors. */
+ if (peer_dynamic_neighbor(peer))
+ return;
+
+ if (peer->conf_if)
+ addr = peer->conf_if;
+ else
+ addr = peer->host;
+
+ /************************************
+ ****** Global to the neighbor ******
+ ************************************/
+ if (peer->conf_if) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))
+ vty_out(vty, " neighbor %s interface v6only", addr);
+ else
+ vty_out(vty, " neighbor %s interface", addr);
+
+ if (peer_group_active(peer)) {
+ vty_out(vty, " peer-group %s", peer->group->name);
+ if_pg_printed = true;
+ } else if (peer->as_type == AS_SPECIFIED) {
+ vty_out(vty, " remote-as %u", peer->as);
+ if_ras_printed = true;
+ } else if (peer->as_type == AS_INTERNAL) {
+ vty_out(vty, " remote-as internal");
+ if_ras_printed = true;
+ } else if (peer->as_type == AS_EXTERNAL) {
+ vty_out(vty, " remote-as external");
+ if_ras_printed = true;
+ }
+
+ vty_out(vty, "\n");
+ }
+
+ /* remote-as and peer-group */
+ /* peer is a member of a peer-group */
+ if (peer_group_active(peer)) {
+ g_peer = peer->group->conf;
+
+ if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) {
+ if (peer->as_type == AS_SPECIFIED) {
+ vty_out(vty, " neighbor %s remote-as %u\n",
+ addr, peer->as);
+ } else if (peer->as_type == AS_INTERNAL) {
+ vty_out(vty,
+ " neighbor %s remote-as internal\n",
+ addr);
+ } else if (peer->as_type == AS_EXTERNAL) {
+ vty_out(vty,
+ " neighbor %s remote-as external\n",
+ addr);
+ }
+ }
+
+ /* For swpX peers we displayed the peer-group
+ * via 'neighbor swpX interface peer-group PGNAME' */
+ if (!if_pg_printed)
+ vty_out(vty, " neighbor %s peer-group %s\n", addr,
+ peer->group->name);
+ }
+
+ /* peer is NOT a member of a peer-group */
+ else {
+ /* peer is a peer-group, declare the peer-group */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ vty_out(vty, " neighbor %s peer-group\n", addr);
+ }
+
+ if (!if_ras_printed) {
+ if (peer->as_type == AS_SPECIFIED) {
+ vty_out(vty, " neighbor %s remote-as %u\n",
+ addr, peer->as);
+ } else if (peer->as_type == AS_INTERNAL) {
+ vty_out(vty,
+ " neighbor %s remote-as internal\n",
+ addr);
+ } else if (peer->as_type == AS_EXTERNAL) {
+ vty_out(vty,
+ " neighbor %s remote-as external\n",
+ addr);
+ }
+ }
+ }
+
+ /* local-as */
+ if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) {
+ vty_out(vty, " neighbor %s local-as %u", addr,
+ peer->change_local_as);
+ if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND))
+ vty_out(vty, " no-prepend");
+ if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS))
+ vty_out(vty, " replace-as");
+ vty_out(vty, "\n");
+ }
+
+ /* description */
+ if (peer->desc) {
+ vty_out(vty, " neighbor %s description %s\n", addr, peer->desc);
+ }
+
+ /* shutdown */
+ if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) {
+ if (peer->tx_shutdown_message)
+ vty_out(vty, " neighbor %s shutdown message %s\n", addr,
+ peer->tx_shutdown_message);
+ else
+ vty_out(vty, " neighbor %s shutdown\n", addr);
+ }
+
+ /* bfd */
+ if (peer->bfd_info) {
+ if (!peer_group_active(peer) || !g_peer->bfd_info) {
+ bgp_bfd_peer_config_write(vty, peer, addr);
+ }
+ }
+
+ /* password */
+ if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD))
+ vty_out(vty, " neighbor %s password %s\n", addr,
+ peer->password);
+
+ /* neighbor solo */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) {
+ if (!peer_group_active(peer)) {
+ vty_out(vty, " neighbor %s solo\n", addr);
+ }
+ }
+
+ /* BGP port */
+ if (peer->port != BGP_PORT_DEFAULT) {
+ vty_out(vty, " neighbor %s port %d\n", addr, peer->port);
+ }
+
+ /* Local interface name */
+ if (peer->ifname) {
+ vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname);
+ }
+
+ /* passive */
+ if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE))
+ vty_out(vty, " neighbor %s passive\n", addr);
+
+ /* ebgp-multihop */
+ if (peer->sort != BGP_PEER_IBGP && peer->ttl != BGP_DEFAULT_TTL
+ && !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL)) {
+ if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) {
+ vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr,
+ peer->ttl);
+ }
+ }
+
+ /* ttl-security hops */
+ if (peer->gtsm_hops != 0) {
+ if (!peer_group_active(peer)
+ || g_peer->gtsm_hops != peer->gtsm_hops) {
+ vty_out(vty, " neighbor %s ttl-security hops %d\n",
+ addr, peer->gtsm_hops);
+ }
+ }
+
+ /* disable-connected-check */
+ if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+ vty_out(vty, " neighbor %s disable-connected-check\n", addr);
+
+ /* enforce-first-as */
+ if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS))
+ vty_out(vty, " neighbor %s enforce-first-as\n", addr);
+
+ /* update-source */
+ if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) {
+ if (peer->update_source)
+ vty_out(vty, " neighbor %s update-source %s\n", addr,
+ sockunion2str(peer->update_source, buf,
+ SU_ADDRSTRLEN));
+ else if (peer->update_if)
+ vty_out(vty, " neighbor %s update-source %s\n", addr,
+ peer->update_if);
+ }
+
+ /* advertisement-interval */
+ if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV))
+ vty_out(vty, " neighbor %s advertisement-interval %u\n", addr,
+ peer->routeadv);
+
+ /* timers */
+ if (peergroup_flag_check(peer, PEER_FLAG_TIMER))
+ vty_out(vty, " neighbor %s timers %u %u\n", addr,
+ peer->keepalive, peer->holdtime);
+
+ /* timers connect */
+ if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT))
+ vty_out(vty, " neighbor %s timers connect %u\n", addr,
+ peer->connect);
+ /* need special-case handling for changed default values due to
+ * config profile / version (because there is no "timers bgp connect"
+ * command, we need to save this per-peer :/)
+ */
+ else if (!peer_group_active(peer) && !peer->connect &&
+ peer->bgp->default_connect_retry != SAVE_BGP_CONNECT_RETRY)
+ vty_out(vty, " neighbor %s timers connect %u\n", addr,
+ peer->bgp->default_connect_retry);
+
+ /* capability dynamic */
+ if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY))
+ vty_out(vty, " neighbor %s capability dynamic\n", addr);
+
+ /* capability extended-nexthop */
+ if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) {
+ if (!peer->conf_if) {
+ if (CHECK_FLAG(peer->flags_invert,
+ PEER_FLAG_CAPABILITY_ENHE))
+ vty_out(vty,
+ " no neighbor %s capability extended-nexthop\n",
+ addr);
+ else
+ vty_out(vty,
+ " neighbor %s capability extended-nexthop\n",
+ addr);
+ }
+ }
+
+ /* dont-capability-negotiation */
+ if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
+ vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
+
+ /* override-capability */
+ if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY))
+ vty_out(vty, " neighbor %s override-capability\n", addr);
+
+ /* strict-capability-match */
+ if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH))
+ vty_out(vty, " neighbor %s strict-capability-match\n", addr);
+
+ /* Sender side AS path loop detection. */
+ if (peer->as_path_loop_detection)
+ vty_out(vty, " neighbor %s sender-as-path-loop-detection\n",
+ addr);
+}
+
+/* BGP peer configuration display function. */
+static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
+ struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *g_peer = NULL;
+ char *addr;
+ bool flag_scomm, flag_secomm, flag_slcomm;
+
+ /* Skip dynamic neighbors. */
+ if (peer_dynamic_neighbor(peer))
+ return;
+
+ if (peer->conf_if)
+ addr = peer->conf_if;
+ else
+ addr = peer->host;
+
+ /************************************
+ ****** Per AF to the neighbor ******
+ ************************************/
+ if (peer_group_active(peer)) {
+ g_peer = peer->group->conf;
+
+ /* If the peer-group is active but peer is not, print a 'no
+ * activate' */
+ if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) {
+ vty_out(vty, " no neighbor %s activate\n", addr);
+ }
+
+ /* If the peer-group is not active but peer is, print an
+ 'activate' */
+ else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) {
+ vty_out(vty, " neighbor %s activate\n", addr);
+ }
+ } else {
+ if (peer->afc[afi][safi]) {
+ if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) {
+ if (bgp_flag_check(bgp,
+ BGP_FLAG_NO_DEFAULT_IPV4)) {
+ vty_out(vty, " neighbor %s activate\n",
+ addr);
+ }
+ } else
+ vty_out(vty, " neighbor %s activate\n", addr);
+ } else {
+ if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) {
+ if (!bgp_flag_check(bgp,
+ BGP_FLAG_NO_DEFAULT_IPV4)) {
+ vty_out(vty,
+ " no neighbor %s activate\n",
+ addr);
+ }
+ }
+ }
+ }
+
+ /* addpath TX knobs */
+ if (peergroup_af_addpath_check(peer, afi, safi)) {
+ switch (peer->addpath_type[afi][safi]) {
+ case BGP_ADDPATH_ALL:
+ vty_out(vty, " neighbor %s addpath-tx-all-paths\n",
+ addr);
+ break;
+ case BGP_ADDPATH_BEST_PER_AS:
+ vty_out(vty,
+ " neighbor %s addpath-tx-bestpath-per-AS\n",
+ addr);
+ break;
+ case BGP_ADDPATH_MAX:
+ case BGP_ADDPATH_NONE:
+ break;
+ }
+ }
+
+ /* ORF capability. */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_ORF_PREFIX_RM)) {
+ vty_out(vty, " neighbor %s capability orf prefix-list", addr);
+
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_ORF_PREFIX_SM)
+ && peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_ORF_PREFIX_RM))
+ vty_out(vty, " both");
+ else if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_ORF_PREFIX_SM))
+ vty_out(vty, " send");
+ else
+ vty_out(vty, " receive");
+ vty_out(vty, "\n");
+ }
+
+ /* BGP flag dampening. */
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_DAMPENING))
+ bgp_config_write_damp(vty, afi, safi);
+
+ /* Route reflector client. */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REFLECTOR_CLIENT)) {
+ vty_out(vty, " neighbor %s route-reflector-client\n", addr);
+ }
+
+ /* next-hop-self force */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_FORCE_NEXTHOP_SELF)) {
+ vty_out(vty, " neighbor %s next-hop-self force\n", addr);
+ }
+
+ /* next-hop-self */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) {
+ vty_out(vty, " neighbor %s next-hop-self\n", addr);
+ }
+
+ /* remove-private-AS */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) {
+ vty_out(vty, " neighbor %s remove-private-AS all replace-AS\n",
+ addr);
+ }
+
+ else if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) {
+ vty_out(vty, " neighbor %s remove-private-AS replace-AS\n",
+ addr);
+ }
+
+ else if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) {
+ vty_out(vty, " neighbor %s remove-private-AS all\n", addr);
+ }
+
+ else if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS)) {
+ vty_out(vty, " neighbor %s remove-private-AS\n", addr);
+ }
+
+ /* as-override */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) {
+ vty_out(vty, " neighbor %s as-override\n", addr);
+ }
+
+ /* send-community print. */
+ flag_scomm = peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_SEND_COMMUNITY);
+ flag_secomm = peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_SEND_EXT_COMMUNITY);
+ flag_slcomm = peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_SEND_LARGE_COMMUNITY);
+
+ if (flag_scomm && flag_secomm && flag_slcomm) {
+ vty_out(vty, " no neighbor %s send-community all\n", addr);
+ } else {
+ if (flag_scomm)
+ vty_out(vty, " no neighbor %s send-community\n", addr);
+ if (flag_secomm)
+ vty_out(vty,
+ " no neighbor %s send-community extended\n",
+ addr);
+
+ if (flag_slcomm)
+ vty_out(vty, " no neighbor %s send-community large\n",
+ addr);
+ }
+
+ /* Default information */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_DEFAULT_ORIGINATE)) {
+ vty_out(vty, " neighbor %s default-originate", addr);
+
+ if (peer->default_rmap[afi][safi].name)
+ vty_out(vty, " route-map %s",
+ peer->default_rmap[afi][safi].name);
+
+ vty_out(vty, "\n");
+ }
+
+ /* Soft reconfiguration inbound. */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) {
+ vty_out(vty, " neighbor %s soft-reconfiguration inbound\n",
+ addr);
+ }
+
+ /* maximum-prefix. */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) {
+ vty_out(vty, " neighbor %s maximum-prefix %" PRIu32, addr,
+ peer->pmax[afi][safi]);
+
+ if (peer->pmax_threshold[afi][safi]
+ != MAXIMUM_PREFIX_THRESHOLD_DEFAULT)
+ vty_out(vty, " %u", peer->pmax_threshold[afi][safi]);
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_WARNING))
+ vty_out(vty, " warning-only");
+ if (peer->pmax_restart[afi][safi])
+ vty_out(vty, " restart %u",
+ peer->pmax_restart[afi][safi]);
+
+ vty_out(vty, "\n");
+ }
+
+ /* maximum-prefix-out */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT))
+ vty_out(vty, " neighbor %s maximum-prefix-out %" PRIu32 "\n",
+ addr, peer->pmax_out[afi][safi]);
+
+ /* Route server client. */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_RSERVER_CLIENT)) {
+ vty_out(vty, " neighbor %s route-server-client\n", addr);
+ }
+
+ /* Nexthop-local unchanged. */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) {
+ vty_out(vty, " neighbor %s nexthop-local unchanged\n", addr);
+ }
+
+ /* allowas-in <1-10> */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) {
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+ vty_out(vty, " neighbor %s allowas-in origin\n", addr);
+ } else if (peer->allowas_in[afi][safi] == 3) {
+ vty_out(vty, " neighbor %s allowas-in\n", addr);
+ } else {
+ vty_out(vty, " neighbor %s allowas-in %d\n", addr,
+ peer->allowas_in[afi][safi]);
+ }
+ }
+
+ /* weight */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT))
+ vty_out(vty, " neighbor %s weight %lu\n", addr,
+ peer->weight[afi][safi]);
+
+ /* Filter. */
+ bgp_config_write_filter(vty, peer, afi, safi);
+
+ /* atribute-unchanged. */
+ if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED)
+ || (safi != SAFI_EVPN
+ && peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED))
+ || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) {
+
+ if (!peer_group_active(peer)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MED_UNCHANGED)) {
+
+ vty_out(vty,
+ " neighbor %s attribute-unchanged%s%s%s\n",
+ addr,
+ peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED)
+ ? " as-path"
+ : "",
+ peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)
+ ? " next-hop"
+ : "",
+ peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MED_UNCHANGED)
+ ? " med"
+ : "");
+ }
+ }
+}
+
+/* Address family based peer configuration display. */
+static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ struct peer *peer;
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+
+
+ vty_frame(vty, " !\n address-family ");
+ if (afi == AFI_IP) {
+ if (safi == SAFI_UNICAST)
+ vty_frame(vty, "ipv4 unicast");
+ else if (safi == SAFI_LABELED_UNICAST)
+ vty_frame(vty, "ipv4 labeled-unicast");
+ else if (safi == SAFI_MULTICAST)
+ vty_frame(vty, "ipv4 multicast");
+ else if (safi == SAFI_MPLS_VPN)
+ vty_frame(vty, "ipv4 vpn");
+ else if (safi == SAFI_ENCAP)
+ vty_frame(vty, "ipv4 encap");
+ else if (safi == SAFI_FLOWSPEC)
+ vty_frame(vty, "ipv4 flowspec");
+ } else if (afi == AFI_IP6) {
+ if (safi == SAFI_UNICAST)
+ vty_frame(vty, "ipv6 unicast");
+ else if (safi == SAFI_LABELED_UNICAST)
+ vty_frame(vty, "ipv6 labeled-unicast");
+ else if (safi == SAFI_MULTICAST)
+ vty_frame(vty, "ipv6 multicast");
+ else if (safi == SAFI_MPLS_VPN)
+ vty_frame(vty, "ipv6 vpn");
+ else if (safi == SAFI_ENCAP)
+ vty_frame(vty, "ipv6 encap");
+ else if (safi == SAFI_FLOWSPEC)
+ vty_frame(vty, "ipv6 flowspec");
+ } else if (afi == AFI_L2VPN) {
+ if (safi == SAFI_EVPN)
+ vty_frame(vty, "l2vpn evpn");
+ }
+ vty_frame(vty, "\n");
+
+ bgp_config_write_distance(vty, bgp, afi, safi);
+
+ bgp_config_write_network(vty, bgp, afi, safi);
+
+ bgp_config_write_redistribute(vty, bgp, afi, safi);
+
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
+ bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ /* Skip dynamic neighbors. */
+ if (peer_dynamic_neighbor(peer))
+ continue;
+
+ /* Do not display doppelganger peers */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ bgp_config_write_peer_af(vty, bgp, peer, afi, safi);
+ }
+
+ bgp_config_write_maxpaths(vty, bgp, afi, safi);
+ bgp_config_write_table_map(vty, bgp, afi, safi);
+
+ if (safi == SAFI_EVPN)
+ bgp_config_write_evpn_info(vty, bgp, afi, safi);
+
+ if (safi == SAFI_FLOWSPEC)
+ bgp_fs_config_write_pbr(vty, bgp, afi, safi);
+
+ if (safi == SAFI_UNICAST) {
+ bgp_vpn_policy_config_write_afi(vty, bgp, afi);
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) {
+
+ vty_out(vty, " export vpn\n");
+ }
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) {
+
+ vty_out(vty, " import vpn\n");
+ }
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ char *name;
+
+ for (ALL_LIST_ELEMENTS_RO(
+ bgp->vpn_policy[afi].import_vrf, node,
+ name))
+ vty_out(vty, " import vrf %s\n", name);
+ }
+ }
+
+ vty_endframe(vty, " exit-address-family\n");
+}
+
+int bgp_config_write(struct vty *vty)
+{
+ struct bgp *bgp;
+ struct peer_group *group;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+ struct listnode *mnode, *mnnode;
+
+ if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER)
+ vty_out(vty, "bgp route-map delay-timer %u\n",
+ bm->rmap_update_timer);
+
+ /* BGP configuration. */
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+
+ /* skip all auto created vrf as they dont have user config */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO))
+ continue;
+
+ /* Router bgp ASN */
+ vty_out(vty, "router bgp %u", bgp->as);
+
+ if (bgp->name)
+ vty_out(vty, " %s %s",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
+ ? "view" : "vrf", bgp->name);
+ vty_out(vty, "\n");
+
+ /* BGP fast-external-failover. */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER))
+ vty_out(vty, " no bgp fast-external-failover\n");
+
+ /* BGP router ID. */
+ if (bgp->router_id_static.s_addr != 0)
+ vty_out(vty, " bgp router-id %s\n",
+ inet_ntoa(bgp->router_id_static));
+
+ /* BGP log-neighbor-changes. */
+ if (!!bgp_flag_check(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)
+ != SAVE_BGP_LOG_NEIGHBOR_CHANGES)
+ vty_out(vty, " %sbgp log-neighbor-changes\n",
+ bgp_flag_check(bgp,
+ BGP_FLAG_LOG_NEIGHBOR_CHANGES)
+ ? ""
+ : "no ");
+
+ /* BGP configuration. */
+ if (bgp_flag_check(bgp, BGP_FLAG_ALWAYS_COMPARE_MED))
+ vty_out(vty, " bgp always-compare-med\n");
+
+ /* RFC8212 default eBGP policy. */
+ if (bgp->ebgp_requires_policy
+ == DEFAULT_EBGP_POLICY_ENABLED)
+ vty_out(vty, " bgp ebgp-requires-policy\n");
+
+ /* draft-ietf-idr-deprecate-as-set-confed-set */
+ if (bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED)
+ vty_out(vty, " bgp reject-as-sets\n");
+
+ /* BGP default ipv4-unicast. */
+ if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4))
+ vty_out(vty, " no bgp default ipv4-unicast\n");
+
+ /* BGP default local-preference. */
+ if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF)
+ vty_out(vty, " bgp default local-preference %u\n",
+ bgp->default_local_pref);
+
+ /* BGP default show-hostname */
+ if (!!bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)
+ != SAVE_BGP_SHOW_HOSTNAME)
+ vty_out(vty, " %sbgp default show-hostname\n",
+ bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)
+ ? ""
+ : "no ");
+
+ /* BGP default subgroup-pkt-queue-max. */
+ if (bgp->default_subgroup_pkt_queue_max
+ != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX)
+ vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n",
+ bgp->default_subgroup_pkt_queue_max);
+
+ /* BGP client-to-client reflection. */
+ if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT))
+ vty_out(vty, " no bgp client-to-client reflection\n");
+
+ /* BGP cluster ID. */
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID))
+ vty_out(vty, " bgp cluster-id %s\n",
+ inet_ntoa(bgp->cluster_id));
+
+ /* Disable ebgp connected nexthop check */
+ if (bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
+ vty_out(vty,
+ " bgp disable-ebgp-connected-route-check\n");
+
+ /* Confederation identifier*/
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
+ vty_out(vty, " bgp confederation identifier %u\n",
+ bgp->confed_id);
+
+ /* Confederation peer */
+ if (bgp->confed_peers_cnt > 0) {
+ int i;
+
+ vty_out(vty, " bgp confederation peers");
+
+ for (i = 0; i < bgp->confed_peers_cnt; i++)
+ vty_out(vty, " %u", bgp->confed_peers[i]);
+
+ vty_out(vty, "\n");
+ }
+
+ /* BGP deterministic-med. */
+ if (!!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)
+ != SAVE_BGP_DETERMINISTIC_MED)
+ vty_out(vty, " %sbgp deterministic-med\n",
+ bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)
+ ? ""
+ : "no ");
+
+ /* BGP update-delay. */
+ bgp_config_write_update_delay(vty, bgp);
+
+ if (bgp->v_maxmed_onstartup
+ != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) {
+ vty_out(vty, " bgp max-med on-startup %u",
+ bgp->v_maxmed_onstartup);
+ if (bgp->maxmed_onstartup_value
+ != BGP_MAXMED_VALUE_DEFAULT)
+ vty_out(vty, " %u",
+ bgp->maxmed_onstartup_value);
+ vty_out(vty, "\n");
+ }
+ if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) {
+ vty_out(vty, " bgp max-med administrative");
+ if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT)
+ vty_out(vty, " %u", bgp->maxmed_admin_value);
+ vty_out(vty, "\n");
+ }
+
+ /* write quanta */
+ bgp_config_write_wpkt_quanta(vty, bgp);
+ /* read quanta */
+ bgp_config_write_rpkt_quanta(vty, bgp);
+
+ /* coalesce time */
+ bgp_config_write_coalesce_time(vty, bgp);
+
+ /* BGP graceful-restart. */
+ if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME)
+ vty_out(vty,
+ " bgp graceful-restart stalepath-time %u\n",
+ bgp->stalepath_time);
+ if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME)
+ vty_out(vty, " bgp graceful-restart restart-time %u\n",
+ bgp->restart_time);
+ if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART))
+ vty_out(vty, " bgp graceful-restart\n");
+
+ /* BGP graceful-shutdown */
+ if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN))
+ vty_out(vty, " bgp graceful-shutdown\n");
+
+ /* BGP graceful-restart Preserve State F bit. */
+ if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD))
+ vty_out(vty,
+ " bgp graceful-restart preserve-fw-state\n");
+
+ /* BGP bestpath method. */
+ if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE))
+ vty_out(vty, " bgp bestpath as-path ignore\n");
+ if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED))
+ vty_out(vty, " bgp bestpath as-path confed\n");
+
+ if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
+ if (bgp_flag_check(bgp,
+ BGP_FLAG_MULTIPATH_RELAX_AS_SET)) {
+ vty_out(vty,
+ " bgp bestpath as-path multipath-relax as-set\n");
+ } else {
+ vty_out(vty,
+ " bgp bestpath as-path multipath-relax\n");
+ }
+ }
+
+ if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
+ vty_out(vty,
+ " bgp route-reflector allow-outbound-policy\n");
+ }
+ if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID))
+ vty_out(vty, " bgp bestpath compare-routerid\n");
+ if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)
+ || bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) {
+ vty_out(vty, " bgp bestpath med");
+ if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED))
+ vty_out(vty, " confed");
+ if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST))
+ vty_out(vty, " missing-as-worst");
+ vty_out(vty, "\n");
+ }
+
+ /* BGP network import check. */
+ if (!!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)
+ != SAVE_BGP_IMPORT_CHECK)
+ vty_out(vty, " %sbgp network import-check\n",
+ bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)
+ ? ""
+ : "no ");
+
+ /* BGP timers configuration. */
+ if (bgp->default_keepalive != SAVE_BGP_KEEPALIVE
+ && bgp->default_holdtime != SAVE_BGP_HOLDTIME)
+ vty_out(vty, " timers bgp %u %u\n",
+ bgp->default_keepalive, bgp->default_holdtime);
+
+ /* peer-group */
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ bgp_config_write_peer_global(vty, bgp, group->conf);
+ }
+
+ /* Normal neighbor configuration. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ bgp_config_write_peer_global(vty, bgp, peer);
+ }
+
+ /* listen range and limit for dynamic BGP neighbors */
+ bgp_config_write_listen(vty, bgp);
+
+ /*
+ * BGP default autoshutdown neighbors
+ *
+ * This must be placed after any peer and peer-group
+ * configuration, to avoid setting all peers to shutdown after
+ * a daemon restart, which is undesired behavior. (see #2286)
+ */
+ if (bgp->autoshutdown)
+ vty_out(vty, " bgp default shutdown\n");
+
+ /* IPv4 unicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);
+
+ /* IPv4 multicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST);
+
+ /* IPv4 labeled-unicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST);
+
+ /* IPv4 VPN configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN);
+
+ /* ENCAPv4 configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP);
+
+ /* FLOWSPEC v4 configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC);
+
+ /* IPv6 unicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST);
+
+ /* IPv6 multicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST);
+
+ /* IPv6 labeled-unicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6,
+ SAFI_LABELED_UNICAST);
+
+ /* IPv6 VPN configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN);
+
+ /* ENCAPv6 configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP);
+
+ /* FLOWSPEC v6 configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC);
+
+ /* EVPN configuration. */
+ bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN);
+
+ hook_call(bgp_inst_config_write, bgp, vty);
+
+#if ENABLE_BGP_VNC
+ bgp_rfapi_cfg_write(vty, bgp);
+#endif
+
+ vty_out(vty, "!\n");
+ }
+ return 0;
+}
+
/* BGP node structure. */
static struct cmd_node bgp_node = {
install_element(BGP_VPNV6_NODE, &neighbor_unsuppress_map_cmd);
install_element(BGP_VPNV6_NODE, &no_neighbor_unsuppress_map_cmd);
+ /* neighbor maximum-prefix-out commands. */
+ install_element(BGP_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_out_cmd);
+
/* "neighbor maximum-prefix" commands. */
install_element(BGP_NODE, &neighbor_maximum_prefix_hidden_cmd);
install_element(BGP_NODE,
#ifndef _QUAGGA_BGP_VTY_H
#define _QUAGGA_BGP_VTY_H
+#include "bgpd/bgpd.h"
+
struct bgp;
#define BGP_INSTANCE_HELP_STR "BGP view\nBGP VRF\nView/VRF name\n"
extern void bgp_vty_init(void);
extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json);
+extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
+ enum bgp_instance_type inst_type);
extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp);
extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp);
extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp);
bool use_json);
extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
safi_t safi, bool show_failed, bool use_json);
-extern void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
- afi_t afi);
+
#endif /* _QUAGGA_BGP_VTY_H */
*/
if (is_evpn) {
api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- api_nh->onlink = true;
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
} else if (nh_othervrf &&
api_nh->gate.ipv4.s_addr == INADDR_ANY) {
if (is_evpn) {
api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
- api_nh->onlink = true;
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
} else if (nh_othervrf) {
if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) {
has_valid_label = 1;
label = label_pton(&mpinfo->extra->label[0]);
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
+
api_nh->label_num = 1;
api_nh->labels[0] = label;
}
valid_nh_count++;
}
-
- /* if this is a evpn route we don't have to include the label */
- if (has_valid_label && !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)))
- SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
-
/*
* When we create an aggregate route we must also
* install a Null0 route in the RIB, so overwrite
if (bgp_debug_zebra(p)) {
char prefix_buf[PREFIX_STRLEN];
char nh_buf[INET6_ADDRSTRLEN];
+ char eth_buf[ETHER_ADDR_STRLEN + 7] = {'\0'};
+ char buf1[ETHER_ADDR_STRLEN];
char label_buf[20];
int i;
}
label_buf[0] = '\0';
+ eth_buf[0] = '\0';
if (has_valid_label
&& !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))
- sprintf(label_buf, "label %u",
- api_nh->labels[0]);
- zlog_debug(" nhop [%d]: %s if %u VRF %u %s",
+ snprintf(label_buf, sizeof(label_buf),
+ "label %u", api_nh->labels[0]);
+ if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)
+ && !is_zero_mac(&api_nh->rmac))
+ snprintf(eth_buf, sizeof(eth_buf), " RMAC %s",
+ prefix_mac2str(&api_nh->rmac,
+ buf1, sizeof(buf1)));
+ zlog_debug(" nhop [%d]: %s if %u VRF %u %s %s",
i + 1, nh_buf, api_nh->ifindex,
- api_nh->vrf_id, label_buf);
+ api_nh->vrf_id, label_buf, eth_buf);
}
}
struct attr *old_attr;
struct attr new_attr;
- bgp_attr_dup(&new_attr, pi->attr);
+ new_attr = *pi->attr;
new_attr.med = red->redist_metric;
old_attr = pi->attr;
pi->attr = bgp_attr_intern(&new_attr);
extern int bgp_zebra_get_table_range(uint32_t chunk_size,
uint32_t *start, uint32_t *end);
extern int bgp_if_update_all(void);
-extern void bgp_config_write_maxpaths(struct vty *, struct bgp *, afi_t,
- safi_t);
-extern void bgp_config_write_redistribute(struct vty *, struct bgp *, afi_t,
- safi_t);
extern void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
struct bgp_path_info *path, struct bgp *bgp,
afi_t afi, safi_t safi);
DEFINE_QOBJ_TYPE(bgp)
DEFINE_QOBJ_TYPE(peer)
DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp))
-DEFINE_HOOK(bgp_inst_config_write,
- (struct bgp *bgp, struct vty *vty),
- (bgp, vty))
/* BGP process wide configuration. */
static struct bgp_master bgp_master;
}
/* BGP timer configuration. */
-int bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime)
+int bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime,
+ uint32_t connect_retry)
{
bgp->default_keepalive =
(keepalive < holdtime / 3 ? keepalive : holdtime / 3);
bgp->default_holdtime = holdtime;
+ bgp->default_connect_retry = connect_retry;
return 0;
}
+/* mostly for completeness - CLI uses its own defaults */
int bgp_timers_unset(struct bgp *bgp)
{
bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
+ bgp->default_connect_retry = BGP_DEFAULT_CONNECT_RETRY;
return 0;
}
COND_FLAG(peer->af_flags[afi][safi], flag, group_val);
}
-static bool peergroup_flag_check(struct peer *peer, uint32_t flag)
-{
- if (!peer_group_active(peer)) {
- if (CHECK_FLAG(peer->flags_invert, flag))
- return !CHECK_FLAG(peer->flags, flag);
- else
- return !!CHECK_FLAG(peer->flags, flag);
- }
-
- return !!CHECK_FLAG(peer->flags_override, flag);
-}
-
-static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi,
- uint32_t flag)
-{
- if (!peer_group_active(peer)) {
- if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag))
- return !peer_af_flag_check(peer, afi, safi, flag);
- else
- return !!peer_af_flag_check(peer, afi, safi, flag);
- }
-
- return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag);
-}
-
-static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi,
- uint8_t type, int direct)
-{
- struct bgp_filter *filter;
-
- if (peer_group_active(peer))
- return !!CHECK_FLAG(peer->filter_override[afi][safi][direct],
- type);
-
- filter = &peer->filter[afi][safi];
- switch (type) {
- case PEER_FT_DISTRIBUTE_LIST:
- return !!(filter->dlist[direct].name);
- case PEER_FT_FILTER_LIST:
- return !!(filter->aslist[direct].name);
- case PEER_FT_PREFIX_LIST:
- return !!(filter->plist[direct].name);
- case PEER_FT_ROUTE_MAP:
- return !!(filter->map[direct].name);
- case PEER_FT_UNSUPPRESS_MAP:
- return !!(filter->usmap.name);
- default:
- return false;
- }
-}
-
-/* Return true if the addpath type is set for peer and different from
- * peer-group.
- */
-static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi)
-{
- enum bgp_addpath_strat type, g_type;
-
- type = peer->addpath_type[afi][safi];
-
- if (type != BGP_ADDPATH_NONE) {
- if (peer_group_active(peer)) {
- g_type = peer->group->conf->addpath_type[afi][safi];
-
- if (type != g_type)
- return 1;
- else
- return 0;
- }
-
- return 1;
- }
-
- return 0;
-}
-
/* Check peer's AS number and determines if this peer is IBGP or EBGP */
static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer)
{
/* Set default value. */
peer->fd = -1;
peer->v_start = BGP_INIT_START_TIMER;
- peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+ peer->v_connect = bgp->default_connect_retry;
peer->status = Idle;
peer->ostatus = Idle;
peer->cur_event = peer->last_event = peer->last_major_event = 0;
if (CHECK_FLAG(conf->flags, PEER_FLAG_TIMER_CONNECT))
peer->v_connect = conf->connect;
else
- peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+ peer->v_connect = peer->bgp->default_connect_retry;
}
/* advertisement-interval apply */
bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
bgp->default_subgroup_pkt_queue_max =
BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX;
- bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
- bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
+ bgp_timers_unset(bgp);
bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT;
bgp->dynamic_neighbors_count = 0;
bgp->ebgp_requires_policy = DEFAULT_EBGP_POLICY_DISABLED;
bgp->reject_as_sets = BGP_REJECT_AS_SETS_DISABLED;
-#if DFLT_BGP_IMPORT_CHECK
- bgp_flag_set(bgp, BGP_FLAG_IMPORT_CHECK);
-#endif
-#if DFLT_BGP_SHOW_HOSTNAME
- bgp_flag_set(bgp, BGP_FLAG_SHOW_HOSTNAME);
-#endif
-#if DFLT_BGP_LOG_NEIGHBOR_CHANGES
- bgp_flag_set(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
-#endif
-#if DFLT_BGP_DETERMINISTIC_MED
- bgp_flag_set(bgp, BGP_FLAG_DETERMINISTIC_MED);
-#endif
bgp_addpath_init_bgp_data(&bgp->tx_addpath);
bgp->as = *as;
bgp_zebra_instance_register(bgp);
}
- return BGP_SUCCESS;
+ return BGP_CREATED;
}
/*
if (peer->connect)
peer->v_connect = peer->connect;
else
- peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+ peer->v_connect = peer->bgp->default_connect_retry;
/* Skip peer-group mechanics for regular peers. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
/* Remove flag and configuration on peer-group member. */
UNSET_FLAG(member->flags, PEER_FLAG_TIMER_CONNECT);
member->connect = 0;
- member->v_connect = BGP_DEFAULT_CONNECT_RETRY;
+ member->v_connect = peer->bgp->default_connect_retry;
}
return 0;
return buf;
}
-static void bgp_config_write_filter(struct vty *vty, struct peer *peer,
- afi_t afi, safi_t safi)
-{
- struct bgp_filter *filter;
- char *addr;
-
- addr = peer->host;
- filter = &peer->filter[afi][safi];
-
- /* distribute-list. */
- if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST,
- FILTER_IN))
- vty_out(vty, " neighbor %s distribute-list %s in\n", addr,
- filter->dlist[FILTER_IN].name);
-
- if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST,
- FILTER_OUT))
- vty_out(vty, " neighbor %s distribute-list %s out\n", addr,
- filter->dlist[FILTER_OUT].name);
-
- /* prefix-list. */
- if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST,
- FILTER_IN))
- vty_out(vty, " neighbor %s prefix-list %s in\n", addr,
- filter->plist[FILTER_IN].name);
-
- if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST,
- FILTER_OUT))
- vty_out(vty, " neighbor %s prefix-list %s out\n", addr,
- filter->plist[FILTER_OUT].name);
-
- /* route-map. */
- if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN))
- vty_out(vty, " neighbor %s route-map %s in\n", addr,
- filter->map[RMAP_IN].name);
-
- if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP,
- RMAP_OUT))
- vty_out(vty, " neighbor %s route-map %s out\n", addr,
- filter->map[RMAP_OUT].name);
-
- /* unsuppress-map */
- if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0))
- vty_out(vty, " neighbor %s unsuppress-map %s\n", addr,
- filter->usmap.name);
-
- /* filter-list. */
- if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
- FILTER_IN))
- vty_out(vty, " neighbor %s filter-list %s in\n", addr,
- filter->aslist[FILTER_IN].name);
-
- if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
- FILTER_OUT))
- vty_out(vty, " neighbor %s filter-list %s out\n", addr,
- filter->aslist[FILTER_OUT].name);
-}
-
-/* BGP peer configuration display function. */
-static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
- struct peer *peer)
-{
- struct peer *g_peer = NULL;
- char buf[SU_ADDRSTRLEN];
- char *addr;
- int if_pg_printed = false;
- int if_ras_printed = false;
-
- /* Skip dynamic neighbors. */
- if (peer_dynamic_neighbor(peer))
- return;
-
- if (peer->conf_if)
- addr = peer->conf_if;
- else
- addr = peer->host;
-
- /************************************
- ****** Global to the neighbor ******
- ************************************/
- if (peer->conf_if) {
- if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))
- vty_out(vty, " neighbor %s interface v6only", addr);
- else
- vty_out(vty, " neighbor %s interface", addr);
-
- if (peer_group_active(peer)) {
- vty_out(vty, " peer-group %s", peer->group->name);
- if_pg_printed = true;
- } else if (peer->as_type == AS_SPECIFIED) {
- vty_out(vty, " remote-as %u", peer->as);
- if_ras_printed = true;
- } else if (peer->as_type == AS_INTERNAL) {
- vty_out(vty, " remote-as internal");
- if_ras_printed = true;
- } else if (peer->as_type == AS_EXTERNAL) {
- vty_out(vty, " remote-as external");
- if_ras_printed = true;
- }
-
- vty_out(vty, "\n");
- }
-
- /* remote-as and peer-group */
- /* peer is a member of a peer-group */
- if (peer_group_active(peer)) {
- g_peer = peer->group->conf;
-
- if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) {
- if (peer->as_type == AS_SPECIFIED) {
- vty_out(vty, " neighbor %s remote-as %u\n",
- addr, peer->as);
- } else if (peer->as_type == AS_INTERNAL) {
- vty_out(vty,
- " neighbor %s remote-as internal\n",
- addr);
- } else if (peer->as_type == AS_EXTERNAL) {
- vty_out(vty,
- " neighbor %s remote-as external\n",
- addr);
- }
- }
-
- /* For swpX peers we displayed the peer-group
- * via 'neighbor swpX interface peer-group PGNAME' */
- if (!if_pg_printed)
- vty_out(vty, " neighbor %s peer-group %s\n", addr,
- peer->group->name);
- }
-
- /* peer is NOT a member of a peer-group */
- else {
- /* peer is a peer-group, declare the peer-group */
- if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
- vty_out(vty, " neighbor %s peer-group\n", addr);
- }
-
- if (!if_ras_printed) {
- if (peer->as_type == AS_SPECIFIED) {
- vty_out(vty, " neighbor %s remote-as %u\n",
- addr, peer->as);
- } else if (peer->as_type == AS_INTERNAL) {
- vty_out(vty,
- " neighbor %s remote-as internal\n",
- addr);
- } else if (peer->as_type == AS_EXTERNAL) {
- vty_out(vty,
- " neighbor %s remote-as external\n",
- addr);
- }
- }
- }
-
- /* local-as */
- if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) {
- vty_out(vty, " neighbor %s local-as %u", addr,
- peer->change_local_as);
- if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND))
- vty_out(vty, " no-prepend");
- if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS))
- vty_out(vty, " replace-as");
- vty_out(vty, "\n");
- }
-
- /* description */
- if (peer->desc) {
- vty_out(vty, " neighbor %s description %s\n", addr, peer->desc);
- }
-
- /* shutdown */
- if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) {
- if (peer->tx_shutdown_message)
- vty_out(vty, " neighbor %s shutdown message %s\n", addr,
- peer->tx_shutdown_message);
- else
- vty_out(vty, " neighbor %s shutdown\n", addr);
- }
-
- /* bfd */
- if (peer->bfd_info) {
- if (!peer_group_active(peer) || !g_peer->bfd_info) {
- bgp_bfd_peer_config_write(vty, peer, addr);
- }
- }
-
- /* password */
- if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD))
- vty_out(vty, " neighbor %s password %s\n", addr,
- peer->password);
-
- /* neighbor solo */
- if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) {
- if (!peer_group_active(peer)) {
- vty_out(vty, " neighbor %s solo\n", addr);
- }
- }
-
- /* BGP port */
- if (peer->port != BGP_PORT_DEFAULT) {
- vty_out(vty, " neighbor %s port %d\n", addr, peer->port);
- }
-
- /* Local interface name */
- if (peer->ifname) {
- vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname);
- }
-
- /* passive */
- if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE))
- vty_out(vty, " neighbor %s passive\n", addr);
-
- /* ebgp-multihop */
- if (peer->sort != BGP_PEER_IBGP && peer->ttl != BGP_DEFAULT_TTL
- && !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL)) {
- if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) {
- vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr,
- peer->ttl);
- }
- }
-
- /* ttl-security hops */
- if (peer->gtsm_hops != 0) {
- if (!peer_group_active(peer)
- || g_peer->gtsm_hops != peer->gtsm_hops) {
- vty_out(vty, " neighbor %s ttl-security hops %d\n",
- addr, peer->gtsm_hops);
- }
- }
-
- /* disable-connected-check */
- if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK))
- vty_out(vty, " neighbor %s disable-connected-check\n", addr);
-
- /* enforce-first-as */
- if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS))
- vty_out(vty, " neighbor %s enforce-first-as\n", addr);
-
- /* update-source */
- if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) {
- if (peer->update_source)
- vty_out(vty, " neighbor %s update-source %s\n", addr,
- sockunion2str(peer->update_source, buf,
- SU_ADDRSTRLEN));
- else if (peer->update_if)
- vty_out(vty, " neighbor %s update-source %s\n", addr,
- peer->update_if);
- }
-
- /* advertisement-interval */
- if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV))
- vty_out(vty, " neighbor %s advertisement-interval %u\n", addr,
- peer->routeadv);
-
- /* timers */
- if (peergroup_flag_check(peer, PEER_FLAG_TIMER))
- vty_out(vty, " neighbor %s timers %u %u\n", addr,
- peer->keepalive, peer->holdtime);
-
- /* timers connect */
- if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT))
- vty_out(vty, " neighbor %s timers connect %u\n", addr,
- peer->connect);
-
- /* capability dynamic */
- if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY))
- vty_out(vty, " neighbor %s capability dynamic\n", addr);
-
- /* capability extended-nexthop */
- if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) {
- if (!peer->conf_if) {
- if (CHECK_FLAG(peer->flags_invert,
- PEER_FLAG_CAPABILITY_ENHE))
- vty_out(vty,
- " no neighbor %s capability extended-nexthop\n",
- addr);
- else
- vty_out(vty,
- " neighbor %s capability extended-nexthop\n",
- addr);
- }
- }
-
- /* dont-capability-negotiation */
- if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
- vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
-
- /* override-capability */
- if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY))
- vty_out(vty, " neighbor %s override-capability\n", addr);
-
- /* strict-capability-match */
- if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH))
- vty_out(vty, " neighbor %s strict-capability-match\n", addr);
-
- /* Sender side AS path loop detection. */
- if (peer->as_path_loop_detection)
- vty_out(vty, " neighbor %s sender-as-path-loop-detection\n",
- addr);
-}
-
-/* BGP peer configuration display function. */
-static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
- struct peer *peer, afi_t afi, safi_t safi)
-{
- struct peer *g_peer = NULL;
- char *addr;
- bool flag_scomm, flag_secomm, flag_slcomm;
-
- /* Skip dynamic neighbors. */
- if (peer_dynamic_neighbor(peer))
- return;
-
- if (peer->conf_if)
- addr = peer->conf_if;
- else
- addr = peer->host;
-
- /************************************
- ****** Per AF to the neighbor ******
- ************************************/
- if (peer_group_active(peer)) {
- g_peer = peer->group->conf;
-
- /* If the peer-group is active but peer is not, print a 'no
- * activate' */
- if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) {
- vty_out(vty, " no neighbor %s activate\n", addr);
- }
-
- /* If the peer-group is not active but peer is, print an
- 'activate' */
- else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) {
- vty_out(vty, " neighbor %s activate\n", addr);
- }
- } else {
- if (peer->afc[afi][safi]) {
- if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) {
- if (bgp_flag_check(bgp,
- BGP_FLAG_NO_DEFAULT_IPV4)) {
- vty_out(vty, " neighbor %s activate\n",
- addr);
- }
- } else
- vty_out(vty, " neighbor %s activate\n", addr);
- } else {
- if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) {
- if (!bgp_flag_check(bgp,
- BGP_FLAG_NO_DEFAULT_IPV4)) {
- vty_out(vty,
- " no neighbor %s activate\n",
- addr);
- }
- }
- }
- }
-
- /* addpath TX knobs */
- if (peergroup_af_addpath_check(peer, afi, safi)) {
- switch (peer->addpath_type[afi][safi]) {
- case BGP_ADDPATH_ALL:
- vty_out(vty, " neighbor %s addpath-tx-all-paths\n",
- addr);
- break;
- case BGP_ADDPATH_BEST_PER_AS:
- vty_out(vty,
- " neighbor %s addpath-tx-bestpath-per-AS\n",
- addr);
- break;
- case BGP_ADDPATH_MAX:
- case BGP_ADDPATH_NONE:
- break;
- }
- }
-
- /* ORF capability. */
- if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM)
- || peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_ORF_PREFIX_RM)) {
- vty_out(vty, " neighbor %s capability orf prefix-list", addr);
-
- if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_ORF_PREFIX_SM)
- && peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_ORF_PREFIX_RM))
- vty_out(vty, " both");
- else if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_ORF_PREFIX_SM))
- vty_out(vty, " send");
- else
- vty_out(vty, " receive");
- vty_out(vty, "\n");
- }
-
- /* BGP flag dampening. */
- if (CHECK_FLAG(bgp->af_flags[afi][safi],
- BGP_CONFIG_DAMPENING))
- bgp_config_write_damp(vty, afi, safi);
-
- /* Route reflector client. */
- if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_REFLECTOR_CLIENT)) {
- vty_out(vty, " neighbor %s route-reflector-client\n", addr);
- }
-
- /* next-hop-self force */
- if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_FORCE_NEXTHOP_SELF)) {
- vty_out(vty, " neighbor %s next-hop-self force\n", addr);
- }
-
- /* next-hop-self */
- if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) {
- vty_out(vty, " neighbor %s next-hop-self\n", addr);
- }
-
- /* remove-private-AS */
- if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) {
- vty_out(vty, " neighbor %s remove-private-AS all replace-AS\n",
- addr);
- }
-
- else if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) {
- vty_out(vty, " neighbor %s remove-private-AS replace-AS\n",
- addr);
- }
-
- else if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) {
- vty_out(vty, " neighbor %s remove-private-AS all\n", addr);
- }
-
- else if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_REMOVE_PRIVATE_AS)) {
- vty_out(vty, " neighbor %s remove-private-AS\n", addr);
- }
-
- /* as-override */
- if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) {
- vty_out(vty, " neighbor %s as-override\n", addr);
- }
-
- /* send-community print. */
- flag_scomm = peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_SEND_COMMUNITY);
- flag_secomm = peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_SEND_EXT_COMMUNITY);
- flag_slcomm = peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_SEND_LARGE_COMMUNITY);
-
- if (flag_scomm && flag_secomm && flag_slcomm) {
- vty_out(vty, " no neighbor %s send-community all\n", addr);
- } else {
- if (flag_scomm)
- vty_out(vty, " no neighbor %s send-community\n", addr);
- if (flag_secomm)
- vty_out(vty,
- " no neighbor %s send-community extended\n",
- addr);
-
- if (flag_slcomm)
- vty_out(vty, " no neighbor %s send-community large\n",
- addr);
- }
-
- /* Default information */
- if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_DEFAULT_ORIGINATE)) {
- vty_out(vty, " neighbor %s default-originate", addr);
-
- if (peer->default_rmap[afi][safi].name)
- vty_out(vty, " route-map %s",
- peer->default_rmap[afi][safi].name);
-
- vty_out(vty, "\n");
- }
-
- /* Soft reconfiguration inbound. */
- if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) {
- vty_out(vty, " neighbor %s soft-reconfiguration inbound\n",
- addr);
- }
-
- /* maximum-prefix. */
- if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) {
- vty_out(vty, " neighbor %s maximum-prefix %" PRIu32, addr,
- peer->pmax[afi][safi]);
-
- if (peer->pmax_threshold[afi][safi]
- != MAXIMUM_PREFIX_THRESHOLD_DEFAULT)
- vty_out(vty, " %u", peer->pmax_threshold[afi][safi]);
- if (peer_af_flag_check(peer, afi, safi,
- PEER_FLAG_MAX_PREFIX_WARNING))
- vty_out(vty, " warning-only");
- if (peer->pmax_restart[afi][safi])
- vty_out(vty, " restart %u",
- peer->pmax_restart[afi][safi]);
-
- vty_out(vty, "\n");
- }
-
- /* Route server client. */
- if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_RSERVER_CLIENT)) {
- vty_out(vty, " neighbor %s route-server-client\n", addr);
- }
-
- /* Nexthop-local unchanged. */
- if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) {
- vty_out(vty, " neighbor %s nexthop-local unchanged\n", addr);
- }
-
- /* allowas-in <1-10> */
- if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) {
- if (peer_af_flag_check(peer, afi, safi,
- PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
- vty_out(vty, " neighbor %s allowas-in origin\n", addr);
- } else if (peer->allowas_in[afi][safi] == 3) {
- vty_out(vty, " neighbor %s allowas-in\n", addr);
- } else {
- vty_out(vty, " neighbor %s allowas-in %d\n", addr,
- peer->allowas_in[afi][safi]);
- }
- }
-
- /* weight */
- if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT))
- vty_out(vty, " neighbor %s weight %lu\n", addr,
- peer->weight[afi][safi]);
-
- /* Filter. */
- bgp_config_write_filter(vty, peer, afi, safi);
-
- /* atribute-unchanged. */
- if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED)
- || (safi != SAFI_EVPN
- && peer_af_flag_check(peer, afi, safi,
- PEER_FLAG_NEXTHOP_UNCHANGED))
- || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) {
-
- if (!peer_group_active(peer)
- || peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_AS_PATH_UNCHANGED)
- || peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_NEXTHOP_UNCHANGED)
- || peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_MED_UNCHANGED)) {
-
- vty_out(vty,
- " neighbor %s attribute-unchanged%s%s%s\n",
- addr,
- peer_af_flag_check(peer, afi, safi,
- PEER_FLAG_AS_PATH_UNCHANGED)
- ? " as-path"
- : "",
- peer_af_flag_check(peer, afi, safi,
- PEER_FLAG_NEXTHOP_UNCHANGED)
- ? " next-hop"
- : "",
- peer_af_flag_check(peer, afi, safi,
- PEER_FLAG_MED_UNCHANGED)
- ? " med"
- : "");
- }
- }
-}
-
-/* Address family based peer configuration display. */
-static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
- safi_t safi)
-{
- struct peer *peer;
- struct peer_group *group;
- struct listnode *node, *nnode;
-
-
- vty_frame(vty, " !\n address-family ");
- if (afi == AFI_IP) {
- if (safi == SAFI_UNICAST)
- vty_frame(vty, "ipv4 unicast");
- else if (safi == SAFI_LABELED_UNICAST)
- vty_frame(vty, "ipv4 labeled-unicast");
- else if (safi == SAFI_MULTICAST)
- vty_frame(vty, "ipv4 multicast");
- else if (safi == SAFI_MPLS_VPN)
- vty_frame(vty, "ipv4 vpn");
- else if (safi == SAFI_ENCAP)
- vty_frame(vty, "ipv4 encap");
- else if (safi == SAFI_FLOWSPEC)
- vty_frame(vty, "ipv4 flowspec");
- } else if (afi == AFI_IP6) {
- if (safi == SAFI_UNICAST)
- vty_frame(vty, "ipv6 unicast");
- else if (safi == SAFI_LABELED_UNICAST)
- vty_frame(vty, "ipv6 labeled-unicast");
- else if (safi == SAFI_MULTICAST)
- vty_frame(vty, "ipv6 multicast");
- else if (safi == SAFI_MPLS_VPN)
- vty_frame(vty, "ipv6 vpn");
- else if (safi == SAFI_ENCAP)
- vty_frame(vty, "ipv6 encap");
- else if (safi == SAFI_FLOWSPEC)
- vty_frame(vty, "ipv6 flowspec");
- } else if (afi == AFI_L2VPN) {
- if (safi == SAFI_EVPN)
- vty_frame(vty, "l2vpn evpn");
- }
- vty_frame(vty, "\n");
-
- bgp_config_write_distance(vty, bgp, afi, safi);
-
- bgp_config_write_network(vty, bgp, afi, safi);
-
- bgp_config_write_redistribute(vty, bgp, afi, safi);
-
- for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
- bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi);
-
- for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
- /* Skip dynamic neighbors. */
- if (peer_dynamic_neighbor(peer))
- continue;
-
- /* Do not display doppelganger peers */
- if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
- bgp_config_write_peer_af(vty, bgp, peer, afi, safi);
- }
-
- bgp_config_write_maxpaths(vty, bgp, afi, safi);
- bgp_config_write_table_map(vty, bgp, afi, safi);
-
- if (safi == SAFI_EVPN)
- bgp_config_write_evpn_info(vty, bgp, afi, safi);
-
- if (safi == SAFI_FLOWSPEC)
- bgp_fs_config_write_pbr(vty, bgp, afi, safi);
-
- if (safi == SAFI_UNICAST) {
- bgp_vpn_policy_config_write_afi(vty, bgp, afi);
- if (CHECK_FLAG(bgp->af_flags[afi][safi],
- BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) {
-
- vty_out(vty, " export vpn\n");
- }
- if (CHECK_FLAG(bgp->af_flags[afi][safi],
- BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) {
-
- vty_out(vty, " import vpn\n");
- }
- if (CHECK_FLAG(bgp->af_flags[afi][safi],
- BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
- char *name;
-
- for (ALL_LIST_ELEMENTS_RO(
- bgp->vpn_policy[afi].import_vrf, node,
- name))
- vty_out(vty, " import vrf %s\n", name);
- }
- }
-
- vty_endframe(vty, " exit-address-family\n");
-}
-
-int bgp_config_write(struct vty *vty)
-{
- struct bgp *bgp;
- struct peer_group *group;
- struct peer *peer;
- struct listnode *node, *nnode;
- struct listnode *mnode, *mnnode;
-
- if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER)
- vty_out(vty, "bgp route-map delay-timer %u\n",
- bm->rmap_update_timer);
-
- /* BGP configuration. */
- for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
-
- /* skip all auto created vrf as they dont have user config */
- if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO))
- continue;
-
- /* Router bgp ASN */
- vty_out(vty, "router bgp %u", bgp->as);
-
- if (bgp->name)
- vty_out(vty, " %s %s",
- (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
- ? "view" : "vrf", bgp->name);
- vty_out(vty, "\n");
-
- /* BGP fast-external-failover. */
- if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER))
- vty_out(vty, " no bgp fast-external-failover\n");
-
- /* BGP router ID. */
- if (bgp->router_id_static.s_addr != 0)
- vty_out(vty, " bgp router-id %s\n",
- inet_ntoa(bgp->router_id_static));
-
- /* BGP log-neighbor-changes. */
- if (!!bgp_flag_check(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)
- != DFLT_BGP_LOG_NEIGHBOR_CHANGES)
- vty_out(vty, " %sbgp log-neighbor-changes\n",
- bgp_flag_check(bgp,
- BGP_FLAG_LOG_NEIGHBOR_CHANGES)
- ? ""
- : "no ");
-
- /* BGP configuration. */
- if (bgp_flag_check(bgp, BGP_FLAG_ALWAYS_COMPARE_MED))
- vty_out(vty, " bgp always-compare-med\n");
-
- /* RFC8212 default eBGP policy. */
- if (bgp->ebgp_requires_policy
- == DEFAULT_EBGP_POLICY_ENABLED)
- vty_out(vty, " bgp ebgp-requires-policy\n");
-
- /* draft-ietf-idr-deprecate-as-set-confed-set */
- if (bgp->reject_as_sets == BGP_REJECT_AS_SETS_ENABLED)
- vty_out(vty, " bgp reject-as-sets\n");
-
- /* BGP default ipv4-unicast. */
- if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4))
- vty_out(vty, " no bgp default ipv4-unicast\n");
-
- /* BGP default local-preference. */
- if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF)
- vty_out(vty, " bgp default local-preference %u\n",
- bgp->default_local_pref);
-
- /* BGP default show-hostname */
- if (!!bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)
- != DFLT_BGP_SHOW_HOSTNAME)
- vty_out(vty, " %sbgp default show-hostname\n",
- bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)
- ? ""
- : "no ");
-
- /* BGP default subgroup-pkt-queue-max. */
- if (bgp->default_subgroup_pkt_queue_max
- != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX)
- vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n",
- bgp->default_subgroup_pkt_queue_max);
-
- /* BGP client-to-client reflection. */
- if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT))
- vty_out(vty, " no bgp client-to-client reflection\n");
-
- /* BGP cluster ID. */
- if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID))
- vty_out(vty, " bgp cluster-id %s\n",
- inet_ntoa(bgp->cluster_id));
-
- /* Disable ebgp connected nexthop check */
- if (bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
- vty_out(vty,
- " bgp disable-ebgp-connected-route-check\n");
-
- /* Confederation identifier*/
- if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
- vty_out(vty, " bgp confederation identifier %u\n",
- bgp->confed_id);
-
- /* Confederation peer */
- if (bgp->confed_peers_cnt > 0) {
- int i;
-
- vty_out(vty, " bgp confederation peers");
-
- for (i = 0; i < bgp->confed_peers_cnt; i++)
- vty_out(vty, " %u", bgp->confed_peers[i]);
-
- vty_out(vty, "\n");
- }
-
- /* BGP deterministic-med. */
- if (!!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)
- != DFLT_BGP_DETERMINISTIC_MED)
- vty_out(vty, " %sbgp deterministic-med\n",
- bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)
- ? ""
- : "no ");
-
- /* BGP update-delay. */
- bgp_config_write_update_delay(vty, bgp);
-
- if (bgp->v_maxmed_onstartup
- != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) {
- vty_out(vty, " bgp max-med on-startup %u",
- bgp->v_maxmed_onstartup);
- if (bgp->maxmed_onstartup_value
- != BGP_MAXMED_VALUE_DEFAULT)
- vty_out(vty, " %u",
- bgp->maxmed_onstartup_value);
- vty_out(vty, "\n");
- }
- if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) {
- vty_out(vty, " bgp max-med administrative");
- if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT)
- vty_out(vty, " %u", bgp->maxmed_admin_value);
- vty_out(vty, "\n");
- }
-
- /* write quanta */
- bgp_config_write_wpkt_quanta(vty, bgp);
- /* read quanta */
- bgp_config_write_rpkt_quanta(vty, bgp);
-
- /* coalesce time */
- bgp_config_write_coalesce_time(vty, bgp);
-
- /* BGP graceful-restart. */
- if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME)
- vty_out(vty,
- " bgp graceful-restart stalepath-time %u\n",
- bgp->stalepath_time);
- if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME)
- vty_out(vty, " bgp graceful-restart restart-time %u\n",
- bgp->restart_time);
- if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_RESTART))
- vty_out(vty, " bgp graceful-restart\n");
-
- /* BGP graceful-shutdown */
- if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN))
- vty_out(vty, " bgp graceful-shutdown\n");
-
- /* BGP graceful-restart Preserve State F bit. */
- if (bgp_flag_check(bgp, BGP_FLAG_GR_PRESERVE_FWD))
- vty_out(vty,
- " bgp graceful-restart preserve-fw-state\n");
-
- /* BGP bestpath method. */
- if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_IGNORE))
- vty_out(vty, " bgp bestpath as-path ignore\n");
- if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_CONFED))
- vty_out(vty, " bgp bestpath as-path confed\n");
-
- if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
- if (bgp_flag_check(bgp,
- BGP_FLAG_MULTIPATH_RELAX_AS_SET)) {
- vty_out(vty,
- " bgp bestpath as-path multipath-relax as-set\n");
- } else {
- vty_out(vty,
- " bgp bestpath as-path multipath-relax\n");
- }
- }
-
- if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
- vty_out(vty,
- " bgp route-reflector allow-outbound-policy\n");
- }
- if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID))
- vty_out(vty, " bgp bestpath compare-routerid\n");
- if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)
- || bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) {
- vty_out(vty, " bgp bestpath med");
- if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED))
- vty_out(vty, " confed");
- if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST))
- vty_out(vty, " missing-as-worst");
- vty_out(vty, "\n");
- }
-
- /* BGP network import check. */
- if (!!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)
- != DFLT_BGP_IMPORT_CHECK)
- vty_out(vty, " %sbgp network import-check\n",
- bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)
- ? ""
- : "no ");
-
- /* BGP timers configuration. */
- if (bgp->default_keepalive != BGP_DEFAULT_KEEPALIVE
- && bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
- vty_out(vty, " timers bgp %u %u\n",
- bgp->default_keepalive, bgp->default_holdtime);
-
- /* peer-group */
- for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
- bgp_config_write_peer_global(vty, bgp, group->conf);
- }
-
- /* Normal neighbor configuration. */
- for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
- if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
- bgp_config_write_peer_global(vty, bgp, peer);
- }
-
- /* listen range and limit for dynamic BGP neighbors */
- bgp_config_write_listen(vty, bgp);
-
- /*
- * BGP default autoshutdown neighbors
- *
- * This must be placed after any peer and peer-group
- * configuration, to avoid setting all peers to shutdown after
- * a daemon restart, which is undesired behavior. (see #2286)
- */
- if (bgp->autoshutdown)
- vty_out(vty, " bgp default shutdown\n");
-
- /* IPv4 unicast configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);
-
- /* IPv4 multicast configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST);
-
- /* IPv4 labeled-unicast configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST);
-
- /* IPv4 VPN configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN);
-
- /* ENCAPv4 configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP);
-
- /* FLOWSPEC v4 configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC);
-
- /* IPv6 unicast configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST);
-
- /* IPv6 multicast configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST);
-
- /* IPv6 labeled-unicast configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP6,
- SAFI_LABELED_UNICAST);
-
- /* IPv6 VPN configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN);
-
- /* ENCAPv6 configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP);
-
- /* FLOWSPEC v6 configuration. */
- bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC);
-
- /* EVPN configuration. */
- bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN);
-
- hook_call(bgp_inst_config_write, bgp, vty);
-
-#if ENABLE_BGP_VNC
- bgp_rfapi_cfg_write(vty, bgp);
-#endif
-
- vty_out(vty, "!\n");
- }
- return 0;
-}
-
void bgp_master_init(struct thread_master *master, const int buffer_size)
{
qobj_init();
/* BGP default timer. */
uint32_t default_holdtime;
uint32_t default_keepalive;
+ uint32_t default_connect_retry;
/* BGP graceful restart */
uint32_t restart_time;
#define PEER_FLAG_WEIGHT (1 << 24) /* weight */
#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1 << 25) /* allowas-in origin */
#define PEER_FLAG_SEND_LARGE_COMMUNITY (1 << 26) /* Send large Communities */
+#define PEER_FLAG_MAX_PREFIX_OUT (1 << 27) /* outgoing maximum prefix */
enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX];
/* timestamp when the last msg was written */
_Atomic time_t last_update;
- /* Send prefix count. */
- unsigned long scount[AFI_MAX][SAFI_MAX];
-
/* Notify data. */
struct bgp_notify notify;
uint16_t pmax_restart[AFI_MAX][SAFI_MAX];
#define MAXIMUM_PREFIX_THRESHOLD_DEFAULT 75
+ /* Send prefix count. */
+ uint32_t pmax_out[AFI_MAX][SAFI_MAX];
+
/* allowas-in. */
char allowas_in[AFI_MAX][SAFI_MAX];
#define BGP_EVENTS_MAX 15
/* BGP timers default value. */
-/* note: the DFLT_ ones depend on compile-time "defaults" selection */
#define BGP_INIT_START_TIMER 1
-#define BGP_DEFAULT_HOLDTIME DFLT_BGP_HOLDTIME
-#define BGP_DEFAULT_KEEPALIVE DFLT_BGP_KEEPALIVE
+/* The following 3 are RFC defaults that are overridden in bgp_vty.c with
+ * version-/profile-specific values. The values here do not matter, they only
+ * exist to provide a clear layering separation between core and CLI.
+ */
+#define BGP_DEFAULT_HOLDTIME 180
+#define BGP_DEFAULT_KEEPALIVE 60
+#define BGP_DEFAULT_CONNECT_RETRY 120
+
#define BGP_DEFAULT_EBGP_ROUTEADV 0
#define BGP_DEFAULT_IBGP_ROUTEADV 0
-#define BGP_DEFAULT_CONNECT_RETRY DFLT_BGP_TIMERS_CONNECT
/* BGP default local preference. */
#define BGP_DEFAULT_LOCAL_PREF 100
/* BGP error codes. */
#define BGP_SUCCESS 0
+#define BGP_CREATED 1
#define BGP_ERR_INVALID_VALUE -1
#define BGP_ERR_INVALID_FLAG -2
#define BGP_ERR_INVALID_AS -3
extern int bgp_confederation_peers_add(struct bgp *, as_t);
extern int bgp_confederation_peers_remove(struct bgp *, as_t);
-extern int bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime);
+extern int bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime,
+ uint32_t connect_retry);
extern int bgp_timers_unset(struct bgp *);
extern int bgp_default_local_preference_set(struct bgp *, uint32_t);
* Make "new" a ghost attr copy of "orig"
*/
memset(new, 0, sizeof(struct attr));
- bgp_attr_dup(new, orig);
+ *new = *orig;
/*
* Set nexthop
* Make "new" a ghost attr copy of "orig"
*/
memset(new, 0, sizeof(struct attr));
- bgp_attr_dup(new, orig);
+ *new = *orig;
/*
* Set nexthop
* all of the possible returns above.
*/
memset(&hattr, 0, sizeof(struct attr));
- bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */
+ /* hattr becomes a ghost attr */
+ hattr = *attr;
if (rmap) {
struct bgp_path_info info;
* all of the possible returns above.
*/
memset(&hattr, 0, sizeof(struct attr));
- bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */
+ /* hattr becomes a ghost attr */
+ hattr = *attr;
if (rmap) {
struct bgp_path_info info;
* all of the possible returns above.
*/
memset(&hattr, 0, sizeof(struct attr));
- bgp_attr_dup(&hattr, attr); /* hattr becomes a ghost attr */
+ /* hattr becomes a ghost attr */
+ hattr = *attr;
if (rmap) {
struct bgp_path_info path;
/* use local_pref from unicast route */
memset(&new_attr, 0, sizeof(struct attr));
- bgp_attr_dup(&new_attr, bpi_interior->attr);
+ new_attr = *bpi_interior->attr;
if (info->attr->flag
& ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
new_attr.local_pref =
/* use local_pref from unicast route */
memset(&new_attr, 0, sizeof(struct attr));
- bgp_attr_dup(&new_attr, bpi_interior->attr);
+ new_attr = *bpi_interior->attr;
if (bpi_exterior
&& (bpi_exterior->attr->flag
& ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
/* use local_pref from unicast route */
memset(&new_attr, 0, sizeof(struct attr));
- bgp_attr_dup(&new_attr, bpi_interior->attr);
+ new_attr = *bpi_interior->attr;
if (bpi_exterior
&& (bpi_exterior->attr->flag
& ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
/* use local_pref from unicast route */
memset(&new_attr, 0, sizeof(struct attr));
- bgp_attr_dup(&new_attr, bpi_interior->attr);
+ new_attr = *bpi_interior->attr;
if (bpi_exterior
&& (bpi_exterior->attr->flag
& ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
/* use local_pref from unicast route */
memset(&new_attr, 0, sizeof(struct attr));
- bgp_attr_dup(&new_attr, bpi->attr);
+ new_attr = *bpi->attr;
if (bpi_exterior
&& (bpi_exterior->attr->flag
& ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
if BGP_BMP
module_LTLIBRARIES += bgpd/bgpd_bmp.la
endif
-man8 += $(MANBUILD)/bgpd.8
+man8 += $(MANBUILD)/frr-bgpd.8
endif
bgpd_libbgp_a_SOURCES = \
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)
##
AC_PREREQ([2.60])
-AC_INIT([frr], [7.3-dev], [https://github.com/frrouting/frr/issues])
+AC_INIT([frr], [7.4-dev], [https://github.com/frrouting/frr/issues])
PACKAGE_URL="https://frrouting.org/"
AC_SUBST([PACKAGE_URL])
PACKAGE_FULLNAME="FRRouting"
dnl case 1: external clippy
if test -n "$with_clippy" -a "$with_clippy" != "no" -a "$with_clippy" != "yes"; then
- if test "$enable_clippy_only" == "yes"; then
+ if test "$enable_clippy_only" = "yes"; then
AC_MSG_ERROR([--enable-clippy-only does not make sense with --with-clippy])
fi
fi
LDFLAGS="${LDFLAGS} -lgcov"
-elif test "x${enable_dev_build}" = "xyes"; then
+fi
+
+if test "x${enable_clang_coverage}" = "xyes"; then
+ AC_C_FLAG([-fprofile-instr-generate], [
+ AC_MSG_ERROR([$CC does not support -fprofile-instr-generate.])
+ ])
+ AC_C_FLAG([-fcoverage-mapping], [
+ AC_MSG_ERROR([$CC does not support -fcoverage-mapping.])
+ ])
+fi
+
+if test "x${enable_dev_build}" = "xyes"; then
AC_DEFINE([DEV_BUILD], [1], [Build for development])
if test "z$orig_cflags" = "z"; then
AC_C_FLAG([-g3])
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])
AC_ARG_ENABLE([numeric_version],
AS_HELP_STRING([--enable-numeric-version], [Only numeric digits allowed in version (for Alpine)]))
AC_ARG_ENABLE([gcov],
- AS_HELP_STRING([--enable-gcov], [Add code coverage information]))
+ AS_HELP_STRING([--enable-gcov], [Collect coverage information with gcov]))
+AC_ARG_ENABLE([clang_coverage],
+ AS_HELP_STRING([--enable-clang-coverage], [Collect coverage information with Clang Coverage]))
AC_ARG_ENABLE([bfdd],
AS_HELP_STRING([--disable-bfdd], [do not build bfdd]))
AC_ARG_ENABLE([address-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)]))
if test "${enable_datacenter}" = "yes" ; then
AC_DEFINE([HAVE_DATACENTER], [1], [Compile extensions for a DataCenter])
+ AC_MSG_WARN([The --enable-datacenter compile time option is deprecated. Please modify the init script to pass -F datacenter to the daemons instead.])
DFLT_NAME="datacenter"
else
DFLT_NAME="traditional"
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*)
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])
;;
esac
AM_CONDITIONAL([SOLARIS], [test "${SOLARIS}" = "solaris"])
+AM_CONDITIONAL([LINUX], [${is_linux}])
AC_SYS_LARGEFILE
#
# Logic for protobuf support.
#
+PROTO3=false
if test "$enable_protobuf" = "yes"; then
# Check for protoc & protoc-c
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
AC_DEFINE([HAVE_LIBUNWIND], [1], [libunwind])
backtrace_ok=yes
], [
+ true
+ ])
+
+ if test "$backtrace_ok" = "no"; then
+ AC_CHECK_HEADER([unwind.h], [
+ AC_SEARCH_LIBS([unw_getcontext], [unwind], [
+ AC_DEFINE([HAVE_LIBUNWIND], [1], [libunwind])
+ backtrace_ok=yes
+ ])
+ ])
+ fi
+
+ if test "$backtrace_ok" = "no"; then
case "$host_os" in
sunos* | solaris2*)
AC_CHECK_FUNCS([printstack], [
],, [-lm])
])
fi
- ])
+ fi
if test x"${enable_backtrace}" = x"yes" -a x"${backtrace_ok}" = x"no"; then
dnl user explicitly requested backtrace but we failed to find support
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"])
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
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
# open, as well as the daemons, so always signal the daemons.
# It's safe, a NOP if (only) syslog is being used.
for i in babeld bgpd eigrpd isisd ldpd nhrpd ospf6d ospfd \
- pimd ripd ripngd zebra staticd fabricd; do
+ pimd ripd ripngd zebra pbrd staticd bfdd fabricd vrrpd; do
if [ -e /var/run/frr/$i.pid ] ; then
pids="$pids $(cat /var/run/frr/$i.pid)"
fi
+doc/manpages/_build/man/frr-bgpd.8
+doc/manpages/_build/man/frr-eigrpd.8
+doc/manpages/_build/man/frr-fabricd.8
+doc/manpages/_build/man/frr-isisd.8
+doc/manpages/_build/man/frr-ldpd.8
+doc/manpages/_build/man/frr-nhrpd.8
+doc/manpages/_build/man/frr-ospf6d.8
+doc/manpages/_build/man/frr-ospfd.8
+doc/manpages/_build/man/frr-pimd.8
+doc/manpages/_build/man/frr-ripd.8
+doc/manpages/_build/man/frr-ripngd.8
+doc/manpages/_build/man/frr-watchfrr.8
+doc/manpages/_build/man/frr-zebra.8
doc/manpages/_build/man/frr.1
-doc/manpages/_build/man/bgpd.8
-doc/manpages/_build/man/pimd.8
-doc/manpages/_build/man/eigrpd.8
-doc/manpages/_build/man/ldpd.8
-doc/manpages/_build/man/nhrpd.8
-doc/manpages/_build/man/ospf6d.8
-doc/manpages/_build/man/ospfd.8
-doc/manpages/_build/man/ripd.8
-doc/manpages/_build/man/ripngd.8
-doc/manpages/_build/man/vtysh.1
-doc/manpages/_build/man/zebra.8
-doc/manpages/_build/man/isisd.8
-doc/manpages/_build/man/watchfrr.8
doc/manpages/_build/man/mtracebis.8
-doc/manpages/_build/man/fabricd.8
+doc/manpages/_build/man/vtysh.1
+++ /dev/null
-/*
- * FRR switchable defaults.
- * Copyright (C) 2017 David Lamparter for NetDEF, Inc.
- *
- * This file is part of FRRouting (FRR).
- *
- * FRR is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 2, or (at your option) any later version.
- *
- * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef _FRR_DEFAULTS_H
-#define _FRR_DEFAULTS_H
-
-#include "config.h"
-
-#ifdef HAVE_DATACENTER
-
-#define DFLT_BGP_IMPORT_CHECK 1
-#define DFLT_BGP_TIMERS_CONNECT 10
-#define DFLT_BGP_HOLDTIME 9
-#define DFLT_BGP_KEEPALIVE 3
-#define DFLT_BGP_LOG_NEIGHBOR_CHANGES 1
-#define DFLT_BGP_SHOW_HOSTNAME 1
-#define DFLT_BGP_DETERMINISTIC_MED 1
-
-#define DFLT_OSPF_LOG_ADJACENCY_CHANGES 1
-#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES 1
-
-#else /* !HAVE_DATACENTER */
-
-#define DFLT_BGP_IMPORT_CHECK 0
-#define DFLT_BGP_TIMERS_CONNECT 120
-#define DFLT_BGP_HOLDTIME 180
-#define DFLT_BGP_KEEPALIVE 60
-#define DFLT_BGP_LOG_NEIGHBOR_CHANGES 0
-#define DFLT_BGP_SHOW_HOSTNAME 0
-#define DFLT_BGP_DETERMINISTIC_MED 0
-
-#define DFLT_OSPF_LOG_ADJACENCY_CHANGES 0
-#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES 0
-
-#endif /* !HAVE_DATACENTER */
-
-#endif /* _FRR_DEFAULTS_H */
.. code-block:: shell
- sudo install -p -m 644 redhat/daemons /etc/frr/
+ sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/
sudo chown frr:frr /etc/frr/daemons
Edit /etc/frr/daemons as needed to select the required daemons
sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf
-Add init.d startup files
-^^^^^^^^^^^^^^^^^^^^^^^^
+Add init.d startup file
+^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: shell
- sudo install -p -m 755 redhat/frr.init /etc/init.d/frr
+ sudo install -p -m 755 tools/frr /etc/init.d/frr
sudo chkconfig --add frr
Enable FRR daemon at startup
::
- sudo install -p -m 644 redhat/daemons /etc/frr/
+ sudo install -p -m 644 tools/etc/frr/daemons /etc/frr/
sudo chown frr:frr /etc/frr/daemons
Edit /etc/frr/daemons as needed to select the required daemons
sudo sysctl -p /etc/sysctl.d/90-routing-sysctl.conf
-Install frr Service and redhat init files
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Install frr Service
+^^^^^^^^^^^^^^^^^^^
::
- 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
+ sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service
Register the systemd files
^^^^^^^^^^^^^^^^^^^^^^^^^^
--- /dev/null
+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 tools/etc/frr/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
+^^^^^^^^^^^^^^^^^^^
+
+::
+
+ sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service
+
+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
sudo systemctl stop firewalld.service
sudo iptables -F
-Install service files
-^^^^^^^^^^^^^^^^^^^^^
+Install frr Service
+^^^^^^^^^^^^^^^^^^^
.. code-block:: console
- 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
+ sudo install -p -m 644 tools/frr.service /usr/lib/systemd/system/frr.service
sudo systemctl enable frr
Enable daemons
.. toctree::
:maxdepth: 2
+ static-linking
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
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.
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
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
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::
%{!?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
--- /dev/null
+.. _static-linking:
+
+Static Linking
+==============
+
+This document describes how to build FRR without hard dependencies on shared
+libraries. Note that it's not possible to build FRR *completely* statically.
+This document just covers how to statically link the dependencies that aren't
+likely to be present on a given platform - libfrr and libyang. The resultant
+binaries should still be fairly portable. For example, here is the DSO
+dependency list for `bgpd` after using these steps:
+
+.. code-block::
+
+ $ ldd bgpd
+ linux-vdso.so.1 (0x00007ffe3a989000)
+ libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f9dc10c0000)
+ libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007f9dc0eba000)
+ libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9dc0b1c000)
+ libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9dc0918000)
+ libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007f9dc06e0000)
+ libjson-c.so.3 => /lib/x86_64-linux-gnu/libjson-c.so.3 (0x00007f9dc04d5000)
+ librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f9dc02cd000)
+ libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9dc00ae000)
+ libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f9dbfe96000)
+ libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9dbfaa5000)
+ /lib64/ld-linux-x86-64.so.2 (0x00007f9dc1449000)
+
+Procedure
+---------
+Note that these steps have only been tested with LLVM 9 / clang.
+
+Today, libfrr can already be statically linked by passing these configure
+options::
+
+ --enable-static --enable-static-bin --enable-shared
+
+libyang is more complicated. You must build and install libyang as a static
+library. To do this, follow the usual libyang build procedure as listed in the
+FRR developer docs, but set the ``ENABLE_STATIC`` option in your cmake
+invocation. You also need to build with PIC enabled, which today is disabled
+when building libyang statically.
+
+The resultant cmake command is::
+
+ cmake -DENABLE_STATIC=ON -DENABLE_LYD_PRIV=ON \
+ -DCMAKE_INSTALL_PREFIX:PATH=/usr \
+ -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \
+ -DCMAKE_BUILD_TYPE:String="Release" ..
+
+This produces a bunch of ``.a`` static archives that need to ultimately be linked
+into FRR. However, not only is it 6 archives rather than the usual ``libyang.so``,
+you will now also need to link FRR with ``libpcre.a``. Ubuntu's ``libpcre3-dev``
+package provides this, but it hasn't been built with PIC enabled, so it's not
+usable for our purposes. So download ``libpcre`` from
+`SourceForge <https://sourceforge.net/projects/pcre/>`_, and build it
+like this:
+
+.. code-block::
+
+ ./configure --with-pic
+ make
+
+Hopefully you get a nice, usable, PIC ``libpcre.a``.
+
+So now we have to link all these static libraries into FRR. Rather than modify
+FRR to accomodate this, the best option is to create an archive with all of
+libyang's dependencies. Then to avoid making any changes to FRR build foo,
+rename this ``libyang.a`` and copy it over the usual static library location.
+Ugly but it works. To do this, go into your libyang build directory, which
+should have a bunch of ``.a`` files. Copy ``libpcre.a`` into this directory.
+Write the following into a shell script and run it:
+
+.. code-block:: shell
+
+ #!/bin/bash
+ ar -M <<EOM
+ CREATE libyang_fat.a
+ ADDLIB libyang.a
+ ADDLIB libyangdata.a
+ ADDLIB libmetadata.a
+ ADDLIB libnacm.a
+ ADDLIB libuser_inet_types.a
+ ADDLIB libuser_yang_types.a
+ ADDLIB libpcre.a
+ SAVE
+ END
+ EOM
+ ranlib libyang_fat.a
+
+``libyang_fat.a`` is your archive. Now copy this over your install
+``libyang.a``, which on my machine is located at
+``/usr/lib/x86_64-linux-gnu/libyang.a`` (try ``locate libyang.a`` if not).
+
+Now when you build FRR with the static options enabled as above, clang should
+pick up the static libyang and link it, leaving you with FRR binaries that have
+no hard DSO dependencies beyond common system libraries. To verify, run ``ldd``
+over the resultant binaries.
doc/developer/packaging-redhat.rst \
doc/developer/packaging.rst \
doc/developer/rcu.rst \
+ doc/developer/static-linking.rst \
doc/developer/testing.rst \
doc/developer/topotests-snippets.rst \
doc/developer/topotests.rst \
--sysconfdir=/etc/frr \
--enable-vtysh \
--enable-pimd \
+ --enable-sharpd \
--enable-multipath=64 \
--enable-user=frr \
--enable-group=frr \
- ``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:
using the signed-off-by process as the Linux kernel. In short, you must include
a ``Signed-off-by`` tag in every patch.
+An easy way to do this is to use ``git commit -s`` where ``-s`` will automatically
+append a signed-off line to the end of your commit message. Also, if you commit
+and forgot to add the line you can use ``git commit --amend -s`` to add the
+signed-off line to the last commit.
+
``Signed-off-by`` is a developer's certification that they have the right to
submit the patch for inclusion into the project. It is an agreement to the
:ref:`Developer's Certificate of Origin <developers-certificate-of-origin>`.
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
========================
+++ /dev/null
-****
-BFDD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: bfdd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a communication failure detection component that works with
-the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-.. include:: bfd-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-****
-BGPD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: bgpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-.. option:: -p, --bgp_port <port>
-
- Set the bgp protocol's port number. When port number is 0, that means do not
- listen bgp port.
-
-.. option:: -l, --listenon
-
- Specify a specific IP address for bgpd to listen on, rather than its default
- of ``0.0.0.0`` / ``::``. This can be useful to constrain bgpd to an internal
- address, or to run multiple bgpd processes on one host.
-
-.. option:: -n, --no_kernel
-
- Do not install learned routes into the linux kernel. This option is useful
- for a route-reflector environment or if you are running multiple bgp
- 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
- was compiled with. The value specified must be greater than 0 and less
- than or equal to the MULTIPATH_NUM specified on compilation.
-
-.. option:: -Z, --no_zebra
-
- Do not communicate with zebra at all. This is different than the --no_kernel
- option in that we do not even open a ZAPI connection to the zebra process.
-
-.. option:: -s, --socket_size
-
- When opening tcp connections to our peers, set the socket send buffer
- size that the kernel will use for the peers socket. This option
- is only really useful at a very large scale. Experimentation should
- be done to see if this is helping or not at the scale you are running
- at.
-
-LABEL MANAGER
--------------
-
-.. option:: -I, --int_num
-
- Set zclient id. This is required when using Zebra label manager in proxy mode.
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
fwfrr = "{0} routing engine for use with FRRouting."
man_pages = [
- ('bgpd', 'bgpd', fwfrr.format("a BGPv4, BGPv4+, BGPv4- "), [], 8),
- ('eigrpd', 'eigrpd', fwfrr.format("an EIGRP "), [], 8),
- ('ospf6d', 'ospf6d', fwfrr.format("an OSPFv3 "), [], 8),
- ('ospfd', 'ospfd', fwfrr.format("an OSPFv2 "), [], 8),
- ('isisd', 'isisd', fwfrr.format("an IS-IS "), [], 8),
- ('ospfclient', 'ospfclient', 'an example ospf-api client', [], 8),
- ('ldpd', 'ldpd', fwfrr.format("an LDP "), [], 8),
- ('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8),
- ('pimd', 'pimd', fwfrr.format("a PIM "), [], 8),
- ('pbrd', 'pbrd', fwfrr.format("a PBR "), [], 8),
- ('sharpd', 'sharpd', fwfrr.format("a SHARP "), [], 8),
- ('staticd', 'staticd', fwfrr.format("a static route manager "), [], 8),
- ('mtracebis', 'mtracebis', "a multicast trace client", [], 8),
- ('ripd', 'ripd', fwfrr.format("a RIP "), [], 8),
- ('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8),
- ('zebra', 'zebra', 'a routing manager for use with associated FRRouting components.', [], 8),
- ('watchfrr', 'watchfrr', 'a program to monitor the status of FRRouting daemons', [], 8),
- ('vtysh', 'vtysh', 'an integrated shell for FRRouting.', [], 1),
+ ('frr-bfdd', 'frr-bfdd', fwfrr.format("a bfd"), [], 8),
+ ('frr-bgpd', 'frr-bgpd', fwfrr.format("a BGPv4, BGPv4+, BGPv4-"), [], 8),
+ ('frr-eigrpd', 'frr-eigrpd', fwfrr.format("an EIGRP"), [], 8),
+ ('frr-fabricd', 'frr-fabricd', fwfrr.format("an OpenFabric"), [], 8),
+ ('frr-isisd', 'frr-isisd', fwfrr.format("an IS-IS"), [], 8),
+ ('frr-ldpd', 'frr-ldpd', fwfrr.format("an LDP"), [], 8),
+ ('frr-nhrpd', 'frr-nhrpd', fwfrr.format("a Next Hop Routing Protocol"), [], 8),
+ ('frr-ospf6d', 'frr-ospf6d', fwfrr.format("an OSPFv3"), [], 8),
+ ('frr-ospfclient', 'frr-ospfclient', 'an example ospf-api client', [], 8),
+ ('frr-ospfd', 'frr-ospfd', fwfrr.format("an OSPFv2"), [], 8),
+ ('frr-pbrd', 'frr-pbrd', fwfrr.format("a PBR"), [], 8),
+ ('frr-pimd', 'frr-pimd', fwfrr.format("a PIM"), [], 8),
+ ('frr-ripd', 'frr-ripd', fwfrr.format("a RIP"), [], 8),
+ ('frr-ripngd', 'frr-ripngd', fwfrr.format("a RIPNG"), [], 8),
+ ('frr-sharpd', 'frr-sharpd', fwfrr.format("a SHARP"), [], 8),
+ ('frr-staticd', 'frr-staticd', fwfrr.format("a static route manager"), [], 8),
+ ('frr-vrrpd', 'frr-vrrpd', fwfrr.format("a VRRP"), [], 8),
+ ('frr-watchfrr', 'frr-watchfrr', 'a program to monitor the status of FRRouting daemons', [], 8),
+ ('frr-zebra', 'frr-zebra', 'a routing manager for use with associated FRRouting components.', [], 8),
('frr', 'frr', 'a systemd interaction script', [], 1),
- ('bfdd', 'bfdd', fwfrr.format("a bfd"), [], 8),
- ('fabricd', 'fabricd', fwfrr.format("an OpenFabric "), [], 8),
- ('vrrpd', 'vrrpd', fwfrr.format("a VRRP"), [], 8),
+ ('mtracebis', 'mtracebis', "a multicast trace client", [], 8),
+ ('vtysh', 'vtysh', 'an integrated shell for FRRouting.', [], 1),
]
# -- Options for Texinfo output -------------------------------------------
.. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path]
.. |synopsis-options-hv| replace:: [-h] [-v]
-.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), vrrpd(8), mtracebis(8)
+.. |seealso-programs| replace:: frr-zebra(8), vtysh(1), frr-ripd(8), frr-ripngd(8), frr-ospfd(8), frr-ospf6d(8), frr-bgpd(8), frr-isisd(8), frr-babeld(8), frr-nhrpd(8), frr-pimd(8), frr-pbrd(8), frr-ldpd(8), frr-eigrpd(8), frr-staticd(8), frr-fabricd(8), frr-vrrpd(8), mtracebis(8)
+++ /dev/null
-******
-EIGRPD
-******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: eigrpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-*******
-FABRICD
-*******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: fabricd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
--- /dev/null
+****
+BFDD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: bfdd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a communication failure detection component that works with
+the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+.. include:: bfd-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+****
+BGPD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: bgpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+.. option:: -p, --bgp_port <port>
+
+ Set the bgp protocol's port number. When port number is 0, that means do not
+ listen bgp port.
+
+.. option:: -l, --listenon
+
+ Specify a specific IP address for bgpd to listen on, rather than its default
+ of ``0.0.0.0`` / ``::``. This can be useful to constrain bgpd to an internal
+ address, or to run multiple bgpd processes on one host.
+
+.. option:: -n, --no_kernel
+
+ Do not install learned routes into the linux kernel. This option is useful
+ for a route-reflector environment or if you are running multiple bgp
+ processes in the same namespace. This option is different than the --no_zebra
+ option in that a ZAPI connection is made.
+
+.. option:: -e, --ecmp
+
+ Run BGP with a limited ecmp capability, that is different than what BGP
+ was compiled with. The value specified must be greater than 0 and less
+ than or equal to the MULTIPATH_NUM specified on compilation.
+
+.. option:: -Z, --no_zebra
+
+ Do not communicate with zebra at all. This is different than the --no_kernel
+ option in that we do not even open a ZAPI connection to the zebra process.
+
+.. option:: -s, --socket_size
+
+ When opening tcp connections to our peers, set the socket send buffer
+ size that the kernel will use for the peers socket. This option
+ is only really useful at a very large scale. Experimentation should
+ be done to see if this is helping or not at the scale you are running
+ at.
+
+LABEL MANAGER
+-------------
+
+.. option:: -I, --int_num
+
+ Set zclient id. This is required when using Zebra label manager in proxy mode.
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+******
+EIGRPD
+******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: eigrpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+*******
+FABRICD
+*******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: fabricd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+*****
+ISISD
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: isisd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+****
+LDPD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: ldpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+*****
+NHRPD
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: nhrpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+******
+OSPF6D
+******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: ospf6d
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+**********
+OSPFCLIENT
+**********
+
+.. include:: defines.rst
+
+SYNOPSIS
+========
+ospfclient <ospfd> <lsatype> <opaquetype> <opaqueid> <ifaddr> <areaid>
+
+DESCRIPTION
+===========
+ospfclient is an example ospf-api client to test the ospfd daemon.
+
+OPTIONS
+=======
+
+.. option:: ospfd
+
+ A router where the API-enabled OSPF daemon is running.
+
+.. option:: lsatype
+
+ The value has to be either "9", "10", or "11", depending on the flooding scope.
+
+.. option:: opaquetype
+
+ The value has to be in the range of 0-255 (for example, experimental applications might use opaquetype larger than 128).
+
+.. option:: opaqueid
+
+ Arbitrary application instance (24 bits).
+
+.. option:: ifaddr
+
+ Interface IP address for type 9, otherwise it will be ignored.
+
+.. option:: areaid
+
+ Area in the IP address format for type 10, otherwise it will be ignored.
+
+
+.. include:: epilogue.rst
--- /dev/null
+*****
+OSPFD
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: ospfd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+.. option:: -a, --apiserver
+
+ Enable the OSPF API server.
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+****
+PBRD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: pbrd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
+
--- /dev/null
+****
+PIMD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: pimd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+****
+RIPD
+****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: ripd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+******
+RIPNGD
+******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: ripngd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+******
+SHARPD
+******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: sharpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
+
--- /dev/null
+*******
+STATICD
+*******
+
+.. include:: defines.rst
+.. |DAEMON| replace:: staticd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting engine.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
+
--- /dev/null
+*****
+VRRPD
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: vrrpd
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing component that works with the FRRouting routing engine.
+It implements the Virtual Router Redundancy Protocol. Support for both VRRPv2
+and VRRPv3 is present.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+********
+WATCHFRR
+********
+
+.. include:: defines.rst
+.. |DAEMON| replace:: watchfrr
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| [option...] <daemon>...
+
+
+DESCRIPTION
+===========
+|DAEMON| is a watchdog program that monitors the status of supplied frr daemons and tries to restart them in case they become unresponsive or shut down.
+
+To determine whether a daemon is running, it tries to connect to the daemon's VTY UNIX stream socket, and send echo commands to ensure the daemon responds. When the daemon crashes, EOF is received from the socket, so that |DAEMON| can react immediately.
+
+In order to avoid restarting the daemons in quick succession, you can supply the -m and -M options to set the minimum and maximum delay between the restart commands. The minimum restart delay is recalculated each time a restart is attempted. If the time since the last restart attempt exceeds twice the value of -M, the restart delay is set to the value of -m, otherwise the interval is doubled (but capped at the value of -M).
+
+OPTIONS
+=======
+
+.. option:: --dry
+
+ Run |DAEMON| in "dry-run" mode, only monitoring the specified daemons but not performing any start/stop/restart actions.
+
+.. option:: -d, --daemon
+
+ Run in daemon mode. When supplied, error messages are sent to Syslog instead of standard output (stdout).
+
+.. option:: -S <directory>, --statedir <directory>
+
+ Set the VTY socket directory (the default value is "/var/run/frr").
+
+.. option:: -l <level>, --loglevel <level>
+
+ Set the logging level (the default value is "6"). The value should range from 0 (LOG_EMERG) to 7 (LOG_DEBUG), but higher number can be supplied if extra debugging messages are required.
+
+.. option:: --min-restart-interval <number>
+
+ Set the minimum number of seconds to wait between invocations of the daemon restart commands (the default value is "60").
+
+.. option:: --max-restart-interval <number>
+
+ Set the maximum number of seconds to wait between invocations of the daemon restart commands (the default value is "600").
+
+.. option:: -i <number>, --interval <number>
+
+ Set the status polling interval in seconds (the default value is "5").
+
+.. option:: -t <number>, --timeout <number>
+
+ Set the unresponsiveness timeout in seconds (the default value is "10").
+
+.. option:: -T <number>, --restart-timeout <number>
+
+ Set the restart (kill) timeout in seconds (the default value is "20"). If any background jobs are still running after this period has elapsed, they will be killed.
+
+.. option:: -p <filename>, --pid-file <filename>
+
+ Set the process identifier filename (the default value is "/var/run/frr/|DAEMON|.pid").
+
+.. option:: -b <string>, --blank-string <string>
+
+ When the supplied string is found in any of the command line option arguments (i.e., -r, -s, or -k), replace it with a space.
+
+ This is an ugly hack to circumvent problems with passing the command line arguments containing embedded spaces.
+
+.. option:: -v, --version
+
+ Display the version information and exit.
+
+.. option:: -h, --help
+
+ Display the usage information and exit.
+
+The following 3 options specify scripts that |DAEMON| uses to perform start/stop/restart actions. Reasonable default values are built into watchfrr, so the use of these options should no longer be necessary:
+
+.. option:: -s command, --start-command command
+
+ Supply a Bourne shell command to start a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
+
+.. option:: -k command, --kill-command command
+
+ Supply a Bourne shell command to stop a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
+
+.. option:: -r command, --restart command
+
+ Supply a Bourne shell command to restart a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
+
+PREVIOUS OPTIONS
+================
+Prior versions of |DAEMON| supported some additional options that no longer exist:::
+
+ -a, -A, -e, -R, -z
+
+The ``-a``, ``-A`` and ``-R`` options were used to select alternate monitoring modes that offered different patterns of restarting daemons. The "correct" mode (phased restart) is now the default. The -e and -z options used to disable some monitoring aspects, |DAEMON| now always has all monitoring features enabled.
+
+Removing these options should result in correct operation, if it does not please file a bug report.
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
--- /dev/null
+*****
+ZEBRA
+*****
+
+.. include:: defines.rst
+.. |DAEMON| replace:: zebra
+
+SYNOPSIS
+========
+|DAEMON| |synopsis-options-hv|
+
+|DAEMON| |synopsis-options|
+
+DESCRIPTION
+===========
+|DAEMON| is a routing manager that implements the zebra route engine. zebra supports all protocol daemons in the FRRouting suite.
+
+OPTIONS
+=======
+OPTIONS available for the |DAEMON| command:
+
+.. include:: common-options.rst
+
+.. option:: -b, --batch
+
+ Runs in batch mode, zebra parses its config and exits.
+
+.. option:: -k, --keep_kernel
+
+ On startup, don't delete self inserted routes.
+
+.. option:: -s, --nl-bufsize <netlink-buffer-size>
+
+ Set netlink receive buffer size. There are cases where zebra daemon can't handle flood of netlink messages from kernel. If you ever see "recvmsg overrun" messages in zebra log, you are in trouble.
+
+ Solution is to increase receive buffer of netlink socket. Note that kernel < 2.6.14 doesn't allow increasing it over maximum value defined in /proc/sys/net/core/rmem_max. If you want to do it, you have to increase maximum before starting zebra.
+
+ Note that this affects Linux only.
+
+
+.. option:: -n, --vrfwnetns
+
+ Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite support from the Linux kernel. This option permits discovering Linux named network namespaces and mapping it to FRR VRF contexts.
+
+ROUTES
+------
+
+.. option:: -r, --retain
+
+ When the program terminates, do not flush routes installed by zebra from the kernel.
+
+
+FILES
+=====
+
+|INSTALL_PREFIX_SBIN|/|DAEMON|
+ The default location of the |DAEMON| binary.
+
+|INSTALL_PREFIX_ETC|/|DAEMON|.conf
+ The default location of the |DAEMON| config file.
+
+$(PWD)/|DAEMON|.log
+ If the |DAEMON| process is configured to output logs to a file, then you
+ will find this file in the directory where you started |DAEMON|.
+
+.. include:: epilogue.rst
+
.. toctree::
:maxdepth: 2
- bfdd
- bgpd
- eigrpd
- isisd
- fabricd
- ldpd
- nhrpd
- ospf6d
- ospfclient
- ospfd
- pimd
- pbrd
+ frr
+ frr-bfdd
+ frr-bgpd
+ frr-eigrpd
+ frr-isisd
+ frr-fabricd
+ frr-ldpd
+ frr-nhrpd
+ frr-ospf6d
+ frr-ospfclient
+ frr-ospfd
+ frr-pimd
+ frr-pbrd
+ frr-ripd
+ frr-ripngd
+ frr-sharpd
+ frr-staticd
+ frr-watchfrr
+ frr-zebra
+ frr-vrrpd
mtracebis
- ripd
- ripngd
- sharpd
- staticd
- watchfrr
- zebra
vtysh
- vrrpd
- frr
+++ /dev/null
-*****
-ISISD
-*****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: isisd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-****
-LDPD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: ldpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-*****
-NHRPD
-*****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: nhrpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-******
-OSPF6D
-******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: ospf6d
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-**********
-OSPFCLIENT
-**********
-
-.. include:: defines.rst
-
-SYNOPSIS
-========
-ospfclient <ospfd> <lsatype> <opaquetype> <opaqueid> <ifaddr> <areaid>
-
-DESCRIPTION
-===========
-ospfclient is an example ospf-api client to test the ospfd daemon.
-
-OPTIONS
-=======
-
-.. option:: ospfd
-
- A router where the API-enabled OSPF daemon is running.
-
-.. option:: lsatype
-
- The value has to be either "9", "10", or "11", depending on the flooding scope.
-
-.. option:: opaquetype
-
- The value has to be in the range of 0-255 (for example, experimental applications might use opaquetype larger than 128).
-
-.. option:: opaqueid
-
- Arbitrary application instance (24 bits).
-
-.. option:: ifaddr
-
- Interface IP address for type 9, otherwise it will be ignored.
-
-.. option:: areaid
-
- Area in the IP address format for type 10, otherwise it will be ignored.
-
-
-.. include:: epilogue.rst
+++ /dev/null
-*****
-OSPFD
-*****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: ospfd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-.. option:: -a, --apiserver
-
- Enable the OSPF API server.
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-****
-PBRD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: pbrd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
-
+++ /dev/null
-****
-PIMD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: pimd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-****
-RIPD
-****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: ripd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-******
-RIPNGD
-******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: ripngd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-******
-SHARPD
-******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: sharpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
-
+++ /dev/null
-*******
-STATICD
-*******
-
-.. include:: defines.rst
-.. |DAEMON| replace:: staticd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting engine.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
-
#
man_RSTFILES = \
- doc/manpages/bgpd.rst \
+ doc/manpages/bfd-options.rst \
doc/manpages/common-options.rst \
doc/manpages/conf.py \
doc/manpages/defines.rst \
- doc/manpages/eigrpd.rst \
doc/manpages/epilogue.rst \
- doc/manpages/fabricd.rst \
+ doc/manpages/frr-bfdd.rst \
+ doc/manpages/frr-bgpd.rst \
+ doc/manpages/frr-eigrpd.rst \
+ doc/manpages/frr-fabricd.rst \
+ doc/manpages/frr-isisd.rst \
+ doc/manpages/frr-ldpd.rst \
+ doc/manpages/frr-nhrpd.rst \
+ doc/manpages/frr-ospf6d.rst \
+ doc/manpages/frr-ospfclient.rst \
+ doc/manpages/frr-ospfd.rst \
+ doc/manpages/frr-pbrd.rst \
+ doc/manpages/frr-pimd.rst \
+ doc/manpages/frr-ripd.rst \
+ doc/manpages/frr-ripngd.rst \
+ doc/manpages/frr-sharpd.rst \
+ doc/manpages/frr-staticd.rst \
+ doc/manpages/frr-vrrpd.rst \
+ doc/manpages/frr-watchfrr.rst \
+ doc/manpages/frr-zebra.rst \
doc/manpages/frr.rst \
doc/manpages/index.rst \
- doc/manpages/isisd.rst \
- doc/manpages/ldpd.rst \
doc/manpages/mtracebis.rst \
- doc/manpages/nhrpd.rst \
- doc/manpages/ospf6d.rst \
- doc/manpages/ospfclient.rst \
- doc/manpages/ospfd.rst \
- doc/manpages/pimd.rst \
- doc/manpages/ripd.rst \
- doc/manpages/pbrd.rst \
- doc/manpages/ripngd.rst \
- doc/manpages/sharpd.rst \
- doc/manpages/staticd.rst \
doc/manpages/vtysh.rst \
- doc/manpages/watchfrr.rst \
- doc/manpages/zebra.rst \
- doc/manpages/bfdd.rst \
- doc/manpages/bfd-options.rst \
- doc/manpages/vrrpd.rst \
# end
EXTRA_DIST += $(man_RSTFILES)
+++ /dev/null
-*****
-VRRPD
-*****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: vrrpd
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing component that works with the FRRouting routing engine.
-It implements the Virtual Router Redundancy Protocol. Support for both VRRPv2
-and VRRPv3 is present.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-********
-WATCHFRR
-********
-
-.. include:: defines.rst
-.. |DAEMON| replace:: watchfrr
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| [option...] <daemon>...
-
-
-DESCRIPTION
-===========
-|DAEMON| is a watchdog program that monitors the status of supplied frr daemons and tries to restart them in case they become unresponsive or shut down.
-
-To determine whether a daemon is running, it tries to connect to the daemon's VTY UNIX stream socket, and send echo commands to ensure the daemon responds. When the daemon crashes, EOF is received from the socket, so that |DAEMON| can react immediately.
-
-In order to avoid restarting the daemons in quick succession, you can supply the -m and -M options to set the minimum and maximum delay between the restart commands. The minimum restart delay is recalculated each time a restart is attempted. If the time since the last restart attempt exceeds twice the value of -M, the restart delay is set to the value of -m, otherwise the interval is doubled (but capped at the value of -M).
-
-OPTIONS
-=======
-
-.. option:: --dry
-
- Run |DAEMON| in "dry-run" mode, only monitoring the specified daemons but not performing any start/stop/restart actions.
-
-.. option:: -d, --daemon
-
- Run in daemon mode. When supplied, error messages are sent to Syslog instead of standard output (stdout).
-
-.. option:: -S <directory>, --statedir <directory>
-
- Set the VTY socket directory (the default value is "/var/run/frr").
-
-.. option:: -l <level>, --loglevel <level>
-
- Set the logging level (the default value is "6"). The value should range from 0 (LOG_EMERG) to 7 (LOG_DEBUG), but higher number can be supplied if extra debugging messages are required.
-
-.. option:: --min-restart-interval <number>
-
- Set the minimum number of seconds to wait between invocations of the daemon restart commands (the default value is "60").
-
-.. option:: --max-restart-interval <number>
-
- Set the maximum number of seconds to wait between invocations of the daemon restart commands (the default value is "600").
-
-.. option:: -i <number>, --interval <number>
-
- Set the status polling interval in seconds (the default value is "5").
-
-.. option:: -t <number>, --timeout <number>
-
- Set the unresponsiveness timeout in seconds (the default value is "10").
-
-.. option:: -T <number>, --restart-timeout <number>
-
- Set the restart (kill) timeout in seconds (the default value is "20"). If any background jobs are still running after this period has elapsed, they will be killed.
-
-.. option:: -p <filename>, --pid-file <filename>
-
- Set the process identifier filename (the default value is "/var/run/frr/|DAEMON|.pid").
-
-.. option:: -b <string>, --blank-string <string>
-
- When the supplied string is found in any of the command line option arguments (i.e., -r, -s, or -k), replace it with a space.
-
- This is an ugly hack to circumvent problems with passing the command line arguments containing embedded spaces.
-
-.. option:: -v, --version
-
- Display the version information and exit.
-
-.. option:: -h, --help
-
- Display the usage information and exit.
-
-The following 3 options specify scripts that |DAEMON| uses to perform start/stop/restart actions. Reasonable default values are built into watchfrr, so the use of these options should no longer be necessary:
-
-.. option:: -s command, --start-command command
-
- Supply a Bourne shell command to start a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
-
-.. option:: -k command, --kill-command command
-
- Supply a Bourne shell command to stop a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
-
-.. option:: -r command, --restart command
-
- Supply a Bourne shell command to restart a single daemon. The command string should contain the '%s' placeholder to be substituted with the daemon name.
-
-PREVIOUS OPTIONS
-================
-Prior versions of |DAEMON| supported some additional options that no longer exist:::
-
- -a, -A, -e, -R, -z
-
-The ``-a``, ``-A`` and ``-R`` options were used to select alternate monitoring modes that offered different patterns of restarting daemons. The "correct" mode (phased restart) is now the default. The -e and -z options used to disable some monitoring aspects, |DAEMON| now always has all monitoring features enabled.
-
-Removing these options should result in correct operation, if it does not please file a bug report.
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
+++ /dev/null
-*****
-ZEBRA
-*****
-
-.. include:: defines.rst
-.. |DAEMON| replace:: zebra
-
-SYNOPSIS
-========
-|DAEMON| |synopsis-options-hv|
-
-|DAEMON| |synopsis-options|
-
-DESCRIPTION
-===========
-|DAEMON| is a routing manager that implements the zebra route engine. zebra supports all protocol daemons in the FRRouting suite.
-
-OPTIONS
-=======
-OPTIONS available for the |DAEMON| command:
-
-.. include:: common-options.rst
-
-.. option:: -b, --batch
-
- Runs in batch mode, zebra parses its config and exits.
-
-.. option:: -k, --keep_kernel
-
- On startup, don't delete self inserted routes.
-
-.. option:: -s, --nl-bufsize <netlink-buffer-size>
-
- Set netlink receive buffer size. There are cases where zebra daemon can't handle flood of netlink messages from kernel. If you ever see "recvmsg overrun" messages in zebra log, you are in trouble.
-
- Solution is to increase receive buffer of netlink socket. Note that kernel < 2.6.14 doesn't allow increasing it over maximum value defined in /proc/sys/net/core/rmem_max. If you want to do it, you have to increase maximum before starting zebra.
-
- Note that this affects Linux only.
-
-
-.. option:: -n, --vrfwnetns
-
- Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite support from the Linux kernel. This option permits discovering Linux named network namespaces and mapping it to FRR VRF contexts.
-
-ROUTES
-------
-
-.. option:: -r, --retain
-
- When the program terminates, do not flush routes installed by zebra from the kernel.
-
-
-FILES
-=====
-
-|INSTALL_PREFIX_SBIN|/|DAEMON|
- The default location of the |DAEMON| binary.
-
-|INSTALL_PREFIX_ETC|/|DAEMON|.conf
- The default location of the |DAEMON| config file.
-
-$(PWD)/|DAEMON|.log
- If the |DAEMON| process is configured to output logs to a file, then you
- will find this file in the directory where you started |DAEMON|.
-
-.. include:: epilogue.rst
-
!
! Zebra configuration file
!
+ frr version 6.0
+ frr defaults traditional
+ !
hostname Router
password zebra
enable password zebra
character. So in the above example ``!`` will not be regarded as a comment and
the password is set to ``zebra!password``.
+
+Configuration versioning, profiles and upgrade behavior
+-------------------------------------------------------
+
+All |PACKAGE_NAME| daemons share a mechanism to specify a configuration profile
+and version for loading and saving configuration. Specific configuration
+settings take different default values depending on the selected profile and
+version.
+
+While the profile can be selected by user configuration and will remain over
+upgrades, |PACKAGE_NAME| will always write configurations using its current
+version. This means that, after upgrading, a ``write file`` may write out a
+slightly different configuration than what was read in.
+
+Since the previous configuration is loaded with its version's defaults, but
+the new configuration is written with the new defaults, any default that
+changed between versions will result in an appropriate configuration entry
+being written out. **FRRouting configuration is sticky, staying consistent
+over upgrades.** Changed defaults will only affect new configuration.
+
+Note that the loaded version persists into interactive configuration
+sessions. Commands executed in an interactive configuration session are
+no different from configuration loaded at startup. This means that when,
+say, you configure a new BGP peer, the defaults used for configuration
+are the ones selected by the last ``frr version`` command.
+
+.. warning::
+
+ Saving the configuration does not bump the daemons forward to use the new
+ version for their defaults, but restarting them will, since they will then
+ apply the new ``frr version`` command that was written out. Manually
+ execute the ``frr version`` command in ``show running-config`` to avoid
+ this intermediate state.
+
+This is visible in ``show running-config``:
+
+.. code-block:: frr
+
+ Current configuration:
+ !
+ ! loaded from 6.0
+ frr version 6.1-dev
+ frr defaults traditional
+ !
+
+If you save and then restart with this configuration, the old defaults will
+no longer apply. Similarly, you could execute ``frr version 6.1-dev``, causing
+the new defaults to apply and the ``loaded from 6.0`` comment to disappear.
+
+
+Profiles
+^^^^^^^^
+
+|PACKAGE_NAME| provides configuration profiles to adapt its default settings
+to various usage scenarios. Currently, the following profiles are
+implemented:
+
+* ``traditional`` - reflects defaults adhering mostly to IETF standards or
+ common practices in wide-area internet routing.
+* ``datacenter`` - reflects a single administrative domain with intradomain
+ links using aggressive timers.
+
+Your distribution/installation may pre-set a profile through the ``-F`` command
+line option on all daemons. All daemons must be configured for the same
+profile. The value specified on the command line is only a pre-set and any
+``frr defaults`` statement in the configuration will take precedence.
+
+.. note::
+
+ The profile must be the same across all daemons. Mismatches may result
+ in undefined behavior.
+
+You can freely switch between profiles without causing any interruption or
+configuration changes. All settings remain at their previous values, and
+``show running-configuration`` output will have new output listing the previous
+default values as explicit configuration. New configuration, e.g. adding a
+BGP peer, will use the new defaults. To apply the new defaults for existing
+configuration, the previously-invisible old defaults that are now shown must
+be removed from the configuration.
+
+
+Upgrade practices for interactive configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you configure |PACKAGE_NAME| interactively and use the configuration
+writing functionality to make changes persistent, the following
+recommendations apply in regards to upgrades:
+
+1. Skipping major versions should generally work but is still inadvisable.
+ To avoid unneeded issue, upgrade one major version at a time and write
+ out the configuration after each update.
+
+2. After installing a new |PACKAGE_NAME| version, check the configuration
+ for differences against your old configuration. If any defaults changed
+ that affect your setup, lines may appear or disappear. If a new line
+ appears, it was previously the default (or not supported) and is now
+ neccessary to retain previous behavior. If a line disappears, it
+ previously wasn't the default, but now is, so it is no longer necessary.
+
+3. Check the log files for deprecation warnings by using ``grep -i deprecat``.
+
+4. After completing each upgrade, save the configuration and either restart
+ |PACKAGE_NAME| or execute ``frr version <CURRENT>`` to ensure defaults of
+ the new version are fully applied.
+
+
+Upgrade practices for autogenerated configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When using |PACKAGE_NAME| with generated configurations (e.g. Ansible,
+Puppet, etc.), upgrade considerations differ somewhat:
+
+1. Always write out a ``frr version`` statement in the configurations you
+ generate. This ensures that defaults are applied consistently.
+
+2. Try to not run more distinct versions of |PACKAGE_NAME| than necessary.
+ Each version may need to be checked individually. If running a mix of
+ older and newer installations, use the oldest version for the
+ ``frr version`` statement.
+
+3. When rolling out upgrades, generate a configuration as usual with the old
+ version identifier and load it. Check for any differences or deprecation
+ warnings. If there are differences in the configuration, propagate these
+ back to the configuration generator to minimize relying on actual default
+ values.
+
+4. After the last installation of an old version is removed, change the
+ configuration generation to a newer ``frr version`` as appropriate. Perform
+ the same checks as when rolling out upgrades.
+
+
.. _terminal-mode-commands:
Terminal Mode Commands
Show status for a specific BFD peer.
+.. index:: show bfd [vrf NAME] peers brief [json]
+.. clicmd:: show bfd [vrf NAME] peers brief [json]
+
+ Show all configured BFD peers information and current status in brief.
.. _bfd-peer-config:
Uptime: 1 minute(s), 51 second(s)
Diagnostics: ok
Remote diagnostics: ok
+ Peer Type: dynamic
Local timers:
+ Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
Echo transmission interval: disabled
Remote timers:
+ Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
Echo transmission interval: 50ms
Uptime: 1 minute(s), 53 second(s)
Diagnostics: ok
Remote diagnostics: ok
+ Peer Type: configured
Local timers:
+ Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
Echo transmission interval: disabled
Remote timers:
+ Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
Echo transmission interval: 50ms
Uptime: 3 minute(s), 4 second(s)
Diagnostics: ok
Remote diagnostics: ok
+ Peer Type: dynamic
Local timers:
+ Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
Echo transmission interval: disabled
Remote timers:
+ Detect-multiplier: 3
Receive interval: 300ms
Transmission interval: 300ms
Echo transmission interval: 50ms
frr# show bfd peer 192.168.0.1 json
- {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50}
+ {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"detect-multiplier":3,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50,"remote-detect-multiplier":3,"peer-type":"dynamic"}
+
+
+You can inspect the current BFD peer status in brief with the following commands:
+
+::
+
+ frr# show bfd peers brief
+ Session count: 1
+ SessionId LocalAddress PeerAddress Status
+ ========= ============ =========== ======
+ 1 192.168.0.1 192.168.0.2 up
You can also inspect peer session counters with the following commands:
frr# show bfd peer 192.168.0.1 counters json
{"multihop":false,"peer":"192.168.0.1","control-packet-input":348,"control-packet-output":685,"echo-packet-input":6815,"echo-packet-output":6816,"session-up":1,"session-down":0,"zebra-notifications":4}
+
+You can also clear packet counters per session with the following commands, only the packet counters will be reset:
+
+::
+
+ frr# clear bfd peers counters
+
+ frr# show bfd peers counters
+ BFD Peers:
+ peer 192.168.2.1 interface r2-eth2
+ Control packet input: 0 packets
+ Control packet output: 0 packets
+ Echo packet input: 0 packets
+ Echo packet output: 0 packets
+ Session up events: 1
+ Session down events: 0
+ Zebra notifications: 2
+
+ peer 192.168.0.1
+ Control packet input: 0 packets
+ Control packet output: 0 packets
+ Echo packet input: 0 packets
+ Echo packet output: 0 packets
+ Session up events: 1
+ Session down events: 0
+ Zebra notifications: 4
+
+Logging / debugging
+===================
+
+There are no fine grained debug controls for bfdd. Just enable debug logs.
+
+::
+
+ config
+ log file /var/log/frr/frr.log debugging
+ log syslog debugging
This command requires incoming and outgoing filters to be applied for eBGP sessions. Without the incoming filter, no routes will be accepted. Without the outgoing filter, no routes will be announced.
Reject routes with AS_SET or AS_CONFED_SET types
--------------------------------
+------------------------------------------------
.. index:: [no] bgp reject-as-sets
.. clicmd:: [no] bgp reject-as-sets
.. 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
granular and offers much smarter matching criterion than number of received
prefixes, making it more suited to implementing policy.
+.. index:: [no] neighbor PEER maximum-prefix-out NUMBER
+.. clicmd:: [no] neighbor PEER maximum-prefix-out NUMBER
+
+ Sets a maximum number of prefixes we can send to a given peer.
+
.. index:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as]
.. clicmd:: [no] neighbor PEER local-as AS-NUMBER [no-prepend] [replace-as]
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
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.
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
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:
--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:
NHRP Events
===========
-FIXME
+.. index:: nhrp event socket SOCKET
+.. clicmd:: nhrp event socket SOCKET
+
+ Configure the Unix path for the event socket.
Configuration Example
=====================
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
==============
`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.
+-------------+ +------------------+ +-------------+
-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
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
- 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
.. 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:**
sub-mode where you can specify individual nexthops. To exit this mode type
exit or end as per normal conventions for leaving a sub-mode.
-.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME]
+.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] [label LABELS]
Create a v4 or v6 nexthop. All normal rules for creating nexthops that you
are used to are allowed here. The syntax was intentionally kept the same as
Not supported with NETNS VRF backend.
+.. clicmd:: show pbr map [NAME] [detail]
+
+ Display pbr maps either all or by ``NAME``. If ``detail`` is set, it will
+ give information about the rules unique ID used internally and some extra
+ debugging information about install state for the nexthop/nexthop group.
+
.. _pbr-policy:
PBR Policy
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
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
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)
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:
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.
+
+
RUN cd /dist \
&& abuild-keygen -a -n \
&& abuild checksum \
+ && git init \
&& abuild -r -P /pkgs/apk
# This stage installs frr from the apk
--- /dev/null
+# This stage builds an rpm from the source
+FROM centos:centos7 as centos-7-builder
+
+RUN yum install -y rpm-build autoconf automake libtool make \
+ readline-devel texinfo net-snmp-devel groff pkgconfig \
+ json-c-devel pam-devel bison flex pytest c-ares-devel \
+ python-devel systemd-devel python-sphinx libcap-devel \
+ https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \
+ https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-devel-0.16.111-0.x86_64.rpm \
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm
+
+COPY . /src
+ARG PKGVER
+
+RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \
+ && cd /src \
+ && ./bootstrap.sh \
+ && ./configure \
+ --enable-rpki \
+ --enable-numeric-version \
+ --with-pkg-extra-version="_git$PKGVER" \
+ && make dist \
+ && cd / \
+ && mkdir -p /rpmbuild/{SOURCES,SPECS} \
+ && cp /src/frr*.tar.gz /rpmbuild/SOURCES \
+ && cp /src/redhat/frr.spec /rpmbuild/SPECS \
+ && rpmbuild \
+ --define "_topdir /rpmbuild" \
+ -ba /rpmbuild/SPECS/frr.spec
+
+# This stage installs frr from the rpm
+FROM centos:centos7
+RUN mkdir -p /pkgs/rpm \
+ && yum install -y https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \
+ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm
+
+COPY --from=centos-7-builder /rpmbuild/RPMS/ /pkgs/rpm/
+
+RUN yum install -y /pkgs/rpm/*/*.rpm \
+ && rm -rf /pkgs
+COPY docker/centos-7/docker-start /usr/lib/frr/docker-start
+ENTRYPOINT [ "/usr/lib/frr/docker-start" ]
--- /dev/null
+#!/bin/sh
+
+set -e
+
+##
+# Package version needs to be decimal
+##
+GITREV="$(git rev-parse --short=10 HEAD)"
+PKGVER="$(printf '%u\n' 0x$GITREV)"
+
+mkdir -p docker/centos-7/pkgs
+docker build \
+ --file=docker/centos-7/Dockerfile \
+ --build-arg="PKGVER=$PKGVER" \
+ --tag="frr:centos-7-builder-$GITREV" \
+ --target=centos-7-builder \
+ .
+
+# Copy RPM package from container to host
+CONTAINER_ID="$(docker create "frr:centos-7-builder-$GITREV")"
+docker cp "${CONTAINER_ID}:/rpmbuild/RPMS/x86_64/" docker/centos-7/pkgs
+docker rm "${CONTAINER_ID}"
+
+docker build \
+ --cache-from="frr:centos-7-builder-$GITREV" \
+ --file=docker/centos-7/Dockerfile \
+ --build-arg="PKGVER=$PKGVER" \
+ --tag="frr:centos-7-$GITREV" \
+ .
+
+docker rmi "frr:centos-7-builder-$GITREV"
--- /dev/null
+#!/bin/sh
+
+set -e
+
+##
+# Change owner for docker volume mount
+##
+chown -R frr:frr /etc/frr
+/usr/lib/frr/frrinit.sh start
+
+# Sleep forever
+exec tail -f /dev/null
--- /dev/null
+# 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" ]
--- /dev/null
+#!/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"
--- /dev/null
+#!/bin/sh
+
+set -e
+
+chown -R frr:frr /etc/frr
+/usr/lib/frr/frrinit.sh start
+
+# Sleep forever
+exec tail -f /dev/null
+++ /dev/null
-# This stage builds an rpm from the source
-FROM centos:centos7 as centos-builder
-
-RUN yum install -y rpm-build autoconf automake libtool make \
- readline-devel texinfo net-snmp-devel groff pkgconfig \
- json-c-devel pam-devel bison flex pytest c-ares-devel \
- python-devel systemd-devel python-sphinx libcap-devel \
- https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \
- https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-devel-0.16.111-0.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-devel-0.7.0-1.el7.centos.x86_64.rpm
-
-COPY . /src
-ARG PKGVER
-
-RUN echo '%_smp_mflags %( echo "-j$(/usr/bin/getconf _NPROCESSORS_ONLN)"; )' >> /root/.rpmmacros \
- && cd /src \
- && ./bootstrap.sh \
- && ./configure \
- --enable-rpki \
- --enable-numeric-version \
- --with-pkg-extra-version="_git$PKGVER" \
- && make dist \
- && cd / \
- && mkdir -p /rpmbuild/{SOURCES,SPECS} \
- && cp /src/frr*.tar.gz /rpmbuild/SOURCES \
- && cp /src/redhat/frr.spec /rpmbuild/SPECS \
- && rpmbuild \
- --define "_topdir /rpmbuild" \
- -ba /rpmbuild/SPECS/frr.spec
-
-# This stage installs frr from the rpm
-FROM centos:centos7
-RUN mkdir -p /pkgs/rpm \
- && yum install -y https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/CentOS-7-x86_64-Packages/libyang-0.16.111-0.x86_64.rpm \
- https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-110/CentOS-7-x86_64-Packages/librtr-0.7.0-1.el7.centos.x86_64.rpm
-
-COPY --from=centos-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
-ENTRYPOINT [ "/usr/lib/frr/docker-start" ]
+++ /dev/null
-#!/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/pkgs
-docker build \
- --file=docker/centos/Dockerfile \
- --build-arg="PKGVER=$PKGVER" \
- --tag="frr:centos-builder-$GITREV" \
- --target=centos-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
-docker rm "${CONTAINER_ID}"
-
-docker build \
- --cache-from="frr:centos-builder-$GITREV" \
- --file=docker/centos/Dockerfile \
- --build-arg="PKGVER=$PKGVER" \
- --tag="frr:centos-$GITREV" \
- .
-
-docker rmi "frr:centos-builder-$GITREV"
+++ /dev/null
-#!/bin/sh
-
-set -e
-
-##
-# Change owner for docker volume mount
-##
-chown -R frr:frr /etc/frr
-/usr/lib/frr/frrinit.sh start
-
-# Sleep forever
-exec tail -f /dev/null
ei->params.auth_type = EIGRP_AUTH_TYPE_NONE;
ei->params.auth_keychain = NULL;
+ ei->curr_bandwidth = ifp->bandwidth;
+ ei->curr_mtu = ifp->mtu;
+
return ei;
}
static int eigrp_ifp_up(struct interface *ifp)
{
- /* Interface is already up. */
- if (if_is_operative(ifp)) {
- /* Temporarily keep ifp values. */
- struct interface if_tmp;
- memcpy(&if_tmp, ifp, sizeof(struct interface));
+ struct eigrp_interface *ei = ifp->info;
- if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
- zlog_debug("Zebra: Interface[%s] state update.",
- ifp->name);
+ if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
+ zlog_debug("Zebra: Interface[%s] state change to up.",
+ ifp->name);
- if (if_tmp.bandwidth != ifp->bandwidth) {
- if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
- zlog_debug(
- "Zebra: Interface[%s] bandwidth change %d -> %d.",
- ifp->name, if_tmp.bandwidth,
- ifp->bandwidth);
+ if (!ei)
+ return 0;
- // eigrp_if_recalculate_output_cost (ifp);
- }
+ if (ei->curr_bandwidth != ifp->bandwidth) {
+ if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
+ zlog_debug(
+ "Zebra: Interface[%s] bandwidth change %d -> %d.",
+ ifp->name, ei->curr_bandwidth,
+ ifp->bandwidth);
- if (if_tmp.mtu != ifp->mtu) {
- if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
- zlog_debug(
- "Zebra: Interface[%s] MTU change %u -> %u.",
- ifp->name, if_tmp.mtu, ifp->mtu);
+ ei->curr_bandwidth = ifp->bandwidth;
+ // eigrp_if_recalculate_output_cost (ifp);
+ }
- /* Must reset the interface (simulate down/up) when MTU
- * changes. */
- eigrp_if_reset(ifp);
- }
+ if (ei->curr_mtu != ifp->mtu) {
+ if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
+ zlog_debug(
+ "Zebra: Interface[%s] MTU change %u -> %u.",
+ ifp->name, ei->curr_mtu, ifp->mtu);
+
+ ei->curr_mtu = ifp->mtu;
+ /* Must reset the interface (simulate down/up) when MTU
+ * changes. */
+ eigrp_if_reset(ifp);
return 0;
}
- if (IS_DEBUG_EIGRP(zebra, ZEBRA_INTERFACE))
- zlog_debug("Zebra: Interface[%s] state change to up.",
- ifp->name);
-
- if (ifp->info)
- eigrp_if_up(ifp->info);
+ eigrp_if_up(ifp->info);
return 0;
}
/* To which multicast groups do we currently belong? */
+ uint32_t curr_bandwidth;
+ uint32_t curr_mtu;
uint8_t multicast_memberships;
$(top_srcdir)/eigrpd/eigrp_vty.c \
# end
# $(top_srcdir)/eigrpd/eigrp_routemap.c
-man8 += $(MANBUILD)/eigrpd.8
+man8 += $(MANBUILD)/frr-eigrpd.8
endif
eigrpd_libeigrp_a_SOURCES = \
#include "command.h"
#include "vty.h"
#include "memory.h"
-#include "memory_vty.h"
#include "stream.h"
#include "if.h"
#include "privs.h"
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;
}
circuit->rcv_stream,
circuit->area, level,
lsp_confusion);
+ if (lsp_confusion)
+ isis_free_tlvs(tlvs);
tlvs = NULL;
/* ii */
lsp_flood_or_update(lsp, NULL,
break;
api_nh = &api.nexthops[count];
if (fabricd)
- api_nh->onlink = true;
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
api_nh->vrf_id = VRF_DEFAULT;
switch (nexthop->family) {
/*
* 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");
$(top_srcdir)/isisd/isis_vty_fabricd.c \
$(top_srcdir)/isisd/isisd.c \
# end
-man8 += $(MANBUILD)/isisd.8
+man8 += $(MANBUILD)/frr-isisd.8
endif
if FABRICD
sbin_PROGRAMS += ldpd/ldpd
dist_examples_DATA += ldpd/ldpd.conf.sample
vtysh_scan += $(top_srcdir)/ldpd/ldp_vty_cmds.c
-man8 += $(MANBUILD)/ldpd.8
+man8 += $(MANBUILD)/frr-ldpd.8
endif
ldpd_libldp_a_SOURCES = \
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,
};
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)
{
return CMD_SUCCESS;
}
-/* "Set" version ... ignore version tags */
-DEFUN (frr_version_defaults,
- frr_version_defaults_cmd,
- "frr <version|defaults> LINE...",
- "FRRouting global parameters\n"
- "version configuration was written by\n"
- "set of configuration defaults used\n"
- "version string\n")
-{
- return CMD_SUCCESS;
-}
-
/* Help display function for all node. */
DEFUN (config_help,
config_help_cmd,
vty_out(vty, "!\n");
}
+ if (strcmp(frr_defaults_version(), FRR_VER_SHORT))
+ vty_out(vty, "! loaded from %s\n", frr_defaults_version());
vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
- vty_out(vty, "frr defaults %s\n", DFLT_NAME);
+ vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
vty_out(vty, "!\n");
for (i = 0; i < vector_active(cmdvec); i++)
install_element(CONFIG_NODE, &no_hostname_cmd);
install_element(CONFIG_NODE, &domainname_cmd);
install_element(CONFIG_NODE, &no_domainname_cmd);
- install_element(CONFIG_NODE, &frr_version_defaults_cmd);
if (terminal > 0) {
install_element(CONFIG_NODE, &debug_memstats_cmd);
. {return yytext[0];}
%%
-YY_BUFFER_STATE buffer;
+static YY_BUFFER_STATE buffer;
void set_lexer_string (yyscan_t *scn, const char *string)
{
--- /dev/null
+/*
+ * FRR switchable defaults.
+ * Copyright (c) 2017-2019 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <zebra.h>
+
+#include "defaults.h"
+#include "version.h"
+
+static char df_version[128] = FRR_VER_SHORT, df_profile[128] = DFLT_NAME;
+static struct frr_default *dflt_first = NULL, **dflt_next = &dflt_first;
+
+/* these are global for all FRR daemons. they have to be, since we write an
+ * integrated config with the same value for all daemons.
+ */
+const char *frr_defaults_profiles[] = {
+ "traditional",
+ "datacenter",
+ NULL,
+};
+
+static int version_value(int ch)
+{
+ /* non-ASCII shouldn't happen */
+ if (ch < 0 || ch >= 128)
+ return 2;
+
+ /* ~foo sorts older than nothing */
+ if (ch == '~')
+ return 0;
+ if (ch == '\0')
+ return 1;
+ if (isalpha(ch))
+ return 0x100 + tolower(ch);
+
+ /* punctuation and digits (and everything else) */
+ return 0x200 + ch;
+}
+
+int frr_version_cmp(const char *aa, const char *bb)
+{
+ const char *apos = aa, *bpos = bb;
+
+ /* || is correct, we won't scan past the end of a string since that
+ * doesn't compare equal to anything else */
+ while (apos[0] || bpos[0]) {
+ if (isdigit((unsigned char)apos[0]) &&
+ isdigit((unsigned char)bpos[0])) {
+ unsigned long av, bv;
+ char *aend = NULL, *bend = NULL;
+
+ av = strtoul(apos, &aend, 10);
+ bv = strtoul(bpos, &bend, 10);
+ if (av < bv)
+ return -1;
+ if (av > bv)
+ return 1;
+
+ apos = aend;
+ bpos = bend;
+ continue;
+ }
+
+ int a = version_value(*apos++);
+ int b = version_value(*bpos++);
+
+ if (a < b)
+ return -1;
+ if (a > b)
+ return 1;
+ }
+ return 0;
+}
+
+static void frr_default_apply_one(struct frr_default *dflt, bool check);
+
+void frr_default_add(struct frr_default *dflt)
+{
+ dflt->next = NULL;
+ *dflt_next = dflt;
+ dflt_next = &dflt->next;
+
+ frr_default_apply_one(dflt, true);
+}
+
+static bool frr_match_version(const char *name, const char *vspec,
+ const char *version, bool check)
+{
+ int cmp;
+ static struct spec {
+ const char *str;
+ bool dir, eq;
+ } *s, specs[] = {
+ {"<=", -1, 1},
+ {">=", 1, 1},
+ {"==", 0, 1},
+ {"<", -1, 0},
+ {">", 1, 0},
+ {"=", 0, 1},
+ {NULL, 0, 0},
+ };
+
+ if (!vspec)
+ /* NULL = all versions */
+ return true;
+
+ for (s = specs; s->str; s++)
+ if (!strncmp(s->str, vspec, strlen(s->str)))
+ break;
+ if (!s->str) {
+ if (check)
+ fprintf(stderr, "invalid version specifier for %s: %s",
+ name, vspec);
+ /* invalid version spec, never matches */
+ return false;
+ }
+
+ vspec += strlen(s->str);
+ while (isspace((unsigned char)*vspec))
+ vspec++;
+
+ cmp = frr_version_cmp(version, vspec);
+ if (cmp == s->dir || (s->eq && cmp == 0))
+ return true;
+
+ return false;
+}
+
+static void frr_default_apply_one(struct frr_default *dflt, bool check)
+{
+ struct frr_default_entry *entry = dflt->entries;
+ struct frr_default_entry *dfltentry = NULL, *saveentry = NULL;
+
+ for (; entry->match_version || entry->match_profile; entry++) {
+ if (entry->match_profile
+ && strcmp(entry->match_profile, df_profile))
+ continue;
+
+ if (!dfltentry && frr_match_version(dflt->name,
+ entry->match_version, df_version, check))
+ dfltentry = entry;
+ if (!saveentry && frr_match_version(dflt->name,
+ entry->match_version, FRR_VER_SHORT, check))
+ saveentry = entry;
+
+ if (dfltentry && saveentry && !check)
+ break;
+ }
+ /* found default or arrived at last entry that has NULL,NULL spec */
+
+ if (!dfltentry)
+ dfltentry = entry;
+ if (!saveentry)
+ saveentry = entry;
+
+ if (dflt->dflt_bool)
+ *dflt->dflt_bool = dfltentry->val_bool;
+ if (dflt->dflt_str)
+ *dflt->dflt_str = dfltentry->val_str;
+ if (dflt->dflt_long)
+ *dflt->dflt_long = dfltentry->val_long;
+ if (dflt->dflt_ulong)
+ *dflt->dflt_ulong = dfltentry->val_ulong;
+ if (dflt->dflt_float)
+ *dflt->dflt_float = dfltentry->val_float;
+ if (dflt->save_bool)
+ *dflt->save_bool = saveentry->val_bool;
+ if (dflt->save_str)
+ *dflt->save_str = saveentry->val_str;
+ if (dflt->save_long)
+ *dflt->save_long = saveentry->val_long;
+ if (dflt->save_ulong)
+ *dflt->save_ulong = saveentry->val_ulong;
+ if (dflt->save_float)
+ *dflt->save_float = saveentry->val_float;
+}
+
+void frr_defaults_apply(void)
+{
+ struct frr_default *dflt;
+
+ for (dflt = dflt_first; dflt; dflt = dflt->next)
+ frr_default_apply_one(dflt, false);
+}
+
+bool frr_defaults_profile_valid(const char *profile)
+{
+ const char **p;
+
+ for (p = frr_defaults_profiles; *p; p++)
+ if (!strcmp(profile, *p))
+ return true;
+ return false;
+}
+
+const char *frr_defaults_version(void)
+{
+ return df_version;
+}
+
+const char *frr_defaults_profile(void)
+{
+ return df_profile;
+}
+
+void frr_defaults_version_set(const char *version)
+{
+ strlcpy(df_version, version, sizeof(df_version));
+ frr_defaults_apply();
+}
+
+void frr_defaults_profile_set(const char *profile)
+{
+ strlcpy(df_profile, profile, sizeof(df_profile));
+ frr_defaults_apply();
+}
--- /dev/null
+/*
+ * FRR switchable defaults.
+ * Copyright (C) 2017-2019 David Lamparter for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRR_DEFAULTS_H
+#define _FRR_DEFAULTS_H
+
+#include <stdbool.h>
+
+#include "compiler.h"
+
+/* frr_default wraps information about a default that has different
+ * values depending on FRR version or default-set
+ *
+ * frr_default_entry describes one match rule and the resulting value;
+ * entries are evaluated in order and the first matching is used.
+ *
+ * If both match_version and match_profile are specified, they must both
+ * match. A NULL value matches everything.
+ */
+struct frr_default_entry {
+ /* syntax: "(<|<=|==|>=|>) [whitespace] version", e.g.
+ * ">= 6.1-dev" "<6.0"
+ */
+ const char *match_version;
+ /* exact profile string to compare against */
+ const char *match_profile;
+
+ /* value to use */
+ bool val_bool;
+ const char *val_str;
+ long val_long;
+ unsigned long val_ulong;
+ float val_float;
+};
+
+/* one struct frr_default exists for each malleable default value */
+struct frr_default {
+ struct frr_default *next;
+
+ /* for UI/debug use */
+ const char *name;
+
+ /* the following two sets of variables differ because the written
+ * config always targets the *current* FRR version
+ *
+ * e.g. if you load a config that has "frr version 5.0" on 6.0
+ * *dflt_long => set to the default value in 5.0
+ * *save_long => set to the default value in 6.0
+ * config save will write "frr version 6.0" with 6.0 defaults
+ */
+
+ /* variable holding the default value for reading/use */
+ bool *dflt_bool;
+ const char **dflt_str;
+ long *dflt_long;
+ unsigned long *dflt_ulong;
+ float *dflt_float;
+
+ /* variable to use when comparing for config save */
+ bool *save_bool;
+ const char **save_str;
+ long *save_long;
+ unsigned long *save_ulong;
+ float *save_float;
+
+ struct frr_default_entry entries[];
+};
+
+#define _FRR_CFG_DEFAULT(type, typname, varname, ...) \
+ static type DFLT_##varname; \
+ static type SAVE_##varname; \
+ static struct frr_default _dflt_##varname = { \
+ .name = #varname, \
+ .dflt_##typname = &DFLT_##varname, \
+ .save_##typname = &SAVE_##varname, \
+ .entries = { __VA_ARGS__ }, \
+ }; \
+ static void _dfltinit_##varname(void) \
+ __attribute__((_CONSTRUCTOR(1000))); \
+ static void _dfltinit_##varname(void) \
+ { \
+ frr_default_add(&_dflt_##varname); \
+ }
+
+/* use:
+ * FRR_CFG_DEFAULT_LONG(SHARP_BLUNTNESS,
+ * { .val_long = 2, .match_version = ">= 10.0" },
+ * { .val_long = 1, .match_profile = "datacenter" },
+ * { .val_long = 0 },
+ * )
+ *
+ * This will create DFLT_SHARP_BLUNTNESS and SAVE_SHARP_BLUNTNESS variables.
+ *
+ * Note: preprocessor defines cannot be used as variable names because they
+ * will be expanded and blow up with a compile error. Use an enum or add an
+ * extra _ at the beginning (e.g. _SHARP_BLUNTNESS => DFLT__SHARP_BLUNTNESS)
+ */
+#define FRR_CFG_DEFAULT_BOOL(varname, ...) \
+ _FRR_CFG_DEFAULT(bool, bool, varname, ## __VA_ARGS__)
+#define FRR_CFG_DEFAULT_LONG(varname, ...) \
+ _FRR_CFG_DEFAULT(long, long, varname, ## __VA_ARGS__)
+#define FRR_CFG_DEFAULT_ULONG(varname, ...) \
+ _FRR_CFG_DEFAULT(unsigned long, ulong, varname, ## __VA_ARGS__)
+#define FRR_CFG_DEFAULT_FLOAT(varname, ...) \
+ _FRR_CFG_DEFAULT(float, float, varname, ## __VA_ARGS__)
+#define FRR_CFG_DEFAULT_STR(varname, ...) \
+ _FRR_CFG_DEFAULT(const char *, str, varname, ## __VA_ARGS__)
+
+
+/* daemons don't need to call any of these, libfrr handles that */
+extern void frr_default_add(struct frr_default *dflt);
+extern void frr_defaults_version_set(const char *version);
+extern void frr_defaults_profile_set(const char *profile);
+extern const char *frr_defaults_version(void);
+extern const char *frr_defaults_profile(void);
+extern void frr_defaults_apply(void);
+
+extern const char *frr_defaults_profiles[];
+extern bool frr_defaults_profile_valid(const char *profile);
+
+/* like strcmp(), but with version ordering */
+extern int frr_version_cmp(const char *aa, const char *bb);
+
+#endif /* _FRR_DEFAULTS_H */
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)
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;
}
}
-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;
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);
* "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.
*
#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, \
my $gitdesc = `git describe --always --first-parent --tags --dirty --match 'frr-*' || echo -- \"0-gUNKNOWN\"`;
chomp $gitdesc;
-my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN";
+my $gitsuffix = ($gitdesc =~ /-g([0-9a-fA-F]+(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN";
printf STDERR "git suffix: %s\n", $gitsuffix;
printf "#define GIT_SUFFIX \"%s\"\n", $gitsuffix;
#endif
#include "command.h"
-#include "memory_vty.h"
#include "graph.h"
#include "linklist.h"
#include "command_match.h"
/** 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;
* @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];
}
/** 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();
#endif
#include "command.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
static void vty_do_exit(int isexit)
{
host.domainname = strdup("testdomainname");
vty_init(master, true);
- memory_init();
+ lib_cmd_init();
yang_init();
nb_init(master, NULL, 0);
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);
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)
"/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++;
}
}
memcpy(in, (char *)in6 + 12, sizeof(struct in_addr));
}
+static inline bool ipaddr_isset(struct ipaddr *ip)
+{
+ static struct ipaddr a = {};
+ return (0 == memcmp(&a, ip, sizeof(struct ipaddr)));
+}
+
#ifdef __cplusplus
}
#endif
--- /dev/null
+/*
+ * Assorted library VTY commands
+ *
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+/* malloc.h is generally obsolete, however GNU Libc mallinfo wants it. */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#ifdef HAVE_MALLOC_MALLOC_H
+#include <malloc/malloc.h>
+#endif
+#include <dlfcn.h>
+#ifdef HAVE_LINK_H
+#include <link.h>
+#endif
+
+#include "log.h"
+#include "memory.h"
+#include "module.h"
+#include "defaults.h"
+#include "lib_vty.h"
+
+/* Looking up memory status from vty interface. */
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+
+#ifdef HAVE_MALLINFO
+static int show_memory_mallinfo(struct vty *vty)
+{
+ struct mallinfo minfo = mallinfo();
+ char buf[MTYPE_MEMSTR_LEN];
+
+ vty_out(vty, "System allocator statistics:\n");
+ vty_out(vty, " Total heap allocated: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.arena));
+ vty_out(vty, " Holding block headers: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.hblkhd));
+ vty_out(vty, " Used small blocks: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.usmblks));
+ vty_out(vty, " Used ordinary blocks: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.uordblks));
+ vty_out(vty, " Free small blocks: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fsmblks));
+ vty_out(vty, " Free ordinary blocks: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fordblks));
+ vty_out(vty, " Ordinary blocks: %ld\n",
+ (unsigned long)minfo.ordblks);
+ vty_out(vty, " Small blocks: %ld\n",
+ (unsigned long)minfo.smblks);
+ vty_out(vty, " Holding blocks: %ld\n",
+ (unsigned long)minfo.hblks);
+ vty_out(vty, "(see system documentation for 'mallinfo' for meaning)\n");
+ return 1;
+}
+#endif /* HAVE_MALLINFO */
+
+static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt)
+{
+ struct vty *vty = arg;
+ if (!mt) {
+ vty_out(vty, "--- qmem %s ---\n", mg->name);
+ vty_out(vty, "%-30s: %8s %-8s%s %8s %9s\n",
+ "Type", "Current#", " Size",
+#ifdef HAVE_MALLOC_USABLE_SIZE
+ " Total",
+#else
+ "",
+#endif
+ "Max#",
+#ifdef HAVE_MALLOC_USABLE_SIZE
+ "MaxBytes"
+#else
+ ""
+#endif
+ );
+ } else {
+ if (mt->n_alloc != 0) {
+ char size[32];
+ snprintf(size, sizeof(size), "%6zu", mt->size);
+#ifdef HAVE_MALLOC_USABLE_SIZE
+#define TSTR " %9zu"
+#define TARG , mt->total
+#define TARG2 , mt->max_size
+#else
+#define TSTR ""
+#define TARG
+#define TARG2
+#endif
+ vty_out(vty, "%-30s: %8zu %-8s"TSTR" %8zu"TSTR"\n",
+ mt->name,
+ mt->n_alloc,
+ mt->size == 0 ? ""
+ : mt->size == SIZE_VAR
+ ? "variable"
+ : size
+ TARG,
+ mt->n_max
+ TARG2);
+ }
+ }
+ return 0;
+}
+
+
+DEFUN_NOSH (show_memory,
+ show_memory_cmd,
+ "show memory",
+ "Show running system information\n"
+ "Memory statistics\n")
+{
+#ifdef HAVE_MALLINFO
+ show_memory_mallinfo(vty);
+#endif /* HAVE_MALLINFO */
+
+ qmem_walk(qmem_walker, vty);
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_modules,
+ show_modules_cmd,
+ "show modules",
+ "Show running system information\n"
+ "Loaded modules\n")
+{
+ struct frrmod_runtime *plug = frrmod_list;
+
+ vty_out(vty, "%-12s %-25s %s\n\n", "Module Name", "Version",
+ "Description");
+ while (plug) {
+ const struct frrmod_info *i = plug->info;
+
+ vty_out(vty, "%-12s %-25s %s\n", i->name, i->version,
+ i->description);
+ if (plug->dl_handle) {
+#ifdef HAVE_DLINFO_ORIGIN
+ char origin[MAXPATHLEN] = "";
+ dlinfo(plug->dl_handle, RTLD_DI_ORIGIN, &origin);
+#ifdef HAVE_DLINFO_LINKMAP
+ const char *name;
+ struct link_map *lm = NULL;
+ dlinfo(plug->dl_handle, RTLD_DI_LINKMAP, &lm);
+ if (lm) {
+ name = strrchr(lm->l_name, '/');
+ name = name ? name + 1 : lm->l_name;
+ vty_out(vty, "\tfrom: %s/%s\n", origin, name);
+ }
+#else
+ vty_out(vty, "\tfrom: %s \n", origin, plug->load_name);
+#endif
+#else
+ vty_out(vty, "\tfrom: %s\n", plug->load_name);
+#endif
+ }
+ plug = plug->next;
+ }
+
+ vty_out(vty, "pid: %u\n", (uint32_t)(getpid()));
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (frr_defaults,
+ frr_defaults_cmd,
+ "frr defaults PROFILE...",
+ "FRRouting global parameters\n"
+ "set of configuration defaults used\n"
+ "profile string\n")
+{
+ char *profile = argv_concat(argv, argc, 2);
+ int rv = CMD_SUCCESS;
+
+ if (!frr_defaults_profile_valid(profile)) {
+ vty_out(vty, "%% WARNING: profile %s is not known in this version\n",
+ profile);
+ rv = CMD_WARNING;
+ }
+ frr_defaults_profile_set(profile);
+ XFREE(MTYPE_TMP, profile);
+ return rv;
+}
+
+DEFUN (frr_version,
+ frr_version_cmd,
+ "frr version VERSION...",
+ "FRRouting global parameters\n"
+ "version configuration was written by\n"
+ "version string\n")
+{
+ char *version = argv_concat(argv, argc, 2);
+
+ frr_defaults_version_set(version);
+ XFREE(MTYPE_TMP, version);
+ return CMD_SUCCESS;
+}
+
+static void defaults_autocomplete(vector comps, struct cmd_token *token)
+{
+ const char **p;
+
+ for (p = frr_defaults_profiles; *p; p++)
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, *p));
+}
+
+static const struct cmd_variable_handler default_var_handlers[] = {
+ {.tokenname = "PROFILE", .completions = defaults_autocomplete},
+ {.completions = NULL},
+};
+
+void lib_cmd_init(void)
+{
+ cmd_variable_handler_register(default_var_handlers);
+
+ install_element(CONFIG_NODE, &frr_defaults_cmd);
+ install_element(CONFIG_NODE, &frr_version_cmd);
+
+ install_element(VIEW_NODE, &show_memory_cmd);
+ install_element(VIEW_NODE, &show_modules_cmd);
+}
+
+/* Stats querying from users */
+/* Return a pointer to a human friendly string describing
+ * the byte count passed in. E.g:
+ * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc.
+ * Up to 4 significant figures will be given.
+ * The pointer returned may be NULL (indicating an error)
+ * or point to the given buffer, or point to static storage.
+ */
+const char *mtype_memstr(char *buf, size_t len, unsigned long bytes)
+{
+ unsigned int m, k;
+
+ /* easy cases */
+ if (!bytes)
+ return "0 bytes";
+ if (bytes == 1)
+ return "1 byte";
+
+ /*
+ * When we pass the 2gb barrier mallinfo() can no longer report
+ * correct data so it just does something odd...
+ * Reporting like Terrabytes of data. Which makes users...
+ * edgy.. yes edgy that's the term for it.
+ * So let's just give up gracefully
+ */
+ if (bytes > 0x7fffffff)
+ return "> 2GB";
+
+ m = bytes >> 20;
+ k = bytes >> 10;
+
+ if (m > 10) {
+ if (bytes & (1 << 19))
+ m++;
+ snprintf(buf, len, "%d MiB", m);
+ } else if (k > 10) {
+ if (bytes & (1 << 9))
+ k++;
+ snprintf(buf, len, "%d KiB", k);
+ } else
+ snprintf(buf, len, "%ld bytes", bytes);
+
+ return buf;
+}
--- /dev/null
+/* Memory management routine
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _ZEBRA_LIB_VTY_H
+#define _ZEBRA_LIB_VTY_H
+
+#include "memory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void lib_cmd_init(void);
+
+/* Human friendly string for given byte count */
+#define MTYPE_MEMSTR_LEN 20
+extern const char *mtype_memstr(char *, size_t, unsigned long);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZEBRA_LIB_VTY_H */
#include "vty.h"
#include "command.h"
#include "version.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
#include "log_vty.h"
#include "zclient.h"
#include "log_int.h"
#include "debug.h"
#include "frrcu.h"
#include "frr_pthread.h"
+#include "defaults.h"
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
DEFINE_KOOH(frr_early_fini, (), ())
{"version", no_argument, NULL, 'v'},
{"daemon", no_argument, NULL, 'd'},
{"module", no_argument, NULL, 'M'},
+ {"profile", required_argument, NULL, 'F'},
{"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
{"moduledir", required_argument, NULL, OPTION_MODULEDIR},
{"log", required_argument, NULL, OPTION_LOG},
{"command-log-always", no_argument, NULL, OPTION_LOGGING},
{NULL}};
static const struct optspec os_always = {
- "hvdM:",
+ "hvdM:F:",
" -h, --help Display this help and exit\n"
" -v, --version Print program version\n"
" -d, --daemon Runs in daemon mode\n"
" -M, --module Load specified module\n"
+ " -F, --profile Use specified configuration profile\n"
" --vty_socket Override vty socket path\n"
" --moduledir Override modules directory\n"
" --log Set Logging to stdout, syslog, or file:<name>\n"
" -g, --group Group to run as\n",
lo_user};
-
bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
const char *path)
{
*modnext = oc;
modnext = &oc->next;
break;
+ case 'F':
+ if (!frr_defaults_profile_valid(optarg)) {
+ const char **p;
+ FILE *ofd = stderr;
+
+ if (!strcmp(optarg, "help"))
+ ofd = stdout;
+ else
+ fprintf(stderr,
+ "The \"%s\" configuration profile is not valid for this FRR version.\n",
+ optarg);
+
+ fprintf(ofd, "Available profiles are:\n");
+ for (p = frr_defaults_profiles; *p; p++)
+ fprintf(ofd, "%s%s\n",
+ strcmp(*p, DFLT_NAME) ? " " : " * ",
+ *p);
+
+ if (ofd == stdout)
+ exit(0);
+ fprintf(ofd, "\n");
+ errors++;
+ break;
+ }
+ frr_defaults_profile_set(optarg);
+ break;
case 'i':
if (di->flags & FRR_NO_CFG_PID_DRY)
return 1;
dir = di->module_path ? di->module_path : frr_moduledir;
srandom(time(NULL));
+ frr_defaults_apply();
if (di->instance) {
snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]",
cmd_init(1);
vty_init(master, di->log_always);
- memory_init();
+ lib_cmd_init();
log_filter_cmd_init();
frr_pthread_init();
hook_call(frr_fini);
- /* memory_init -> nothing needed */
vty_terminate();
cmd_terminate();
nb_terminate();
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;
*/
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.
*
*/
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.
DESC_ENTRY(ZEBRA_VXLAN_SG_ADD),
DESC_ENTRY(ZEBRA_VXLAN_SG_DEL),
DESC_ENTRY(ZEBRA_VXLAN_SG_REPLAY),
+ DESC_ENTRY(ZEBRA_ERROR),
};
#undef DESC_ENTRY
}
#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)
+++ /dev/null
-/*
- * Memory and dynamic module VTY routine
- *
- * Copyright (C) 1998 Kunihiro Ishiguro
- * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <zebra.h>
-/* malloc.h is generally obsolete, however GNU Libc mallinfo wants it. */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-#ifdef HAVE_MALLOC_MALLOC_H
-#include <malloc/malloc.h>
-#endif
-#include <dlfcn.h>
-#ifdef HAVE_LINK_H
-#include <link.h>
-#endif
-
-#include "log.h"
-#include "memory.h"
-#include "module.h"
-#include "memory_vty.h"
-
-/* Looking up memory status from vty interface. */
-#include "vector.h"
-#include "vty.h"
-#include "command.h"
-
-#ifdef HAVE_MALLINFO
-static int show_memory_mallinfo(struct vty *vty)
-{
- struct mallinfo minfo = mallinfo();
- char buf[MTYPE_MEMSTR_LEN];
-
- vty_out(vty, "System allocator statistics:\n");
- vty_out(vty, " Total heap allocated: %s\n",
- mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.arena));
- vty_out(vty, " Holding block headers: %s\n",
- mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.hblkhd));
- vty_out(vty, " Used small blocks: %s\n",
- mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.usmblks));
- vty_out(vty, " Used ordinary blocks: %s\n",
- mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.uordblks));
- vty_out(vty, " Free small blocks: %s\n",
- mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fsmblks));
- vty_out(vty, " Free ordinary blocks: %s\n",
- mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fordblks));
- vty_out(vty, " Ordinary blocks: %ld\n",
- (unsigned long)minfo.ordblks);
- vty_out(vty, " Small blocks: %ld\n",
- (unsigned long)minfo.smblks);
- vty_out(vty, " Holding blocks: %ld\n",
- (unsigned long)minfo.hblks);
- vty_out(vty, "(see system documentation for 'mallinfo' for meaning)\n");
- return 1;
-}
-#endif /* HAVE_MALLINFO */
-
-static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt)
-{
- struct vty *vty = arg;
- if (!mt) {
- vty_out(vty, "--- qmem %s ---\n", mg->name);
- vty_out(vty, "%-30s: %8s %-8s%s %8s %9s\n",
- "Type", "Current#", " Size",
-#ifdef HAVE_MALLOC_USABLE_SIZE
- " Total",
-#else
- "",
-#endif
- "Max#",
-#ifdef HAVE_MALLOC_USABLE_SIZE
- "MaxBytes"
-#else
- ""
-#endif
- );
- } else {
- if (mt->n_alloc != 0) {
- char size[32];
- snprintf(size, sizeof(size), "%6zu", mt->size);
-#ifdef HAVE_MALLOC_USABLE_SIZE
-#define TSTR " %9zu"
-#define TARG , mt->total
-#define TARG2 , mt->max_size
-#else
-#define TSTR ""
-#define TARG
-#define TARG2
-#endif
- vty_out(vty, "%-30s: %8zu %-8s"TSTR" %8zu"TSTR"\n",
- mt->name,
- mt->n_alloc,
- mt->size == 0 ? ""
- : mt->size == SIZE_VAR
- ? "variable"
- : size
- TARG,
- mt->n_max
- TARG2);
- }
- }
- return 0;
-}
-
-
-DEFUN (show_memory,
- show_memory_cmd,
- "show memory",
- "Show running system information\n"
- "Memory statistics\n")
-{
-#ifdef HAVE_MALLINFO
- show_memory_mallinfo(vty);
-#endif /* HAVE_MALLINFO */
-
- qmem_walk(qmem_walker, vty);
- return CMD_SUCCESS;
-}
-
-DEFUN (show_modules,
- show_modules_cmd,
- "show modules",
- "Show running system information\n"
- "Loaded modules\n")
-{
- struct frrmod_runtime *plug = frrmod_list;
-
- vty_out(vty, "%-12s %-25s %s\n\n", "Module Name", "Version",
- "Description");
- while (plug) {
- const struct frrmod_info *i = plug->info;
-
- vty_out(vty, "%-12s %-25s %s\n", i->name, i->version,
- i->description);
- if (plug->dl_handle) {
-#ifdef HAVE_DLINFO_ORIGIN
- char origin[MAXPATHLEN] = "";
- dlinfo(plug->dl_handle, RTLD_DI_ORIGIN, &origin);
-#ifdef HAVE_DLINFO_LINKMAP
- const char *name;
- struct link_map *lm = NULL;
- dlinfo(plug->dl_handle, RTLD_DI_LINKMAP, &lm);
- if (lm) {
- name = strrchr(lm->l_name, '/');
- name = name ? name + 1 : lm->l_name;
- vty_out(vty, "\tfrom: %s/%s\n", origin, name);
- }
-#else
- vty_out(vty, "\tfrom: %s \n", origin, plug->load_name);
-#endif
-#else
- vty_out(vty, "\tfrom: %s\n", plug->load_name);
-#endif
- }
- plug = plug->next;
- }
-
- vty_out(vty, "pid: %u\n", (uint32_t)(getpid()));
-
- return CMD_SUCCESS;
-}
-
-void memory_init(void)
-{
- install_element(VIEW_NODE, &show_memory_cmd);
- install_element(VIEW_NODE, &show_modules_cmd);
-}
-
-/* Stats querying from users */
-/* Return a pointer to a human friendly string describing
- * the byte count passed in. E.g:
- * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc.
- * Up to 4 significant figures will be given.
- * The pointer returned may be NULL (indicating an error)
- * or point to the given buffer, or point to static storage.
- */
-const char *mtype_memstr(char *buf, size_t len, unsigned long bytes)
-{
- unsigned int m, k;
-
- /* easy cases */
- if (!bytes)
- return "0 bytes";
- if (bytes == 1)
- return "1 byte";
-
- /*
- * When we pass the 2gb barrier mallinfo() can no longer report
- * correct data so it just does something odd...
- * Reporting like Terrabytes of data. Which makes users...
- * edgy.. yes edgy that's the term for it.
- * So let's just give up gracefully
- */
- if (bytes > 0x7fffffff)
- return "> 2GB";
-
- m = bytes >> 20;
- k = bytes >> 10;
-
- if (m > 10) {
- if (bytes & (1 << 19))
- m++;
- snprintf(buf, len, "%d MiB", m);
- } else if (k > 10) {
- if (bytes & (1 << 9))
- k++;
- snprintf(buf, len, "%d KiB", k);
- } else
- snprintf(buf, len, "%ld bytes", bytes);
-
- return buf;
-}
+++ /dev/null
-/* Memory management routine
- * Copyright (C) 1998 Kunihiro Ishiguro
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef _ZEBRA_MEMORY_VTY_H
-#define _ZEBRA_MEMORY_VTY_H
-
-#include "memory.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern void memory_init(void);
-
-/* Human friendly string for given byte count */
-#define MTYPE_MEMSTR_LEN 20
-extern const char *mtype_memstr(char *, size_t, unsigned long);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ZEBRA_MEMORY_VTY_H */
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;
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;
}
/* 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);
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)
{
#include "mpls.h"
#include "jhash.h"
#include "printfrr.h"
+#include "vrf.h"
+#include "nexthop_group.h"
DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop")
DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label")
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:
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. */
return true;
}
+/*
+ * Allocate a new nexthop object and initialize it from various args.
+ */
+struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
+{
+ struct nexthop *nexthop;
+
+ nexthop = nexthop_new();
+ nexthop->type = NEXTHOP_TYPE_IFINDEX;
+ nexthop->ifindex = ifindex;
+ nexthop->vrf_id = vrf_id;
+
+ return nexthop;
+}
+
+struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4,
+ const struct in_addr *src,
+ vrf_id_t vrf_id)
+{
+ struct nexthop *nexthop;
+
+ nexthop = nexthop_new();
+ nexthop->type = NEXTHOP_TYPE_IPV4;
+ nexthop->vrf_id = vrf_id;
+ nexthop->gate.ipv4 = *ipv4;
+ if (src)
+ nexthop->src.ipv4 = *src;
+
+ return nexthop;
+}
+
+struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4,
+ const struct in_addr *src,
+ ifindex_t ifindex, vrf_id_t vrf_id)
+{
+ struct nexthop *nexthop;
+
+ nexthop = nexthop_new();
+ nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ nexthop->vrf_id = vrf_id;
+ nexthop->gate.ipv4 = *ipv4;
+ if (src)
+ nexthop->src.ipv4 = *src;
+ nexthop->ifindex = ifindex;
+
+ return nexthop;
+}
+
+struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6,
+ vrf_id_t vrf_id)
+{
+ struct nexthop *nexthop;
+
+ nexthop = nexthop_new();
+ nexthop->vrf_id = vrf_id;
+ nexthop->type = NEXTHOP_TYPE_IPV6;
+ nexthop->gate.ipv6 = *ipv6;
+
+ return nexthop;
+}
+
+struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6,
+ ifindex_t ifindex, vrf_id_t vrf_id)
+{
+ struct nexthop *nexthop;
+
+ nexthop = nexthop_new();
+ nexthop->vrf_id = vrf_id;
+ nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ nexthop->gate.ipv6 = *ipv6;
+ nexthop->ifindex = ifindex;
+
+ return nexthop;
+}
+
+struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type)
+{
+ struct nexthop *nexthop;
+
+ nexthop = nexthop_new();
+ nexthop->vrf_id = VRF_DEFAULT;
+ nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
+ nexthop->bh_type = bh_type;
+
+ return nexthop;
+}
+
/* Update nexthop with label information. */
void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type,
uint8_t num_labels, mpls_label_t *label)
struct mpls_label_stack *nh_label;
int i;
+ if (num_labels == 0)
+ return;
+
nexthop->nh_label_type = type;
nh_label = XCALLOC(MTYPE_NH_LABEL,
sizeof(struct mpls_label_stack)
return key;
}
-void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
- struct nexthop *rparent)
+void nexthop_copy_no_recurse(struct nexthop *copy,
+ const struct nexthop *nexthop,
+ struct nexthop *rparent)
{
copy->vrf_id = nexthop->vrf_id;
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));
&nexthop->nh_label->label[0]);
}
+void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
+ struct nexthop *rparent)
+{
+ nexthop_copy_no_recurse(copy, nexthop, rparent);
+
+ /* Bit of a special case here, we need to handle the case
+ * of a nexthop resolving to agroup. Hence, we need to
+ * use a nexthop_group API.
+ */
+ if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE))
+ copy_nexthops(©->resolved, nexthop->resolved, copy);
+}
+
+struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
+ struct nexthop *rparent)
+{
+ struct nexthop *new = nexthop_new();
+
+ nexthop_copy_no_recurse(new, nexthop, rparent);
+ return new;
+}
+
struct nexthop *nexthop_dup(const struct nexthop *nexthop,
struct nexthop *rparent)
{
/* 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);
mpls_label_t *);
void nexthop_del_labels(struct nexthop *);
+/*
+ * Allocate a new nexthop object and initialize it from various args.
+ */
+struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id);
+struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4,
+ const struct in_addr *src,
+ vrf_id_t vrf_id);
+struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4,
+ const struct in_addr *src,
+ ifindex_t ifindex, vrf_id_t vrf_id);
+struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6,
+ vrf_id_t vrf_id);
+struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6,
+ ifindex_t ifindex, vrf_id_t vrf_id);
+struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type);
+
/*
* Hash a nexthop. Suitable for use with hash tables.
*
/* Copies to an already allocated nexthop struct */
extern void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
struct nexthop *rparent);
+/* Copies to an already allocated nexthop struct, not including recurse info */
+extern void nexthop_copy_no_recurse(struct nexthop *copy,
+ const struct nexthop *nexthop,
+ struct nexthop *rparent);
/* Duplicates a nexthop and returns the newly allocated nexthop */
extern struct nexthop *nexthop_dup(const struct nexthop *nexthop,
struct nexthop *rparent);
+/* Duplicates a nexthop and returns the newly allocated nexthop */
+extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
+ struct nexthop *rparent);
#ifdef __cplusplus
}
DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group")
+/*
+ * Internal struct used to hold nhg config strings
+ */
+struct nexthop_hold {
+ char *nhvrf_name;
+ union sockunion *addr;
+ char *intf;
+ char *labels;
+ uint32_t weight;
+};
+
struct nexthop_group_hooks {
void (*new)(const char *name);
void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
nexthop->prev = last;
}
-void _nexthop_group_add_sorted(struct nexthop_group *nhg,
- struct nexthop *nexthop)
+/* Add nexthop to sorted list of nexthops */
+static void _nexthop_add_sorted(struct nexthop **head,
+ struct nexthop *nexthop)
{
- struct nexthop *position, *prev, *tail;
-
- /* Try to just append to the end first
- * This trust it is already sorted
- */
-
- tail = nexthop_group_tail(nhg);
-
- if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
- tail->next = nexthop;
- nexthop->prev = tail;
+ struct nexthop *position, *prev;
- return;
- }
+ assert(!nexthop->next);
- 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;
if (nexthop->prev)
nexthop->prev->next = nexthop;
else
- nhg->nexthop = nexthop;
+ *head = nexthop;
position->prev = nexthop;
return;
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;
+
+ assert(!nexthop->next);
+
+ /* 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. */
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)
{
for (nh1 = nh; nh1; nh1 = nh1->next) {
nexthop = nexthop_dup(nh1, rparent);
_nexthop_add(tnh, nexthop);
-
- if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE))
- copy_nexthops(&nexthop->resolved, nh1->resolved,
- nexthop);
}
}
if (ret)
return ret;
- return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
+ ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
+ if (ret)
+ return ret;
+
+ return nhgc_cmp_helper(nh1->labels, nh2->labels);
}
static void nhgl_delete(struct nexthop_hold *nh)
if (nh->addr)
sockunion_free(nh->addr);
+ XFREE(MTYPE_TMP, nh->labels);
+
XFREE(MTYPE_TMP, nh);
}
static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
const char *nhvrf_name,
const union sockunion *addr,
- const char *intf)
+ const char *intf, const char *labels,
+ const uint32_t weight)
{
struct nexthop_hold *nh;
nh->intf = XSTRDUP(MTYPE_TMP, intf);
if (addr)
nh->addr = sockunion_dup(addr);
+ if (labels)
+ nh->labels = XSTRDUP(MTYPE_TMP, labels);
+
+ nh->weight = weight;
listnode_add_sort(nhgc->nhg_list, nh);
}
static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
const char *nhvrf_name,
const union sockunion *addr,
- const char *intf)
+ const char *intf, const char *labels,
+ const uint32_t weight)
{
struct nexthop_hold *nh;
struct listnode *node;
for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
- if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 &&
- nhgc_addr_cmp_helper(addr, nh->addr) == 0 &&
- nhgc_cmp_helper(intf, nh->intf) == 0)
+ if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
+ && nhgc_addr_cmp_helper(addr, nh->addr) == 0
+ && nhgc_cmp_helper(intf, nh->intf) == 0
+ && nhgc_cmp_helper(labels, nh->labels) == 0
+ && weight == nh->weight)
break;
}
nhgl_delete(nh);
}
+/*
+ * Parse the config strings we support for a single nexthop. This gets used
+ * in a couple of different ways, and we distinguish between transient
+ * failures - such as a still-unprocessed interface - and fatal errors
+ * from label-string parsing.
+ */
static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
const union sockunion *addr,
- const char *intf, const char *name)
+ const char *intf, const char *name,
+ const char *labels, int *lbl_ret,
+ uint32_t weight)
{
+ int ret = 0;
struct vrf *vrf;
memset(nhop, 0, sizeof(*nhop));
} else
nhop->type = NEXTHOP_TYPE_IFINDEX;
+ if (labels) {
+ uint8_t num = 0;
+ mpls_label_t larray[MPLS_MAX_LABELS];
+
+ ret = mpls_str2label(labels, &num, larray);
+
+ /* Return label parse result */
+ if (lbl_ret)
+ *lbl_ret = ret;
+
+ if (ret < 0)
+ return false;
+ else if (num > 0)
+ nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
+ num, larray);
+ }
+
+ nhop->weight = weight;
+
return true;
}
+/*
+ * Wrapper to parse the strings in a 'nexthop_hold'
+ */
+static bool nexthop_group_parse_nhh(struct nexthop *nhop,
+ const struct nexthop_hold *nhh)
+{
+ return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf,
+ nhh->nhvrf_name, nhh->labels, NULL,
+ nhh->weight));
+}
+
DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
"[no] nexthop\
<\
<A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
|INTERFACE$intf\
>\
- [nexthop-vrf NAME$vrf_name]",
+ [{ \
+ nexthop-vrf NAME$vrf_name \
+ |label WORD \
+ |weight (1-255) \
+ }]",
NO_STR
"Specify one of the nexthops in this ECMP group\n"
"v4 Address\n"
"Interface to use\n"
"Interface to use\n"
"If the nexthop is in a different vrf tell us\n"
- "The nexthop-vrf Name\n")
+ "The nexthop-vrf Name\n"
+ "Specify label(s) for this nexthop\n"
+ "One or more labels in the range (16-1048575) separated by '/'\n"
+ "Weight to be used by the nexthop for purposes of ECMP\n"
+ "Weight value to be used\n")
{
VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
struct nexthop nhop;
struct nexthop *nh;
+ int lbl_ret = 0;
bool legal;
- legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name);
+ legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label,
+ &lbl_ret, weight);
if (nhop.type == NEXTHOP_TYPE_IPV6
&& IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
return CMD_WARNING_CONFIG_FAILED;
}
+ /* Handle label-string errors */
+ if (!legal && lbl_ret < 0) {
+ switch (lbl_ret) {
+ case -1:
+ vty_out(vty, "%% Malformed label(s)\n");
+ break;
+ case -2:
+ vty_out(vty,
+ "%% Cannot use reserved label(s) (%d-%d)\n",
+ MPLS_LABEL_RESERVED_MIN,
+ MPLS_LABEL_RESERVED_MAX);
+ break;
+ case -3:
+ vty_out(vty,
+ "%% Too many labels. Enter %d or fewer\n",
+ MPLS_MAX_LABELS);
+ break;
+ }
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
nh = nexthop_exists(&nhgc->nhg, &nhop);
if (no) {
- nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
+ nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label,
+ weight);
if (nh) {
_nexthop_del(&nhgc->nhg, nh);
_nexthop_add(&nhgc->nhg.nexthop, nh);
}
- nexthop_group_save_nhop(nhgc, vrf_name, addr, intf);
+ nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label,
+ weight);
if (legal && nhg_hooks.add_nexthop)
nhg_hooks.add_nexthop(nhgc, nh);
return CMD_SUCCESS;
}
-struct cmd_node nexthop_group_node = {
+static struct cmd_node nexthop_group_node = {
NH_GROUP_NODE,
"%s(config-nh-group)# ",
1
vrf = vrf_lookup_by_id(nh->vrf_id);
vty_out(vty, " nexthop-vrf %s", vrf->name);
}
+
+ if (nh->nh_label && nh->nh_label->num_labels > 0) {
+ char buf[200];
+
+ mpls_label2str(nh->nh_label->num_labels,
+ nh->nh_label->label,
+ buf, sizeof(buf), 0);
+ vty_out(vty, " label %s", buf);
+ }
+
+ if (nh->weight)
+ vty_out(vty, " weight %u", nh->weight);
+
vty_out(vty, "\n");
}
if (nh->nhvrf_name)
vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
+ if (nh->labels)
+ vty_out(vty, " label %s", nh->labels);
+
+ if (nh->weight)
+ vty_out(vty, " weight %u", nh->weight);
+
vty_out(vty, "\n");
}
struct nexthop nhop;
struct nexthop *nh;
- if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
- nhh->intf,
- nhh->nhvrf_name))
+ if (!nexthop_group_parse_nhh(&nhop, nhh))
continue;
nh = nexthop_exists(&nhgc->nhg, &nhop);
struct nexthop nhop;
struct nexthop *nh;
- if (!nexthop_group_parse_nexthop(&nhop, nhh->addr,
- nhh->intf,
- nhh->nhvrf_name))
+ if (!nexthop_group_parse_nhh(&nhop, nhh))
continue;
nh = nexthop_exists(&nhgc->nhg, &nhop);
for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
struct nexthop nhop;
- if (!nexthop_group_parse_nexthop(
- &nhop, nhh->addr, nhh->intf,
- nhh->nhvrf_name))
+ if (!nexthop_group_parse_nhh(&nhop, nhh))
continue;
switch (nhop.type) {
void nexthop_group_copy(struct nexthop_group *to,
struct nexthop_group *from);
+
+/*
+ * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
+ */
+void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg,
+ const struct nexthop *nh);
+
void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
struct nexthop *rparent);
uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg);
uint32_t nexthop_group_hash(const struct nexthop_group *nhg);
void nexthop_group_mark_duplicates(struct nexthop_group *nhg);
+void nexthop_group_add_sorted(struct nexthop_group *nhg,
+ struct nexthop *nexthop);
/* The following for loop allows to iterate over the nexthop
* structure of routes.
(nhop) = nexthop_next(nhop)
-struct nexthop_hold {
- char *nhvrf_name;
- union sockunion *addr;
- char *intf;
-};
-
struct nexthop_group_cmd {
RB_ENTRY(nexthop_group_cmd) nhgc_entry;
void _nexthop_add(struct nexthop **target, struct nexthop *nexthop);
void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nexthop);
-void _nexthop_group_add_sorted(struct nexthop_group *nhg,
- struct nexthop *nexthop);
#ifdef __cplusplus
}
#include "libfrr.h"
#include "version.h"
+#include "defaults.h"
#include "log.h"
#include "lib_errors.h"
#include "command.h"
vty_out(vty, "Configuration:\n");
vty_out(vty, "!\n");
vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
- vty_out(vty, "frr defaults %s\n", DFLT_NAME);
+ vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
LY_TREE_FOR (config->dnode, root)
nb_cli_show_dnode_cmds(vty, root, with_defaults);
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...
if (!plist)
return CMD_WARNING_CONFIG_FAILED;
+ apply_mask(&orfp->p);
+
if (set) {
pentry = prefix_list_entry_make(
&orfp->p, (permit ? PREFIX_PERMIT : PREFIX_DENY),
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
#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;
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))
* 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)
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)
}
}
-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;
/* 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);
#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 *);
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
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)
{
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;
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;
}
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,
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);
}
#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 */
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)(
/* 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)
{
};
/* 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);
struct skiplist *l;
register int i, k;
void *keys[sampleSize];
- void *v;
+ void *v = NULL;
zlog_debug("%s: entry", __func__);
return ret;
}
-/* 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)
{
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);
}
/* Put long word to the stream. */
-int stream_put_in_addr(struct stream *s, struct in_addr *addr)
+int stream_put_in_addr(struct stream *s, const struct in_addr *addr)
{
STREAM_VERIFY_SANE(s);
}
/* Put in_addr at location in the stream. */
-int stream_put_in_addr_at(struct stream *s, size_t putp, struct in_addr *addr)
+int stream_put_in_addr_at(struct stream *s, size_t putp,
+ const struct in_addr *addr)
{
STREAM_VERIFY_SANE(s);
}
/* Put in6_addr at location in the stream. */
-int stream_put_in6_addr_at(struct stream *s, size_t putp, struct in6_addr *addr)
+int stream_put_in6_addr_at(struct stream *s, size_t putp,
+ const struct in6_addr *addr)
{
STREAM_VERIFY_SANE(s);
}
/* Put prefix by nlri type format. */
-int stream_put_prefix_addpath(struct stream *s, struct prefix *p,
+int stream_put_prefix_addpath(struct stream *s, const struct prefix *p,
int addpath_encode, uint32_t addpath_tx_id)
{
size_t psize;
return psize;
}
-int stream_put_prefix(struct stream *s, struct prefix *p)
+int stream_put_prefix(struct stream *s, const struct prefix *p)
{
return stream_put_prefix_addpath(s, p, 0, 0);
}
extern int stream_putq(struct stream *, uint64_t);
extern int stream_putq_at(struct stream *, size_t, uint64_t);
extern int stream_put_ipv4(struct stream *, uint32_t);
-extern int stream_put_in_addr(struct stream *, struct in_addr *);
-extern int stream_put_in_addr_at(struct stream *, size_t, struct in_addr *);
-extern int stream_put_in6_addr_at(struct stream *, size_t, struct in6_addr *);
-extern int stream_put_prefix_addpath(struct stream *, struct prefix *,
+extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr);
+extern int stream_put_in_addr_at(struct stream *s, size_t putp,
+ const struct in_addr *addr);
+extern int stream_put_in6_addr_at(struct stream *s, size_t putp,
+ const struct in6_addr *addr);
+extern int stream_put_prefix_addpath(struct stream *s,
+ const struct prefix *p,
int addpath_encode,
uint32_t addpath_tx_id);
-extern int stream_put_prefix(struct stream *, struct prefix *);
+extern int stream_put_prefix(struct stream *s, const struct prefix *p);
extern int stream_put_labeled_prefix(struct stream *, struct prefix *,
mpls_label_t *, int addpath_encode,
uint32_t addpath_tx_id);
lib/command_parse.y \
lib/csv.c \
lib/debug.c \
+ lib/defaults.c \
lib/distribute.c \
lib/ferr.c \
lib/filter.c \
lib/json.c \
lib/keychain.c \
lib/lib_errors.c \
+ lib/lib_vty.c \
lib/libfrr.c \
lib/linklist.c \
lib/log.c \
lib/log_vty.c \
lib/md5.c \
lib/memory.c \
- lib/memory_vty.c \
lib/mlag.c \
lib/module.c \
lib/mpls.c \
$(top_srcdir)/lib/if.c \
$(top_srcdir)/lib/if_rmap.c \
$(top_srcdir)/lib/keychain.c \
+ $(top_srcdir)/lib/lib_vty.c \
$(top_srcdir)/lib/nexthop_group.c \
$(top_srcdir)/lib/plist.c \
$(top_srcdir)/lib/routemap.c \
lib/csv.h \
lib/db.h \
lib/debug.h \
+ lib/defaults.h \
lib/distribute.h \
lib/ferr.h \
lib/filter.h \
lib/json.h \
lib/keychain.h \
lib/lib_errors.h \
+ lib/lib_vty.h \
lib/libfrr.h \
lib/libospf.h \
lib/linklist.h \
lib/log_vty.h \
lib/md5.h \
lib/memory.h \
- lib/memory_vty.h \
lib/module.h \
lib/monotime.h \
lib/mpls.h \
* 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);
/*
* 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)
{
* To turn on systemd compilation, use --enable-systemd on
* configure run.
*/
-void systemd_send_information(const char *info);
void systemd_send_stopping(void);
/*
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 *);
}
}
-/* Create a socket for the VRF. */
int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
const char *interfacename)
{
}
-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,
* 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 */
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;
"frr-ripd",
"frr-ripngd",
"frr-isisd",
+ "frr-vrrpd",
};
/* Generate the yang_modules tree. */
}
}
+void redist_del_all_instances(struct redist_proto *red)
+{
+ struct listnode *ln, *nn;
+ unsigned short *id;
+
+ if (!red->instances)
+ return;
+
+ for (ALL_LIST_ELEMENTS(red->instances, ln, nn, id))
+ redist_del_instance(red, *id);
+}
+
/* Stop zebra client services. */
void zclient_stop(struct zclient *zclient)
{
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:
&zapi_nexthop_cmp);
}
+/*
+ * Encode a single zapi nexthop
+ */
+int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
+ uint32_t api_flags)
+{
+ int ret = 0;
+ int nh_flags = api_nh->flags;
+
+ stream_putl(s, api_nh->vrf_id);
+ stream_putc(s, api_nh->type);
+
+ /* If needed, set 'labelled nexthop' flag */
+ if (api_nh->label_num > 0) {
+ SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_LABEL);
+
+ /* Validate label count */
+ if (api_nh->label_num > MPLS_MAX_LABELS) {
+ ret = -1;
+ goto done;
+ }
+ }
+
+ if (api_nh->weight)
+ SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_WEIGHT);
+
+ /* Note that we're only encoding a single octet */
+ stream_putc(s, nh_flags);
+
+ switch (api_nh->type) {
+ case NEXTHOP_TYPE_BLACKHOLE:
+ stream_putc(s, api_nh->bh_type);
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ stream_put_in_addr(s, &api_nh->gate.ipv4);
+ stream_putl(s, api_nh->ifindex);
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ stream_putl(s, api_nh->ifindex);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ stream_write(s, (uint8_t *)&api_nh->gate.ipv6,
+ 16);
+ stream_putl(s, api_nh->ifindex);
+ break;
+ }
+
+ /* We only encode labels if we have >0 - we use
+ * the per-nexthop flag above to signal that the count
+ * is present in the payload.
+ */
+ if (api_nh->label_num > 0) {
+ stream_putc(s, api_nh->label_num);
+ stream_put(s, &api_nh->labels[0],
+ api_nh->label_num * sizeof(mpls_label_t));
+ }
+
+ if (api_nh->weight)
+ stream_putl(s, api_nh->weight);
+
+ /* Router MAC for EVPN routes. */
+ if (CHECK_FLAG(api_flags, ZEBRA_FLAG_EVPN_ROUTE))
+ stream_put(s, &(api_nh->rmac),
+ sizeof(struct ethaddr));
+
+done:
+ return ret;
+}
+
int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
{
struct zapi_nexthop *api_nh;
for (i = 0; i < api->nexthop_num; i++) {
api_nh = &api->nexthops[i];
- stream_putl(s, api_nh->vrf_id);
- stream_putc(s, api_nh->type);
- stream_putc(s, api_nh->onlink);
- switch (api_nh->type) {
- case NEXTHOP_TYPE_BLACKHOLE:
- stream_putc(s, api_nh->bh_type);
- break;
- case NEXTHOP_TYPE_IPV4:
- stream_put_in_addr(s, &api_nh->gate.ipv4);
- break;
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- stream_put_in_addr(s, &api_nh->gate.ipv4);
- stream_putl(s, api_nh->ifindex);
- break;
- case NEXTHOP_TYPE_IFINDEX:
- stream_putl(s, api_nh->ifindex);
- break;
- case NEXTHOP_TYPE_IPV6:
- stream_write(s, (uint8_t *)&api_nh->gate.ipv6,
- 16);
- break;
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- stream_write(s, (uint8_t *)&api_nh->gate.ipv6,
- 16);
- stream_putl(s, api_nh->ifindex);
- break;
- }
-
/* MPLS labels for BGP-LU or Segment Routing */
- if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) {
- if (api_nh->label_num > MPLS_MAX_LABELS) {
- char buf[PREFIX2STR_BUFFER];
- prefix2str(&api->prefix, buf,
- sizeof(buf));
- flog_err(EC_LIB_ZAPI_ENCODE,
- "%s: prefix %s: can't encode "
- "%u labels (maximum is %u)",
- __func__, buf,
- api_nh->label_num,
- MPLS_MAX_LABELS);
- return -1;
- }
-
- stream_putc(s, api_nh->label_num);
- stream_put(s, &api_nh->labels[0],
- api_nh->label_num
- * sizeof(mpls_label_t));
+ if (api_nh->label_num > MPLS_MAX_LABELS) {
+ char buf[PREFIX2STR_BUFFER];
+
+ prefix2str(&api->prefix, buf, sizeof(buf));
+
+ flog_err(EC_LIB_ZAPI_ENCODE,
+ "%s: prefix %s: can't encode %u labels (maximum is %u)",
+ __func__, buf,
+ api_nh->label_num,
+ MPLS_MAX_LABELS);
+ return -1;
}
- /* Router MAC for EVPN routes. */
- if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
- stream_put(s, &(api_nh->rmac),
- sizeof(struct ethaddr));
+ if (zapi_nexthop_encode(s, api_nh, api->flags) != 0)
+ return -1;
}
}
return 0;
}
+/*
+ * Decode a single zapi nexthop object
+ */
+static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
+ uint32_t api_flags)
+{
+ int ret = -1;
+
+ STREAM_GETL(s, api_nh->vrf_id);
+ STREAM_GETC(s, api_nh->type);
+
+ /* Note that we're only using a single octet of flags */
+ STREAM_GETC(s, api_nh->flags);
+
+ switch (api_nh->type) {
+ case NEXTHOP_TYPE_BLACKHOLE:
+ STREAM_GETC(s, api_nh->bh_type);
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ STREAM_GET(&api_nh->gate.ipv4.s_addr, s,
+ IPV4_MAX_BYTELEN);
+ STREAM_GETL(s, api_nh->ifindex);
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ STREAM_GETL(s, api_nh->ifindex);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ STREAM_GET(&api_nh->gate.ipv6, s, 16);
+ STREAM_GETL(s, api_nh->ifindex);
+ break;
+ }
+
+ /* MPLS labels for BGP-LU or Segment Routing */
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)) {
+ STREAM_GETC(s, api_nh->label_num);
+ if (api_nh->label_num > MPLS_MAX_LABELS) {
+ flog_err(
+ EC_LIB_ZAPI_ENCODE,
+ "%s: invalid number of MPLS labels (%u)",
+ __func__, api_nh->label_num);
+ return -1;
+ }
+
+ STREAM_GET(&api_nh->labels[0], s,
+ api_nh->label_num * sizeof(mpls_label_t));
+ }
+
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT))
+ STREAM_GETL(s, api_nh->weight);
+
+ /* Router MAC for EVPN routes. */
+ if (CHECK_FLAG(api_flags, ZEBRA_FLAG_EVPN_ROUTE))
+ STREAM_GET(&(api_nh->rmac), s,
+ sizeof(struct ethaddr));
+
+ /* Success */
+ ret = 0;
+
+stream_failure:
+
+ return ret;
+}
+
int zapi_route_decode(struct stream *s, struct zapi_route *api)
{
struct zapi_nexthop *api_nh;
for (i = 0; i < api->nexthop_num; i++) {
api_nh = &api->nexthops[i];
- STREAM_GETL(s, api_nh->vrf_id);
- STREAM_GETC(s, api_nh->type);
- STREAM_GETC(s, api_nh->onlink);
- switch (api_nh->type) {
- case NEXTHOP_TYPE_BLACKHOLE:
- STREAM_GETC(s, api_nh->bh_type);
- break;
- case NEXTHOP_TYPE_IPV4:
- STREAM_GET(&api_nh->gate.ipv4.s_addr, s,
- IPV4_MAX_BYTELEN);
- break;
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- STREAM_GET(&api_nh->gate.ipv4.s_addr, s,
- IPV4_MAX_BYTELEN);
- STREAM_GETL(s, api_nh->ifindex);
- break;
- case NEXTHOP_TYPE_IFINDEX:
- STREAM_GETL(s, api_nh->ifindex);
- break;
- case NEXTHOP_TYPE_IPV6:
- STREAM_GET(&api_nh->gate.ipv6, s, 16);
- break;
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- STREAM_GET(&api_nh->gate.ipv6, s, 16);
- STREAM_GETL(s, api_nh->ifindex);
- break;
- }
-
- /* MPLS labels for BGP-LU or Segment Routing */
- if (CHECK_FLAG(api->message, ZAPI_MESSAGE_LABEL)) {
- STREAM_GETC(s, api_nh->label_num);
-
- if (api_nh->label_num > MPLS_MAX_LABELS) {
- flog_err(
- EC_LIB_ZAPI_ENCODE,
- "%s: invalid number of MPLS labels (%u)",
- __func__, api_nh->label_num);
- return -1;
- }
-
- STREAM_GET(&api_nh->labels[0], s,
- api_nh->label_num
- * sizeof(mpls_label_t));
- }
-
- /* Router MAC for EVPN routes. */
- if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
- stream_get(&(api_nh->rmac), s,
- sizeof(struct ethaddr));
+ if (zapi_nexthop_decode(s, api_nh, api->flags) != 0)
+ return -1;
}
}
return n;
}
+/*
+ * Convert nexthop to zapi nexthop
+ */
+int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
+ const struct nexthop *nh)
+{
+ int i;
+
+ memset(znh, 0, sizeof(*znh));
+
+ znh->type = nh->type;
+ znh->vrf_id = nh->vrf_id;
+ znh->ifindex = nh->ifindex;
+ znh->gate = nh->gate;
+
+ if (nh->nh_label && (nh->nh_label->num_labels > 0)) {
+ for (i = 0; i < nh->nh_label->num_labels; i++)
+ znh->labels[i] = nh->nh_label->label[i];
+
+ znh->label_num = i;
+ SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
+ }
+
+ return 0;
+}
+
+/*
+ * Decode the nexthop-tracking update message
+ */
bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
{
uint32_t i;
STREAM_GETC(s, nhr->nexthop_num);
for (i = 0; i < nhr->nexthop_num; i++) {
- STREAM_GETL(s, nhr->nexthops[i].vrf_id);
- STREAM_GETC(s, nhr->nexthops[i].type);
- switch (nhr->nexthops[i].type) {
- case NEXTHOP_TYPE_IPV4:
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- STREAM_GET(&nhr->nexthops[i].gate.ipv4.s_addr, s,
- IPV4_MAX_BYTELEN);
- STREAM_GETL(s, nhr->nexthops[i].ifindex);
- break;
- case NEXTHOP_TYPE_IFINDEX:
- STREAM_GETL(s, nhr->nexthops[i].ifindex);
- break;
- case NEXTHOP_TYPE_IPV6:
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- STREAM_GET(&nhr->nexthops[i].gate.ipv6, s,
- IPV6_MAX_BYTELEN);
- STREAM_GETL(s, nhr->nexthops[i].ifindex);
- break;
- case NEXTHOP_TYPE_BLACKHOLE:
- break;
- }
- STREAM_GETC(s, nhr->nexthops[i].label_num);
- if (nhr->nexthops[i].label_num > MPLS_MAX_LABELS) {
- flog_err(EC_LIB_ZAPI_ENCODE,
- "%s: invalid number of MPLS labels (%u)",
- __func__, nhr->nexthops[i].label_num);
- return false;
- }
- if (nhr->nexthops[i].label_num)
- STREAM_GET(&nhr->nexthops[i].labels[0], s,
- nhr->nexthops[i].label_num
- * sizeof(mpls_label_t));
+ if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0) != 0)
+ return -1;
}
return true;
return false;
}
+bool zapi_error_decode(struct stream *s, enum zebra_error_types *error)
+{
+ memset(error, 0, sizeof(*error));
+
+ STREAM_GET(error, s, sizeof(*error));
+
+ if (zclient_debug)
+ zlog_debug("%s: type: %s", __func__,
+ zebra_error_type2str(*error));
+
+ return true;
+stream_failure:
+ return false;
+}
+
/*
* send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE
* for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will
if_down_via_zapi(ifp);
}
+static void zclient_handle_error(ZAPI_CALLBACK_ARGS)
+{
+ enum zebra_error_types error;
+ struct stream *s = zclient->ibuf;
+
+ zapi_error_decode(s, &error);
+
+ if (zclient->handle_error)
+ (*zclient->handle_error)(error);
+}
+
static void link_params_set_value(struct stream *s, struct if_link_params *iflp)
{
}
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];
case ZEBRA_MLAG_FORWARD_MSG:
zclient_mlag_handle_msg(command, zclient, length, vrf_id);
break;
+ case ZEBRA_ERROR:
+ zclient_handle_error(command, zclient, length, vrf_id);
default:
break;
}
ZEBRA_MLAG_CLIENT_REGISTER,
ZEBRA_MLAG_CLIENT_UNREGISTER,
ZEBRA_MLAG_FORWARD_MSG,
+ ZEBRA_ERROR,
} zebra_message_types_t;
+enum zebra_error_types {
+ ZEBRA_UNKNOWN_ERROR, /* Error of unknown type */
+ ZEBRA_NO_VRF, /* Vrf in header was not found */
+ ZEBRA_INVALID_MSG_TYPE, /* No handler found for msg type */
+};
+
+static inline const char *zebra_error_type2str(enum zebra_error_types type)
+{
+ const char *ret = "UNKNOWN";
+
+ switch (type) {
+ case ZEBRA_UNKNOWN_ERROR:
+ ret = "ZEBRA_UNKNOWN_ERROR";
+ break;
+ case ZEBRA_NO_VRF:
+ ret = "ZEBRA_NO_VRF";
+ break;
+ case ZEBRA_INVALID_MSG_TYPE:
+ ret = "ZEBRA_INVALID_MSG_TYPE";
+ break;
+ }
+
+ return ret;
+}
+
struct redist_proto {
uint8_t enabled;
struct list *instances;
int (*mlag_process_up)(void);
int (*mlag_process_down)(void);
int (*mlag_handle_msg)(struct stream *msg, int len);
+ int (*handle_error)(enum zebra_error_types error);
};
/* Zebra API message flag. */
#define ZAPI_MESSAGE_TAG 0x08
#define ZAPI_MESSAGE_MTU 0x10
#define ZAPI_MESSAGE_SRCPFX 0x20
-#define ZAPI_MESSAGE_LABEL 0x40
/*
* This should only be used by a DAEMON that needs to communicate
* the table being used is not in the VRF. You must pass the
enum nexthop_types_t type;
vrf_id_t vrf_id;
ifindex_t ifindex;
- bool onlink;
+ uint8_t flags;
union {
union g_addr gate;
enum blackhole_type bh_type;
mpls_label_t labels[MPLS_MAX_LABELS];
struct ethaddr rmac;
+
+ uint32_t weight;
};
+/*
+ * ZAPI nexthop flags values
+ */
+#define ZAPI_NEXTHOP_FLAG_ONLINK 0x01
+#define ZAPI_NEXTHOP_FLAG_LABEL 0x02
+#define ZAPI_NEXTHOP_FLAG_WEIGHT 0x04
+
/*
* Some of these data structures do not map easily to
* a actual data structure size giving different compilers
ZAPI_IPTABLE_FAIL_REMOVE,
};
+static inline const char *
+zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note)
+{
+ const char *ret = "UNKNOWN";
+
+ switch (note) {
+ case ZAPI_RULE_FAIL_INSTALL:
+ ret = "ZAPI_RULE_FAIL_INSTALL";
+ break;
+ case ZAPI_RULE_INSTALLED:
+ ret = "ZAPI_RULE_INSTALLED";
+ break;
+ case ZAPI_RULE_FAIL_REMOVE:
+ ret = "ZAPI_RULE_FAIL_REMOVE";
+ break;
+ case ZAPI_RULE_REMOVED:
+ ret = "ZAPI_RULE_REMOVED";
+ break;
+ }
+
+ return ret;
+}
+
/* Zebra MAC types */
#define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/
#define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/
unsigned short);
extern void redist_add_instance(struct redist_proto *, unsigned short);
extern void redist_del_instance(struct redist_proto *, unsigned short);
+extern void redist_del_all_instances(struct redist_proto *red);
/*
* Send to zebra that the specified vrf is using label to resolve
extern int zclient_send_rnh(struct zclient *zclient, int command,
struct prefix *p, bool exact_match,
vrf_id_t vrf_id);
+int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
+ uint32_t api_flags);
extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *);
extern int zapi_route_decode(struct stream *, struct zapi_route *);
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
enum zapi_iptable_notify_owner *note);
extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
+int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
+ const struct nexthop *nh);
extern bool zapi_nexthop_update_decode(struct stream *s,
struct zapi_route *nhr);
+/* Decode the zebra error message */
+extern bool zapi_error_decode(struct stream *s, enum zebra_error_types *error);
+
static inline void zapi_route_set_blackhole(struct zapi_route *api,
enum blackhole_type bh_type)
{
AC_MSG_RESULT([yes])
PYTHON_CFLAGS="`\"$pycfg\" --includes`"
- if test x"${py_ver}" == x"3.8" || test x"{py_ver}" == x"3.9"; then
+ if test x"${py_ver}" = x"3.8" || test x"{py_ver}" = x"3.9"; then
PYTHON_LIBS="`\"$pycfg\" --ldflags --embed`"
else
PYTHON_LIBS="`\"$pycfg\" --ldflags`"
-if HAVE_PROTOBUF
+if HAVE_PROTOBUF3
lib_LTLIBRARIES += mlag/libmlag_pb.la
endif
#include "version.h"
#include "log.h"
#include "memory.h"
-#include "memory_vty.h"
#include "command.h"
#include "libfrr.h"
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;
extoff = htons(hdr->extension_offset);
if (extoff) {
- if (extoff >= realsize) {
- info = "extoff larger than packet";
+ assert(zb->head > zb->buf);
+ uint32_t header_offset = zb->head - zb->buf;
+ if ((extoff >= realsize) || (extoff < (header_offset))) {
+ info = "extoff larger than packet, or smaller than header";
goto drop;
}
paylen = extoff - (zb->head - zb->buf);
if NHRPD
sbin_PROGRAMS += nhrpd/nhrpd
vtysh_scan += $(top_srcdir)/nhrpd/nhrp_vty.c
-man8 += $(MANBUILD)/nhrpd.8
+man8 += $(MANBUILD)/frr-nhrpd.8
endif
nhrpd_nhrpd_LDADD = lib/libfrr.la lib/libfrrcares.la $(LIBCAP)
#include "command.h"
#include "vty.h"
#include "memory.h"
-#include "memory_vty.h"
#include "if.h"
#include "filter.h"
#include "prefix.h"
DEFINE_QOBJ_TYPE(ospf6)
+FRR_CFG_DEFAULT_BOOL(OSPF6_LOG_ADJACENCY_CHANGES,
+ { .val_long = true, .match_profile = "datacenter", },
+ { .val_long = false },
+)
+
/* global ospf6d variable */
struct ospf6 *ospf6;
static struct ospf6_master ospf6_master;
o->distance_table = route_table_init();
-/* Enable "log-adjacency-changes" */
-#if DFLT_OSPF6_LOG_ADJACENCY_CHANGES
- SET_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES);
-#endif
-
QOBJ_REG(o, ospf6);
return o;
{
if (ospf6 == NULL) {
ospf6 = ospf6_create(VRF_DEFAULT);
+ if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES)
+ SET_FLAG(ospf6->config_flags,
+ OSPF6_LOG_ADJACENCY_CHANGES);
if (ospf6->router_id == 0)
ospf6_router_id_update();
}
if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) {
if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL))
vty_out(vty, " log-adjacency-changes detail\n");
- else if (!DFLT_OSPF6_LOG_ADJACENCY_CHANGES)
+ else if (!SAVE_OSPF6_LOG_ADJACENCY_CHANGES)
vty_out(vty, " log-adjacency-changes\n");
- } else if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES) {
+ } else if (SAVE_OSPF6_LOG_ADJACENCY_CHANGES) {
vty_out(vty, " no log-adjacency-changes\n");
}
uint32_t zebra_router_id;
};
+/* ospf6->config_flags */
+enum {
+ OSPF6_LOG_ADJACENCY_CHANGES = (1 << 0),
+ OSPF6_LOG_ADJACENCY_DETAIL = (1 << 1),
+};
+
/* OSPFv3 top level data structure */
struct ospf6 {
/* The relevant vrf_id */
uint8_t flag;
- /* Configured flags */
+ /* Configuration bitmask, refer to enum above */
uint8_t config_flags;
-#define OSPF6_LOG_ADJACENCY_CHANGES (1 << 0)
-#define OSPF6_LOG_ADJACENCY_DETAIL (1 << 1)
/* LSA timer parameters */
unsigned int lsa_minarrival; /* LSA minimum arrival in milliseconds. */
if SNMP
module_LTLIBRARIES += ospf6d/ospf6d_snmp.la
endif
-man8 += $(MANBUILD)/ospf6d.8
+man8 += $(MANBUILD)/frr-ospf6d.8
endif
ospf6d_libospf6_a_SOURCES = \
if OSPFCLIENT
lib_LTLIBRARIES += ospfclient/libfrrospfapiclient.la
noinst_PROGRAMS += ospfclient/ospfclient
-man8 += $(MANBUILD)/ospfclient.8
+#man8 += $(MANBUILD)/frr-ospfclient.8
endif
ospfclient_libfrrospfapiclient_la_LDFLAGS = -version-info 0:0:0
void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr)
{
struct msg *msg;
- struct in_addr ifaddr = {.s_addr = 0L};
+ struct in_addr ifaddr;
struct in_addr nbraddr;
assert(nbr);
int ospf_if_new_hook(struct interface *ifp)
{
int rc = 0;
+ struct ospf_if_info *oii;
ifp->info = XCALLOC(MTYPE_OSPF_IF_INFO, sizeof(struct ospf_if_info));
+ oii = ifp->info;
+ oii->curr_mtu = ifp->mtu;
+
IF_OIFS(ifp) = route_table_init();
IF_OIFS_PARAMS(ifp) = route_table_init();
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--;
}
{
struct ospf_interface *oi;
struct route_node *rn;
+ struct ospf_if_info *oii = ifp->info;
- /* Interface is already up. */
- if (if_is_operative(ifp)) {
- /* Temporarily keep ifp values. */
- struct interface if_tmp;
- memcpy(&if_tmp, ifp, sizeof(struct interface));
+ ospf_if_recalculate_output_cost(ifp);
+ if (oii && oii->curr_mtu != ifp->mtu) {
if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE))
zlog_debug(
- "Zebra: Interface[%s] state update speed %u -> %u, bw %d -> %d",
- ifp->name, if_tmp.speed, ifp->speed,
- if_tmp.bandwidth, ifp->bandwidth);
+ "Zebra: Interface[%s] MTU change %u -> %u.",
+ ifp->name, oii->curr_mtu, ifp->mtu);
- ospf_if_recalculate_output_cost(ifp);
+ oii->curr_mtu = ifp->mtu;
+ /* Must reset the interface (simulate down/up) when MTU
+ * changes. */
+ ospf_if_reset(ifp);
- if (if_tmp.mtu != ifp->mtu) {
- if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE))
- zlog_debug(
- "Zebra: Interface[%s] MTU change %u -> %u.",
- ifp->name, if_tmp.mtu, ifp->mtu);
-
- /* Must reset the interface (simulate down/up) when MTU
- * changes. */
- ospf_if_reset(ifp);
- }
return 0;
}
struct route_table *oifs;
unsigned int
membership_counts[MEMBER_MAX]; /* multicast group refcnts */
+
+ uint32_t curr_mtu;
};
struct ospf_interface;
#include "stream.h"
#include "log.h"
#include "memory.h"
-#include "memory_vty.h"
#include "privs.h"
#include "sigevent.h"
#include "zclient.h"
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 "
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
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_bfd.h"
+FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES,
+ { .val_long = true, .match_profile = "datacenter", },
+ { .val_long = false },
+)
+
static const char *const ospf_network_type_str[] = {
"Null", "POINTOPOINT", "BROADCAST", "NBMA", "POINTOMULTIPOINT",
"VIRTUALLINK", "LOOPBACK"};
struct ospf *ospf = NULL;
int idx_vrf = 0, idx_inst = 0;
const char *vrf_name = NULL;
+ bool created = false;
*instance = 0;
if (argv_find(argv, argc, "(1-65535)", &idx_inst))
vrf_name = NULL;
if (enable) {
/* Allocate VRF aware instance */
- ospf = ospf_get(*instance, vrf_name);
+ ospf = ospf_get(*instance, vrf_name, &created);
} else {
ospf = ospf_lookup_by_inst_name(*instance, vrf_name);
}
} else {
if (enable) {
- ospf = ospf_get(*instance, NULL);
+ ospf = ospf_get(*instance, NULL, &created);
} else {
ospf = ospf_lookup_instance(*instance);
}
}
+ if (created) {
+ if (DFLT_OSPF_LOG_ADJACENCY_CHANGES)
+ SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES);
+ }
+
return ospf;
}
static void show_ip_ospf_neighbour_header(struct vty *vty)
{
- vty_out(vty, "\n%-15s %3s %-15s %9s %-15s %-20s %5s %5s %5s\n",
+ vty_out(vty, "\n%-15s %3s %-15s %9s %-15s %-32s %5s %5s %5s\n",
"Neighbor ID", "Pri", "State", "Dead Time", "Address",
"Interface", "RXmtL", "RqstL", "DBsmL");
}
timebuf,
sizeof(timebuf)));
vty_out(vty, "%-15s ", inet_ntoa(nbr->src));
- vty_out(vty, "%-20s %5ld %5ld %5d\n",
+ vty_out(vty, "%-32s %5ld %5ld %5d\n",
IF_NAME(oi),
ospf_ls_retransmit_count(nbr),
ospf_ls_request_count(nbr),
"-", nbr_nbma->priority, "Down",
"-");
vty_out(vty,
- "%-15s %-20s %5d %5d %5d\n",
+ "%-32s %-20s %5d %5d %5d\n",
inet_ntoa(nbr_nbma->addr),
IF_NAME(oi), 0, 0, 0);
}
if (uj)
json_vrf = json_object_new_object();
- if (ospf->vrf_id == 0)
+ if (ospf->vrf_id == VRF_DEFAULT)
name = VRF_DEFAULT_NAME;
else
name = ospf->name;
if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES)) {
if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL))
vty_out(vty, " log-adjacency-changes detail\n");
- else if (!DFLT_OSPF_LOG_ADJACENCY_CHANGES)
+ else if (!SAVE_OSPF_LOG_ADJACENCY_CHANGES)
vty_out(vty, " log-adjacency-changes\n");
- } else if (DFLT_OSPF_LOG_ADJACENCY_CHANGES) {
+ } else if (SAVE_OSPF_LOG_ADJACENCY_CHANGES) {
vty_out(vty, " no log-adjacency-changes\n");
}
new->oi_write_q = list_new();
new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT;
-/* Enable "log-adjacency-changes" */
-#if DFLT_OSPF_LOG_ADJACENCY_CHANGES
- SET_FLAG(new->config, OSPF_LOG_ADJACENCY_CHANGES);
-#endif
-
QOBJ_REG(new, ospf);
new->fd = -1;
return NULL;
}
-struct ospf *ospf_get(unsigned short instance, const char *name)
+struct ospf *ospf_get(unsigned short instance, const char *name, bool *created)
{
struct ospf *ospf;
else
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ *created = (ospf == NULL);
if (ospf == NULL) {
ospf = ospf_new(instance, name);
ospf_add(ospf);
return ospf;
}
-struct ospf *ospf_get_instance(unsigned short instance)
+struct ospf *ospf_get_instance(unsigned short instance, bool *created)
{
struct ospf *ospf;
ospf = ospf_lookup_instance(instance);
+ *created = (ospf == NULL);
if (ospf == NULL) {
ospf = ospf_new(instance, NULL /* VRF_DEFAULT*/);
ospf_add(ospf);
- if (ospf->router_id_static.s_addr == 0) {
- if (vrf_lookup_by_id(ospf->vrf_id))
- ospf_router_id_update(ospf);
- else {
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug(
- "%s: ospf VRF (id %d) is not active yet, skip router id update",
- __PRETTY_FUNCTION__,
- ospf->vrf_id);
- }
+ if (ospf->router_id_static.s_addr == 0)
ospf_router_id_update(ospf);
- }
ospf_opaque_type11_lsa_init(ospf);
}
struct ospf_vl_data *vl_data;
struct listnode *node, *nnode;
int i;
- unsigned short instance = 0;
QOBJ_UNREG(ospf);
ospf_distance_reset(ospf);
route_table_finish(ospf->distance_table);
- if (!CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN))
- instance = ospf->instance;
-
list_delete(&ospf->areas);
list_delete(&ospf->oi_write_q);
}
XFREE(MTYPE_OSPF_TOP, ospf);
-
- if (!CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN))
- ospf_get_instance(instance);
}
#define ROUTEMAP(R) (R->route_map.map)
};
+/* ospf->config */
+enum {
+ OSPF_RFC1583_COMPATIBLE = (1 << 0),
+ OSPF_OPAQUE_CAPABLE = (1 << 2),
+ OSPF_LOG_ADJACENCY_CHANGES = (1 << 3),
+ OSPF_LOG_ADJACENCY_DETAIL = (1 << 4),
+};
+
/* OSPF instance structure. */
struct ospf {
/* OSPF's running state based on the '[no] router ospf [<instance>]'
/* NSSA ABR */
uint8_t anyNSSA; /* Bump for every NSSA attached. */
- /* Configured variables. */
+ /* Configuration bitmask, refer to enum above */
uint8_t config;
-#define OSPF_RFC1583_COMPATIBLE (1 << 0)
-#define OSPF_OPAQUE_CAPABLE (1 << 2)
-#define OSPF_LOG_ADJACENCY_CHANGES (1 << 3)
-#define OSPF_LOG_ADJACENCY_DETAIL (1 << 4)
/* Opaque-LSA administrative flags. */
uint8_t opaque;
/* Prototypes. */
extern const char *ospf_redist_string(unsigned int route_type);
extern struct ospf *ospf_lookup_instance(unsigned short);
-extern struct ospf *ospf_get(unsigned short instance, const char *name);
-extern struct ospf *ospf_get_instance(unsigned short);
+extern struct ospf *ospf_get(unsigned short instance, const char *name,
+ bool *created);
+extern struct ospf *ospf_get_instance(unsigned short, bool *created);
extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance,
const char *name);
extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id);
if SNMP
module_LTLIBRARIES += ospfd/ospfd_snmp.la
endif
-man8 += $(MANBUILD)/ospfd.8
+man8 += $(MANBUILD)/frr-ospfd.8
endif
ospfd_libfrrospf_a_SOURCES = \
}
}
+static bool pbrms_is_installed(const struct pbr_map_sequence *pbrms,
+ const struct pbr_map_interface *pmi)
+{
+ uint64_t is_installed = (uint64_t)1 << pmi->install_bit;
+
+ is_installed &= pbrms->installed;
+
+ if (is_installed)
+ return true;
+
+ return false;
+}
+
+/* If any sequence is installed on the interface, assume installed */
+static bool
+pbr_map_interface_is_installed(const struct pbr_map *pbrm,
+ const struct pbr_map_interface *check_pmi)
+{
+
+ struct pbr_map_sequence *pbrms;
+ struct pbr_map_interface *pmi;
+ struct listnode *node, *inode;
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ if (pmi == check_pmi && pbrms_is_installed(pbrms, pmi))
+ return true;
+
+ return false;
+}
+
static bool pbr_map_interface_is_valid(const struct pbr_map_interface *pmi)
{
/* Don't install rules without a real ifindex on the incoming interface.
void pbr_map_final_interface_deletion(struct pbr_map *pbrm,
struct pbr_map_interface *pmi)
{
- if (pmi->delete == true) {
+ if (pmi->delete == true && !pbr_map_interface_is_installed(pbrm, pmi)) {
listnode_delete(pbrm->incoming, pmi);
pmi->pbrm = NULL;
pbrms->family = prefix->family;
if (!no) {
- if (prefix_same(pbrms->src, prefix))
- return CMD_SUCCESS;
+ if (pbrms->src) {
+ if (prefix_same(pbrms->src, prefix))
+ return CMD_SUCCESS;
+
+ vty_out(vty,
+ "A `match src-ip XX` command already exists, please remove that first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
- if (!pbrms->src)
- pbrms->src = prefix_new();
+ pbrms->src = prefix_new();
prefix_copy(pbrms->src, prefix);
} else
prefix_free(&pbrms->src);
"[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
NO_STR
"Match the rest of the command\n"
- "Choose the src ip or ipv6 prefix to use\n"
+ "Choose the dst ip or ipv6 prefix to use\n"
"v4 Prefix\n"
"v6 Prefix\n")
{
pbrms->family = prefix->family;
if (!no) {
- if (prefix_same(pbrms->dst, prefix))
- return CMD_SUCCESS;
+ if (pbrms->dst) {
+ if (prefix_same(pbrms->dst, prefix))
+ return CMD_SUCCESS;
- if (!pbrms->dst)
- pbrms->dst = prefix_new();
+ vty_out(vty,
+ "A `match dst-ip XX` command already exists, please remove that first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ pbrms->dst = prefix_new();
prefix_copy(pbrms->dst, prefix);
} else
prefix_free(&pbrms->dst);
#endif
if (!no) {
- if (pbrms->mark == (uint32_t) mark)
- return CMD_SUCCESS;
- pbrms->mark = (uint32_t) mark;
- } else {
+ if (pbrms->mark) {
+ if (pbrms->mark == (uint32_t)mark)
+ return CMD_SUCCESS;
+
+ vty_out(vty,
+ "A `match mark XX` command already exists, please remove that first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ pbrms->mark = (uint32_t)mark;
+ } else
pbrms->mark = 0;
- }
pbr_map_check(pbrms);
pbr_map_delete_nexthops(pbrms);
else {
vty_out(vty,
- "Nexthop Group specified: %s does not exist to remove",
+ "Nexthop Group specified: %s does not exist to remove\n",
name);
return CMD_WARNING_CONFIG_FAILED;
}
if (pbrms->nhgrp_name) {
if (strcmp(name, pbrms->nhgrp_name) != 0) {
vty_out(vty,
- "Please delete current nexthop group before modifying current one");
+ "Please delete current nexthop group before modifying current one\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (pbrms->nhgrp_name) {
vty_out(vty,
- "Please unconfigure the nexthop group before adding an individual nexthop");
+ "Please unconfigure the nexthop group before adding an individual nexthop\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (pbrms->nhg->nexthop) {
vty_out(vty,
- "If you would like more than one nexthop please use nexthop-groups");
+ "If you would like more than one nexthop please use nexthop-groups\n");
return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
}
+static void vty_show_pbrms(struct vty *vty,
+ const struct pbr_map_sequence *pbrms, bool detail)
+{
+ char buf[PREFIX_STRLEN];
+ char rbuf[64];
+
+ if (pbrms->reason)
+ pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf));
+
+ vty_out(vty, " Seq: %u rule: %u\n", pbrms->seqno, pbrms->ruleno);
+
+ if (detail)
+ vty_out(vty, " Installed: %" PRIu64 "(%u) Reason: %s\n",
+ pbrms->installed, pbrms->unique,
+ pbrms->reason ? rbuf : "Valid");
+ else
+ vty_out(vty, " Installed: %s Reason: %s\n",
+ pbrms->installed ? "yes" : "no",
+ pbrms->reason ? rbuf : "Valid");
+
+ if (pbrms->src)
+ vty_out(vty, " SRC Match: %s\n",
+ prefix2str(pbrms->src, buf, sizeof(buf)));
+ if (pbrms->dst)
+ vty_out(vty, " DST Match: %s\n",
+ prefix2str(pbrms->dst, buf, sizeof(buf)));
+ if (pbrms->mark)
+ vty_out(vty, " MARK Match: %u\n", pbrms->mark);
+
+ if (pbrms->nhgrp_name) {
+ vty_out(vty, " Nexthop-Group: %s\n", pbrms->nhgrp_name);
+
+ if (detail)
+ vty_out(vty,
+ " Installed: %u(%d) Tableid: %d\n",
+ pbrms->nhs_installed,
+ pbr_nht_get_installed(pbrms->nhgrp_name),
+ pbr_nht_get_table(pbrms->nhgrp_name));
+ else
+ vty_out(vty, " Installed: %s Tableid: %d\n",
+ pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes"
+ : "no",
+ pbr_nht_get_table(pbrms->nhgrp_name));
+
+ } else if (pbrms->nhg) {
+ vty_out(vty, " ");
+ nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop);
+ if (detail)
+ vty_out(vty,
+ " Installed: %u(%d) Tableid: %d\n",
+ pbrms->nhs_installed,
+ pbr_nht_get_installed(pbrms->internal_nhg_name),
+ pbr_nht_get_table(pbrms->internal_nhg_name));
+ else
+ vty_out(vty, " Installed: %s Tableid: %d\n",
+ pbr_nht_get_installed(pbrms->internal_nhg_name)
+ ? "yes"
+ : "no",
+ pbr_nht_get_table(pbrms->internal_nhg_name));
+
+ } else if (pbrms->vrf_unchanged) {
+ vty_out(vty, " VRF Unchanged (use interface vrf)\n");
+ } else if (pbrms->vrf_lookup) {
+ vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name);
+ } else {
+ vty_out(vty, " Nexthop-Group: Unknown Installed: no\n");
+ }
+}
+
+static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm,
+ bool detail)
+{
+ struct pbr_map_sequence *pbrms;
+ struct listnode *node;
+
+ vty_out(vty, " pbr-map %s valid: %s\n", pbrm->name,
+ pbrm->valid ? "yes" : "no");
+
+ for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
+ vty_show_pbrms(vty, pbrms, detail);
+}
+
DEFPY (show_pbr_map,
show_pbr_map_cmd,
"show pbr map [NAME$name] [detail$detail]",
"PBR Map Name\n"
"Detailed information\n")
{
- struct pbr_map_sequence *pbrms;
struct pbr_map *pbrm;
- struct listnode *node;
- char buf[PREFIX_STRLEN];
- char rbuf[64];
RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
if (name && strcmp(name, pbrm->name) != 0)
continue;
- vty_out(vty, " pbr-map %s valid: %d\n", pbrm->name,
- pbrm->valid);
-
- for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
- if (pbrms->reason)
- pbr_map_reason_string(pbrms->reason, rbuf,
- sizeof(rbuf));
- vty_out(vty,
- " Seq: %u rule: %u Installed: %" PRIu64 "(%u) Reason: %s\n",
- pbrms->seqno, pbrms->ruleno, pbrms->installed,
- pbrms->unique, pbrms->reason ? rbuf : "Valid");
-
- if (pbrms->src)
- vty_out(vty, "\tSRC Match: %s\n",
- prefix2str(pbrms->src, buf,
- sizeof(buf)));
- if (pbrms->dst)
- vty_out(vty, "\tDST Match: %s\n",
- prefix2str(pbrms->dst, buf,
- sizeof(buf)));
- if (pbrms->mark)
- vty_out(vty, "\tMARK Match: %u\n", pbrms->mark);
-
- if (pbrms->nhgrp_name) {
- vty_out(vty,
- "\tNexthop-Group: %s(%u) Installed: %u(%d)\n",
- pbrms->nhgrp_name,
- pbr_nht_get_table(pbrms->nhgrp_name),
- pbrms->nhs_installed,
- pbr_nht_get_installed(
- pbrms->nhgrp_name));
- } else if (pbrms->nhg) {
- vty_out(vty, " ");
- nexthop_group_write_nexthop(
- vty, pbrms->nhg->nexthop);
- vty_out(vty,
- "\tInstalled: %u(%d) Tableid: %d\n",
- pbrms->nhs_installed,
- pbr_nht_get_installed(
- pbrms->internal_nhg_name),
- pbr_nht_get_table(
- pbrms->internal_nhg_name));
- } else if (pbrms->vrf_unchanged) {
- vty_out(vty,
- "\tVRF Unchanged (use interface vrf)\n");
- } else if (pbrms->vrf_lookup) {
- vty_out(vty, "\tVRF Lookup: %s\n",
- pbrms->vrf_name);
- } else {
- vty_out(vty,
- "\tNexthop-Group: Unknown Installed: 0(0)\n");
- }
- }
+ vty_show_pbr_map(vty, pbrm, detail);
}
return CMD_SUCCESS;
}
switch (note) {
case ZAPI_RULE_FAIL_INSTALL:
pbrms->installed &= ~installed;
- DEBUGD(&pbr_dbg_zebra,
- "%s: Received RULE_FAIL_INSTALL: %" PRIu64,
- __PRETTY_FUNCTION__, pbrms->installed);
break;
case ZAPI_RULE_INSTALLED:
pbrms->installed |= installed;
- DEBUGD(&pbr_dbg_zebra, "%s: Received RULE_INSTALLED: %" PRIu64,
- __PRETTY_FUNCTION__, pbrms->installed);
break;
case ZAPI_RULE_FAIL_REMOVE:
+ /* Don't change state on rule removal failure */
+ break;
case ZAPI_RULE_REMOVED:
pbrms->installed &= ~installed;
- DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED: %" PRIu64,
- __PRETTY_FUNCTION__, pbrms->installed);
break;
}
+ DEBUGD(&pbr_dbg_zebra, "%s: Received %s: %" PRIu64, __func__,
+ zapi_rule_notify_owner2str(note), pbrms->installed);
+
pbr_map_final_interface_deletion(pbrms->parent, pmi);
return 0;
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;
$(top_srcdir)/pbrd/pbr_vty.c \
$(top_srcdir)/pbrd/pbr_debug.c \
# end
-man8 += $(MANBUILD)/pbrd.8
+man8 += $(MANBUILD)/frr-pbrd.8
endif
pbrd_libpbr_a_SOURCES = \
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;
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;
// 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;
bool uj)
{
struct interface *ifp;
- struct listnode *upnode;
struct pim_interface *pim_ifp;
struct pim_upstream *up;
int fhr = 0;
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++;
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;
json = json_object_new_object();
} else {
vty_out(vty,
- "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN");
+ "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted");
vty_out(vty,
- "\nInstalled Source Group IIF OIL\n");
+ "\nActive Source Group RPT IIF OIL\n");
}
- for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) {
+ frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
char grp_str[INET_ADDRSTRLEN];
char src_str[INET_ADDRSTRLEN];
char in_ifname[INTERFACE_NAMSIZ + 1];
char out_ifname[INTERFACE_NAMSIZ + 1];
int oif_vif_index;
struct interface *ifp_in;
+ bool isRpt;
first_oif = 1;
+ if ((c_oil->up &&
+ PIM_UPSTREAM_FLAG_TEST_USE_RPT(c_oil->up->flags)) ||
+ c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
+ isRpt = true;
+ else
+ isRpt = false;
+
pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, grp_str,
sizeof(grp_str));
pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, src_str,
json_ifp_in);
json_object_int_add(json_source, "Installed",
c_oil->installed);
+ if (isRpt)
+ json_object_boolean_true_add(
+ json_source, "isRpt");
+ else
+ json_object_boolean_false_add(
+ json_source, "isRpt");
json_object_int_add(json_source, "RefCount",
c_oil->oil_ref_count);
json_object_int_add(json_source, "OilListSize",
c_oil->cc.wrong_if);
}
} else {
- vty_out(vty, "%-9d %-15s %-15s %-16s ",
- c_oil->installed, src_str, grp_str, in_ifname);
+ vty_out(vty, "%-6d %-15s %-15s %-3s %-16s ",
+ c_oil->installed, src_str, grp_str,
+ isRpt ? "y" : "n", in_ifname);
}
for (oif_vif_index = 0; oif_vif_index < MAXVIFS;
} else {
if (first_oif) {
first_oif = 0;
- vty_out(vty, "%s(%c%c%c%c)", out_ifname,
+ vty_out(vty, "%s(%c%c%c%c%c)",
+ out_ifname,
(c_oil->oif_flags[oif_vif_index]
& PIM_OIF_FLAG_PROTO_IGMP)
? 'I'
(c_oil->oif_flags[oif_vif_index]
& PIM_OIF_FLAG_PROTO_STAR)
? '*'
+ : ' ',
+ (c_oil->oif_flags[oif_vif_index]
+ & PIM_OIF_FLAG_MUTE)
+ ? 'M'
: ' ');
} else
- vty_out(vty, ", %s(%c%c%c%c)",
+ vty_out(vty, ", %s(%c%c%c%c%c)",
out_ifname,
(c_oil->oif_flags[oif_vif_index]
& PIM_OIF_FLAG_PROTO_IGMP)
(c_oil->oif_flags[oif_vif_index]
& PIM_OIF_FLAG_PROTO_STAR)
? '*'
+ : ' ',
+ (c_oil->oif_flags[oif_vif_index]
+ & PIM_OIF_FLAG_MUTE)
+ ? 'M'
: ' ');
}
}
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;
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];
}
}
-static void pim_show_join_desired_helper(struct pim_instance *pim,
+static void pim_show_channel_helper(struct pim_instance *pim,
struct vty *vty,
struct pim_interface *pim_ifp,
struct pim_ifchannel *ch,
}
}
-static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty,
+static void pim_show_channel(struct pim_instance *pim, struct vty *vty,
bool uj)
{
struct pim_interface *pim_ifp;
RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
/* scan all interfaces */
- pim_show_join_desired_helper(pim, vty, pim_ifp, ch,
+ pim_show_channel_helper(pim, vty, pim_ifp, ch,
json, uj);
}
}
}
}
+static void pim_show_join_desired_helper(struct pim_instance *pim,
+ struct vty *vty,
+ struct pim_upstream *up,
+ json_object *json, bool uj)
+{
+ json_object *json_group = NULL;
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ json_object *json_row = NULL;
+
+ pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+
+ if (uj) {
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
+
+ json_row = json_object_new_object();
+ json_object_pim_upstream_add(json_row, up);
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+
+ if (pim_upstream_evaluate_join_desired(pim, up))
+ json_object_boolean_true_add(json_row,
+ "evaluateJoinDesired");
+
+ json_object_object_add(json_group, src_str, json_row);
+
+ } else {
+ vty_out(vty, "%-15s %-15s %-6s\n",
+ src_str, grp_str,
+ pim_upstream_evaluate_join_desired(pim, up) ? "yes"
+ : "no");
+ }
+}
+
+static void pim_show_join_desired(struct pim_instance *pim, struct vty *vty,
+ bool uj)
+{
+ struct pim_upstream *up;
+
+ json_object *json = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+ else
+ vty_out(vty,
+ "Source Group EvalJD\n");
+
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ /* scan all interfaces */
+ pim_show_join_desired_helper(pim, vty, up,
+ json, uj);
+ }
+
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
static void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty,
bool uj)
{
- struct listnode *upnode;
struct pim_upstream *up;
json_object *json = NULL;
json_object *json_group = NULL;
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];
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;
"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];
}
/* 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__);
}
}
return CMD_WARNING;
}
+static void pim_show_jp_agg_helper(struct vty *vty,
+ struct interface *ifp,
+ struct pim_neighbor *neigh,
+ struct pim_upstream *up,
+ int is_join)
+{
+ char src_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ char rpf_str[INET_ADDRSTRLEN];
+
+ pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
+ /* pius->address.s_addr */
+ pim_inet4_dump("<rpf?>", neigh->source_addr, rpf_str, sizeof(rpf_str));
+
+ vty_out(vty, "%-16s %-15s %-15s %-15s %5s\n",
+ ifp->name, rpf_str, src_str,
+ grp_str, is_join?"J":"P");
+}
+
+static void pim_show_jp_agg_list(struct pim_instance *pim, struct vty *vty)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct listnode *n_node;
+ struct pim_neighbor *neigh;
+ struct listnode *jag_node;
+ struct pim_jp_agg_group *jag;
+ struct listnode *js_node;
+ struct pim_jp_sources *js;
+
+ vty_out(vty,
+ "Interface RPF Nbr Source Group State\n");
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list,
+ n_node, neigh)) {
+ for (ALL_LIST_ELEMENTS_RO(neigh->upstream_jp_agg,
+ jag_node, jag)) {
+ for (ALL_LIST_ELEMENTS_RO(jag->sources,
+ js_node, js)) {
+ pim_show_jp_agg_helper(vty,
+ ifp, neigh, js->up,
+ js->is_join);
+ }
+ }
+ }
+ }
+}
+
+DEFPY (show_ip_pim_jp_agg,
+ show_ip_pim_jp_agg_cmd,
+ "show ip pim [vrf NAME] jp-agg",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ VRF_CMD_HELP_STR
+ "join prune aggregation list\n")
+{
+ struct vrf *v;
+ struct pim_instance *pim;
+
+ v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
+
+ if (!v) {
+ vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf);
+ return CMD_WARNING;
+ }
+ pim = pim_get_pim_instance(v->vrf_id);
+
+ if (!pim) {
+ vty_out(vty, "%% Unable to find pim instance\n");
+ return CMD_WARNING;
+ }
+
+ pim_show_jp_agg_list(pim, vty);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_ip_pim_local_membership,
show_ip_pim_local_membership_cmd,
"show ip pim [vrf NAME] local-membership [json]",
return CMD_SUCCESS;
}
+DEFUN (show_ip_pim_channel,
+ show_ip_pim_channel_cmd,
+ "show ip pim [vrf NAME] channel [json]",
+ SHOW_STR
+ IP_STR
+ PIM_STR
+ VRF_CMD_HELP_STR
+ "PIM downstream channel info\n"
+ JSON_STR)
+{
+ int idx = 2;
+ struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+ bool uj = use_json(argc, argv);
+
+ if (!vrf)
+ return CMD_WARNING;
+
+ pim_show_channel(vrf->info, vty, uj);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_ip_pim_upstream_join_desired,
show_ip_pim_upstream_join_desired_cmd,
"show ip pim [vrf NAME] upstream-join-desired [json]",
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)
if (ttl < 1)
continue;
+ /* do not display muted OIFs */
+ if (c_oil->oif_flags[oif_vif_index]
+ & PIM_OIF_FLAG_MUTE)
+ continue;
+
+ if (c_oil->oil.mfcc_parent == oif_vif_index &&
+ !pim_mroute_allow_iif_in_oil(c_oil,
+ oif_vif_index))
+ continue;
+
ifp_out = pim_if_find_by_vif_index(pim, oif_vif_index);
pim_time_uptime(
mroute_uptime, sizeof(mroute_uptime),
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;
"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];
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++;
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"
}
/* 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),
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
}
/* 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);
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"
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;
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;
}
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"
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;
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;
}
// 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);
install_element(VIEW_NODE, &show_ip_pim_interface_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_pim_join_cmd);
install_element(VIEW_NODE, &show_ip_pim_join_vrf_all_cmd);
+ install_element(VIEW_NODE, &show_ip_pim_jp_agg_cmd);
install_element(VIEW_NODE, &show_ip_pim_local_membership_cmd);
install_element(VIEW_NODE, &show_ip_pim_neighbor_cmd);
install_element(VIEW_NODE, &show_ip_pim_neighbor_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_pim_state_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_pim_upstream_cmd);
install_element(VIEW_NODE, &show_ip_pim_upstream_vrf_all_cmd);
+ install_element(VIEW_NODE, &show_ip_pim_channel_cmd);
install_element(VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd);
install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd);
install_element(VIEW_NODE, &show_ip_pim_rp_cmd);
pim_if_new(pim->regiface, false, false, true,
false /*vxlan_term*/);
+ /*
+ * On vrf moves we delete the interface if there
+ * is nothing going on with it. We cannot have
+ * the pimregiface deleted.
+ */
+ pim->regiface->configured = true;
+
}
}
int pim_ifp_up(struct interface *ifp)
{
+ struct pim_interface *pim_ifp;
struct pim_instance *pim;
uint32_t table_id;
}
pim = pim_get_pim_instance(ifp->vrf_id);
- if (if_is_operative(ifp)) {
- struct pim_interface *pim_ifp;
- pim_ifp = ifp->info;
- /*
- * If we have a pim_ifp already and this is an if_add
- * that means that we probably have a vrf move event
- * If that is the case, set the proper vrfness.
- */
- if (pim_ifp)
- pim_ifp->pim = pim;
+ pim_ifp = ifp->info;
+ /*
+ * If we have a pim_ifp already and this is an if_add
+ * that means that we probably have a vrf move event
+ * If that is the case, set the proper vrfness.
+ */
+ if (pim_ifp)
+ pim_ifp->pim = pim;
- /*
- pim_if_addr_add_all() suffices for bringing up both IGMP and
- PIM
- */
- pim_if_addr_add_all(ifp);
- }
+ /*
+ pim_if_addr_add_all() suffices for bringing up both IGMP and
+ PIM
+ */
+ pim_if_addr_add_all(ifp);
/*
* If we have a pimreg device callback and it's for a specific
ch->upstream, ch, ch->parent))
pim_channel_add_oif(ch->upstream->channel_oil,
ch->interface,
- PIM_OIF_FLAG_PROTO_STAR);
+ PIM_OIF_FLAG_PROTO_STAR,
+ __func__);
pim_channel_del_oif(ch->upstream->channel_oil,
- ch->interface, mask);
+ ch->interface, mask, __func__);
/*
* Do we have any S,G's that are inheriting?
* Nuke from on high too.
for (ALL_LIST_ELEMENTS_RO(ch->upstream->sources,
up_node, child))
- pim_channel_del_oif(child->channel_oil,
- ch->interface,
- PIM_OIF_FLAG_PROTO_STAR);
+ pim_channel_del_inherited_oif(
+ child->channel_oil,
+ ch->interface,
+ __func__);
}
}
listnode_delete(ch->upstream->ifchannels, ch);
- if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
- pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
- }
+ pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
/* upstream is common across ifchannels, check if upstream's
ifchannel list is empty before deleting upstream_del
{
enum pim_ifjoin_state old_state = ch->ifjoin_state;
struct pim_interface *pim_ifp = ch->interface->info;
+ struct pim_ifchannel *child_ch;
if (PIM_DEBUG_PIM_EVENTS)
zlog_debug(
if (!c_oil)
continue;
- if (!pim_upstream_evaluate_join_desired(
- pim_ifp->pim, child)) {
- pim_channel_del_oif(
- c_oil, ch->interface,
- PIM_OIF_FLAG_PROTO_STAR);
- pim_upstream_update_join_desired(
- pim_ifp->pim, child);
- }
-
/*
* If the S,G has no if channel and the
* c_oil still
* has output here then the *,G was
* supplying the implied
* if channel. So remove it.
- * I think this is dead code now. is it?
*/
if (c_oil->oil.mfcc_ttls
[pim_ifp->mroute_vif_index])
- pim_channel_del_oif(
+ pim_channel_del_inherited_oif(
c_oil, ch->interface,
- PIM_OIF_FLAG_PROTO_STAR);
+ __func__);
}
}
if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
child->sg_str,
up->sg_str);
- if (pim_upstream_evaluate_join_desired(
- pim_ifp->pim, child)) {
+ /* check if the channel can be
+ * inherited into the SG's OIL
+ */
+ child_ch = pim_ifchannel_find(
+ ch->interface,
+ &child->sg);
+ if (pim_upstream_eval_inherit_if(
+ child, child_ch, ch)) {
pim_channel_add_oif(
child->channel_oil,
ch->interface,
- PIM_OIF_FLAG_PROTO_STAR);
+ PIM_OIF_FLAG_PROTO_STAR,
+ __func__);
pim_upstream_update_join_desired(
pim_ifp->pim, child);
}
return 1; /* non-local */
}
+static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel *ch,
+ struct pim_interface *pim_ifp)
+{
+ pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
+ PIM_IFJOIN_JOIN);
+ PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags);
+ /* check if the interface qualifies as an immediate
+ * OIF
+ */
+ if (pim_upstream_evaluate_join_desired_interface(
+ ch->upstream, ch,
+ NULL /*starch*/)) {
+ pim_channel_add_oif(ch->upstream->channel_oil,
+ ch->interface,
+ PIM_OIF_FLAG_PROTO_PIM,
+ __func__);
+ pim_upstream_update_join_desired(pim_ifp->pim,
+ ch->upstream);
+ }
+}
+
+
void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr,
struct in_addr upstream, struct prefix_sg *sg,
uint8_t source_flags, uint16_t holdtime)
if (source_flags & PIM_ENCODE_RPT_BIT)
pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
PIM_IFJOIN_NOINFO);
- else {
- /*
- * We have received a S,G join and we are in
- * S,G RPT Prune state. Which means we need
- * to transition to Join state and setup
- * state as appropriate.
- */
- pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
- PIM_IFJOIN_JOIN);
- PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags);
- if (pim_upstream_evaluate_join_desired(pim_ifp->pim,
- ch->upstream)) {
- pim_channel_add_oif(ch->upstream->channel_oil,
- ch->interface,
- PIM_OIF_FLAG_PROTO_PIM);
- pim_upstream_update_join_desired(pim_ifp->pim,
- ch->upstream);
- }
- }
+ else
+ pim_ifchannel_ifjoin_handler(ch, pim_ifp);
break;
case PIM_IFJOIN_PRUNE_PENDING:
THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
THREAD_OFF(ch->t_ifjoin_expiry_timer);
pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
PIM_IFJOIN_NOINFO);
- } else
- pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
- PIM_IFJOIN_JOIN);
+ } else {
+ pim_ifchannel_ifjoin_handler(ch, pim_ifp);
+ }
break;
case PIM_IFJOIN_PRUNE_TMP:
break;
if (pim_upstream_evaluate_join_desired_interface(
child, ch, starch)) {
pim_channel_add_oif(child->channel_oil, ifp,
- PIM_OIF_FLAG_PROTO_STAR);
- pim_upstream_switch(pim, child,
- PIM_UPSTREAM_JOINED);
+ PIM_OIF_FLAG_PROTO_STAR,
+ __func__);
+ pim_upstream_update_join_desired(pim, child);
}
}
== PREFIX_DENY) {
pim_channel_add_oif(
up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP,
+ __func__);
}
}
} else
pim_channel_add_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP,
+ __func__);
}
return 1;
child->sg_str);
ch = pim_ifchannel_find(ifp, &child->sg);
- if (c_oil
- && !pim_upstream_evaluate_join_desired_interface(
- child, ch, starch))
- pim_channel_del_oif(c_oil, ifp,
- PIM_OIF_FLAG_PROTO_STAR);
-
/*
* If the S,G has no if channel and the c_oil still
* has output here then the *,G was supplying the
* implied
* if channel. So remove it.
*/
- if (!chchannel && c_oil
- && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
- pim_channel_del_oif(c_oil, ifp,
- PIM_OIF_FLAG_PROTO_STAR);
+ if (!pim_upstream_evaluate_join_desired_interface(
+ child, ch, starch) ||
+ (!chchannel &&
+ c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])) {
+ pim_channel_del_inherited_oif(c_oil, ifp,
+ __func__);
+ }
/* Child node removal/ref count-- will happen as part of
* parent' delete_no_info */
child->upstream))) {
pim_channel_add_oif(
child->upstream->channel_oil,
- ch->interface, PIM_OIF_FLAG_PROTO_STAR);
- pim_upstream_switch(pim, child->upstream,
- PIM_UPSTREAM_JOINED);
- pim_jp_agg_single_upstream_send(
- &child->upstream->rpf, child->upstream,
- true);
+ ch->interface, PIM_OIF_FLAG_PROTO_STAR,
+ __func__);
+ pim_upstream_update_join_desired(pim,
+ child->upstream);
}
send_upstream_starg = true;
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",
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];
}
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;
}
/*
#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 {
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 */
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;
#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>
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;
/*
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;
recv_prune(ifp, neigh, msg_holdtime,
msg_upstream_addr.u.prefix4, &sg,
msg_source_flags);
-
/*
* So if we are receiving a S,G,RPT prune
* before we have any data for that S,G
"%s: SGRpt flag is set, del inherit oif from up %s",
__PRETTY_FUNCTION__,
up->sg_str);
- pim_channel_del_oif(
+ pim_channel_del_inherited_oif(
up->channel_oil,
starg_ch->interface,
- PIM_OIF_FLAG_PROTO_STAR);
+ __func__);
}
}
}
static struct pim_iface_upstream_switch *
pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf)
{
- struct pim_interface *pim_ifp = rpf->source_nexthop.interface->info;
+ struct interface *ifp = rpf->source_nexthop.interface;
+ struct pim_interface *pim_ifp;
struct pim_iface_upstream_switch *pius;
struct listnode *node, *nnode;
+ if (!ifp)
+ return NULL;
+
+ pim_ifp = ifp->info;
+
/* Old interface is pim disabled */
if (!pim_ifp)
return NULL;
return pius;
}
-void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up)
+void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up,
+ struct pim_neighbor *nbr)
{
struct listnode *node, *nnode;
struct pim_jp_agg_group *jag = NULL;
break;
}
+ if (nbr) {
+ if (PIM_DEBUG_TRACE) {
+ char src_str[INET_ADDRSTRLEN];
+
+ pim_inet4_dump("<src?>", nbr->source_addr, src_str,
+ sizeof(src_str));
+ zlog_debug(
+ "up %s remove from nbr %s/%s jp-agg-list",
+ up->sg_str,
+ nbr->interface->name,
+ src_str);
+ }
+ }
+
if (js) {
js->up = NULL;
listnode_delete(jag->sources, js);
}
void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up,
- bool is_join)
+ bool is_join, struct pim_neighbor *nbr)
{
struct listnode *node, *nnode;
struct pim_jp_agg_group *jag = NULL;
break;
}
+ if (nbr) {
+ if (PIM_DEBUG_TRACE) {
+ char src_str[INET_ADDRSTRLEN];
+
+ pim_inet4_dump("<src?>", nbr->source_addr, src_str,
+ sizeof(src_str));
+ zlog_debug(
+ "up %s add to nbr %s/%s jp-agg-list",
+ up->sg_str,
+ up->rpf.source_nexthop.interface->name,
+ src_str);
+ }
+ }
+
if (!js) {
js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE,
sizeof(struct pim_jp_sources));
/* send Prune(S,G) to the old upstream neighbor */
if (opius)
- pim_jp_agg_add_group(opius->us, up, false);
+ pim_jp_agg_add_group(opius->us, up, false, NULL);
/* send Join(S,G) to the current upstream neighbor */
- pim_jp_agg_add_group(npius->us, up, true);
+ if (npius)
+ pim_jp_agg_add_group(npius->us, up, true, NULL);
}
static bool first = true;
/* skip JP upstream messages if source is directly connected */
- if (!up || !rpf->source_nexthop.interface || pim_if_connected_to_source(
- rpf->source_nexthop
- .interface,
- up->sg.src))
+ if (!up || !rpf->source_nexthop.interface ||
+ pim_if_connected_to_source(rpf->source_nexthop.interface,
+ up->sg.src) ||
+ if_is_loopback_or_vrf(rpf->source_nexthop.interface))
return;
if (first) {
int pim_jp_agg_group_list_cmp(void *arg1, void *arg2);
void pim_jp_agg_clear_group(struct list *group);
-void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up);
+void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up,
+ struct pim_neighbor *nbr);
void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up,
- bool is_join);
+ bool is_join, struct pim_neighbor *nbr);
void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf,
struct pim_upstream *up);
#include "memory.h"
#include "vrf.h"
-#include "memory_vty.h"
#include "filter.h"
#include "vty.h"
#include "sigevent.h"
up = pim_upstream_find_or_add(
&sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
__PRETTY_FUNCTION__);
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
return 0;
}
pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time);
up->channel_oil->cc.pktcnt++;
- PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
// resolve mfcc_parent prior to mroute_add in channel_add_oif
if (up->rpf.source_nexthop.interface &&
up->channel_oil->oil.mfcc_parent >= MAXVIFS) {
- int vif_index = 0;
- vif_index = pim_if_find_vifindex_by_ifindex(
- pim_ifp->pim,
- up->rpf.source_nexthop.interface->ifindex);
- pim_channel_oil_change_iif(pim_ifp->pim, up->channel_oil,
- vif_index, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_iif_update(up->channel_oil, __func__);
}
pim_register_join(up);
pim_upstream_keep_alive_timer_start(
up, pim_ifp->pim->keep_alive_time);
pim_upstream_inherited_olist(pim_ifp->pim, up);
- pim_upstream_switch(pim_ifp->pim, up,
- PIM_UPSTREAM_JOINED);
+ pim_upstream_update_join_desired(pim_ifp->pim, up);
if (PIM_DEBUG_MROUTE)
zlog_debug("%s: Creating %s upstream on LHR",
pim_str_sg_dump(&sg));
return 0;
}
+
+ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) {
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug(
+ "%s register forward skipped, not FHR",
+ up->sg_str);
+ return 0;
+ }
+
pim_register_send((uint8_t *)buf + sizeof(struct ip),
ntohs(ip_hdr->ip_len) - sizeof(struct ip),
pim_ifp->primary_address, rpg, 0, up);
pim_upstream_inherited_olist(pim_ifp->pim, up);
if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil,
+ pim_upstream_mroute_add(up->channel_oil,
__PRETTY_FUNCTION__);
} else {
if (I_am_RP(pim_ifp->pim, up->sg.grp)) {
up->channel_oil->cc.pktcnt++;
pim_register_join(up);
pim_upstream_inherited_olist(pim_ifp->pim, up);
+ if (!up->channel_oil->installed)
+ pim_upstream_mroute_add(up->channel_oil, __func__);
// Send the packet to the RP
pim_mroute_msg_wholepkt(fd, ifp, buf);
PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
__PRETTY_FUNCTION__, NULL);
if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(up->channel_oil,
+ __PRETTY_FUNCTION__);
}
return 0;
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) {
return 0;
}
-int pim_mroute_add(struct channel_oil *c_oil, const char *name)
+/*
+ * Prevent creating MFC entry with OIF=IIF.
+ *
+ * This is a protection against implementation mistakes.
+ *
+ * PIM protocol implicitely ensures loopfree multicast topology.
+ *
+ * IGMP must be protected against adding looped MFC entries created
+ * by both source and receiver attached to the same interface. See
+ * TODO T22.
+ * We shall allow igmp to create upstream when it is DR for the intf.
+ * Assume RP reachable via non DR.
+ */
+bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil,
+ int oif_index)
+{
+#ifdef PIM_ENFORCE_LOOPFREE_MFC
+ struct interface *ifp_out;
+ struct pim_interface *pim_ifp;
+
+ if (c_oil->up &&
+ PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags))
+ return true;
+
+ ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index);
+ if (!ifp_out)
+ return false;
+ pim_ifp = ifp_out->info;
+ if (!pim_ifp)
+ return false;
+ if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_IGMP) &&
+ PIM_I_am_DR(pim_ifp))
+ return true;
+
+ return false;
+#else
+ return true;
+#endif
+}
+
+static inline void pim_mroute_copy(struct mfcctl *oil,
+ struct channel_oil *c_oil)
+{
+ int i;
+
+ oil->mfcc_origin = c_oil->oil.mfcc_origin;
+ oil->mfcc_mcastgrp = c_oil->oil.mfcc_mcastgrp;
+ oil->mfcc_parent = c_oil->oil.mfcc_parent;
+
+ for (i = 0; i < MAXVIFS; ++i) {
+ if ((oil->mfcc_parent == i) &&
+ !pim_mroute_allow_iif_in_oil(c_oil, i)) {
+ oil->mfcc_ttls[i] = 0;
+ continue;
+ }
+
+ if (c_oil->oif_flags[i] & PIM_OIF_FLAG_MUTE)
+ oil->mfcc_ttls[i] = 0;
+ else
+ oil->mfcc_ttls[i] = c_oil->oil.mfcc_ttls[i];
+ }
+}
+
+/* This function must not be called directly 0
+ * use pim_upstream_mroute_add or pim_static_mroute_add instead
+ */
+static int pim_mroute_add(struct channel_oil *c_oil, const char *name)
{
struct pim_instance *pim = c_oil->pim;
+ struct mfcctl tmp_oil;
int err;
- int orig = 0;
- int orig_iif_vif = 0;
- struct pim_interface *pim_reg_ifp = NULL;
- int orig_pimreg_ttl = 0;
- bool pimreg_ttl_reset = false;
- struct pim_interface *vxlan_ifp = NULL;
- int orig_term_ttl = 0;
- bool orig_term_ttl_reset = false;
pim->mroute_add_last = pim_time_monotonic_sec();
++pim->mroute_add_events;
- /* Do not install route if incoming interface is undefined. */
- if (c_oil->oil.mfcc_parent >= MAXVIFS) {
- if (PIM_DEBUG_MROUTE) {
- char buf[1000];
- zlog_debug(
- "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
- __PRETTY_FUNCTION__, name,
- pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
- }
- return -2;
- }
+ /* Copy the oil to a temporary structure to fixup (without need to
+ * later restore) before sending the mroute add to the dataplane
+ */
+ pim_mroute_copy(&tmp_oil, c_oil);
/* The linux kernel *expects* the incoming
* vif to be part of the outgoing list
* in the case of a (*,G).
*/
if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) {
- orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
- c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
- }
-
- if (c_oil->up) {
- /* suppress pimreg in the OIL if the mroute is not supposed to
- * trigger register encapsulated data
- */
- if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) {
- pim_reg_ifp = pim->regiface->info;
- orig_pimreg_ttl =
- c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index];
- c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] = 0;
- /* remember to flip it back after MFC programming */
- pimreg_ttl_reset = true;
- }
-
- vxlan_ifp = pim_vxlan_get_term_ifp(pim);
- /* 1. vxlan termination device must never be added to the
- * origination mroute (and that can actually happen because
- * of XG inheritance from the termination mroute) otherwise
- * traffic will end up looping.
- * 2. vxlan termination device should be removed from the non-DF
- * to prevent duplicates to the overlay rxer
- */
- if (vxlan_ifp &&
- (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) ||
- PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags))) {
- orig_term_ttl_reset = true;
- orig_term_ttl =
- c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index];
- c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] = 0;
- }
+ tmp_oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
}
/*
*/
if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
&& c_oil->oil.mfcc_parent != 0) {
- orig_iif_vif = c_oil->oil.mfcc_parent;
- c_oil->oil.mfcc_parent = 0;
+ tmp_oil.mfcc_parent = 0;
}
err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
- &c_oil->oil, sizeof(c_oil->oil));
+ &tmp_oil, sizeof(tmp_oil));
if (!err && !c_oil->installed
&& c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
- && orig_iif_vif != 0) {
- c_oil->oil.mfcc_parent = orig_iif_vif;
+ && c_oil->oil.mfcc_parent != 0) {
+ tmp_oil.mfcc_parent = c_oil->oil.mfcc_parent;
err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
- &c_oil->oil, sizeof(c_oil->oil));
- }
-
- if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
- c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
-
- if (pimreg_ttl_reset) {
- assert(pim_reg_ifp);
- c_oil->oil.mfcc_ttls[pim_reg_ifp->mroute_vif_index] =
- orig_pimreg_ttl;
+ &tmp_oil, sizeof(tmp_oil));
}
- if (orig_term_ttl_reset)
- c_oil->oil.mfcc_ttls[vxlan_ifp->mroute_vif_index] =
- orig_term_ttl;
-
if (err) {
zlog_warn(
"%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
return 0;
}
+static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil,
+ const char *name)
+{
+ vifi_t iif = MAXVIFS;
+ struct interface *ifp = NULL;
+ struct pim_interface *pim_ifp;
+ struct pim_upstream *up = c_oil->up;
+
+ if (up) {
+ if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) {
+ if (up->parent)
+ ifp = up->parent->rpf.source_nexthop.interface;
+ } else {
+ ifp = up->rpf.source_nexthop.interface;
+ }
+ if (ifp) {
+ pim_ifp = (struct pim_interface *)ifp->info;
+ if (pim_ifp)
+ iif = pim_ifp->mroute_vif_index;
+ }
+ }
+ return iif;
+}
+
+static int pim_upstream_mroute_update(struct channel_oil *c_oil,
+ const char *name)
+{
+ char buf[1000];
+
+ if (c_oil->oil.mfcc_parent >= MAXVIFS) {
+ /* the c_oil cannot be installed as a mroute yet */
+ if (PIM_DEBUG_MROUTE)
+ zlog_debug(
+ "%s(%s) %s mroute not ready to be installed; %s",
+ __func__, name,
+ pim_channel_oil_dump(c_oil, buf,
+ sizeof(buf)),
+ c_oil->installed ?
+ "uninstall" : "skip");
+ /* if already installed flush it out as we are going to stop
+ * updates to it leaving it in a stale state
+ */
+ if (c_oil->installed)
+ pim_mroute_del(c_oil, name);
+ /* return success (skipped) */
+ return 0;
+ }
+
+ return pim_mroute_add(c_oil, name);
+}
+
+/* IIF associated with SGrpt entries are re-evaluated when the parent
+ * (*,G) entries IIF changes
+ */
+static void pim_upstream_all_sources_iif_update(struct pim_upstream *up)
+{
+ struct listnode *listnode;
+ struct pim_upstream *child;
+
+ for (ALL_LIST_ELEMENTS_RO(up->sources, listnode,
+ child)) {
+ if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
+ pim_upstream_mroute_iif_update(child->channel_oil,
+ __func__);
+ }
+}
+
+/* In the case of "PIM state machine" added mroutes an upstream entry
+ * must be present to decide on the SPT-forwarding vs. RPT-forwarding.
+ */
+int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name)
+{
+ vifi_t iif;
+
+ iif = pim_upstream_get_mroute_iif(c_oil, name);
+
+ if (c_oil->oil.mfcc_parent != iif) {
+ c_oil->oil.mfcc_parent = iif;
+ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY &&
+ c_oil->up)
+ pim_upstream_all_sources_iif_update(c_oil->up);
+ } else {
+ c_oil->oil.mfcc_parent = iif;
+ }
+
+ return pim_upstream_mroute_update(c_oil, name);
+}
+
+/* Look for IIF changes and update the dateplane entry only if the IIF
+ * has changed.
+ */
+int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name)
+{
+ vifi_t iif;
+ char buf[1000];
+
+ iif = pim_upstream_get_mroute_iif(c_oil, name);
+ if (c_oil->oil.mfcc_parent == iif) {
+ /* no change */
+ return 0;
+ }
+ c_oil->oil.mfcc_parent = iif;
+
+ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY &&
+ c_oil->up)
+ pim_upstream_all_sources_iif_update(c_oil->up);
+
+ if (PIM_DEBUG_MROUTE_DETAIL)
+ zlog_debug("%s(%s) %s mroute iif update %d",
+ __func__, name,
+ pim_channel_oil_dump(c_oil, buf,
+ sizeof(buf)), iif);
+ /* XXX: is this hack needed? */
+ c_oil->oil_inherited_rescan = 1;
+ return pim_upstream_mroute_update(c_oil, name);
+}
+
+int pim_static_mroute_add(struct channel_oil *c_oil, const char *name)
+{
+ return pim_mroute_add(c_oil, name);
+}
+
+void pim_static_mroute_iif_update(struct channel_oil *c_oil,
+ int input_vif_index,
+ const char *name)
+{
+ if (c_oil->oil.mfcc_parent == input_vif_index)
+ return;
+
+ c_oil->oil.mfcc_parent = input_vif_index;
+ if (input_vif_index == MAXVIFS)
+ pim_mroute_del(c_oil, name);
+ else
+ pim_static_mroute_add(c_oil, name);
+}
+
int pim_mroute_del(struct channel_oil *c_oil, const char *name)
{
struct pim_instance *pim = c_oil->pim;
unsigned char flags);
int pim_mroute_del_vif(struct interface *ifp);
-int pim_mroute_add(struct channel_oil *c_oil, const char *name);
+int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name);
+int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name);
+int pim_static_mroute_add(struct channel_oil *c_oil, const char *name);
+void pim_static_mroute_iif_update(struct channel_oil *c_oil,
+ int input_vif_index,
+ const char *name);
int pim_mroute_del(struct channel_oil *c_oil, const char *name);
void pim_mroute_update_counters(struct channel_oil *c_oil);
+bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil,
+ int oif_index);
#endif /* PIM_MROUTE_H */
if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) {
PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags);
sa->flags |= PIM_MSDP_SAF_UP_DEL_IN_PROG;
- pim_upstream_del(sa->pim, up, __PRETTY_FUNCTION__);
+ up = pim_upstream_del(sa->pim, up, __PRETTY_FUNCTION__);
+ /* re-eval joinDesired; clearing peer-msdp-sa flag can
+ * cause JD to change
+ */
+ if (up)
+ pim_upstream_update_join_desired(sa->pim, up);
sa->flags &= ~PIM_MSDP_SAF_UP_DEL_IN_PROG;
}
return false;
}
- if (!up->t_ka_timer) {
+ if (!pim_upstream_is_kat_running(up))
/* stream is not active */
return false;
- }
if (!I_am_RP(pim, up->sg.grp)) {
/* we are not RP for the group */
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 */
__PRETTY_FUNCTION__, up->sg_str);
for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
- if (child->sptbit == PIM_UPSTREAM_SPTBIT_TRUE) {
- if (!pim_rpf_is_same(&up->rpf, &child->rpf)) {
+ if (!PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags)) {
+ /* If we are using SPT and the SPT and RPT IIFs
+ * are different we can prune the source off
+ * of the RPT.
+ * If RPF_interface(S) is not resolved hold
+ * decision to prune as SPT may end up on the
+ * same IIF as RPF_interface(RP).
+ */
+ if (child->rpf.source_nexthop.interface &&
+ !pim_rpf_is_same(&up->rpf,
+ &child->rpf)) {
size += sizeof(
struct pim_encoded_source_ipv4);
PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(
"%s: SPT Bit and RPF'(%s) == RPF'(S,G): Not adding Prune for (%s,rpt)",
__PRETTY_FUNCTION__, up->sg_str,
child->sg_str);
- } else if (pim_upstream_is_sg_rpt(child)) {
- if (pim_upstream_empty_inherited_olist(child)) {
- size += sizeof(
+ } else if (pim_upstream_empty_inherited_olist(child)) {
+ /* S is supposed to be forwarded along the RPT
+ * but it's inherited OIL is empty. So just
+ * prune it off.
+ */
+ size += sizeof(
struct pim_encoded_source_ipv4);
- PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(
+ PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(
child->flags);
- if (PIM_DEBUG_PIM_PACKETS)
- zlog_debug(
+ if (PIM_DEBUG_PIM_PACKETS)
+ zlog_debug(
"%s: inherited_olist(%s,rpt) is NULL, Add Prune to compound message",
__PRETTY_FUNCTION__,
child->sg_str);
- } else if (!pim_rpf_is_same(&up->rpf,
- &child->rpf)) {
- size += sizeof(
- struct pim_encoded_source_ipv4);
- PIM_UPSTREAM_FLAG_SET_SEND_SG_RPT_PRUNE(
- child->flags);
- if (PIM_DEBUG_PIM_PACKETS)
- zlog_debug(
- "%s: RPF'(%s) != RPF'(%s,rpt), Add Prune to compound message",
- __PRETTY_FUNCTION__,
- up->sg_str,
- child->sg_str);
- } else if (PIM_DEBUG_PIM_PACKETS)
- zlog_debug(
- "%s: RPF'(%s) == RPF'(%s,rpt), Do not add Prune to compound message",
- __PRETTY_FUNCTION__, up->sg_str,
- child->sg_str);
} else if (PIM_DEBUG_PIM_PACKETS)
- zlog_debug("%s: SPT bit is not set for (%s)",
- __PRETTY_FUNCTION__, child->sg_str);
+ zlog_debug(
+ "%s: Do not add Prune %s to compound message %s",
+ __PRETTY_FUNCTION__, child->sg_str,
+ up->sg_str);
}
}
return size;
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();
/* 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;
{
struct pim_instance *pim = (struct pim_instance *)arg;
struct pim_upstream *up = (struct pim_upstream *)bucket->data;
- int vif_index = 0;
enum pim_rpf_result rpf_result;
struct pim_rpf old;
old.source_nexthop.interface = up->rpf.source_nexthop.interface;
- rpf_result = pim_rpf_update(pim, up, &old);
- if (rpf_result == PIM_RPF_FAILURE) {
- pim_upstream_rpf_clear(pim, up);
- return HASHWALK_CONTINUE;
- }
-
- /* update kernel multicast forwarding cache (MFC) */
- if (up->rpf.source_nexthop.interface) {
- ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex;
+ rpf_result = pim_rpf_update(pim, up, &old, __func__);
- vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
- /* Pass Current selected NH vif index to mroute download
- */
- if (vif_index)
- pim_scan_individual_oil(up->channel_oil, vif_index);
- else {
- if (PIM_DEBUG_PIM_NHT)
- zlog_debug(
- "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid",
- __PRETTY_FUNCTION__, up->sg_str,
- up->rpf.source_nexthop.interface->name);
- }
- }
+ /* update kernel multicast forwarding cache (MFC); if the
+ * RPF nbr is now unreachable the MFC has already been updated
+ * by pim_rpf_clear
+ */
+ if (rpf_result != PIM_RPF_FAILURE)
+ pim_upstream_mroute_iif_update(up->channel_oil, __func__);
- if (rpf_result == PIM_RPF_CHANGED)
+ if (rpf_result == PIM_RPF_CHANGED ||
+ (rpf_result == PIM_RPF_FAILURE && old.source_nexthop.interface))
pim_zebra_upstream_rpf_changed(pim, up, &old);
__PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
old.source_nexthop.interface
? old.source_nexthop.interface->name : "Unknown",
- up->rpf.source_nexthop.interface->name);
+ up->rpf.source_nexthop.interface
+ ? up->rpf.source_nexthop.interface->name : "Unknown");
}
return HASHWALK_CONTINUE;
#include "pim_str.h"
#include "pim_iface.h"
#include "pim_time.h"
+#include "pim_vxlan.h"
// struct list *pim_channel_oil_list = NULL;
// struct hash *pim_channel_oil_hash = NULL;
+static void pim_channel_update_mute(struct channel_oil *c_oil);
+
char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size)
{
char *out;
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))
return 0;
}
-static bool pim_oil_equal(const void *arg1, const void *arg2)
-{
- const struct channel_oil *c1 = (const struct channel_oil *)arg1;
- const struct channel_oil *c2 = (const struct channel_oil *)arg2;
-
- if ((c1->oil.mfcc_mcastgrp.s_addr == c2->oil.mfcc_mcastgrp.s_addr)
- && (c1->oil.mfcc_origin.s_addr == c2->oil.mfcc_origin.s_addr))
- return true;
-
- return false;
-}
-
-static unsigned int pim_oil_hash_key(const void *arg)
-{
- const struct channel_oil *oil = arg;
-
- return jhash_2words(oil->oil.mfcc_mcastgrp.s_addr,
- oil->oil.mfcc_origin.s_addr, 0);
-}
-
void pim_oil_init(struct pim_instance *pim)
{
- char hash_name[64];
-
- snprintf(hash_name, 64, "PIM %s Oil Hash", pim->vrf->name);
- pim->channel_oil_hash = hash_create_size(8192, pim_oil_hash_key,
- pim_oil_equal, hash_name);
-
- pim->channel_oil_list = list_new();
- pim->channel_oil_list->del = (void (*)(void *))pim_channel_oil_free;
- pim->channel_oil_list->cmp =
- (int (*)(void *, void *))pim_channel_oil_compare;
+ rb_pim_oil_init(&pim->channel_oil_head);
}
void pim_oil_terminate(struct pim_instance *pim)
{
- if (pim->channel_oil_list)
- list_delete(&pim->channel_oil_list);
+ struct channel_oil *c_oil;
- if (pim->channel_oil_hash)
- hash_free(pim->channel_oil_hash);
- pim->channel_oil_hash = NULL;
+ while ((c_oil = rb_pim_oil_pop(&pim->channel_oil_head)))
+ pim_channel_oil_free(c_oil);
+
+ rb_pim_oil_fini(&pim->channel_oil_head);
}
void pim_channel_oil_free(struct channel_oil *c_oil)
lookup.oil.mfcc_mcastgrp = sg->grp;
lookup.oil.mfcc_origin = sg->src;
- c_oil = hash_lookup(pim->channel_oil_hash, &lookup);
+ c_oil = rb_pim_oil_find(&pim->channel_oil_head, &lookup);
return c_oil;
}
-void pim_channel_oil_change_iif(struct pim_instance *pim,
- struct channel_oil *c_oil,
- int input_vif_index,
- const char *name)
-{
- int old_vif_index = c_oil->oil.mfcc_parent;
- struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp,
- .grp = c_oil->oil.mfcc_origin};
-
- if (c_oil->oil.mfcc_parent == input_vif_index) {
- if (PIM_DEBUG_MROUTE_DETAIL)
- zlog_debug("%s(%s): Existing channel oil %pSG4 already using %d as IIF",
- __PRETTY_FUNCTION__, name, &sg,
- input_vif_index);
-
- return;
- }
-
- if (PIM_DEBUG_MROUTE_DETAIL)
- zlog_debug("%s(%s): Changing channel oil %pSG4 IIF from %d to %d installed: %d",
- __PRETTY_FUNCTION__, name, &sg,
- c_oil->oil.mfcc_parent, input_vif_index,
- c_oil->installed);
-
- c_oil->oil.mfcc_parent = input_vif_index;
- if (c_oil->installed) {
- if (input_vif_index == MAXVIFS)
- pim_mroute_del(c_oil, name);
- else
- pim_mroute_add(c_oil, name);
- } else
- if (old_vif_index == MAXVIFS)
- pim_mroute_add(c_oil, name);
-
- return;
-}
-
struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
struct prefix_sg *sg,
- int input_vif_index, const char *name)
+ const char *name)
{
struct channel_oil *c_oil;
- struct interface *ifp;
c_oil = pim_find_channel_oil(pim, sg);
if (c_oil) {
- if (c_oil->oil.mfcc_parent != input_vif_index) {
- c_oil->oil_inherited_rescan = 1;
- if (PIM_DEBUG_MROUTE_DETAIL)
- zlog_debug(
- "%s: Existing channel oil %pSG4 points to %d, modifying to point at %d",
- __PRETTY_FUNCTION__, sg,
- c_oil->oil.mfcc_parent,
- input_vif_index);
- }
- pim_channel_oil_change_iif(pim, c_oil, input_vif_index,
- name);
++c_oil->oil_ref_count;
- /* channel might be present prior to upstream */
- c_oil->up = pim_upstream_find(pim, sg);
+
+ if (!c_oil->up) {
+ /* channel might be present prior to upstream */
+ c_oil->up = pim_upstream_find(
+ pim, sg);
+ /* if the upstream entry is being anchored to an
+ * already existing channel OIL we need to re-evaluate
+ * the "Mute" state on AA OIFs
+ */
+ pim_channel_update_mute(c_oil);
+ }
+
+ /* check if the IIF has changed
+ * XXX - is this really needed
+ */
+ pim_upstream_mroute_iif_update(c_oil, __func__);
if (PIM_DEBUG_MROUTE)
zlog_debug(
return c_oil;
}
- if (input_vif_index != MAXVIFS) {
- ifp = pim_if_find_by_vif_index(pim, input_vif_index);
- if (!ifp) {
- /* warning only */
- zlog_warn(
- "%s:%s (S,G)=%pSG4 could not find input interface for input_vif_index=%d",
- __PRETTY_FUNCTION__, name, sg, input_vif_index);
- }
- }
-
c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil));
c_oil->oil.mfcc_mcastgrp = sg->grp;
c_oil->oil.mfcc_origin = sg->src;
- c_oil = hash_get(pim->channel_oil_hash, c_oil, hash_alloc_intern);
- c_oil->oil.mfcc_parent = input_vif_index;
+ c_oil->oil.mfcc_parent = MAXVIFS;
c_oil->oil_ref_count = 1;
c_oil->installed = 0;
c_oil->up = pim_upstream_find(pim, sg);
c_oil->pim = pim;
- listnode_add_sort(pim->channel_oil_list, c_oil);
+ rb_pim_oil_add(&pim->channel_oil_head, c_oil);
if (PIM_DEBUG_MROUTE)
- zlog_debug(
- "%s(%s): New oil for %pSG4 vif_index: %d Ref Count: 1 (Post Increment)",
- __PRETTY_FUNCTION__, name, sg, input_vif_index);
+ zlog_debug("%s(%s): c_oil %s add",
+ __func__, name, pim_str_sg_dump(sg));
+
return c_oil;
}
-void pim_channel_oil_del(struct channel_oil *c_oil, const char *name)
+struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
+ const char *name)
{
if (PIM_DEBUG_MROUTE) {
struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp,
* called by list_delete_all_node()
*/
c_oil->up = NULL;
- listnode_delete(c_oil->pim->channel_oil_list, c_oil);
- hash_release(c_oil->pim->channel_oil_hash, c_oil);
+ rb_pim_oil_del(&c_oil->pim->channel_oil_head, c_oil);
pim_channel_oil_free(c_oil);
+ return NULL;
+ }
+
+ return c_oil;
+}
+
+void pim_channel_oil_upstream_deref(struct channel_oil *c_oil)
+{
+ /* The upstream entry associated with a channel_oil is abt to be
+ * deleted. If the channel_oil is kept around because of other
+ * references we need to remove upstream based states out of it.
+ */
+ c_oil = pim_channel_oil_del(c_oil, __func__);
+ if (c_oil) {
+ /* note: here we assume that c_oil->up has already been
+ * cleared
+ */
+ pim_channel_update_mute(c_oil);
}
}
int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif,
- uint32_t proto_mask)
+ uint32_t proto_mask, const char *caller)
{
struct pim_interface *pim_ifp;
channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
- if (channel_oil->oif_flags[pim_ifp->mroute_vif_index]) {
+ if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] &
+ PIM_OIF_FLAG_PROTO_ANY) {
if (PIM_DEBUG_MROUTE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
}
channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
+ /* clear mute; will be re-evaluated when the OIF becomes valid again */
+ channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE;
- if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+ if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
if (PIM_DEBUG_MROUTE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
source_str, sizeof(source_str));
zlog_debug(
- "%s %s: (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
- __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
+ "%s(%s): (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
+ __PRETTY_FUNCTION__, caller, source_str, group_str,
proto_mask, channel_oil->oil.mfcc_parent, oif->name,
pim_ifp->mroute_vif_index);
}
return 0;
}
+void pim_channel_del_inherited_oif(struct channel_oil *c_oil,
+ struct interface *oif, const char *caller)
+{
+ struct pim_upstream *up = c_oil->up;
+
+ pim_channel_del_oif(c_oil, oif, PIM_OIF_FLAG_PROTO_STAR,
+ caller);
+
+ /* if an inherited OIF is being removed join-desired can change
+ * if the inherited OIL is now empty and KAT is running
+ */
+ if (up && up->sg.src.s_addr != INADDR_ANY &&
+ pim_upstream_empty_inherited_olist(up))
+ pim_upstream_update_join_desired(up->pim, up);
+}
+
+static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil,
+ struct pim_interface *pim_ifp)
+{
+ struct pim_interface *pim_reg_ifp;
+ struct pim_interface *vxlan_ifp;
+ bool do_mute = false;
+ struct pim_instance *pim = c_oil->pim;
+
+ if (!c_oil->up)
+ return do_mute;
+
+ pim_reg_ifp = pim->regiface->info;
+ if (pim_ifp == pim_reg_ifp) {
+ /* suppress pimreg in the OIL if the mroute is not supposed to
+ * trigger register encapsulated data
+ */
+ if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags))
+ do_mute = true;
+
+ return do_mute;
+ }
+
+ vxlan_ifp = pim_vxlan_get_term_ifp(pim);
+ if (pim_ifp == vxlan_ifp) {
+ /* 1. vxlan termination device must never be added to the
+ * origination mroute (and that can actually happen because
+ * of XG inheritance from the termination mroute) otherwise
+ * traffic will end up looping.
+ * PS: This check has also been extended to non-orig mroutes
+ * that have a local SIP as such mroutes can move back and
+ * forth between orig<=>non-orig type.
+ * 2. vxlan termination device should be removed from the non-DF
+ * to prevent duplicates to the overlay rxer
+ */
+ if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) ||
+ PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) ||
+ pim_vxlan_is_local_sip(c_oil->up))
+ do_mute = true;
+
+ return do_mute;
+ }
+
+ return do_mute;
+}
+
+void pim_channel_update_oif_mute(struct channel_oil *c_oil,
+ struct pim_interface *pim_ifp)
+{
+ bool old_mute;
+ bool new_mute;
+
+ /* If pim_ifp is not a part of the OIL there is nothing to do */
+ if (!c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
+ return;
+
+ old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] &
+ PIM_OIF_FLAG_MUTE);
+ new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp);
+ if (old_mute == new_mute)
+ return;
+
+ if (new_mute)
+ c_oil->oif_flags[pim_ifp->mroute_vif_index] |=
+ PIM_OIF_FLAG_MUTE;
+ else
+ c_oil->oif_flags[pim_ifp->mroute_vif_index] &=
+ ~PIM_OIF_FLAG_MUTE;
+
+ pim_upstream_mroute_add(c_oil, __PRETTY_FUNCTION__);
+}
+
+/* pim_upstream has been set or cleared on the c_oil. re-eval mute state
+ * on all existing OIFs
+ */
+static void pim_channel_update_mute(struct channel_oil *c_oil)
+{
+ struct pim_interface *pim_reg_ifp;
+ struct pim_interface *vxlan_ifp;
+
+ pim_reg_ifp = c_oil->pim->regiface->info;
+ if (pim_reg_ifp)
+ pim_channel_update_oif_mute(c_oil, pim_reg_ifp);
+ vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim);
+ if (vxlan_ifp)
+ pim_channel_update_oif_mute(c_oil, vxlan_ifp);
+}
int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif,
- uint32_t proto_mask)
+ uint32_t proto_mask, const char *caller)
{
struct pim_interface *pim_ifp;
int old_ttl;
- bool allow_iif_in_oil = false;
/*
* If we've gotten here we've gone bad, but let's
pim_ifp = oif->info;
-#ifdef PIM_ENFORCE_LOOPFREE_MFC
- /*
- Prevent creating MFC entry with OIF=IIF.
-
- This is a protection against implementation mistakes.
-
- PIM protocol implicitely ensures loopfree multicast topology.
-
- IGMP must be protected against adding looped MFC entries created
- by both source and receiver attached to the same interface. See
- TODO T22.
- We shall allow igmp to create upstream when it is DR for the intf.
- Assume RP reachable via non DR.
- */
- if ((channel_oil->up &&
- PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(channel_oil->up->flags)) ||
- ((proto_mask == PIM_OIF_FLAG_PROTO_IGMP) && PIM_I_am_DR(pim_ifp))) {
- allow_iif_in_oil = true;
- }
-
- if (!allow_iif_in_oil &&
- pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
- channel_oil->oil_inherited_rescan = 1;
- if (PIM_DEBUG_MROUTE) {
- char group_str[INET_ADDRSTRLEN];
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<group?>",
- channel_oil->oil.mfcc_mcastgrp,
- group_str, sizeof(group_str));
- pim_inet4_dump("<source?>",
- channel_oil->oil.mfcc_origin, source_str,
- sizeof(source_str));
- zlog_debug(
- "%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
- __FILE__, __PRETTY_FUNCTION__, proto_mask,
- oif->name, pim_ifp->mroute_vif_index,
- source_str, group_str);
- }
- return -2;
- }
-#endif
-
/* Prevent single protocol from subscribing same interface to
channel (S,G) multiple times */
if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] =
PIM_MROUTE_MIN_TTL;
+ /* Some OIFs are held in a muted state i.e. the PIM state machine
+ * decided to include the OIF but additional status check such as
+ * MLAG DF role prevent it from being activated for traffic
+ * forwarding.
+ */
+ if (pim_channel_eval_oif_mute(channel_oil, pim_ifp))
+ channel_oil->oif_flags[pim_ifp->mroute_vif_index] |=
+ PIM_OIF_FLAG_MUTE;
+ else
+ channel_oil->oif_flags[pim_ifp->mroute_vif_index] &=
+ ~PIM_OIF_FLAG_MUTE;
+
/* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
* valid to get installed in kernel.
*/
if (channel_oil->oil.mfcc_parent != MAXVIFS) {
- if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+ if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
if (PIM_DEBUG_MROUTE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin,
source_str, sizeof(source_str));
zlog_debug(
- "%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
- __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
+ "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
+ __PRETTY_FUNCTION__, caller, source_str, group_str,
proto_mask, oif->name, pim_ifp->mroute_vif_index);
}
int pim_channel_oil_empty(struct channel_oil *c_oil)
{
- static uint32_t zero[MAXVIFS];
- static int inited = 0;
+ static struct mfcctl null_oil;
if (!c_oil)
return 1;
- /*
- * Not sure that this is necessary, but I would rather ensure
- * that this works.
- */
- if (!inited) {
- memset(&zero, 0, sizeof(uint32_t) * MAXVIFS);
- inited = 1;
- }
- return !memcmp(c_oil->oif_flags, zero, MAXVIFS * sizeof(uint32_t));
+ /* exclude pimreg from the OIL when checking if the inherited_oil is
+ * non-NULL.
+ * pimreg device (in all vrfs) uses a vifi of
+ * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
+ return !memcmp(&c_oil->oil.mfcc_ttls[1], &null_oil.mfcc_ttls[1],
+ sizeof(null_oil.mfcc_ttls) - sizeof(null_oil.mfcc_ttls[0]));
}
#define PIM_OIL_H
#include "pim_mroute.h"
+#include "pim_iface.h"
/*
* Where did we get this (S,G) from?
(PIM_OIF_FLAG_PROTO_IGMP | PIM_OIF_FLAG_PROTO_PIM \
| PIM_OIF_FLAG_PROTO_STAR | PIM_OIF_FLAG_PROTO_VXLAN)
+/* OIF is present in the OIL but must not be used for forwarding traffic */
+#define PIM_OIF_FLAG_MUTE (1 << 4)
/*
* We need a pimreg vif id from the kernel.
* Since ifindex == vif id for most cases and the number
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;
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);
struct prefix_sg *sg);
struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
struct prefix_sg *sg,
- int input_vif_index, const char *name);
+ const char *name);
void pim_channel_oil_change_iif(struct pim_instance *pim,
struct channel_oil *c_oil, int input_vif_index,
const char *name);
-void pim_channel_oil_del(struct channel_oil *c_oil, const char *name);
+struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil,
+ const char *name);
int pim_channel_add_oif(struct channel_oil *c_oil, struct interface *oif,
- uint32_t proto_mask);
+ uint32_t proto_mask, const char *caller);
int pim_channel_del_oif(struct channel_oil *c_oil, struct interface *oif,
- uint32_t proto_mask);
+ uint32_t proto_mask, const char *caller);
int pim_channel_oil_empty(struct channel_oil *c_oil);
char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size);
+
+void pim_channel_update_oif_mute(struct channel_oil *c_oil,
+ struct pim_interface *pim_ifp);
+
+void pim_channel_oil_upstream_deref(struct channel_oil *c_oil);
+void pim_channel_del_inherited_oif(struct channel_oil *c_oil,
+ struct interface *oif, const char *caller);
+
#endif /* PIM_OIL_H */
}
pim_channel_add_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM, __func__);
up->reg_state = PIM_REG_JOIN;
pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/);
}
case PIM_REG_JOIN:
upstream->reg_state = PIM_REG_PRUNE;
pim_channel_del_oif(upstream->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM, __func__);
pim_upstream_start_register_stop_timer(upstream, 0);
pim_vxlan_update_sg_reg_state(pim, upstream,
false/*reg_join*/);
}
if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE)
- || ((SwitchToSptDesired(pim_ifp->pim, &sg))
+ || ((SwitchToSptDesiredOnRp(pim_ifp->pim, &sg))
&& pim_upstream_inherited_olist(pim_ifp->pim, upstream)
== 0)) {
- // pim_scan_individual_oil (upstream->channel_oil);
pim_register_stop_send(ifp, &sg, dest_addr, src_addr);
sentRegisterStop = 1;
} else {
upstream->sptbit);
}
if ((upstream->sptbit == PIM_UPSTREAM_SPTBIT_TRUE)
- || (SwitchToSptDesired(pim_ifp->pim, &sg))) {
+ || (SwitchToSptDesiredOnRp(pim_ifp->pim, &sg))) {
if (sentRegisterStop) {
pim_upstream_keep_alive_timer_start(
upstream,
static void pim_rp_refresh_group_to_rp_mapping(struct pim_instance *pim)
{
pim_msdp_i_am_rp_changed(pim);
+ pim_upstream_reeval_use_rpt(pim);
}
void pim_rp_prefix_list_update(struct pim_instance *pim,
old_rpf.source_nexthop.interface = up->rpf.source_nexthop.interface;
- rpf_result = pim_rpf_update(pim, up, &old_rpf);
+ rpf_result = pim_rpf_update(pim, up, &old_rpf, __func__);
if (rpf_result == PIM_RPF_FAILURE)
pim_mroute_del(up->channel_oil, __PRETTY_FUNCTION__);
/* update kernel multicast forwarding cache (MFC) */
- if (up->rpf.source_nexthop.interface && up->channel_oil) {
- ifindex_t ifindex = up->rpf.source_nexthop.interface->ifindex;
- int vif_index = pim_if_find_vifindex_by_ifindex(pim, ifindex);
- /* Pass Current selected NH vif index to mroute download */
- if (vif_index)
- pim_scan_individual_oil(up->channel_oil, vif_index);
- else {
- if (PIM_DEBUG_PIM_NHT)
- zlog_debug(
- "%s: NHT upstream %s channel_oil IIF %s vif_index is not valid",
- __PRETTY_FUNCTION__, up->sg_str,
- up->rpf.source_nexthop.interface->name);
- }
- }
+ if (up->rpf.source_nexthop.interface && up->channel_oil)
+ pim_upstream_mroute_iif_update(up->channel_oil, __func__);
- if (rpf_result == PIM_RPF_CHANGED)
+ if (rpf_result == PIM_RPF_CHANGED ||
+ (rpf_result == PIM_RPF_FAILURE &&
+ old_rpf.source_nexthop.interface))
pim_zebra_upstream_rpf_changed(pim, up, &old_rpf);
pim_zebra_update_all_interfaces(pim);
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)
__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
*/
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;
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];
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
*/
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
*/
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) {
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;
if (i_am_rp_changed) {
pim_msdp_i_am_rp_changed(pim);
+ pim_upstream_reeval_use_rpt(pim);
}
}
if (i_am_rp_changed) {
pim_msdp_i_am_rp_changed(pim);
+ pim_upstream_reeval_use_rpt(pim);
}
}
}
enum pim_rpf_result pim_rpf_update(struct pim_instance *pim,
- struct pim_upstream *up, struct pim_rpf *old)
+ struct pim_upstream *up, struct pim_rpf *old,
+ const char *caller)
{
struct pim_rpf *rpf = &up->rpf;
struct pim_rpf saved;
return PIM_RPF_OK;
if (up->upstream_addr.s_addr == INADDR_ANY) {
- zlog_debug("%s: RP is not configured yet for %s",
- __PRETTY_FUNCTION__, up->sg_str);
+ zlog_debug("%s(%s): RP is not configured yet for %s",
+ __func__, caller, up->sg_str);
return PIM_RPF_OK;
}
saved.source_nexthop = rpf->source_nexthop;
saved.rpf_addr = rpf->rpf_addr;
+ if (old) {
+ old->source_nexthop = saved.source_nexthop;
+ old->rpf_addr = saved.rpf_addr;
+ }
nht_p.family = AF_INET;
nht_p.prefixlen = IPV4_MAX_BITLEN;
rpf->rpf_addr.u.prefix4 = pim_rpf_find_rpf_addr(up);
if (pim_rpf_addr_is_inaddr_any(rpf) && PIM_DEBUG_ZEBRA) {
/* RPF'(S,G) not found */
- zlog_debug("%s %s: RPF'%s not found: won't send join upstream",
- __FILE__, __PRETTY_FUNCTION__, up->sg_str);
+ zlog_debug("%s(%s): RPF'%s not found: won't send join upstream",
+ __func__, caller, up->sg_str);
/* warning only */
}
pim_addr_dump("<addr?>",
&rpf->source_nexthop.mrib_nexthop_addr,
nhaddr_str, sizeof(nhaddr_str));
- zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
- __FILE__, __PRETTY_FUNCTION__,
+ zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
+ __func__, caller,
up->sg_str,
rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>",
nhaddr_str,
if (saved.source_nexthop.interface != rpf->source_nexthop.interface) {
if (PIM_DEBUG_ZEBRA) {
- zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s",
- __FILE__, __PRETTY_FUNCTION__,
+ zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s",
+ __func__, caller,
up->sg_str,
saved.source_nexthop.interface ? saved.source_nexthop.interface->name : "<oldif?>",
rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<newif?>");
|| saved.source_nexthop
.interface != rpf->source_nexthop.interface) {
- /* return old rpf to caller ? */
- if (old) {
- old->source_nexthop = saved.source_nexthop;
- old->rpf_addr = saved.rpf_addr;
- }
return PIM_RPF_CHANGED;
}
struct pim_upstream *up)
{
if (up->rpf.source_nexthop.interface) {
- if (up->channel_oil)
- pim_channel_oil_change_iif(pim, up->channel_oil,
- MAXVIFS,
- __PRETTY_FUNCTION__);
-
- pim_upstream_switch(pim, up, PIM_UPSTREAM_NOTJOINED);
up->rpf.source_nexthop.interface = NULL;
up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr =
PIM_NET_INADDR_ANY;
up->rpf.source_nexthop.mrib_route_metric =
router->infinite_assert_metric.route_metric;
up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
+ pim_upstream_mroute_iif_update(up->channel_oil, __func__);
}
}
struct in_addr addr, int neighbor_needed);
enum pim_rpf_result pim_rpf_update(struct pim_instance *pim,
struct pim_upstream *up,
- struct pim_rpf *old);
+ struct pim_rpf *old, const char *caller);
void pim_upstream_rpf_clear(struct pim_instance *pim,
struct pim_upstream *up);
int pim_rpf_addr_is_inaddr_none(struct pim_rpf *rpf);
} else {
/* input interface changed */
s_route->iif = iif_index;
- pim_channel_oil_change_iif(pim, &s_route->c_oil,
+ pim_static_mroute_iif_update(&s_route->c_oil,
iif_index,
__PRETTY_FUNCTION__);
s_route->c_oil.pim = pim;
- if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
+ if (pim_static_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
char gifaddr_str[INET_ADDRSTRLEN];
char sifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
if (s_route->c_oil.oil_ref_count <= 0
? pim_mroute_del(&s_route->c_oil,
__PRETTY_FUNCTION__)
- : pim_mroute_add(&s_route->c_oil,
+ : pim_static_mroute_add(&s_route->c_oil,
__PRETTY_FUNCTION__)) {
char gifaddr_str[INET_ADDRSTRLEN];
char sifaddr_str[INET_ADDRSTRLEN];
child = pim_upstream_del(pim, child,
__PRETTY_FUNCTION__);
}
- if (child)
+ if (child) {
child->parent = NULL;
+ if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
+ pim_upstream_mroute_iif_update(
+ child->channel_oil,
+ __func__);
+ }
}
list_delete(&up->sources);
}
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))
&& (up->sg.grp.s_addr == INADDR_ANY))
return;
- for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, ch_node, child)) {
+ frr_each (rb_pim_upstream, &pim->upstream_head, child) {
if ((up->sg.grp.s_addr != INADDR_ANY)
&& (child->sg.grp.s_addr == up->sg.grp.s_addr)
&& (child != up)) {
child->parent = up;
listnode_add_sort(up->sources, child);
+ if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
+ pim_upstream_mroute_iif_update(
+ child->channel_oil,
+ __func__);
}
}
}
static void upstream_channel_oil_detach(struct pim_upstream *up)
{
- if (up->channel_oil) {
+ struct channel_oil *channel_oil = up->channel_oil;
+
+ if (channel_oil) {
/* Detaching from channel_oil, channel_oil may exist post del,
but upstream would not keep reference of it
*/
- up->channel_oil->up = NULL;
- pim_channel_oil_del(up->channel_oil, __PRETTY_FUNCTION__);
+ channel_oil->up = NULL;
up->channel_oil = NULL;
+
+ /* attempt to delete channel_oil; if channel_oil is being held
+ * because of other references cleanup info such as "Mute"
+ * inferred from the parent upstream
+ */
+ pim_channel_oil_upstream_deref(channel_oil);
}
+
}
struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
if (up->ref_count >= 1)
return up;
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("pim_upstream free vrf:%s %s flags 0x%x",
+ pim->vrf->name, up->sg_str, up->flags);
+
THREAD_OFF(up->t_ka_timer);
THREAD_OFF(up->t_rs_timer);
THREAD_OFF(up->t_msdp_reg_timer);
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);
up->rpf.rpf_addr.u.prefix4);
if (nbr)
- pim_jp_agg_remove_group(nbr->upstream_jp_agg, up);
+ pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr);
pim_jp_agg_upstream_verification(up, false);
}
}
if (nbr)
- pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1);
+ pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1, nbr);
else {
THREAD_OFF(up->t_join_timer);
thread_add_timer(router->master, on_join_timer, up,
* we re-revaluate register setup for existing upstream entries */
void pim_upstream_register_reevaluate(struct pim_instance *pim)
{
- struct listnode *upnode;
struct pim_upstream *up;
- for (ALL_LIST_ELEMENTS_RO(pim->upstream_list, upnode, up)) {
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
/* If FHR is set CouldRegister is True. Also check if the flow
* is actually active; if it is not kat setup will trigger
* source
* registration whenever the flow becomes active. */
- if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || !up->t_ka_timer)
+ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) ||
+ !pim_upstream_is_kat_running(up))
continue;
if (pim_is_grp_ssm(pim, up->sg.grp)) {
/* remove regiface from the OIL if it is there*/
pim_channel_del_oif(up->channel_oil,
pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM,
+ __func__);
up->reg_state = PIM_REG_NOINFO;
}
} else {
up->sg_str);
pim_channel_add_oif(up->channel_oil,
pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM,
+ __func__);
up->reg_state = PIM_REG_JOIN;
}
}
}
}
+/* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should
+ * forward a S -
+ * 1. along the SPT if SPTbit is set
+ * 2. and along the RPT if SPTbit is not set
+ * If forwarding is hw accelerated i.e. control and dataplane components
+ * are separate you may not be able to reliably set SPT bit on intermediate
+ * routers while still fowarding on the (S,G,rpt).
+ *
+ * This macro is a slight deviation on the RFC and uses "traffic-agnostic"
+ * criteria to decide between using the RPT vs. SPT for forwarding.
+ */
+void pim_upstream_update_use_rpt(struct pim_upstream *up,
+ bool update_mroute)
+{
+ bool old_use_rpt;
+ bool new_use_rpt;
+
+ if (up->sg.src.s_addr == INADDR_ANY)
+ return;
+
+ old_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+
+ /* We will use the SPT (IIF=RPF_interface(S) if -
+ * 1. We have decided to join the SPT
+ * 2. We are FHR
+ * 3. Source is directly connected
+ * 4. We are RP (parent's IIF is lo or vrf-device)
+ * In all other cases the source will stay along the RPT and
+ * IIF=RPF_interface(RP).
+ */
+ if (up->join_state == PIM_UPSTREAM_JOINED ||
+ PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) ||
+ pim_if_connected_to_source(
+ up->rpf.source_nexthop.interface,
+ up->sg.src) ||
+ /* XXX - need to switch this to a more efficient
+ * lookup API
+ */
+ I_am_RP(up->pim, up->sg.grp))
+ /* use SPT */
+ PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up->flags);
+ else
+ /* use RPT */
+ PIM_UPSTREAM_FLAG_SET_USE_RPT(up->flags);
+
+ new_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+ if (old_use_rpt != new_use_rpt) {
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_debug("%s switched from %s to %s",
+ up->sg_str,
+ old_use_rpt?"RPT":"SPT",
+ new_use_rpt?"RPT":"SPT");
+ if (update_mroute)
+ pim_upstream_mroute_add(up->channel_oil, __func__);
+ }
+}
+
+/* some events like RP change require re-evaluation of SGrpt across
+ * all groups
+ */
+void pim_upstream_reeval_use_rpt(struct pim_instance *pim)
+{
+ struct pim_upstream *up;
+
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ if (up->sg.src.s_addr == INADDR_ANY)
+ continue;
+
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
+ }
+}
+
void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up,
enum pim_upstream_state new_state)
{
join_timer_start(up);
}
}
+ if (old_state != new_state)
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
} else {
+ bool old_use_rpt;
+ bool new_use_rpt;
+ bool send_xg_jp = false;
forward_off(up);
if (old_state == PIM_UPSTREAM_JOINED)
pim_msdp_up_join_state_changed(pim, up);
+ if (old_state != new_state) {
+ old_use_rpt =
+ !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
+ new_use_rpt =
+ !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+ if (new_use_rpt &&
+ (new_use_rpt != old_use_rpt) &&
+ up->parent)
+ /* we have decided to switch from the SPT back
+ * to the RPT which means we need to cancel
+ * any previously sent SGrpt prunes immediately
+ */
+ send_xg_jp = true;
+ }
+
/* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards
RP.
If I am RP for G then send S,G prune to its IIF. */
- if (pim_upstream_is_sg_rpt(up) && up->parent
- && !I_am_RP(pim, up->sg.grp)) {
+ if (pim_upstream_is_sg_rpt(up) && up->parent &&
+ !I_am_RP(pim, up->sg.grp))
+ send_xg_jp = true;
+ else
+ pim_jp_agg_single_upstream_send(&up->rpf, up,
+ 0 /* prune */);
+
+ if (send_xg_jp) {
if (PIM_DEBUG_PIM_TRACE_DETAIL)
zlog_debug(
- "%s: *,G IIF %s S,G IIF %s ",
- __PRETTY_FUNCTION__,
+ "re-join RPT; *,G IIF %s S,G IIF %s ",
up->parent->rpf.source_nexthop.interface ?
up->parent->rpf.source_nexthop.interface->name
: "Unknown",
pim_jp_agg_single_upstream_send(&up->parent->rpf,
up->parent,
1 /* (W,G) Join */);
- } else
- pim_jp_agg_single_upstream_send(&up->rpf, up,
- 0 /* prune */);
+ }
join_timer_stop(up);
}
}
-int pim_upstream_compare(void *arg1, void *arg2)
+int pim_upstream_compare(const struct pim_upstream *up1,
+ const struct pim_upstream *up2)
{
- const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
- const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
-
if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr))
return -1;
up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
+ up->pim = pim;
up->sg = *sg;
pim_str_sg_set(sg, up->sg_str);
if (ch)
ch->upstream = up;
- up = hash_get(pim->upstream_hash, up, hash_alloc_intern);
+ rb_pim_upstream_add(&pim->upstream_head, up);
/* Set up->upstream_addr as INADDR_ANY, if RP is not
* configured and retain the upstream data structure
*/
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;
up->reg_state = PIM_REG_NOINFO;
up->state_transition = pim_time_monotonic_sec();
up->channel_oil =
- pim_channel_oil_add(pim, &up->sg, MAXVIFS, __PRETTY_FUNCTION__);
+ pim_channel_oil_add(pim, &up->sg, __PRETTY_FUNCTION__);
up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
up->rpf.source_nexthop.interface = NULL;
pim_upstream_fill_static_iif(up, incoming);
pim_ifp = up->rpf.source_nexthop.interface->info;
assert(pim_ifp);
- pim_channel_oil_change_iif(pim, up->channel_oil,
- pim_ifp->mroute_vif_index,
- __PRETTY_FUNCTION__);
+ pim_upstream_update_use_rpt(up,
+ false /*update_mroute*/);
+ pim_upstream_mroute_iif_update(up->channel_oil, __func__);
if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags))
pim_upstream_keep_alive_timer_start(
up, pim->keep_alive_time);
} else if (up->upstream_addr.s_addr != INADDR_ANY) {
- rpf_result = pim_rpf_update(pim, up, NULL);
+ pim_upstream_update_use_rpt(up,
+ false /*update_mroute*/);
+ rpf_result = pim_rpf_update(pim, up, NULL, __func__);
if (rpf_result == PIM_RPF_FAILURE) {
if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
}
if (up->rpf.source_nexthop.interface) {
- pim_ifp = up->rpf.source_nexthop.interface->info;
- if (pim_ifp)
- pim_channel_oil_change_iif(
- pim, up->channel_oil,
- pim_ifp->mroute_vif_index,
- __PRETTY_FUNCTION__);
+ pim_upstream_mroute_iif_update(up->channel_oil,
+ __func__);
}
}
- listnode_add_sort(pim->upstream_list, up);
-
if (PIM_DEBUG_PIM_TRACE) {
zlog_debug(
"%s: Created Upstream %s upstream_addr %s ref count %d increment",
struct pim_upstream *up = NULL;
lookup.sg = *sg;
- up = hash_lookup(pim->upstream_hash, &lookup);
+ up = rb_pim_upstream_find(&pim->upstream_head, &lookup);
return up;
}
struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg,
- struct interface *incoming,
- int flags, const char *name)
+ struct interface *incoming,
+ int flags, const char *name)
{
- struct pim_upstream *up;
- struct pim_interface *pim_ifp;
-
- pim_ifp = incoming->info;
-
- up = pim_upstream_find(pim_ifp->pim, sg);
-
- if (up) {
- if (!(up->flags & flags)) {
- up->flags |= flags;
- up->ref_count++;
- if (PIM_DEBUG_PIM_TRACE)
- zlog_debug(
- "%s(%s): upstream %s ref count %d increment",
- __PRETTY_FUNCTION__, name, up->sg_str,
- up->ref_count);
- }
- } else
- up = pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name,
- NULL);
+ struct pim_interface *pim_ifp = incoming->info;
- return up;
+ return (pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name,
+ NULL));
}
void pim_upstream_ref(struct pim_upstream *up, int flags, const char *name)
{
+ /* when we go from non-FHR to FHR we need to re-eval traffic
+ * forwarding path
+ */
+ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) &&
+ PIM_UPSTREAM_FLAG_TEST_FHR(flags)) {
+ PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
+ }
+
+ /* re-eval joinDesired; clearing peer-msdp-sa flag can
+ * cause JD to change
+ */
+ if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags) &&
+ PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags)) {
+ PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up->flags);
+ pim_upstream_update_join_desired(up->pim, up);
+ }
+
up->flags |= flags;
++up->ref_count;
if (PIM_DEBUG_PIM_TRACE)
return up;
}
+/*
+ * Passed in up must be the upstream for ch. starch is NULL if no
+ * information
+ * This function is copied over from
+ * pim_upstream_evaluate_join_desired_interface but limited to
+ * parent (*,G)'s includes/joins.
+ */
+int pim_upstream_eval_inherit_if(struct pim_upstream *up,
+ struct pim_ifchannel *ch,
+ struct pim_ifchannel *starch)
+{
+ /* if there is an explicit prune for this interface we cannot
+ * add it to the OIL
+ */
+ if (ch) {
+ if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
+ return 0;
+ }
+
+ /* Check if the OIF can be inherited fron the (*,G) entry
+ */
+ if (starch) {
+ if (!pim_macro_ch_lost_assert(starch)
+ && pim_macro_chisin_joins_or_include(starch))
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* Passed in up must be the upstream for ch. starch is NULL if no
* information
* joins (*,G)
*/
if (starch) {
+ /* XXX: check on this with donald
+ * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in
+ * upstream flags?
+ */
+#if 0
if (PIM_IF_FLAG_TEST_S_G_RPT(starch->upstream->flags))
return 0;
+#endif
if (!pim_macro_ch_lost_assert(starch)
&& pim_macro_chisin_joins_or_include(starch))
return 0;
}
-/*
- Evaluate JoinDesired(S,G):
-
- JoinDesired(S,G) is true if there is a downstream (S,G) interface I
- in the set:
-
- inherited_olist(S,G) =
- joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
-
- JoinDesired(S,G) may be affected by changes in the following:
-
- pim_ifp->primary_address
- pim_ifp->pim_dr_addr
- ch->ifassert_winner_metric
- ch->ifassert_winner
- ch->local_ifmembership
- ch->ifjoin_state
- ch->upstream->rpf.source_nexthop.mrib_metric_preference
- ch->upstream->rpf.source_nexthop.mrib_route_metric
- ch->upstream->rpf.source_nexthop.interface
-
- See also pim_upstream_update_join_desired() below.
+/* Returns true if immediate OIL is empty and is used to evaluate
+ * JoinDesired. See pim_upstream_evaluate_join_desired.
*/
-int pim_upstream_evaluate_join_desired(struct pim_instance *pim,
+static bool pim_upstream_empty_immediate_olist(struct pim_instance *pim,
struct pim_upstream *up)
{
struct interface *ifp;
- struct pim_ifchannel *ch, *starch;
- struct pim_upstream *starup = up->parent;
- int ret = 0;
+ struct pim_ifchannel *ch;
FOR_ALL_INTERFACES (pim->vrf, ifp) {
if (!ifp->info)
continue;
ch = pim_ifchannel_find(ifp, &up->sg);
-
- if (starup)
- starch = pim_ifchannel_find(ifp, &starup->sg);
- else
- starch = NULL;
-
- if (!ch && !starch)
+ if (!ch)
continue;
- ret += pim_upstream_evaluate_join_desired_interface(up, ch,
- starch);
+ /* If we have even one immediate OIF we can return with
+ * not-empty
+ */
+ if (pim_upstream_evaluate_join_desired_interface(up, ch,
+ NULL /* starch */))
+ return false;
} /* scan iface channel list */
- return ret; /* false */
+ /* immediate_oil is empty */
+ return true;
+}
+
+
+static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream *up)
+{
+ return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags);
+}
+
+/*
+ * bool JoinDesired(*,G) {
+ * if (immediate_olist(*,G) != NULL)
+ * return TRUE
+ * else
+ * return FALSE
+ * }
+ *
+ * bool JoinDesired(S,G) {
+ * return( immediate_olist(S,G) != NULL
+ * OR ( KeepaliveTimer(S,G) is running
+ * AND inherited_olist(S,G) != NULL ) )
+ * }
+ */
+int pim_upstream_evaluate_join_desired(struct pim_instance *pim,
+ struct pim_upstream *up)
+{
+ bool empty_imm_oil;
+ bool empty_inh_oil;
+
+ empty_imm_oil = pim_upstream_empty_immediate_olist(pim, up);
+
+ /* (*,G) */
+ if (up->sg.src.s_addr == INADDR_ANY)
+ return !empty_imm_oil;
+
+ /* (S,G) */
+ if (!empty_imm_oil)
+ return true;
+ empty_inh_oil = pim_upstream_empty_inherited_olist(up);
+ if (!empty_inh_oil &&
+ (pim_upstream_is_kat_running(up) ||
+ pim_upstream_is_msdp_peer_sa(up)))
+ return true;
+
+ return false;
}
/*
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];
THREAD_OFF(up->t_rs_timer);
/* remove regiface from the OIL if it is there*/
pim_channel_del_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM, __func__);
/* clear the register state */
up->reg_state = PIM_REG_NOINFO;
PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags);
PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
if (up->reg_state == PIM_REG_NOINFO)
pim_register_join(up);
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
}
}
/* source is no longer active - pull the SA from MSDP's cache */
pim_msdp_sa_local_del(pim, &up->sg);
+ /* JoinDesired can change when KAT is started or stopped */
+ pim_upstream_update_join_desired(pim, up);
+
/* if entry was created because of activity we need to deref it */
if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
pim_upstream_fhr_kat_expiry(pim, up);
/* any time keepalive is started against a SG we will have to
* re-evaluate our active source database */
pim_msdp_sa_local_update(up);
+ /* JoinDesired can change when KAT is started or stopped */
+ pim_upstream_update_join_desired(up->pim, up);
}
/* MSDP on RP needs to know if a source is registerable to this RP */
* SwitchToSptDesired(S,G) return true once a single packet has been
* received for the source and group.
*/
-int pim_upstream_switch_to_spt_desired(struct pim_instance *pim,
+int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim,
struct prefix_sg *sg)
{
if (I_am_RP(pim, sg->grp))
case PIM_REG_JOIN_PENDING:
up->reg_state = PIM_REG_JOIN;
pim_channel_add_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM,
+ __func__);
pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/);
break;
case PIM_REG_JOIN:
if (!ch)
flag = PIM_OIF_FLAG_PROTO_STAR;
- pim_channel_add_oif(up->channel_oil, ifp, flag);
+ pim_channel_add_oif(up->channel_oil, ifp, flag,
+ __func__);
output_intf++;
}
}
* switch on a stick so turn on forwarding to just accept the
* incoming packets so we don't bother the other stuff!
*/
- if (output_intf)
- pim_upstream_switch(pim, up, PIM_UPSTREAM_JOINED);
- else
+ pim_upstream_update_join_desired(pim, up);
+
+ if (!output_intf)
forward_on(up);
return output_intf;
*/
void pim_upstream_find_new_rpf(struct pim_instance *pim)
{
- struct listnode *up_node;
- struct listnode *up_nextnode;
struct pim_upstream *up;
+ struct pim_rpf old;
+ enum pim_rpf_result rpf_result;
/*
* Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
*/
- for (ALL_LIST_ELEMENTS(pim->upstream_list, up_node, up_nextnode, up)) {
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
if (up->upstream_addr.s_addr == INADDR_ANY) {
if (PIM_DEBUG_PIM_TRACE)
zlog_debug(
zlog_debug(
"%s: Upstream %s without a path to send join, checking",
__PRETTY_FUNCTION__, up->sg_str);
- pim_rpf_update(pim, up, NULL);
+ old.source_nexthop.interface =
+ up->rpf.source_nexthop.interface;
+ rpf_result = pim_rpf_update(pim, up, &old, __func__);
+ if (rpf_result == PIM_RPF_CHANGED ||
+ (rpf_result == PIM_RPF_FAILURE &&
+ old.source_nexthop.interface))
+ pim_zebra_upstream_rpf_changed(pim, up, &old);
+ /* update kernel multicast forwarding cache (MFC) */
+ pim_upstream_mroute_iif_update(up->channel_oil,
+ __func__);
}
}
+ pim_zebra_update_all_interfaces(pim);
}
unsigned int pim_upstream_hash_key(const void *arg)
{
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);
*/
static bool pim_upstream_kat_start_ok(struct pim_upstream *up)
{
- struct pim_instance *pim = up->channel_oil->pim;
+ struct channel_oil *c_oil = up->channel_oil;
+ struct interface *ifp = up->rpf.source_nexthop.interface;
+ struct pim_interface *pim_ifp;
+
+ /* "iif == RPF_interface(S)" check is not easy to do as the info
+ * we get from the kernel/ASIC is really a "lookup/key hit".
+ * So we will do an approximate check here to avoid starting KAT
+ * because of (S,G,rpt) forwarding on a non-LHR.
+ */
+ if (!ifp)
+ return false;
- /* "iif == RPF_interface(S)" check has to be done by the kernel or hw
- * so we will skip that here */
- if (up->rpf.source_nexthop.interface &&
- pim_if_connected_to_source(up->rpf.source_nexthop.interface,
+ pim_ifp = ifp->info;
+ if (pim_ifp->mroute_vif_index != c_oil->oil.mfcc_parent)
+ return false;
+
+ if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
up->sg.src)) {
return true;
}
if ((up->join_state == PIM_UPSTREAM_JOINED)
- && !pim_upstream_empty_inherited_olist(up)) {
- /* XXX: I have added this RP check just for 3.2 and it's a
- * digression from
- * what rfc-4601 says. Till now we were only running KAT on FHR
- * and RP and
- * there is some angst around making the change to run it all
- * routers that
- * maintain the (S, G) state. This is tracked via CM-13601 and
- * MUST be
- * removed to handle spt turn-arounds correctly in a 3-tier clos
- */
- if (I_am_RP(pim, up->sg.grp))
- return true;
+ && !pim_upstream_empty_inherited_olist(up)) {
+ return true;
}
return false;
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;
continue;
pim_channel_add_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP, __func__);
}
}
const char *nlist)
{
struct pim_upstream *up;
- struct listnode *node;
struct prefix_list *np;
struct prefix g;
enum prefix_list_type apply_new;
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;
if (!nlist) {
pim_channel_del_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP, __func__);
continue;
}
g.u.prefix4 = up->sg.grp;
apply_new = prefix_list_apply(np, &g);
if (apply_new == PREFIX_DENY)
pim_channel_add_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP,
+ __func__);
else
pim_channel_del_oif(up->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP, __func__);
}
}
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);
}
* associated with an upstream
*/
#define PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE (1 << 19)
+/* By default as SG entry will use the SPT for forwarding traffic
+ * unless it was setup as a result of a Prune(S,G,rpt) from a
+ * downstream router and has JoinDesired(S,G) as False.
+ * This flag is only relevant for (S,G) entries.
+ */
+#define PIM_UPSTREAM_FLAG_MASK_USE_RPT (1 << 20)
#define PIM_UPSTREAM_FLAG_ALL 0xFFFFFFFF
#define PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
#define PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
#define PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(flags) ((flags) &PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE)
+#define PIM_UPSTREAM_FLAG_TEST_USE_RPT(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_USE_RPT)
#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
#define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM)
#define PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
#define PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
+#define PIM_UPSTREAM_FLAG_SET_USE_RPT(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_USE_RPT)
#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
#define PIM_UPSTREAM_FLAG_UNSET_MLAG_VXLAN(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
#define PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
#define PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE)
+#define PIM_UPSTREAM_FLAG_UNSET_USE_RPT(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_USE_RPT)
enum pim_upstream_state {
PIM_UPSTREAM_NOTJOINED,
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
*/
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*/
int64_t state_transition; /* Record current state uptime */
};
+static inline bool pim_upstream_is_kat_running(struct pim_upstream *up)
+{
+ return (up->t_ka_timer != NULL);
+}
+
struct pim_upstream *pim_upstream_find(struct pim_instance *pim,
struct prefix_sg *sg);
struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg,
int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up,
struct pim_ifchannel *ch,
struct pim_ifchannel *starch);
+int pim_upstream_eval_inherit_if(struct pim_upstream *up,
+ struct pim_ifchannel *ch,
+ struct pim_ifchannel *starch);
void pim_upstream_update_join_desired(struct pim_instance *pim,
struct pim_upstream *up);
void pim_upstream_keep_alive_timer_start(struct pim_upstream *up,
uint32_t time);
-int pim_upstream_switch_to_spt_desired(struct pim_instance *pim,
+int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim,
struct prefix_sg *sg);
-#define SwitchToSptDesired(pim, sg) pim_upstream_switch_to_spt_desired (pim, sg)
+#define SwitchToSptDesiredOnRp(pim, sg) pim_upstream_switch_to_spt_desired_on_rp (pim, sg)
int pim_upstream_is_sg_rpt(struct pim_upstream *up);
void pim_upstream_set_sptbit(struct pim_upstream *up,
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);
struct pim_upstream *up);
void pim_upstream_fill_static_iif(struct pim_upstream *up,
struct interface *incoming);
+void pim_upstream_update_use_rpt(struct pim_upstream *up,
+ bool update_mroute);
+void pim_upstream_reeval_use_rpt(struct pim_instance *pim);
#endif /* PIM_UPSTREAM_H */
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;
}
}
* for nht
*/
if (up)
- pim_rpf_update(vxlan_sg->pim, up, NULL);
+ pim_rpf_update(vxlan_sg->pim, up, NULL, __func__);
}
}
static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg *vxlan_sg)
{
- int vif_index;
-
/* update MFC with the new IIF */
pim_upstream_fill_static_iif(vxlan_sg->up, vxlan_sg->iif);
- vif_index = pim_if_find_vifindex_by_ifindex(vxlan_sg->pim,
- vxlan_sg->iif->ifindex);
- if (vif_index > 0)
- pim_scan_individual_oil(vxlan_sg->up->channel_oil,
- vif_index);
+ pim_upstream_mroute_iif_update(vxlan_sg->up->channel_oil, __func__);
if (PIM_DEBUG_VXLAN)
- zlog_debug("vxlan SG %s orig mroute-up updated with iif %s vifi %d",
+ zlog_debug("vxlan SG %s orig mroute-up updated with iif %s",
vxlan_sg->sg_str,
- vxlan_sg->iif?vxlan_sg->iif->name:"-", vif_index);
+ vxlan_sg->iif?vxlan_sg->iif->name:"-");
}
struct pim_upstream *up;
int flags = 0;
struct prefix nht_p;
+ struct pim_instance *pim = vxlan_sg->pim;
if (vxlan_sg->up) {
/* nothing to do */
pim_delete_tracked_nexthop(vxlan_sg->pim,
&nht_p, up, NULL, false);
}
+ /* We are acting FHR; clear out use_rpt setting if any */
+ pim_upstream_update_use_rpt(up, false /*update_mroute*/);
pim_upstream_ref(up, flags, __PRETTY_FUNCTION__);
vxlan_sg->up = up;
pim_vxlan_orig_mr_up_iif_update(vxlan_sg);
+ /* mute pimreg on origination mroutes */
+ if (pim->regiface)
+ pim_channel_update_oif_mute(up->channel_oil,
+ pim->regiface->info);
} else {
up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg,
vxlan_sg->iif, flags,
/* update the inherited OIL */
pim_upstream_inherited_olist(vxlan_sg->pim, up);
+ if (!up->channel_oil->installed)
+ pim_upstream_mroute_add(up->channel_oil, __func__);
}
static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg)
vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED;
pim_channel_add_oif(vxlan_sg->up->channel_oil,
- vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN);
+ vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN,
+ __func__);
}
static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg)
vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED;
pim_channel_del_oif(vxlan_sg->up->channel_oil,
- orig_oif, PIM_OIF_FLAG_PROTO_VXLAN);
+ orig_oif, PIM_OIF_FLAG_PROTO_VXLAN, __func__);
}
static inline struct interface *pim_vxlan_orig_mr_oif_get(
return (vxlan_sg->sg.src.s_addr != 0);
}
+static inline bool pim_vxlan_is_local_sip(struct pim_upstream *up)
+{
+ return (up->sg.src.s_addr != INADDR_ANY) &&
+ up->rpf.source_nexthop.interface &&
+ if_is_loopback_or_vrf(up->rpf.source_nexthop.interface);
+}
+
extern struct pim_vxlan *pim_vxlan_p;
extern struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim,
struct prefix_sg *sg);
nbr = pim_neighbor_find(old->source_nexthop.interface,
old->rpf_addr.u.prefix4);
if (nbr)
- pim_jp_agg_remove_group(nbr->upstream_jp_agg, up);
+ pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr);
/*
* We have detected a case where we might need
* so install it.
*/
if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil,
+ pim_upstream_mroute_add(up->channel_oil,
__PRETTY_FUNCTION__);
/*
up->channel_oil->oil_inherited_rescan = 0;
}
+ if (up->join_state == PIM_UPSTREAM_JOINED)
+ pim_jp_agg_switch_interface(old, &up->rpf, up);
+
if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(up->channel_oil,
+ __PRETTY_FUNCTION__);
}
/* FIXME can join_desired actually be changed by pim_rpf_update()
zclient_send_message(zclient);
}
-void pim_scan_individual_oil(struct channel_oil *c_oil, int in_vif_index)
-{
- struct in_addr vif_source;
- int input_iface_vif_index;
-
- pim_rp_set_upstream_addr(c_oil->pim, &vif_source,
- c_oil->oil.mfcc_origin,
- c_oil->oil.mfcc_mcastgrp);
-
- if (in_vif_index)
- input_iface_vif_index = in_vif_index;
- else {
- struct prefix src, grp;
-
- src.family = AF_INET;
- src.prefixlen = IPV4_MAX_BITLEN;
- src.u.prefix4 = vif_source;
- grp.family = AF_INET;
- grp.prefixlen = IPV4_MAX_BITLEN;
- grp.u.prefix4 = c_oil->oil.mfcc_mcastgrp;
-
- if (PIM_DEBUG_ZEBRA) {
- char source_str[INET_ADDRSTRLEN];
- char group_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin,
- source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp,
- group_str, sizeof(group_str));
- zlog_debug(
- "%s: channel_oil (%s,%s) upstream info is not present.",
- __PRETTY_FUNCTION__, source_str, group_str);
- }
- input_iface_vif_index = pim_ecmp_fib_lookup_if_vif_index(
- c_oil->pim, &src, &grp);
- }
-
- if (input_iface_vif_index < 1) {
- if (PIM_DEBUG_ZEBRA) {
- char source_str[INET_ADDRSTRLEN];
- char group_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin,
- source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp,
- group_str, sizeof(group_str));
- zlog_debug(
- "%s %s: could not find input interface(%d) for (S,G)=(%s,%s)",
- __FILE__, __PRETTY_FUNCTION__,
- c_oil->oil.mfcc_parent, source_str, group_str);
- }
- pim_mroute_del(c_oil, __PRETTY_FUNCTION__);
- return;
- }
-
- if (input_iface_vif_index == c_oil->oil.mfcc_parent) {
- if (!c_oil->installed)
- pim_mroute_add(c_oil, __PRETTY_FUNCTION__);
-
- /* RPF unchanged */
- return;
- }
-
- if (PIM_DEBUG_ZEBRA) {
- struct interface *old_iif = pim_if_find_by_vif_index(
- c_oil->pim, c_oil->oil.mfcc_parent);
- struct interface *new_iif = pim_if_find_by_vif_index(
- c_oil->pim, input_iface_vif_index);
- char source_str[INET_ADDRSTRLEN];
- char group_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str,
- sizeof(source_str));
- pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str,
- sizeof(group_str));
- zlog_debug(
- "%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d",
- __FILE__, __PRETTY_FUNCTION__, source_str, group_str,
- (old_iif) ? old_iif->name : "<old_iif?>",
- c_oil->oil.mfcc_parent,
- (new_iif) ? new_iif->name : "<new_iif?>",
- input_iface_vif_index);
- }
-
- /* new iif loops to existing oif ? */
- if (c_oil->oil.mfcc_ttls[input_iface_vif_index]) {
- struct interface *new_iif = pim_if_find_by_vif_index(
- c_oil->pim, input_iface_vif_index);
-
- if (PIM_DEBUG_ZEBRA) {
- char source_str[INET_ADDRSTRLEN];
- char group_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin,
- source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp,
- group_str, sizeof(group_str));
- zlog_debug(
- "%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d",
- __FILE__, __PRETTY_FUNCTION__, source_str,
- group_str,
- (new_iif) ? new_iif->name : "<new_iif?>",
- input_iface_vif_index);
- }
- }
-
- /* update iif vif_index */
- pim_channel_oil_change_iif(c_oil->pim, c_oil, input_iface_vif_index,
- __PRETTY_FUNCTION__);
- pim_mroute_add(c_oil, __PRETTY_FUNCTION__);
-}
-
void pim_scan_oil(struct pim_instance *pim)
{
- struct listnode *node;
- struct listnode *nextnode;
struct channel_oil *c_oil;
- ifindex_t ifindex;
- int vif_index = 0;
pim->scan_oil_last = pim_time_monotonic_sec();
++pim->scan_oil_events;
- for (ALL_LIST_ELEMENTS(pim->channel_oil_list, node, nextnode, c_oil)) {
- if (c_oil->up && c_oil->up->rpf.source_nexthop.interface) {
- ifindex = c_oil->up->rpf.source_nexthop
- .interface->ifindex;
- vif_index =
- pim_if_find_vifindex_by_ifindex(pim, ifindex);
- /* Pass Current selected NH vif index to mroute
- * download */
- if (vif_index)
- pim_scan_individual_oil(c_oil, vif_index);
- } else
- pim_scan_individual_oil(c_oil, 0);
- }
+ frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil)
+ pim_upstream_mroute_iif_update(c_oil, __func__);
}
static int on_rpf_cache_refresh(struct thread *t)
source->source_addr, sg.grp)) {
/*Create a dummy channel oil */
source->source_channel_oil = pim_channel_oil_add(
- pim, &sg, MAXVIFS, __PRETTY_FUNCTION__);
+ pim, &sg, __PRETTY_FUNCTION__);
}
else {
}
source->source_channel_oil =
pim_channel_oil_add(
- pim, &sg, MAXVIFS,
+ pim, &sg,
__PRETTY_FUNCTION__);
}
source->source_channel_oil =
pim_channel_oil_add(
- pim, &sg, input_iface_vif_index,
+ pim, &sg,
__PRETTY_FUNCTION__);
if (!source->source_channel_oil) {
if (PIM_DEBUG_IGMP_TRACE) {
if (PIM_I_am_DR(pim_oif)) {
result = pim_channel_add_oif(source->source_channel_oil,
group->group_igmp_sock->interface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP, __func__);
if (result) {
if (PIM_DEBUG_MROUTE) {
zlog_warn("%s: add_oif() failed with return=%d",
pim_channel_del_oif(source->source_channel_oil,
group->group_igmp_sock->interface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP, __func__);
return;
}
*/
result = pim_channel_del_oif(source->source_channel_oil,
group->group_igmp_sock->interface,
- PIM_OIF_FLAG_PROTO_IGMP);
+ PIM_OIF_FLAG_PROTO_IGMP,
+ __func__);
if (result) {
if (PIM_DEBUG_IGMP_TRACE)
zlog_debug(
if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
mask = PIM_OIF_FLAG_PROTO_IGMP;
- pim_channel_add_oif(up->channel_oil, ch->interface, mask);
+ pim_channel_add_oif(up->channel_oil, ch->interface,
+ mask, __func__);
}
void pim_forward_stop(struct pim_ifchannel *ch, bool install_it)
*/
if (pim_upstream_evaluate_join_desired_interface(up, ch, ch->parent))
pim_channel_add_oif(up->channel_oil, ch->interface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM, __func__);
else
pim_channel_del_oif(up->channel_oil, ch->interface,
- PIM_OIF_FLAG_PROTO_PIM);
+ PIM_OIF_FLAG_PROTO_PIM, __func__);
if (install_it && !up->channel_oil->installed)
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
}
void pim_zebra_zclient_update(struct vty *vty)
zclient_lookup_failed(zlookup);
return -1;
}
+
+ if (command == ZEBRA_ERROR) {
+ enum zebra_error_types error;
+
+ zapi_error_decode(s, &error);
+ /* Do nothing with it for now */
+ return -1;
+ }
}
raddr.s_addr = stream_get_ipv4(s);
s = zlookup->obuf;
stream_reset(s);
- zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, pim->vrf_id);
+ zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB,
+ pim->vrf->vrf_id);
stream_put_in_addr(s, &addr);
stream_putw_at(s, 0, stream_get_endp(s));
noinst_PROGRAMS += pimd/test_igmpv3_join
dist_examples_DATA += pimd/pimd.conf.sample
vtysh_scan += $(top_srcdir)/pimd/pim_cmd.c
-man8 += $(MANBUILD)/pimd.8
+man8 += $(MANBUILD)/frr-pimd.8
man8 += $(MANBUILD)/mtracebis.8
endif
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
%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
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
{
# 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
}
%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}/*
%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>
VRF_CMD_HELP_STR)
{
struct list *input;
+ int ret;
input = list_new();
if (vrf) {
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)
#include "thread.h"
#include "command.h"
#include "memory.h"
-#include "memory_vty.h"
#include "prefix.h"
#include "filter.h"
#include "keychain.h"
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)
if SNMP
module_LTLIBRARIES += ripd/ripd_snmp.la
endif
-man8 += $(MANBUILD)/ripd.8
+man8 += $(MANBUILD)/frr-ripd.8
endif
ripd_librip_a_SOURCES = \
VRF_CMD_HELP_STR)
{
struct list *input;
+ int ret;
input = list_new();
if (vrf) {
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)
#include "vty.h"
#include "command.h"
#include "memory.h"
-#include "memory_vty.h"
#include "thread.h"
#include "log.h"
#include "prefix.h"
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)
$(top_srcdir)/ripngd/ripng_debug.c \
$(top_srcdir)/ripngd/ripngd.c \
# end
-man8 += $(MANBUILD)/ripngd.8
+man8 += $(MANBUILD)/frr-ripngd.8
endif
ripngd_libripng_a_SOURCES = \
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;
api_nh->bh_type = nh->bh_type;
break;
}
+
+ if (nh->nh_label && nh->nh_label->num_labels > 0) {
+ int j;
+
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
+
+ api_nh->label_num = nh->nh_label->num_labels;
+ for (j = 0; j < nh->nh_label->num_labels; j++)
+ api_nh->labels[j] = nh->nh_label->label[j];
+ }
+
i++;
}
api.nexthop_num = i;
sbin_PROGRAMS += sharpd/sharpd
dist_examples_DATA += sharpd/sharpd.conf.sample
vtysh_scan += $(top_srcdir)/sharpd/sharp_vty.c
-man8 += $(MANBUILD)/sharpd.8
+man8 += $(MANBUILD)/frr-sharpd.8
endif
sharpd_libsharp_a_SOURCES = \
d none @mandir@/man1=$DESTDIR/@mandir@/man1 0755 root bin
f none @mandir@/man1/vtysh.1=$DESTDIR/@mandir@/man1/vtysh.1 0644 root bin
d none @mandir@/man8=$DESTDIR/@mandir@/man8 0755 root bin
-f none @mandir@/man8/bgpd.8=$DESTDIR/@mandir@/man8/bgpd.8 0644 root bin
-f none @mandir@/man8/ospf6d.8=$DESTDIR/@mandir@/man8/ospf6d.8 0644 root bin
-f none @mandir@/man8/ospfd.8=$DESTDIR/@mandir@/man8/ospfd.8 0644 root bin
-f none @mandir@/man8/ripd.8=$DESTDIR/@mandir@/man8/ripd.8 0644 root bin
-f none @mandir@/man8/ripngd.8=$DESTDIR/@mandir@/man8/ripngd.8 0644 root bin
-f none @mandir@/man8/zebra.8=$DESTDIR/@mandir@/man8/zebra.8 0644 root bin
-f none @mandir@/man8/isisd.8=$DESTDIR/@mandir@/man8/isisd.8 0644 root bin
+f none @mandir@/man8/frr-bgpd.8=$DESTDIR/@mandir@/man8/bgpd.8 0644 root bin
+f none @mandir@/man8/frr-ospf6d.8=$DESTDIR/@mandir@/man8/frr-ospf6d.8 0644 root bin
+f none @mandir@/man8/frr-ospfd.8=$DESTDIR/@mandir@/man8/frr-ospfd.8 0644 root bin
+f none @mandir@/man8/frr-ripd.8=$DESTDIR/@mandir@/man8/frr-ripd.8 0644 root bin
+f none @mandir@/man8/frr-ripngd.8=$DESTDIR/@mandir@/man8/frr-ripngd.8 0644 root bin
+f none @mandir@/man8/frr-zebra.8=$DESTDIR/@mandir@/man8/frr-zebra.8 0644 root bin
+f none @mandir@/man8/frr-isisd.8=$DESTDIR/@mandir@/man8/frr-isisd.8 0644 root bin
--- /dev/null
+/*
+ * Staticd debug related functions
+ * Copyright (C) 2019 Volta Networks Inc.
+ * Mark Stapp
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "lib/command.h"
+#include "lib/debug.h"
+
+#include "static_debug.h"
+
+/*
+ * Debug infra: a debug struct for each category, and a corresponding
+ * string.
+ */
+
+/* clang-format off */
+struct debug static_dbg_events = {0, "Staticd events"};
+
+struct debug *static_debug_arr[] = {
+ &static_dbg_events
+};
+
+const char *static_debugs_conflines[] = {
+ "debug static events"
+};
+/* clang-format on */
+
+
+/*
+ * Set or unset all staticd debugs
+ *
+ * flags
+ * The flags to set
+ *
+ * set
+ * Whether to set or unset the specified flags
+ */
+static void static_debug_set_all(uint32_t flags, bool set)
+{
+ for (unsigned int i = 0; i < array_size(static_debug_arr); i++) {
+ DEBUG_FLAGS_SET(static_debug_arr[i], flags, set);
+
+ /* if all modes have been turned off, don't preserve options */
+ if (!DEBUG_MODE_CHECK(static_debug_arr[i], DEBUG_MODE_ALL))
+ DEBUG_CLEAR(static_debug_arr[i]);
+ }
+}
+
+static int static_debug_config_write_helper(struct vty *vty, bool config)
+{
+ uint32_t mode = DEBUG_MODE_ALL;
+
+ if (config)
+ mode = DEBUG_MODE_CONF;
+
+ for (unsigned int i = 0; i < array_size(static_debug_arr); i++)
+ if (DEBUG_MODE_CHECK(static_debug_arr[i], mode))
+ vty_out(vty, "%s\n", static_debugs_conflines[i]);
+
+ return 0;
+}
+
+int static_config_write_debug(struct vty *vty)
+{
+ return static_debug_config_write_helper(vty, true);
+}
+
+int static_debug_status_write(struct vty *vty)
+{
+ return static_debug_config_write_helper(vty, false);
+}
+
+/*
+ * Set debugging status.
+ *
+ * vtynode
+ * vty->node
+ *
+ * onoff
+ * Whether to turn the specified debugs on or off
+ *
+ * events
+ * Debug general internal events
+ *
+ */
+void static_debug_set(int vtynode, bool onoff, bool events)
+{
+ uint32_t mode = DEBUG_NODE2MODE(vtynode);
+
+ if (events)
+ DEBUG_MODE_SET(&static_dbg_events, mode, onoff);
+}
+
+/*
+ * Debug lib initialization
+ */
+
+struct debug_callbacks static_dbg_cbs = {
+ .debug_set_all = static_debug_set_all
+};
+
+void static_debug_init(void)
+{
+ debug_init(&static_dbg_cbs);
+}
--- /dev/null
+/*
+ * Staticd debug related functions
+ * Copyright (C) 2019 Volta Networks Inc.
+ * Mark Stapp
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _STATIC_DEBUG_H
+#define _STATIC_DEBUG_H
+
+
+#include <zebra.h>
+
+#include "lib/debug.h"
+
+/* staticd debugging records */
+struct debug static_dbg_events;
+
+/*
+ * Initialize staticd debugging.
+ *
+ * Installs VTY commands and registers callbacks.
+ */
+void static_debug_init(void);
+
+/*
+ * Print staticd debugging configuration.
+ *
+ * vty
+ * VTY to print debugging configuration to.
+ */
+int static_config_write_debug(struct vty *vty);
+
+/*
+ * Print staticd debugging configuration, human readable form.
+ *
+ * vty
+ * VTY to print debugging configuration to.
+ */
+int static_debug_status_write(struct vty *vty);
+
+/*
+ * Set debugging status.
+ *
+ * vtynode
+ * vty->node
+ *
+ * onoff
+ * Whether to turn the specified debugs on or off
+ *
+ * events
+ * Debug general internal events
+ *
+ */
+void static_debug_set(int vtynode, bool onoff, bool events);
+
+
+#endif /* _STATIC_DEBUG_H */
#include "static_vty.h"
#include "static_routes.h"
#include "static_zebra.h"
+#include "static_debug.h"
char backup_config_file[256];
master = frr_init();
access_list_init();
+ static_debug_init();
static_vrf_init();
static_zebra_init();
#include "static_memory.h"
#include "static_vty.h"
#include "static_routes.h"
+#include "static_debug.h"
#ifndef VTYSH_EXTRACT_PL
#include "staticd/static_vty_clippy.c"
#endif
+
+#define STATICD_STR "Static route daemon\n"
+
static struct static_vrf *static_vty_get_unknown_vrf(struct vty *vty,
const char *vrf_name)
{
from_str, gate_str, ifname, flag, tag_str, distance_str, label,
table_str, false);
}
+DEFPY(debug_staticd,
+ debug_staticd_cmd,
+ "[no] debug static [{events$events}]",
+ NO_STR
+ DEBUG_STR
+ STATICD_STR
+ "Debug events\n")
+{
+ /* If no specific category, change all */
+ if (strmatch(argv[argc - 1]->text, "static"))
+ static_debug_set(vty->node, !no, true);
+ else
+ static_debug_set(vty->node, !no, !!events);
-DEFUN_NOSH (show_debugging_staticd,
- show_debugging_staticd_cmd,
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_debugging_static,
+ show_debugging_static_cmd,
"show debugging [static]",
SHOW_STR
DEBUG_STR
"Static Information\n")
{
- vty_out(vty, "Static debugging status\n");
+ vty_out(vty, "Staticd debugging status\n");
+
+ static_debug_status_write(vty);
return CMD_SUCCESS;
}
+static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
+
void static_vty_init(void)
{
+ install_node(&debug_node, static_config_write_debug);
+
install_element(CONFIG_NODE, &ip_mroute_dist_cmd);
install_element(CONFIG_NODE, &ip_route_blackhole_cmd);
install_element(CONFIG_NODE, &ipv6_route_cmd);
install_element(VRF_NODE, &ipv6_route_vrf_cmd);
- install_element(VIEW_NODE, &show_debugging_staticd_cmd);
+ install_element(VIEW_NODE, &show_debugging_static_cmd);
+ install_element(VIEW_NODE, &debug_staticd_cmd);
+ install_element(CONFIG_NODE, &debug_staticd_cmd);
static_list = list_new();
static_list->cmp = (int (*)(void *, void *))static_list_compare;
continue;
api_nh->vrf_id = si->nh_vrf_id;
- api_nh->onlink = si->onlink;
+ if (si->onlink)
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
si->state = STATIC_SENT_TO_ZEBRA;
if (si->snh_label.num_labels) {
int i;
- SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
api_nh->label_num = si->snh_label.num_labels;
for (i = 0; i < api_nh->label_num; i++)
api_nh->labels[i] = si->snh_label.label[i];
sbin_PROGRAMS += staticd/staticd
dist_examples_DATA += staticd/staticd.conf.sample
vtysh_scan += $(top_srcdir)/staticd/static_vty.c
-man8 += $(MANBUILD)/staticd.8
+man8 += $(MANBUILD)/frr-staticd.8
endif
staticd_libstatic_a_SOURCES = \
+ staticd/static_debug.c \
staticd/static_memory.c \
staticd/static_nht.c \
staticd/static_routes.c \
# end
noinst_HEADERS += \
+ staticd/static_debug.h \
staticd/static_memory.h \
staticd/static_nht.h \
staticd/static_zebra.h \
/lib/test_timer_performance
/lib/test_ttable
/lib/test_typelist
+/lib/test_versioncmp
/lib/test_zlog
/lib/test_zmq
/ospf6d/test_lsdb
if (fileno(stdout) >= 0)
tty = isatty(fileno(stdout));
- if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT))
+ if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0)
return -1;
peer = peer_create_accept(bgp);
parse_ret = bgp_mp_unreach_parse(&attr_args, &nlri);
break;
case BGP_ATTR_PREFIX_SID:
- parse_ret = bgp_attr_prefix_sid(t->len, &attr_args, &nlri);
+ parse_ret = bgp_attr_prefix_sid(&attr_args, &nlri);
break;
default:
printf("unknown type");
if (fileno(stdout) >= 0)
tty = isatty(fileno(stdout));
- if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT))
+ if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0)
return -1;
peer = peer_create_accept(bgp);
vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN);
- if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT))
+ if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT) < 0)
return -1;
peer = peer_create_accept(bgp);
#include "vty.h"
#include "command.h"
#include "memory.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
extern void test_init(void);
/* Library inits. */
cmd_init(1);
vty_init(master, false);
- memory_init();
+ lib_cmd_init();
yang_init();
nb_init(master, NULL, 0);
#include "vty.h"
#include "command.h"
#include "memory.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
#include "log.h"
#include "common_cli.h"
cmd_domainname_set("test.domain");
vty_init(master, false);
- memory_init();
+ lib_cmd_init();
yang_init();
nb_init(master, NULL, 0);
#include "lib/json.h"
#include "lib/keychain.h"
#include "lib/lib_errors.h"
+#include "lib/lib_vty.h"
#include "lib/libfrr.h"
#include "lib/libospf.h"
#include "lib/linklist.h"
#include "lib/log.h"
#include "lib/md5.h"
#include "lib/memory.h"
-#include "lib/memory_vty.h"
#include "lib/mlag.h"
#include "lib/module.h"
#include "lib/monotime.h"
#include "vty.h"
#include "command.h"
#include "memory.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
#include "log.h"
#include "northbound.h"
cmd_init(1);
cmd_hostname_set("test");
vty_init(master, false);
- memory_init();
+ lib_cmd_init();
yang_init();
nb_init(master, modules, array_size(modules));
#include <zebra.h>
#include <memory.h>
-#include <memory_vty.h>
+#include <lib_vty.h>
#include <buffer.h>
struct thread_master *master;
char junk[3];
char c = 'a';
- memory_init();
+ lib_cmd_init();
if ((argc != 2) || (sscanf(argv[1], "%d%1s", &n, junk) != 1)) {
fprintf(stderr, "Usage: %s <number of chars to simulate>\n",
#include "getopt.h"
#include "privs.h"
#include "memory.h"
-#include "memory_vty.h"
+#include "lib_vty.h"
zebra_capabilities_t _caps_p[] = {
ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_DAC_OVERRIDE,
}
/* Library inits. */
- memory_init();
+ lib_cmd_init();
zprivs_preinit(&test_privs);
zprivs_init(&test_privs);
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 {
};
SHA256_Update(&ctx, &hashitem, sizeof(hashitem));
i++;
- assert(i < count);
+ assert(i <= count);
}
SHA256_Final(hash, &ctx);
--- /dev/null
+/*
+ * frr_version_cmp() tests
+ * Copyright (C) 2018 David Lamparter for NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+#include <defaults.h>
+
+static const char *rel(int x)
+{
+ if (x < 0)
+ return "<";
+ if (x > 0)
+ return ">";
+ return "==";
+}
+
+static int fail;
+
+static void compare(const char *a, const char *b, int expect)
+{
+ int result = frr_version_cmp(a, b);
+
+ if (expect == result)
+ printf("\"%s\" %s \"%s\"\n", a, rel(result), b);
+ else {
+ printf("\"%s\" %s \"%s\", expected %s!\n", a, rel(result), b,
+ rel(expect));
+ fail = 1;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ compare("", "", 0);
+ compare("1", "1", 0);
+ compare("1.0", "1.00", 0);
+ compare("10.0", "1", 1);
+ compare("10.0", "2", 1);
+ compare("2.1", "10.0", -1);
+ compare("1.1.1", "1.1.0", 1);
+ compare("1.0a", "1.0", 1);
+ compare("1.0a", "1.0b", -1);
+ compare("1.0a10", "1.0a2", 1);
+ compare("1.00a2", "1.0a2", 0);
+ compare("1.00a2", "1.0a3", -1);
+ compare("1.0-dev", "1.0", 1);
+ compare("1.0~foo", "1.0", -1);
+ compare("1.0~1", "1.0~0", 1);
+ compare("1.00~1", "1.0~0", 1);
+ printf("final tally: %s\n", fail ? "FAILED" : "ok");
+ return fail;
+}
--- /dev/null
+import frrtest
+
+class TestVersionCmp(frrtest.TestMultiOut):
+ program = './test_versioncmp'
+
+TestVersionCmp.exit_cleanly()
tests/lib/test_timer_performance \
tests/lib/test_ttable \
tests/lib/test_typelist \
+ tests/lib/test_versioncmp \
tests/lib/test_zlog \
tests/lib/test_graph \
tests/lib/cli/test_cli \
tests_lib_test_typelist_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_typelist_LDADD = $(ALL_TESTS_LDADD)
tests_lib_test_typelist_SOURCES = tests/lib/test_typelist.c tests/helpers/c/prng.c
+tests_lib_test_versioncmp_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_versioncmp_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_versioncmp_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_versioncmp_SOURCES = tests/lib/test_versioncmp.c
tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD)
tests/lib/test_ttable.py \
tests/lib/test_ttable.refout \
tests/lib/test_typelist.py \
+ tests/lib/test_versioncmp.py \
tests/lib/test_zlog.py \
tests/lib/test_graph.py \
tests/lib/test_graph.refout \
C>* 192.168.9.0/26 is directly connected, r1-eth9, XX:XX:XX
O 192.168.0.0/24 [110/10] is directly connected, r1-eth0, XX:XX:XX
O 192.168.3.0/26 [110/10] is directly connected, r1-eth3, XX:XX:XX
+S>* 1.1.1.1/32 [1/0] is directly connected, r1-eth0, XX:XX:XX
+S>* 1.1.1.2/32 [1/0] is directly connected, r1-eth1, XX:XX:XX
S>* 4.5.6.10/32 [1/0] via 192.168.0.2, r1-eth0, XX:XX:XX
S>* 4.5.6.11/32 [1/0] via 192.168.0.2, r1-eth0, XX:XX:XX
S>* 4.5.6.12/32 [1/0] is directly connected, r1-eth0, XX:XX:XX
# by zebra but not installed.
ip route 4.5.6.15/32 192.168.0.2 255
ipv6 route 4:5::6:15/128 fc00:0:0:0::2 255
+
+# Routes to put into a nexthop-group
+ip route 1.1.1.1/32 r1-eth0
+ip route 1.1.1.2/32 r1-eth1
+
!
interface r1-eth0
description to sw0 - no routing protocol
if net['r%s' % i].daemon_available('ldpd'):
# Only test LDPd if it's installed and Kernel >= 4.5
net['r%s' % i].loadConf('ldpd', '%s/r%s/ldpd.conf' % (thisDir, i))
+ net['r%s' % i].loadConf('sharpd')
net['r%s' % i].startRouter()
# For debugging after starting Quagga/FRR daemons, uncomment the next line
# For debugging after starting FRR/Quagga daemons, uncomment the next line
## CLI(net)
+def test_nexthop_groups():
+ global fatal_error
+ global net
+
+ # Skip if previous fatal error condition is raised
+ if (fatal_error != ""):
+ pytest.skip(fatal_error)
+
+ print("\n\n** Verifying Nexthop Groups")
+ print("******************************************\n")
+
+ # Create a lib nexthop-group
+ net["r1"].cmd('vtysh -c "c t" -c "nexthop-group red" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"')
+
+ # Create with sharpd using nexthop-group
+ net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.1 nexthop-group red 1"')
+
+ # Verify route and that zebra created NHGs for and they are valid/installed
+ output = net["r1"].cmd('vtysh -c "show ip route 2.2.2.1/32 nexthop-group"')
+ match = re.search(r"Nexthop Group ID: (\d+)", output);
+ assert match is not None, "Nexthop Group ID not found for sharpd route 2.2.2.1/32"
+
+ nhe_id = int(match.group(1))
+
+ output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhe_id)
+ match = re.search(r"Valid", output)
+ assert match is not None, "Nexthop Group ID=%d not marked Valid" % nhe_id
+
+ match = re.search(r"Installed", output)
+ assert match is not None, "Nexthop Group ID=%d not marked Installed" % nhe_id
def test_rip_status():
global fatal_error
--- /dev/null
+router bgp 65000
+ neighbor 192.168.255.2 remote-as 65001
+ address-family ipv4 unicast
+ neighbor 192.168.255.2 default-originate route-map default
+ exit-address-family
+!
+route-map default permit 10
+ set metric 123
+!
--- /dev/null
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
--- /dev/null
+router bgp 65001
+ neighbor 192.168.255.1 remote-as 65000
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
--- /dev/null
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
--- /dev/null
+#!/usr/bin/env python
+
+#
+# bgp_default-originate_route-map.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+bgp_default-originate_route-map.py:
+
+Test if works the following commands:
+router bgp 65031
+ address-family ipv4 unicast
+ neighbor 192.168.255.2 default-originate route-map default
+
+route-map default permit 10
+ set metric 123
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, '../'))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+class TemplateTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+
+ for routern in range(1, 3):
+ tgen.add_router('r{}'.format(routern))
+
+ switch = tgen.add_switch('s1')
+ switch.add_link(tgen.gears['r1'])
+ switch.add_link(tgen.gears['r2'])
+
+def setup_module(mod):
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.iteritems(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, '{}/zebra.conf'.format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP,
+ os.path.join(CWD, '{}/bgpd.conf'.format(rname))
+ )
+
+ tgen.start_router()
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+def test_bgp_default_originate_route_map():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears['r2']
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ '192.168.255.1': {
+ 'bgpState': 'Established',
+ 'addressFamilyInfo': {
+ 'ipv4Unicast': {
+ 'acceptedPrefixCounter': 1
+ }
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_default_route_has_metric(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
+ expected = {
+ 'paths': [
+ {
+ 'med': 123
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed to see bgp convergence in "{}"'.format(router)
+
+ test_func = functools.partial(_bgp_default_route_has_metric, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed to see applied metric for default route in "{}"'.format(router)
+
+if __name__ == '__main__':
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
--- /dev/null
+router bgp 65000
+ neighbor 192.168.255.2 remote-as 65000
+ address-family ipv4 unicast
+ redistribute connected
+!
--- /dev/null
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r5-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
--- /dev/null
+router bgp 65000
+ bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
--- /dev/null
+!
+interface r6-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
# 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
import json
import time
import pytest
+import functools
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, '../'))
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')
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()
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:]
if topotest.version_cmp(krel, '4.15') >= 0 and \
topotest.version_cmp(krel, '4.18') <= 0:
l3mdev_accept = 1
- else:
- l3mdev_accept = 0
+
+ if topotest.version_cmp(krel, '5.0') >= 0:
+ l3mdev_accept = 1
+
logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept))
#check for mpls
if tgen.hasmpls != True:
mem_z[rtr] = {'value': int(found.group(1)), 'units': found.group(2)}
mem_b[rtr] = {'value': int(found.group(3)), 'units': found.group(4)}
-luCommand('ce1', 'vtysh -c "sharp data nexthop"', 'sharpd is not running', 'none','check if sharpd running')
-doSharp = True
+luCommand('ce1', 'vtysh -c "show mem"', 'qmem sharpd', 'none','check if sharpd running')
+doSharp = False
found = luLast()
if ret != False and found != None:
if len(found.group()):
- luCommand('ce1', 'vtysh -c "sharp data nexthop"', 'sharpd is not running', 'pass','sharpd NOT running, skipping test')
- doSharp = False
+ doSharp = True
-if doSharp == True:
+if doSharp != True:
+ luCommand('ce1', 'vtysh -c "sharp data nexthop"', '.', 'pass','sharpd NOT running, skipping test')
+else:
luCommand('ce1', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.1 {}"'.format(num),'','pass','Adding {} routes'.format(num))
luCommand('ce2', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.2 {}"'.format(num),'','pass','Adding {} routes'.format(num))
rtrs = ['ce1', 'ce2', 'ce3']
--- /dev/null
+!
+router bgp 65001
+ neighbor 192.168.255.1 remote-as 65002
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.255.1 maximum-prefix-out 2
+ exit-address-family
+ !
+!
--- /dev/null
+!
+interface lo
+ ip address 172.16.255.250/32
+ ip address 172.16.255.251/32
+ ip address 172.16.255.252/32
+ ip address 172.16.255.253/32
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.2/30
+!
+ip forwarding
+!
--- /dev/null
+!
+router bgp 65002
+ neighbor 192.168.255.2 remote-as 65001
+ exit-address-family
+ !
+!
--- /dev/null
+!
+interface r2-eth0
+ ip address 192.168.255.1/30
+!
+ip forwarding
+!
--- /dev/null
+#!/usr/bin/env python
+
+#
+# test_bgp_maximum_prefix_out.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Test if `neighbor <X.X.X.X> maximum-prefix-out <Y>` is working
+correctly.
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, '../'))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+class TemplateTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+
+ for routern in range(1, 3):
+ tgen.add_router('r{}'.format(routern))
+
+ switch = tgen.add_switch('s1')
+ switch.add_link(tgen.gears['r1'])
+ switch.add_link(tgen.gears['r2'])
+
+def setup_module(mod):
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.iteritems(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, '{}/zebra.conf'.format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP,
+ os.path.join(CWD, '{}/bgpd.conf'.format(rname))
+ )
+
+ tgen.start_router()
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+def test_bgp_maximum_prefix_out():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears['r2']
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ expected = {
+ '192.168.255.2': {
+ 'bgpState': 'Established',
+ 'addressFamilyInfo': {
+ 'ipv4Unicast': {
+ 'acceptedPrefixCounter': 2
+ }
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(router)
+
+if __name__ == '__main__':
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
# 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
import json
import time
import pytest
+import functools
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, '../'))
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:]
import json
from functools import partial
import pytest
+import platform
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
router_list = tgen.routers()
logger.info('Testing with VRF Lite support')
+ krel = platform.release()
+
+ # May need to adjust handling of vrf traffic depending on kernel version
+ l3mdev_accept = 0
+ if topotest.version_cmp(krel, '4.15') >= 0 and \
+ topotest.version_cmp(krel, '4.18') <= 0:
+ l3mdev_accept = 1
+
+ if topotest.version_cmp(krel, '5.0') >= 0:
+ l3mdev_accept = 1
+
+ logger.info('krel \'{0}\' setting net.ipv4.tcp_l3mdev_accept={1}'.format(
+ krel, l3mdev_accept))
cmds = ['ip link add {0}-cust1 type vrf table 1001',
'ip link add loop1 type dummy',
for cmd in cmds:
output = tgen.net[rname].cmd(cmd.format(rname))
+ output = tgen.net[rname].cmd('sysctl -n net.ipv4.tcp_l3mdev_accept')
+ logger.info(
+ 'router {0}: existing tcp_l3mdev_accept was {1}'.format(
+ rname, output))
+
+ if l3mdev_accept:
+ output = tgen.net[rname].cmd(
+ 'sysctl -w net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept))
+
for rname, router in router_list.iteritems():
router.load_config(
TopoRouter.RD_ZEBRA,
from subprocess import Popen
from functools import wraps
from re import search as re_search
+from tempfile import mkdtemp
import StringIO
import os
run_cfg_file = "{}/{}/frr.sav".format(TMPDIR, rname)
init_cfg_file = "{}/{}/frr_json_initial.conf".format(TMPDIR, rname)
- command = "/usr/lib/frr/frr-reload.py --input {} --test {} > {}". \
- format(run_cfg_file, init_cfg_file, dname)
+
+ tempdir = mkdtemp()
+ with open(os.path.join(tempdir, 'vtysh.conf'), 'w') as fd:
+ pass
+
+ command = "/usr/lib/frr/frr-reload.py --confdir {} --input {} --test {} > {}". \
+ format(tempdir, run_cfg_file, init_cfg_file, dname)
result = call(command, shell=True, stderr=SUB_STDOUT,
stdout=SUB_PIPE)
+ os.unlink(os.path.join(tempdir, 'vtysh.conf'))
+ os.rmdir(tempdir)
+
# Assert if command fail
if result > 0:
logger.error("Delta file creation failed. Command executed %s",
tgen = get_topogen()
if _lt != None and _lt.scriptdir != None and _lt.prestarthooksuccess == True:
+ luShowResults(logger.info)
print(luFinish())
# This function tears down the whole topology.
def luResult(target, success, str, logstr=None):
return LUtil.result(target, success, str, logstr)
+def luShowResults(prFunction):
+ printed = 0
+ sf = open(LUtil.fsum_name, 'r')
+ for line in sf:
+ printed+=1
+ prFunction(line.rstrip())
+ sf.close()
+
def luShowFail():
printed = 0
sf = open(LUtil.fsum_name, 'r')
--- /dev/null
+fc00:1111:1111:1111::/64 nhid XXXX via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium
+fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
-
+import platform
#####################################################
##
# Now compare the routing tables (after substituting link-local addresses)
for i in range(1, 5):
- refTableFile = os.path.join(CWD, 'r{}/ip_6_address.ref'.format(i))
+ if topotest.version_cmp(platform.release(), '5.3') < 0:
+ refTableFile = os.path.join(CWD, 'r{}/ip_6_address.ref'.format(i))
+ else:
+ refTableFile = os.path.join(CWD, 'r{}/ip_6_address.nhg.ref'.format(i))
if os.path.isfile(refTableFile):
expected = open(refTableFile).read().rstrip()
actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0])
# Mask out protocol name or number
actual = re.sub(r"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual)
+ actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual)
# Remove ff00::/8 routes (seen on some kernels - not from FRR)
actual = re.sub(r'ff00::/8.*', '', actual)
--- /dev/null
+router bgp 65001
+ neighbor 10.0.30.3 remote-as external
+ redistribute connected
!
interface r1-eth0
ip igmp
- ip pim sm
+ ip pim
+!
+interface r1-eth1
+ ip pim
!
interface lo
- ip pim sm
+ ip pim
!
-ip pim rp 10.254.0.1
+ip pim rp 10.254.0.3
--- /dev/null
+{
+ "10.254.0.3":[
+ {
+ "outboundInterface":"r1-eth1",
+ "group":"224.0.0.0\/4",
+ "source":"Static"
+ }
+ ]
+}
interface r1-eth0
ip address 10.0.20.1/24
!
+interface r1-eth1
+ ip address 10.0.30.1/24
+!
interface lo
ip address 10.254.0.1/32
!
--- /dev/null
+router bgp 65003
+ neighbor 10.0.30.1 remote-as external
+ redistribute connected
--- /dev/null
+hostname rp
+!
+interface rp-eth0
+ ip pim
+!
+interface lo
+ ip pim
+!
+ip pim rp 10.254.0.3
--- /dev/null
+{
+ "229.1.1.1":{
+ "10.0.20.2":{
+ "sourceStream":true,
+ "inboundInterface":"rp-eth0",
+ "rpfAddress":"10.0.20.2",
+ "source":"10.0.20.2",
+ "group":"229.1.1.1",
+ "state":"NotJ",
+ "joinState":"NotJoined",
+ "regState":"RegNoInfo",
+ "resetTimer":"--:--:--",
+ "refCount":1,
+ "sptBit":0
+ }
+ }
+}
--- /dev/null
+hostname rp
+!
+interface rp-eth0
+ ip address 10.0.30.3/24
+!
+interface lo
+ ip address 10.254.0.3/32
+!
import os
import sys
import pytest
+import json
+from functools import partial
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, '../'))
for routern in range(1, 3):
tgen.add_router('r{}'.format(routern))
+ tgen.add_router('rp')
+
+ # r1 -> .1
+ # r2 -> .2
+ # rp -> .3
+ # loopback network is 10.254.0.X/32
+ #
# r1 <- sw1 -> r2
+ # r1-eth0 <-> r2-eth0
+ # 10.0.20.0/24
sw = tgen.add_switch('sw1')
sw.add_link(tgen.gears['r1'])
sw.add_link(tgen.gears['r2'])
+ # r1 <- sw2 -> rp
+ # r1-eth1 <-> rp-eth0
+ # 10.0.30.0/24
+ sw = tgen.add_switch('sw2')
+ sw.add_link(tgen.gears['r1'])
+ sw.add_link(tgen.gears['rp'])
+
def setup_module(mod):
"Sets up the pytest environment"
TopoRouter.RD_PIM,
os.path.join(CWD, '{}/pimd.conf'.format(rname))
)
+ router.load_config(
+ TopoRouter.RD_BGP,
+ os.path.join(CWD, '{}/bgpd.conf'.format(rname))
+ )
# After loading the configurations, this function loads configured daemons.
tgen.start_router()
+ #tgen.mininet_cli()
def teardown_module(mod):
# This function tears down the whole topology.
tgen.stop_topology()
+def test_pim_rp_setup():
+ "Ensure basic routing has come up and the rp has an outgoing interface"
+ #Ensure rp and r1 establish pim neighbor ship and bgp has come up
+ #Finally ensure that the rp has an outgoing interface on r1
+ tgen = get_topogen()
+
+ r1 = tgen.gears['r1']
+ json_file = '{}/{}/rp-info.json'.format(CWD, r1.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp,
+ r1, 'show ip pim rp-info json', expected)
+ _, result = topotest.run_and_expect(test_func, None, count=15, wait=5)
+ assertmsg = '"{}" JSON output mismatches'.format(r1.name)
+ assert result is None, assertmsg
+ #tgen.mininet_cli()
def test_pim_send_mcast_stream():
"Establish a Multicast stream from r2 -> r1 and then ensure S,G is created as appropriate"
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
+ rp = tgen.gears['rp']
r2 = tgen.gears['r2']
r1 = tgen.gears['r1']
}
assert topotest.json_cmp(out, expected) is None, 'failed to converge pim'
+ #tgen.mininet_cli()
+
+def test_pim_rp_sees_stream():
+ "Ensure that the RP sees the stream and has acted accordingly"
+ tgen = get_topogen()
+
+ rp = tgen.gears['rp']
+ json_file = '{}/{}/upstream.json'.format(CWD, rp.name)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(topotest.router_json_cmp,
+ rp, 'show ip pim upstream json', expected)
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=.5)
+ assertmsg = '"{}" JSON output mismatches'.format(rp.name)
+ assert result is None, assertmsg
def test_pim_igmp_report():
"Send a igmp report from r2->r1 and ensure that the *,G state is created on r1"
fabricd_options="-A 127.0.0.1"
vrrpd_options=" -A 127.0.0.1"
+# configuration profile
+#
+#frr_profile="traditional"
+#frr_profile="datacenter"
+
#
# This is the maximum number of FD's that will be available.
# Upon startup this is read by the control files and ulimit
try:
config_text = subprocess.check_output(
bindir + "/vtysh --config_dir " + confdir + " -c 'show run " + daemon + "' | /usr/bin/tail -n +4 | " + bindir + "/vtysh --config_dir " + confdir + " -m -f -",
- shell=True, stderr=subprocess.STDOUT)
+ shell=True)
except subprocess.CalledProcessError as e:
ve = VtyshMarkException(e)
ve.output = e.output
lines_to_del_to_del.append((ctx_keys, route_target_export_line))
lines_to_add_to_del.append((ctx_keys, route_target_both_line))
+ # Deleting static routes under a vrf can lead to time-outs if each is sent
+ # as separate vtysh -c commands. Change them from being in lines_to_del and
+ # put the "no" form in lines_to_add
+ if ctx_keys[0].startswith('vrf ') and line:
+ if (line.startswith('ip route') or
+ line.startswith('ipv6 route')):
+ add_cmd = ('no ' + line)
+ lines_to_add.append((ctx_keys, add_cmd))
+ lines_to_del_to_del.append((ctx_keys, line))
+
if not deleted:
found_add_line = line_exist(lines_to_add, ctx_keys, line)
for line in running_ctx.lines:
lines_to_del.append((running_ctx_keys, line))
+ # Some commands can happen at higher counts that make
+ # doing vtysh -c inefficient (and can time out.) For
+ # these commands, instead of adding them to lines_to_del,
+ # add the "no " version to lines_to_add.
+ elif (running_ctx_keys[0].startswith('ip route') or
+ running_ctx_keys[0].startswith('ipv6 route') or
+ running_ctx_keys[0].startswith('access-list') or
+ running_ctx_keys[0].startswith('ipv6 access-list') or
+ running_ctx_keys[0].startswith('ip prefix-list') or
+ running_ctx_keys[0].startswith('ipv6 prefix-list')):
+ add_cmd = ('no ' + running_ctx_keys[0],)
+ lines_to_add.append((add_cmd, None))
+
# Non-global context
elif running_ctx_keys and not any("address-family" in key for key in running_ctx_keys):
lines_to_del.append((running_ctx_keys, None))
try:
cmd = [str(bindir + '/vtysh'), '--config_dir', confdir, '-c', 'conf t']
- output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip()
+ output = subprocess.check_output(cmd).strip()
if 'VTY configuration is locked by other VTY' in output.decode('utf-8'):
print(output)
while True:
try:
- _ = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ _ = subprocess.check_output(cmd)
except subprocess.CalledProcessError:
if line == '!':
continue
+ # Don't run "no" commands twice since they can error
+ # out the second time due to first deletion
+ if x == 1 and ctx_keys[0].startswith('no '):
+ continue
+
cmd = line_for_vtysh_file(ctx_keys, line, False)
lines_to_configure.append(cmd)
fh.write(line + '\n')
try:
- subprocess.check_output([str(args.bindir + '/vtysh'), '--config_dir', args.confdir, '-f', filename], stderr=subprocess.STDOUT)
+ subprocess.check_output([str(args.bindir + '/vtysh'), '--config_dir', args.confdir, '-f', filename])
except subprocess.CalledProcessError as e:
log.warning("frr-reload.py failed due to\n%s" % e.output)
reload_ok = False
FRR_USER="@enable_user@" # frr
FRR_GROUP="@enable_group@" # frr
FRR_VTY_GROUP="@enable_vty_group@" # frrvty
+FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter
# Local Daemon selection may be done by using /etc/frr/daemons.
# See /usr/share/doc/frr/README.Debian.gz for further information.
--pidfile=`pidfile $dmn-$inst` \
--exec "$D_PATH/$dmn" \
-- \
- `eval echo "$""$dmn""_options"` -n "$inst"
+ `eval echo "$""$dmn""_options"` $frr_global_options -n "$inst"
else
if ! check_daemon $dmn; then
echo -n " (binary does not exist)"
--pidfile=`pidfile $dmn` \
--exec "$valgrind" \
-- --trace-children=no --leak-check=full --log-file=/var/log/frr/$dmn-valgrind.log $D_PATH/$dmn \
- `eval echo "$""$dmn""_options"`
+ `eval echo "$""$dmn""_options"` $frr_global_options
else
${SSD} \
--start \
--pidfile=`pidfile $dmn` \
--exec "$D_PATH/$dmn" \
-- \
- `eval echo "$""$dmn""_options"`
+ `eval echo "$""$dmn""_options"` $frr_global_options
fi
fi
--pidfile=`pidfile staticd` \
--exec "$D_PATH/staticd" \
-- \
- `eval echo "$"staticd"_options"`
+ `eval echo "$"staticd"_options"` $frr_global_options
fi
}
# Read configuration variable file if it is present
[ -r /etc/default/frr ] && . /etc/default/frr
+if test -z "$frr_profile"; then
+ # try to autodetect config profile
+ if test -d /etc/cumulus; then
+ frr_profile=datacenter
+ # elif test ...; then
+ # -- add your distro/system here
+ elif test -n "$FRR_DEFAULT_PROFILE"; then
+ frr_profile="$FRR_DEFAULT_PROFILE"
+ fi
+fi
+test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile"
+
MAX_INSTANCES=${MAX_INSTANCES:=5}
# Set priority of un-startable daemons to 'no' and substitute 'yes' to '0'
stop_prio 0 $dmn
fi
- if [ [ -n "$dmn" ] && [ "$dmn" != "zebra" ] ]; then
+ if [ -n "$dmn" -a "$dmn" != "zebra" ]; then
[ -n "$dmn" ] && eval "${dmn/-/_}=0"
start_watchfrr
fi
FRR_USER="@enable_user@" # frr
FRR_GROUP="@enable_group@" # frr
FRR_VTY_GROUP="@enable_vty_group@" # frrvty
+FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter
# ORDER MATTERS FOR $DAEMONS!
# - keep zebra first
instopt="${inst:+-n $inst}"
eval args="\$${daemon}_options"
- if eval "$all_wrap $wrap $bin -d $instopt $args"; then
+ if eval "$all_wrap $wrap $bin -d $frr_global_options $instopt $args"; then
log_success_msg "Started $dmninst"
vtysh_b "$daemon"
else
unset watchfrr_options
fi
+if test -z "$frr_profile"; then
+ # try to autodetect config profile
+ if test -d /etc/cumulus; then
+ frr_profile=datacenter
+ # elif test ...; then
+ # -- add your distro/system here
+ elif test -n "$FRR_DEFAULT_PROFILE"; then
+ frr_profile="$FRR_DEFAULT_PROFILE"
+ fi
+fi
+test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile"
+
#
# other defaults and dispatch
#
--- /dev/null
+<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>
--- /dev/null
+#!/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)
sbin_PROGRAMS += vrrpd/vrrpd
# dist_examples_DATA += staticd/staticd.conf.sample
vtysh_scan += $(top_srcdir)/vrrpd/vrrp_vty.c
-man8 += $(MANBUILD)/vrrpd.8
+man8 += $(MANBUILD)/frr-vrrpd.8
endif
vrrpd_libvrrp_a_SOURCES = \
vrrpd/vrrp_arp.c \
vrrpd/vrrp_debug.c \
vrrpd/vrrp_ndisc.c \
+ vrrpd/vrrp_northbound.c \
vrrpd/vrrp_packet.c \
vrrpd/vrrp_vty.c \
vrrpd/vrrp_zebra.c \
vrrpd_vrrpd_SOURCES = vrrpd/vrrp_main.c
vrrpd_vrrpd_LDADD = vrrpd/libvrrp.a lib/libfrr.la @LIBCAP@
+nodist_vrrpd_vrrpd_SOURCES = \
+ yang/frr-vrrpd.yang.c \
+ # end
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);
return false;
}
-int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip)
+int vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr *ip)
{
- int af = (ip->ipa_type == IPADDR_V6) ? AF_INET6 : AF_INET;
+ struct vrrp_router *r = IS_IPADDR_V4(ip) ? vr->v4 : vr->v6;
+ int af = r->family;
assert(r->family == af);
assert(!(r->vr->version == 2 && ip->ipa_type == IPADDR_V6));
ip.ipa_type = IPADDR_V4;
ip.ipaddr_v4 = v4;
- return vrrp_add_ip(vr->v4, &ip);
+ return vrrp_add_ip(vr, &ip);
}
int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6)
ip.ipa_type = IPADDR_V6;
ip.ipaddr_v6 = v6;
- return vrrp_add_ip(vr->v6, &ip);
+ return vrrp_add_ip(vr, &ip);
}
-int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip)
+int vrrp_del_ip(struct vrrp_vrouter *vr, struct ipaddr *ip)
{
struct listnode *ln, *nn;
struct ipaddr *iter;
int ret = 0;
+ struct vrrp_router *r = IS_IPADDR_V4(ip) ? vr->v4 : vr->v6;
+
if (!vrrp_has_ip(r->vr, ip))
return 0;
ip.ipa_type = IPADDR_V6;
ip.ipaddr_v6 = v6;
- return vrrp_del_ip(vr->v6, &ip);
+ return vrrp_del_ip(vr, &ip);
}
int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4)
ip.ipa_type = IPADDR_V4;
ip.ipaddr_v4 = v4;
- return vrrp_del_ip(vr->v4, &ip);
+ return vrrp_del_ip(vr, &ip);
}
/* 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
XFREE(MTYPE_VRRP_RTR, vr);
}
-struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid)
+struct vrrp_vrouter *vrrp_lookup(const struct interface *ifp, uint8_t vrid)
{
struct vrrp_vrouter vr;
vr.vrid = vrid;
- vr.ifp = ifp;
+ vr.ifp = (struct interface *)ifp;
return hash_lookup(vrrp_vrouters_hash, &vr);
}
*
* 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
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) {
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 */
r->ndisc_pending = false;
/* Disable ND Router Advertisements */
- if (r->family == AF_INET6)
+ if (r->family == AF_INET6 && r->mvl_ifp)
vrrp_zebra_radv_set(r, false);
}
THREAD_OFF(r->t_write);
/* Protodown macvlan */
- vrrp_zclient_send_interface_protodown(r->mvl_ifp, true);
+ if (r->mvl_ifp)
+ vrrp_zclient_send_interface_protodown(r->mvl_ifp, true);
/* Throw away our source address */
memset(&r->src, 0x00, sizeof(r->src));
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;
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;
}
/* Other ------------------------------------------------------------------- */
-int vrrp_config_write_interface(struct vty *vty)
-{
- struct list *vrs = hash_to_list(vrrp_vrouters_hash);
- struct listnode *ln, *ipln;
- struct vrrp_vrouter *vr;
- int writes = 0;
-
- for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) {
- vty_frame(vty, "interface %s\n", vr->ifp->name);
- ++writes;
-
- vty_out(vty, " vrrp %" PRIu8 "%s\n", vr->vrid,
- vr->version == 2 ? " version 2" : "");
- ++writes;
-
- if (vr->shutdown != vd.shutdown && ++writes)
- vty_out(vty, " %svrrp %" PRIu8 " shutdown\n",
- vr->shutdown ? "" : "no ", vr->vrid);
-
- if (vr->preempt_mode != vd.preempt_mode && ++writes)
- vty_out(vty, " %svrrp %" PRIu8 " preempt\n",
- vr->preempt_mode ? "" : "no ", vr->vrid);
-
- if (vr->accept_mode != vd.accept_mode && ++writes)
- vty_out(vty, " %svrrp %" PRIu8 " accept\n",
- vr->accept_mode ? "" : "no ", vr->vrid);
-
- if (vr->advertisement_interval != vd.advertisement_interval
- && ++writes)
- vty_out(vty,
- " vrrp %" PRIu8
- " advertisement-interval %d\n",
- vr->vrid, vr->advertisement_interval * CS2MS);
-
- if (vr->priority != vd.priority && ++writes)
- vty_out(vty, " vrrp %" PRIu8 " priority %" PRIu8 "\n",
- vr->vrid, vr->priority);
-
- struct ipaddr *ip;
-
- for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ipln, ip)) {
- char ipbuf[INET6_ADDRSTRLEN];
-
- ipaddr2str(ip, ipbuf, sizeof(ipbuf));
- vty_out(vty, " vrrp %" PRIu8 " ip %s\n", vr->vrid,
- ipbuf);
- ++writes;
- }
-
- for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ipln, ip)) {
- char ipbuf[INET6_ADDRSTRLEN];
-
- ipaddr2str(ip, ipbuf, sizeof(ipbuf));
- vty_out(vty, " vrrp %" PRIu8 " ipv6 %s\n", vr->vrid,
- ipbuf);
- ++writes;
- }
- vty_endframe(vty, "!\n");
- }
-
- list_delete(&vrs);
-
- return writes;
-}
-
int vrrp_config_write_global(struct vty *vty)
{
unsigned int writes = 0;
vty_out(vty, "vrrp autoconfigure%s\n",
vrrp_autoconfig_version == 2 ? " version 2" : "");
+ /* FIXME: needs to be udpated for full YANG conversion. */
if (vd.priority != VRRP_DEFAULT_PRIORITY && ++writes)
vty_out(vty, "vrrp default priority %" PRIu8 "\n", vd.priority);
void vrrp_init(void)
{
/* Set default defaults */
- vd.priority = VRRP_DEFAULT_PRIORITY;
- vd.advertisement_interval = VRRP_DEFAULT_ADVINT;
- vd.preempt_mode = VRRP_DEFAULT_PREEMPT;
- vd.accept_mode = VRRP_DEFAULT_ACCEPT;
+ vd.version = yang_get_default_uint8("%s/version", VRRP_XPATH_FULL);
+ vd.priority = yang_get_default_uint8("%s/priority", VRRP_XPATH_FULL);
+ vd.advertisement_interval = yang_get_default_uint16(
+ "%s/advertisement-interval", VRRP_XPATH_FULL);
+ vd.preempt_mode = yang_get_default_bool("%s/preempt", VRRP_XPATH_FULL);
+ vd.accept_mode =
+ yang_get_default_bool("%s/accept-mode", VRRP_XPATH_FULL);
vd.shutdown = VRRP_DEFAULT_SHUTDOWN;
vrrp_autoconfig_version = 3;
#include "lib/hook.h"
#include "lib/if.h"
#include "lib/linklist.h"
+#include "lib/northbound.h"
#include "lib/privs.h"
#include "lib/stream.h"
#include "lib/thread.h"
#define VRRP_LOGPFX_FAM "[%s] "
/* Default defaults */
+#define VRRP_XPATH_FULL "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group"
+#define VRRP_XPATH "./frr-vrrpd:vrrp/vrrp-group"
#define VRRP_DEFAULT_PRIORITY 100
#define VRRP_DEFAULT_ADVINT 100
#define VRRP_DEFAULT_PREEMPT true
DECLARE_MGROUP(VRRPD)
+/* Northbound */
+extern const struct frr_yang_module_info frr_vrrpd_info;
+
/* Configured defaults */
struct vrrp_defaults {
+ uint8_t version;
uint8_t priority;
uint16_t advertisement_interval;
bool preempt_mode;
/*
* Add an IPvX address to a VRRP Virtual Router.
*
- * r
+ * vr
* Virtual Router to add IPvx address to
*
* ip
* -1 on error
* 0 otherwise
*/
-int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip);
+int vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr *ip);
/*
* Add an IPv4 address to a VRRP Virtual Router.
/*
* Remove an IP address from a VRRP Virtual Router.
*
- * r
+ * vr
* Virtual Router to remove IP address from
*
* ip
* -1 on error
* 0 otherwise
*/
-int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip);
+int vrrp_del_ip(struct vrrp_vrouter *vr, struct ipaddr *ip);
/*
* Remove an IPv4 address from a VRRP Virtual Router.
/* Other ------------------------------------------------------------------- */
-/*
- * Write interface block-level configuration to vty.
- *
- * vty
- * vty to write config to
- *
- * Returns:
- * # of lines written
- */
-int vrrp_config_write_interface(struct vty *vty);
-
/*
* Write global level configuration to vty.
*
/*
* Find VRRP Virtual Router by Virtual Router ID
*/
-struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid);
+struct vrrp_vrouter *vrrp_lookup(const struct interface *ifp, uint8_t vrid);
#endif /* __VRRP_H__ */
#include "lib/sigevent.h"
#include "lib/thread.h"
#include "lib/vrf.h"
+#include "lib/vty.h"
#include "vrrp.h"
#include "vrrp_debug.h"
/* Master of threads. */
struct thread_master *master;
+static struct frr_daemon_info vrrpd_di;
+
/* SIGHUP handler. */
static void sighup(void)
{
zlog_info("SIGHUP received");
+
+ vty_read_config(NULL, vrrpd_di.config_file, config_default);
}
/* SIGINT / SIGTERM handler. */
static const struct frr_yang_module_info *const vrrp_yang_modules[] = {
&frr_interface_info,
+ &frr_vrrpd_info,
};
#define VRRP_VTY_PORT 2619
--- /dev/null
+/*
+ * VRRP northbound bindings.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "northbound.h"
+#include "libfrr.h"
+#include "vrrp.h"
+#include "vrrp_vty.h"
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group
+ */
+static int lib_interface_vrrp_vrrp_group_create(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ struct interface *ifp;
+ uint8_t vrid;
+ uint8_t version = 3;
+ struct vrrp_vrouter *vr;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(dnode, NULL, true);
+ vrid = yang_dnode_get_uint8(dnode, "./virtual-router-id");
+ version = yang_dnode_get_enum(dnode, "./version");
+ vr = vrrp_vrouter_create(ifp, vrid, version);
+ nb_running_set_entry(dnode, vr);
+
+ return NB_OK;
+}
+
+static int lib_interface_vrrp_vrrp_group_destroy(enum nb_event event,
+ const struct lyd_node *dnode)
+{
+ struct vrrp_vrouter *vr;
+
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ vr = nb_running_unset_entry(dnode);
+ vrrp_vrouter_destroy(vr);
+
+ return NB_OK;
+}
+
+static const void *
+lib_interface_vrrp_vrrp_group_get_next(const void *parent_list_entry,
+ const void *list_entry)
+{
+ struct list *l = hash_to_list(vrrp_vrouters_hash);
+ struct listnode *ln;
+ const struct vrrp_vrouter *curr;
+ const struct interface *ifp = parent_list_entry;
+
+ /*
+ * If list_entry is null, we return the first vrrp instance with a
+ * matching interface
+ */
+ bool nextone = list_entry ? false : true;
+
+ for (ALL_LIST_ELEMENTS_RO(l, ln, curr)) {
+ if (curr == list_entry) {
+ nextone = true;
+ continue;
+ }
+
+ if (nextone && curr->ifp == ifp)
+ goto done;
+ }
+
+ curr = NULL;
+
+done:
+ list_delete(&l);
+ return curr;
+}
+
+static int lib_interface_vrrp_vrrp_group_get_keys(const void *list_entry,
+ struct yang_list_keys *keys)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ keys->num = 1;
+ snprintf(keys->key[0], sizeof(keys->key[0]), "%" PRIu32, vr->vrid);
+
+ return NB_OK;
+}
+
+static const void *
+lib_interface_vrrp_vrrp_group_lookup_entry(const void *parent_list_entry,
+ const struct yang_list_keys *keys)
+{
+ uint32_t vrid = strtoul(keys->key[0], NULL, 10);
+ const struct interface *ifp = parent_list_entry;
+
+ return vrrp_lookup(ifp, vrid);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/version
+ */
+static int
+lib_interface_vrrp_vrrp_group_version_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ struct vrrp_vrouter *vr;
+ uint8_t version;
+
+ vr = nb_running_get_entry(dnode, NULL, true);
+ vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN);
+ vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN);
+ version = yang_dnode_get_enum(dnode, NULL);
+ vr->version = version;
+
+ vrrp_check_start(vr);
+
+ return NB_OK;
+}
+
+/*
+ * Helper function for address list OP_MODIFY callbacks.
+ */
+static void vrrp_yang_add_del_virtual_address(const struct lyd_node *dnode,
+ bool add)
+{
+ struct vrrp_vrouter *vr;
+ struct ipaddr ip;
+
+ vr = nb_running_get_entry(dnode, NULL, true);
+ yang_dnode_get_ip(&ip, dnode, NULL);
+ if (add)
+ vrrp_add_ip(vr, &ip);
+ else
+ vrrp_del_ip(vr, &ip);
+
+ vrrp_check_start(vr);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address
+ */
+static int lib_interface_vrrp_vrrp_group_v4_virtual_address_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ vrrp_yang_add_del_virtual_address(dnode, true);
+
+ return NB_OK;
+}
+
+static int lib_interface_vrrp_vrrp_group_v4_virtual_address_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ vrrp_yang_add_del_virtual_address(dnode, false);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/current-priority
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_current_priority_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint8(xpath, vr->v4->priority);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/vrrp-interface
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_vrrp_interface_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ struct yang_data *val = NULL;
+
+ if (vr->v4->mvl_ifp)
+ val = yang_data_new_string(xpath, vr->v4->mvl_ifp->name);
+
+ return val;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/source-address
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_source_address_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+ struct yang_data *val = NULL;
+ struct ipaddr ip;
+
+ memset(&ip, 0x00, sizeof(ip));
+
+ if (memcmp(&vr->v4->src.ipaddr_v4, &ip.ipaddr_v4, sizeof(ip.ipaddr_v4)))
+ val = yang_data_new_ip(xpath, &vr->v4->src);
+
+ return val;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/state
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_state_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_enum(xpath, vr->v4->fsm.state);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/master-advertisement-interval
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_master_advertisement_interval_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint16(xpath, vr->v4->master_adver_interval);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/skew-time
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_skew_time_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint16(xpath, vr->v4->skew_time);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/state-transition
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_counter_state_transition_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint32(xpath, vr->v4->stats.trans_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/advertisement
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_counter_tx_advertisement_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint32(xpath, vr->v4->stats.adver_tx_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/gratuitous-arp
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_counter_tx_gratuitous_arp_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint32(xpath, vr->v4->stats.garp_tx_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/rx/advertisement
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v4_counter_rx_advertisement_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint32(xpath, vr->v4->stats.adver_rx_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address
+ */
+static int lib_interface_vrrp_vrrp_group_v6_virtual_address_create(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ vrrp_yang_add_del_virtual_address(dnode, true);
+
+ return NB_OK;
+}
+
+static int lib_interface_vrrp_vrrp_group_v6_virtual_address_destroy(
+ enum nb_event event, const struct lyd_node *dnode)
+{
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ vrrp_yang_add_del_virtual_address(dnode, false);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/current-priority
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_current_priority_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint8(xpath, vr->v6->priority);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/vrrp-interface
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_vrrp_interface_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+ struct yang_data *val = NULL;
+
+ if (vr->v6->mvl_ifp)
+ val = yang_data_new_string(xpath, vr->v6->mvl_ifp->name);
+
+ return val;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/source-address
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_source_address_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+ struct yang_data *val = NULL;
+
+ if (ipaddr_isset(&vr->v6->src))
+ val = yang_data_new_ip(xpath, &vr->v6->src);
+
+ return val;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/state
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_state_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_enum(xpath, vr->v6->fsm.state);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/master-advertisement-interval
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_master_advertisement_interval_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint16(xpath, vr->v6->master_adver_interval);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/skew-time
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_skew_time_get_elem(const char *xpath,
+ const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint16(xpath, vr->v6->skew_time);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/state-transition
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_counter_state_transition_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint32(xpath, vr->v6->stats.trans_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/advertisement
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_counter_tx_advertisement_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ const struct vrrp_vrouter *vr = list_entry;
+
+ return yang_data_new_uint32(xpath, vr->v6->stats.adver_tx_cnt);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/neighbor-advertisement
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_counter_tx_neighbor_advertisement_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ /* TODO: implement me. */
+ return NULL;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/rx/advertisement
+ */
+static struct yang_data *
+lib_interface_vrrp_vrrp_group_v6_counter_rx_advertisement_get_elem(
+ const char *xpath, const void *list_entry)
+{
+ /* TODO: implement me. */
+ return NULL;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority
+ */
+static int
+lib_interface_vrrp_vrrp_group_priority_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ struct vrrp_vrouter *vr;
+ uint8_t priority;
+
+ vr = nb_running_get_entry(dnode, NULL, true);
+ priority = yang_dnode_get_uint8(dnode, NULL);
+ vrrp_set_priority(vr, priority);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt
+ */
+static int
+lib_interface_vrrp_vrrp_group_preempt_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ struct vrrp_vrouter *vr;
+ bool preempt;
+
+ vr = nb_running_get_entry(dnode, NULL, true);
+ preempt = yang_dnode_get_bool(dnode, NULL);
+ vr->preempt_mode = preempt;
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/accept-mode
+ */
+static int
+lib_interface_vrrp_vrrp_group_accept_mode_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ struct vrrp_vrouter *vr;
+ bool accept;
+
+ vr = nb_running_get_entry(dnode, NULL, true);
+ accept = yang_dnode_get_bool(dnode, NULL);
+ vr->accept_mode = accept;
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval
+ */
+static int lib_interface_vrrp_vrrp_group_advertisement_interval_modify(
+ enum nb_event event, const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ struct vrrp_vrouter *vr;
+ uint16_t advert_int;
+
+ vr = nb_running_get_entry(dnode, NULL, true);
+ advert_int = yang_dnode_get_uint16(dnode, NULL);
+ vrrp_set_advertisement_interval(vr, advert_int);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown
+ */
+static int
+lib_interface_vrrp_vrrp_group_shutdown_modify(enum nb_event event,
+ const struct lyd_node *dnode,
+ union nb_resource *resource)
+{
+ if (event != NB_EV_APPLY)
+ return NB_OK;
+
+ struct vrrp_vrouter *vr;
+ bool shutdown;
+
+ vr = nb_running_get_entry(dnode, NULL, true);
+ shutdown = yang_dnode_get_bool(dnode, NULL);
+
+ vr->shutdown = shutdown;
+
+ if (shutdown) {
+ vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN);
+ vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN);
+ } else {
+ vrrp_check_start(vr);
+ }
+
+ return NB_OK;
+}
+
+/* clang-format off */
+const struct frr_yang_module_info frr_vrrpd_info = {
+ .name = "frr-vrrpd",
+ .nodes = {
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group",
+ .cbs = {
+ .create = lib_interface_vrrp_vrrp_group_create,
+ .destroy = lib_interface_vrrp_vrrp_group_destroy,
+ .get_next = lib_interface_vrrp_vrrp_group_get_next,
+ .get_keys = lib_interface_vrrp_vrrp_group_get_keys,
+ .lookup_entry = lib_interface_vrrp_vrrp_group_lookup_entry,
+ .cli_show = cli_show_vrrp,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/version",
+ .cbs = {
+ .modify = lib_interface_vrrp_vrrp_group_version_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority",
+ .cbs = {
+ .modify = lib_interface_vrrp_vrrp_group_priority_modify,
+ .cli_show = cli_show_priority,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt",
+ .cbs = {
+ .modify = lib_interface_vrrp_vrrp_group_preempt_modify,
+ .cli_show = cli_show_preempt,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/accept-mode",
+ .cbs = {
+ .modify = lib_interface_vrrp_vrrp_group_accept_mode_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval",
+ .cbs = {
+ .modify = lib_interface_vrrp_vrrp_group_advertisement_interval_modify,
+ .cli_show = cli_show_advertisement_interval,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown",
+ .cbs = {
+ .modify = lib_interface_vrrp_vrrp_group_shutdown_modify,
+ .cli_show = cli_show_shutdown,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address",
+ .cbs = {
+ .create = lib_interface_vrrp_vrrp_group_v4_virtual_address_create,
+ .destroy = lib_interface_vrrp_vrrp_group_v4_virtual_address_destroy,
+ .cli_show = cli_show_ip,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/current-priority",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v4_current_priority_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/vrrp-interface",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v4_vrrp_interface_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/source-address",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v4_source_address_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/state",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v4_state_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/master-advertisement-interval",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v4_master_advertisement_interval_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/skew-time",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v4_skew_time_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/state-transition",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_state_transition_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/advertisement",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_tx_advertisement_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/tx/gratuitous-arp",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_tx_gratuitous_arp_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/counter/rx/advertisement",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v4_counter_rx_advertisement_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address",
+ .cbs = {
+ .create = lib_interface_vrrp_vrrp_group_v6_virtual_address_create,
+ .destroy = lib_interface_vrrp_vrrp_group_v6_virtual_address_destroy,
+ .cli_show = cli_show_ipv6,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/current-priority",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v6_current_priority_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/vrrp-interface",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v6_vrrp_interface_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/source-address",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v6_source_address_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/state",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v6_state_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/master-advertisement-interval",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v6_master_advertisement_interval_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/skew-time",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v6_skew_time_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/state-transition",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_state_transition_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/advertisement",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_tx_advertisement_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/tx/neighbor-advertisement",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_tx_neighbor_advertisement_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/counter/rx/advertisement",
+ .cbs = {
+ .get_elem = lib_interface_vrrp_vrrp_group_v6_counter_rx_advertisement_get_elem,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
#include "lib/if.h"
#include "lib/ipaddr.h"
#include "lib/json.h"
+#include "lib/northbound_cli.h"
#include "lib/prefix.h"
#include "lib/termtable.h"
#include "lib/vty.h"
+#include "lib/vrf.h"
#include "vrrp.h"
#include "vrrp_debug.h"
#define VRRP_VRID_STR "Virtual Router ID\n"
#define VRRP_PRIORITY_STR "Virtual Router Priority\n"
#define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n"
-#define VRRP_IP_STR "Virtual Router IPv4 address\n"
+#define VRRP_IP_STR "Virtual Router IP address\n"
#define VRRP_VERSION_STR "VRRP protocol version\n"
-#define VROUTER_GET_VTY(_vty, _ifp, _vrid, _vr) \
- do { \
- _vr = vrrp_lookup(_ifp, _vrid); \
- if (!_vr) { \
- vty_out(_vty, \
- "%% Please configure VRRP instance %u\n", \
- (unsigned int)_vrid); \
- return CMD_WARNING_CONFIG_FAILED; \
- } \
- } while (0)
+#define VRRP_XPATH_ENTRY VRRP_XPATH "[virtual-router-id='%ld']"
/* clang-format off */
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group
+ */
DEFPY(vrrp_vrid,
vrrp_vrid_cmd,
"[no] vrrp (1-255)$vrid [version (2-3)]",
VRRP_VERSION_STR
VRRP_VERSION_STR)
{
- VTY_DECLVAR_CONTEXT(interface, ifp);
+ char valbuf[20];
- struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid);
+ snprintf(valbuf, sizeof(valbuf), "%ld", version ? version : vd.version);
- if (version == 0)
- version = 3;
+ if (no)
+ nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+ else {
+ nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
+ nb_cli_enqueue_change(vty, "./version", NB_OP_MODIFY, valbuf);
+ }
- if (no && vr)
- vrrp_vrouter_destroy(vr);
- else if (no && !vr)
- vty_out(vty, "%% VRRP instance %ld does not exist on %s\n",
- vrid, ifp->name);
- else if (!vr)
- vrrp_vrouter_create(ifp, vrid, version);
- else if (vr)
- vty_out(vty, "%% VRRP instance %ld already exists on %s\n",
- vrid, ifp->name);
+ return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
- return CMD_SUCCESS;
+void cli_show_vrrp(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
+{
+ const char *vrid = yang_dnode_get_string(dnode, "./virtual-router-id");
+ const char *ver = yang_dnode_get_string(dnode, "./version");
+
+ vty_out(vty, " vrrp %s", vrid);
+ if (show_defaults || !yang_dnode_is_default(dnode, "./version"))
+ vty_out(vty, " version %s", ver);
+ vty_out(vty, "\n");
}
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown
+ */
DEFPY(vrrp_shutdown,
vrrp_shutdown_cmd,
"[no] vrrp (1-255)$vrid shutdown",
VRRP_VRID_STR
"Force VRRP router into administrative shutdown\n")
{
- VTY_DECLVAR_CONTEXT(interface, ifp);
+ nb_cli_enqueue_change(vty, "./shutdown", NB_OP_MODIFY,
+ no ? "false" : "true");
- struct vrrp_vrouter *vr;
+ return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
- VROUTER_GET_VTY(vty, ifp, vrid, vr);
-
- if (!no) {
- if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE)
- vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN);
- if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE)
- vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN);
- vr->shutdown = true;
- } else {
- vr->shutdown = false;
- vrrp_check_start(vr);
- }
+void cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+ const bool shut = yang_dnode_get_bool(dnode, NULL);
- return CMD_SUCCESS;
+ vty_out(vty, " %svrrp %s shutdown\n", shut ? "" : "no ", vrid);
}
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority
+ */
DEFPY(vrrp_priority,
vrrp_priority_cmd,
- "[no] vrrp (1-255)$vrid priority (1-254)",
- NO_STR
+ "vrrp (1-255)$vrid priority (1-254)",
VRRP_STR
VRRP_VRID_STR
VRRP_PRIORITY_STR
"Priority value")
{
- VTY_DECLVAR_CONTEXT(interface, ifp);
+ nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, priority_str);
- struct vrrp_vrouter *vr;
- uint8_t newprio = no ? vd.priority : priority;
+ return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
- VROUTER_GET_VTY(vty, ifp, vrid, vr);
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority
+ */
+DEFPY(no_vrrp_priority,
+ no_vrrp_priority_cmd,
+ "no vrrp (1-255)$vrid priority [(1-254)]",
+ NO_STR
+ VRRP_STR
+ VRRP_VRID_STR
+ VRRP_PRIORITY_STR
+ "Priority value")
+{
+ nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, NULL);
- vrrp_set_priority(vr, newprio);
+ return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
- return CMD_SUCCESS;
+void cli_show_priority(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+ const char *prio = yang_dnode_get_string(dnode, NULL);
+
+ vty_out(vty, " vrrp %s priority %s\n", vrid, prio);
}
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval
+ */
DEFPY(vrrp_advertisement_interval,
vrrp_advertisement_interval_cmd,
- "[no] vrrp (1-255)$vrid advertisement-interval (10-40950)",
- NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
+ "vrrp (1-255)$vrid advertisement-interval (10-40950)",
+ VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
"Advertisement interval in milliseconds; must be multiple of 10")
{
- VTY_DECLVAR_CONTEXT(interface, ifp);
+ char val[20];
- struct vrrp_vrouter *vr;
- uint16_t newadvint =
- no ? vd.advertisement_interval * CS2MS : advertisement_interval;
+ /* all internal computations are in centiseconds */
+ advertisement_interval /= CS2MS;
+ snprintf(val, sizeof(val), "%ld", advertisement_interval);
+ nb_cli_enqueue_change(vty, "./advertisement-interval", NB_OP_MODIFY,
+ val);
- if (newadvint % CS2MS != 0) {
- vty_out(vty, "%% Value must be a multiple of %u\n",
- (unsigned int)CS2MS);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
- /* all internal computations are in centiseconds */
- newadvint /= CS2MS;
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval
+ */
+DEFPY(no_vrrp_advertisement_interval,
+ no_vrrp_advertisement_interval_cmd,
+ "no vrrp (1-255)$vrid advertisement-interval [(10-40950)]",
+ NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
+ "Advertisement interval in milliseconds; must be multiple of 10")
+{
+ nb_cli_enqueue_change(vty, "./advertisement-interval", NB_OP_MODIFY,
+ NULL);
- VROUTER_GET_VTY(vty, ifp, vrid, vr);
- vrrp_set_advertisement_interval(vr, newadvint);
+ return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
- return CMD_SUCCESS;
+void cli_show_advertisement_interval(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+ uint16_t advint = yang_dnode_get_uint16(dnode, NULL);
+
+ vty_out(vty, " vrrp %s advertisement-interval %u\n", vrid,
+ advint * CS2MS);
}
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address
+ */
DEFPY(vrrp_ip,
vrrp_ip_cmd,
"[no] vrrp (1-255)$vrid ip A.B.C.D",
"Add IPv4 address\n"
VRRP_IP_STR)
{
- VTY_DECLVAR_CONTEXT(interface, ifp);
+ int op = no ? NB_OP_DESTROY : NB_OP_CREATE;
+ nb_cli_enqueue_change(vty, "./v4/virtual-address", op, ip_str);
- struct vrrp_vrouter *vr;
- bool deactivated = false;
- bool activated = false;
- bool failed = false;
- int ret = CMD_SUCCESS;
- int oldstate;
-
- VROUTER_GET_VTY(vty, ifp, vrid, vr);
-
- bool will_activate = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE);
-
- if (no) {
- oldstate = vr->v4->fsm.state;
- failed = vrrp_del_ipv4(vr, ip);
- vrrp_check_start(vr);
- deactivated = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE
- && oldstate != VRRP_STATE_INITIALIZE);
- } else {
- oldstate = vr->v4->fsm.state;
- failed = vrrp_add_ipv4(vr, ip);
- vrrp_check_start(vr);
- activated = (vr->v4->fsm.state != VRRP_STATE_INITIALIZE
- && oldstate == VRRP_STATE_INITIALIZE);
- }
+ return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
- if (activated)
- vty_out(vty, "%% Activated IPv4 Virtual Router %ld\n", vrid);
- if (deactivated)
- vty_out(vty, "%% Deactivated IPv4 Virtual Router %ld\n", vrid);
- if (failed) {
- vty_out(vty, "%% Failed to %s virtual IP\n",
- no ? "remove" : "add");
- ret = CMD_WARNING_CONFIG_FAILED;
- if (will_activate && !activated) {
- vty_out(vty,
- "%% Failed to activate IPv4 Virtual Router %ld\n",
- vrid);
- }
- }
+void cli_show_ip(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
+{
+ const char *vrid =
+ yang_dnode_get_string(dnode, "../../virtual-router-id");
+ const char *ipv4 = yang_dnode_get_string(dnode, NULL);
- return ret;
+ vty_out(vty, " vrrp %s ip %s\n", vrid, ipv4);
}
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address
+ */
DEFPY(vrrp_ip6,
vrrp_ip6_cmd,
"[no] vrrp (1-255)$vrid ipv6 X:X::X:X",
"Add IPv6 address\n"
VRRP_IP_STR)
{
- VTY_DECLVAR_CONTEXT(interface, ifp);
-
- struct vrrp_vrouter *vr;
- bool deactivated = false;
- bool activated = false;
- bool failed = false;
- int ret = CMD_SUCCESS;
- int oldstate;
-
- VROUTER_GET_VTY(vty, ifp, vrid, vr);
-
- if (vr->version != 3) {
- vty_out(vty,
- "%% Cannot add IPv6 address to VRRPv2 virtual router\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
+ int op = no ? NB_OP_DESTROY : NB_OP_CREATE;
+ nb_cli_enqueue_change(vty, "./v6/virtual-address", op, ipv6_str);
- bool will_activate = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE);
-
- if (no) {
- oldstate = vr->v6->fsm.state;
- failed = vrrp_del_ipv6(vr, ipv6);
- vrrp_check_start(vr);
- deactivated = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE
- && oldstate != VRRP_STATE_INITIALIZE);
- } else {
- oldstate = vr->v6->fsm.state;
- failed = vrrp_add_ipv6(vr, ipv6);
- vrrp_check_start(vr);
- activated = (vr->v6->fsm.state != VRRP_STATE_INITIALIZE
- && oldstate == VRRP_STATE_INITIALIZE);
- }
+ return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
- if (activated)
- vty_out(vty, "%% Activated IPv6 Virtual Router %ld\n", vrid);
- if (deactivated)
- vty_out(vty, "%% Deactivated IPv6 Virtual Router %ld\n", vrid);
- if (failed) {
- vty_out(vty, "%% Failed to %s virtual IP\n",
- no ? "remove" : "add");
- ret = CMD_WARNING_CONFIG_FAILED;
- if (will_activate && !activated) {
- vty_out(vty,
- "%% Failed to activate IPv6 Virtual Router %ld\n",
- vrid);
- }
- }
+void cli_show_ipv6(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
+{
+ const char *vrid =
+ yang_dnode_get_string(dnode, "../../virtual-router-id");
+ const char *ipv6 = yang_dnode_get_string(dnode, NULL);
- return ret;
+ vty_out(vty, " vrrp %s ipv6 %s\n", vrid, ipv6);
}
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt
+ */
DEFPY(vrrp_preempt,
vrrp_preempt_cmd,
"[no] vrrp (1-255)$vrid preempt",
VRRP_VRID_STR
"Preempt mode\n")
{
- VTY_DECLVAR_CONTEXT(interface, ifp);
-
- struct vrrp_vrouter *vr;
+ nb_cli_enqueue_change(vty, "./preempt", NB_OP_MODIFY,
+ no ? "false" : "true");
- VROUTER_GET_VTY(vty, ifp, vrid, vr);
+ return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
- vr->preempt_mode = !no;
+void cli_show_preempt(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+ const bool pre = yang_dnode_get_bool(dnode, NULL);
- return CMD_SUCCESS;
+ vty_out(vty, " %svrrp %s preempt\n", pre ? "" : "no ", vrid);
}
+/* XXX: yang conversion */
DEFPY(vrrp_autoconfigure,
vrrp_autoconfigure_cmd,
"[no] vrrp autoconfigure [version (2-3)]",
return CMD_SUCCESS;
}
+/* XXX: yang conversion */
DEFPY(vrrp_default,
vrrp_default_cmd,
"[no] vrrp default <advertisement-interval$adv (10-40950)$advint|preempt$p|priority$prio (1-254)$prioval|shutdown$s>",
/* clang-format on */
+/*
+ * Write per interface VRRP config.
+ */
+static int vrrp_config_write_interface(struct vty *vty)
+{
+ struct vrf *vrf;
+ int write = 0;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ struct lyd_node *dnode;
+
+ dnode = yang_dnode_get(
+ running_config->dnode,
+ "/frr-interface:lib/interface[name='%s'][vrf='%s']",
+ ifp->name, vrf->name);
+ if (dnode == NULL)
+ continue;
+
+ write = 1;
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+ }
+ }
+
+ return write;
+}
+
static struct cmd_node interface_node = {INTERFACE_NODE, "%s(config-if)# ", 1};
static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
static struct cmd_node vrrp_node = {VRRP_NODE, "", 1};
install_element(INTERFACE_NODE, &vrrp_vrid_cmd);
install_element(INTERFACE_NODE, &vrrp_shutdown_cmd);
install_element(INTERFACE_NODE, &vrrp_priority_cmd);
+ install_element(INTERFACE_NODE, &no_vrrp_priority_cmd);
install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd);
+ install_element(INTERFACE_NODE, &no_vrrp_advertisement_interval_cmd);
install_element(INTERFACE_NODE, &vrrp_ip_cmd);
install_element(INTERFACE_NODE, &vrrp_ip6_cmd);
install_element(INTERFACE_NODE, &vrrp_preempt_cmd);
#ifndef __VRRP_VTY_H__
#define __VRRP_VTY_H__
+#include "lib/northbound.h"
+
void vrrp_vty_init(void);
+/* Northbound callbacks */
+void cli_show_vrrp(struct vty *vty, struct lyd_node *dnode, bool show_defaults);
+void cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_priority(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_advertisement_interval(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip(struct vty *vty, struct lyd_node *dnode, bool show_defaults);
+void cli_show_ipv6(struct vty *vty, struct lyd_node *dnode, bool show_defaults);
+void cli_show_preempt(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+
#endif /* __VRRP_VTY_H__ */
"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);
}
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);
}
elsif ($file =~ /lib\/if\.c$/) {
$protocol = "VTYSH_INTERFACE";
}
- elsif ($file =~ /lib\/filter\.c$/) {
+ elsif ($file =~ /lib\/(filter|lib_vty)\.c$/) {
$protocol = "VTYSH_ALL";
}
elsif ($file =~ /lib\/agentx\.c$/) {
$protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA";
}
elsif ($file =~ /lib\/nexthop_group\.c$/) {
- $protocol = "VTYSH_PBRD | VTYSH_SHARPD";
+ $protocol = "VTYSH_NH_GROUP";
}
elsif ($file =~ /lib\/plist\.c$/) {
if ($defun_array[1] =~ m/ipv6/) {
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")
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"
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);
#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,
|| !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
#include "command.h"
#include "memory.h"
#include "linklist.h"
-#include "memory_vty.h"
#include "libfrr.h"
#include "ferr.h"
#include "lib_errors.h"
if WATCHFRR
sbin_PROGRAMS += watchfrr/watchfrr
vtysh_scan += $(top_srcdir)/watchfrr/watchfrr_vty.c
-man8 += $(MANBUILD)/watchfrr.8
+man8 += $(MANBUILD)/frr-watchfrr.8
endif
noinst_HEADERS += \
#include <sigevent.h>
#include <lib/version.h>
#include "command.h"
-#include "memory_vty.h"
#include "libfrr.h"
#include "lib_errors.h"
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
leaf is-type {
type level;
- default "level-1";
+ default "level-1-2";
description
"Level of the IS-IS routing instance (OSI only).";
}
--- /dev/null
+module frr-vrrpd {
+ yang-version 1.1;
+ namespace "http://frrouting.org/yang/vrrpd";
+ prefix frr-vrrpd;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ import frr-interface {
+ prefix frr-interface;
+ }
+
+ organization
+ "Free Range Routing";
+ contact
+ "FRR Users List: <mailto:frog@lists.frrouting.org>
+ FRR Development List: <mailto:dev@lists.frrouting.org>";
+ description
+ "This module defines a model for managing FRR vrrpd daemon.";
+
+ revision 2019-09-09 {
+ description
+ "Initial revision.";
+ }
+
+ grouping ip-vrrp-config {
+ description
+ "Configuration data for VRRP on IP interfaces";
+ leaf virtual-router-id {
+ type uint8 {
+ range "1..255";
+ }
+ description
+ "Set the virtual router id for use by the VRRP group. This
+ usually also determines the virtual MAC address that is
+ generated for the VRRP group";
+ }
+
+ leaf version {
+ type enumeration {
+ enum "2" {
+ value 2;
+ description
+ "VRRP version 2.";
+ }
+ enum "3" {
+ value 3;
+ description
+ "VRRP version 3.";
+ }
+ }
+ default "3";
+ }
+
+ leaf priority {
+ type uint8 {
+ range "1..254";
+ }
+ default "100";
+ description
+ "Specifies the sending VRRP interface's priority
+ for the virtual router. Higher values equal higher
+ priority";
+ }
+
+ leaf preempt {
+ type boolean;
+ default "true";
+ description
+ "When set to true, enables preemption by a higher
+ priority backup router of a lower priority master router";
+ }
+
+ leaf accept-mode {
+ type boolean;
+ default "true";
+ description
+ "Configure whether packets destined for
+ virtual addresses are accepted even when the virtual
+ address is not owned by the router interface";
+ }
+
+ leaf advertisement-interval {
+ type uint16 {
+ range "1..4095";
+ }
+ units "centiseconds";
+ default "100";
+ description
+ "Sets the interval between successive VRRP
+ advertisements -- RFC 5798 defines this as a 12-bit
+ value expressed as 0.1 seconds, with default 100, i.e.,
+ 1 second. Several implementation express this in units of
+ seconds";
+ }
+
+ leaf shutdown {
+ type boolean;
+ default "false";
+ description
+ "Administrative shutdown for this VRRP group.";
+ }
+ }
+
+ grouping ip-vrrp-state {
+ description
+ "Grouping for operational state data for a virtual router";
+ leaf current-priority {
+ type uint8;
+ config false;
+ description
+ "Operational value of the priority for the
+ interface in the VRRP group.";
+ }
+
+ leaf vrrp-interface {
+ type frr-interface:interface-ref;
+ config false;
+ description
+ "The interface used to transmit VRRP traffic.";
+ }
+
+ leaf source-address {
+ type inet:ip-address;
+ config false;
+ description
+ "The source IP address used for VRRP advertisements.";
+ }
+
+ leaf state {
+ type enumeration {
+ enum "Initialize" {
+ description
+ "State when virtual router is waiting for a Startup event.";
+ }
+ enum "Master" {
+ description
+ "State when virtual router is functioning as the forwarding router
+ for the virtual addresses.";
+ }
+ enum "Backup" {
+ description
+ "State when virtual router is monitoring the availability and state
+ of the Master router.";
+ }
+ }
+ config false;
+ }
+
+ leaf master-advertisement-interval {
+ type uint16 {
+ range "0..4095";
+ }
+ units "centiseconds";
+ config false;
+ description
+ "Advertisement interval contained in advertisements received from the Master.";
+ }
+
+ leaf skew-time {
+ type uint16;
+ units "centiseconds";
+ config false;
+ description
+ "Time to skew Master_Down_Interval.";
+ }
+
+ container counter {
+ config false;
+ leaf state-transition {
+ type yang:zero-based-counter32;
+ description
+ "Number of state transitions the virtual router has experienced.";
+ }
+
+ container tx {
+ leaf advertisement {
+ type yang:zero-based-counter32;
+ description
+ "Number of sent VRRP advertisements.";
+ }
+ }
+
+ container rx {
+ leaf advertisement {
+ type yang:zero-based-counter32;
+ description
+ "Number of received VRRP advertisements.";
+ }
+ }
+ }
+ }
+
+ grouping ip-vrrp-top {
+ description
+ "Top-level grouping for Virtual Router Redundancy Protocol";
+ container vrrp {
+ description
+ "Enclosing container for VRRP groups handled by this
+ IP interface";
+ reference
+ "RFC 5798 - Virtual Router Redundancy Protocol
+ (VRRP) Version 3 for IPv4 and IPv6";
+ list vrrp-group {
+ key "virtual-router-id";
+ description
+ "List of VRRP groups, keyed by virtual router id";
+ uses ip-vrrp-config;
+
+ container v4 {
+ leaf-list virtual-address {
+ type inet:ipv4-address;
+ description
+ "Configure one or more IPv4 virtual addresses for the
+ VRRP group";
+ }
+
+ uses ip-vrrp-state {
+ augment "./counter/tx" {
+ leaf gratuitous-arp {
+ type yang:zero-based-counter32;
+ description
+ "Number of sent gratuitous ARP requests.";
+ }
+ }
+ }
+ }
+
+ container v6 {
+ when "../version = 3";
+ leaf-list virtual-address {
+ type inet:ipv6-address;
+ description
+ "Configure one or more IPv6 virtual addresses for the
+ VRRP group";
+ }
+
+ uses ip-vrrp-state {
+ augment "./counter/tx" {
+ leaf neighbor-advertisement {
+ type yang:zero-based-counter32;
+ description
+ "Number of sent unsolicited Neighbor Advertisements.";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ augment "/frr-interface:lib/frr-interface:interface" {
+ uses ip-vrrp-top;
+ }
+}
if ISISD
dist_yangmodels_DATA += yang/frr-isisd.yang
endif
+
+if VRRPD
+dist_yangmodels_DATA += yang/frr-vrrpd.yang
+endif
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));
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;
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};
zebra_debug_dplane = 0;
zebra_debug_mlag = 0;
zebra_debug_nht = 0;
+ zebra_debug_nexthop = 0;
install_node(&debug_node, config_write_debug);
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);
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);
#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)
#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;
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);
}
}
-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;
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)
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,
struct zebra_if *if_data;
if (ifp->ifindex != IFINDEX_INTERNAL) {
+ /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */
+ rtadv_stop_ra(ifp);
ret = if_unset_flags(ifp, IFF_UP);
if (ret < 0) {
vty_out(vty, "Can't shutdown interface\n");
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:
struct rtattr *nest = NLMSG_TAIL(n);
addattr_l(n, maxlen, type, NULL, 0);
+ nest->rta_type |= NLA_F_NESTED;
return nest;
}
struct rtattr *nest = RTA_TAIL(rta);
rta_addattr_l(rta, maxlen, type, NULL, 0);
+ nest->rta_type |= NLA_F_NESTED;
return nest;
}
netlink_socket (). */
void kernel_init(struct zebra_ns *zns)
{
- unsigned long groups;
+ uint32_t groups;
#if defined SOL_NETLINK
int one, ret;
#endif
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);
* included in the previous one */
for (node = first_node; node && (node != last_node);
node = next) {
+ struct label_manager_chunk *death;
+
next = listnextnode(node);
+ death = listgetdata(node);
list_delete_node(lbl_mgr.lc_list, node);
+ delete_label_chunk(death);
}
lmc = create_label_chunk(proto, instance, keep, base, end);
#include "filter.h"
#include "memory.h"
#include "zebra_memory.h"
-#include "memory_vty.h"
#include "prefix.h"
#include "log.h"
#include "plist.h"
atomic_store_explicit(&zrouter.in_shutdown, true,
memory_order_relaxed);
+ /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */
+ rtadv_stop_ra_all();
+
frr_early_fini();
zebra_dplane_pre_finish();
struct route_entry *newre;
struct route_entry *same;
struct prefix p;
+ struct nexthop_group *ng;
route_map_result_t ret = RMAP_PERMITMATCH;
afi_t afi;
afi = family2afi(rn->p.family);
if (rmap_name)
ret = zebra_import_table_route_map_check(
- afi, re->type, re->instance, &rn->p, re->ng->nexthop,
+ afi, re->type, re->instance, &rn->p,
+ re->nhe->nhg->nexthop,
zvrf->vrf->vrf_id, re->tag, rmap_name);
if (ret != RMAP_PERMITMATCH) {
newre->table = zvrf->table_id;
newre->uptime = monotime(NULL);
newre->instance = re->table;
- newre->ng = nexthop_group_new();
- route_entry_copy_nexthops(newre, re->ng->nexthop);
- rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre);
+ ng = nexthop_group_new();
+ copy_nexthops(&ng->nexthop, re->nhe->nhg->nexthop, NULL);
+
+ rib_add_multipath(afi, SAFI_UNICAST, &p, NULL, newre, ng);
return 0;
}
prefix_copy(&p, &rn->p);
rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_TABLE,
- re->table, re->flags, &p, NULL, re->ng->nexthop, re->nhe_id,
- zvrf->table_id, re->metric, re->distance, false);
+ re->table, re->flags, &p, NULL, re->nhe->nhg->nexthop,
+ re->nhe_id, zvrf->table_id, re->metric, re->distance,
+ false);
return 0;
}
#include "if.h"
#include "mpls.h"
#include "srcdest_table.h"
+#include "zebra/zebra_nhg.h"
#ifdef __cplusplus
extern "C" {
/* Link list. */
struct re_list_item next;
- /* Nexthop structure (from RIB) */
- struct nexthop_group *ng;
+ /* Nexthop group, shared/refcounted, based on the nexthop(s)
+ * provided by the owner of the route
+ */
+ struct nhg_hash_entry *nhe;
- /* Nexthop group from FIB (optional) */
+ /* Nexthop group from FIB (optional), reflecting what is actually
+ * installed in the FIB if that differs.
+ */
struct nexthop_group fib_ng;
/* Nexthop group hash entry ID */
RIB_UPDATE_MAX
} rib_update_event_t;
-extern struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *re,
- ifindex_t ifindex,
- vrf_id_t nh_vrf_id);
-extern struct nexthop *
-route_entry_nexthop_blackhole_add(struct route_entry *re,
- enum blackhole_type bh_type);
-extern struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *re,
- struct in_addr *ipv4,
- struct in_addr *src,
- vrf_id_t nh_vrf_id);
-extern struct nexthop *
-route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re,
- struct in_addr *ipv4, struct in_addr *src,
- ifindex_t ifindex, vrf_id_t nh_vrf_id);
-extern void route_entry_nexthop_delete(struct route_entry *re,
- struct nexthop *nexthop);
-extern struct nexthop *route_entry_nexthop_ipv6_add(struct route_entry *re,
- struct in6_addr *ipv6,
- vrf_id_t nh_vrf_id);
-extern struct nexthop *
-route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re,
- struct in6_addr *ipv6, ifindex_t ifindex,
- vrf_id_t nh_vrf_id);
-extern void route_entry_nexthop_add(struct route_entry *re,
- struct nexthop *nexthop);
extern void route_entry_copy_nexthops(struct route_entry *re,
struct nexthop *nh);
+int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new);
#define route_entry_dump(prefix, src, re) _route_entry_dump(__func__, prefix, src, re)
extern void _route_entry_dump(const char *func, union prefixconstptr pp,
uint32_t mtu, uint8_t distance, route_tag_t tag);
extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
- struct prefix_ipv6 *src_p, struct route_entry *re);
+ struct prefix_ipv6 *src_p, struct route_entry *re,
+ struct nexthop_group *ng);
extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
unsigned short instance, int flags, struct prefix *p,
if (re->fib_ng.nexthop)
return &(re->fib_ng);
else
- return re->ng;
+ return re->nhe->nhg;
}
extern void zebra_vty_init(void);
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)
{
{
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)
}
static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id,
- struct route_entry *re,
+ struct nexthop_group *ng,
struct rtmsg *rtm,
struct rtnexthop *rtnh,
struct rtattr **tb,
int len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
vrf_id_t nh_vrf_id = vrf_id;
- re->ng = nexthop_group_new();
-
for (;;) {
struct nexthop *nh = NULL;
if (gate && rtm->rtm_family == AF_INET) {
if (index)
- nh = route_entry_nexthop_ipv4_ifindex_add(
- re, gate, prefsrc, index, nh_vrf_id);
+ nh = nexthop_from_ipv4_ifindex(
+ gate, prefsrc, index, nh_vrf_id);
else
- nh = route_entry_nexthop_ipv4_add(
- re, gate, prefsrc, nh_vrf_id);
+ nh = nexthop_from_ipv4(gate, prefsrc,
+ nh_vrf_id);
} else if (gate && rtm->rtm_family == AF_INET6) {
if (index)
- nh = route_entry_nexthop_ipv6_ifindex_add(
- re, gate, index, nh_vrf_id);
+ nh = nexthop_from_ipv6_ifindex(
+ gate, index, nh_vrf_id);
else
- nh = route_entry_nexthop_ipv6_add(re, gate,
- nh_vrf_id);
+ nh = nexthop_from_ipv6(gate, nh_vrf_id);
} else
- nh = route_entry_nexthop_ifindex_add(re, index,
- nh_vrf_id);
+ nh = nexthop_from_ifindex(index, nh_vrf_id);
if (nh) {
+ nh->weight = rtnh->rtnh_hops + 1;
+
if (num_labels)
nexthop_add_labels(nh, ZEBRA_LSP_STATIC,
num_labels, labels);
if (rtnh->rtnh_flags & RTNH_F_ONLINK)
SET_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK);
+
+ /* Add to temporary list */
+ nexthop_group_add_sorted(ng, nh);
}
if (rtnh->rtnh_len == 0)
rtnh = RTNH_NEXT(rtnh);
}
- uint8_t nhop_num = nexthop_group_nexthop_num(re->ng);
-
- if (!nhop_num)
- nexthop_group_delete(&re->ng);
+ uint8_t nhop_num = nexthop_group_nexthop_num(ng);
return nhop_num;
}
} else {
/* This is a multipath route */
struct route_entry *re;
+ struct nexthop_group *ng = NULL;
struct rtnexthop *rtnh =
(struct rtnexthop *)RTA_DATA(tb[RTA_MULTIPATH]);
re->nhe_id = nhe_id;
if (!nhe_id) {
- uint8_t nhop_num =
+ uint8_t nhop_num;
+
+ /* Use temporary list of nexthops; parse
+ * message payload's nexthops.
+ */
+ ng = nexthop_group_new();
+ nhop_num =
parse_multipath_nexthops_unicast(
- ns_id, re, rtm, rtnh, tb,
+ ns_id, ng, rtm, rtnh, tb,
prefsrc, vrf_id);
zserv_nexthop_num_warn(
__func__, (const struct prefix *)&p,
nhop_num);
+
+ if (nhop_num == 0) {
+ nexthop_group_delete(&ng);
+ ng = NULL;
+ }
}
- if (nhe_id || re->ng)
+ if (nhe_id || ng)
rib_add_multipath(afi, SAFI_UNICAST, &p,
- &src_p, re);
+ &src_p, re, ng);
else
XFREE(MTYPE_RE, re);
}
} 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);
"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,
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)
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));
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;
}
unsigned char family;
int type;
afi_t afi = AFI_UNSPEC;
- vrf_id_t vrf_id = 0;
+ vrf_id_t vrf_id = VRF_DEFAULT;
struct interface *ifp = NULL;
struct nhmsg *nhm = NULL;
struct nexthop nh = {};
#define RTADV_MSG_SIZE 4096
/* Send router advertisement packet. */
-static void rtadv_send_packet(int sock, struct interface *ifp)
+static void rtadv_send_packet(int sock, struct interface *ifp,
+ ipv6_nd_suppress_ra_status stop)
{
struct msghdr msg;
struct iovec iov;
zif->rtadv.AdvDefaultLifetime != -1
? zif->rtadv.AdvDefaultLifetime
: MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval);
- rtadv->nd_ra_router_lifetime = htons(pkt_RouterLifetime);
+
+ /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */
+ rtadv->nd_ra_router_lifetime =
+ (stop == RA_SUPPRESS) ? htons(0) : htons(pkt_RouterLifetime);
rtadv->nd_ra_reachable = htonl(zif->rtadv.AdvReachableTime);
rtadv->nd_ra_retransmit = htonl(0);
ifp->name);
rtadv_send_packet(rtadv_get_socket(zvrf),
- ifp);
+ ifp, RA_ENABLE);
} else {
zif->rtadv.AdvIntervalTimer -= period;
if (zif->rtadv.AdvIntervalTimer <= 0) {
.MaxRtrAdvInterval;
rtadv_send_packet(
rtadv_get_socket(zvrf),
- ifp);
+ ifp, RA_ENABLE);
}
}
}
if ((zif->rtadv.UseFastRexmit)
|| (zif->rtadv.AdvIntervalTimer <=
(zif->rtadv.MaxRtrAdvInterval - MIN_DELAY_BETWEEN_RAS))) {
- rtadv_send_packet(rtadv_get_socket(zvrf), ifp);
+ rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_ENABLE);
zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval;
} else
zif->rtadv.AdvIntervalTimer = MIN_DELAY_BETWEEN_RAS;
if (status == RA_SUPPRESS) {
/* RA is currently enabled */
if (zif->rtadv.AdvSendAdvertisements) {
+ rtadv_send_packet(rtadv_get_socket(zvrf), ifp,
+ RA_SUPPRESS);
zif->rtadv.AdvSendAdvertisements = 0;
zif->rtadv.AdvIntervalTimer = 0;
zvrf->rtadv.adv_if_count--;
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);
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;
return;
}
+/*
+ * send router lifetime value of zero in RAs on this interface since we're
+ * ceasing to advertise and want to let our neighbors know.
+ * RFC 4861 secion 6.2.5
+ */
+void rtadv_stop_ra(struct interface *ifp)
+{
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+
+ zif = ifp->info;
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+
+ if (zif->rtadv.AdvSendAdvertisements)
+ rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_SUPPRESS);
+}
+
+/*
+ * send router lifetime value of zero in RAs on all interfaces since we're
+ * ceasing to advertise globally and want to let all of our neighbors know
+ * RFC 4861 secion 6.2.5
+ */
+void rtadv_stop_ra_all(void)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ FOR_ALL_INTERFACES (vrf, ifp)
+ rtadv_stop_ra(ifp);
+}
+
void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS)
{
zebra_interface_radv_set(client, hdr, msg, zvrf, 0);
extern void rtadv_init(struct zebra_vrf *zvrf);
extern void rtadv_terminate(struct zebra_vrf *zvrf);
+extern void rtadv_stop_ra(struct interface *ifp);
+extern void rtadv_stop_ra_all(void);
extern void rtadv_cmd_init(void);
extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS);
extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS);
$(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 \
if FPM
module_LTLIBRARIES += zebra/zebra_fpm.la
endif
+if LINUX
+module_LTLIBRARIES += zebra/zebra_cumulus_mlag.la
+endif
-man8 += $(MANBUILD)/zebra.8
+man8 += $(MANBUILD)/frr-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 = \
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 \
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)
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 \
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
#include "lib/vrf.h"
#include "lib/libfrr.h"
#include "lib/sockopt.h"
+#include "lib/lib_errors.h"
#include "zebra/zebra_router.h"
#include "zebra/rib.h"
return 1;
}
+/*
+ * Zebra error addition adds error type.
+ *
+ *
+ * 0 1
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | enum zebra_error_types |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+static void zserv_encode_error(struct stream *s, enum zebra_error_types error)
+{
+ stream_put(s, &error, sizeof(error));
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+}
+
/* Send handlers ----------------------------------------------------------- */
/* Interface is added. Send ZEBRA_INTERFACE_ADD to client. */
memcpy(&api.src_prefix, src_p, sizeof(api.src_prefix));
}
- for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next) {
+ for (nexthop = re->nhe->nhg->nexthop;
+ nexthop; nexthop = nexthop->next) {
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
continue;
api_nh = &api.nexthops[count];
api_nh->vrf_id = nexthop->vrf_id;
api_nh->type = nexthop->type;
+ api_nh->weight = nexthop->weight;
switch (nexthop->type) {
case NEXTHOP_TYPE_BLACKHOLE:
api_nh->bh_type = nexthop->bh_type;
* nexthop we are looking up. Therefore, we will just iterate
* over the top chain of nexthops.
*/
- for (nexthop = re->ng->nexthop; nexthop;
+ for (nexthop = re->nhe->nhg->nexthop; nexthop;
nexthop = nexthop->next)
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
num += zserv_encode_nexthop(s, nexthop);
struct prefix_ipv6 *src_p = NULL;
struct route_entry *re;
struct nexthop *nexthop = NULL;
+ struct nexthop_group *ng = NULL;
int i, ret;
- vrf_id_t vrf_id = 0;
+ vrf_id_t vrf_id;
struct ipaddr vtep_ip;
s = msg;
char buf_prefix[PREFIX_STRLEN];
prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix));
- zlog_debug("%s: p=%s, ZAPI_MESSAGE_LABEL: %sset, flags=0x%x",
- __func__, buf_prefix,
- (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL) ? ""
- : "un"),
- api.flags);
+ zlog_debug("%s: p=%s, flags=0x%x",
+ __func__, buf_prefix, api.flags);
}
/* Allocate new route. */
re->flags = api.flags;
re->uptime = monotime(NULL);
re->vrf_id = vrf_id;
- re->ng = nexthop_group_new();
if (api.tableid)
re->table = api.tableid;
__func__, &api.prefix,
zebra_route_string(client->proto));
- nexthop_group_delete(&re->ng);
XFREE(MTYPE_RE, re);
return;
}
+ /* Use temporary list of nexthops */
+ ng = nexthop_group_new();
+
/*
* TBD should _all_ of the nexthop add operations use
* api_nh->vrf_id instead of re->vrf_id ? I only changed
switch (api_nh->type) {
case NEXTHOP_TYPE_IFINDEX:
- nexthop = route_entry_nexthop_ifindex_add(
- re, api_nh->ifindex, api_nh->vrf_id);
+ nexthop = nexthop_from_ifindex(api_nh->ifindex,
+ api_nh->vrf_id);
break;
case NEXTHOP_TYPE_IPV4:
if (IS_ZEBRA_DEBUG_RECV) {
zlog_debug("%s: nh=%s, vrf_id=%d", __func__,
nhbuf, api_nh->vrf_id);
}
- nexthop = route_entry_nexthop_ipv4_add(
- re, &api_nh->gate.ipv4, NULL, api_nh->vrf_id);
+ nexthop = nexthop_from_ipv4(&api_nh->gate.ipv4,
+ NULL, api_nh->vrf_id);
break;
case NEXTHOP_TYPE_IPV4_IFINDEX:
__func__, nhbuf, api_nh->vrf_id,
re->vrf_id, ifindex);
}
- nexthop = route_entry_nexthop_ipv4_ifindex_add(
- re, &api_nh->gate.ipv4, NULL, ifindex,
+ nexthop = nexthop_from_ipv4_ifindex(
+ &api_nh->gate.ipv4, NULL, ifindex,
api_nh->vrf_id);
/* Special handling for IPv4 routes sourced from EVPN:
}
break;
case NEXTHOP_TYPE_IPV6:
- nexthop = route_entry_nexthop_ipv6_add(
- re, &api_nh->gate.ipv6, api_nh->vrf_id);
+ nexthop = nexthop_from_ipv6(&api_nh->gate.ipv6,
+ api_nh->vrf_id);
break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
memset(&vtep_ip, 0, sizeof(struct ipaddr));
ifindex = api_nh->ifindex;
- nexthop = route_entry_nexthop_ipv6_ifindex_add(
- re, &api_nh->gate.ipv6, ifindex,
- api_nh->vrf_id);
+ nexthop = nexthop_from_ipv6_ifindex(&api_nh->gate.ipv6,
+ ifindex,
+ api_nh->vrf_id);
/* Special handling for IPv6 routes sourced from EVPN:
* the nexthop and associated MAC need to be installed.
}
break;
case NEXTHOP_TYPE_BLACKHOLE:
- nexthop = route_entry_nexthop_blackhole_add(
- re, api_nh->bh_type);
+ nexthop = nexthop_from_blackhole(api_nh->bh_type);
break;
}
EC_ZEBRA_NEXTHOP_CREATION_FAILED,
"%s: Nexthops Specified: %d but we failed to properly create one",
__PRETTY_FUNCTION__, api.nexthop_num);
- nexthop_group_delete(&re->ng);
+ nexthop_group_delete(&ng);
XFREE(MTYPE_RE, re);
return;
}
- if (api_nh->onlink)
+
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK);
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_WEIGHT))
+ nexthop->weight = api_nh->weight;
+
/* MPLS labels for BGP-LU or Segment Routing */
- if (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL)
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)
&& api_nh->type != NEXTHOP_TYPE_IFINDEX
&& api_nh->type != NEXTHOP_TYPE_BLACKHOLE) {
enum lsp_types_t label_type;
api_nh->label_num,
&api_nh->labels[0]);
}
+
+ /* Add new nexthop to temporary list */
+ nexthop_group_add_sorted(ng, nexthop);
}
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE))
flog_warn(EC_ZEBRA_RX_SRCDEST_WRONG_AFI,
"%s: Received SRC Prefix but afi is not v6",
__PRETTY_FUNCTION__);
- nexthop_group_delete(&re->ng);
+ nexthop_group_delete(&ng);
XFREE(MTYPE_RE, re);
return;
}
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
src_p = &api.src_prefix;
- ret = rib_add_multipath(afi, api.safi, &api.prefix, src_p, re);
+ if (api.safi != SAFI_UNICAST && api.safi != SAFI_MULTICAST) {
+ flog_warn(EC_LIB_ZAPI_MISSMATCH,
+ "%s: Received safi: %d but we can only accept UNICAST or MULTICAST",
+ __func__, api.safi);
+ nexthop_group_delete(&ng);
+ XFREE(MTYPE_RE, re);
+ return;
+ }
+ ret = rib_add_multipath(afi, api.safi, &api.prefix, src_p, re, ng);
/* Stats */
switch (api.prefix.family) {
/* 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);
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 (%" PRIu8
+ ")\n",
+ family2str(zpr.rule.filter.src_ip.family),
+ 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 IP family: %s (%" PRIu8
+ ")\n",
+ family2str(zpr.rule.filter.dst_ip.family),
+ 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);
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));
/* 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
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;
}
+static void zsend_error_msg(struct zserv *client, enum zebra_error_types error,
+ struct zmsghdr *bad_hdr)
+{
+
+ struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_ERROR, bad_hdr->vrf_id);
+
+ zserv_encode_error(s, error);
+
+ client->error_cnt++;
+ zserv_send_message(client, s);
+}
+
+static void zserv_error_no_vrf(ZAPI_HANDLER_ARGS)
+{
+ if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
+ zlog_debug("ZAPI message specifies unknown VRF: %d",
+ hdr->vrf_id);
+
+ return zsend_error_msg(client, ZEBRA_NO_VRF, hdr);
+}
+
+static void zserv_error_invalid_msg_type(ZAPI_HANDLER_ARGS)
+{
+ zlog_info("Zebra received unknown command %d", hdr->command);
+
+ return zsend_error_msg(client, ZEBRA_INVALID_MSG_TYPE, hdr);
+}
+
void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_ROUTER_ID_ADD] = zread_router_id_add,
[ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete,
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)
/* lookup vrf */
zvrf = zebra_vrf_lookup_by_id(hdr.vrf_id);
- if (!zvrf) {
- if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV)
- zlog_debug("ZAPI message specifies unknown VRF: %d",
- hdr.vrf_id);
- return;
- }
+ if (!zvrf)
+ return zserv_error_no_vrf(client, &hdr, msg, zvrf);
if (hdr.command >= array_size(zserv_handlers)
|| zserv_handlers[hdr.command] == NULL)
- zlog_info("Zebra received unknown command %d", hdr.command);
- else
- zserv_handlers[hdr.command](client, &hdr, msg, zvrf);
+ return zserv_error_invalid_msg_type(client, &hdr, msg, zvrf);
+
+ zserv_handlers[hdr.command](client, &hdr, msg, zvrf);
}
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);
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(
ctx->u.rinfo.zd_safi = info->safi;
/* Copy nexthops; recursive info is included too */
- copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->ng->nexthop, NULL);
+ copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop),
+ re->nhe->nhg->nexthop, NULL);
/* Ensure that the dplane's nexthops flags are clear. */
for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop))
if (re)
copy_nexthops(&(ctx->u.pw.nhg.nexthop),
- re->ng->nexthop, NULL);
+ re->nhe->nhg->nexthop, NULL);
route_unlock_node(rn);
}
* We'll need these to do per-nexthop deletes.
*/
copy_nexthops(&(ctx->u.rinfo.zd_old_ng.nexthop),
- old_re->ng->nexthop, NULL);
+ old_re->nhe->nhg->nexthop, NULL);
#endif /* !HAVE_NETLINK */
}
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;
}
TAILQ_INIT(&(p->dp_ctx_in_q));
TAILQ_INIT(&(p->dp_ctx_out_q));
+ p->dp_flags = flags;
p->dp_priority = prio;
p->dp_fp = fp;
p->dp_start = start_fp;
.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,
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,
if (!re)
continue;
- if (nexthop_group_active_nexthop_num(re->ng) == 0)
+ if (nexthop_group_active_nexthop_num(re->nhe->nhg) == 0)
continue;
*dest_p = dest;
ri->rtm_type = RTN_UNICAST;
ri->metric = &re->metric;
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
if (ri->num_nhs >= zrouter.multipath_num)
break;
* Figure out the set of nexthops to be added to the message.
*/
num_nhs = 0;
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
if (num_nhs >= zrouter.multipath_num)
break;
#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
* 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
} 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);
}
}
/*
* Send Notification to clean private data
*/
- zebra_mlag_private_cleanup_data();
+ hook_call(zebra_mlag_private_cleanup_data);
return 0;
}
"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
* 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)
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];
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);
}
}
}
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
* ProtoBuf Encoding APIs
*/
-#ifdef HAVE_PROTOBUF
+#ifdef HAVE_PROTOBUF_VERSION_3
DEFINE_MTYPE_STATIC(ZEBRA, MLAG_PBUF, "ZEBRA MLAG PROTOBUF")
#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;
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
*/
#include "zebra/debug.h"
#include "zebra/zebra_router.h"
#include "zebra/zebra_mlag.h"
-#include "zebra/zebra_mlag_private.h"
#include <sys/un.h>
*
*/
-#ifdef HAVE_CUMULUS
-
static struct thread_master *zmlag_master;
static int mlag_socket;
/*
* 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;
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;
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;
/*
* 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;
return 0;
}
-int zebra_mlag_private_close_channel(void)
+static int zebra_mlag_private_close_channel(void)
{
if (zmlag_master == NULL)
return -1;
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,
+)
+++ /dev/null
-/*
- * 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
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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
* the label advertised by the recursive nexthop (plus we don't have the
* logic yet to push multiple labels).
*/
- for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next) {
+ for (nexthop = re->nhe->nhg->nexthop;
+ nexthop; nexthop = nexthop->next) {
/* Skip inactive and recursive entries. */
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
continue;
|| !CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED))
continue;
- for (match_nh = match->ng->nexthop; match_nh;
+ for (match_nh = match->nhe->nhg->nexthop; match_nh;
match_nh = match_nh->next) {
if (match->type == ZEBRA_ROUTE_CONNECT
|| nexthop->ifindex == match_nh->ifindex) {
break;
}
- if (!match || !match->ng->nexthop)
+ if (!match || !match->nhe->nhg->nexthop)
return 0;
- nexthop->ifindex = match->ng->nexthop->ifindex;
+ nexthop->ifindex = match->nhe->nhg->nexthop->ifindex;
return 1;
}
nhe = zebra_nhg_rib_find(0, new_grp, afi);
- zebra_nhg_re_update_ref(re, nhe);
+ route_entry_update_nhe(re, nhe);
}
static bool mpls_ftn_update_nexthop(int add, struct nexthop *nexthop,
* We can't just change the values here since we are hashing
* on labels. We need to create a whole new group
*/
- nexthop_group_copy(&new_grp, re->ng);
+ nexthop_group_copy(&new_grp, re->nhe->nhg);
found = false;
for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) {
if (re == NULL)
return -1;
- nexthop_group_copy(&new_grp, re->ng);
+ nexthop_group_copy(&new_grp, re->nhe->nhg);
for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next)
nexthop_del_labels(nexthop);
RNODE_FOREACH_RE (rn, re) {
struct nexthop_group new_grp = {};
- nexthop_group_copy(&new_grp, re->ng);
+ nexthop_group_copy(&new_grp, re->nhe->nhg);
for (nexthop = new_grp.nexthop; nexthop;
nexthop = nexthop->next) {
/* 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 *
}
}
-static struct nhg_hash_entry *zebra_nhg_copy(struct nhg_hash_entry *copy,
- uint32_t id)
+struct nhg_hash_entry *zebra_nhg_alloc(void)
{
struct nhg_hash_entry *nhe;
nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry));
+ return nhe;
+}
+
+static struct nhg_hash_entry *zebra_nhg_copy(const struct nhg_hash_entry *copy,
+ uint32_t id)
+{
+ struct nhg_hash_entry *nhe;
+
+ nhe = zebra_nhg_alloc();
+
nhe->id = id;
nhe->nhg = nexthop_group_new();
struct nhg_hash_entry *depend = NULL;
struct nexthop_group resolved_ng = {};
- _nexthop_group_add_sorted(&resolved_ng, nh);
+ resolved_ng.nexthop = nh;
depend = zebra_nhg_rib_find(0, &resolved_ng, afi);
depends_add(nhg_depends, depend);
if (lookup.nhg->nexthop->next) {
/* Groups can have all vrfs and AF's in them */
lookup.afi = AFI_UNSPEC;
- lookup.vrf_id = 0;
+ lookup.vrf_id = VRF_DEFAULT;
} else {
switch (lookup.nhg->nexthop->type) {
case (NEXTHOP_TYPE_IFINDEX):
struct nhg_hash_entry *nhe = NULL;
struct nexthop_group nhg = {};
- _nexthop_group_add_sorted(&nhg, nh);
+ nexthop_group_add_sorted(&nhg, nh);
- zebra_nhg_find(&nhe, id, &nhg, NULL, nh->vrf_id, afi, 0);
+ zebra_nhg_find(&nhe, id, &nhg, NULL, nh->vrf_id, afi, type);
return nhe;
}
}
/* Some dependency helper functions */
-static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi)
+static struct nhg_hash_entry *depends_find_recursive(const struct nexthop *nh,
+ afi_t afi)
{
+ struct nhg_hash_entry *nhe;
struct nexthop *lookup = NULL;
- 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;
+ lookup = nexthop_dup(nh, NULL);
nhe = zebra_nhg_find_nexthop(0, lookup, afi, 0);
nexthops_free(lookup);
+ return nhe;
+}
+
+static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh,
+ afi_t afi)
+{
+ struct nhg_hash_entry *nhe;
+ struct nexthop lookup = {};
+
+ /* Capture a snapshot of this single nh; it might be part of a list,
+ * so we need to make a standalone copy.
+ */
+ nexthop_copy_no_recurse(&lookup, nh, NULL);
+
+ nhe = zebra_nhg_find_nexthop(0, &lookup, afi, 0);
+
+ /* The copy may have allocated labels; free them if necessary. */
+ nexthop_del_labels(&lookup);
+
+ return nhe;
+}
+
+static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi)
+{
+ struct nhg_hash_entry *nhe = NULL;
+
+ if (!nh)
+ goto done;
+
+ /* We are separating these functions out to increase handling speed
+ * in the non-recursive case (by not alloc/freeing)
+ */
+ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE))
+ nhe = depends_find_recursive(nh, afi);
+ else
+ nhe = depends_find_singleton(nh, afi);
+
done:
return nhe;
}
nhg_connected_tree_free(&nhe->nhg_dependents);
}
-void zebra_nhg_free(void *arg)
+void zebra_nhg_free(struct nhg_hash_entry *nhe)
{
- struct nhg_hash_entry *nhe = NULL;
-
- nhe = (struct nhg_hash_entry *)arg;
-
if (nhe->refcnt)
zlog_debug("nhe_id=%u hash refcnt=%d", nhe->id, nhe->refcnt);
XFREE(MTYPE_NHG, nhe);
}
+void zebra_nhg_hash_free(void *p)
+{
+ zebra_nhg_free((struct nhg_hash_entry *)p);
+}
+
void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe)
{
nhe->refcnt--;
if (match->type == ZEBRA_ROUTE_CONNECT) {
/* Directly point connected route. */
- newhop = match->ng->nexthop;
+ newhop = match->nhe->nhg->nexthop;
if (newhop) {
if (nexthop->type == NEXTHOP_TYPE_IPV4
|| nexthop->type == NEXTHOP_TYPE_IPV6)
return 1;
} else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
resolved = 0;
- for (ALL_NEXTHOPS_PTR(match->ng, newhop)) {
+ for (ALL_NEXTHOPS_PTR(match->nhe->nhg, newhop)) {
if (!CHECK_FLAG(match->status,
ROUTE_ENTRY_INSTALLED))
continue;
return resolved;
} else if (re->type == ZEBRA_ROUTE_STATIC) {
resolved = 0;
- for (ALL_NEXTHOPS_PTR(match->ng, newhop)) {
+ for (ALL_NEXTHOPS_PTR(match->nhe->nhg, newhop)) {
if (!CHECK_FLAG(match->status,
ROUTE_ENTRY_INSTALLED))
continue;
UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
/* Copy over the nexthops in current state */
- nexthop_group_copy(&new_grp, re->ng);
+ nexthop_group_copy(&new_grp, re->nhe->nhg);
for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) {
new_nhe = zebra_nhg_rib_find(0, &new_grp, rt_afi);
- zebra_nhg_re_update_ref(re, new_nhe);
+ route_entry_update_nhe(re, new_nhe);
}
if (curr_active) {
return curr_active;
}
-static void zebra_nhg_re_attach_ref(struct route_entry *re,
- struct nhg_hash_entry *new)
-{
- re->ng = new->nhg;
- re->nhe_id = new->id;
-
- zebra_nhg_increment_ref(new);
-}
-
-int zebra_nhg_re_update_ref(struct route_entry *re, struct nhg_hash_entry *new)
-{
- struct nhg_hash_entry *old = NULL;
- int ret = 0;
-
- if (new == NULL) {
- re->ng = NULL;
- goto done;
- }
-
- if (re->nhe_id != new->id) {
- old = zebra_nhg_lookup_id(re->nhe_id);
-
- zebra_nhg_re_attach_ref(re, new);
-
- if (old)
- zebra_nhg_decrement_ref(old);
- } else if (!re->ng)
- /* This is the first time it's being attached */
- zebra_nhg_re_attach_ref(re, new);
-
-done:
- return ret;
-}
-
/* Convert a nhe into a group array */
uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe,
int max_num)
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++;
}
#ifndef __ZEBRA_NHG_H__
#define __ZEBRA_NHG_H__
-#include "zebra/rib.h"
+#include "lib/nexthop.h"
#include "lib/nexthop_group.h"
-#include "zebra/zebra_dplane.h"
-
/* This struct is used exclusively for dataplane
* interaction via a dataplane context.
*
* NHE abstracted tree functions.
* Use these where possible instead of the direct ones access ones.
*/
+struct nhg_hash_entry *zebra_nhg_alloc(void);
+void zebra_nhg_free(struct nhg_hash_entry *nhe);
+/* In order to clear a generic hash, we need a generic api, sigh. */
+void zebra_nhg_hash_free(void *p);
+
extern struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe);
extern unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe);
/* Reference counter functions */
extern void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe);
extern void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe);
-extern int zebra_nhg_re_update_ref(struct route_entry *re,
- struct nhg_hash_entry *nhe);
/* Check validity of nhe, if invalid will update dependents as well */
extern void zebra_nhg_check_valid(struct nhg_hash_entry *nhe);
extern void zebra_nhg_sweep_table(struct hash *hash);
/* Nexthop resolution processing */
+struct route_entry; /* Forward ref to avoid circular includes */
extern int nexthop_active_update(struct route_node *rn, struct route_entry *re);
#endif
extern void nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head,
struct nhg_hash_entry *nhe);
-extern void zebra_nhg_free(void *arg);
-
#endif /* __ZEBRA_NHG_PRIVATE_H__ */
#include "zebra_pbr.h"
#include "rib.h"
#include "table_manager.h"
+#include "zebra_errors.h"
extern struct zebra_privs_t zserv_privs;
{
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);
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;
}
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();
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);
}
{
struct zebra_pbr_iptable *zpi;
struct zebra_pbr_iptable *new;
+ struct listnode *ln;
+ char *ifname;
zpi = (struct zebra_pbr_iptable *)arg;
new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable));
+ /* Deep structure copy */
memcpy(new, zpi, sizeof(*zpi));
+ new->interface_name_list = list_new();
+
+ if (zpi->interface_name_list) {
+ for (ALL_LIST_ELEMENTS_RO(zpi->interface_name_list, ln, ifname))
+ listnode_add(new->interface_name_list,
+ XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifname));
+ }
return new;
}
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",
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);
+ msgc = NULL;
/* Registrate process PID for shutdown hook. */
STREAM_GETL(msg, ppid);
return;
stream_failure:
+ if (msgc)
+ stream_free(msgc);
zlog_err("%s:%d failed to registrate client pid", __FILE__, __LINE__);
}
* Need to ensure that there's a label binding for all nexthops.
* Otherwise, ECMP for this route could render the pseudowire unusable.
*/
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
if (!nexthop->nh_label) {
if (IS_ZEBRA_DEBUG_PW)
zlog_debug("%s: unlabeled route for %s",
return 1;
}
-/* Add nexthop to the end of a rib node's nexthop list */
-void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop)
-{
- _nexthop_group_add_sorted(re->ng, nexthop);
-}
-
-
/**
* copy_nexthop - copy a nexthop to the rib structure.
*/
void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh)
{
- assert(!re->ng->nexthop);
- copy_nexthops(&re->ng->nexthop, nh, NULL);
-}
-
-/* Delete specified nexthop from the list. */
-void route_entry_nexthop_delete(struct route_entry *re, struct nexthop *nexthop)
-{
- if (nexthop->next)
- nexthop->next->prev = nexthop->prev;
- if (nexthop->prev)
- nexthop->prev->next = nexthop->next;
- else
- re->ng->nexthop = nexthop->next;
+ assert(!re->nhe->nhg->nexthop);
+ copy_nexthops(&re->nhe->nhg->nexthop, nh, NULL);
}
-
-struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *re,
- ifindex_t ifindex,
- vrf_id_t nh_vrf_id)
+static void route_entry_attach_ref(struct route_entry *re,
+ struct nhg_hash_entry *new)
{
- struct nexthop *nexthop;
-
- nexthop = nexthop_new();
- nexthop->type = NEXTHOP_TYPE_IFINDEX;
- nexthop->ifindex = ifindex;
- nexthop->vrf_id = nh_vrf_id;
-
- route_entry_nexthop_add(re, nexthop);
-
- return nexthop;
-}
-
-struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *re,
- struct in_addr *ipv4,
- struct in_addr *src,
- vrf_id_t nh_vrf_id)
-{
- struct nexthop *nexthop;
-
- nexthop = nexthop_new();
- nexthop->type = NEXTHOP_TYPE_IPV4;
- nexthop->vrf_id = nh_vrf_id;
- nexthop->gate.ipv4 = *ipv4;
- if (src)
- nexthop->src.ipv4 = *src;
-
- route_entry_nexthop_add(re, nexthop);
+ re->nhe = new;
+ re->nhe_id = new->id;
- return nexthop;
+ zebra_nhg_increment_ref(new);
}
-struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re,
- struct in_addr *ipv4,
- struct in_addr *src,
- ifindex_t ifindex,
- vrf_id_t nh_vrf_id)
+int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new)
{
- struct nexthop *nexthop;
- struct interface *ifp;
-
- nexthop = nexthop_new();
- nexthop->vrf_id = nh_vrf_id;
- nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- nexthop->gate.ipv4 = *ipv4;
- if (src)
- nexthop->src.ipv4 = *src;
- nexthop->ifindex = ifindex;
- ifp = if_lookup_by_index(nexthop->ifindex, nh_vrf_id);
- /*Pending: need to think if null ifp here is ok during bootup?
- There was a crash because ifp here was coming to be NULL */
- if (ifp)
- if (connected_is_unnumbered(ifp))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK);
-
- route_entry_nexthop_add(re, nexthop);
-
- return nexthop;
-}
-
-struct nexthop *route_entry_nexthop_ipv6_add(struct route_entry *re,
- struct in6_addr *ipv6,
- vrf_id_t nh_vrf_id)
-{
- struct nexthop *nexthop;
-
- nexthop = nexthop_new();
- nexthop->vrf_id = nh_vrf_id;
- nexthop->type = NEXTHOP_TYPE_IPV6;
- nexthop->gate.ipv6 = *ipv6;
-
- route_entry_nexthop_add(re, nexthop);
-
- return nexthop;
-}
-
-struct nexthop *route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re,
- struct in6_addr *ipv6,
- ifindex_t ifindex,
- vrf_id_t nh_vrf_id)
-{
- struct nexthop *nexthop;
-
- nexthop = nexthop_new();
- nexthop->vrf_id = nh_vrf_id;
- nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
- nexthop->gate.ipv6 = *ipv6;
- nexthop->ifindex = ifindex;
-
- route_entry_nexthop_add(re, nexthop);
+ struct nhg_hash_entry *old = NULL;
+ int ret = 0;
- return nexthop;
-}
+ if (new == NULL) {
+ re->nhe->nhg = NULL;
+ goto done;
+ }
-struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *re,
- enum blackhole_type bh_type)
-{
- struct nexthop *nexthop;
+ if (re->nhe_id != new->id) {
+ old = zebra_nhg_lookup_id(re->nhe_id);
- nexthop = nexthop_new();
- nexthop->vrf_id = VRF_DEFAULT;
- nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
- nexthop->bh_type = bh_type;
+ route_entry_attach_ref(re, new);
- route_entry_nexthop_add(re, nexthop);
+ if (old)
+ zebra_nhg_decrement_ref(old);
+ } else if (!re->nhe)
+ /* This is the first time it's being attached */
+ route_entry_attach_ref(re, new);
- return nexthop;
+done:
+ return ret;
}
struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
if (re->type != ZEBRA_ROUTE_BGP)
return 0;
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop))
if (!nexthop->nh_label || !nexthop->nh_label->num_labels)
return 0;
srcdest_rnode_prefixes(rn, &p, &src_p);
if (info->safi != SAFI_UNICAST) {
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
return;
}
if (info->safi != SAFI_UNICAST) {
UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
return;
}
re->fib_ng.nexthop = NULL;
}
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
}
/* Update real nexthop. This may actually determine if nexthop is active
* or not. */
- if (!nexthop_group_active_nexthop_num(new->ng)) {
+ if (!nexthop_group_active_nexthop_num(new->nhe->nhg)) {
UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED);
return;
}
/* Update the nexthop; we could determine here that nexthop is
* inactive. */
- if (nexthop_group_active_nexthop_num(new->ng))
+ if (nexthop_group_active_nexthop_num(new->nhe->nhg))
nh_active = 1;
/* If nexthop is active, install the selected route, if
/* both are connected. are either loop or vrf? */
struct nexthop *nexthop = NULL;
- for (ALL_NEXTHOPS_PTR(alternate->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(alternate->nhe->nhg, nexthop)) {
struct interface *ifp = if_lookup_by_index(
nexthop->ifindex, alternate->vrf_id);
return alternate;
}
- for (ALL_NEXTHOPS_PTR(current->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(current->nhe->nhg, nexthop)) {
struct interface *ifp = if_lookup_by_index(
nexthop->ifindex, current->vrf_id);
SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED);
- for (ALL_NEXTHOPS_PTR(re->ng, nhop)) {
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nhop)) {
if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE))
continue;
ctx_nexthop = dplane_ctx_get_ng(ctx)->nexthop;
+ /* Nothing installed - we can skip some of the checking/comparison
+ * of nexthops.
+ */
+ if (ctx_nexthop == NULL) {
+ changed_p = true;
+ goto no_nexthops;
+ }
+
/* Get the first `installed` one to check against.
* If the dataplane doesn't set these to be what was actually installed,
- * it will just be whatever was in re->ng?
+ * it will just be whatever was in re->nhe->nhg?
*/
if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE)
|| !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE))
ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop);
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
continue;
goto done;
}
+no_nexthops:
+
/* FIB nexthop set differs from the RIB set:
* create a fib-specific nexthop-group
*/
/* 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;
}
* 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
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;
}
/*
/* 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",
/* 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)
nhe = zebra_nhg_lookup_id(re->nhe_id);
if (nhe)
zebra_nhg_decrement_ref(nhe);
- } else if (re->ng)
- nexthop_group_delete(&re->ng);
+ } else if (re->nhe->nhg)
+ nexthop_group_delete(&re->nhe->nhg);
nexthops_free(re->fib_ng.nexthop);
"%s: metric == %u, mtu == %u, distance == %u, flags == %u, status == %u",
straddr, re->metric, re->mtu, re->distance, re->flags, re->status);
zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", straddr,
- nexthop_group_nexthop_num(re->ng),
- nexthop_group_active_nexthop_num(re->ng));
+ nexthop_group_nexthop_num(re->nhe->nhg),
+ nexthop_group_active_nexthop_num(re->nhe->nhg));
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
}
int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
- struct prefix_ipv6 *src_p, struct route_entry *re)
+ struct prefix_ipv6 *src_p, struct route_entry *re,
+ struct nexthop_group *ng)
{
struct nhg_hash_entry *nhe = NULL;
struct route_table *table;
table = zebra_vrf_get_table_with_table_id(afi, safi, re->vrf_id,
re->table);
if (!table) {
- if (re->ng)
- nexthop_group_delete(&re->ng);
+ if (ng)
+ nexthop_group_delete(&ng);
XFREE(MTYPE_RE, re);
return 0;
}
return -1;
}
} else {
- nhe = zebra_nhg_rib_find(0, re->ng, afi);
+ nhe = zebra_nhg_rib_find(0, ng, afi);
/*
* The nexthops got copied over into an nhe,
* so free them now.
*/
- nexthop_group_delete(&re->ng);
+ nexthop_group_delete(&ng);
if (!nhe) {
char buf[PREFIX_STRLEN] = "";
* level protocols, as the refcnt might be wrong, since it checks
* if old_id != new_id.
*/
- zebra_nhg_re_update_ref(re, nhe);
+ route_entry_update_nhe(re, nhe);
/* Make it sure prefixlen is applied to the prefix. */
apply_mask(p);
if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != metric)
continue;
- if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->ng->nexthop)
+ if (re->type == ZEBRA_ROUTE_CONNECT &&
+ (rtnh = re->nhe->nhg->nexthop)
&& rtnh->type == NEXTHOP_TYPE_IFINDEX && nh) {
if (rtnh->ifindex != nh->ifindex)
continue;
same = re;
break;
}
- for (ALL_NEXTHOPS_PTR(re->ng, rtnh)) {
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, rtnh)) {
/*
* No guarantee all kernel send nh with labels
* on delete.
if (allow_delete) {
UNSET_FLAG(fib->status, ROUTE_ENTRY_INSTALLED);
/* Unset flags. */
- for (rtnh = fib->ng->nexthop; rtnh;
+ for (rtnh = fib->nhe->nhg->nexthop; rtnh;
rtnh = rtnh->next)
UNSET_FLAG(rtnh->flags,
NEXTHOP_FLAG_FIB);
if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) {
struct nexthop *tmp_nh;
- for (ALL_NEXTHOPS_PTR(re->ng, tmp_nh)) {
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, tmp_nh)) {
struct ipaddr vtep_ip;
memset(&vtep_ip, 0, sizeof(struct ipaddr));
{
struct route_entry *re = NULL;
struct nexthop *nexthop = NULL;
+ struct nexthop_group *ng = NULL;
/* Allocate new route_entry structure. */
re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
re->tag = tag;
re->nhe_id = nhe_id;
+ /* If the owner of the route supplies a shared nexthop-group id,
+ * we'll use that. Otherwise, pass the nexthop along directly.
+ */
if (!nhe_id) {
- re->ng = nexthop_group_new();
+ ng = nexthop_group_new();
/* Add nexthop. */
nexthop = nexthop_new();
*nexthop = *nh;
- route_entry_nexthop_add(re, nexthop);
+ nexthop_group_add_sorted(ng, nexthop);
}
- return rib_add_multipath(afi, safi, p, src_p, re);
+ return rib_add_multipath(afi, safi, p, src_p, re, ng);
}
static const char *rib_update_event2str(rib_update_event_t event)
* this decision needs to be revisited
*/
SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
rib_uninstall_kernel(rn, re);
struct nexthop *nexthop;
if (re) {
- for (nexthop = re->ng->nexthop; nexthop;
+ for (nexthop = re->nhe->nhg->nexthop; nexthop;
nexthop = nexthop->next) {
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RNH_FILTERED);
}
route_map_result_t ret;
if (prn && re) {
- for (nexthop = re->ng->nexthop; nexthop;
+ for (nexthop = re->nhe->nhg->nexthop; nexthop;
nexthop = nexthop->next) {
ret = zebra_nht_route_map_check(
afi, proto, &prn->p, zvrf, re, nexthop);
/* Just being SELECTED isn't quite enough - must
* have an installed nexthop to be useful.
*/
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
if (rnh_nexthop_valid(re, nexthop))
break;
}
break;
if (re->type == ZEBRA_ROUTE_NHRP) {
- for (nexthop = re->ng->nexthop; nexthop;
+ for (nexthop = re->nhe->nhg->nexthop;
+ nexthop;
nexthop = nexthop->next)
if (nexthop->type
== NEXTHOP_TYPE_IFINDEX)
return;
/* free RE and nexthops */
- nexthop_group_delete(&re->ng);
+ zebra_nhg_free(re->nhe);
XFREE(MTYPE_RE, re);
}
state->metric = re->metric;
state->vrf_id = re->vrf_id;
state->status = re->status;
- state->ng = nexthop_group_new();
- route_entry_copy_nexthops(state, re->ng->nexthop);
+ state->nhe = zebra_nhg_alloc();
+ state->nhe->nhg = nexthop_group_new();
+
+ nexthop_group_copy(state->nhe->nhg, re->nhe->nhg);
rnh->state = state;
}
if (r1->metric != r2->metric)
return 1;
- if (nexthop_group_nexthop_num(r1->ng)
- != nexthop_group_nexthop_num(r2->ng))
+ if (nexthop_group_nexthop_num(r1->nhe->nhg)
+ != nexthop_group_nexthop_num(r2->nhe->nhg))
return 1;
- if (nexthop_group_hash(r1->ng) != nexthop_group_hash(r2->ng))
+ if (nexthop_group_hash(r1->nhe->nhg) !=
+ nexthop_group_hash(r2->nhe->nhg))
return 1;
return 0;
break;
}
if (re) {
+ struct zapi_nexthop znh;
+
stream_putc(s, re->type);
stream_putw(s, re->instance);
stream_putc(s, re->distance);
num = 0;
nump = stream_get_endp(s);
stream_putc(s, 0);
- for (ALL_NEXTHOPS_PTR(re->ng, nh))
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nh))
if (rnh_nexthop_valid(re, nh)) {
- stream_putl(s, nh->vrf_id);
- stream_putc(s, nh->type);
- switch (nh->type) {
- case NEXTHOP_TYPE_IPV4:
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- stream_put_in_addr(s, &nh->gate.ipv4);
- stream_putl(s, nh->ifindex);
- break;
- case NEXTHOP_TYPE_IFINDEX:
- stream_putl(s, nh->ifindex);
- break;
- case NEXTHOP_TYPE_IPV6:
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- stream_put(s, &nh->gate.ipv6, 16);
- stream_putl(s, nh->ifindex);
- break;
- default:
- /* do nothing */
- break;
- }
- if (nh->nh_label) {
- stream_putc(s,
- nh->nh_label->num_labels);
- if (nh->nh_label->num_labels)
- stream_put(
- s,
- &nh->nh_label->label[0],
- nh->nh_label->num_labels
- * sizeof(mpls_label_t));
- } else
- stream_putc(s, 0);
+ zapi_nexthop_from_nexthop(&znh, nh);
+ zapi_nexthop_encode(s, &znh, 0 /* flags */);
num++;
}
stream_putc_at(s, nump, num);
if (rnh->state) {
vty_out(vty, " resolved via %s\n",
zebra_route_string(rnh->state->type));
- for (nexthop = rnh->state->ng->nexthop; nexthop;
+ for (nexthop = rnh->state->nhe->nhg->nexthop; nexthop;
nexthop = nexthop->next)
print_nh(nexthop, vty);
} else
#include "zebra_pbr.h"
#include "zebra_vxlan.h"
#include "zebra_mlag.h"
-#include "zebra_nhg_private.h"
+#include "zebra_nhg.h"
#include "debug.h"
DEFINE_MTYPE_STATIC(ZEBRA, RIB_TABLE_INFO, "RIB table info")
zebra_vxlan_disable();
zebra_mlag_terminate();
- hash_clean(zrouter.nhgs, zebra_nhg_free);
+ hash_clean(zrouter.nhgs, zebra_nhg_hash_free);
hash_free(zrouter.nhgs);
hash_clean(zrouter.nhgs_id, NULL);
hash_free(zrouter.nhgs_id);
return;
}
- if (in_addr_cmp((uint8_t *)&(*re)->ng->nexthop->gate.ipv4,
- (uint8_t *)&re2->ng->nexthop->gate.ipv4)
+ if (in_addr_cmp((uint8_t *)&(*re)->nhe->nhg->nexthop->gate.ipv4,
+ (uint8_t *)&re2->nhe->nhg->nexthop->gate.ipv4)
<= 0)
return;
if (!in_addr_cmp(&(*np)->p.u.prefix,
(uint8_t *)&dest)) {
RNODE_FOREACH_RE (*np, *re) {
- if (!in_addr_cmp((uint8_t *)&(*re)
- ->ng->nexthop
- ->gate.ipv4,
+ if (!in_addr_cmp((uint8_t *)&(*re)->nhe
+ ->nhg->nexthop
+ ->gate.ipv4,
(uint8_t *)&nexthop))
if (proto
== proto_trans((*re)->type))
|| ((policy == policy2) && (proto < proto2))
|| ((policy == policy2) && (proto == proto2)
&& (in_addr_cmp(
- (uint8_t *)&re2->ng->nexthop
- ->gate.ipv4,
+ (uint8_t *)&re2->nhe
+ ->nhg->nexthop->gate.ipv4,
(uint8_t *)&nexthop)
>= 0)))
check_replace(np2, re2, np, re);
{
struct nexthop *nexthop;
- nexthop = (*re)->ng->nexthop;
+ nexthop = (*re)->nhe->nhg->nexthop;
if (nexthop) {
pnt = (uint8_t *)&nexthop->gate.ipv4;
for (i = 0; i < 4; i++)
if (!np)
return NULL;
- nexthop = re->ng->nexthop;
+ nexthop = re->nhe->nhg->nexthop;
if (!nexthop)
return NULL;
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,
if (show_ng)
vty_out(vty, " Nexthop Group ID: %u\n", re->nhe_id);
- for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->nhe->nhg, nexthop)) {
char addrstr[32];
vty_out(vty, " %c%s",
sizeof buf, 1));
}
+ if (nexthop->weight)
+ vty_out(vty, ", weight %u", nexthop->weight);
+
vty_out(vty, "\n");
}
vty_out(vty, "\n");
if (is_fib)
nhg = rib_active_nhg(re);
else
- nhg = re->ng;
+ nhg = re->nhe->nhg;
if (json) {
json_route = json_object_new_object();
json_object_int_add(json_route, "internalFlags",
re->flags);
json_object_int_add(json_route, "internalNextHopNum",
- nexthop_group_nexthop_num(re->ng));
+ nexthop_group_nexthop_num(re->nhe->nhg));
json_object_int_add(json_route, "internalNextHopActiveNum",
- nexthop_group_active_nexthop_num(re->ng));
+ nexthop_group_active_nexthop_num(
+ re->nhe->nhg));
if (uptime < ONE_DAY_SECOND)
sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
tm->tm_sec);
sizeof(buf), 1));
}
+ if (nexthop->weight)
+ vty_out(vty, ", weight %u", nexthop->weight);
+
vty_out(vty, "\n");
}
DEFPY (show_nexthop_group,
show_nexthop_group_cmd,
- "show nexthop-group <(0-4294967295)$id|[<ip$v4|ipv6$v6>] [vrf <NAME$vrf_name|all$vrf_all>]>",
+ "show nexthop-group rib <(0-4294967295)$id|[<ip$v4|ipv6$v6>] [vrf <NAME$vrf_name|all$vrf_all>]>",
SHOW_STR
"Show Nexthop Groups\n"
+ "RIB information\n"
"Nexthop Group ID\n"
IP_STR
IP6_STR
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
"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;
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;
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;
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
}
}
- 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");
+ }
}
/*
*
*/
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;
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) {
fib_cnt[ZEBRA_ROUTE_TOTAL]++;
fib_cnt[re->type]++;
}
- for (nexthop = re->ng->nexthop; (!cnt && nexthop);
+ for (nexthop = re->nhe->nhg->nexthop; (!cnt && nexthop);
nexthop = nexthop->next) {
cnt++;
rib_cnt[ZEBRA_ROUTE_TOTAL]++;
}
}
- 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");
+ }
}
/*
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.
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",
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",
struct interface *ifp;
ifp = if_lookup_by_index_all_vrf(zif->link_ifindex);
- zlog_debug("macvlan %s parent link is not found. Parent index %d ifp %s",
- ifp->name, zif->link_ifindex,
- ifp ? ifp->name : " ");
+ zlog_debug("macvlan parent link is not found. Parent index %d ifp %s",
+ zif->link_ifindex, ifp ? ifp->name : " ");
}
return;
}
s = msg;
STREAM_GETC(s, advertise);
- vni = stream_get3(s);
+ STREAM_GET(&vni, s, 3);
zvni = zvni_lookup(vni);
if (!zvni)
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;
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;
/* Free bitmaps. */
for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) {
- for (int i = 0; i < ZEBRA_ROUTE_MAX; i++)
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) {
vrf_bitmap_free(client->redist[afi][i]);
+ redist_del_all_instances(&client->mi_redist[afi][i]);
+ }
vrf_bitmap_free(client->redist_default[afi]);
}
uint32_t v6_nh_watch_rem_cnt;
uint32_t vxlan_sg_add_cnt;
uint32_t vxlan_sg_del_cnt;
+ uint32_t error_cnt;
time_t nh_reg_time;
time_t nh_dereg_time;