install_element(BABEL_NODE, &babel_ipv6_distribute_list_cmd);
install_element(BABEL_NODE, &babel_no_ipv6_distribute_list_cmd);
+ vrf_cmd_init(NULL, &babeld_privs);
+
babel_if_init();
/* Access list install. */
{
bgp_attr_unintern(&bai->attr);
BGP_ADJ_IN_DEL(dest, bai);
+ bgp_dest_unlock_node(dest);
peer_unlock(bai->peer); /* adj_in peer reference */
XFREE(MTYPE_BGP_ADJ_IN, bai);
}
while (adj) {
adj_next = adj->next;
- if (adj->peer == peer && adj->addpath_rx_id == addpath_id) {
+ if (adj->peer == peer && adj->addpath_rx_id == addpath_id)
bgp_adj_in_remove(dest, adj);
- bgp_dest_unlock_node(dest);
- }
adj = adj_next;
}
if (!seg || seg->length == 0)
return 0;
- if (seg) {
- /*
- * Hey, what do we do when we have > STREAM_WRITABLE(s) here?
- * At the moment, we would write out a partial aspath, and our
- * peer
- * will complain and drop the session :-/
- *
- * The general assumption here is that many things tested will
- * never happen. And, in real live, up to now, they have not.
- */
- while (seg && (ASSEGMENT_LEN(seg, use32bit)
- <= STREAM_WRITEABLE(s))) {
- struct assegment *next = seg->next;
- int written = 0;
- int asns_packed = 0;
- size_t lenp;
-
- /* Overlength segments have to be split up */
- while ((seg->length - written) > AS_SEGMENT_MAX) {
- assegment_header_put(s, seg->type,
- AS_SEGMENT_MAX);
- assegment_data_put(s, (seg->as + written), AS_SEGMENT_MAX,
- use32bit);
- written += AS_SEGMENT_MAX;
- bytes += ASSEGMENT_SIZE(AS_SEGMENT_MAX,
- use32bit);
- }
-
- /* write the final segment, probably is also the first
- */
- lenp = assegment_header_put(s, seg->type,
- seg->length - written);
+ /*
+ * Hey, what do we do when we have > STREAM_WRITABLE(s) here?
+ * At the moment, we would write out a partial aspath, and our
+ * peer
+ * will complain and drop the session :-/
+ *
+ * The general assumption here is that many things tested will
+ * never happen. And, in real live, up to now, they have not.
+ */
+ while (seg && (ASSEGMENT_LEN(seg, use32bit) <= STREAM_WRITEABLE(s))) {
+ struct assegment *next = seg->next;
+ int written = 0;
+ int asns_packed = 0;
+ size_t lenp;
+
+ /* Overlength segments have to be split up */
+ while ((seg->length - written) > AS_SEGMENT_MAX) {
+ assegment_header_put(s, seg->type, AS_SEGMENT_MAX);
assegment_data_put(s, (seg->as + written),
- seg->length - written, use32bit);
+ AS_SEGMENT_MAX, use32bit);
+ written += AS_SEGMENT_MAX;
+ bytes += ASSEGMENT_SIZE(AS_SEGMENT_MAX, use32bit);
+ }
- /* Sequence-type segments can be 'packed' together
- * Case of a segment which was overlength and split up
- * will be missed here, but that doesn't matter.
+ /* write the final segment, probably is also the first
+ */
+ lenp = assegment_header_put(s, seg->type,
+ seg->length - written);
+ assegment_data_put(s, (seg->as + written),
+ seg->length - written, use32bit);
+
+ /* Sequence-type segments can be 'packed' together
+ * Case of a segment which was overlength and split up
+ * will be missed here, but that doesn't matter.
+ */
+ while (next && ASSEGMENTS_PACKABLE(seg, next)) {
+ /* NB: We should never normally get here given
+ * we
+ * normalise aspath data when parse them.
+ * However, better
+ * safe than sorry. We potentially could call
+ * assegment_normalise here instead, but it's
+ * cheaper and
+ * easier to do it on the fly here rather than
+ * go through
+ * the segment list twice every time we write
+ * out
+ * aspath's.
*/
- while (next && ASSEGMENTS_PACKABLE(seg, next)) {
- /* NB: We should never normally get here given
- * we
- * normalise aspath data when parse them.
- * However, better
- * safe than sorry. We potentially could call
- * assegment_normalise here instead, but it's
- * cheaper and
- * easier to do it on the fly here rather than
- * go through
- * the segment list twice every time we write
- * out
- * aspath's.
- */
-
- /* Next segment's data can fit in this one */
- assegment_data_put(s, next->as, next->length,
- use32bit);
-
- /* update the length of the segment header */
- stream_putc_at(s, lenp,
- seg->length - written
- + next->length);
- asns_packed += next->length;
-
- next = next->next;
- }
- bytes += ASSEGMENT_SIZE(
- seg->length - written + asns_packed, use32bit);
- seg = next;
+ /* Next segment's data can fit in this one */
+ assegment_data_put(s, next->as, next->length, use32bit);
+
+ /* update the length of the segment header */
+ stream_putc_at(s, lenp,
+ seg->length - written + next->length);
+ asns_packed += next->length;
+
+ next = next->next;
}
+
+ bytes += ASSEGMENT_SIZE(seg->length - written + asns_packed,
+ use32bit);
+ seg = next;
}
return bytes;
}
struct in6_addr ipv6;
};
+enum overlay_index_type {
+ OVERLAY_INDEX_TYPE_NONE,
+ OVERLAY_INDEX_GATEWAY_IP,
+ OVERLAY_INDEX_ESI,
+ OVERLAY_INDEX_MAC,
+};
+
+/*
+ * Structure to store ovrelay index for EVPN type-5 route
+ * This structure stores ESI and Gateway IP overlay index.
+ * MAC overlay index is stored in the RMAC attribute.
+ */
struct bgp_route_evpn {
+ enum overlay_index_type type;
+ esi_t eth_s_id;
union gw_addr gw_ip;
};
}
if (bss->state == BSS_UP && bss->previous_state != BSS_UP
- && peer->status != Established) {
+ && !peer_established(peer)) {
if (!BGP_PEER_START_SUPPRESSED(peer)) {
bgp_fsm_nht_update(peer, true);
BGP_EVENT_ADD(peer, BGP_Start);
#define BGP_BMP_MAX_PACKET_SIZE 1024
s = stream_new(BGP_MAX_PACKET_SIZE);
- if (peer->status == Established && !down) {
+ if (peer_established(peer) && !down) {
struct bmp_bgp_peer *bbpeer;
bmp_common_hdr(s, BMP_VERSION_3,
zlog_info("bmp: skipping queued item for deleted peer");
goto out;
}
- if (peer->status != Established)
+ if (!peer_established(peer))
goto out;
bn = bgp_node_lookup(bmp->targets->bgp->rib[afi][safi], &bqe->p);
for (ALL_LIST_ELEMENTS_RO(bt->bgp->peer, node, peer)) {
size_t count = 0, count_pos, len;
- if (peer->status != Established)
+ if (!peer_established(peer))
continue;
s = stream_new(BGP_MAX_PACKET_SIZE);
if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
continue;
- if (peer->status != Established)
+ if (!peer_established(peer))
continue;
FOREACH_AFI_SAFI (afi, safi) {
* list head entry. */
assert(bdc->reuse_offset < bdc->reuse_list_size);
plist = bdc->reuse_list[bdc->reuse_offset];
- node = SLIST_FIRST(&plist);
SLIST_INIT(&bdc->reuse_list[bdc->reuse_offset]);
/* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby
/* If dampening is not enabled or there is no dampening information,
return immediately. */
- if (!bdc || !bdi)
+ if (!bdi)
return NULL;
/* Calculate new penalty. */
}
int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi,
- uint8_t show_flags)
+ uint16_t show_flags)
{
struct bgp *bgp;
safi_t safi, bool use_json,
json_object *json);
extern int bgp_show_dampening_parameters(struct vty *vty, afi_t, safi_t,
- uint8_t);
+ uint16_t);
extern void bgp_peer_damp_enable(struct peer *peer, afi_t afi, safi_t safi,
time_t half, unsigned int reuse,
unsigned int suppress, time_t max);
union prefixconstptr pu,
mpls_label_t *label, uint32_t num_labels,
int addpath_valid, uint32_t addpath_id,
+ struct bgp_route_evpn *overlay_index,
char *str, int size)
{
char rd_buf[RD_ADDRSTRLEN];
char tag_buf[30];
+ char overlay_index_buf[INET6_ADDRSTRLEN + 14];
+ const struct prefix_evpn *evp;
+
/* ' with addpath ID ' 17
* max strlen of uint32 + 10
* +/- (just in case) + 1
snprintf(pathid_buf, sizeof(pathid_buf), " with addpath ID %u",
addpath_id);
+ overlay_index_buf[0] = '\0';
+ if (overlay_index && overlay_index->type == OVERLAY_INDEX_GATEWAY_IP) {
+ char obuf[INET6_ADDRSTRLEN];
+
+ obuf[0] = '\0';
+ evp = pu.evp;
+ if (is_evpn_prefix_ipaddr_v4(evp))
+ inet_ntop(AF_INET, &overlay_index->gw_ip, obuf,
+ sizeof(obuf));
+ else if (is_evpn_prefix_ipaddr_v6(evp))
+ inet_ntop(AF_INET6, &overlay_index->gw_ip, obuf,
+ sizeof(obuf));
+
+ snprintf(overlay_index_buf, sizeof(overlay_index_buf),
+ " gateway IP %s", obuf);
+ }
+
tag_buf[0] = '\0';
if (bgp_labeled_safi(safi) && num_labels) {
}
if (prd)
- snprintfrr(str, size, "RD %s %pFX%s%s %s %s",
+ snprintfrr(str, size, "RD %s %pFX%s%s%s %s %s",
prefix_rd2str(prd, rd_buf, sizeof(rd_buf)), pu.p,
- tag_buf, pathid_buf, afi2str(afi), safi2str(safi));
+ overlay_index_buf, tag_buf, pathid_buf, afi2str(afi),
+ safi2str(safi));
else if (safi == SAFI_FLOWSPEC) {
char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX];
const struct prefix_fs *fs = pu.fs;
#define DUMP_DETAIL 32
/* RD + Prefix + Path-Id */
-#define BGP_PRD_PATH_STRLEN (PREFIX_STRLEN + RD_ADDRSTRLEN + 20)
+#define BGP_PRD_PATH_STRLEN \
+ (PREFIX_STRLEN + RD_ADDRSTRLEN + INET6_ADDRSTRLEN + 34)
extern int dump_open;
extern int dump_update;
extern bool bgp_debug_bestpath(struct bgp_dest *dest);
extern bool bgp_debug_zebra(const struct prefix *p);
-extern const char *
-bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, const struct prefix_rd *prd,
- union prefixconstptr pu, mpls_label_t *label,
- uint32_t num_labels, int addpath_valid,
- uint32_t addpath_id, char *str, int size);
+extern const char *bgp_debug_rdpfxpath2str(
+ afi_t afi, safi_t safi, const struct prefix_rd *prd,
+ union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels,
+ int addpath_valid, uint32_t addpath_id,
+ struct bgp_route_evpn *overlay_index, char *str, int size);
const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data,
size_t datalen);
#include "bgpd/bgp_addpath.h"
#include "bgpd/bgp_mac.h"
#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_nht.h"
/*
* Definitions and external declarations.
* Static function declarations
*/
static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn);
+static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *evpn);
+static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *evpn);
+static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn,
+ struct bgp_path_info *pi);
+static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn,
+ struct bgp_path_info *pi);
+static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn,
+ void (*func)(struct hash_bucket *,
+ void *),
+ void *arg);
+static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn);
+static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp,
+ struct bgpevpn *vpn);
+static unsigned int vni_svi_hash_key_make(const void *p);
+static bool vni_svi_hash_cmp(const void *p1, const void *p2);
+static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn,
+ struct ipaddr *addr,
+ bool resolve);
+static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket,
+ void *args);
+static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket,
+ void *args);
static struct in_addr zero_vtep_ip;
/*
/* update evpn type-5 route entry */
static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
- struct attr *src_attr)
+ struct attr *src_attr, afi_t src_afi,
+ safi_t src_safi)
{
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ if (src_afi == AFI_IP6 &&
+ CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) {
+ if (src_attr &&
+ !IN6_IS_ADDR_UNSPECIFIED(&src_attr->mp_nexthop_global)) {
+ attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP;
+ memcpy(&attr.evpn_overlay.gw_ip.ipv6,
+ &src_attr->mp_nexthop_global,
+ sizeof(struct in6_addr));
+ }
+ } else if (src_afi == AFI_IP &&
+ CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) {
+ if (src_attr && src_attr->nexthop.s_addr != 0) {
+ attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP;
+ memcpy(&attr.evpn_overlay.gw_ip.ipv4,
+ &src_attr->nexthop, sizeof(struct in_addr));
+ }
+ }
+
/* Setup RT and encap extended community */
build_evpn_type5_route_extcomm(bgp_vrf, &attr);
dest = bgp_route_next(dest)) {
for (pi = bgp_dest_get_bgp_path_info(dest);
(pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) {
+ bgp_evpn_remote_ip_hash_del(vpn, pi);
bgp_path_info_delete(dest, pi);
bgp_path_info_reap(dest, pi);
}
bool new_pi = false;
bool use_l3nhg = false;
bool is_l3nhg_active = false;
+ char buf1[INET6_ADDRSTRLEN];
memset(pp, 0, sizeof(struct prefix));
ip_prefix_from_evpn_prefix(evp, pp);
* make sure to set the flag for next hop attribute.
*/
attr = *parent_pi->attr;
- if (afi == AFI_IP6)
- evpn_convert_nexthop_to_ipv6(&attr);
- else
- attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ if (attr.evpn_overlay.type != OVERLAY_INDEX_GATEWAY_IP) {
+ if (afi == AFI_IP6)
+ evpn_convert_nexthop_to_ipv6(&attr);
+ else
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ } else {
+
+ /*
+ * If gateway IP overlay index is specified in the NLRI of
+ * EVPN RT-5, this gateway IP should be used as the nexthop
+ * for the prefix in the VRF
+ */
+ if (bgp_debug_zebra(NULL)) {
+ zlog_debug(
+ "Install gateway IP %s as nexthop for prefix %pFX in vrf %s",
+ inet_ntop(pp->family, &attr.evpn_overlay.gw_ip,
+ buf1, sizeof(buf1)), pp,
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+
+ if (afi == AFI_IP6) {
+ memcpy(&attr.mp_nexthop_global,
+ &attr.evpn_overlay.gw_ip.ipv6,
+ sizeof(struct in6_addr));
+ attr.mp_nexthop_len = IPV6_MAX_BYTELEN;
+ } else {
+ attr.nexthop = attr.evpn_overlay.gw_ip.ipv4;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ }
+ }
bgp_evpn_es_vrf_use_nhg(bgp_vrf, &parent_pi->attr->esi, &use_l3nhg,
&is_l3nhg_active, NULL);
pi->attr = attr_new;
pi->uptime = bgp_clock();
}
- /* as it is an importation, change nexthop */
- bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF);
+
+ /* Gateway IP nexthop should be resolved */
+ if (attr.evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) {
+ if (bgp_find_or_add_nexthop(bgp_vrf, bgp_vrf, afi, safi, pi,
+ NULL, 0))
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID);
+ else {
+ if (BGP_DEBUG(nht, NHT)) {
+ inet_ntop(pp->family,
+ &attr.evpn_overlay.gw_ip,
+ buf1, sizeof(buf1));
+ zlog_debug("%s: gateway IP NH unresolved",
+ buf1);
+ }
+ bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID);
+ }
+ } else {
+
+ /* as it is an importation, change nexthop */
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF);
+ }
/* Link path to evpn nexthop */
bgp_evpn_path_nh_add(bgp_vrf, pi);
pi->uptime = bgp_clock();
}
+ /* Add this route to remote IP hashtable */
+ bgp_evpn_remote_ip_hash_add(vpn, pi);
+
/* Perform route selection and update zebra, if required. */
ret = evpn_route_select_install(bgp, vpn, dest);
&& (struct bgp_path_info *)pi->extra->parent == parent_pi)
break;
- if (!pi)
+ if (!pi) {
+ bgp_dest_unlock_node(dest);
return 0;
+ }
if (bgp_debug_zebra(NULL))
zlog_debug("... delete dest %p (l %d) pi %p (l %d, f 0x%x)",
&& (struct bgp_path_info *)pi->extra->parent == parent_pi)
break;
- if (!pi)
+ if (!pi) {
+ bgp_dest_unlock_node(dest);
return 0;
+ }
+
+ bgp_evpn_remote_ip_hash_del(vpn, pi);
/* Mark entry for deletion */
bgp_path_info_delete(dest, pi);
mpls_label_t label; /* holds the VNI as in the packet */
int ret;
afi_t gw_afi;
- bool is_valid_update = false;
+ bool is_valid_update = true;
/* Type-5 route should be 34 or 58 bytes:
* RD (8), ESI (10), Eth Tag (4), IP len (1), IP (4 or 16),
/* Additional information outside of prefix - ESI and GW IP */
memset(&evpn, 0, sizeof(evpn));
- /* Fetch ESI */
+ /* Fetch ESI overlay index */
if (attr)
- memcpy(&attr->esi, pfx, sizeof(esi_t));
+ memcpy(&evpn.eth_s_id, pfx, sizeof(esi_t));
pfx += ESI_BYTES;
/* Fetch Ethernet Tag. */
* field
*/
+ /*
+ * An update containing a non-zero gateway IP and a non-zero ESI
+ * at the same time is should be treated as withdraw
+ */
+ if (bgp_evpn_is_esi_valid(&evpn.eth_s_id)
+ && !is_zero_gw_ip(&evpn.gw_ip, gw_afi)) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%s - Rx EVPN Type-5 ESI and gateway-IP both non-zero.",
+ peer->host);
+ is_valid_update = false;
+ } else if (bgp_evpn_is_esi_valid(&evpn.eth_s_id))
+ evpn.type = OVERLAY_INDEX_ESI;
+ else if (!is_zero_gw_ip(&evpn.gw_ip, gw_afi))
+ evpn.type = OVERLAY_INDEX_GATEWAY_IP;
if (attr) {
- is_valid_update = true;
- if (is_zero_mac(&attr->rmac) &&
- is_zero_gw_ip(&evpn.gw_ip, gw_afi))
+ if (is_zero_mac(&attr->rmac)
+ && !bgp_evpn_is_esi_valid(&evpn.eth_s_id)
+ && is_zero_gw_ip(&evpn.gw_ip, gw_afi) && label == 0) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%s - Rx EVPN Type-5 ESI, gateway-IP, RMAC and label all zero",
+ peer->host);
is_valid_update = false;
+ }
if (is_mcast_mac(&attr->rmac) || is_bcast_mac(&attr->rmac))
is_valid_update = false;
}
/* Process the route. */
- if (is_valid_update)
+ if (attr && is_valid_update)
ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
&prd, &label, 1, 0, &evpn);
- else
+ else {
+ if (!is_valid_update) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(attr, attr_str, BUFSIZ);
+ zlog_warn(
+ "Invalid update from peer %s vrf %u prefix %pFX attr %s - treat as withdraw",
+ peer->hostname, peer->bgp->vrf_id, &p,
+ attr_str);
+ }
ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
&prd, &label, 1, &evpn);
+ }
return ret;
}
/* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */
stream_putc(s, 8 + 10 + 4 + 1 + len + 3);
stream_put(s, prd->val, 8);
- if (attr)
+ if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_ESI)
stream_put(s, &attr->esi, sizeof(esi_t));
else
stream_put(s, 0, sizeof(esi_t));
stream_put_ipv4(s, p_evpn_p->prefix_addr.ip.ipaddr_v4.s_addr);
else
stream_put(s, &p_evpn_p->prefix_addr.ip.ipaddr_v6, 16);
- if (attr) {
+ if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) {
const struct bgp_route_evpn *evpn_overlay =
bgp_attr_get_evpn_overlay(attr);
struct prefix_evpn evp;
build_type5_prefix_from_ip_prefix(&evp, p);
- ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr);
+ ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr, afi, safi);
if (ret)
flog_err(EC_BGP_EVPN_ROUTE_CREATE,
"%u: Failed to create type-5 route for prefix %pFX",
struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id,
- struct in_addr mcast_grp)
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex)
{
struct bgpevpn *vpn;
vpn->originator_ip = originator_ip;
vpn->tenant_vrf_id = tenant_vrf_id;
vpn->mcast_grp = mcast_grp;
+ vpn->svi_ifindex = svi_ifindex;
/* Initialize route-target import and export lists */
vpn->import_rtl = list_new();
return NULL;
}
+ bgp_evpn_remote_ip_hash_init(vpn);
+ bgp_evpn_link_to_vni_svi_hash(bgp, vpn);
+
/* add to l2vni list on corresponding vrf */
bgpevpn_link_to_l3vni(vpn);
*/
void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
{
+ bgp_evpn_remote_ip_hash_destroy(vpn);
bgp_evpn_vni_es_cleanup(vpn);
bgpevpn_unlink_from_l3vni(vpn);
bgp_table_unlock(vpn->route_table);
list_delete(&vpn->import_rtl);
list_delete(&vpn->export_rtl);
bf_release_index(bm->rd_idspace, vpn->rd_id);
+ hash_release(bgp->vni_svi_hash, vpn);
hash_release(bgp->vnihash, vpn);
QOBJ_UNREG(vpn);
XFREE(MTYPE_BGP_EVPN, vpn);
} else {
/* Re-instate the current remote best path if any */
dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p);
- if (dest)
+ if (dest) {
evpn_zebra_reinstall_best_route(bgp, vpn, dest);
+ bgp_dest_unlock_node(dest);
+ }
}
return 0;
*/
delete_routes_for_vni(bgp, vpn);
+ bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn);
+
+ vpn->svi_ifindex = 0;
/*
* tunnel is no longer active, del tunnel ip address from tip_hash
*/
int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id,
- struct in_addr mcast_grp)
-
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex)
{
struct bgpevpn *vpn;
struct prefix_evpn p;
if (is_vni_live(vpn)
&& IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)
&& IPV4_ADDR_SAME(&vpn->mcast_grp, &mcast_grp)
- && vpn->tenant_vrf_id == tenant_vrf_id)
+ && vpn->tenant_vrf_id == tenant_vrf_id
+ && vpn->svi_ifindex == svi_ifindex)
/* Probably some other param has changed that we don't
* care about. */
return 0;
bgp_evpn_mcast_grp_change(bgp, vpn, mcast_grp);
+ if (vpn->svi_ifindex != svi_ifindex) {
+
+ /*
+ * Unresolve all the gateway IP nexthops for this VNI
+ * for old SVI
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_unlink_nexthop,
+ vpn);
+ bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn);
+ vpn->svi_ifindex = svi_ifindex;
+ bgp_evpn_link_to_vni_svi_hash(bgp, vpn);
+
+ /*
+ * Resolve all the gateway IP nexthops for this VNI
+ * for new SVI
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_link_nexthop,
+ vpn);
+ }
+
/* Update tenant_vrf_id if it has changed. */
if (vpn->tenant_vrf_id != tenant_vrf_id) {
+
+ /*
+ * Unresolve all the gateway IP nexthops for this VNI
+ * in old tenant vrf
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_unlink_nexthop,
+ vpn);
bgpevpn_unlink_from_l3vni(vpn);
vpn->tenant_vrf_id = tenant_vrf_id;
bgpevpn_link_to_l3vni(vpn);
+
+ /*
+ * Resolve all the gateway IP nexthops for this VNI
+ * in new tenant vrf
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_link_nexthop,
+ vpn);
}
/* If tunnel endpoint IP has changed, update (and delete prior
/* Create or update as appropriate. */
if (!vpn) {
vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id,
- mcast_grp);
+ mcast_grp, svi_ifindex);
if (!vpn) {
flog_err(
EC_BGP_VNI,
hash_free(bgp->vrf_import_rt_hash);
bgp->vrf_import_rt_hash = NULL;
+ hash_free(bgp->vni_svi_hash);
+ bgp->vni_svi_hash = NULL;
hash_free(bgp->vnihash);
bgp->vnihash = NULL;
{
bgp->vnihash =
hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash");
+ bgp->vni_svi_hash =
+ hash_create(vni_svi_hash_key_make, vni_svi_hash_cmp,
+ "BGP VNI hash based on SVI ifindex");
bgp->import_rt_hash =
hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
"BGP Import RT Hash");
return false;
}
+
+static void *bgp_evpn_remote_ip_hash_alloc(void *p)
+{
+ const struct evpn_remote_ip *key = (const struct evpn_remote_ip *)p;
+ struct evpn_remote_ip *ip;
+
+ ip = XMALLOC(MTYPE_EVPN_REMOTE_IP, sizeof(struct evpn_remote_ip));
+ *ip = *key;
+ ip->macip_path_list = list_new();
+
+ return ip;
+}
+
+static unsigned int bgp_evpn_remote_ip_hash_key_make(const void *p)
+{
+ const struct evpn_remote_ip *ip = p;
+ const struct ipaddr *addr = &ip->addr;
+
+ if (IS_IPADDR_V4(addr))
+ return jhash_1word(addr->ipaddr_v4.s_addr, 0);
+
+ return jhash2(addr->ipaddr_v6.s6_addr32,
+ array_size(addr->ipaddr_v6.s6_addr32), 0);
+}
+
+static bool bgp_evpn_remote_ip_hash_cmp(const void *p1, const void *p2)
+{
+ const struct evpn_remote_ip *ip1 = p1;
+ const struct evpn_remote_ip *ip2 = p2;
+
+ return (memcmp(&ip1->addr, &ip2->addr, sizeof(struct ipaddr)) == 0);
+}
+
+static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *vpn)
+{
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ vpn->remote_ip_hash = hash_create(bgp_evpn_remote_ip_hash_key_make,
+ bgp_evpn_remote_ip_hash_cmp,
+ "BGP EVPN remote IP hash");
+}
+
+static void bgp_evpn_remote_ip_hash_free(struct hash_bucket *bucket, void *args)
+{
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+ struct bgpevpn *vpn = (struct bgpevpn *)args;
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false);
+
+ list_delete(&ip->macip_path_list);
+
+ hash_release(vpn->remote_ip_hash, ip);
+ XFREE(MTYPE_EVPN_REMOTE_IP, ip);
+}
+
+static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *vpn)
+{
+ if (!evpn_resolve_overlay_index() || vpn->remote_ip_hash == NULL)
+ return;
+
+ hash_iterate(vpn->remote_ip_hash,
+ (void (*)(struct hash_bucket *, void *))bgp_evpn_remote_ip_hash_free,
+ vpn);
+
+ hash_free(vpn->remote_ip_hash);
+ vpn->remote_ip_hash = NULL;
+}
+
+/* Add a remote MAC/IP route to hash table */
+static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn,
+ struct bgp_path_info *pi)
+{
+ struct evpn_remote_ip tmp;
+ struct evpn_remote_ip *ip;
+ struct prefix_evpn *evp;
+
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ if (pi->type != ZEBRA_ROUTE_BGP || pi->sub_type != BGP_ROUTE_IMPORTED
+ || !CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+ return;
+
+ evp = (struct prefix_evpn *)&pi->net->p;
+
+ if (evp->family != AF_EVPN
+ || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE
+ || is_evpn_prefix_ipaddr_none(evp))
+ return;
+
+ tmp.addr = evp->prefix.macip_addr.ip;
+ ip = hash_lookup(vpn->remote_ip_hash, &tmp);
+ if (ip) {
+ if (listnode_lookup(ip->macip_path_list, pi) != NULL)
+ return;
+ (void)listnode_add(ip->macip_path_list, pi);
+ return;
+ }
+
+ ip = hash_get(vpn->remote_ip_hash, &tmp, bgp_evpn_remote_ip_hash_alloc);
+ if (!ip)
+ return;
+
+ (void)listnode_add(ip->macip_path_list, pi);
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true);
+}
+
+/* Delete a remote MAC/IP route from hash table */
+static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn,
+ struct bgp_path_info *pi)
+{
+ struct evpn_remote_ip tmp;
+ struct evpn_remote_ip *ip;
+ struct prefix_evpn *evp;
+
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ evp = (struct prefix_evpn *)&pi->net->p;
+
+ if (evp->family != AF_EVPN
+ || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE
+ || is_evpn_prefix_ipaddr_none(evp))
+ return;
+
+ tmp.addr = evp->prefix.macip_addr.ip;
+ ip = hash_lookup(vpn->remote_ip_hash, &tmp);
+ if (ip == NULL)
+ return;
+
+ listnode_delete(ip->macip_path_list, pi);
+
+ if (ip->macip_path_list->count == 0) {
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false);
+ hash_release(vpn->remote_ip_hash, ip);
+ XFREE(MTYPE_EVPN_REMOTE_IP, ip);
+ }
+}
+
+static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn,
+ void (*func)(struct hash_bucket *,
+ void *),
+ void *arg)
+{
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ hash_iterate(vpn->remote_ip_hash, func, arg);
+}
+
+static void show_remote_ip_entry(struct hash_bucket *bucket, void *args)
+{
+ char buf[INET6_ADDRSTRLEN];
+ char buf2[EVPN_ROUTE_STRLEN];
+ struct prefix_evpn *evp;
+
+ struct listnode *node = NULL;
+ struct bgp_path_info *pi = NULL;
+ struct vty *vty = (struct vty *)args;
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+
+ vty_out(vty, " Remote IP: %s\n",
+ ipaddr2str(&ip->addr, buf, sizeof(buf)));
+ vty_out(vty, " Linked MAC/IP routes:\n");
+ for (ALL_LIST_ELEMENTS_RO(ip->macip_path_list, node, pi)) {
+ evp = (struct prefix_evpn *)&pi->net->p;
+ prefix2str(evp, buf2, sizeof(buf2));
+ vty_out(vty, " %s\n", buf2);
+ }
+}
+
+void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, void *args)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ struct vty *vty = (struct vty *)args;
+
+ vty_out(vty, "VNI: %u\n", vpn->vni);
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))show_remote_ip_entry,
+ vty);
+ vty_out(vty, "\n");
+}
+
+static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket,
+ void *args)
+{
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+ struct bgpevpn *vpn = (struct bgpevpn *)args;
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true);
+}
+
+static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket,
+ void *args)
+{
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+ struct bgpevpn *vpn = (struct bgpevpn *)args;
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false);
+}
+
+static unsigned int vni_svi_hash_key_make(const void *p)
+{
+ const struct bgpevpn *vpn = p;
+
+ return jhash_1word(vpn->svi_ifindex, 0);
+}
+
+static bool vni_svi_hash_cmp(const void *p1, const void *p2)
+{
+ const struct bgpevpn *vpn1 = p1;
+ const struct bgpevpn *vpn2 = p2;
+
+ return (vpn1->svi_ifindex == vpn2->svi_ifindex);
+}
+
+static struct bgpevpn *bgp_evpn_vni_svi_hash_lookup(struct bgp *bgp,
+ ifindex_t svi)
+{
+ struct bgpevpn *vpn;
+ struct bgpevpn tmp;
+
+ memset(&tmp, 0, sizeof(struct bgpevpn));
+ tmp.svi_ifindex = svi;
+ vpn = hash_lookup(bgp->vni_svi_hash, &tmp);
+ return vpn;
+}
+
+static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ if (vpn->svi_ifindex == 0)
+ return;
+
+ hash_get(bgp->vni_svi_hash, vpn, hash_alloc_intern);
+}
+
+static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp,
+ struct bgpevpn *vpn)
+{
+ if (vpn->svi_ifindex == 0)
+ return;
+
+ hash_release(bgp->vni_svi_hash, vpn);
+}
+
+void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args)
+{
+ struct bgpevpn *evpn = (struct bgpevpn *)bucket->data;
+ struct vty *vty = (struct vty *)args;
+
+ vty_out(vty, "SVI: %u VNI: %u\n", evpn->svi_ifindex, evpn->vni);
+}
+
+/*
+ * This function is called for a bgp_nexthop_cache entry when the nexthop is
+ * gateway IP overlay index.
+ * This function returns true if there is a remote MAC/IP route for the gateway
+ * IP in the EVI of the nexthop SVI.
+ */
+bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc)
+{
+ struct bgp *bgp_evpn = NULL;
+ struct bgpevpn *vpn = NULL;
+ struct evpn_remote_ip tmp;
+ struct prefix *p;
+
+ if (!evpn_resolve_overlay_index())
+ return false;
+
+ if (!bnc->nexthop || bnc->nexthop->ifindex == 0)
+ return false;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return false;
+
+ /*
+ * Gateway IP is resolved by nht over SVI interface.
+ * Use this SVI to find corresponding EVI(L2 context)
+ */
+ vpn = bgp_evpn_vni_svi_hash_lookup(bgp_evpn, bnc->nexthop->ifindex);
+ if (!vpn)
+ return false;
+
+ if (vpn->bgp_vrf != bnc->bgp)
+ return false;
+
+ /*
+ * Check if the gateway IP is present in the EVI remote_ip_hash table
+ * which stores all the remote IP addresses received via MAC/IP routes
+ * in this EVI
+ */
+ memset(&tmp, 0, sizeof(struct evpn_remote_ip));
+
+ p = &bnc->prefix;
+ if (p->family == AF_INET) {
+ tmp.addr.ipa_type = IPADDR_V4;
+ memcpy(&(tmp.addr.ipaddr_v4), &(p->u.prefix4),
+ sizeof(struct in_addr));
+ } else if (p->family == AF_INET6) {
+ tmp.addr.ipa_type = IPADDR_V6;
+ memcpy(&(tmp.addr.ipaddr_v6), &(p->u.prefix6),
+ sizeof(struct in6_addr));
+ } else
+ return false;
+
+ if (hash_lookup(vpn->remote_ip_hash, &tmp) == NULL)
+ return false;
+
+ return true;
+}
+
+/* Resolve/Unresolve nexthops when a MAC/IP route is added/deleted */
+static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn,
+ struct ipaddr *addr,
+ bool resolve)
+{
+ afi_t afi;
+ struct prefix p;
+ struct bgp_nexthop_cache *bnc;
+ struct bgp_nexthop_cache_head *tree = NULL;
+
+ if (!vpn->bgp_vrf || vpn->svi_ifindex == 0)
+ return;
+
+ memset(&p, 0, sizeof(struct prefix));
+
+ if (addr->ipa_type == IPADDR_V4) {
+ afi = AFI_IP;
+ p.family = AF_INET;
+ memcpy(&(p.u.prefix4), &(addr->ipaddr_v4),
+ sizeof(struct in_addr));
+ p.prefixlen = IPV4_MAX_BITLEN;
+ } else if (addr->ipa_type == IPADDR_V6) {
+ afi = AFI_IP6;
+ p.family = AF_INET6;
+ memcpy(&(p.u.prefix6), &(addr->ipaddr_v6),
+ sizeof(struct in6_addr));
+ p.prefixlen = IPV6_MAX_BITLEN;
+ } else
+ return;
+
+ tree = &vpn->bgp_vrf->nexthop_cache_table[afi];
+ bnc = bnc_find(tree, &p, 0);
+
+ if (!bnc || !bnc->is_evpn_gwip_nexthop)
+ return;
+
+ if (!bnc->nexthop || bnc->nexthop->ifindex != vpn->svi_ifindex)
+ return;
+
+ if (BGP_DEBUG(nht, NHT)) {
+ char buf[PREFIX2STR_BUFFER];
+
+ prefix2str(&bnc->prefix, buf, sizeof(buf));
+ zlog_debug("%s(%u): vni %u mac/ip %s for NH %s",
+ vpn->bgp_vrf->name_pretty, vpn->tenant_vrf_id,
+ vpn->vni, (resolve ? "add" : "delete"), buf);
+ }
+
+ /*
+ * MAC/IP route or SVI or tenant vrf being added to EVI.
+ * Set nexthop as valid only if it is already L3 reachable
+ */
+ if (resolve && bnc->flags & BGP_NEXTHOP_EVPN_INCOMPLETE) {
+ bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->flags |= BGP_NEXTHOP_VALID;
+ bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED;
+ evaluate_paths(bnc);
+ }
+
+ /* MAC/IP route or SVI or tenant vrf being deleted from EVI */
+ if (!resolve && bnc->flags & BGP_NEXTHOP_VALID) {
+ bnc->flags &= ~BGP_NEXTHOP_VALID;
+ bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED;
+ evaluate_paths(bnc);
+ }
+}
+
+void bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+ bgp_evpn_remote_ip_hash_init(vpn);
+
+ for (dest = bgp_table_top(vpn->route_table); dest;
+ dest = bgp_route_next(dest))
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ bgp_evpn_remote_ip_hash_add(vpn, pi);
+}
+
+void bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+
+ bgp_evpn_remote_ip_hash_destroy(vpn);
+}
if (!bgp_vrf->l3vni)
return 0;
- if (afi == AFI_IP &&
- CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST))
+ if ((afi == AFI_IP)
+ && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))
+ || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))))
return 1;
- if (afi == AFI_IP6 &&
- CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))
+ if ((afi == AFI_IP6)
+ && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))
+ || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))))
return 1;
return 0;
return true;
}
+static inline bool evpn_resolve_overlay_index(void)
+{
+ struct bgp *bgp = NULL;
+
+ bgp = bgp_get_evpn();
+ return bgp ? bgp->resolve_overlay_index : false;
+}
+
extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf,
const struct prefix *p,
struct attr *src_attr, afi_t afi,
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id,
- struct in_addr mcast_grp);
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex);
extern void bgp_evpn_flood_control_change(struct bgp *bgp);
extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp);
extern void bgp_evpn_cleanup(struct bgp *bgp);
extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx);
extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx);
extern void update_advertise_vrf_routes(struct bgp *bgp_vrf);
+extern void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket,
+ void *args);
+extern void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args);
+extern bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc);
+extern void
+bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket,
+ void *arg);
+extern void
+bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket,
+ void *arg);
+
#endif /* _QUAGGA_BGP_EVPN_H */
parent_pi)
break;
- if (!pi)
+ if (!pi) {
+ bgp_dest_unlock_node(dest);
return 0;
+ }
/* Mark entry for deletion */
bgp_path_info_delete(dest, pi);
/* Lookup ESI hash - should exist. */
es = bgp_evpn_es_find(esi);
if (!es) {
- flog_warn(EC_BGP_EVPN_ESI,
- "%u: ES %s missing at local ES DEL",
- bgp->vrf_id, es->esi_str);
+ flog_warn(EC_BGP_EVPN_ESI, "%u: ES missing at local ES DEL",
+ bgp->vrf_id);
return -1;
}
{
struct bgpevpn *vpn = es_evi->vpn;
- if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
- return es_evi;
-
UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL);
list_delete_node(vpn->local_es_evi_list, &es_evi->l2vni_listnode);
struct bgpevpn {
vni_t vni;
vrf_id_t tenant_vrf_id;
+ ifindex_t svi_ifindex;
uint32_t flags;
#define VNI_FLAG_CFGD 0x1 /* VNI is user configured */
#define VNI_FLAG_LIVE 0x2 /* VNI is "live" */
struct list *import_rtl;
struct list *export_rtl;
+ /*
+ * EVPN route that uses gateway IP overlay index as its nexthop
+ * needs to do a recursive lookup.
+ * A remote MAC/IP entry should be present for the gateway IP.
+ * Maintain a hash of the addresses received via remote MAC/IP routes
+ * for efficient gateway IP recursive lookup in this EVI
+ */
+ struct hash *remote_ip_hash;
+
/* Route table for EVPN routes for
* this VNI. */
struct bgp_table *route_table;
bool is_anycast_mac;
};
+/* This structure defines an entry in remote_ip_hash */
+struct evpn_remote_ip {
+ struct ipaddr addr;
+ struct list *macip_path_list;
+};
+
static inline int is_vrf_rd_configured(struct bgp *bgp_vrf)
{
return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD));
extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id,
- struct in_addr mcast_grp);
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex);
extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn);
extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni);
extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn);
int detail;
};
+int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, int *oly_idx,
+ enum overlay_index_type *oly)
+{
+ *oly = OVERLAY_INDEX_TYPE_NONE;
+ if (argv_find(argv, argc, "gateway-ip", oly_idx)) {
+ if (oly)
+ *oly = OVERLAY_INDEX_GATEWAY_IP;
+ }
+ return 1;
+}
+
static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt,
json_object *json)
{
else
json_object_string_add(json, "advertiseSviMacIp",
"Disabled");
+ json_object_string_add(
+ json, "sviInterface",
+ ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id));
} else {
vty_out(vty, "VNI: %d", vpn->vni);
if (is_vni_live(vpn))
else
vty_out(vty, " Advertise-svi-macip : %s\n",
"Disabled");
+ vty_out(vty, " SVI interface : %s\n",
+ ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id));
}
if (!json)
static void bgp_evpn_show_routes_mac_ip_evi_es(struct vty *vty, esi_t *esi,
json_object *json, int detail)
{
- return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, false);
+ bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, false);
}
static void bgp_evpn_show_routes_mac_ip_global_es(struct vty *vty, esi_t *esi,
json_object *json, int detail)
{
- return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, true);
+ bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, true);
}
static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
/* tenant vrf will be updated when we get local_vni_add from
* zebra
*/
- vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp);
+ vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp, 0);
if (!vpn) {
flog_err(
EC_BGP_VNI,
if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
if (!json)
vty_out(vty, "%% Network not in table\n");
+
+ if (dest)
+ bgp_dest_unlock_node(dest);
+
return;
}
vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
path_cnt);
}
+
+ bgp_dest_unlock_node(dest);
}
/*
if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
if (!json)
vty_out(vty, "%% Network not in table\n");
+
+ if (dest)
+ bgp_dest_unlock_node(dest);
+
return;
}
vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
path_cnt);
}
+
+ bgp_dest_unlock_node(dest);
}
/* Disaplay EVPN routes for a ESI - VTY handler */
if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
if (!json)
vty_out(vty, "%% Network not in table\n");
+
+ if (dest)
+ bgp_dest_unlock_node(dest);
+
return;
}
vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
path_cnt);
}
+
+ bgp_dest_unlock_node(dest);
}
/*
return;
table = bgp_dest_get_bgp_table_info(rd_dest);
- if (table == NULL)
+ if (table == NULL) {
+ bgp_dest_unlock_node(rd_dest);
return;
+ }
if (json) {
json_rd = json_object_new_object();
json_object_string_add(json_rd, "rd", rd_str);
}
+ bgp_dest_unlock_node(rd_dest);
+
/* Display all prefixes with this RD. */
for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
const struct prefix_evpn *evp =
json_rd = NULL;
}
}
+
+ bgp_dest_unlock_node(dest);
}
if (json) {
bgp_evpn_cleanup_on_disable(bgp);
}
+/* Set resolve overlay index flag */
+static void bgp_evpn_set_unset_resolve_overlay_index(struct bgp *bgp, bool set)
+{
+ if (set == bgp->resolve_overlay_index)
+ return;
+
+ if (set) {
+ bgp->resolve_overlay_index = true;
+ hash_iterate(bgp->vnihash,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_handle_resolve_overlay_index_set,
+ NULL);
+ } else {
+ hash_iterate(
+ bgp->vnihash,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_handle_resolve_overlay_index_unset,
+ NULL);
+ bgp->resolve_overlay_index = false;
+ }
+}
+
/*
* EVPN - use RFC8365 to auto-derive RT
*/
DEFUN (bgp_evpn_advertise_type5,
bgp_evpn_advertise_type5_cmd,
- "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]",
+ "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [gateway-ip] [route-map WORD]",
"Advertise prefix routes\n"
BGP_AFI_HELP_STR
BGP_SAFI_HELP_STR
+ "advertise gateway IP overlay index\n"
"route-map for filtering specific routes\n"
"Name of the route map\n")
{
safi_t safi = 0;
int ret = 0;
int rmap_changed = 0;
+ enum overlay_index_type oly = OVERLAY_INDEX_TYPE_NONE;
+ int idx_oly = 0;
+ bool adv_flag_changed = false;
argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+ argv_find_and_parse_oly_idx(argv, argc, &idx_oly, &oly);
+
ret = argv_find(argv, argc, "route-map", &idx_rmap);
if (ret) {
if (!bgp_vrf->adv_cmd_rmap[afi][safi].name)
return CMD_WARNING;
}
+ if ((oly != OVERLAY_INDEX_TYPE_NONE)
+ && (oly != OVERLAY_INDEX_GATEWAY_IP)) {
+ vty_out(vty, "%%Unknown overlay-index type specified");
+ return CMD_WARNING;
+ }
+
if (afi == AFI_IP) {
+ if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))
+ && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {
+
+ /*
+ * this is the case for first time ever configuration
+ * adv ipv4 unicast is enabled for the first time.
+ * So no need to reset any flag
+ */
+ if (oly == OVERLAY_INDEX_TYPE_NONE)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ else if (oly == OVERLAY_INDEX_GATEWAY_IP)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
+ } else if ((oly == OVERLAY_INDEX_TYPE_NONE)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))) {
+
+ /*
+ * This is modify case from gateway-ip
+ * to no overlay index
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ } else if ((oly == OVERLAY_INDEX_GATEWAY_IP)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {
+
+ /*
+ * This is modify case from no overlay index
+ * to gateway-ip
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
+ } else {
- /* if we are already advertising ipv4 prefix as type-5
- * nothing to do
- */
- if (!rmap_changed &&
- CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST))
- return CMD_WARNING;
- SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST);
+ /*
+ * Command is issued with the same option
+ * (no overlay index or gateway-ip) which was
+ * already configured. So nothing to do.
+ * However, route-map may have been modified.
+ * check if route-map has been modified.
+ * If not, return an error
+ */
+ if (!rmap_changed)
+ return CMD_WARNING;
+ }
} else {
+ if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))
+ && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) {
+
+ /*
+ * this is the case for first time ever configuration
+ * adv ipv6 unicast is enabled for the first time.
+ * So no need to reset any flag
+ */
+ if (oly == OVERLAY_INDEX_TYPE_NONE)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ else if (oly == OVERLAY_INDEX_GATEWAY_IP)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
+ } else if ((oly == OVERLAY_INDEX_TYPE_NONE)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))) {
+
+ /*
+ * This is modify case from gateway-ip
+ * to no overlay index
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ } else if ((oly == OVERLAY_INDEX_GATEWAY_IP)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) {
+
+ /*
+ * This is modify case from no overlay index
+ * to gateway-ip
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
+ } else {
- /* if we are already advertising ipv6 prefix as type-5
- * nothing to do
- */
- if (!rmap_changed &&
- CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))
- return CMD_WARNING;
- SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST);
+ /*
+ * Command is issued with the same option
+ * (no overlay index or gateway-ip) which was
+ * already configured. So nothing to do.
+ * However, route-map may have been modified.
+ * check if route-map has been modified.
+ * If not, return an error
+ */
+ if (!rmap_changed)
+ return CMD_WARNING;
+ }
}
- if (rmap_changed) {
+ if ((rmap_changed) || (adv_flag_changed)) {
+
+ /* If either of these are changed, then FRR needs to
+ * withdraw already advertised type5 routes.
+ */
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
- if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
- XFREE(MTYPE_ROUTE_MAP_NAME,
- bgp_vrf->adv_cmd_rmap[afi][safi].name);
- route_map_counter_decrement(
+ if (rmap_changed) {
+ if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_vrf->adv_cmd_rmap[afi][safi].name);
+ route_map_counter_decrement(
bgp_vrf->adv_cmd_rmap[afi][safi].map);
- bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
- bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
+ bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
+ bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
+ }
}
}
/* if we are not advertising ipv4 prefix as type-5
* nothing to do
*/
- if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) {
+ if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) ||
+ (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST);
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
}
} else {
/* if we are not advertising ipv6 prefix as type-5
* nothing to do
*/
- if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) {
+ if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) ||
+ (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))){
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST);
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
}
}
return CMD_SUCCESS;
}
+DEFPY (bgp_evpn_enable_resolve_overlay_index,
+ bgp_evpn_enable_resolve_overlay_index_cmd,
+ "[no$no] enable-resolve-overlay-index",
+ NO_STR
+ "Enable Recursive Resolution of type-5 route overlay index\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (bgp != bgp_get_evpn()) {
+ vty_out(vty, "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ bgp_evpn_set_unset_resolve_overlay_index(bgp, no ? false : true);
+ return CMD_SUCCESS;
+}
+
DEFPY (bgp_evpn_advertise_pip_ip_mac,
bgp_evpn_advertise_pip_ip_mac_cmd,
"[no$no] advertise-pip [ip <A.B.C.D> [mac <X:X:X:X:X:X|X:X:X:X:X:X/M>]]",
return CMD_SUCCESS;
}
+DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_remote_ip_hash,
+ show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd,
+ "show bgp l2vpn evpn vni remote-ip-hash",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show VNI\n"
+ "Remote IP hash\n")
+{
+ struct bgp *bgp_evpn;
+ int idx = 0;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return CMD_WARNING;
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ hash_iterate(bgp_evpn->vnihash,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_evpn_show_remote_ip_hash,
+ vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_svi_hash,
+ show_bgp_l2vpn_evpn_vni_svi_hash_cmd,
+ "show bgp l2vpn evpn vni-svi-hash",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show vni-svi-hash\n")
+{
+ struct bgp *bgp_evpn;
+ int idx = 0;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return CMD_WARNING;
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ hash_iterate(bgp_evpn->vni_svi_hash,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_evpn_show_vni_svi_hash,
+ vty);
+
+ return CMD_SUCCESS;
+}
+
DEFPY(show_bgp_l2vpn_evpn_es_evi,
show_bgp_l2vpn_evpn_es_evi_cmd,
"show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]",
* Display EVPN neighbor summary.
*/
DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd,
- "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [wide] [json]",
+ "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [terse] [wide] [json]",
SHOW_STR BGP_STR
"bgp vrf\n"
"vrf name\n" L2VPN_HELP_STR EVPN_HELP_STR
"AS number\n"
"Internal (iBGP) AS sessions\n"
"External (eBGP) AS sessions\n"
+ "Shorten the information on BGP instances\n"
"Increase table width for longer output\n" JSON_STR)
{
int idx_vrf = 0;
char *neighbor = NULL;
as_t as = 0; /* 0 means AS filter not set */
int as_type = AS_UNSPECIFIED;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
if (argv_find(argv, argc, "vrf", &idx_vrf))
vrf = argv[++idx_vrf]->arg;
as = (as_t)atoi(argv[idx + 1]->arg);
}
+ if (argv_find(argv, argc, "terse", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_TERSE);
+
if (argv_find(argv, argc, "wide", &idx))
SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
if (bgp->evpn_info->advertise_svi_macip)
vty_out(vty, " advertise-svi-ip\n");
+ if (bgp->resolve_overlay_index)
+ vty_out(vty, " enable-resolve-overlay-index\n");
+
if (bgp_mh_info->host_routes_use_l3nhg !=
BGP_EVPN_MH_USE_ES_L3NHG_DEF) {
if (bgp_mh_info->host_routes_use_l3nhg)
vty_out(vty, " flooding disable\n");
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) {
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) {
if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
vty_out(vty, " advertise ipv4 unicast route-map %s\n",
bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
else
- vty_out(vty, " advertise ipv4 unicast\n");
+ vty_out(vty,
+ " advertise ipv4 unicast\n");
+ } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) {
+ if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
+ vty_out(vty,
+ " advertise ipv4 unicast gateway-ip route-map %s\n",
+ bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
+ else
+ vty_out(vty, " advertise ipv4 unicast gateway-ip\n");
}
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) {
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) {
if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name)
- vty_out(vty, " advertise ipv6 unicast route-map %s\n",
+ vty_out(vty,
+ " advertise ipv6 unicast route-map %s\n",
+ bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name);
+ else
+ vty_out(vty,
+ " advertise ipv6 unicast\n");
+ } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) {
+ if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name)
+ vty_out(vty,
+ " advertise ipv6 unicast gateway-ip route-map %s\n",
bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name);
else
- vty_out(vty, " advertise ipv6 unicast\n");
+ vty_out(vty, " advertise ipv6 unicast gateway-ip\n");
}
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
install_element(BGP_EVPN_NODE, &bgp_evpn_use_es_l3nhg_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_rx_disable_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_tx_disable_cmd);
+ install_element(BGP_EVPN_NODE,
+ &bgp_evpn_enable_resolve_overlay_index_cmd);
/* test commands */
install_element(BGP_EVPN_NODE, &test_es_add_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_svi_hash_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
#define L2VPN_HELP_STR "Layer 2 Virtual Private Network\n"
#define EVPN_HELP_STR "Ethernet Virtual Private Network\n"
+extern int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc,
+ int *oly_idx,
+ enum overlay_index_type *oly);
+
/* Parse type from "type <ead|1|...>", return -1 on failure */
extern int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv,
int argc);
__func__, type);
}
}
- if (bpem->match_packet_length_num || bpem->match_fragment_num ||
- bpem->match_tcpflags_num || bpem->match_dscp_num ||
- bpem->match_packet_length_num || bpem->match_icmp_code_num ||
- bpem->match_icmp_type_num || bpem->match_port_num ||
- bpem->match_src_port_num || bpem->match_dst_port_num ||
- bpem->match_protocol_num || bpem->match_bitmask ||
- bpem->match_flowlabel_num)
+ if (bpem->match_packet_length_num || bpem->match_fragment_num
+ || bpem->match_tcpflags_num || bpem->match_dscp_num
+ || bpem->match_icmp_code_num || bpem->match_icmp_type_num
+ || bpem->match_port_num || bpem->match_src_port_num
+ || bpem->match_dst_port_num || bpem->match_protocol_num
+ || bpem->match_bitmask || bpem->match_flowlabel_num)
bpem->type = BGP_PBR_IPSET;
else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) ||
(bpem->match_bitmask_iprule & PREFIX_DST_PRESENT))
sizeof(bgp->update_delay_peers_resume_time));
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
- if (peer->status != Established)
+ if (!peer_established(peer))
continue;
BGP_TIMER_OFF(peer->t_routeadv);
BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0);
static void bgp_maxmed_onstartup_process_status_change(struct peer *peer)
{
- if (peer->status == Established && !peer->bgp->established) {
+ if (peer_established(peer) && !peer->bgp->established) {
bgp_maxmed_onstartup_begin(peer->bgp);
}
}
static void bgp_update_delay_process_status_change(struct peer *peer)
{
- if (peer->status == Established) {
+ if (peer_established(peer)) {
if (!peer->bgp->established++) {
bgp_update_delay_begin(peer->bgp);
zlog_info(
if (status == Established)
bgp->established_peers++;
- else if ((peer->status == Established) && (status != Established))
+ else if ((peer_established(peer)) && (status != Established))
bgp->established_peers--;
if (bgp_debug_neighbor_events(peer)) {
}
/* Increment Dropped count. */
- if (peer->status == Established) {
+ if (peer_established(peer)) {
peer->dropped++;
/* bgp log-neighbor-changes of neighbor Down */
/* Received ORF prefix-filter */
peer->orf_plist[afi][safi] = NULL;
- if ((peer->status == OpenConfirm)
- || (peer->status == Established)) {
+ if ((peer->status == OpenConfirm) || (peer_established(peer))) {
/* ORF received prefix-filter pnt */
snprintf(orf_name, sizeof(orf_name), "%s.%d.%d",
peer->host, afi, safi);
bgp_set_valid_label(&dest->local_label);
}
SET_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED);
- bgp_dest_unlock_node(dest);
bgp_process(bgp, dest, afi, safi);
+ bgp_dest_unlock_node(dest);
return 1;
}
AFI_L2VPN, SAFI_EVPN, &prd,
p, label_pnt, num_labels,
pi->addpath_rx_id ? 1 : 0,
- pi->addpath_rx_id, pfx_buf,
- sizeof(pfx_buf));
+ pi->addpath_rx_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug(
"%s skip update of %s marked as removed",
peer->host, pfx_buf);
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
continue;
- if (peer->status != Established)
+ if (!peer_established(peer))
continue;
if (CHECK_FLAG(peer->af_flags[afi][safi],
DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service");
DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id");
DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function");
+DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry");
DECLARE_MTYPE(BGP_SRV6_SID);
DECLARE_MTYPE(BGP_SRV6_FUNCTION);
+DECLARE_MTYPE(EVPN_REMOTE_IP);
+
#endif /* _QUAGGA_BGP_MEMORY_H */
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) {
if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
continue;
- if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
- BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
- BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
- BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
- BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
- BGP_CONFIG_VRF_TO_VRF_EXPORT) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
- BGP_CONFIG_VRF_TO_VRF_EXPORT) ||
- (bgp == bgp_get_evpn() &&
- (CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))) ||
- (tmp_bgp->vnihash && hashcount(tmp_bgp->vnihash))) {
+ if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP]
+ [SAFI_UNICAST],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP6]
+ [SAFI_UNICAST],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP6]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP6]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)
+ || (bgp == bgp_get_evpn()
+ && (CHECK_FLAG(
+ tmp_bgp->af_flags
+ [AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags
+ [AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags
+ [AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags
+ [AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)))
+ || (tmp_bgp->vnihash
+ && hashcount(tmp_bgp->vnihash))) {
snprintf(
args->errmsg, args->errmsg_len,
"Cannot delete default BGP instance. Dependent VRF instances exist\n");
SET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
/* Make dummy peer until read Open packet. */
- if (peer1->status == Established
+ if (peer_established(peer1)
&& CHECK_FLAG(peer1->sflags, PEER_STATUS_NSF_MODE)) {
/* If we have an existing established connection with graceful
* restart
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
if (peer->conf_if
&& (strcmp(peer->conf_if, ifc->ifp->name) == 0)
- && peer->status != Established
+ && !peer_established(peer)
&& !CHECK_FLAG(peer->flags,
PEER_FLAG_IFPEER_V6ONLY)) {
if (peer_active(peer))
bnc->metric, bnc->path_count);
if (peer)
vty_out(vty, ", peer %s", peer->host);
+ if (bnc->is_evpn_gwip_nexthop)
+ vty_out(vty, " EVPN Gateway IP");
+ vty_out(vty, "\n");
+ bgp_show_nexthops_detail(vty, bgp, bnc);
+ } else if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE)) {
+ vty_out(vty,
+ " %s overlay index unresolved [IGP metric %d], #paths %d",
+ inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix,
+ buf, sizeof(buf)),
+ bnc->metric, bnc->path_count);
+ if (bnc->is_evpn_gwip_nexthop)
+ vty_out(vty, " EVPN Gateway IP");
vty_out(vty, "\n");
bgp_show_nexthops_detail(vty, bgp, bnc);
} else {
bnc->path_count);
if (peer)
vty_out(vty, ", peer %s", peer->host);
+ if (bnc->is_evpn_gwip_nexthop)
+ vty_out(vty, " EVPN Gateway IP");
vty_out(vty, "\n");
if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))
vty_out(vty, " Must be Connected\n");
time_t last_update;
uint16_t flags;
+/*
+ * If the nexthop is EVPN gateway IP NH, VALID flag is set only if the nexthop
+ * is RIB reachable as well as MAC/IP is present
+ */
#define BGP_NEXTHOP_VALID (1 << 0)
#define BGP_NEXTHOP_REGISTERED (1 << 1)
#define BGP_NEXTHOP_CONNECTED (1 << 2)
#define BGP_STATIC_ROUTE_EXACT_MATCH (1 << 5)
#define BGP_NEXTHOP_LABELED_VALID (1 << 6)
+/*
+ * This flag is added for EVPN gateway IP nexthops.
+ * If the nexthop is RIB reachable, but a MAC/IP is not yet
+ * resolved, this flag is set.
+ * Following table explains the combination of L3 and L2 reachability w.r.t.
+ * VALID and INCOMPLETE flags
+ *
+ * | MACIP resolved | MACIP unresolved
+ *----------------|----------------|------------------
+ * L3 reachable | VALID = 1 | VALID = 0
+ * | INCOMPLETE = 0 | INCOMPLETE = 1
+ * ---------------|----------------|--------------------
+ * L3 unreachable | VALID = 0 | VALID = 0
+ * | INCOMPLETE = 0 | INCOMPLETE = 0
+ */
+#define BGP_NEXTHOP_EVPN_INCOMPLETE (1 << 7)
+
uint16_t change_flags;
#define BGP_NEXTHOP_CHANGED (1 << 0)
#define BGP_NEXTHOP_METRIC_CHANGED (1 << 1)
#define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2)
+#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3)
/* Back pointer to the cache tree this entry belongs to. */
struct bgp_nexthop_cache_head *tree;
LIST_HEAD(path_list, bgp_path_info) paths;
unsigned int path_count;
struct bgp *bgp;
+
+ /* This flag is set to TRUE for a bnc that is gateway IP overlay index
+ * nexthop.
+ */
+ bool is_evpn_gwip_nexthop;
};
extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a,
int is_bgp_static_route);
static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc,
int is_bgp_static_route);
-static void evaluate_paths(struct bgp_nexthop_cache *bnc);
static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
static int bgp_nht_ifp_initial(struct thread *thread);
}
}
+ if (pi && is_route_parent_evpn(pi))
+ bnc->is_evpn_gwip_nexthop = true;
+
if (is_bgp_static_route) {
SET_FLAG(bnc->flags, BGP_STATIC_ROUTE);
return 1;
else if (safi == SAFI_UNICAST && pi
&& pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra
- && pi->extra->num_labels) {
+ && pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) {
return bgp_isvalid_labeled_nexthop(bnc);
} else
return (bgp_isvalid_nexthop(bnc));
struct nexthop *nhlist_head = NULL;
struct nexthop *nhlist_tail = NULL;
int i;
+ bool evpn_resolved = false;
bnc->last_update = bgp_clock();
bnc->change_flags = 0;
if (!bnc->nexthop_num)
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
- bnc->flags |= BGP_NEXTHOP_VALID;
+ if (!bnc->is_evpn_gwip_nexthop)
+ bnc->flags |= BGP_NEXTHOP_VALID;
bnc->metric = nhr->metric;
bnc->nexthop_num = nhr->nexthop_num;
}
bnc_nexthop_free(bnc);
bnc->nexthop = nhlist_head;
+
+ /*
+ * Gateway IP nexthop is L3 reachable. Mark it as
+ * BGP_NEXTHOP_VALID only if it is recursively resolved with a
+ * remote EVPN RT-2.
+ * Else, mark it as BGP_NEXTHOP_EVPN_INCOMPLETE.
+ * When its mapping with EVPN RT-2 is established, unset
+ * BGP_NEXTHOP_EVPN_INCOMPLETE and set BGP_NEXTHOP_VALID.
+ */
+ if (bnc->is_evpn_gwip_nexthop) {
+ evpn_resolved = bgp_evpn_is_gateway_ip_resolved(bnc);
+
+ if (BGP_DEBUG(nht, NHT)) {
+ char buf2[PREFIX2STR_BUFFER];
+
+ prefix2str(&bnc->prefix, buf2, sizeof(buf2));
+ zlog_debug(
+ "EVPN gateway IP %s recursive MAC/IP lookup %s",
+ buf2,
+ (evpn_resolved ? "successful"
+ : "failed"));
+ }
+
+ if (evpn_resolved) {
+ bnc->flags |= BGP_NEXTHOP_VALID;
+ bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED;
+ } else {
+ bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->flags &= ~BGP_NEXTHOP_VALID;
+ }
+ }
} else {
+ bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
bnc->flags &= ~BGP_NEXTHOP_VALID;
bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID;
bnc->nexthop_num = nhr->nexthop_num;
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE);
}
}
}
* RETURNS:
* void.
*/
-static void evaluate_paths(struct bgp_nexthop_cache *bnc)
+void evaluate_paths(struct bgp_nexthop_cache *bnc)
{
struct bgp_dest *dest;
struct bgp_path_info *path;
* In case of unicast routes that were imported from vpn
* and that have labels, they are valid only if there are
* nexthops with labels
+ *
+ * If the nexthop is EVPN gateway-IP,
+ * do not check for a valid label.
*/
bool bnc_is_valid_nexthop = false;
bool path_valid = false;
- if (safi == SAFI_UNICAST &&
- path->sub_type == BGP_ROUTE_IMPORTED &&
- path->extra &&
- path->extra->num_labels) {
-
+ if (safi == SAFI_UNICAST && path->sub_type == BGP_ROUTE_IMPORTED
+ && path->extra && path->extra->num_labels
+ && (path->attr->evpn_overlay.type
+ != OVERLAY_INDEX_GATEWAY_IP)) {
bnc_is_valid_nexthop =
bgp_isvalid_labeled_nexthop(bnc) ? true : false;
} else {
*/
extern void bgp_nht_reg_enhe_cap_intfs(struct peer *peer);
extern void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer);
+extern void evaluate_paths(struct bgp_nexthop_cache *bnc);
/* APIs for setting up and allocating L3 nexthop group ids */
extern uint32_t bgp_l3nhg_id_alloc(void);
if (bgp_debug_neighbor_events(peer))
zlog_debug("Peer %s: Checking restarted", peer->host);
- if (peer->status == Established) {
+ if (peer_established(peer)) {
peer->update_delay_over = 1;
peer->bgp->restarted_peers++;
bgp_check_update_delay(peer->bgp);
if (bgp_debug_neighbor_events(peer))
zlog_debug("Peer %s: Checking implicit EORs", peer->host);
- if (peer->status == Established) {
+ if (peer_established(peer)) {
peer->update_delay_over = 1;
peer->bgp->implicit_eors++;
bgp_check_update_delay(peer->bgp);
* if peer is Established and updates are not on hold (as part of
* update-delay processing).
*/
- if (peer->status != Established)
+ if (!peer_established(peer))
return 0;
if ((peer->bgp->main_peers_update_hold)
* states. Note that a peer GR is handled by closing the existing
* connection upon receipt of new one.
*/
- if (peer->status == Established || peer->status == Clearing) {
+ if (peer_established(peer) || peer->status == Clearing) {
bgp_notify_send(new, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
return -1;
struct bgp_nlri nlris[NLRI_TYPE_MAX];
/* Status must be Established. */
- if (peer->status != Established) {
+ if (!peer_established(peer)) {
flog_err(EC_BGP_INVALID_STATUS,
"%s [FSM] Update packet received under status %s",
peer->host,
&& nlri_ret != BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW) {
flog_err(EC_BGP_UPDATE_RCV,
"%s [Error] Error parsing NLRI", peer->host);
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_notify_send(
peer, BGP_NOTIFY_UPDATE_ERR,
i <= NLRI_WITHDRAW
}
/* Status must be Established. */
- if (peer->status != Established) {
+ if (!peer_established(peer)) {
flog_err(
EC_BGP_INVALID_STATUS,
"%s [Error] Route refresh packet received under status %s",
bgp_set_stale_route(peer, afi, safi);
}
- if (peer->status == Established)
+ if (peer_established(peer))
thread_add_timer(bm->master,
bgp_refresh_stalepath_timer_expire,
paf, peer->bgp->stalepath_time,
}
/* Status must be Established. */
- if (peer->status != Established) {
+ if (!peer_established(peer)) {
flog_err(
EC_BGP_NO_CAP,
"%s [Error] Dynamic capability packet received under status %s",
peer->host, peer->fd, code);
/* Closed connection or error on the socket */
- if (peer->status == Established) {
+ if (peer_established(peer)) {
if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)
|| CHECK_FLAG(peer->flags,
PEER_FLAG_GRACEFUL_RESTART_HELPER))
};
#define VRFID_NONE_STR "-"
+#define SOFT_RECONFIG_TASK_MAX_PREFIX 25000
DEFINE_HOOK(bgp_process,
(struct bgp * bgp, afi_t afi, safi_t safi, struct bgp_dest *bn,
if (BGP_PATH_HOLDDOWN(pi1))
continue;
if (pi1->peer != bgp->peer_self)
- if (pi1->peer->status != Established)
+ if (!peer_established(pi1->peer))
continue;
new_select = pi1;
if (pi->peer && pi->peer != bgp->peer_self
&& !CHECK_FLAG(pi->peer->sflags, PEER_STATUS_NSF_WAIT))
- if (pi->peer->status != Established) {
+ if (!peer_established(pi->peer)) {
if (debug)
zlog_debug(
if (pi->peer && pi->peer != bgp->peer_self
&& !CHECK_FLAG(pi->peer->sflags,
PEER_STATUS_NSF_WAIT))
- if (pi->peer->status != Established)
+ if (!peer_established(pi->peer))
continue;
if (!bgp_path_info_nexthop_cmp(pi, new_select)) {
return;
}
+ if (CHECK_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG)) {
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug(
+ "Soft reconfigure table in progress for route %p",
+ dest);
+ return;
+ }
+
if (wq == NULL)
return;
return new;
}
-static void overlay_index_update(struct attr *attr,
- union gw_addr *gw_ip)
-{
- if (!attr)
- return;
- if (gw_ip == NULL) {
- struct bgp_route_evpn eo;
-
- memset(&eo, 0, sizeof(eo));
- bgp_attr_set_evpn_overlay(attr, &eo);
- } else {
- struct bgp_route_evpn eo = {.gw_ip = *gw_ip};
-
- bgp_attr_set_evpn_overlay(attr, &eo);
- }
-}
-
static bool overlay_index_equal(afi_t afi, struct bgp_path_info *path,
union gw_addr *gw_ip)
{
if (has_valid_label)
assert(label != NULL);
+ /* Update overlay index of the attribute */
+ if (afi == AFI_L2VPN && evpn)
+ memcpy(&attr->evpn_overlay, evpn,
+ sizeof(struct bgp_route_evpn));
+
/* When peer's soft reconfiguration enabled. Record input packet in
Adj-RIBs-In. */
if (!soft_reconfig
goto filtered;
}
- /* Update Overlay Index */
- if (afi == AFI_L2VPN) {
- overlay_index_update(&new_attr,
- evpn == NULL ? NULL : &evpn->gw_ip);
- }
-
/* The flag BGP_NODE_FIB_INSTALL_PENDING is for the following
* condition :
* Suppress fib is enabled
&& (!has_valid_label
|| memcmp(&(bgp_path_info_extra_get(pi))->label, label,
num_labels * sizeof(mpls_label_t))
- == 0)
- && (overlay_index_equal(
- afi, pi,
- evpn == NULL ? NULL : &evpn->gw_ip))) {
+ == 0)) {
if (get_active_bdc_from_pi(pi, afi, safi)
&& peer->sort == BGP_PEER_EBGP
&& CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) {
bgp_debug_rdpfxpath2str(
afi, safi, prd, p, label,
num_labels, addpath_id ? 1 : 0,
- addpath_id, pfx_buf,
+ addpath_id, evpn, pfx_buf,
sizeof(pfx_buf));
zlog_debug("%s rcvd %s", peer->host,
pfx_buf);
bgp_debug_rdpfxpath2str(
afi, safi, prd, p, label,
num_labels, addpath_id ? 1 : 0,
- addpath_id, pfx_buf,
+ addpath_id, evpn, pfx_buf,
sizeof(pfx_buf));
zlog_debug(
"%s rcvd %s...duplicate ignored",
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(
afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, evpn,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug(
"%s rcvd %s, flapped quicker than processing",
peer->host, pfx_buf);
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label,
num_labels, addpath_id ? 1 : 0,
- addpath_id, pfx_buf,
+ addpath_id, evpn, pfx_buf,
sizeof(pfx_buf));
zlog_debug("%s rcvd %s", peer->host, pfx_buf);
}
}
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, evpn,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug("%s rcvd %s", peer->host, pfx_buf);
}
}
}
- /* Update Overlay Index */
- if (afi == AFI_L2VPN) {
- overlay_index_update(new->attr,
- evpn == NULL ? NULL : &evpn->gw_ip);
- }
/* Nexthop reachability check. */
if (((afi == AFI_IP || afi == AFI_IP6)
&& (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST))
}
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, evpn,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug("%s rcvd UPDATE about %s -- DENIED due to: %s",
peer->host, pfx_buf, reason);
}
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(
afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug(
"%s withdrawing route %s not in adj-in",
peer->host, pfx_buf);
/* Logging. */
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug("%s rcvd UPDATE about %s -- withdrawn", peer->host,
pfx_buf);
}
}
} else if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug("%s Can't find the route %s", peer->host, pfx_buf);
}
paf = THREAD_ARG(t);
peer = paf->peer;
- if (peer->status != Established)
+ if (!peer_established(peer))
return 0;
if (!peer->afc_nego[paf->afi][paf->safi])
bgp_announce_route(peer, afi, safi);
}
+/* Flag or unflag bgp_dest to determine whether it should be treated by
+ * bgp_soft_reconfig_table_task.
+ * Flag if flag is true. Unflag if flag is false.
+ */
+static void bgp_soft_reconfig_table_flag(struct bgp_table *table, bool flag)
+{
+ struct bgp_dest *dest;
+ struct bgp_adj_in *ain;
+
+ if (!table)
+ return;
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ for (ain = dest->adj_in; ain; ain = ain->next) {
+ if (ain->peer != NULL)
+ break;
+ }
+ if (flag && ain != NULL && ain->peer != NULL)
+ SET_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG);
+ else
+ UNSET_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG);
+ }
+}
+
+static int bgp_soft_reconfig_table_update(struct peer *peer,
+ struct bgp_dest *dest,
+ struct bgp_adj_in *ain, afi_t afi,
+ safi_t safi, struct prefix_rd *prd)
+{
+ struct bgp_path_info *pi;
+ uint32_t num_labels = 0;
+ mpls_label_t *label_pnt = NULL;
+ struct bgp_route_evpn evpn;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->peer == peer)
+ break;
+
+ if (pi && pi->extra)
+ num_labels = pi->extra->num_labels;
+ if (num_labels)
+ label_pnt = &pi->extra->label[0];
+ if (pi)
+ memcpy(&evpn, bgp_attr_get_evpn_overlay(pi->attr),
+ sizeof(evpn));
+ else
+ memset(&evpn, 0, sizeof(evpn));
+
+ return bgp_update(peer, bgp_dest_get_prefix(dest), ain->addpath_rx_id,
+ ain->attr, afi, safi, ZEBRA_ROUTE_BGP,
+ BGP_ROUTE_NORMAL, prd, label_pnt, num_labels, 1,
+ &evpn);
+}
+
static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi,
struct bgp_table *table,
struct prefix_rd *prd)
if (ain->peer != peer)
continue;
- struct bgp_path_info *pi;
- uint32_t num_labels = 0;
- mpls_label_t *label_pnt = NULL;
- struct bgp_route_evpn evpn;
-
- for (pi = bgp_dest_get_bgp_path_info(dest); pi;
- pi = pi->next)
- if (pi->peer == peer)
- break;
-
- if (pi && pi->extra)
- num_labels = pi->extra->num_labels;
- if (num_labels)
- label_pnt = &pi->extra->label[0];
- if (pi)
- memcpy(&evpn,
- bgp_attr_get_evpn_overlay(pi->attr),
- sizeof(evpn));
- else
- memset(&evpn, 0, sizeof(evpn));
-
- ret = bgp_update(peer, bgp_dest_get_prefix(dest),
- ain->addpath_rx_id, ain->attr, afi,
- safi, ZEBRA_ROUTE_BGP,
- BGP_ROUTE_NORMAL, prd, label_pnt,
- num_labels, 1, &evpn);
+ ret = bgp_soft_reconfig_table_update(peer, dest, ain,
+ afi, safi, prd);
if (ret < 0) {
bgp_dest_unlock_node(dest);
}
}
+/* Do soft reconfig table per bgp table.
+ * Walk on SOFT_RECONFIG_TASK_MAX_PREFIX bgp_dest,
+ * when BGP_NODE_SOFT_RECONFIG is set,
+ * reconfig bgp_dest for list of table->soft_reconfig_peers peers.
+ * Schedule a new thread to continue the job.
+ * Without splitting the full job into several part,
+ * vtysh waits for the job to finish before responding to a BGP command
+ */
+static int bgp_soft_reconfig_table_task(struct thread *thread)
+{
+ uint32_t iter, max_iter;
+ int ret;
+ struct bgp_dest *dest;
+ struct bgp_adj_in *ain;
+ struct peer *peer;
+ struct bgp_table *table;
+ struct prefix_rd *prd;
+ struct listnode *node, *nnode;
+
+ table = THREAD_ARG(thread);
+ prd = NULL;
+
+ max_iter = SOFT_RECONFIG_TASK_MAX_PREFIX;
+ if (table->soft_reconfig_init) {
+ /* first call of the function with a new srta structure.
+ * Don't do any treatment this time on nodes
+ * in order vtysh to respond quickly
+ */
+ max_iter = 0;
+ }
+
+ for (iter = 0, dest = bgp_table_top(table); (dest && iter < max_iter);
+ dest = bgp_route_next(dest)) {
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG))
+ continue;
+
+ UNSET_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG);
+
+ for (ain = dest->adj_in; ain; ain = ain->next) {
+ for (ALL_LIST_ELEMENTS(table->soft_reconfig_peers, node,
+ nnode, peer)) {
+ if (ain->peer != peer)
+ continue;
+
+ ret = bgp_soft_reconfig_table_update(
+ peer, dest, ain, table->afi,
+ table->safi, prd);
+ iter++;
+
+ if (ret < 0) {
+ bgp_dest_unlock_node(dest);
+ listnode_delete(
+ table->soft_reconfig_peers,
+ peer);
+ bgp_announce_route(peer, table->afi,
+ table->safi);
+ if (list_isempty(
+ table->soft_reconfig_peers)) {
+ list_delete(
+ &table->soft_reconfig_peers);
+ bgp_soft_reconfig_table_flag(
+ table, false);
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ /* we're either starting the initial iteration,
+ * or we're going to continue an ongoing iteration
+ */
+ if (dest || table->soft_reconfig_init) {
+ table->soft_reconfig_init = false;
+ thread_add_event(bm->master, bgp_soft_reconfig_table_task,
+ table, 0, &table->soft_reconfig_thread);
+ return 0;
+ }
+ /* we're done, clean up the background iteration context info and
+ schedule route annoucement
+ */
+ for (ALL_LIST_ELEMENTS(table->soft_reconfig_peers, node, nnode, peer)) {
+ listnode_delete(table->soft_reconfig_peers, peer);
+ bgp_announce_route(peer, table->afi, table->safi);
+ }
+
+ list_delete(&table->soft_reconfig_peers);
+
+ return 0;
+}
+
+
+/* Cancel soft_reconfig_table task matching bgp instance, bgp_table
+ * and peer.
+ * - bgp cannot be NULL
+ * - if table and peer are NULL, cancel all threads within the bgp instance
+ * - if table is NULL and peer is not,
+ * remove peer in all threads within the bgp instance
+ * - if peer is NULL, cancel all threads matching table within the bgp instance
+ */
+void bgp_soft_reconfig_table_task_cancel(const struct bgp *bgp,
+ const struct bgp_table *table,
+ const struct peer *peer)
+{
+ struct peer *npeer;
+ struct listnode *node, *nnode;
+ int afi, safi;
+ struct bgp_table *ntable;
+
+ if (!bgp)
+ return;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ ntable = bgp->rib[afi][safi];
+ if (!ntable)
+ continue;
+ if (table && table != ntable)
+ continue;
+
+ for (ALL_LIST_ELEMENTS(ntable->soft_reconfig_peers, node, nnode,
+ npeer)) {
+ if (peer && peer != npeer)
+ continue;
+ listnode_delete(ntable->soft_reconfig_peers, npeer);
+ }
+
+ if (!ntable->soft_reconfig_peers
+ || !list_isempty(ntable->soft_reconfig_peers))
+ continue;
+
+ list_delete(&ntable->soft_reconfig_peers);
+ bgp_soft_reconfig_table_flag(ntable, false);
+ BGP_TIMER_OFF(ntable->soft_reconfig_thread);
+ }
+}
+
void bgp_soft_reconfig_in(struct peer *peer, afi_t afi, safi_t safi)
{
struct bgp_dest *dest;
struct bgp_table *table;
+ struct listnode *node, *nnode;
+ struct peer *npeer;
+ struct peer_af *paf;
- if (peer->status != Established)
+ if (!peer_established(peer))
return;
if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)
- && (safi != SAFI_EVPN))
- bgp_soft_reconfig_table(peer, afi, safi, NULL, NULL);
- else
+ && (safi != SAFI_EVPN)) {
+ table = peer->bgp->rib[afi][safi];
+ if (!table)
+ return;
+
+ table->soft_reconfig_init = true;
+
+ if (!table->soft_reconfig_peers)
+ table->soft_reconfig_peers = list_new();
+ npeer = NULL;
+ /* add peer to the table soft_reconfig_peers if not already
+ * there
+ */
+ for (ALL_LIST_ELEMENTS(table->soft_reconfig_peers, node, nnode,
+ npeer)) {
+ if (peer == npeer)
+ break;
+ }
+ if (peer != npeer)
+ listnode_add(table->soft_reconfig_peers, peer);
+
+ /* (re)flag all bgp_dest in table. Existing soft_reconfig_in job
+ * on table would start back at the beginning.
+ */
+ bgp_soft_reconfig_table_flag(table, true);
+
+ if (!table->soft_reconfig_thread)
+ thread_add_event(bm->master,
+ bgp_soft_reconfig_table_task, table, 0,
+ &table->soft_reconfig_thread);
+ /* Cancel bgp_announce_route_timer_expired threads.
+ * bgp_announce_route_timer_expired threads have been scheduled
+ * to announce routes as soon as the soft_reconfigure process
+ * finishes.
+ * In this case, soft_reconfigure is also scheduled by using
+ * a thread but is planned after the
+ * bgp_announce_route_timer_expired threads. It means that,
+ * without cancelling the threads, the route announcement task
+ * would run before the soft reconfiguration one. That would
+ * useless and would block vtysh during several seconds. Route
+ * announcements are rescheduled as soon as the soft_reconfigure
+ * process finishes.
+ */
+ paf = peer_af_find(peer, afi, safi);
+ if (paf)
+ bgp_stop_announce_route_timer(paf);
+ } else
for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
dest = bgp_route_next(dest)) {
table = bgp_dest_get_bgp_table_info(dest);
while (ain) {
ain_next = ain->next;
- if (ain->peer == peer) {
+ if (ain->peer == peer)
bgp_adj_in_remove(dest, ain);
- bgp_dest_unlock_node(dest);
- }
ain = ain_next;
}
while (ain) {
ain_next = ain->next;
- if (ain->peer == peer) {
+ if (ain->peer == peer)
bgp_adj_in_remove(dest, ain);
- bgp_dest_unlock_node(dest);
- }
ain = ain_next;
}
struct bgp_table *table;
struct bgp_static *bgp_static;
+ SET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS);
FOREACH_AFI_SAFI (afi, safi)
for (dest = bgp_table_top(bgp->route[afi][safi]); dest;
dest = bgp_route_next(dest)) {
safi);
}
}
+ UNSET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS);
}
/* Called from bgp_delete(). Delete all static routes from the BGP
json_nexthop_global = json_object_new_object();
}
+ if (safi == SAFI_EVPN) {
+ if (!json_paths)
+ vty_out(vty, " Route %pRN", bn);
+ }
+
if (path->extra) {
char tag_buf[30];
}
if (safi == SAFI_EVPN) {
if (!json_paths) {
- vty_out(vty, " Route %pFX",
- (struct prefix_evpn *)
- bgp_dest_get_prefix(bn));
if (tag_buf[0] != '\0')
vty_out(vty, " VNI %s", tag_buf);
- vty_out(vty, "\n");
} else {
if (tag_buf[0])
json_object_string_add(json_path, "VNI",
}
}
+ if (safi == SAFI_EVPN
+ && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) {
+ char gwip_buf[INET6_ADDRSTRLEN];
+
+ if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)&bn->p))
+ inet_ntop(AF_INET, &attr->evpn_overlay.gw_ip.ipv4,
+ gwip_buf, sizeof(gwip_buf));
+ else
+ inet_ntop(AF_INET6, &attr->evpn_overlay.gw_ip.ipv6,
+ gwip_buf, sizeof(gwip_buf));
+
+ if (json_paths)
+ json_object_string_add(json_path, "gatewayIP",
+ gwip_buf);
+ else
+ vty_out(vty, " Gateway IP %s", gwip_buf);
+ }
+
+ if (safi == SAFI_EVPN)
+ vty_out(vty, "\n");
+
/* Line1 display AS-path, Aggregator */
if (attr->aspath) {
if (json_paths) {
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, uint8_t show_flags);
+ safi_t safi, uint16_t show_flags);
static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
struct bgp_table *table, enum bgp_show_type type,
void *output_arg, char *rd, int is_last,
unsigned long *output_cum, unsigned long *total_cum,
- unsigned long *json_header_depth, uint8_t show_flags,
+ unsigned long *json_header_depth, uint16_t show_flags,
enum rpki_states rpki_target_state)
{
struct bgp_path_info *pi;
unsigned long json_header_depth = 0;
struct bgp_table *itable;
bool show_msg;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
show_msg = (!use_json && type == bgp_show_type_normal);
}
static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
enum bgp_show_type type, void *output_arg,
- uint8_t show_flags, enum rpki_states rpki_target_state)
+ uint16_t show_flags, enum rpki_states rpki_target_state)
{
struct bgp_table *table;
unsigned long json_header_depth = 0;
}
static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi,
- safi_t safi, uint8_t show_flags)
+ safi_t safi, uint16_t show_flags)
{
struct listnode *node, *nnode;
struct bgp *bgp;
int i;
char *str;
int first = 0;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
int ret;
if (uj)
safi_t safi, bool uj)
{
struct community_list *list;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
if (uj)
SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
bool exact_match = 0;
struct bgp *bgp = NULL;
bool uj = use_json(argc, argv);
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
if (uj) {
argc--;
int exact_match = 0;
struct bgp *bgp = NULL;
int idx = 0;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
/* [<ipv4|ipv6> [all]] */
if (all) {
char *prefix_version = NULL;
char *bgp_community_alias = NULL;
bool first = true;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
enum rpki_states rpki_target_state = RPKI_NOT_BEING_USED;
if (uj) {
safi_t safi = SAFI_UNICAST;
struct bgp *bgp = NULL;
int idx = 0;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
if (uj) {
argc--;
{
regex_t *regex;
int rc;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
if (use_json)
SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
safi_t safi, enum bgp_show_type type)
{
struct prefix_list *plist;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
plist = prefix_list_lookup(afi, prefix_list_str);
if (plist == NULL) {
enum bgp_show_type type)
{
struct as_list *as_list;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
as_list = as_list_lookup(filter);
if (as_list == NULL) {
enum bgp_show_type type)
{
struct route_map *rmap;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
rmap = route_map_lookup_by_name(rmap_str);
if (!rmap) {
static int bgp_show_community(struct vty *vty, struct bgp *bgp,
const char *comstr, int exact, afi_t afi,
- safi_t safi, uint8_t show_flags)
+ safi_t safi, uint16_t show_flags)
{
struct community *com;
int ret = 0;
safi_t safi)
{
struct community_list *list;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
list = community_list_lookup(bgp_clist, com, 0, COMMUNITY_LIST_MASTER);
if (list == NULL) {
{
int ret;
struct prefix *p;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
p = prefix_new();
afi_t afi, safi_t safi, enum bgp_show_adj_route_type type,
const char *rmap_name, json_object *json, json_object *json_ar,
json_object *json_scode, json_object *json_ocode,
- uint8_t show_flags, int *header1, int *header2, char *rd_str,
+ uint16_t show_flags, int *header1, int *header2, char *rd_str,
unsigned long *output_count, unsigned long *filtered_count)
{
struct bgp_adj_in *ain;
static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
safi_t safi, enum bgp_show_adj_route_type type,
- const char *rmap_name, uint8_t show_flags)
+ const char *rmap_name, uint16_t show_flags)
{
struct bgp *bgp;
struct bgp_table *table;
struct peer *peer;
enum bgp_show_adj_route_type type = bgp_show_adj_route_bestpath;
int idx = 0;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
if (uj)
SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
enum bgp_show_adj_route_type type = bgp_show_adj_route_advertised;
int idx = 0;
bool first = true;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
if (uj) {
argc--;
afi_t afi, safi_t safi,
enum bgp_show_type type, bool use_json)
{
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
if (use_json)
SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
struct bgp *bgp = NULL;
int idx = 0;
bool uj = use_json(argc, argv);
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
if (uj) {
argc--;
#define BGP_SHOW_OPT_ESTABLISHED (1 << 5)
#define BGP_SHOW_OPT_FAILED (1 << 6)
#define BGP_SHOW_OPT_DETAIL (1 << 7)
+#define BGP_SHOW_OPT_TERSE (1 << 8)
/* Prototypes. */
extern void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi,
extern void bgp_stop_announce_route_timer(struct peer_af *paf);
extern void bgp_announce_route_all(struct peer *);
extern void bgp_default_originate(struct peer *, afi_t, safi_t, int);
+extern void bgp_soft_reconfig_table_task_cancel(const struct bgp *bgp,
+ const struct bgp_table *table,
+ const struct peer *peer);
extern void bgp_soft_reconfig_in(struct peer *, afi_t, safi_t);
extern void bgp_clear_route(struct peer *, afi_t, safi_t);
extern void bgp_clear_route_all(struct peer *);
route_match_rd_free
};
+static enum route_map_cmd_result_t
+route_set_evpn_gateway_ip(void *rule, const struct prefix *prefix, void *object)
+{
+ struct ipaddr *gw_ip = rule;
+ struct bgp_path_info *path;
+ struct prefix_evpn *evp;
+
+ if (prefix->family != AF_EVPN)
+ return RMAP_OKAY;
+
+ evp = (struct prefix_evpn *)prefix;
+ if (evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE)
+ return RMAP_OKAY;
+
+ if ((is_evpn_prefix_ipaddr_v4(evp) && IPADDRSZ(gw_ip) != 4)
+ || (is_evpn_prefix_ipaddr_v6(evp) && IPADDRSZ(gw_ip) != 16))
+ return RMAP_OKAY;
+
+ path = object;
+
+ /* Set gateway-ip value. */
+ path->attr->evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP;
+ memcpy(&path->attr->evpn_overlay.gw_ip, &gw_ip->ip.addr,
+ IPADDRSZ(gw_ip));
+
+ return RMAP_OKAY;
+}
+
+/*
+ * Route map `evpn gateway-ip' compile function.
+ * Given string is converted to struct ipaddr structure
+ */
+static void *route_set_evpn_gateway_ip_compile(const char *arg)
+{
+ struct ipaddr *gw_ip = NULL;
+ int ret;
+
+ gw_ip = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct ipaddr));
+
+ ret = str2ipaddr(arg, gw_ip);
+ if (ret < 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip);
+ return NULL;
+ }
+ return gw_ip;
+}
+
+/* Free route map's compiled `evpn gateway_ip' value. */
+static void route_set_evpn_gateway_ip_free(void *rule)
+{
+ struct ipaddr *gw_ip = rule;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip);
+}
+
+/* Route map commands for set evpn gateway-ip ipv4. */
+struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv4_cmd = {
+ "evpn gateway-ip ipv4", route_set_evpn_gateway_ip,
+ route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free};
+
+/* Route map commands for set evpn gateway-ip ipv6. */
+struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv6_cmd = {
+ "evpn gateway-ip ipv6", route_set_evpn_gateway_ip,
+ route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free};
+
/* Route map commands for VRF route leak with source vrf matching */
static enum route_map_cmd_result_t
route_match_vrl_source_vrf(void *rule, const struct prefix *prefix,
&& (strcmp(rmap_name, filter->map[RMAP_IN].name) == 0)) {
filter->map[RMAP_IN].map = map;
- if (route_update && peer->status == Established) {
+ if (route_update && peer_established(peer)) {
if (CHECK_FLAG(peer->af_flags[afi][safi],
PEER_FLAG_SOFT_RECONFIG)) {
if (bgp_debug_update(peer, NULL, NULL, 1))
bgp_route_map_process_update(bgp, rmap_name, 1);
#ifdef ENABLE_BGP_VNC
- /* zlog_debug("%s: calling vnc_routemap_update", __func__); */
vnc_routemap_update(bgp, __func__);
#endif
}
BGP_POLICY_ROUTE_MAP,
rmap_name, 1, 1);
} else {
- for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
bgp_route_map_process_update(bgp, rmap_name, 0);
#ifdef ENABLE_BGP_VNC
- zlog_debug("%s: calling vnc_routemap_update", __func__);
- vnc_routemap_update(bgp, __func__);
+ vnc_routemap_update(bgp, __func__);
#endif
+ }
+
+ vpn_policy_routemap_event(rmap_name);
}
}
return nb_cli_apply_changes(vty, NULL);
}
+DEFUN_YANG (set_evpn_gw_ip_ipv4,
+ set_evpn_gw_ip_ipv4_cmd,
+ "set evpn gateway-ip ipv4 A.B.C.D",
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv4 address\n"
+ "Gateway IP address in IPv4 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']";
+ char xpath_value[XPATH_MAXLEN];
+
+ ret = str2sockunion(argv[4]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (su.sin.sin_addr.s_addr == 0
+ || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) {
+ vty_out(vty,
+ "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4",
+ xpath);
+
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_evpn_gw_ip_ipv4,
+ no_set_evpn_gw_ip_ipv4_cmd,
+ "no set evpn gateway-ip ipv4 A.B.C.D",
+ NO_STR
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv4 address\n"
+ "Gateway IP address in IPv4 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']";
+
+ ret = str2sockunion(argv[5]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (su.sin.sin_addr.s_addr == 0
+ || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) {
+ vty_out(vty,
+ "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_evpn_gw_ip_ipv6,
+ set_evpn_gw_ip_ipv6_cmd,
+ "set evpn gateway-ip ipv6 X:X::X:X",
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv6 address\n"
+ "Gateway IP address in IPv6 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']";
+ char xpath_value[XPATH_MAXLEN];
+
+ ret = str2sockunion(argv[4]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr)
+ || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) {
+ vty_out(vty,
+ "%% Gateway IP cannot be a linklocal or multicast address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6",
+ xpath);
+
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_evpn_gw_ip_ipv6,
+ no_set_evpn_gw_ip_ipv6_cmd,
+ "no set evpn gateway-ip ipv6 X:X::X:X",
+ NO_STR
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv4 address\n"
+ "Gateway IP address in IPv4 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']";
+
+ ret = str2sockunion(argv[5]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr)
+ || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) {
+ vty_out(vty,
+ "%% Gateway IP cannot be a linklocal or multicast address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
DEFPY_YANG(match_vrl_source_vrf,
match_vrl_source_vrf_cmd,
"match source-vrf NAME$vrf_name",
route_map_install_match(&route_match_evpn_default_route_cmd);
route_map_install_match(&route_match_vrl_source_vrf_cmd);
+ route_map_install_set(&route_set_evpn_gateway_ip_ipv4_cmd);
+ route_map_install_set(&route_set_evpn_gateway_ip_ipv6_cmd);
route_map_install_set(&route_set_table_id_cmd);
route_map_install_set(&route_set_srte_color_cmd);
route_map_install_set(&route_set_ip_nexthop_cmd);
install_element(RMAP_NODE, &no_match_evpn_rd_cmd);
install_element(RMAP_NODE, &match_evpn_default_route_cmd);
install_element(RMAP_NODE, &no_match_evpn_default_route_cmd);
+ install_element(RMAP_NODE, &set_evpn_gw_ip_ipv4_cmd);
+ install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv4_cmd);
+ install_element(RMAP_NODE, &set_evpn_gw_ip_ipv6_cmd);
+ install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv6_cmd);
install_element(RMAP_NODE, &match_vrl_source_vrf_cmd);
install_element(RMAP_NODE, &no_match_vrl_source_vrf_cmd);
.destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy,
}
},
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy,
+ }
+ },
{
.xpath = NULL,
},
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy(
+ struct nb_cb_destroy_args *args);
#ifdef __cplusplus
}
{
return lib_route_map_entry_set_destroy(args);
}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4
+ */
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "evpn gateway-ip ipv4";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv4", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6
+ */
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "evpn gateway-ip ipv6";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv6", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
dest = bgp_node_lookup(bgp->rib[AFI_IP][SAFI_UNICAST],
(struct prefix *)addr);
if (dest) {
- bgp_dest_unlock_node(dest);
-
for (path = bgp_dest_get_bgp_path_info(dest); path;
path = path->next)
if (sockunion_same(&path->peer->su, &su))
return path;
+
+ bgp_dest_unlock_node(dest);
}
} else {
offset = name + v->namelen;
#include "linklist.h"
#include "bgpd.h"
#include "bgp_advertise.h"
+#include "bgpd/bgp_trace.h"
struct bgp_table {
/* table belongs to this instance */
int lock;
+ /* soft_reconfig_table in progress */
+ bool soft_reconfig_init;
+ struct thread *soft_reconfig_thread;
+
+ /* list of peers on which soft_reconfig_table has to run */
+ struct list *soft_reconfig_peers;
+
struct route_table *route_table;
uint64_t version;
};
mpls_label_t local_label;
- uint8_t flags;
+ uint16_t flags;
#define BGP_NODE_PROCESS_SCHEDULED (1 << 0)
#define BGP_NODE_USER_CLEAR (1 << 1)
#define BGP_NODE_LABEL_CHANGED (1 << 2)
#define BGP_NODE_FIB_INSTALL_PENDING (1 << 5)
#define BGP_NODE_FIB_INSTALLED (1 << 6)
#define BGP_NODE_LABEL_REQUESTED (1 << 7)
+#define BGP_NODE_SOFT_RECONFIG (1 << 8)
struct bgp_addpath_node_data tx_addpath;
*/
static inline void bgp_dest_unlock_node(struct bgp_dest *dest)
{
+ frrtrace(1, frr_bgp, bgp_dest_unlock, dest);
bgp_delete_listnode(dest);
route_unlock_node(bgp_dest_to_rnode(dest));
}
*/
static inline struct bgp_dest *bgp_dest_lock_node(struct bgp_dest *dest)
{
+ frrtrace(1, frr_bgp, bgp_dest_lock, dest);
struct route_node *rn = route_lock_node(bgp_dest_to_rnode(dest));
return bgp_dest_from_rnode(rn);
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
peer_lonesoul_or_not(peer, set);
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_announce_route_all(peer);
} else {
group = peer->group;
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
peer_lonesoul_or_not(peer, set);
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_announce_route_all(peer);
}
}
* will trigger a write job on the I/O thread.
*/
SUBGRP_FOREACH_PEER (subgrp, paf)
- if (paf->peer->status == Established)
+ if (peer_established(paf->peer))
thread_add_timer_msec(
bm->master, bgp_generate_updgrp_packets,
paf->peer, 0,
struct bgp_path_info_extra tmp_pie;
tmp_attr = *pi->attr;
+ tmp_attr.aspath = attr.aspath;
prep_for_rmap_apply(&tmp_pi, &tmp_pie, dest, pi,
pi->peer, &tmp_attr);
continue;
} else {
new_attr = bgp_attr_intern(&tmp_attr);
- new_attr->aspath = attr.aspath;
subgroup_announce_reset_nhop(
(peer_cap_enhe(peer, afi, safi)
/* If default route is present in the local RIB, advertise the
* route
*/
- if (dest != NULL) {
+ if (dest) {
for (pi = bgp_dest_get_bgp_path_info(dest); pi;
pi = pi->next) {
if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
dest, subgrp, &attr,
pi);
}
+ bgp_dest_unlock_node(dest);
}
} else {
if (!CHECK_FLAG(subgrp->sflags,
* clear adj_out for the 0.0.0.0/0 prefix in the BGP
* table.
*/
- if (dest != NULL) {
+ if (dest) {
/* Remove the adjacency for the previously
* advertised default route
*/
/* Free allocated information. */
adj_free(adj);
}
+ bgp_dest_unlock_node(dest);
}
/* Advertise the default route */
bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p,
label_pnt, num_labels,
addpath_encode, addpath_tx_id,
+ &adv->baa->attr->evpn_overlay,
pfx_buf, sizeof(pfx_buf));
zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s",
subgrp->update_group->id, subgrp->id,
bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, NULL, 0,
addpath_encode, addpath_tx_id,
- pfx_buf, sizeof(pfx_buf));
+ NULL, pfx_buf, sizeof(pfx_buf));
zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s -- unreachable",
subgrp->update_group->id, subgrp->id,
pfx_buf);
if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
continue;
peers_cfg++;
- if (peer->status == Established)
+ if (peer_established(peer))
peers_estb++;
}
static inline bool bgp_has_peer_failed(struct peer *peer, afi_t afi,
safi_t safi)
{
- return ((peer->status != Established) ||
- !peer->afc_recv[afi][safi]);
+ return ((!peer_established(peer)) || !peer->afc_recv[afi][safi]);
}
static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp,
peer->dropped);
peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN,
use_json, json_peer);
- if (peer->status == Established)
+ if (peer_established(peer))
json_object_string_add(json_peer, "lastResetDueTo",
"AFI/SAFI Not Negotiated");
else
peer->dropped,
peer_uptime(peer->uptime, timebuf,
BGP_UPTIME_LEN, 0, NULL));
- if (peer->status == Established)
+ if (peer_established(peer))
vty_out(vty, " AFI/SAFI Not Negotiated\n");
else
bgp_show_peer_reset(vty, peer, NULL,
/* Show BGP peer's summary information. */
static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
struct peer *fpeer, int as_type, as_t as,
- uint8_t show_flags)
+ uint16_t show_flags)
{
struct peer *peer;
struct listnode *node, *nnode;
char neighbor_buf[VTY_BUFSIZ];
int neighbor_col_default_width = 16;
int len, failed_count = 0;
+ unsigned int filtered_count = 0;
int max_neighbor_width = 0;
int pfx_rcd_safi;
json_object *json = NULL;
bool show_established =
CHECK_FLAG(show_flags, BGP_SHOW_OPT_ESTABLISHED);
bool show_wide = CHECK_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+ bool show_terse = CHECK_FLAG(show_flags, BGP_SHOW_OPT_TERSE);
/* labeled-unicast routes are installed in the unicast table so in order
* to
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
if (bgp_show_summary_is_peer_filtered(peer, fpeer,
as_type, as)) {
+ filtered_count++;
count++;
continue;
}
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
if (bgp_show_summary_is_peer_filtered(peer, fpeer,
as_type, as)) {
+ filtered_count++;
count++;
continue;
}
json_object_free(json);
} else {
vty_out(vty, "%% No failed BGP neighbors found\n");
- vty_out(vty, "\nTotal number of neighbors %d\n", count);
}
return CMD_SUCCESS;
}
count = 0; /* Reset the value as its used again */
+ filtered_count = 0;
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
continue;
json_object_boolean_true_add(
json, "dampeningEnabled");
} else {
- if (bgp_maxmed_onstartup_configured(bgp)
- && bgp->maxmed_active)
- vty_out(vty,
- "Max-med on-startup active\n");
- if (bgp->v_maxmed_admin)
- vty_out(vty,
- "Max-med administrative active\n");
+ if (!show_terse) {
+ if (bgp_maxmed_onstartup_configured(bgp)
+ && bgp->maxmed_active)
+ vty_out(vty,
+ "Max-med on-startup active\n");
+ if (bgp->v_maxmed_admin)
+ vty_out(vty,
+ "Max-med administrative active\n");
- vty_out(vty, "BGP table version %" PRIu64 "\n",
- bgp_table_version(bgp->rib[afi][safi]));
+ vty_out(vty,
+ "BGP table version %" PRIu64
+ "\n",
+ bgp_table_version(
+ bgp->rib[afi][safi]));
- ents = bgp_table_count(bgp->rib[afi][safi]);
- vty_out(vty,
- "RIB entries %ld, using %s of memory\n",
- ents,
- mtype_memstr(
- memstrbuf, sizeof(memstrbuf),
- ents
- * sizeof(struct
- bgp_dest)));
-
- /* Peer related usage */
- ents = bgp->af_peer_count[afi][safi];
- vty_out(vty, "Peers %ld, using %s of memory\n",
- ents,
- mtype_memstr(
- memstrbuf, sizeof(memstrbuf),
- ents * sizeof(struct peer)));
+ ents = bgp_table_count(
+ bgp->rib[afi][safi]);
+ vty_out(vty,
+ "RIB entries %ld, using %s of memory\n",
+ ents,
+ mtype_memstr(
+ memstrbuf,
+ sizeof(memstrbuf),
+ ents
+ * sizeof(
+ struct
+ bgp_dest)));
- if ((ents = listcount(bgp->group)))
+ /* Peer related usage */
+ ents = bgp->af_peer_count[afi][safi];
vty_out(vty,
- "Peer groups %ld, using %s of memory\n",
+ "Peers %ld, using %s of memory\n",
ents,
mtype_memstr(
memstrbuf,
sizeof(memstrbuf),
- ents * sizeof(struct
- peer_group)));
+ ents
+ * sizeof(
+ struct
+ peer)));
- if (CHECK_FLAG(bgp->af_flags[afi][safi],
- BGP_CONFIG_DAMPENING))
- vty_out(vty, "Dampening enabled.\n");
- vty_out(vty, "\n");
+ if ((ents = listcount(bgp->group)))
+ vty_out(vty,
+ "Peer groups %ld, using %s of memory\n",
+ ents,
+ mtype_memstr(
+ memstrbuf,
+ sizeof(memstrbuf),
+ ents
+ * sizeof(
+ struct
+ peer_group)));
+
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_DAMPENING))
+ vty_out(vty,
+ "Dampening enabled.\n");
+ }
+ if (show_failed) {
+ vty_out(vty, "\n");
- /* Subtract 8 here because 'Neighbor' is
- * 8 characters */
- vty_out(vty, "Neighbor");
- vty_out(vty, "%*s", max_neighbor_width - 8,
- " ");
- if (show_failed)
+ /* Subtract 8 here because 'Neighbor' is
+ * 8 characters */
+ vty_out(vty, "Neighbor");
+ vty_out(vty, "%*s",
+ max_neighbor_width - 8, " ");
vty_out(vty,
BGP_SHOW_SUMMARY_HEADER_FAILED);
- else
- vty_out(vty,
- show_wide
- ? BGP_SHOW_SUMMARY_HEADER_ALL_WIDE
- : BGP_SHOW_SUMMARY_HEADER_ALL);
+ }
}
}
if (use_json) {
json_peer = NULL;
-
if (bgp_show_summary_is_peer_filtered(peer, fpeer,
- as_type, as))
+ as_type, as)) {
+ filtered_count++;
continue;
-
+ }
if (show_failed &&
bgp_has_peer_failed(peer, afi, safi)) {
json_peer = json_object_new_object();
json_peer, 0, use_json);
} else if (!show_failed) {
if (show_established
- && bgp_has_peer_failed(peer, afi, safi))
+ && bgp_has_peer_failed(peer, afi, safi)) {
+ filtered_count++;
continue;
+ }
json_peer = json_object_new_object();
if (peer_dynamic_neighbor(peer)) {
json_peer);
} else {
if (bgp_show_summary_is_peer_filtered(peer, fpeer,
- as_type, as))
+ as_type, as)) {
+ filtered_count++;
continue;
+ }
if (show_failed &&
bgp_has_peer_failed(peer, afi, safi)) {
bgp_show_failed_summary(vty, bgp, peer, NULL,
use_json);
} else if (!show_failed) {
if (show_established
- && bgp_has_peer_failed(peer, afi, safi))
+ && bgp_has_peer_failed(peer, afi, safi)) {
+ filtered_count++;
continue;
+ }
+
+ if ((count - filtered_count) == 1) {
+ /* display headline before the first
+ * neighbor line */
+ vty_out(vty, "\n");
+
+ /* Subtract 8 here because 'Neighbor' is
+ * 8 characters */
+ vty_out(vty, "Neighbor");
+ vty_out(vty, "%*s",
+ max_neighbor_width - 8, " ");
+ vty_out(vty,
+ show_wide
+ ? BGP_SHOW_SUMMARY_HEADER_ALL_WIDE
+ : BGP_SHOW_SUMMARY_HEADER_ALL);
+ }
+
memset(dn_flag, '\0', sizeof(dn_flag));
if (peer_dynamic_neighbor(peer)) {
dn_flag[0] = '*';
BGP_UPTIME_LEN, 0,
NULL));
- if (peer->status == Established) {
+ if (peer_established(peer)) {
if (peer->afc_recv[afi][safi]) {
if (CHECK_FLAG(
bgp->flags,
if (use_json) {
json_object_object_add(json, "peers", json_peers);
json_object_int_add(json, "failedPeers", failed_count);
+ json_object_int_add(json, "displayedPeers",
+ count - filtered_count);
json_object_int_add(json, "totalPeers", count);
json_object_int_add(json, "dynamicPeers", dn_count);
json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
} else {
- if (count)
- vty_out(vty, "\nTotal number of neighbors %d\n", count);
- else {
+ if (count) {
+ if (filtered_count == count)
+ vty_out(vty, "\n%% No matching neighbor\n");
+ else {
+ if (show_failed)
+ vty_out(vty, "\nDisplayed neighbors %d",
+ failed_count);
+ else if (as_type != AS_UNSPECIFIED || as
+ || fpeer || show_established)
+ vty_out(vty, "\nDisplayed neighbors %d",
+ count - filtered_count);
+
+ vty_out(vty, "\nTotal number of neighbors %d\n",
+ count);
+ }
+ } else {
vty_out(vty, "No %s neighbor is configured\n",
get_afi_safi_str(afi, safi, false));
}
static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi,
int safi, struct peer *fpeer, int as_type,
- as_t as, uint8_t show_flags)
+ as_t as, uint16_t show_flags)
{
int is_first = 1;
int afi_wildcard = (afi == AFI_MAX);
safi,
true));
} else {
- vty_out(vty, "\n%s Summary:\n",
+ vty_out(vty,
+ "\n%s Summary (%s):\n",
get_afi_safi_str(afi,
safi,
- false));
+ false),
+ bgp->name_pretty);
}
}
bgp_show_summary(vty, bgp, afi, safi, fpeer,
if (use_json)
vty_out(vty, "{}\n");
else
- vty_out(vty, "%% No BGP neighbors found\n");
+ vty_out(vty, "%% No BGP neighbors found in %s\n",
+ bgp->name_pretty);
}
}
safi_t safi,
const char *neighbor,
int as_type, as_t as,
- uint8_t show_flags)
+ uint16_t show_flags)
{
struct listnode *node, *nnode;
struct bgp *bgp;
(bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
? VRF_DEFAULT_NAME
: bgp->name);
- } else {
- vty_out(vty, "\nInstance %s:\n",
- (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
- ? VRF_DEFAULT_NAME
- : bgp->name);
}
if (neighbor) {
fpeer = peer_lookup_in_view(vty, bgp, neighbor,
int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
safi_t safi, const char *neighbor, int as_type,
- as_t as, uint8_t show_flags)
+ as_t as, uint16_t show_flags)
{
struct bgp *bgp;
bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd,
"show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR
" [" BGP_SAFI_WITH_LABEL_CMD_STR
- "]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [wide] [json$uj]",
+ "]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [terse] [wide] [json$uj]",
SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
BGP_SAFI_WITH_LABEL_HELP_STR
"Display the entries for all address families\n"
"AS number\n"
"Internal (iBGP) AS sessions\n"
"External (eBGP) AS sessions\n"
+ "Shorten the information on BGP instances\n"
"Increase table width for longer output\n" JSON_STR)
{
char *vrf = NULL;
safi_t safi = SAFI_MAX;
as_t as = 0; /* 0 means AS filter not set */
int as_type = AS_UNSPECIFIED;
- uint8_t show_flags = 0;
+ uint16_t show_flags = 0;
int idx = 0;
as = (as_t)atoi(argv[idx + 1]->arg);
}
+ if (argv_find(argv, argc, "terse", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_TERSE);
+
if (argv_find(argv, argc, "wide", &idx))
SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)
&& (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV))
- && (p->status == Established)) {
+ && (peer_established(p))) {
if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_BIT_RCV))
rbit_status = true;
vty_out(vty, "\n Remote GR Mode: ");
if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV)
- && (peer->status == Established)) {
+ && (peer_established(peer))) {
if ((peer->nsf_af_count == 0)
&& !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) {
json_neigh, "bgpState",
lookup_msg(bgp_status_msg, p->status, NULL));
- if (p->status == Established) {
+ if (peer_established(p)) {
time_t uptime;
uptime = bgp_clock();
vty_out(vty, " BGP state = %s",
lookup_msg(bgp_status_msg, p->status, NULL));
- if (p->status == Established)
+ if (peer_established(p))
vty_out(vty, ", up for %8s",
peer_uptime(p->uptime, timebuf, BGP_UPTIME_LEN,
0, NULL));
}
}
/* Capability. */
- if (p->status == Established) {
+ if (peer_established(p)) {
if (p->cap || p->afc_adv[AFI_IP][SAFI_UNICAST]
|| p->afc_recv[AFI_IP][SAFI_UNICAST]
|| p->afc_adv[AFI_IP][SAFI_MULTICAST]
json_grace_send = json_object_new_object();
json_grace_recv = json_object_new_object();
- if ((p->status == Established)
+ if ((peer_established(p))
&& CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) {
FOREACH_AFI_SAFI (afi, safi) {
if (CHECK_FLAG(p->af_sflags[afi][safi],
json_neigh, "gracefulRestartInfo", json_grace);
} else {
vty_out(vty, " Graceful restart information:\n");
- if ((p->status == Established)
+ if ((peer_established(p))
&& CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) {
vty_out(vty, " End-of-RIB send: ");
if (use_json) {
json_object_int_add(json_neigh, "connectRetryTimer",
p->v_connect);
- if (p->status == Established && p->rtt)
+ if (peer_established(p) && p->rtt)
json_object_int_add(json_neigh, "estimatedRttInMsecs",
p->rtt);
if (p->t_start)
} else {
vty_out(vty, "BGP Connect Retry Timer in Seconds: %d\n",
p->v_connect);
- if (p->status == Established && p->rtt)
+ if (peer_established(p) && p->rtt)
vty_out(vty, "Estimated round trip time: %d ms\n",
p->rtt);
if (p->t_start)
int argc, struct bgp **bgp, bool use_json);
extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
safi_t safi, const char *neighbor, int as_type,
- as_t as, uint8_t show_flags);
+ as_t as, uint16_t show_flags);
extern int bgp_clear_star_soft_in(const char *name, char *errmsg,
size_t errmsg_len);
extern int bgp_clear_star_soft_out(const char *name, char *errmsg,
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
if (peer->conf_if && (strcmp(peer->conf_if, ifp->name) == 0)
- && peer->status != Established) {
+ && !peer_established(peer)) {
if (peer_active(peer))
BGP_EVENT_ADD(peer, BGP_Stop);
BGP_EVENT_ADD(peer, BGP_Start);
* connected routes leaked into a VRF.
*/
if (is_evpn) {
- api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
- api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
+
+ /*
+ * If the nexthop is EVPN overlay index gateway IP,
+ * treat the nexthop as NEXTHOP_TYPE_IPV4
+ * Else, mark the nexthop as onlink.
+ */
+ if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP)
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+ else {
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ 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) {
api_nh->type = NEXTHOP_TYPE_IFINDEX;
api_nh->vrf_id = nh_bgp->vrf_id;
if (is_evpn) {
- api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
- SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
- api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
+
+ /*
+ * If the nexthop is EVPN overlay index gateway IP,
+ * treat the nexthop as NEXTHOP_TYPE_IPV4
+ * Else, mark the nexthop as onlink.
+ */
+ if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP)
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+ else {
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ 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)) {
api_nh->type = NEXTHOP_TYPE_IFINDEX;
api_nh->label_num = 1;
api_nh->labels[0] = label;
}
- memcpy(&api_nh->rmac, &(mpinfo->attr->rmac),
- sizeof(struct ethaddr));
+
+ if (is_evpn
+ && mpinfo->attr->evpn_overlay.type
+ != OVERLAY_INDEX_GATEWAY_IP)
+ memcpy(&api_nh->rmac, &(mpinfo->attr->rmac),
+ sizeof(struct ethaddr));
+
api_nh->weight = nh_weight;
if (mpinfo->extra
if (!dest)
return -1;
- bgp_dest_unlock_node(dest);
-
switch (note) {
case ZAPI_ROUTE_INSTALLED:
new_select = NULL;
flog_err(EC_BGP_INVALID_ROUTE,
"selected route %pRN not found",
dest);
+
+ bgp_dest_unlock_node(dest);
return -1;
}
}
__func__, dest);
break;
}
+
+ bgp_dest_unlock_node(dest);
return 0;
}
struct in_addr vtep_ip = {INADDR_ANY};
vrf_id_t tenant_vrf_id = VRF_DEFAULT;
struct in_addr mcast_grp = {INADDR_ANY};
+ ifindex_t svi_ifindex = 0;
s = zclient->ibuf;
vni = stream_getl(s);
vtep_ip.s_addr = stream_get_ipv4(s);
stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t));
mcast_grp.s_addr = stream_get_ipv4(s);
+ stream_get(&svi_ifindex, s, sizeof(ifindex_t));
}
bgp = bgp_lookup_by_vrf_id(vrf_id);
return 0;
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s",
- (cmd == ZEBRA_VNI_ADD) ? "add" : "del",
- vrf_id_to_name(vrf_id), vni,
- vrf_id_to_name(tenant_vrf_id));
+ zlog_debug(
+ "Rx VNI %s VRF %s VNI %u tenant-vrf %s SVI ifindex %u",
+ (cmd == ZEBRA_VNI_ADD) ? "add" : "del",
+ vrf_id_to_name(vrf_id), vni,
+ vrf_id_to_name(tenant_vrf_id), svi_ifindex);
if (cmd == ZEBRA_VNI_ADD)
return bgp_evpn_local_vni_add(
bgp, vni,
vtep_ip.s_addr != INADDR_ANY ? vtep_ip : bgp->router_id,
- tenant_vrf_id, mcast_grp);
+ tenant_vrf_id, mcast_grp, svi_ifindex);
else
return bgp_evpn_local_vni_del(bgp, vni);
}
return -1;
bgp = peer->bgp;
+ bgp_soft_reconfig_table_task_cancel(bgp, bgp->rib[afi][safi], peer);
+
bgp_stop_announce_route_timer(af);
if (PAF_SUBGRP(af)) {
if (!active && peer_active(peer)) {
bgp_timer_set(peer);
} else {
- if (peer->status == Established) {
+ if (peer_established(peer)) {
if (CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV)) {
peer->afc_adv[afi][safi] = 1;
bgp_capability_send(peer, afi, safi,
return true;
}
- if (peer->status == Established) {
+ if (peer_established(peer)) {
if (CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV)) {
peer->afc_adv[afi][safi] = 0;
peer->afc_nego[afi][safi] = 0;
bgp = peer->bgp;
accept_peer = CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
+ bgp_soft_reconfig_table_task_cancel(bgp, NULL, peer);
+
bgp_keepalives_off(peer);
bgp_reads_off(peer);
bgp_writes_off(peer);
assert(bgp);
+ bgp_soft_reconfig_table_task_cancel(bgp, NULL, NULL);
+
/* make sure we withdraw any exported routes */
vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_get_default(),
bgp);
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
return;
- if (peer->status != Established)
+ if (!peer_established(peer))
return;
if (type == peer_change_reset) {
/* Execute action when peer is established. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
- && peer->status == Established) {
+ && peer_established(peer)) {
if (!set && flag == PEER_FLAG_SOFT_RECONFIG)
bgp_clear_adj_in(peer, afi, safi);
else {
set != member_invert);
/* Execute flag action on peer-group member. */
- if (member->status == Established) {
+ if (peer_established(member)) {
if (!set && flag == PEER_FLAG_SOFT_RECONFIG)
bgp_clear_adj_in(member, afi, safi);
else {
/* Check if handling a regular peer. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Update peer route announcements. */
- if (peer->status == Established && peer->afc_nego[afi][safi]) {
+ if (peer_established(peer) && peer->afc_nego[afi][safi]) {
update_group_adjust_peer(peer_af_find(peer, afi, safi));
bgp_default_originate(peer, afi, safi, 0);
bgp_announce_route(peer, afi, safi);
}
/* Update peer route announcements. */
- if (member->status == Established
- && member->afc_nego[afi][safi]) {
+ if (peer_established(member) && member->afc_nego[afi][safi]) {
update_group_adjust_peer(
peer_af_find(member, afi, safi));
bgp_default_originate(member, afi, safi, 0);
/* Check if handling a regular peer. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Update peer route announcements. */
- if (peer->status == Established && peer->afc_nego[afi][safi]) {
+ if (peer_established(peer) && peer->afc_nego[afi][safi]) {
update_group_adjust_peer(peer_af_find(peer, afi, safi));
bgp_default_originate(peer, afi, safi, 1);
bgp_announce_route(peer, afi, safi);
member->default_rmap[afi][safi].map = NULL;
/* Update peer route announcements. */
- if (member->status == Established && member->afc_nego[afi][safi]) {
+ if (peer_established(member) && member->afc_nego[afi][safi]) {
update_group_adjust_peer(peer_af_find(member, afi, safi));
bgp_default_originate(member, afi, safi, 1);
bgp_announce_route(member, afi, safi);
{
if (outbound) {
update_group_adjust_peer(peer_af_find(peer, afi, safi));
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_announce_route(peer, afi, safi);
} else {
- if (peer->status != Established)
+ if (!peer_established(peer))
return;
if (CHECK_FLAG(peer->af_flags[afi][safi],
/* Skip peer-group mechanics for regular peers. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
- if (peer->status != Established) {
+ if (!peer_established(peer)) {
if (peer_active(peer))
BGP_EVENT_ADD(peer, BGP_Stop);
BGP_EVENT_ADD(peer, BGP_Start);
member->connect = connect;
member->v_connect = connect;
- if (member->status != Established) {
+ if (!peer_established(member)) {
if (peer_active(member))
BGP_EVENT_ADD(member, BGP_Stop);
BGP_EVENT_ADD(member, BGP_Start);
/* Skip peer-group mechanics for regular peers. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
- if (peer->status != Established) {
+ if (!peer_established(peer)) {
if (peer_active(peer))
BGP_EVENT_ADD(peer, BGP_Stop);
BGP_EVENT_ADD(peer, BGP_Start);
member->connect = 0;
member->v_connect = peer->bgp->default_connect_retry;
- if (member->status != Established) {
+ if (!peer_established(member)) {
if (peer_active(member))
BGP_EVENT_ADD(member, BGP_Stop);
BGP_EVENT_ADD(member, BGP_Start);
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Update peer route announcements. */
update_group_adjust_peer_afs(peer);
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_announce_route_all(peer);
/* Skip peer-group mechanics for regular peers. */
/* Update peer route announcements. */
update_group_adjust_peer_afs(member);
- if (member->status == Established)
+ if (peer_established(member))
bgp_announce_route_all(member);
}
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Update peer route announcements. */
update_group_adjust_peer_afs(peer);
- if (peer->status == Established)
+ if (peer_established(peer))
bgp_announce_route_all(peer);
/* Skip peer-group mechanics for regular peers. */
/* Update peer route announcements. */
update_group_adjust_peer_afs(member);
- if (member->status == Established)
+ if (peer_established(member))
bgp_announce_route_all(member);
}
/* Check if handling a regular peer. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
/* Re-check if peer violates maximum-prefix. */
- if ((peer->status == Established) && (peer->afc[afi][safi]))
+ if ((peer_established(peer)) && (peer->afc[afi][safi]))
bgp_maximum_prefix_overflow(peer, afi, safi, 1);
/* Skip peer-group mechanics for regular peers. */
PEER_FLAG_MAX_PREFIX_WARNING);
/* Re-check if peer violates maximum-prefix. */
- if ((member->status == Established) && (member->afc[afi][safi]))
+ if ((peer_established(member)) && (member->afc[afi][safi]))
bgp_maximum_prefix_overflow(member, afi, safi, 1);
}
{
struct peer_af *paf;
- if (peer->status != Established)
+ if (!peer_established(peer))
return 0;
if (!peer->afc[afi][safi])
for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp))
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
- if (peer->status == Established
- || peer->status == OpenSent
+ if (peer_established(peer) || peer->status == OpenSent
|| peer->status == OpenConfirm)
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_PEER_UNCONFIG);
json_no, JSON_C_TO_STRING_PRETTY));
json_object_free(json_no);
} else
- vty_out(vty, "No such neighbor in this view/vrf\n");
+ vty_out(vty, "No such neighbor in %s\n",
+ bgp->name_pretty);
return NULL;
}
uint16_t af_flags[AFI_MAX][SAFI_MAX];
#define BGP_CONFIG_DAMPENING (1 << 0)
/* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */
-#define BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST (1 << 1)
-#define BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST (1 << 2)
-#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 3)
-#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 4)
+#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST (1 << 1)
+#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP (1 << 2)
+#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST (1 << 3)
+#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP (1 << 4)
+#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 5)
+#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 6)
/* import/export between address families */
-#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 5)
-#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 6)
+#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 7)
+#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 8)
/* vrf-route leaking flags */
-#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 7)
-#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 8)
+#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 9)
+#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 10)
/* BGP per AF peer count */
uint32_t af_peer_count[AFI_MAX][SAFI_MAX];
/* EVI hash table */
struct hash *vnihash;
+ /*
+ * VNI hash table based on SVI ifindex as its key.
+ * We use SVI ifindex as key to lookup a VNI table for gateway IP
+ * overlay index recursive lookup.
+ * For this purpose, a hashtable is added which optimizes this lookup.
+ */
+ struct hash *vni_svi_hash;
+
/* EVPN enable - advertise gateway macip routes */
int advertise_gw_macip;
/* Hash table of EVPN nexthops maintained per-tenant-VRF */
struct hash *evpn_nh_table;
+ /*
+ * Flag resolve_overlay_index is used for recursive resolution
+ * procedures for EVPN type-5 route's gateway IP overlay index.
+ * When this flag is set, we build remote-ip-hash for
+ * all L2VNIs and resolve overlay index nexthops using this hash.
+ * Overlay index nexthops remain unresolved if this flag is not set.
+ */
+ bool resolve_overlay_index;
+
/* vrf flags */
uint32_t vrf_flags;
#define BGP_VRF_AUTO (1 << 0)
return ctime(&tbuf);
}
-static inline int peer_established(struct peer *peer)
+static inline bool peer_established(struct peer *peer)
{
- if (peer->status == Established)
- return 1;
- return 0;
+ return peer->status == Established;
}
static inline int peer_dynamic_neighbor(struct peer *peer)
vnc_zlog_debug_verbose("%s administrative close rfd=%p",
__func__, rfd);
- if (h && h->rfp_methods.close_cb) {
+ if (h->rfp_methods.close_cb) {
vnc_zlog_debug_verbose(
"%s calling close callback rfd=%p", __func__,
rfd);
* instrumentation to debug segfault of 091127
*/
vnc_zlog_debug_verbose("%s: vpn_bpi=%p", __func__, vpn_bpi);
- if (vpn_bpi) {
- vnc_zlog_debug_verbose("%s: vpn_bpi->extra=%p",
- __func__, vpn_bpi->extra);
- }
+ vnc_zlog_debug_verbose("%s: vpn_bpi->extra=%p", __func__,
+ vpn_bpi->extra);
vpn_bpi->extra->vnc.import.un_family = AF_INET;
vpn_bpi->extra->vnc.import.un.addr4 =
# printfrr extensions
app.add_object_type("frrfmt", "frrfmt", parse_node=parse_frrfmt)
- # css overrides for HTML theme
- app.add_stylesheet("overrides.css")
+ if "add_css_file" in dir(app):
+ app.add_css_file("overrides.css")
+ else:
+ app.add_stylesheet("overrides.css")
+
# load Pygments lexer for FRR config syntax
#
# NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we
--gdb-breakpoints=nb_config_diff \
all-protocol-startup
+Detecting Memleaks with Valgrind
+""""""""""""""""""""""""""""""""
+
+Topotest can automatically launch all daemons with ``valgrind`` to check for
+memleaks. This is enabled by specifying 1 or 2 CLI arguments.
+``--valgrind-memleaks`` will enable general memleak detection, and
+``--valgrind-extra`` enables extra functionality including generating a
+suppression file. The suppression file ``tools/valgrind.supp`` is used when
+memleak detection is enabled.
+
+.. code:: shell
+
+ pytest --valgrind-memleaks all-protocol-startup
+
.. _topotests_docker:
Running Tests with Docker
--- /dev/null
+sphinx==4.0.2
.. clicmd:: hostname HOSTNAME
- Set hostname of the router.
+ Set hostname of the router. It is only for current ``vtysh``, it will not be
+ saved to any configuration file even with ``write file``.
+
+.. clicmd:: domainname DOMAINNAME
+
+ Set domainname of the router. It is only for current ``vtysh``, it will not
+ be saved to any configuration file even with ``write file``.
+
+.. clicmd:: domainname DOMAINNAME
+
+ Set domainname of the router.
.. clicmd:: password PASSWORD
is used to start the daemon then this command is turned on by default
and cannot be turned off and the [no] form of the command is dissallowed.
-.. clicmd:: log-filter WORD [DAEMON]
+.. clicmd:: log filtered-file [FILENAME [LEVEL]]
+
+ Configure a destination file for filtered logs with the
+ :clicmd:`log filter-text WORD` command.
+
+.. clicmd:: log filter-text WORD
This command forces logs to be filtered on a specific string. A log message
will only be printed if it matches on one of the filters in the log-filter
- table. Can be daemon independent.
+ table. The filter only applies to file logging targets configured with
+ :clicmd:`log filtered-file [FILENAME [LEVEL]]`.
.. note::
Log filters prevent this but you should still expect a small performance
hit due to filtering each of all those logs.
-.. clicmd:: log-filter clear [DAEMON]
+ .. note::
+
+ This setting is not saved to ``frr.conf`` and not shown in
+ :clicmd:`show running-config`. It is intended for ephemeral debugging
+ purposes only.
+
+.. clicmd:: clear log filter-text
- This command clears all current filters in the log-filter table. Can be
- daemon independent.
+ This command clears all current filters in the log-filter table.
.. clicmd:: log immediate-mode
exit1# show bgp summary
- IPv4 Unicast Summary:
+ IPv4 Unicast Summary (VRF default):
BGP router identifier 10.10.10.1, local AS number 65001 vrf-id 0
BGP table version 4
RIB entries 7, using 1344 bytes of memory
Note that you should not enable both the advertise-svi-ip and the advertise-default-gw
at the same time.
+.. _bgp-evpn-overlay-index-gateway-ip:
+
+EVPN Overlay Index Gateway IP
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Draft https://tools.ietf.org/html/draft-ietf-bess-evpn-prefix-advertisement-11
+explains the use of overlay indexes for recursive route resolution for EVPN
+type-5 route.
+
+We support gateway IP overlay index.
+A gateway IP, advertised with EVPN prefix route, is used to find an EVPN MAC/IP
+route with its IP field same as the gateway IP. This MAC/IP entry provides the
+nexthop VTEP and the tunnel information required for the VxLAN encapsulation.
+
+Functionality:
+
+::
+
+ . +--------+ BGP +--------+ BGP +--------+ +--------+
+ SN1 | | IPv4 | | EVPN | | | |
+ ======+ Host1 +------+ PE1 +------+ PE2 +------+ Host2 +
+ | | | | | | | |
+ +--------+ +--------+ +--------+ +--------+
+
+Consider above topology where prefix SN1 is connected behind host1. Host1
+advertises SN1 to PE1 over BGP IPv4 session. PE1 advertises SN1 to PE2 using
+EVPN type-5 route with host1 IP as the gateway IP. PE1 also advertises
+Host1 MAC/IP as type-2 route which is used to resolve host1 gateway IP.
+
+PE2 receives this type-5 route and imports it into the vrf based on route
+targets. BGP prefix imported into the vrf uses gateway IP as its BGP nexthop.
+This route is installed into zebra if following conditions are satisfied:
+1. Gateway IP nexthop is L3 reachable.
+2. PE2 has received EVPN type-2 route with IP field set to gateway IP.
+
+Topology requirements:
+1. This feature is supported for asymmetric routing model only. While
+ sending packets to SN1, ingress PE (PE2) performs routing and
+ egress PE (PE1) performs only bridging.
+2. This feature supports only tratitional(non vlan-aware) bridge model. Bridge
+ interface associated with L2VNI is an L3 interface. i.e., this interface is
+ configured with an address in the L2VNI subnet. Note that the gateway IP
+ should also have an address in the same subnet.
+3. As this feature works in asymmetric routing model, all L2VNIs and corresponding
+ VxLAN and bridge interfaces should be present at all the PEs.
+4. L3VNI configuration is required to generate and import EVPN type-5 routes.
+ L3VNI VxLAN and bridge interfaces also should be present.
+
+A PE can use one of the following two mechanisms to advertise an EVPN type-5
+route with gateway IP.
+
+1. CLI to add gateway IP while generating EVPN type-5 route from a BGP IPv4/IPv6
+prefix:
+
+.. index:: advertise <ipv4|ipv6> unicast [gateway-ip]
+.. clicmd:: [no] advertise <ipv4|ipv6> unicast [gateway-ip]
+
+When this CLI is configured for a BGP vrf under L2VPN EVPN address family, EVPN
+type-5 routes are generated for BGP prefixes in the vrf. Nexthop of the BGP
+prefix becomes the gateway IP of the corresponding type-5 route.
+
+If the above command is configured without the "gateway-ip" keyword, type-5
+routes are generated without overlay index.
+
+2. Add gateway IP to EVPN type-5 route using a route-map:
+
+.. index:: set evpn gateway-ip <ipv4|ipv6> <addr>
+.. clicmd:: [no] set evpn gateway-ip <ipv4|ipv6> <addr>
+
+When route-map with above set clause is applied as outbound policy in BGP, it
+will set the gateway-ip in EVPN type-5 NLRI.
+
+Example configuration:
+
+.. code-block:: frr
+
+ router bgp 100
+ neighbor 192.168.0.1 remote-as 101
+ !
+ address-family ipv4 l2vpn evpn
+ neighbor 192.168.0.1 route-map RMAP out
+ exit-address-family
+ !
+ route-map RMAP permit 10
+ set evpn gateway-ip 10.0.0.1
+ set evpn gateway-ip 10::1
+
+A PE that receives a type-5 route with gateway IP overlay index should have
+"enable-resolve-overlay-index" configuration enabled to recursively resolve the
+overlay index nexthop and install the prefix into zebra.
+
+.. index:: enable-resolve-overlay-index
+.. clicmd:: [no] enable-resolve-overlay-index
+
+Example configuration:
+
+.. code-block:: frr
+
+ router bgp 65001
+ bgp router-id 192.168.100.1
+ no bgp ebgp-requires-policy
+ neighbor 10.0.1.2 remote-as 65002
+ !
+ address-family l2vpn evpn
+ neighbor 10.0.1.2 activate
+ advertise-all-vni
+ enable-resolve-overlay-index
+ exit-address-family
+ !
+
EVPN Multihoming
^^^^^^^^^^^^^^^^
exit1# show ip bgp summary wide
- IPv4 Unicast Summary:
+ IPv4 Unicast Summary (VRF default):
BGP router identifier 192.168.100.1, local AS number 65534 vrf-id 0
BGP table version 3
RIB entries 5, using 920 bytes of memory
address-family. The remote-as filter can be used in combination with the
failed, established filters.
+.. clicmd:: show bgp [afi] [safi] [all] summary terse [json]
+
+ Shorten the output. Do not show the following information about the BGP
+ instances: the number of RIB entries, the table version and the used memory.
+ The ``terse`` option can be used in combination with the remote-as, neighbor,
+ failed and established filters, and with the ``wide`` option as well.
+
.. clicmd:: show bgp [afi] [safi] [neighbor [PEER] [routes|advertised-routes|received-routes] [json]
This command shows information on a specific BGP peer of the relevant
# node later on
app.add_object_type("clicmd", "clicmd", indextemplate="pair: %s; configuration command")
- # css overrides for HTML theme
- # Note sphinx version differences
- sver = vparse(sphinx.__version__)
-
- if sver < vparse("1.8.0"):
- app.add_stylesheet("overrides.css")
- app.add_javascript("overrides.js")
+ # I dont care how stupid this is
+ if "add_js_file" in dir(app):
+ app.add_js_file("overrides.js")
else:
+ app.add_javascript("overrides.js")
+
+ if "add_css_file" in dir(app):
app.add_css_file("overrides.css")
- app.add_js_file("overrides.js")
+ else:
+ app.add_stylesheet("overrides.css")
+
# load Pygments lexer for FRR config syntax
#
Set router's Router-ID.
-.. clicmd:: interface IFNAME area (0-4294967295)
-
-.. clicmd:: interface IFNAME area A.B.C.D
-
- Bind interface to specified area, and start sending OSPF packets. `area` can
- be specified as 0.
-
.. clicmd:: timers throttle spf (0-600000) (0-600000) (0-600000)
This command sets the initial `delay`, the `initial-holdtime`
Use this command to control the maximum number of parallel routes that
OSPFv3 can support. The default is 64.
+.. clicmd:: write-multiplier (1-100)
+
+ Use this command to tune the amount of work done in the packet read and
+ write threads before relinquishing control. The parameter is the number
+ of packets to process before returning. The default value of this parameter
+ is 20.
+
.. _ospf6-area:
OSPF6 area
==========
-Area support for OSPFv3 is not yet implemented.
+.. index:: [no] area A.B.C.D nssa
+.. clicmd:: [no] area A.B.C.D nssa
+
+NSSA Support in OSPFv3
+=======================
+
+The configuration of NSSA areas in OSPFv3 is supported using the CLI command
+area A.B.C.D nssa in ospf6 router configuration mode.
+The following functionalities are implemented as per RFC 3101:
+
+1. Advertising Type-7 LSA into NSSA area when external route is redistributed
+ into OSPFv3
+2. Processing Type-7 LSA received from neighbor and installing route in the
+ route table
+3. Support for NSSA ABR functionality which is generating Type-5 LSA when
+ backbone area is configured. Currently translation od TYpe-7 LSA to Type-5 LSA
+ is enabled by default.
+4. Support for NSSA Translator functionality when there are multiple NSSA ABR
+ in an area
.. _ospf6-interface:
OSPF6 interface
===============
+.. clicmd:: ipv6 ospf6 area <A.B.C.D|(0-4294967295)>
+
+ Enable OSPFv3 on the interface and add it to the specified area.
+
.. clicmd:: ipv6 ospf6 cost COST
Sets interface's output cost. Default value depends on the interface
.. code-block:: frr
interface eth0
+ ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 instance-id 0
!
router ospf6
ospf6 router-id 212.17.55.53
area 0.0.0.0 range 2001:770:105:2::/64
- interface eth0 area 0.0.0.0
!
debug ospf6 neighbor state
!
interface fxp0
+ ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 cost 1
ipv6 ospf6 hello-interval 10
ipv6 ospf6 dead-interval 40
router ospf6
router-id 255.1.1.1
redistribute static route-map static-ospf6
- interface fxp0 area 0.0.0.0
!
access-list access4 permit 127.0.0.1/32
!
Stub links may also be used as a way to describe links on which OSPF is
*not* spoken, known as `passive interfaces`, see
- :clicmd:`passive-interface INTERFACE`.
+ :clicmd:`ip ospf passive [A.B.C.D]`.
- Network LSA
detail argument, all changes in adjacency status are shown. Without detail,
only changes to full or regressions are shown.
-.. clicmd:: passive-interface INTERFACE
+.. clicmd:: passive-interface default
-
- Do not speak OSPF interface on the
- given interface, but do advertise the interface as a stub link in the
- router-:abbr:`LSA (Link State Advertisement)` for this router. This
- allows one to advertise addresses on such connected interfaces without
- having to originate AS-External/Type-5 LSAs (which have global flooding
- scope) - as would occur if connected addresses were redistributed into
- OSPF (:ref:`redistribute-routes-to-ospf`). This is the only way to
- advertise non-OSPF links into stub areas.
+ Make all interfaces that belong to this router passive by default. For the
+ description of passive interface look at :clicmd:`ip ospf passive [A.B.C.D]`.
+ Per-interface configuration takes precedence over the default value.
.. clicmd:: timers throttle spf (0-600000) (0-600000) (0-600000)
a specific destination. The upper limit may differ if you change the value
of MULTIPATH_NUM during compilation. The default is MULTIPATH_NUM (64).
+.. clicmd:: write-multiplier (1-100)
+
+ Use this command to tune the amount of work done in the packet read and
+ write threads before relinquishing control. The parameter is the number
+ of packets to process before returning. The defult value of this parameter
+ is 20.
+
.. _ospf-area:
Areas
Set number of seconds for InfTransDelay value. LSAs' age should be
incremented by this value when transmitting. The default value is 1 second.
+.. clicmd:: ip ospf passive [A.B.C.D]
+
+ Do not speak OSPF on the interface, but do advertise the interface as a stub
+ link in the router-:abbr:`LSA (Link State Advertisement)` for this router.
+ This allows one to advertise addresses on such connected interfaces without
+ having to originate AS-External/Type-5 LSAs (which have global flooding
+ scope) - as would occur if connected addresses were redistributed into
+ OSPF (:ref:`redistribute-routes-to-ospf`). This is the only way to
+ advertise non-OSPF links into stub areas.
+
.. clicmd:: ip ospf area (A.B.C.D|(0-4294967295))
NSSA areas and are not redistributed at all into Stub areas, where external
routes are not permitted.
- Note that for connected routes, one may instead use the `passive-interface`
- configuration.
-
-.. seealso::
-
- clicmd:`passive-interface INTERFACE`.
+ Note that for connected routes, one may instead use the
+ :clicmd:`ip ospf passive [A.B.C.D]` configuration.
.. clicmd:: default-information originate
ip ospf message-digest-key 1 md5 ABCDEFGHIJK
!
interface ppp0
+ ip ospf passive
!
interface br0
ip ospf authentication message-digest
router ospf
ospf router-id 192.168.0.1
redistribute connected
- passive interface ppp0
network 192.168.0.0/24 area 0.0.0.0
network 10.0.0.0/16 area 0.0.0.0
network 192.168.1.0/24 area 0.0.0.1
--no-cache \
--allow-untrusted /pkgs/apk/*/*.apk \
&& rm -rf /pkgs
+
+# Own the config / PID files
+RUN mkdir -p /var/run/frr
+RUN chown -R frr:frr /etc/frr /var/run/frr
+
+# Simple init manager for reaping processes and forwarding signals
+ENTRYPOINT ["/sbin/tini", "--"]
+
+# Default CMD starts watchfrr
COPY docker/alpine/docker-start /usr/lib/frr/docker-start
-CMD [ "/sbin/tini", "--", "/usr/lib/frr/docker-start" ]
+CMD ["/usr/lib/frr/docker-start"]
-#!/bin/sh
+#!/bin/ash
-set -e
-
-##
-# For volume mounts...
-##
-chown -R frr:frr /etc/frr || true
-/usr/lib/frr/frrinit.sh start
-
-# Sleep forever
-exec tail -f /dev/null
+source /usr/lib/frr/frrcommon.sh
+/usr/lib/frr/watchfrr $(daemon_list)
RUN yum install -y /pkgs/rpm/*/*.rpm \
&& rm -rf /pkgs
+
+# Own the config / PID files
+RUN mkdir -p /var/run/frr
+RUN chown -R frr:frr /etc/frr /var/run/frr
+
+# Add tini because no CentOS7 package
+ENV TINI_VERSION v0.19.0
+ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /sbin/tini
+RUN chmod +x /sbin/tini
+
+# Simple init manager for reaping processes and forwarding signals
+ENTRYPOINT ["/sbin/tini", "--"]
+
+# Default CMD starts watchfrr
COPY docker/centos-7/docker-start /usr/lib/frr/docker-start
-CMD [ "/usr/lib/frr/docker-start" ]
+CMD ["/usr/lib/frr/docker-start"]
-#!/bin/sh
+#!/bin/bash
-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
+source /usr/lib/frr/frrcommon.sh
+/usr/lib/frr/watchfrr $(daemon_list)
RUN yum install -y /pkgs/rpm/*/*.rpm \
&& rm -rf /pkgs
+
+# Own the config / PID files
+RUN mkdir -p /var/run/frr
+RUN chown -R frr:frr /etc/frr /var/run/frr
+
+# Add tini because no CentOS8 package
+ENV TINI_VERSION v0.19.0
+ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /sbin/tini
+RUN chmod +x /sbin/tini
+
+# Simple init manager for reaping processes and forwarding signals
+ENTRYPOINT ["/sbin/tini", "--"]
+
+# Default CMD starts watchfrr
COPY docker/centos-8/docker-start /usr/lib/frr/docker-start
-CMD [ "/usr/lib/frr/docker-start" ]
+CMD ["/usr/lib/frr/docker-start"]
-#!/bin/sh
+#!/bin/bash
-set -e
-
-chown -R frr:frr /etc/frr
-/usr/lib/frr/frrinit.sh start
-
-# Sleep forever
-exec tail -f /dev/null
+source /usr/lib/frr/frrcommon.sh
+/usr/lib/frr/watchfrr $(daemon_list)
RUN apt-get update && \
apt-get install -y libpcre3-dev apt-transport-https ca-certificates curl wget logrotate \
- libc-ares2 libjson-c3 vim procps libreadline7 gnupg2 lsb-release apt-utils && \
- rm -rf /var/lib/apt/lists/*
+ libc-ares2 libjson-c3 vim procps libreadline7 gnupg2 lsb-release apt-utils \
+ tini && rm -rf /var/lib/apt/lists/*
RUN curl -s https://deb.frrouting.org/frr/keys.asc | apt-key add -
RUN echo deb https://deb.frrouting.org/frr $(lsb_release -s -c) frr-stable | tee -a /etc/apt/sources.list.d/frr.list
apt-get install -y frr frr-pythontools && \
rm -rf /var/lib/apt/lists/*
-ADD docker-start /usr/sbin/docker-start
-CMD ["/usr/sbin/docker-start"]
+# Own the config / PID files
+RUN mkdir -p /var/run/frr
+RUN chown -R frr:frr /etc/frr /var/run/frr
+
+# Simple init manager for reaping processes and forwarding signals
+ENTRYPOINT ["/usr/bin/tini", "--"]
+
+# Default CMD starts watchfrr
+COPY docker-start /usr/lib/frr/docker-start
+CMD ["/usr/lib/frr/docker-start"]
-#!/bin/sh
+#!/bin/bash
-set -e
-
-##
-# For volume mounts...
-##
-chown -R frr:frr /etc/frr
-/etc/init.d/frr start
-
-# Sleep forever
-exec tail -f /dev/null
+source /usr/lib/frr/frrcommon.sh
+/usr/lib/frr/watchfrr $(daemon_list)
install-info build-essential libsystemd-dev libsnmp-dev perl \
libcap-dev python2 libelf-dev \
sudo gdb curl iputils-ping time \
+ libgrpc++-dev libgrpc-dev protobuf-compiler-grpc \
mininet iproute2 iperf && \
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output /tmp/get-pip.py && \
python2 /tmp/get-pip.py && \
--sbindir=/usr/lib/frr \
--sysconfdir=/etc/frr \
--enable-vtysh \
+ --enable-grpc \
--enable-pimd \
--enable-sharpd \
--enable-multipath=64 \
install_element(EIGRP_NODE, &eigrp_neighbor_cmd);
install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd);
+ vrf_cmd_init(NULL, &eigrpd_privs);
+
install_node(&eigrp_interface_node);
if_cmd_init();
struct isis_dynhn *dyn;
- dyn = dynhn_find_by_id(adj->sysid);
+ dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid);
if (dyn)
return dyn->hostname;
else
if (!adj)
return;
- dyn = dynhn_find_by_id(adj->sysid);
+ dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid);
if (dyn)
zlog_debug("%s", dyn->hostname);
vty_out(vty, " SNPA: %s", snpa_print(adj->snpa));
if (adj->circuit
&& (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) {
- dyn = dynhn_find_by_id(adj->lanid);
+ dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid);
if (dyn)
vty_out(vty, ", LAN id: %s.%02x", dyn->hostname,
adj->lanid[ISIS_SYS_ID_LEN]);
static void isis_circuit_enable(struct isis_circuit *circuit)
{
- struct isis_area *area;
+ struct isis_area *area = circuit->area;
struct interface *ifp = circuit->interface;
- area = isis_area_lookup(circuit->tag, ifp->vrf_id);
- if (area)
- isis_area_add_circuit(area, circuit);
+ if (!area) {
+ area = isis_area_lookup(circuit->tag, ifp->vrf_id);
+ if (area)
+ isis_area_add_circuit(area, circuit);
+ }
if (if_is_operative(ifp))
isis_csm_state_change(IF_UP_FROM_Z, circuit, ifp);
isis = isis_lookup_by_vrfid(vrf->vrf_id);
- if (isis == NULL) {
- vty_out(vty, "ISIS routing instance not found");
+ if (isis == NULL)
return 0;
- }
FOR_ALL_INTERFACES (vrf, ifp) {
/* IF name */
DEFINE_MTYPE_STATIC(ISISD, ISIS_DYNHN, "ISIS dyn hostname");
-extern struct host host;
-
-struct list *dyn_cache = NULL;
static int dyn_cache_cleanup(struct thread *);
void dyn_cache_init(struct isis *isis)
{
- if (dyn_cache == NULL)
- dyn_cache = list_new();
+ isis->dyn_cache = list_new();
if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST))
thread_add_timer(master, dyn_cache_cleanup, isis, 120,
&isis->t_dync_clean);
- return;
}
-void dyn_cache_cleanup_all(void)
+void dyn_cache_finish(struct isis *isis)
{
struct listnode *node, *nnode;
struct isis_dynhn *dyn;
- for (ALL_LIST_ELEMENTS(dyn_cache, node, nnode, dyn)) {
- list_delete_node(dyn_cache, node);
+ thread_cancel(&isis->t_dync_clean);
+
+ for (ALL_LIST_ELEMENTS(isis->dyn_cache, node, nnode, dyn)) {
+ list_delete_node(isis->dyn_cache, node);
XFREE(MTYPE_ISIS_DYNHN, dyn);
}
+
+ list_delete(&isis->dyn_cache);
}
static int dyn_cache_cleanup(struct thread *thread)
isis->t_dync_clean = NULL;
- for (ALL_LIST_ELEMENTS(dyn_cache, node, nnode, dyn)) {
+ for (ALL_LIST_ELEMENTS(isis->dyn_cache, node, nnode, dyn)) {
if ((now - dyn->refresh) < MAX_LSP_LIFETIME)
continue;
- list_delete_node(dyn_cache, node);
+ list_delete_node(isis->dyn_cache, node);
XFREE(MTYPE_ISIS_DYNHN, dyn);
}
return ISIS_OK;
}
-struct isis_dynhn *dynhn_find_by_id(const uint8_t *id)
+struct isis_dynhn *dynhn_find_by_id(struct isis *isis, const uint8_t *id)
{
struct listnode *node = NULL;
struct isis_dynhn *dyn = NULL;
- for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn))
+ for (ALL_LIST_ELEMENTS_RO(isis->dyn_cache, node, dyn))
if (memcmp(dyn->id, id, ISIS_SYS_ID_LEN) == 0)
return dyn;
return NULL;
}
-struct isis_dynhn *dynhn_find_by_name(const char *hostname)
+struct isis_dynhn *dynhn_find_by_name(struct isis *isis, const char *hostname)
{
struct listnode *node = NULL;
struct isis_dynhn *dyn = NULL;
- for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn))
+ for (ALL_LIST_ELEMENTS_RO(isis->dyn_cache, node, dyn))
if (strncmp(dyn->hostname, hostname, 255) == 0)
return dyn;
return NULL;
}
-void isis_dynhn_insert(const uint8_t *id, const char *hostname, int level)
+void isis_dynhn_insert(struct isis *isis, const uint8_t *id,
+ const char *hostname, int level)
{
struct isis_dynhn *dyn;
- dyn = dynhn_find_by_id(id);
+ dyn = dynhn_find_by_id(isis, id);
if (!dyn) {
dyn = XCALLOC(MTYPE_ISIS_DYNHN, sizeof(struct isis_dynhn));
memcpy(dyn->id, id, ISIS_SYS_ID_LEN);
dyn->level = level;
- listnode_add(dyn_cache, dyn);
+ listnode_add(isis->dyn_cache, dyn);
}
snprintf(dyn->hostname, sizeof(dyn->hostname), "%s", hostname);
dyn->refresh = time(NULL);
}
-void isis_dynhn_remove(const uint8_t *id)
+void isis_dynhn_remove(struct isis *isis, const uint8_t *id)
{
struct isis_dynhn *dyn;
- dyn = dynhn_find_by_id(id);
+ dyn = dynhn_find_by_id(isis, id);
if (!dyn)
return;
- listnode_delete(dyn_cache, dyn);
+ listnode_delete(isis->dyn_cache, dyn);
XFREE(MTYPE_ISIS_DYNHN, dyn);
}
if (!isis->sysid_set)
return;
vty_out(vty, "Level System ID Dynamic Hostname\n");
- for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->dyn_cache, node, dyn)) {
vty_out(vty, "%-7d", dyn->level);
vty_out(vty, "%-15s%-15s\n", sysid_print(dyn->id),
dyn->hostname);
return;
}
-struct isis_dynhn *dynhn_snmp_next(const uint8_t *id, int level)
+struct isis_dynhn *dynhn_snmp_next(struct isis *isis, const uint8_t *id,
+ int level)
{
struct listnode *node = NULL;
struct isis_dynhn *dyn = NULL;
struct isis_dynhn *found_dyn = NULL;
int res;
- for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->dyn_cache, node, dyn)) {
res = memcmp(dyn->id, id, ISIS_SYS_ID_LEN);
if (res < 0)
};
void dyn_cache_init(struct isis *isis);
-void dyn_cache_cleanup_all(void);
-void isis_dynhn_insert(const uint8_t *id, const char *hostname, int level);
-void isis_dynhn_remove(const uint8_t *id);
-struct isis_dynhn *dynhn_find_by_id(const uint8_t *id);
-struct isis_dynhn *dynhn_find_by_name(const char *hostname);
+void dyn_cache_finish(struct isis *isis);
+void isis_dynhn_insert(struct isis *isis, const uint8_t *id,
+ const char *hostname, int level);
+void isis_dynhn_remove(struct isis *isis, const uint8_t *id);
+struct isis_dynhn *dynhn_find_by_id(struct isis *isis, const uint8_t *id);
+struct isis_dynhn *dynhn_find_by_name(struct isis *isis, const char *hostname);
void dynhn_print_all(struct vty *vty, struct isis *isis);
/* Snmp support */
-struct isis_dynhn *dynhn_snmp_next(const uint8_t *id, int level);
+struct isis_dynhn *dynhn_snmp_next(struct isis *isis, const uint8_t *id,
+ int level);
#endif /* _ZEBRA_ISIS_DYNHN_H */
if (area->dynhostname && lsp->tlvs->hostname
&& lsp->hdr.rem_lifetime) {
- isis_dynhn_insert(lsp->hdr.lsp_id, lsp->tlvs->hostname,
- (lsp->hdr.lsp_bits & LSPBIT_IST)
- == IS_LEVEL_1_AND_2
- ? IS_LEVEL_2
- : IS_LEVEL_1);
+ isis_dynhn_insert(
+ area->isis, lsp->hdr.lsp_id, lsp->tlvs->hostname,
+ (lsp->hdr.lsp_bits & LSPBIT_IST) == IS_LEVEL_1_AND_2
+ ? IS_LEVEL_2
+ : IS_LEVEL_1);
}
return;
char id[SYSID_STRLEN];
if (dynhost)
- dyn = dynhn_find_by_id(lsp_id);
+ dyn = dynhn_find_by_id(isis, lsp_id);
else
dyn = NULL;
{
struct isis_dynhn *dyn;
struct isis *isis = NULL;
+ struct listnode *node;
if (!sysid)
return "nullsysid";
if (isis && !CHECK_FLAG(im->options, F_ISIS_UNIT_TEST))
return cmd_hostname_get();
- dyn = dynhn_find_by_id(sysid);
- if (dyn)
- return dyn->hostname;
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
+ dyn = dynhn_find_by_id(isis, sysid);
+ if (dyn)
+ return dyn->hostname;
+ }
return sysid_print(sysid);
}
struct yang_data *data;
struct isis_circuit *circuit = adj->circuit;
struct isis_area *area = circuit->area;
- struct isis_dynhn *dyn = dynhn_find_by_id(adj->sysid);
+ struct isis_dynhn *dyn = dynhn_find_by_id(circuit->isis, adj->sysid);
notif_prep_instance_hdr(xpath, area, "default", arguments);
notif_prepr_iface_hdr(xpath, circuit, arguments);
oid *oid_idx;
size_t oid_idx_len;
size_t off = 0;
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+
+ if (isis == NULL)
+ return NULL;
*write_method = NULL;
cmp_level = (int)oid_idx[ISIS_SYS_ID_LEN + 1];
- dyn = dynhn_find_by_id(cmp_buf);
+ dyn = dynhn_find_by_id(isis, cmp_buf);
if (dyn == NULL || dyn->level != cmp_level)
return NULL;
*/
cmp_level = (int)(IS_LEVEL_2 + 1);
- dyn = dynhn_snmp_next(cmp_buf, cmp_level);
+ dyn = dynhn_snmp_next(isis, cmp_buf, cmp_level);
if (dyn == NULL)
return NULL;
vty_out(vty, "Area %s:\n",
area->area_tag ? area->area_tag : "null");
if (lspid) {
- lsp = lsp_for_arg(head, lspid, isis);
+ lsp = lsp_for_sysid(head, lspid, isis);
if (lsp)
lsp_print_flooding(vty, lsp, isis);
continue;
isis_redist_free(isis);
list_delete(&isis->area_list);
+ dyn_cache_finish(isis);
XFREE(MTYPE_ISIS, isis);
}
continue;
circuit = ifp->info;
- if (circuit)
+ if (circuit && strmatch(circuit->tag, area->area_tag))
isis_area_add_circuit(area, circuit);
}
}
{
vrf_init(isis_vrf_new, isis_vrf_enable, isis_vrf_disable,
isis_vrf_delete, isis_vrf_enable);
+
+ vrf_cmd_init(NULL, &isisd_privs);
}
void isis_terminate()
vrf_name, all_vrf);
}
+static int id_to_sysid(struct isis *isis, const char *id, uint8_t *sysid)
+{
+ struct isis_dynhn *dynhn;
+
+ memset(sysid, 0, ISIS_SYS_ID_LEN);
+ if (id) {
+ if (sysid2buff(sysid, id) == 0) {
+ dynhn = dynhn_find_by_name(isis, id);
+ if (dynhn == NULL)
+ return -1;
+ memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN);
+ }
+ }
+
+ return 0;
+}
+
static void isis_neighbor_common(struct vty *vty, const char *id, char detail,
struct isis *isis, uint8_t *sysid)
{
const char *vrf_name, bool all_vrf)
{
struct listnode *node;
- struct isis_dynhn *dynhn;
uint8_t sysid[ISIS_SYS_ID_LEN];
struct isis *isis;
return CMD_SUCCESS;
}
- memset(sysid, 0, ISIS_SYS_ID_LEN);
- if (id) {
- if (sysid2buff(sysid, id) == 0) {
- dynhn = dynhn_find_by_name(id);
- if (dynhn == NULL) {
- vty_out(vty, "Invalid system id %s\n", id);
- return CMD_SUCCESS;
- }
- memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN);
- }
- }
-
if (vrf_name) {
if (all_vrf) {
for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
+ if (id_to_sysid(isis, id, sysid)) {
+ vty_out(vty, "Invalid system id %s\n",
+ id);
+ return CMD_SUCCESS;
+ }
isis_neighbor_common(vty, id, detail, isis,
sysid);
}
return CMD_SUCCESS;
}
isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL)
+ if (isis != NULL) {
+ if (id_to_sysid(isis, id, sysid)) {
+ vty_out(vty, "Invalid system id %s\n", id);
+ return CMD_SUCCESS;
+ }
isis_neighbor_common(vty, id, detail, isis, sysid);
+ }
}
return CMD_SUCCESS;
bool all_vrf)
{
struct listnode *node;
- struct isis_dynhn *dynhn;
uint8_t sysid[ISIS_SYS_ID_LEN];
struct isis *isis;
return CMD_SUCCESS;
}
- memset(sysid, 0, ISIS_SYS_ID_LEN);
- if (id) {
- if (sysid2buff(sysid, id) == 0) {
- dynhn = dynhn_find_by_name(id);
- if (dynhn == NULL) {
- vty_out(vty, "Invalid system id %s\n", id);
- return CMD_SUCCESS;
- }
- memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN);
- }
- }
if (vrf_name) {
if (all_vrf) {
- for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
+ if (id_to_sysid(isis, id, sysid)) {
+ vty_out(vty, "Invalid system id %s\n",
+ id);
+ return CMD_SUCCESS;
+ }
isis_neighbor_common_clear(vty, id, sysid,
isis);
+ }
return CMD_SUCCESS;
}
isis = isis_lookup_by_vrfname(vrf_name);
- if (isis != NULL)
+ if (isis != NULL) {
+ if (id_to_sysid(isis, id, sysid)) {
+ vty_out(vty, "Invalid system id %s\n", id);
+ return CMD_SUCCESS;
+ }
isis_neighbor_common_clear(vty, id, sysid, isis);
+ }
}
return CMD_SUCCESS;
return CMD_SUCCESS;
}
-struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
- struct isis *isis)
+struct isis_lsp *lsp_for_sysid(struct lspdb_head *head, const char *sysid_str,
+ struct isis *isis)
{
char sysid[255] = {0};
- uint8_t number[3];
+ uint8_t number[3] = {0};
const char *pos;
uint8_t lspid[ISIS_SYS_ID_LEN + 2] = {0};
struct isis_dynhn *dynhn;
struct isis_lsp *lsp = NULL;
- if (!argv)
+ if (!sysid_str)
return NULL;
/*
- * extract fragment and pseudo id from the string argv
+ * extract fragment and pseudo id from the string sysid_str
* in the forms:
* (a) <systemid/hostname>.<pseudo-id>-<framenent> or
* (b) <systemid/hostname>.<pseudo-id> or
* Where systemid is in the form:
* xxxx.xxxx.xxxx
*/
- if (argv)
- strlcpy(sysid, argv, sizeof(sysid));
- if (argv && strlen(argv) > 3) {
- pos = argv + strlen(argv) - 3;
+ strlcpy(sysid, sysid_str, sizeof(sysid));
+
+ if (strlen(sysid_str) > 3) {
+ pos = sysid_str + strlen(sysid_str) - 3;
if (strncmp(pos, "-", 1) == 0) {
memcpy(number, ++pos, 2);
lspid[ISIS_SYS_ID_LEN + 1] =
memcpy(number, ++pos, 2);
lspid[ISIS_SYS_ID_LEN] =
(uint8_t)strtol((char *)number, NULL, 16);
- sysid[pos - argv - 1] = '\0';
+ sysid[pos - sysid_str - 1] = '\0';
}
}
/*
- * Try to find the lsp-id if the argv
- * string is in
- * the form
+ * Try to find the lsp-id if the sysid_str
+ * is in the form
* hostname.<pseudo-id>-<fragment>
*/
if (sysid2buff(lspid, sysid)) {
lsp = lsp_search(head, lspid);
- } else if ((dynhn = dynhn_find_by_name(sysid))) {
+ } else if ((dynhn = dynhn_find_by_name(isis, sysid))) {
memcpy(lspid, dynhn->id, ISIS_SYS_ID_LEN);
lsp = lsp_search(head, lspid);
} else if (strncmp(cmd_hostname_get(), sysid, 15) == 0) {
void show_isis_database_lspdb(struct vty *vty, struct isis_area *area,
int level, struct lspdb_head *lspdb,
- const char *argv, int ui_level)
+ const char *sysid_str, int ui_level)
{
struct isis_lsp *lsp;
int lsp_count;
if (lspdb_count(lspdb) > 0) {
- lsp = lsp_for_arg(lspdb, argv, area->isis);
+ lsp = lsp_for_sysid(lspdb, sysid_str, area->isis);
- if (lsp != NULL || argv == NULL) {
+ if (lsp != NULL || sysid_str == NULL) {
vty_out(vty, "IS-IS Level-%d link-state database:\n",
level + 1);
else
lsp_print(lsp, vty, area->dynhostname,
area->isis);
- } else if (argv == NULL) {
+ } else if (sysid_str == NULL) {
lsp_count =
lsp_print_all(vty, lspdb, ui_level,
area->dynhostname, area->isis);
}
}
-static void show_isis_database_common(struct vty *vty, const char *argv,
+static void show_isis_database_common(struct vty *vty, const char *sysid_str,
int ui_level, struct isis *isis)
{
struct listnode *node;
for (level = 0; level < ISIS_LEVELS; level++)
show_isis_database_lspdb(vty, area, level,
- &area->lspdb[level], argv,
+ &area->lspdb[level], sysid_str,
ui_level);
}
}
* [ show isis database detail <sysid>.<pseudo-id>-<fragment-number> ]
* [ show isis database detail <hostname>.<pseudo-id>-<fragment-number> ]
*/
-static int show_isis_database(struct vty *vty, const char *argv, int ui_level,
- const char *vrf_name, bool all_vrf)
+static int show_isis_database(struct vty *vty, const char *sysid_str,
+ int ui_level, const char *vrf_name, bool all_vrf)
{
struct listnode *node;
struct isis *isis;
if (vrf_name) {
if (all_vrf) {
for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
- show_isis_database_common(vty, argv, ui_level,
- isis);
+ show_isis_database_common(vty, sysid_str,
+ ui_level, isis);
return CMD_SUCCESS;
}
isis = isis_lookup_by_vrfname(vrf_name);
if (isis)
- show_isis_database_common(vty, argv, ui_level, isis);
+ show_isis_database_common(vty, sysid_str, ui_level,
+ isis);
}
return CMD_SUCCESS;
struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */
uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */
int snmp_notifications;
+ struct list *dyn_cache;
struct route_table *ext_info[REDIST_PROTOCOL_COUNT];
};
void isis_filter_update(struct access_list *access);
void isis_prefix_list_update(struct prefix_list *plist);
void print_debug(struct vty *, int, int);
-struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
- struct isis *isis);
+struct isis_lsp *lsp_for_sysid(struct lspdb_head *head, const char *sysid_str,
+ struct isis *isis);
void isis_area_invalidate_routes(struct isis_area *area, int levels);
void isis_area_verify_routes(struct isis_area *area);
*/
typedef struct {word_t *data; size_t n, m; } bitfield_t;
+DECLARE_MTYPE(BITFIELD);
+
/**
* Initialize the bits.
* @v: an instance of bitfield_t struct.
do { \
(v).n = 0; \
(v).m = ((N) / WORD_SIZE + 1); \
- (v).data = calloc(1, ((v).m * sizeof(word_t))); \
+ (v).data = XCALLOC(MTYPE_BITFIELD, ((v).m * sizeof(word_t))); \
} while (0)
/**
*/
#define bf_free(v) \
do { \
- free((v).data); \
+ XFREE(MTYPE_BITFIELD, (v).data); \
(v).data = NULL; \
} while (0)
return saved_ret;
if (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
&& ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) {
/* This assumes all nodes above CONFIG_NODE are childs of
* CONFIG_NODE */
ret = cmd_execute_command_real(vline, FILTER_RELAXED,
vty, cmd, 0);
if (ret == CMD_SUCCESS || ret == CMD_WARNING
+ || ret == CMD_ERR_AMBIGUOUS || ret == CMD_ERR_INCOMPLETE
|| ret == CMD_NOT_MY_INSTANCE
|| ret == CMD_WARNING_CONFIG_FAILED)
return ret;
while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
&& !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
&& ret != CMD_SUCCESS && ret != CMD_WARNING
+ && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
&& ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
&& ret != CMD_NO_LEVEL_UP)
ret = cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd,
static void print_cmd(struct vty *vty, const char *cmd)
{
int i, j, len = strlen(cmd);
- char buf[len];
+ char buf[len + 1];
bool skip = false;
j = 0;
#define assume(x)
#endif
-/* pure = function does not modify memory & return value is the same if
- * memory hasn't changed (=> allows compiler to optimize)
- *
- * Mostly autodetected by the compiler if function body is available (i.e.
- * static inline functions in headers). Since that implies it should only be
- * used in headers for non-inline functions, the "extern" is included here.
- */
-#define ext_pure extern __attribute__((pure))
-
/* for helper functions defined inside macros */
#define macro_inline static inline __attribute__((unused))
#define macro_pure static inline __attribute__((unused, pure))
#define PRIx64 "Lx"
#else /* !_FRR_ATTRIBUTE_PRINTFRR */
+#ifdef __NetBSD__
+#define PRINTFRR(a, b) __attribute__((format(gnu_syslog, a, b)))
+#else
#define PRINTFRR(a, b) __attribute__((format(printf, a, b)))
+#endif
/* frr-format plugin is C-only for now, so no point in doing these shenanigans
* for C++... (also they can break some C++ stuff...)
char oldpath[XPATH_MAXLEN];
char newpath[XPATH_MAXLEN];
- if_dnode = yang_dnode_getf(
- running_config->dnode,
- "/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf",
- ifp->name, old_vrf->name);
+ snprintf(oldpath, sizeof(oldpath),
+ "/frr-interface:lib/interface[name='%s'][vrf='%s']",
+ ifp->name, old_vrf->name);
+ snprintf(newpath, sizeof(newpath),
+ "/frr-interface:lib/interface[name='%s'][vrf='%s']",
+ ifp->name, vrf->name);
+
+ if_dnode = yang_dnode_getf(running_config->dnode, "%s/vrf",
+ oldpath);
if (if_dnode) {
- yang_dnode_get_path(lyd_parent(if_dnode), oldpath,
- sizeof(oldpath));
yang_dnode_change_leaf(if_dnode, vrf->name);
- yang_dnode_get_path(lyd_parent(if_dnode), newpath,
- sizeof(newpath));
nb_running_move_tree(oldpath, newpath);
running_config->version++;
}
+
+ vty_update_xpath(oldpath, newpath);
}
}
return;
XFREE(MTYPE_LS_DB, node);
- node = NULL;
}
int ls_node_same(struct ls_node *n1, struct ls_node *n2)
ls_attributes_srlg_del(attr);
XFREE(MTYPE_LS_DB, attr);
- attr = NULL;
}
int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2)
return;
XFREE(MTYPE_LS_DB, pref);
- pref = NULL;
}
int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2)
subnets_fini(&ted->subnets);
XFREE(MTYPE_LS_DB, ted);
- ted = NULL;
}
void ls_ted_del_all(struct ls_ted *ted)
return len;
}
-static int search_buf(const char *buf)
+static int search_buf(const char *buf, size_t len)
{
char *found = NULL;
frr_with_mutex(&logfilterlock) {
for (int i = 0; i < zlog_filter_count; i++) {
- found = strstr(buf, zlog_filters[i]);
+ found = memmem(buf, len, zlog_filters[i],
+ strlen(zlog_filters[i]));
if (found != NULL)
return 0;
}
{
struct zlog_msg *msgfilt[nmsgs];
size_t i, o = 0;
+ const char *text;
+ size_t text_len;
for (i = 0; i < nmsgs; i++) {
- if (zlog_msg_prio(msgs[i]) >= LOG_DEBUG
- && search_buf(zlog_msg_text(msgs[i], NULL)) < 0)
- continue;
-
+ if (zlog_msg_prio(msgs[i]) >= LOG_DEBUG) {
+ text = zlog_msg_text(msgs[i], &text_len);
+ if (search_buf(text, text_len) < 0)
+ continue;
+ }
msgfilt[o++] = msgs[i];
}
#define ZLOG_MAXLVL(a, b) MAX(a, b)
DEFINE_HOOK(zlog_rotate, (), ());
+DEFINE_HOOK(zlog_cli_show, (struct vty * vty), (vty));
static const int log_default_lvl = LOG_DEBUG;
},
};
-static const char *zlog_progname;
+const char *zlog_progname;
static const char *zlog_protoname;
static const struct facility_map {
"notifications", "informational", "debugging", NULL,
};
-static const char *facility_name(int facility)
+const char *zlog_priority_str(int priority)
+{
+ if (priority > LOG_DEBUG)
+ return "???";
+ return zlog_priority[priority];
+}
+
+const char *facility_name(int facility)
{
const struct facility_map *fm;
return "";
}
-static int facility_match(const char *str)
+int facility_match(const char *str)
{
const struct facility_map *fm;
vty_out(vty, "Record priority: %s\n",
(zt_file.record_priority ? "enabled" : "disabled"));
vty_out(vty, "Timestamp precision: %d\n", zt_file.ts_subsec);
+
+ hook_call(zlog_cli_show, vty);
return CMD_SUCCESS;
}
DEFPY (log_filter,
log_filter_cmd,
- "[no] log-filter WORD$filter",
+ "[no] log filter-text WORD$filter",
NO_STR
+ "Logging control\n"
FILTER_LOG_STR
"String to filter by\n")
{
/* Clear all log filters */
DEFPY (log_filter_clear,
log_filter_clear_cmd,
- "clear log-filter",
+ "clear log filter-text",
CLEAR_STR
+ "Logging control\n"
FILTER_LOG_STR)
{
zlog_filter_clear();
/* Show log filter */
DEFPY (show_log_filter,
show_log_filter_cmd,
- "show log-filter",
+ "show logging filter-text",
SHOW_STR
+ "Show current logging configuration\n"
FILTER_LOG_STR)
{
char log_filters[ZLOG_FILTERS_MAX * (ZLOG_FILTER_LENGTH_MAX + 3)] = "";
extern int log_level_match(const char *s);
extern void log_show_syslog(struct vty *vty);
+extern int facility_match(const char *str);
+extern const char *facility_name(int facility);
+
DECLARE_HOOK(zlog_rotate, (), ());
extern void zlog_rotate(void);
+DECLARE_HOOK(zlog_cli_show, (struct vty * vty), (vty));
+
#ifdef __cplusplus
}
#endif
DEFINE_MGROUP(LIB, "libfrr");
DEFINE_MTYPE(LIB, TMP, "Temporary memory");
+DEFINE_MTYPE(LIB, BITFIELD, "Bitfield memory");
static inline void mt_count_alloc(struct memtype *mt, size_t size, void *ptr)
{
NULL, LYD_NEW_PATH_UPDATE,
&dep_dnode);
/* Create default nodes */
- if (!err)
+ if (!err && dep_dnode)
err = lyd_new_implicit_tree(
dep_dnode,
LYD_IMPLICIT_NO_STATE, NULL);
if (err) {
flog_warn(
EC_LIB_LIBYANG,
- "%s: lyd_new_path(%s) failed: %d",
+ "%s: dependency: lyd_new_path(%s) failed: %d",
__func__, dep_xpath, err);
return NB_ERR;
}
frr::NAME##Response>( \
(cdb), &frr::Northbound::AsyncService::Request##NAME, \
&HandleUnary##NAME, #NAME); \
- _rpcState->do_request(service, _cq); \
+ _rpcState->do_request(service, s_cq); \
} while (0)
#define REQUEST_NEWRPC_STREAMING(NAME, cdb) \
frr::NAME##Response>( \
(cdb), &frr::Northbound::AsyncService::Request##NAME, \
&HandleStreaming##NAME, #NAME); \
- _rpcState->do_request(service, _cq); \
+ _rpcState->do_request(service, s_cq); \
} while (0)
struct grpc_pthread_attr {
unsigned long port;
};
+// Capture these objects so we can try to shut down cleanly
+static std::unique_ptr<grpc::Server> s_server;
+static grpc::ServerCompletionQueue *s_cq;
+
static void *grpc_pthread_start(void *arg)
{
struct frr_pthread *fpt = static_cast<frr_pthread *>(arg);
std::stringstream server_address;
frr::Northbound::AsyncService *service =
new frr::Northbound::AsyncService();
- grpc::ServerCompletionQueue *_cq;
frr_pthread_set_name(fpt);
grpc::InsecureServerCredentials());
builder.RegisterService(service);
auto cq = builder.AddCompletionQueue();
- _cq = cq.get();
- auto server = builder.BuildAndStart();
+ s_cq = cq.get();
+ s_server = builder.BuildAndStart();
/* Schedule all RPC handlers */
REQUEST_NEWRPC(GetCapabilities, NULL);
void *tag;
bool ok;
- _cq->Next(&tag, &ok);
+ s_cq->Next(&tag, &ok);
+ if (!ok)
+ break;
+
grpc_debug("%s: Got next from CompletionQueue, %p %d", __func__,
tag, ok);
- GPR_ASSERT(ok);
RpcStateBase *rpc = static_cast<RpcStateBase *>(tag);
CallState state = rpc->doCallback();
* user indicating Finish() for cleanup.
*/
if (state == FINISH)
- rpc->do_request(service, _cq);
+ rpc->do_request(service, s_cq);
}
- /*NOTREACHED*/
return NULL;
}
__func__, safe_strerror(errno));
return -1;
}
- pthread_detach(fpt->thread);
return 0;
}
static int frr_grpc_finish(void)
{
- if (fpt)
+ // Shutdown the grpc server
+ if (s_server) {
+ s_server->Shutdown();
+ s_cq->Shutdown();
+
+ // And drain the queue
+ void *ignore;
+ bool ok;
+
+ while (s_cq->Next(&ignore, &ok))
+ ;
+ }
+
+ if (fpt) {
+ pthread_join(fpt->thread, NULL);
frr_pthread_destroy(fpt);
- // TODO: cancel the gRPC pthreads gracefully.
+ }
+
return 0;
}
(strmatch(A, "frr-bgp-route-map:ipv4-vpn-address"))
#define IS_SET_BGP_IPV4_NH(A) \
(strmatch(A, "frr-bgp-route-map:set-ipv4-nexthop"))
+#define IS_SET_BGP_EVPN_GATEWAY_IP_IPV4(A) \
+ (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv4"))
+#define IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(A) \
+ (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv6"))
/* Prototypes. */
extern void route_map_init(void);
yang_dnode_get_string(
dnode,
"./rmap-set-action/frr-bgp-route-map:ipv4-nexthop"));
+ } else if (IS_SET_BGP_EVPN_GATEWAY_IP_IPV4(action)) {
+ vty_out(vty, " set evpn gateway-ip ipv4 %s\n",
+ yang_dnode_get_string(
+ dnode,
+ "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4"));
+ } else if (IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(action)) {
+ vty_out(vty, " set evpn gateway-ip ipv6 %s\n",
+ yang_dnode_get_string(
+ dnode,
+ "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6"));
}
}
#ifndef _FRR_ROUTING_NB_H_
#define _FRR_ROUTING_NB_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern const struct frr_yang_module_info frr_routing_info;
/* Mandatory callbacks. */
void routing_control_plane_protocols_register_vrf_dependency(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _FRR_ROUTING_NB_H_ */
locator = XCALLOC(MTYPE_SRV6_LOCATOR, sizeof(struct srv6_locator));
strlcpy(locator->name, name, sizeof(locator->name));
locator->chunks = list_new();
+ locator->chunks->del = (void (*)(void *))srv6_locator_chunk_free;
+
QOBJ_REG(locator, srv6_locator);
return locator;
}
void srv6_locator_free(struct srv6_locator *locator)
{
- XFREE(MTYPE_SRV6_LOCATOR, locator);
+ if (locator) {
+ QOBJ_UNREG(locator);
+ list_delete(&locator->chunks);
+
+ XFREE(MTYPE_SRV6_LOCATOR, locator);
+ }
}
void srv6_locator_chunk_free(struct srv6_locator_chunk *chunk)
table->info = d;
}
-/* ext_pure => extern __attribute__((pure))
- * does not modify memory (but depends on mem), allows compiler to optimize
- */
-
extern void route_table_finish(struct route_table *table);
-ext_pure struct route_node *route_top(struct route_table *table);
-ext_pure struct route_node *route_next(struct route_node *node);
-ext_pure struct route_node *route_next_until(struct route_node *node,
- const struct route_node *limit);
+extern struct route_node *route_top(struct route_table *table);
+extern struct route_node *route_next(struct route_node *node);
+extern struct route_node *route_next_until(struct route_node *node,
+ const struct route_node *limit);
extern struct route_node *route_node_get(struct route_table *table,
union prefixconstptr pu);
-ext_pure struct route_node *route_node_lookup(struct route_table *table,
- union prefixconstptr pu);
-ext_pure struct route_node *route_node_lookup_maynull(struct route_table *table,
- union prefixconstptr pu);
-ext_pure struct route_node *route_node_match(struct route_table *table,
- union prefixconstptr pu);
-ext_pure struct route_node *route_node_match_ipv4(struct route_table *table,
- const struct in_addr *addr);
-ext_pure struct route_node *route_node_match_ipv6(struct route_table *table,
- const struct in6_addr *addr);
-
-ext_pure unsigned long route_table_count(struct route_table *table);
+extern struct route_node *route_node_lookup(struct route_table *table,
+ union prefixconstptr pu);
+extern struct route_node *route_node_lookup_maynull(struct route_table *table,
+ union prefixconstptr pu);
+extern struct route_node *route_node_match(struct route_table *table,
+ union prefixconstptr pu);
+extern struct route_node *route_node_match_ipv4(struct route_table *table,
+ const struct in_addr *addr);
+extern struct route_node *route_node_match_ipv6(struct route_table *table,
+ const struct in6_addr *addr);
+
+extern unsigned long route_table_count(struct route_table *table);
extern struct route_node *route_node_create(route_table_delegate_t *delegate,
struct route_table *table);
struct route_table *table,
struct route_node *node);
-ext_pure struct route_node *route_table_get_next(struct route_table *table,
- union prefixconstptr pu);
-ext_pure int route_table_prefix_iter_cmp(const struct prefix *p1,
- const struct prefix *p2);
+extern struct route_node *route_table_get_next(struct route_table *table,
+ union prefixconstptr pu);
+extern int route_table_prefix_iter_cmp(const struct prefix *p1,
+ const struct prefix *p2);
/*
* Iterator functions.
cmd_variable_handler_register(vrf_var_handlers);
}
+static void vrf_terminate_single(struct vrf *vrf)
+{
+ /* Clear configured flag and invoke delete. */
+ UNSET_FLAG(vrf->status, VRF_CONFIGURED);
+ vrf_delete(vrf);
+}
+
/* Terminate VRF module. */
void vrf_terminate(void)
{
- struct vrf *vrf;
+ struct vrf *vrf, *tmp;
if (debug_vrf)
zlog_debug("%s: Shutting down vrf subsystem", __func__);
- while (!RB_EMPTY(vrf_id_head, &vrfs_by_id)) {
- vrf = RB_ROOT(vrf_id_head, &vrfs_by_id);
+ RB_FOREACH_SAFE (vrf, vrf_id_head, &vrfs_by_id, tmp) {
+ if (vrf->vrf_id == VRF_DEFAULT)
+ continue;
- /* Clear configured flag and invoke delete. */
- UNSET_FLAG(vrf->status, VRF_CONFIGURED);
- vrf_delete(vrf);
+ vrf_terminate_single(vrf);
}
- while (!RB_EMPTY(vrf_name_head, &vrfs_by_name)) {
- vrf = RB_ROOT(vrf_name_head, &vrfs_by_name);
+ RB_FOREACH_SAFE (vrf, vrf_name_head, &vrfs_by_name, tmp) {
+ if (vrf->vrf_id == VRF_DEFAULT)
+ continue;
- /* Clear configured flag and invoke delete. */
- UNSET_FLAG(vrf->status, VRF_CONFIGURED);
- vrf_delete(vrf);
+ vrf_terminate_single(vrf);
}
+
+ /* Finally terminate default VRF */
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ vrf_terminate_single(vrf);
}
int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
return CMD_WARNING_CONFIG_FAILED;
}
+ if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) {
+ /*
+ * Remove the VRF interface config. Currently, we allow to
+ * remove only inactive VRFs, so we use VRF_DEFAULT_NAME here,
+ * because when the VRF is removed from kernel, the interface
+ * is moved to the default VRF. If we ever allow removing
+ * active VRFs, this code have to be updated accordingly.
+ */
+ snprintf(xpath_list, sizeof(xpath_list),
+ "/frr-interface:lib/interface[name='%s'][vrf='%s']",
+ vrfname, VRF_DEFAULT_NAME);
+ nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL);
+ }
+
snprintf(xpath_list, sizeof(xpath_list), FRR_VRF_KEY_XPATH, vrfname);
nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL);
- return nb_cli_apply_changes(vty, xpath_list);
+ return nb_cli_apply_changes(vty, NULL);
}
/* Vector which store each vty structure. */
static vector vtyvec;
+/* Vector for vtysh connections. */
+static vector vtyshvec;
+
/* Vty timeout value. */
static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
vty->wfd = sock;
vty->type = VTY_SHELL_SERV;
vty->node = VIEW_NODE;
+ vector_set_index(vtyshvec, sock, vty);
vty_event(VTYSH_READ, vty);
}
/* Unset vector. */
- if (vty->fd != -1)
- vector_unset(vtyvec, vty->fd);
+ if (vty->fd != -1) {
+ if (vty->type == VTY_SHELL_SERV)
+ vector_unset(vtyshvec, vty->fd);
+ else
+ vector_unset(vtyvec, vty->fd);
+ }
if (vty->wfd > 0 && vty->type == VTY_FILE)
fsync(vty->wfd);
}
}
+static void update_xpath(struct vty *vty, const char *oldpath,
+ const char *newpath)
+{
+ int i;
+
+ for (i = 0; i < vty->xpath_index; i++) {
+ if (!frrstr_startswith(vty->xpath[i], oldpath))
+ break;
+
+ char *tmp = frrstr_replace(vty->xpath[i], oldpath, newpath);
+ strlcpy(vty->xpath[i], tmp, sizeof(vty->xpath[0]));
+ XFREE(MTYPE_TMP, tmp);
+ }
+}
+
+void vty_update_xpath(const char *oldpath, const char *newpath)
+{
+ struct vty *vty;
+ unsigned int i;
+
+ for (i = 0; i < vector_active(vtyshvec); i++) {
+ if ((vty = vector_slot(vtyshvec, i)) == NULL)
+ continue;
+
+ update_xpath(vty, oldpath, newpath);
+ }
+
+ for (i = 0; i < vector_active(vtyvec); i++) {
+ if ((vty = vector_slot(vtyvec, i)) == NULL)
+ continue;
+
+ update_xpath(vty, oldpath, newpath);
+ }
+}
+
int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
{
if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) {
vty_save_cwd();
vtyvec = vector_init(VECTOR_MIN_SIZE);
+ vtyshvec = vector_init(VECTOR_MIN_SIZE);
vty_master = master_thread;
vtyvec = NULL;
Vvty_serv_thread = NULL;
}
+ if (vtyshvec) {
+ vector_free(vtyshvec);
+ vtyshvec = NULL;
+ }
}
extern char *vty_get_cwd(void);
extern void vty_log(const char *level, const char *proto, const char *msg,
struct timestamp_control *);
+extern void vty_update_xpath(const char *oldpath, const char *newpath);
extern int vty_config_enter(struct vty *vty, bool private_config,
bool exclusive);
extern void vty_config_exit(struct vty *);
length -= ZEBRA_HEADER_SIZE;
if (zclient_debug)
- zlog_debug("zclient 0x%p command %s VRF %u",
- (void *)zclient, zserv_command_string(command),
- vrf_id);
+ zlog_debug("zclient %p command %s VRF %u", zclient,
+ zserv_command_string(command), vrf_id);
switch (command) {
case ZEBRA_CAPABILITIES:
#define static_cast(l, r) (r)
#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifndef HAVE_STRLCAT
size_t strlcat(char *__restrict dest,
const char *__restrict src, size_t destsize);
#define ROUTE_TAG_MAX UINT32_MAX
#define ROUTE_TAG_PRI PRIu32
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _ZEBRA_H */
char zlog_prefix[128];
size_t zlog_prefixsz;
int zlog_tmpdirfd = -1;
+int zlog_instance = -1;
static atomic_bool zlog_ec = true, zlog_xid = true;
size_t stackbufsz;
char *text;
size_t textlen;
+ size_t hdrlen;
/* This is always ISO8601 with sub-second precision 9 here, it's
* converted for callers as needed. ts_dot points to the "."
* Valid if ZLOG_TS_ISO8601 is set.
* (0 if timestamp has not been formatted yet)
*/
- uint32_t ts_flags;
char ts_str[32], *ts_dot, ts_zonetail[8];
+ uint32_t ts_flags;
+
+ /* "mmm dd hh:mm:ss" for 3164 legacy syslog - too dissimilar from
+ * the above, so just kept separately here.
+ */
+ uint32_t ts_3164_flags;
+ char ts_3164_str[16];
+
+ /* at the time of writing, 16 args was the actual maximum of arguments
+ * to a single zlog call. Particularly printing flag bitmasks seems
+ * to drive this. That said, the overhead of dynamically sizing this
+ * probably outweighs the value. If anything, a printfrr extension
+ * for printing flag bitmasks might be a good idea.
+ */
+ struct fmt_outpos argpos[24];
+ size_t n_argpos;
};
/* thread-local log message buffering
#endif
#ifdef CAN_DO_TLS
-static long zlog_gettid(void)
+static intmax_t zlog_gettid(void)
{
+#ifndef __OpenBSD__
+ /* accessing a TLS variable is much faster than a syscall */
+ static thread_local intmax_t cached_tid = -1;
+ if (cached_tid != -1)
+ return cached_tid;
+#endif
+
long rv = -1;
#ifdef HAVE_PTHREAD_GETTHREADID_NP
rv = pthread_getthreadid_np();
rv = mach_thread_self();
mach_port_deallocate(mach_task_self(), rv);
#endif
+
+#ifndef __OpenBSD__
+ cached_tid = rv;
+#endif
return rv;
}
for (i = 0; i < array_size(zlog_tls->msgp); i++)
zlog_tls->msgp[i] = &zlog_tls->msgs[i];
- snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid());
+ snprintfrr(mmpath, sizeof(mmpath), "logbuf.%jd", zlog_gettid());
mmfd = openat(zlog_tmpdirfd, mmpath,
O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
zlog_tls_free(zlog_tls);
zlog_tls_set(NULL);
- snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid());
+ snprintfrr(mmpath, sizeof(mmpath), "logbuf.%jd", zlog_gettid());
if (do_unlink && unlinkat(zlog_tmpdirfd, mmpath, 0))
zlog_err("unlink logbuf: %s (%d)", strerror(errno), errno);
}
}
#endif
+void zlog_msg_pid(struct zlog_msg *msg, intmax_t *pid, intmax_t *tid)
+{
+#ifndef __OpenBSD__
+ static thread_local intmax_t cached_pid = -1;
+ if (cached_pid != -1)
+ *pid = cached_pid;
+ else
+ cached_pid = *pid = (intmax_t)getpid();
+#else
+ *pid = (intmax_t)getpid();
+#endif
+#ifdef CAN_DO_TLS
+ *tid = zlog_gettid();
+#else
+ *tid = *pid;
+#endif
+}
+
static inline void zlog_tls_free(void *arg)
{
struct zlog_tls *zlog_tls = arg;
if (need)
need += bputch(&fb, ' ');
- hdrlen = need;
+ msg->hdrlen = hdrlen = need;
assert(hdrlen < msg->stackbufsz);
+ fb.outpos = msg->argpos;
+ fb.outpos_n = array_size(msg->argpos);
+ fb.outpos_i = 0;
+
va_copy(args, msg->args);
need += vbprintfrr(&fb, msg->fmt, args);
va_end(args);
msg->textlen = need;
- need += bputch(&fb, '\0');
+ need += bputch(&fb, '\n');
if (need <= msg->stackbufsz)
msg->text = msg->stackbuf;
fb.buf = msg->text;
fb.len = need;
fb.pos = msg->text + hdrlen;
+ fb.outpos_i = 0;
va_copy(args, msg->args);
vbprintfrr(&fb, msg->fmt, args);
va_end(args);
- bputch(&fb, '\0');
+ bputch(&fb, '\n');
}
+
+ msg->n_argpos = fb.outpos_i;
}
if (textlen)
*textlen = msg->textlen;
return msg->text;
}
+void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen, size_t *n_argpos,
+ const struct fmt_outpos **argpos)
+{
+ if (!msg->text)
+ zlog_msg_text(msg, NULL);
+
+ if (hdrlen)
+ *hdrlen = msg->hdrlen;
+ if (n_argpos)
+ *n_argpos = msg->n_argpos;
+ if (argpos)
+ *argpos = msg->argpos;
+}
+
#define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
#define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
-size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz,
- uint32_t flags)
+size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out, uint32_t flags)
{
+ size_t outsz = out ? (out->buf + out->len - out->pos) : 0;
size_t len1;
if (!(flags & ZLOG_TS_FORMAT))
len1 = strlen(msg->ts_str);
if (flags & ZLOG_TS_LEGACY) {
- if (len1 + 1 > outsz)
- return 0;
+ if (!out)
+ return len1;
+
+ if (len1 > outsz) {
+ memset(out->pos, 0, outsz);
+ out->pos += outsz;
+ return len1;
+ }
/* just swap out the formatting, faster than redoing it */
for (char *p = msg->ts_str; p < msg->ts_str + len1; p++) {
switch (*p) {
case '-':
- *out++ = '/';
+ *out->pos++ = '/';
break;
case 'T':
- *out++ = ' ';
+ *out->pos++ = ' ';
break;
default:
- *out++ = *p;
+ *out->pos++ = *p;
}
}
- *out = '\0';
return len1;
} else {
size_t len2 = strlen(msg->ts_zonetail);
- if (len1 + len2 + 1 > outsz)
- return 0;
- memcpy(out, msg->ts_str, len1);
- memcpy(out + len1, msg->ts_zonetail, len2);
- out[len1 + len2] = '\0';
+ if (!out)
+ return len1 + len2;
+
+ if (len1 + len2 > outsz) {
+ memset(out->pos, 0, outsz);
+ out->pos += outsz;
+ return len1 + len2;
+ }
+
+ memcpy(out->pos, msg->ts_str, len1);
+ out->pos += len1;
+ memcpy(out->pos, msg->ts_zonetail, len2);
+ out->pos += len2;
return len1 + len2;
}
}
+size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out, uint32_t flags)
+{
+ flags &= ZLOG_TS_UTC;
+
+ if (!msg->ts_3164_str[0] || flags != msg->ts_3164_flags) {
+ /* these are "hardcoded" in RFC3164, so they're here too... */
+ static const char *const months[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ };
+ struct tm tm;
+
+ /* RFC3164 explicitly asks for local time, but common usage
+ * also includes UTC.
+ */
+ if (flags & ZLOG_TS_UTC)
+ gmtime_r(&msg->ts.tv_sec, &tm);
+ else
+ localtime_r(&msg->ts.tv_sec, &tm);
+
+ snprintfrr(msg->ts_3164_str, sizeof(msg->ts_3164_str),
+ "%3s %2d %02d:%02d:%02d", months[tm.tm_mon],
+ tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ msg->ts_3164_flags = flags;
+ }
+ return bputs(out, msg->ts_3164_str);
+}
+
void zlog_set_prefix_ec(bool enable)
{
atomic_store_explicit(&zlog_ec, enable, memory_order_relaxed);
{
zlog_uid = uid;
zlog_gid = gid;
+ zlog_instance = instance;
if (instance) {
snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir),
#include "frrcu.h"
#include "memory.h"
#include "hook.h"
+#include "printfrr.h"
#ifdef __cplusplus
extern "C" {
#endif
+DECLARE_MGROUP(LOG);
+
extern char zlog_prefix[];
extern size_t zlog_prefixsz;
extern int zlog_tmpdirfd;
+extern int zlog_instance;
+extern const char *zlog_progname;
struct xref_logmsg {
struct xref xref;
extern int zlog_msg_prio(struct zlog_msg *msg);
extern const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg);
-/* pass NULL as textlen if you don't need it. */
+/* text is NOT \0 terminated; instead there is a \n after textlen since the
+ * logging targets would jump extra hoops otherwise for a single byte. (the
+ * \n is not included in textlen)
+ *
+ * calling this with NULL textlen is likely wrong.
+ * use "%.*s", (int)textlen, text when passing to printf-like functions
+ */
extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen);
+extern void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen,
+ size_t *n_argpos, const struct fmt_outpos **argpos);
+
/* timestamp formatting control flags */
/* sub-second digit count */
/* default is local time zone */
#define ZLOG_TS_UTC (1 << 10)
-extern size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz,
+extern size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out,
uint32_t flags);
+/* "mmm dd hh:mm:ss" for RFC3164 syslog. Only ZLOG_TS_UTC for flags. */
+extern size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out,
+ uint32_t flags);
+
+/* currently just returns the current PID/TID since we never write another
+ * thread's messages
+ */
+extern void zlog_msg_pid(struct zlog_msg *msg, intmax_t *pid, intmax_t *tid);
+
/* This list & struct implements the actual logging targets. It is accessed
* lock-free from all threads, and thus MUST only be changed atomically, i.e.
* RCU.
/* Enable or disable 'immediate' output - default is to buffer messages. */
extern void zlog_set_immediate(bool set_p);
+extern const char *zlog_priority_str(int priority);
+
#ifdef __cplusplus
}
#endif
* absolute end.
*/
-DECLARE_MGROUP(LOG);
DEFINE_MGROUP_ACTIVEATEXIT(LOG, "logging subsystem");
DEFINE_MTYPE_STATIC(LOG, LOG_FD, "log file target");
int prio = zlog_msg_prio(msg);
if (prio <= zt->prio_min) {
+ struct fbuf fbuf = {
+ .buf = ts_buf,
+ .pos = ts_pos,
+ .len = sizeof(ts_buf),
+ };
+
iov[iovpos].iov_base = ts_pos;
- if (iovpos > 0)
- *ts_pos++ = '\n';
- ts_pos += zlog_msg_ts(msg, ts_pos,
- sizeof(ts_buf) - 1
- - (ts_pos - ts_buf),
- ZLOG_TS_LEGACY | zte->ts_subsec);
+ zlog_msg_ts(msg, &fbuf,
+ ZLOG_TS_LEGACY | zte->ts_subsec);
+ ts_pos = fbuf.pos;
+
*ts_pos++ = ' ';
iov[iovpos].iov_len =
ts_pos - (char *)iov[iovpos].iov_base;
iov[iovpos].iov_base =
(char *)zlog_msg_text(msg, &textlen);
- iov[iovpos].iov_len = textlen;
+ iov[iovpos].iov_len = textlen + 1;
iovpos++;
}
if (iovpos > 0 && (ts_buf + sizeof(ts_buf) - ts_pos < TS_LEN
|| i + 1 == nmsgs
|| array_size(iov) - iovpos < 5)) {
- iov[iovpos].iov_base = (char *)"\n";
- iov[iovpos].iov_len = 1;
-
- iovpos++;
-
writev(fd, iov, iovpos);
iovpos = 0;
{
size_t i;
struct zlt_syslog *zte = container_of(zt, struct zlt_syslog, zt);
+ const char *text;
+ size_t text_len;
for (i = 0; i < nmsgs; i++) {
if (zlog_msg_prio(msgs[i]) > zt->prio_min)
continue;
- syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%s",
- zlog_msg_text(msgs[i], NULL));
+ text = zlog_msg_text(msgs[i], &text_len);
+ syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%.*s",
+ (int)text_len, text);
}
}
static const struct frr_yang_module_info *const nhrpd_yang_modules[] = {
&frr_filter_info,
&frr_interface_info,
+ &frr_vrf_info,
};
FRR_DAEMON_INFO(nhrpd, NHRP, .vty_port = NHRP_VTY_PORT,
install_element(CONFIG_NODE, &nhrp_multicast_nflog_group_cmd);
install_element(CONFIG_NODE, &no_nhrp_multicast_nflog_group_cmd);
+ vrf_cmd_init(NULL, &nhrpd_privs);
+
/* interface specific commands */
install_node(&nhrp_interface_node);
#include "ospf6_asbr.h"
#include "ospf6_abr.h"
#include "ospf6d.h"
+#include "ospf6_nssa.h"
unsigned char conf_debug_ospf6_abr;
-int ospf6_is_router_abr(struct ospf6 *o)
+bool ospf6_check_and_set_router_abr(struct ospf6 *o)
{
struct listnode *node;
struct ospf6_area *oa;
int area_count = 0;
+ bool is_backbone = false;
- for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa))
+ for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s, area_id %pI4", __func__, &oa->area_id);
if (IS_AREA_ENABLED(oa))
area_count++;
- if (area_count > 1)
- return 1;
- return 0;
+ if (o->backbone == oa)
+ is_backbone = true;
+ }
+
+ if ((area_count > 1) && (is_backbone)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : set flag OSPF6_FLAG_ABR", __func__);
+ SET_FLAG(o->flag, OSPF6_FLAG_ABR);
+ return true;
+ } else {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : reset flag OSPF6_FLAG_ABR", __func__);
+ UNSET_FLAG(o->flag, OSPF6_FLAG_ABR);
+ return false;
+ }
}
static int ospf6_abr_nexthops_belong_to_area(struct ospf6_route *route,
uint16_t type;
int is_debug = 0;
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : start area %s, route %pFX", __func__,
+ area->name, &route->prefix);
+
+ if (route->type == OSPF6_DEST_TYPE_ROUTER)
+ summary_table = area->summary_router;
+ else
+ summary_table = area->summary_prefix;
+
+ summary = ospf6_route_lookup(&route->prefix, summary_table);
+ if (summary) {
+ old = ospf6_lsdb_lookup(summary->path.origin.type,
+ summary->path.origin.id,
+ area->ospf6->router_id, area->lsdb);
+ /* Reset the OSPF6_LSA_UNAPPROVED flag */
+ if (old)
+ UNSET_FLAG(old->flag, OSPF6_LSA_UNAPPROVED);
+ }
+
/* Only destination type network, range or ASBR are considered */
if (route->type != OSPF6_DEST_TYPE_NETWORK
&& route->type != OSPF6_DEST_TYPE_RANGE
&& ((route->type != OSPF6_DEST_TYPE_ROUTER)
|| !CHECK_FLAG(route->path.router_bits, OSPF6_ROUTER_BIT_E))) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: Route type %d flag 0x%x is none of network, range nor ASBR, ignore",
+ __func__, route->type, route->path.router_bits);
return 0;
}
/* AS External routes are never considered */
if (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1
|| route->path.type == OSPF6_PATH_TYPE_EXTERNAL2) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Path type is external, skip",
+ __func__);
return 0;
}
/* do not generate if the path's area is the same as target area */
if (route->path.area_id == area->area_id) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: The route is in the area itself, ignore",
+ __func__);
return 0;
}
/* do not generate if the nexthops belongs to the target area */
if (ospf6_abr_nexthops_belong_to_area(route, area)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: The route's nexthop is in the same area, ignore",
+ __func__);
return 0;
}
if (route->type == OSPF6_DEST_TYPE_ROUTER) {
if (ADV_ROUTER_IN_PREFIX(&route->prefix)
== area->ospf6->router_id) {
- zlog_debug(
- "%s: Skipping ASBR announcement for ABR (%pI4)",
- __func__,
- &ADV_ROUTER_IN_PREFIX(&route->prefix));
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: Skipping ASBR announcement for ABR (%pI4)",
+ __func__,
+ &ADV_ROUTER_IN_PREFIX(&route->prefix));
return 0;
}
}
if (IS_OSPF6_DEBUG_ABR
|| IS_OSPF6_DEBUG_ORIGINATE(INTER_ROUTER)) {
is_debug++;
- zlog_debug(
- "Originating summary in area %s for ASBR %pI4",
- area->name,
- &ADV_ROUTER_IN_PREFIX(&route->prefix));
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "Originating summary in area %s for ASBR %pI4",
+ area->name,
+ &ADV_ROUTER_IN_PREFIX(&route->prefix));
}
- summary_table = area->summary_router;
} else {
if (IS_OSPF6_DEBUG_ABR
|| IS_OSPF6_DEBUG_ORIGINATE(INTER_PREFIX))
zlog_debug(
"Originating summary in area %s for %pFX cost %u",
area->name, &route->prefix, route->path.cost);
- summary_table = area->summary_prefix;
}
- summary = ospf6_route_lookup(&route->prefix, summary_table);
- if (summary)
- old = ospf6_lsdb_lookup(summary->path.origin.type,
- summary->path.origin.id,
- area->ospf6->router_id, area->lsdb);
-
/* if this route has just removed, remove corresponding LSA */
if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)) {
if (is_debug)
/* create LSA */
lsa = ospf6_lsa_create(lsa_header);
+ /* Reset the unapproved flag */
+ UNSET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED);
+
/* Originate */
ospf6_lsa_originate_area(lsa, area);
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : finish area %s", __func__, area->name);
+
return 1;
}
return (redo_summary);
}
-static void ospf6_abr_range_update(struct ospf6_route *range,
- struct ospf6 *ospf6)
+void ospf6_abr_range_update(struct ospf6_route *range, struct ospf6 *ospf6)
{
uint32_t cost = 0;
struct listnode *node, *nnode;
/* update range's cost and active flag */
cost = ospf6_abr_range_compute_cost(range, ospf6);
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s: range %pFX, cost %d", __func__, &range->prefix,
+ cost);
+
/* Non-zero cost is a proxy for active longer prefixes in this range.
* If there are active routes covered by this range AND either the
* configured
*/
if (ospf6_abr_range_summary_needs_update(range, cost)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s: range %pFX update", __func__,
+ &range->prefix);
for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa))
summary_orig +=
ospf6_abr_originate_summary_to_area(range, oa);
struct ospf6_area *oa;
struct ospf6_route *range = NULL;
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s: route %pFX", __func__, &route->prefix);
if (route->type == OSPF6_DEST_TYPE_NETWORK) {
oa = ospf6_area_lookup(route->path.area_id, ospf6);
struct ospf6_route *route;
/* if not a ABR return success */
- if (!ospf6_is_router_abr(oa->ospf6))
+ if (!ospf6_check_and_set_router_abr(oa->ospf6))
return;
/* Redo summaries if required */
}
#define OSPF6_ABR_RANGE_CLEAR_COST(range) (range->path.cost = OSPF_AREA_RANGE_COST_UNSPEC)
+#define IS_OSPF6_ABR(o) ((o)->flag & OSPF6_FLAG_ABR)
-extern int ospf6_is_router_abr(struct ospf6 *o);
+extern bool ospf6_check_and_set_router_abr(struct ospf6 *o);
extern void ospf6_abr_enable_area(struct ospf6_area *oa);
extern void ospf6_abr_disable_area(struct ospf6_area *oa);
struct ospf6_route_table *table);
extern void ospf6_abr_init(void);
extern void ospf6_abr_reexport(struct ospf6_area *oa);
+extern void ospf6_abr_range_update(struct ospf6_route *range,
+ struct ospf6 *ospf6);
+extern void ospf6_abr_remove_unapproved_summaries(struct ospf6 *ospf6);
#endif /*OSPF6_ABR_H*/
#include "ospf6_asbr.h"
#include "ospf6d.h"
#include "lib/json.h"
+#include "ospf6_nssa.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name");
+int str2area_id(const char *str, uint32_t *area_id, int *area_id_fmt)
+{
+ char *ep;
+
+ *area_id = htonl(strtoul(str, &ep, 10));
+ if (*ep && inet_pton(AF_INET, str, area_id) != 1)
+ return -1;
+
+ *area_id_fmt =
+ !*ep ? OSPF6_AREA_FMT_DECIMAL : OSPF6_AREA_FMT_DOTTEDQUAD;
+
+ return 0;
+}
+
+void area_id2str(char *buf, int len, uint32_t area_id, int area_id_fmt)
+{
+ if (area_id_fmt == OSPF6_AREA_FMT_DECIMAL)
+ snprintf(buf, len, "%u", ntohl(area_id));
+ else
+ inet_ntop(AF_INET, &area_id, buf, len);
+}
+
int ospf6_area_cmp(void *va, void *vb)
{
struct ospf6_area *oa = (struct ospf6_area *)va;
static void ospf6_area_lsdb_hook_add(struct ospf6_lsa *lsa)
{
switch (ntohs(lsa->header->type)) {
+
case OSPF6_LSTYPE_ROUTER:
case OSPF6_LSTYPE_NETWORK:
if (IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) {
(struct ospf6_area *)lsa->lsdb->data);
break;
+ case OSPF6_LSTYPE_TYPE_7:
+ ospf6_asbr_lsa_add(lsa);
+ break;
+
default:
break;
}
ospf6_abr_examin_summary(lsa,
(struct ospf6_area *)lsa->lsdb->data);
break;
-
+ case OSPF6_LSTYPE_TYPE_7:
+ ospf6_asbr_lsa_remove(lsa, NULL);
+ break;
default:
break;
}
ospf6_route_add(range, oa->range_table);
}
- if (ospf6_is_router_abr(ospf6)) {
+ if (ospf6_check_and_set_router_abr(ospf6)) {
/* Redo summaries if required */
ospf6_abr_prefix_resummarize(ospf6);
}
return CMD_SUCCESS;
}
- if (ospf6_is_router_abr(oa->ospf6)) {
+ if (ospf6_check_and_set_router_abr(oa->ospf6)) {
/* Blow away the aggregated LSA and route */
SET_FLAG(range->flag, OSPF6_ROUTE_REMOVE);
else
vty_out(vty, " area %s stub\n", oa->name);
}
+ if (IS_AREA_NSSA(oa))
+ vty_out(vty, " area %s nssa\n", oa->name);
if (PREFIX_NAME_IN(oa))
vty_out(vty, " area %s filter-list prefix %s in\n",
oa->name, PREFIX_NAME_IN(oa));
return CMD_SUCCESS;
}
+DEFUN(ospf6_area_nssa, ospf6_area_nssa_cmd,
+ "area <A.B.C.D|(0-4294967295)> nssa",
+ "OSPF6 area parameters\n"
+ "OSPF6 area ID in IP address format\n"
+ "OSPF6 area ID as a decimal value\n"
+ "Configure OSPF6 area as nssa\n")
+{
+ int idx_ipv4_number = 1;
+ struct ospf6_area *area;
+
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+ OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6);
+
+ if (!ospf6_area_nssa_set(ospf6, area)) {
+ vty_out(vty,
+ "First deconfigure all virtual link through this area\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd,
+ "no area <A.B.C.D|(0-4294967295)> nssa",
+ NO_STR
+ "OSPF6 area parameters\n"
+ "OSPF6 area ID in IP address format\n"
+ "OSPF6 area ID as a decimal value\n"
+ "Configure OSPF6 area as nssa\n")
+{
+ int idx_ipv4_number = 2;
+ struct ospf6_area *area;
+
+ VTY_DECLVAR_CONTEXT(ospf6, ospf6);
+ OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6);
+
+ ospf6_area_nssa_unset(ospf6, area);
+
+ return CMD_SUCCESS;
+}
+
+
void ospf6_area_init(void)
{
install_element(VIEW_NODE, &show_ipv6_ospf6_spf_tree_cmd);
install_element(OSPF6_NODE, &area_filter_list_cmd);
install_element(OSPF6_NODE, &no_area_filter_list_cmd);
+
+ /* "area nssa" commands. */
+ install_element(OSPF6_NODE, &ospf6_area_nssa_cmd);
+ install_element(OSPF6_NODE, &no_ospf6_area_nssa_cmd);
}
void ospf6_area_interface_delete(struct ospf6_interface *oi)
/* Area-ID */
in_addr_t area_id;
+#define OSPF6_AREA_FMT_UNSET 0
#define OSPF6_AREA_FMT_DOTTEDQUAD 1
#define OSPF6_AREA_FMT_DECIMAL 2
/* Area-ID string */
uint32_t full_nbrs; /* Fully adjacent neighbors. */
uint8_t intra_prefix_originate; /* Force intra_prefix lsa originate */
+ uint8_t NSSATranslatorRole; /* NSSA configured role */
+#define OSPF6_NSSA_ROLE_NEVER 0
+#define OSPF6_NSSA_ROLE_CANDIDATE 1
+#define OSPF6_NSSA_ROLE_ALWAYS 2
+ uint8_t NSSATranslatorState; /* NSSA operational role */
+#define OSPF6_NSSA_TRANSLATE_DISABLED 0
+#define OSPF6_NSSA_TRANSLATE_ENABLED 1
};
+#define OSPF6_AREA_DEFAULT 0x00
#define OSPF6_AREA_ENABLE 0x01
#define OSPF6_AREA_ACTIVE 0x02
#define OSPF6_AREA_TRANSIT 0x04 /* TransitCapability */
#define OSPF6_AREA_STUB 0x08
+#define OSPF6_AREA_NSSA 0x10
#define IS_AREA_ENABLED(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ENABLE))
#define IS_AREA_ACTIVE(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ACTIVE))
#define IS_AREA_TRANSIT(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_TRANSIT))
#define IS_AREA_STUB(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_STUB))
+#define IS_AREA_NSSA(oa) (CHECK_FLAG((oa)->flag, OSPF6_AREA_NSSA))
#define OSPF6_CMD_AREA_GET(str, oa, ospf6) \
{ \
- char *ep; \
- uint32_t area_id = htonl(strtoul(str, &ep, 10)); \
- if (*ep && inet_pton(AF_INET, str, &area_id) != 1) { \
+ uint32_t area_id; \
+ int format, ret; \
+ ret = str2area_id(str, &area_id, &format); \
+ if (ret) { \
vty_out(vty, "Malformed Area-ID: %s\n", str); \
- return CMD_SUCCESS; \
+ return CMD_WARNING; \
} \
- int format = !*ep ? OSPF6_AREA_FMT_DECIMAL \
- : OSPF6_AREA_FMT_DOTTEDQUAD; \
oa = ospf6_area_lookup(area_id, ospf6); \
if (oa == NULL) \
oa = ospf6_area_create(area_id, ospf6, format); \
}
/* prototypes */
+extern int str2area_id(const char *str, uint32_t *area_id, int *area_id_fmt);
+extern void area_id2str(char *buf, int len, uint32_t area_id, int area_id_fmt);
+
extern int ospf6_area_cmp(void *va, void *vb);
extern struct ospf6_area *ospf6_area_create(uint32_t, struct ospf6 *, int);
#include "ospf6_intra.h"
#include "ospf6_flood.h"
#include "ospf6d.h"
+#include "ospf6_spf.h"
+#include "ospf6_nssa.h"
#include "lib/json.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DIST_ARGS, "OSPF6 Distribute arguments");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_REDISTRIBUTE, "OSPF6 Redistribute arguments");
-static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id);
+static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type);
static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
struct ospf6_redist *red, int type);
#define ZROUTE_NAME(x) zebra_route_string(x)
/* AS External LSA origination */
-static void ospf6_as_external_lsa_originate(struct ospf6_route *route,
- struct ospf6 *ospf6)
+void ospf6_as_external_lsa_originate(struct ospf6_route *route,
+ struct ospf6 *ospf6)
{
char buffer[OSPF6_MAX_LSASIZE];
struct ospf6_lsa_header *lsa_header;
if (oi->state == OSPF6_INTERFACE_DOWN)
return 0;
+ if (IS_AREA_NSSA(oi->area))
+ return 0;
type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
adv_router = oi->area->ospf6->router_id;
}
}
+/* Check if the forwarding address is local address */
+static int ospf6_ase_forward_address_check(struct ospf6 *ospf6,
+ struct in6_addr *fwd_addr)
+{
+ struct listnode *anode, *node, *cnode;
+ struct ospf6_interface *oi;
+ struct ospf6_area *oa;
+ struct interface *ifp;
+ struct connected *c;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, oa)) {
+ for (ALL_LIST_ELEMENTS_RO(oa->if_list, node, oi)) {
+ if (!if_is_operative(oi->interface)
+ || oi->type == OSPF_IFTYPE_VIRTUALLINK)
+ continue;
+
+ ifp = oi->interface;
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
+ if (IPV6_ADDR_SAME(&c->address->u.prefix6,
+ fwd_addr))
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
{
struct ospf6_as_external_lsa *external;
struct prefix asbr_id;
- struct ospf6_route *asbr_entry, *route, *old;
+ struct ospf6_route *asbr_entry, *route, *old = NULL;
struct ospf6_path *path;
struct ospf6 *ospf6;
+ int type;
+ struct ospf6_area *oa = NULL;
+ struct prefix fwd_addr;
+ ptrdiff_t offset;
+
+ type = ntohs(lsa->header->type);
+ oa = lsa->lsdb->data;
external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
lsa->header);
ospf6_linkstate_prefix(lsa->header->adv_router, htonl(0), &asbr_id);
asbr_entry = ospf6_route_lookup(&asbr_id, ospf6->brouter_table);
- if (asbr_entry == NULL
- || !CHECK_FLAG(asbr_entry->path.router_bits, OSPF6_ROUTER_BIT_E)) {
+ if (asbr_entry == NULL) {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
zlog_debug("ASBR entry not found: %pFX", &asbr_id);
return;
+ } else {
+ /* The router advertising external LSA can be ASBR or ABR */
+ if (!CHECK_FLAG(asbr_entry->path.router_bits,
+ OSPF6_ROUTER_BIT_E)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug(
+ "External bit reset ASBR route entry : %pFX",
+ &asbr_id);
+ return;
+ }
+ }
+
+ /* Check the forwarding address */
+ if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) {
+ offset = sizeof(*external)
+ + OSPF6_PREFIX_SPACE(external->prefix.prefix_length);
+ memset(&fwd_addr, 0, sizeof(struct prefix));
+ fwd_addr.family = AF_INET6;
+ fwd_addr.prefixlen = IPV6_MAX_PREFIXLEN;
+ memcpy(&fwd_addr.u.prefix6, (caddr_t)external + offset,
+ sizeof(struct in6_addr));
+
+ if (!IN6_IS_ADDR_UNSPECIFIED(&fwd_addr.u.prefix6)) {
+ if (!ospf6_ase_forward_address_check(
+ ospf6, &fwd_addr.u.prefix6)) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug(
+ "Fwd address %pFX is local address",
+ &fwd_addr);
+ return;
+ }
+
+ /* Find the forwarding entry */
+ asbr_entry = ospf6_route_lookup_bestmatch(
+ &fwd_addr, ospf6->route_table);
+ if (asbr_entry == NULL) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug(
+ "Fwd address not found: %pFX",
+ &fwd_addr);
+ return;
+ }
+ }
}
route = ospf6_route_create();
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
zlog_debug(
- "%s: AS-External %u route add %pFX cost %u(%u) nh %u",
- __func__,
+ "%s: %s %u route add %pFX cost %u(%u) nh %u", __func__,
+ (type == OSPF6_LSTYPE_AS_EXTERNAL) ? "AS-External"
+ : "NSSA",
(route->path.type == OSPF6_PATH_TYPE_EXTERNAL1) ? 1 : 2,
&route->prefix, route->path.cost, route->path.u.cost_e2,
listcount(route->nh_list));
- old = ospf6_route_lookup(&route->prefix, ospf6->route_table);
+ if (type == OSPF6_LSTYPE_AS_EXTERNAL)
+ old = ospf6_route_lookup(&route->prefix, ospf6->route_table);
+ else if (type == OSPF6_LSTYPE_TYPE_7)
+ old = ospf6_route_lookup(&route->prefix, oa->route_table);
if (!old) {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug("%s: Adding new route", __func__);
/* Add the new route to ospf6 instance route table. */
- ospf6_route_add(route, ospf6->route_table);
+ if (type == OSPF6_LSTYPE_AS_EXTERNAL)
+ ospf6_route_add(route, ospf6->route_table);
+ /* Add the route to the area route table */
+ else if (type == OSPF6_LSTYPE_TYPE_7) {
+ ospf6_route_add(route, oa->route_table);
+ }
} else {
/* RFC 2328 16.4 (6)
* ECMP: Keep new equal preference path in current
* route's path list, update zebra with new effective
* list along with addition of ECMP path.
*/
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ zlog_debug("%s : old route %pFX cost %u(%u) nh %u",
+ __func__, &route->prefix, route->path.cost,
+ route->path.u.cost_e2,
+ listcount(route->nh_list));
ospf6_asbr_update_route_ecmp_path(old, route, ospf6);
}
}
struct ospf6_route *route, *nroute, *route_to_del;
struct ospf6_area *oa = NULL;
struct ospf6 *ospf6;
+ int type;
+ bool debug = false;
external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
lsa->header);
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
- zlog_debug("Withdraw AS-External route for %s", lsa->name);
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) || (IS_OSPF6_DEBUG_NSSA))
+ debug = true;
ospf6 = ospf6_get_by_lsdb(lsa);
- if (ospf6_is_router_abr(ospf6))
- oa = ospf6->backbone;
- else
- oa = listnode_head(ospf6->area_list);
+ type = ntohs(lsa->header->type);
+
+ if (type == OSPF6_LSTYPE_TYPE_7) {
+ if (debug)
+ zlog_debug("%s: Withdraw Type 7 route for %s",
+ __func__, lsa->name);
+ oa = lsa->lsdb->data;
+ } else {
+ if (debug)
+ zlog_debug("%s: Withdraw AS-External route for %s",
+ __func__, lsa->name);
- if (oa == NULL)
+ if (ospf6_check_and_set_router_abr(ospf6))
+ oa = ospf6->backbone;
+ else
+ oa = listgetdata(listhead(ospf6->area_list));
+ }
+
+ if (oa == NULL) {
+ if (debug)
+ zlog_debug("%s: Invalid area", __func__);
return;
+ }
if (lsa->header->adv_router == oa->ospf6->router_id) {
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
+ if (debug)
zlog_debug("Ignore self-originated AS-External-LSA");
return;
}
prefix.prefixlen = external->prefix.prefix_length;
ospf6_prefix_in6_addr(&prefix.u.prefix6, external, &external->prefix);
- route = ospf6_route_lookup(&prefix, oa->ospf6->route_table);
+ if (type == OSPF6_LSTYPE_TYPE_7)
+ route = ospf6_route_lookup(&prefix, oa->route_table);
+ else
+ route = ospf6_route_lookup(&prefix, oa->ospf6->route_table);
+
if (route == NULL) {
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug)
zlog_debug("AS-External route %pFX not found", &prefix);
- }
-
ospf6_route_delete(route_to_del);
return;
}
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug)
zlog_debug(
"%s: Current route %pFX cost %u e2 %u, route to del cost %u e2 %u",
__func__, &prefix, route->path.cost, route->path.u.cost_e2,
route_to_del->path.cost, route_to_del->path.u.cost_e2);
- }
for (ospf6_route_lock(route);
route && ospf6_route_is_prefix(&prefix, route); route = nroute) {
continue;
}
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug) {
zlog_debug(
"%s: route %pFX path found with cost %u nh %u to remove.",
__func__, &prefix, route->path.cost,
o_path->nh_list);
}
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug) {
zlog_debug(
"%s: AS-External %u route %pFX update paths %u nh %u",
__func__,
h_path->origin.adv_router;
}
} else {
- ospf6_route_remove(
- route, oa->ospf6->route_table);
+ if (type == OSPF6_LSTYPE_TYPE_7)
+ ospf6_route_remove(
+ route, oa->route_table);
+ else
+ ospf6_route_remove(
+ route,
+ oa->ospf6->route_table);
}
}
continue;
|| (route->path.cost != route_to_del->path.cost)
|| (route->path.u.cost_e2
!= route_to_del->path.u.cost_e2))) {
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug) {
zlog_debug(
"%s: route %pFX to delete is not same, cost %u del cost %u. skip",
__func__, &prefix, route->path.cost,
|| (route->path.origin.id != lsa->header->id))
continue;
}
- if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ if (debug) {
zlog_debug(
"%s: AS-External %u route remove %pFX cost %u(%u) nh %u",
__func__,
&route->prefix, route->path.cost, route->path.u.cost_e2,
listcount(route->nh_list));
}
- ospf6_route_remove(route, oa->ospf6->route_table);
+ if (type == OSPF6_LSTYPE_TYPE_7)
+ ospf6_route_remove(route, oa->route_table);
+ else
+ ospf6_route_remove(route, oa->ospf6->route_table);
}
if (route != NULL)
ospf6_route_unlock(route);
&ospf6->t_distribute_update);
}
-static void ospf6_asbr_routemap_update(const char *mapname)
+void ospf6_asbr_routemap_update(const char *mapname)
{
int type;
struct listnode *node, *nnode;
type));
ospf6_asbr_redistribute_unset(ospf6, red, type);
ospf6_asbr_routemap_set(red, mapname);
- ospf6_asbr_redistribute_set(
- type, ospf6->vrf_id);
+ ospf6_asbr_redistribute_set(ospf6, type);
}
}
}
int ospf6_asbr_is_asbr(struct ospf6 *o)
{
- return o->external_table->count;
+ return (o->external_table->count || IS_OSPF6_ASBR(o));
}
struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type,
red = XCALLOC(MTYPE_OSPF6_REDISTRIBUTE, sizeof(struct ospf6_redist));
red->instance = instance;
+ red->dmetric.type = -1;
+ red->dmetric.value = -1;
ROUTEMAP_NAME(red) = NULL;
ROUTEMAP(red) = NULL;
}
}
-static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id)
+/*Set the status of the ospf instance to ASBR based on the status parameter,
+ * rechedule SPF calculation, originate router LSA*/
+void ospf6_asbr_status_update(struct ospf6 *ospf6, int status)
+{
+ struct listnode *lnode, *lnnode;
+ struct ospf6_area *oa;
+
+ zlog_info("ASBR[%s:Status:%d]: Update", ospf6->name, status);
+
+ if (status) {
+ if (IS_OSPF6_ASBR(ospf6)) {
+ zlog_info("ASBR[%s:Status:%d]: Already ASBR",
+ ospf6->name, status);
+ return;
+ }
+ SET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR);
+ } else {
+ if (!IS_OSPF6_ASBR(ospf6)) {
+ zlog_info("ASBR[%s:Status:%d]: Already non ASBR",
+ ospf6->name, status);
+ return;
+ }
+ UNSET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR);
+ }
+
+ /* Transition from/to status ASBR, schedule timer. */
+ ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE);
+
+ /* Reoriginate router LSA for all areas */
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, lnnode, oa))
+ OSPF6_ROUTER_LSA_SCHEDULE(oa);
+}
+
+static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type)
{
- ospf6_zebra_redistribute(type, vrf_id);
+ ospf6_zebra_redistribute(type, ospf6->vrf_id);
+
+ ospf6_asbr_status_update(ospf6, ++ospf6->redist_count);
}
static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
}
ospf6_asbr_routemap_unset(red);
+ ospf6_asbr_status_update(ospf6, --ospf6->redist_count);
}
/* When an area is unstubified, flood all the external LSAs in the area */
}
}
-/* Update ASBR status. */
-static void ospf6_asbr_status_update(struct ospf6 *ospf6, uint8_t status)
-{
- struct listnode *lnode, *lnnode;
- struct ospf6_area *oa;
-
- zlog_info("ASBR[%s:Status:%d]: Update", ospf6->name, status);
-
- if (status) {
- if (IS_OSPF6_ASBR(ospf6)) {
- zlog_info("ASBR[%s:Status:%d]: Already ASBR",
- ospf6->name, status);
- return;
- }
- SET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR);
- } else {
- if (!IS_OSPF6_ASBR(ospf6)) {
- zlog_info("ASBR[%s:Status:%d]: Already non ASBR",
- ospf6->name, status);
- return;
- }
- UNSET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR);
- }
-
- ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE);
- for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, lnnode, oa))
- OSPF6_ROUTER_LSA_SCHEDULE(oa);
-}
-
void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex,
struct prefix *prefix,
unsigned int nexthop_num,
struct ospf6 *ospf6)
{
route_map_result_t ret;
+ struct listnode *lnode;
+ struct ospf6_area *oa;
struct ospf6_route troute;
struct ospf6_external_info tinfo;
struct ospf6_route *route, *match;
if (troute.path.metric_type)
match->path.metric_type =
troute.path.metric_type;
+ else
+ match->path.metric_type =
+ metric_type(ospf6, type, 0);
if (troute.path.cost)
match->path.cost = troute.path.cost;
+ else
+ match->path.cost = metric_value(ospf6, type, 0);
if (!IN6_IS_ADDR_UNSPECIFIED(&tinfo.forwarding))
memcpy(&info->forwarding, &tinfo.forwarding,
sizeof(struct in6_addr));
match->path.origin.id = htonl(info->id);
ospf6_as_external_lsa_originate(match, ospf6);
ospf6_asbr_status_update(ospf6, ospf6->redistribute);
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
+ if (IS_AREA_NSSA(oa))
+ ospf6_nssa_lsa_originate(match, oa);
+ }
+
return;
}
if (ROUTEMAP(red)) {
if (troute.path.metric_type)
route->path.metric_type = troute.path.metric_type;
+ else
+ route->path.metric_type = metric_type(ospf6, type, 0);
if (troute.path.cost)
route->path.cost = troute.path.cost;
+ else
+ route->path.cost = metric_value(ospf6, type, 0);
if (!IN6_IS_ADDR_UNSPECIFIED(&tinfo.forwarding))
memcpy(&info->forwarding, &tinfo.forwarding,
sizeof(struct in6_addr));
route->path.origin.id = htonl(info->id);
ospf6_as_external_lsa_originate(route, ospf6);
ospf6_asbr_status_update(ospf6, ospf6->redistribute);
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
+ if (IS_AREA_NSSA(oa))
+ ospf6_nssa_lsa_originate(route, oa);
+ }
}
void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex,
struct prefix *prefix, struct ospf6 *ospf6)
{
+ struct ospf6_area *oa;
struct ospf6_route *match;
struct ospf6_external_info *info = NULL;
+ struct listnode *lnode;
struct route_node *node;
struct ospf6_lsa *lsa;
struct prefix prefix_id;
lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
htonl(info->id), ospf6->router_id, ospf6->lsdb);
- if (lsa)
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_ASBR) {
+ zlog_debug("withdraw type 5 LSA for route %pFX",
+ prefix);
+ }
ospf6_lsa_purge(lsa);
+ }
+
+ /* Delete the NSSA LSA */
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7),
+ htonl(info->id), ospf6->router_id,
+ oa->lsdb);
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_ASBR) {
+ zlog_debug("withdraw type 7 LSA for route %pFX",
+ prefix);
+ }
+ ospf6_lsa_purge(lsa);
+ }
+ }
/* remove binding in external_id_table */
prefix_id.family = AF_INET;
if (type < 0)
return CMD_WARNING_CONFIG_FAILED;
- red = ospf6_redist_add(ospf6, type, 0);
+ red = ospf6_redist_lookup(ospf6, type, 0);
if (!red)
- return CMD_SUCCESS;
+ ospf6_redist_add(ospf6, type, 0);
+ else
+ ospf6_asbr_redistribute_unset(ospf6, red, type);
- ospf6_asbr_redistribute_unset(ospf6, red, type);
- ospf6_asbr_redistribute_set(type, ospf6->vrf_id);
+ ospf6_asbr_redistribute_set(ospf6, type);
return CMD_SUCCESS;
}
if (type < 0)
return CMD_WARNING_CONFIG_FAILED;
- red = ospf6_redist_add(ospf6, type, 0);
+ red = ospf6_redist_lookup(ospf6, type, 0);
if (!red)
- return CMD_SUCCESS;
+ red = ospf6_redist_add(ospf6, type, 0);
+ else
+ ospf6_asbr_redistribute_unset(ospf6, red, type);
- ospf6_asbr_redistribute_unset(ospf6, red, type);
ospf6_asbr_routemap_set(red, argv[idx_word]->arg);
- ospf6_asbr_redistribute_set(type, ospf6->vrf_id);
+ ospf6_asbr_redistribute_set(ospf6, type);
return CMD_SUCCESS;
}
.lh_get_prefix_str = ospf6_as_external_lsa_get_prefix_str,
.lh_debug = 0};
+static struct ospf6_lsa_handler nssa_external_handler = {
+ .lh_type = OSPF6_LSTYPE_TYPE_7,
+ .lh_name = "NSSA",
+ .lh_short_name = "Type7",
+ .lh_show = ospf6_as_external_lsa_show,
+ .lh_get_prefix_str = ospf6_as_external_lsa_get_prefix_str,
+ .lh_debug = 0};
+
void ospf6_asbr_init(void)
{
ospf6_routemap_init();
ospf6_install_lsa_handler(&as_external_handler);
+ ospf6_install_lsa_handler(&nssa_external_handler);
install_element(VIEW_NODE, &show_ipv6_ospf6_redistribute_cmd);
}
extern void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa);
+
extern void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
struct ospf6_route *asbr_entry);
extern void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry,
extern void ospf6_asbr_distribute_list_update(int type, struct ospf6 *ospf6);
struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type,
unsigned short instance);
+extern void ospf6_asbr_routemap_update(const char *mapname);
+extern void ospf6_as_external_lsa_originate(struct ospf6_route *route,
+ struct ospf6 *ospf6);
+extern void ospf6_asbr_status_update(struct ospf6 *ospf6, int status);
+
#endif /* OSPF6_ASBR_H */
#include "ospf6_neighbor.h"
#include "ospf6_flood.h"
+#include "ospf6_nssa.h"
unsigned char conf_debug_ospf6_flooding;
{
struct timeval now;
struct ospf6_lsa *old;
+ struct ospf6_area *area = NULL;
/* Remove the old instance from all neighbors' Link state
retransmission list (RFC2328 13.2 last paragraph) */
old = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id,
lsa->header->adv_router, lsa->lsdb);
if (old) {
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : old LSA %s", __func__,
+ lsa->name);
+ lsa->external_lsa_id = old->external_lsa_id;
+ }
THREAD_OFF(old->expire);
THREAD_OFF(old->refresh);
ospf6_flood_clear(old);
lsa->installed = now;
ospf6_lsdb_add(lsa, lsa->lsdb);
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) {
+ area = OSPF6_AREA(lsa->lsdb->data);
+ ospf6_translated_nssa_refresh(area, lsa, NULL);
+ ospf6_schedule_abr_task(area->ospf6);
+ }
+
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_ROUTER) {
+ area = OSPF6_AREA(lsa->lsdb->data);
+ if (old == NULL) {
+ if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)
+ || IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type))
+ zlog_debug("%s: New router LSA %s", __func__,
+ lsa->name);
+ ospf6_abr_nssa_check_status(area->ospf6);
+ }
+ }
return;
}
continue;
}
- if (oi->area->ospf6->inst_shutdown) {
+ if ((oi->area->ospf6->inst_shutdown)
+ || CHECK_FLAG(lsa->flag, OSPF6_LSA_FLUSH)) {
if (is_debug)
zlog_debug(
"%s: Send LSA %s (age %d) update now",
continue;
if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL
- && IS_AREA_STUB(oa))
+ && (IS_AREA_STUB(oa) || IS_AREA_NSSA(oa)))
+ continue;
+
+ /* Check for NSSA LSA */
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7
+ && !IS_AREA_NSSA(oa) && !OSPF6_LSA_IS_MAXAGE(lsa))
continue;
ospf6_flood_area(from, lsa, oa);
}
}
-static void ospf6_flood_clear_area(struct ospf6_lsa *lsa, struct ospf6_area *oa)
+void ospf6_flood_clear_area(struct ospf6_lsa *lsa, struct ospf6_area *oa)
{
struct listnode *node, *nnode;
struct ospf6_interface *oi;
continue;
if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL
- && IS_AREA_STUB(oa))
+ && (IS_AREA_STUB(oa) || (IS_AREA_NSSA(oa))))
+ continue;
+ /* Check for NSSA LSA */
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7
+ && !IS_AREA_NSSA(oa))
continue;
ospf6_flood_clear_area(lsa, oa);
struct ospf6 *ospf6;
ospf6 = ospf6_get_by_lsdb(lsa);
+ if (ospf6 == NULL)
+ return;
ospf6_flood_clear_process(lsa, ospf6);
}
ismore_recent = 1;
assert(from);
+ /* if we receive a LSA with invalid seqnum drop it */
+ if (ntohl(lsa_header->seqnum) - 1 == OSPF_MAX_SEQUENCE_NUMBER) {
+ if (IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa_header->type)) {
+ zlog_debug(
+ "received lsa [%s Id:%pI4 Adv:%pI4] with invalid seqnum 0x%x, ignore",
+ ospf6_lstype_name(lsa_header->type),
+ &lsa_header->id, &lsa_header->adv_router,
+ ntohl(lsa_header->seqnum));
+ }
+ return;
+ }
+
/* make lsa structure for received lsa */
new = ospf6_lsa_create(lsa_header);
/* if no database copy, should go above state (5) */
assert(old);
- if (is_debug) {
- zlog_debug(
- "Received is not newer, on the neighbor's request-list");
- zlog_debug("BadLSReq, discard the received LSA");
- }
+ zlog_warn(
+ "Received is not newer, on the neighbor %s request-list",
+ from->name);
+ zlog_warn(
+ "BadLSReq, discard the received LSA lsa %s send badLSReq",
+ new->name);
/* BadLSReq */
thread_add_event(master, bad_lsreq, from, 0, NULL);
extern int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on,
struct ospf6_lsa *lsa);
+extern void ospf6_flood_clear_area(struct ospf6_lsa *lsa,
+ struct ospf6_area *oa);
#endif /* OSPF6_FLOOD_H */
#include "ospf6_message.h"
#include "ospf6_route.h"
#include "ospf6_area.h"
+#include "ospf6_abr.h"
#include "ospf6_interface.h"
#include "ospf6_neighbor.h"
#include "ospf6_intra.h"
oi = XCALLOC(MTYPE_OSPF6_IF, sizeof(struct ospf6_interface));
+ oi->obuf = ospf6_fifo_new();
+
oi->area = (struct ospf6_area *)NULL;
oi->neighbor_list = list_new();
oi->neighbor_list->cmp = ospf6_neighbor_cmp;
QOBJ_UNREG(oi);
+ ospf6_fifo_free(oi->obuf);
+
for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on))
ospf6_neighbor_delete(on);
return l;
}
-void ospf6_interface_if_add(struct interface *ifp)
-{
- struct ospf6_interface *oi;
- unsigned int iobuflen;
-
- oi = (struct ospf6_interface *)ifp->info;
- if (oi == NULL)
- return;
-
- /* Try to adjust I/O buffer size with IfMtu */
- if (oi->ifmtu == 0)
- oi->ifmtu = ifp->mtu6;
- iobuflen = ospf6_iobuf_size(ifp->mtu6);
- if (oi->ifmtu > iobuflen) {
- if (IS_OSPF6_DEBUG_INTERFACE)
- zlog_debug(
- "Interface %s: IfMtu is adjusted to I/O buffer size: %d.",
- ifp->name, iobuflen);
- oi->ifmtu = iobuflen;
- }
-
- /* interface start */
- ospf6_interface_state_update(oi->interface);
-}
-
void ospf6_interface_state_update(struct interface *ifp)
{
struct ospf6_interface *oi;
/* check physical interface is up */
if (!if_is_operative(oi->interface)) {
- if (IS_OSPF6_DEBUG_INTERFACE)
- zlog_debug(
- "Interface %s is down, can't execute [InterfaceUp]",
- oi->interface->name);
+ zlog_warn("Interface %s is down, can't execute [InterfaceUp]",
+ oi->interface->name);
return 0;
}
/* check interface has a link-local address */
if (!(ospf6_interface_get_linklocal_address(oi->interface)
|| if_is_loopback_or_vrf(oi->interface))) {
- if (IS_OSPF6_DEBUG_INTERFACE)
- zlog_debug(
- "Interface %s has no link local address, can't execute [InterfaceUp]",
- oi->interface->name);
+ zlog_warn(
+ "Interface %s has no link local address, can't execute [InterfaceUp]",
+ oi->interface->name);
return 0;
}
/* If no area assigned, return */
if (oi->area == NULL) {
- zlog_debug(
+ zlog_warn(
"%s: Not scheduleing Hello for %s as there is no area assigned yet",
__func__, oi->interface->name);
return 0;
ospf6_sso(oi->interface->ifindex, &allspfrouters6,
IPV6_LEAVE_GROUP, ospf6->fd);
+ /* deal with write fifo */
+ ospf6_fifo_flush(oi->obuf);
+ if (oi->on_write_q) {
+ listnode_delete(ospf6->oi_write_q, oi);
+ if (list_isempty(ospf6->oi_write_q))
+ thread_cancel(&ospf6->t_write);
+ oi->on_write_q = 0;
+ }
+
ospf6_interface_state_change(OSPF6_INTERFACE_DOWN, oi);
return 0;
return 0;
}
+/* Find the global address to be used as a forwarding address in NSSA LSA.*/
+struct in6_addr *ospf6_interface_get_global_address(struct interface *ifp)
+{
+ struct listnode *n;
+ struct connected *c;
+ struct in6_addr *l = (struct in6_addr *)NULL;
+
+ /* for each connected address */
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) {
+ /* if family not AF_INET6, ignore */
+ if (c->address->family != AF_INET6)
+ continue;
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6))
+ l = &c->address->u.prefix6;
+ }
+ return l;
+}
+
+
static int show_ospf6_interface_common(struct vty *vty, vrf_id_t vrf_id,
int argc, struct cmd_token **argv,
int idx_ifname, int intf_idx,
return CMD_SUCCESS;
}
+void ospf6_interface_start(struct ospf6_interface *oi)
+{
+ struct ospf6 *ospf6;
+ struct ospf6_area *oa;
+
+ if (oi->area_id_format == OSPF6_AREA_FMT_UNSET)
+ return;
+
+ if (oi->area)
+ return;
+
+ ospf6 = ospf6_lookup_by_vrf_id(oi->interface->vrf_id);
+ if (!ospf6)
+ return;
+
+ oa = ospf6_area_lookup(oi->area_id, ospf6);
+ if (oa == NULL)
+ oa = ospf6_area_create(oi->area_id, ospf6, oi->area_id_format);
+
+ /* attach interface to area */
+ listnode_add(oa->if_list, oi);
+ oi->area = oa;
+
+ SET_FLAG(oa->flag, OSPF6_AREA_ENABLE);
+
+ /* start up */
+ ospf6_interface_enable(oi);
+
+ /* If the router is ABR, originate summary routes */
+ if (ospf6_check_and_set_router_abr(ospf6))
+ ospf6_abr_enable_area(oa);
+}
+
+void ospf6_interface_stop(struct ospf6_interface *oi)
+{
+ struct ospf6_area *oa;
+
+ oa = oi->area;
+ if (!oa)
+ return;
+
+ ospf6_interface_disable(oi);
+
+ listnode_delete(oa->if_list, oi);
+ oi->area = NULL;
+
+ if (oa->if_list->count == 0) {
+ UNSET_FLAG(oa->flag, OSPF6_AREA_ENABLE);
+ ospf6_abr_disable_area(oa);
+ }
+}
+
/* interface variable set command */
+DEFUN (ipv6_ospf6_area,
+ ipv6_ospf6_area_cmd,
+ "ipv6 ospf6 area <A.B.C.D|(0-4294967295)>",
+ IP6_STR
+ OSPF6_STR
+ "Specify the OSPF6 area ID\n"
+ "OSPF6 area ID in IPv4 address notation\n"
+ "OSPF6 area ID in decimal notation\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct ospf6_interface *oi;
+ int idx_ipv4 = 3;
+ uint32_t area_id;
+ int format;
+ int ipv6_count = 0;
+
+ assert(ifp);
+
+ oi = (struct ospf6_interface *)ifp->info;
+ if (oi == NULL)
+ oi = ospf6_interface_create(ifp);
+ assert(oi);
+
+ if (oi->area) {
+ vty_out(vty, "%s already attached to Area %s\n",
+ oi->interface->name, oi->area->name);
+ return CMD_SUCCESS;
+ }
+
+ /* if more than OSPF6_MAX_IF_ADDRS are configured on this interface
+ * then don't allow ospfv3 to be configured
+ */
+ ipv6_count = connected_count_by_family(ifp, AF_INET6);
+ if (oi->ifmtu == OSPF6_DEFAULT_MTU && ipv6_count > OSPF6_MAX_IF_ADDRS) {
+ vty_out(vty,
+ "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n",
+ ifp->name, OSPF6_MAX_IF_ADDRS, ipv6_count);
+ return CMD_WARNING_CONFIG_FAILED;
+ } else if (oi->ifmtu >= OSPF6_JUMBO_MTU
+ && ipv6_count > OSPF6_MAX_IF_ADDRS_JUMBO) {
+ vty_out(vty,
+ "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n",
+ ifp->name, OSPF6_MAX_IF_ADDRS_JUMBO, ipv6_count);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) {
+ vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ oi->area_id = area_id;
+ oi->area_id_format = format;
+
+ ospf6_interface_start(oi);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_ospf6_area,
+ no_ipv6_ospf6_area_cmd,
+ "no ipv6 ospf6 area [<A.B.C.D|(0-4294967295)>]",
+ NO_STR
+ IP6_STR
+ OSPF6_STR
+ "Specify the OSPF6 area ID\n"
+ "OSPF6 area ID in IPv4 address notation\n"
+ "OSPF6 area ID in decimal notation\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct ospf6_interface *oi;
+
+ assert(ifp);
+
+ oi = (struct ospf6_interface *)ifp->info;
+ if (oi == NULL)
+ oi = ospf6_interface_create(ifp);
+ assert(oi);
+
+ ospf6_interface_stop(oi);
+
+ oi->area_id = 0;
+ oi->area_id_format = OSPF6_AREA_FMT_UNSET;
+
+ return CMD_SUCCESS;
+}
+
DEFUN (ipv6_ospf6_ifmtu,
ipv6_ospf6_ifmtu_cmd,
"ipv6 ospf6 ifmtu (1-65535)",
}
+DEFUN (ospf6_write_multiplier,
+ ospf6_write_multiplier_cmd,
+ "write-multiplier (1-100)",
+ "Write multiplier\n"
+ "Maximum number of interface serviced per write\n")
+{
+ VTY_DECLVAR_CONTEXT(ospf6, o);
+ uint32_t write_oi_count;
+
+ write_oi_count = strtol(argv[1]->arg, NULL, 10);
+ if (write_oi_count < 1 || write_oi_count > 100) {
+ vty_out(vty, "write-multiplier value is invalid\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ o->write_oi_count = write_oi_count;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_write_multiplier,
+ no_ospf6_write_multiplier_cmd,
+ "no write-multiplier (1-100)",
+ NO_STR
+ "Write multiplier\n"
+ "Maximum number of interface serviced per write\n")
+{
+ VTY_DECLVAR_CONTEXT(ospf6, o);
+
+ o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT;
+ return CMD_SUCCESS;
+}
+
DEFUN (ipv6_ospf6_hellointerval,
ipv6_ospf6_hellointerval_cmd,
"ipv6 ospf6 hello-interval (1-65535)",
{
struct ospf6_interface *oi;
struct interface *ifp;
+ char buf[INET_ADDRSTRLEN];
FOR_ALL_INTERFACES (vrf, ifp) {
oi = (struct ospf6_interface *)ifp->info;
if (ifp->desc)
vty_out(vty, " description %s\n", ifp->desc);
+ if (oi->area_id_format != OSPF6_AREA_FMT_UNSET) {
+ area_id2str(buf, sizeof(buf), oi->area_id,
+ oi->area_id_format);
+ vty_out(vty, " ipv6 ospf6 area %s\n", buf);
+ }
if (oi->c_ifmtu)
vty_out(vty, " ipv6 ospf6 ifmtu %d\n", oi->c_ifmtu);
if (IS_OSPF6_DEBUG_ZEBRA(RECV))
zlog_debug("Zebra Interface add: %s index %d mtu %d", ifp->name,
ifp->ifindex, ifp->mtu6);
- ospf6_interface_if_add(ifp);
+
+ if (ifp->info)
+ ospf6_interface_start(ifp->info);
return 0;
}
zlog_debug("Zebra Interface delete: %s index %d mtu %d",
ifp->name, ifp->ifindex, ifp->mtu6);
+ if (ifp->info)
+ ospf6_interface_stop(ifp->info);
+
return 0;
}
&show_ipv6_ospf6_interface_ifname_prefix_cmd);
install_element(VIEW_NODE, &show_ipv6_ospf6_interface_traffic_cmd);
+ install_element(INTERFACE_NODE, &ipv6_ospf6_area_cmd);
+ install_element(INTERFACE_NODE, &no_ipv6_ospf6_area_cmd);
install_element(INTERFACE_NODE, &ipv6_ospf6_cost_cmd);
install_element(INTERFACE_NODE, &no_ipv6_ospf6_cost_cmd);
install_element(INTERFACE_NODE, &ipv6_ospf6_ifmtu_cmd);
/* reference bandwidth commands */
install_element(OSPF6_NODE, &auto_cost_reference_bandwidth_cmd);
install_element(OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd);
+ /* write-multiplier commands */
+ install_element(OSPF6_NODE, &ospf6_write_multiplier_cmd);
+ install_element(OSPF6_NODE, &no_ospf6_write_multiplier_cmd);
}
/* Clear the specified interface structure */
/* back pointer */
struct ospf6_area *area;
+ uint32_t area_id;
+ int area_id_format;
+
/* list of ospf6 neighbor */
struct list *neighbor_list;
/* I/F transmission delay */
uint32_t transdelay;
+ /* Packet send buffer. */
+ struct ospf6_fifo *obuf; /* Output queue */
+
/* Network Type */
uint8_t type;
bool type_cfg;
struct ospf6_route_table *route_connected;
+ /* last hello sent */
+ struct timeval last_hello;
+
/* prefix-list name to filter connected prefix */
char *plist_name;
char *profile;
} bfd_config;
+ int on_write_q;
+
/* Statistics Fields */
uint32_t hello_in;
uint32_t hello_out;
/* Function Prototypes */
+extern void ospf6_interface_start(struct ospf6_interface *oi);
+extern void ospf6_interface_stop(struct ospf6_interface *oi);
+
extern struct ospf6_interface *
ospf6_interface_lookup_by_ifindex(ifindex_t, vrf_id_t vrf_id);
extern struct ospf6_interface *ospf6_interface_create(struct interface *);
extern void ospf6_interface_enable(struct ospf6_interface *);
extern void ospf6_interface_disable(struct ospf6_interface *);
-extern void ospf6_interface_if_add(struct interface *);
extern void ospf6_interface_state_update(struct interface *);
extern void ospf6_interface_connected_route_update(struct interface *);
extern void ospf6_interface_connected_route_add(struct connected *);
+extern struct in6_addr *
+ospf6_interface_get_global_address(struct interface *ifp);
/* interface event */
extern int interface_up(struct thread *);
OSPF6_OPT_CLEAR_ALL(router_lsa->options);
memcpy(router_lsa->options, oa->options, 3);
- if (ospf6_is_router_abr(oa->ospf6))
+ if (ospf6_check_and_set_router_abr(oa->ospf6))
SET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B);
else
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B);
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_E);
}
+ /* If the router is ASBR and the area-type is NSSA set the
+ * translate bit in router LSA.
+ */
+ if (IS_AREA_NSSA(oa)
+ && (ospf6_asbr_is_asbr(oa->ospf6) || IS_OSPF6_ABR(oa->ospf6))) {
+ if (oa->NSSATranslatorRole == OSPF6_NSSA_ROLE_ALWAYS)
+ SET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT);
+ } else {
+ UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT);
+ }
+
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_V);
UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_W);
}
if ((caddr_t)lsdesc
== (caddr_t)router_lsa
+ sizeof(struct ospf6_router_lsa)) {
- if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER))
- zlog_debug(
- "Size limit setting for Router-LSA too short");
+ zlog_warn(
+ "Size limit setting for Router-LSA too short");
return 0;
}
struct ospf6_lsa *lsa;
void (*hook_add)(struct ospf6_route *) = NULL;
void (*hook_remove)(struct ospf6_route *) = NULL;
+ char buf[PREFIX2STR_BUFFER];
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
zlog_debug("Re-examin intra-routes for area %s", oa->name);
oa->route_table->hook_remove = hook_remove;
for (route = ospf6_route_head(oa->route_table); route; route = nroute) {
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
+ prefix2str(&route->prefix, buf, sizeof(buf));
+ zlog_debug("%s: route %s, flag 0x%x", __func__, buf,
+ route->flag);
+ }
+
nroute = ospf6_route_next(route);
if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)
&& CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)) {
route->flag = 0;
} else {
/* Redo the summaries as things might have changed */
+ if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
+ zlog_debug("%s: Originate summary for route %s",
+ __func__, buf);
ospf6_abr_originate_summary(route, oa->ospf6);
route->flag = 0;
}
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) ||
IS_OSPF6_DEBUG_ROUTE(MEMORY))
- zlog_info("%s: border-router calculation for area %s", __func__,
- oa->name);
+ zlog_debug("%s: border-router calculation for area %s",
+ __func__, oa->name);
hook_add = oa->ospf6->brouter_table->hook_add;
hook_remove = oa->ospf6->brouter_table->hook_remove;
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(brouter_id)
|| IS_OSPF6_DEBUG_ROUTE(MEMORY)) {
- zlog_info("%p: mark as removing: area %s brouter %s",
- (void *)brouter, oa->name, brouter_name);
+ zlog_debug("%p: mark as removing: area %s brouter %s",
+ (void *)brouter, oa->name, brouter_name);
ospf6_brouter_debug_print(brouter);
}
}
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(brouter_id)
|| IS_OSPF6_DEBUG_ROUTE(MEMORY)) {
- zlog_info("%p: transfer: area %s brouter %s",
- (void *)brouter, oa->name, brouter_name);
+ zlog_debug("%p: transfer: area %s brouter %s",
+ (void *)brouter, oa->name, brouter_name);
ospf6_brouter_debug_print(brouter);
}
}
brouter_id)
|| IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(
oa->area_id))
- zlog_info(
+ zlog_debug(
"%s: brouter %s disappears via area %s",
__func__, brouter_name, oa->name);
/* This is used to protect nbrouter from removed from
brouter_id)
|| IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(
oa->area_id))
- zlog_info("brouter %s still exists via area %s",
- brouter_name, oa->name);
+ zlog_debug(
+ "brouter %s still exists via area %s",
+ brouter_name, oa->name);
/* But re-originate summaries */
ospf6_abr_originate_summary(brouter, oa->ospf6);
}
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) ||
IS_OSPF6_DEBUG_ROUTE(MEMORY))
- zlog_info("%s: border-router calculation for area %s: done",
- __func__, oa->name);
+ zlog_debug("%s: border-router calculation for area %s: done",
+ __func__, oa->name);
}
static struct ospf6_lsa_handler router_handler = {
case OSPF6_LSTYPE_INTER_PREFIX:
case OSPF6_LSTYPE_INTER_ROUTER:
case OSPF6_LSTYPE_AS_EXTERNAL:
+ case OSPF6_LSTYPE_TYPE_7:
if (use_json) {
json_object_string_add(
json_obj, "type",
case OSPF6_LSTYPE_ROUTER:
case OSPF6_LSTYPE_NETWORK:
case OSPF6_LSTYPE_GROUP_MEMBERSHIP:
- case OSPF6_LSTYPE_TYPE_7:
case OSPF6_LSTYPE_LINK:
case OSPF6_LSTYPE_INTRA_PREFIX:
while (handler->lh_get_prefix_str(lsa, buf, sizeof(buf), cnt)
vty_out(vty, "Flag: %x \n", lsa->flag);
vty_out(vty, "Lock: %d \n", lsa->lock);
vty_out(vty, "ReTx Count: %d\n", lsa->retrans_count);
- vty_out(vty, "Threads: Expire: 0x%p, Refresh: 0x%p \n",
- (void *)lsa->expire, (void *)lsa->refresh);
+ vty_out(vty, "Threads: Expire: %p, Refresh: %p\n", lsa->expire,
+ lsa->refresh);
vty_out(vty, "\n");
}
return;
ospf6_lsa_age_current(lsa));
json_object_string_add(json_obj, "type",
ospf6_lstype_name(lsa->header->type));
+ json_object_string_add(json_obj, "linkStateId", id);
json_object_string_add(json_obj, "advertisingRouter",
adv_router);
json_object_int_add(json_obj, "lsSequenceNumber",
#define OSPF6_SCOPE_AS 0x4000
#define OSPF6_SCOPE_RESERVED 0x6000
+/* AS-external-LSA refresh method. */
+#define LSA_REFRESH_IF_CHANGED 0
+#define LSA_REFRESH_FORCE 1
+
+
/* XXX U-bit handling should be treated here */
#define OSPF6_LSA_SCOPE(type) (ntohs(type) & OSPF6_LSTYPE_SCOPE_MASK)
#define OSPF6_LSA_IS_CHANGED(L1, L2) ospf6_lsa_is_changed (L1, L2)
#define OSPF6_LSA_IS_SEQWRAP(L) ((L)->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER + 1))
+
struct ospf6_lsa {
char name[64]; /* dump string */
struct ospf6_lsdb *lsdb;
+ in_addr_t external_lsa_id;
+
/* lsa instance */
struct ospf6_lsa_header *header;
};
#define OSPF6_LSA_IMPLIEDACK 0x08
#define OSPF6_LSA_UNAPPROVED 0x10
#define OSPF6_LSA_SEQWRAPPED 0x20
+#define OSPF6_LSA_FLUSH 0x40
struct ospf6_lsa_handler {
uint16_t lh_type; /* host byte order */
struct ospf6_lsa *lsa, *lsanext;
for (ALL_LSDB(lsdb, lsa, lsanext)) {
- if (!OSPF6_LSA_IS_MAXAGE(lsa))
+ if (!OSPF6_LSA_IS_MAXAGE(lsa)) {
+ if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type))
+ zlog_debug("Not MaxAge %s", lsa->name);
continue;
+ }
+
if (lsa->retrans_count != 0) {
+ if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type))
+ zlog_debug("Remove MaxAge %s retrans_count %d",
+ lsa->name, lsa->retrans_count);
+
reschedule = 1;
continue;
}
THREAD_OFF(lsa->refresh);
thread_execute(master, ospf6_lsa_refresh, lsa, 0);
} else {
+ zlog_debug("calling ospf6_lsdb_remove %s", lsa->name);
ospf6_lsdb_remove(lsa, lsdb);
}
}
#include <netinet/ip6.h>
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_MESSAGE, "OSPF6 message");
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PACKET, "OSPF6 packet");
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_FIFO, "OSPF6 FIFO queue");
unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0};
static const struct message ospf6_message_type_str[] = {
}
}
+static struct ospf6_packet *ospf6_packet_new(size_t size)
+{
+ struct ospf6_packet *new;
+
+ new = XCALLOC(MTYPE_OSPF6_PACKET, sizeof(struct ospf6_packet));
+ new->s = stream_new(size);
+
+ return new;
+}
+
+static void ospf6_packet_free(struct ospf6_packet *op)
+{
+ if (op->s)
+ stream_free(op->s);
+
+ XFREE(MTYPE_OSPF6_PACKET, op);
+}
+
+struct ospf6_fifo *ospf6_fifo_new(void)
+{
+ struct ospf6_fifo *new;
+
+ new = XCALLOC(MTYPE_OSPF6_FIFO, sizeof(struct ospf6_fifo));
+ return new;
+}
+
+/* Add new packet to fifo. */
+static void ospf6_fifo_push(struct ospf6_fifo *fifo, struct ospf6_packet *op)
+{
+ if (fifo->tail)
+ fifo->tail->next = op;
+ else
+ fifo->head = op;
+
+ fifo->tail = op;
+
+ fifo->count++;
+}
+
+/* Add new packet to head of fifo. */
+static void ospf6_fifo_push_head(struct ospf6_fifo *fifo,
+ struct ospf6_packet *op)
+{
+ op->next = fifo->head;
+
+ if (fifo->tail == NULL)
+ fifo->tail = op;
+
+ fifo->head = op;
+
+ fifo->count++;
+}
+
+/* Delete first packet from fifo. */
+static struct ospf6_packet *ospf6_fifo_pop(struct ospf6_fifo *fifo)
+{
+ struct ospf6_packet *op;
+
+ op = fifo->head;
+
+ if (op) {
+ fifo->head = op->next;
+
+ if (fifo->head == NULL)
+ fifo->tail = NULL;
+
+ fifo->count--;
+ }
+
+ return op;
+}
+
+/* Return first fifo entry. */
+static struct ospf6_packet *ospf6_fifo_head(struct ospf6_fifo *fifo)
+{
+ return fifo->head;
+}
+
+/* Flush ospf packet fifo. */
+void ospf6_fifo_flush(struct ospf6_fifo *fifo)
+{
+ struct ospf6_packet *op;
+ struct ospf6_packet *next;
+
+ for (op = fifo->head; op; op = next) {
+ next = op->next;
+ ospf6_packet_free(op);
+ }
+ fifo->head = fifo->tail = NULL;
+ fifo->count = 0;
+}
+
+/* Free ospf packet fifo. */
+void ospf6_fifo_free(struct ospf6_fifo *fifo)
+{
+ ospf6_fifo_flush(fifo);
+
+ XFREE(MTYPE_OSPF6_FIFO, fifo);
+}
+
+static void ospf6_packet_add(struct ospf6_interface *oi,
+ struct ospf6_packet *op)
+{
+ /* Add packet to end of queue. */
+ ospf6_fifo_push(oi->obuf, op);
+
+ /* Debug of packet fifo*/
+ /* ospf_fifo_debug (oi->obuf); */
+}
+
+static void ospf6_packet_add_top(struct ospf6_interface *oi,
+ struct ospf6_packet *op)
+{
+ /* Add packet to head of queue. */
+ ospf6_fifo_push_head(oi->obuf, op);
+
+ /* Debug of packet fifo*/
+ /* ospf_fifo_debug (oi->obuf); */
+}
+
+static void ospf6_packet_delete(struct ospf6_interface *oi)
+{
+ struct ospf6_packet *op;
+
+ op = ospf6_fifo_pop(oi->obuf);
+
+ if (op)
+ ospf6_packet_free(op);
+}
+
+
static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst,
struct ospf6_interface *oi,
struct ospf6_header *oh)
int neighborchange = 0;
int neighbor_ifindex_change = 0;
int backupseen = 0;
+ int64_t latency = 0;
+ struct timeval timestamp;
+ monotime(×tamp);
hello = (struct ospf6_hello *)((caddr_t)oh
+ sizeof(struct ospf6_header));
on->priority = hello->priority;
}
+ /* check latency against hello period */
+ if (on->hello_in)
+ latency = monotime_since(&on->last_hello, NULL)
+ - (oi->hello_interval * 1000000);
+ /* log if latency exceeds the hello period */
+ if (latency > (oi->hello_interval * 1000000))
+ zlog_warn("%s RX %pI4 high latency %" PRId64 "us.", __func__,
+ &on->router_id, latency);
+ on->last_hello = timestamp;
+ on->hello_in++;
+
/* Always override neighbor's source address */
memcpy(&on->linklocal_addr, src, sizeof(struct in6_addr));
}
if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)) {
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
- zlog_debug("Master/Slave bit mismatch");
+ zlog_warn(
+ "DbDesc recv: Master/Slave bit mismatch Nbr %s",
+ on->name);
thread_add_event(master, seqnumber_mismatch, on, 0,
NULL);
return;
}
if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) {
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
- zlog_debug("Initialize bit mismatch");
+ zlog_warn("DbDesc recv: Initialize bit mismatch Nbr %s",
+ on->name);
thread_add_event(master, seqnumber_mismatch, on, 0,
NULL);
return;
}
if (memcmp(on->options, dbdesc->options, sizeof(on->options))) {
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
- zlog_debug("Option field mismatch");
+ zlog_warn("DbDesc recv: Option field mismatch Nbr %s",
+ on->name);
thread_add_event(master, seqnumber_mismatch, on, 0,
NULL);
return;
}
if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum) {
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
- zlog_debug(
- "Sequence number mismatch (%#lx expected)",
- (unsigned long)on->dbdesc_seqnum);
+ zlog_warn(
+ "DbDesc recv: Sequence number mismatch Nbr %s (%#lx expected)",
+ on->name, (unsigned long)on->dbdesc_seqnum);
thread_add_event(master, seqnumber_mismatch, on, 0,
NULL);
return;
return;
}
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
- zlog_debug("Not duplicate dbdesc in state %s",
- ospf6_neighbor_state_str[on->state]);
+ zlog_warn(
+ "DbDesc recv: Not duplicate dbdesc in state %s Nbr %s",
+ ospf6_neighbor_state_str[on->state], on->name);
thread_add_event(master, seqnumber_mismatch, on, 0, NULL);
return;
}
if (ntohs(his->header->type) == OSPF6_LSTYPE_AS_EXTERNAL
- && IS_AREA_STUB(on->ospf6_if->area)) {
+ && (IS_AREA_STUB(on->ospf6_if->area)
+ || IS_AREA_NSSA(on->ospf6_if->area))) {
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
zlog_debug(
"SeqNumMismatch (E-bit mismatch), discard");
}
if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)) {
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
- zlog_debug("Master/Slave bit mismatch");
+ zlog_warn(
+ "DbDesc slave recv: Master/Slave bit mismatch Nbr %s",
+ on->name);
thread_add_event(master, seqnumber_mismatch, on, 0,
NULL);
return;
}
if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) {
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
- zlog_debug("Initialize bit mismatch");
+ zlog_warn(
+ "DbDesc slave recv: Initialize bit mismatch Nbr %s",
+ on->name);
thread_add_event(master, seqnumber_mismatch, on, 0,
NULL);
return;
}
if (memcmp(on->options, dbdesc->options, sizeof(on->options))) {
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
- zlog_debug("Option field mismatch");
+ zlog_warn(
+ "DbDesc slave recv: Option field mismatch Nbr %s",
+ on->name);
thread_add_event(master, seqnumber_mismatch, on, 0,
NULL);
return;
}
if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum + 1) {
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
- zlog_debug(
- "Sequence number mismatch (%#lx expected)",
- (unsigned long)on->dbdesc_seqnum + 1);
+ zlog_warn(
+ "DbDesc slave recv: Sequence number mismatch Nbr %s (%#lx expected)",
+ on->name, (unsigned long)on->dbdesc_seqnum + 1);
thread_add_event(master, seqnumber_mismatch, on, 0,
NULL);
return;
return;
}
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR))
- zlog_debug("Not duplicate dbdesc in state %s",
- ospf6_neighbor_state_str[on->state]);
+ zlog_warn(
+ "DbDesc slave recv: Not duplicate dbdesc in state %s Nbr %s",
+ ospf6_neighbor_state_str[on->state], on->name);
thread_add_event(master, seqnumber_mismatch, on, 0, NULL);
return;
}
if (OSPF6_LSA_SCOPE(his->header->type) == OSPF6_SCOPE_AS
- && IS_AREA_STUB(on->ospf6_if->area)) {
+ && (IS_AREA_STUB(on->ospf6_if->area)
+ || IS_AREA_NSSA(on->ospf6_if->area))) {
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
zlog_debug("E-bit mismatch with LSA Headers");
ospf6_lsa_delete(his);
/* Find database copy */
lsa = ospf6_lsdb_lookup(e->type, e->id, e->adv_router, lsdb);
if (lsa == NULL) {
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) {
- zlog_debug(
- "Can't find requested [%s Id:%pI4 Adv:%pI4]",
- ospf6_lstype_name(e->type), &e->id,
- &e->adv_router);
- }
+ zlog_warn(
+ "Can't find requested lsa [%s Id:%pI4 Adv:%pI4] send badLSReq",
+ ospf6_lstype_name(e->type), &e->id,
+ &e->adv_router);
thread_add_event(master, bad_lsreq, on, 0, NULL);
return;
}
while (length) {
if (length < OSPF6_PREFIX_MIN_SIZE) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: undersized IPv6 prefix header",
- __func__);
+ zlog_warn("%s: undersized IPv6 prefix header",
+ __func__);
return MSG_NG;
}
/* safe to look deeper */
if (current->prefix_length > IPV6_MAX_BITLEN) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: invalid PrefixLength (%u bits)",
- __func__, current->prefix_length);
+ zlog_warn("%s: invalid PrefixLength (%u bits)",
+ __func__, current->prefix_length);
return MSG_NG;
}
/* covers both fixed- and variable-sized fields */
OSPF6_PREFIX_MIN_SIZE
+ OSPF6_PREFIX_SPACE(current->prefix_length);
if (requested_pfx_bytes > length) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: undersized IPv6 prefix",
- __func__);
+ zlog_warn("%s: undersized IPv6 prefix", __func__);
return MSG_NG;
}
/* next prefix */
real_num_pfxs++;
}
if (real_num_pfxs != req_num_pfxs) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug(
- "%s: IPv6 prefix number mismatch (%u required, %u real)",
- __func__, req_num_pfxs, real_num_pfxs);
+ zlog_warn(
+ "%s: IPv6 prefix number mismatch (%u required, %u real)",
+ __func__, req_num_pfxs, real_num_pfxs);
return MSG_NG;
}
return MSG_OK;
ltindex = lsatype & OSPF6_LSTYPE_FCODE_MASK;
if (ltindex < OSPF6_LSTYPE_SIZE && ospf6_lsa_minlen[ltindex]
&& lsalen < ospf6_lsa_minlen[ltindex] + OSPF6_LSA_HEADER_SIZE) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: undersized (%u B) LSA", __func__,
- lsalen);
+ zlog_warn("%s: undersized (%u B) LSA", __func__, lsalen);
return MSG_NG;
}
switch (lsatype) {
by N>=0 interface descriptions. */
if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_ROUTER_LSA_MIN_SIZE)
% OSPF6_ROUTER_LSDESC_FIX_SIZE) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug(
- "%s: interface description alignment error",
- __func__);
+ zlog_warn(
+ "%s: Router LSA interface description alignment error",
+ __func__);
return MSG_NG;
}
break;
if ((lsalen - OSPF6_LSA_HEADER_SIZE
- OSPF6_NETWORK_LSA_MIN_SIZE)
% OSPF6_NETWORK_LSDESC_FIX_SIZE) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug(
- "%s: router description alignment error",
- __func__);
+ zlog_warn(
+ "%s: Network LSA router description alignment error",
+ __func__);
return MSG_NG;
}
break;
/* RFC5340 A.4.6, fixed-size LSA. */
if (lsalen
> OSPF6_LSA_HEADER_SIZE + OSPF6_INTER_ROUTER_LSA_FIX_SIZE) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: oversized (%u B) LSA", __func__,
- lsalen);
+ zlog_warn("%s: Inter Router LSA oversized (%u B) LSA",
+ __func__, lsalen);
return MSG_NG;
}
break;
IPv6
prefix before ospf6_prefix_examin() confirms its sizing. */
if (exp_length + OSPF6_PREFIX_MIN_SIZE > lsalen) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: undersized (%u B) LSA header",
- __func__, lsalen);
+ zlog_warn(
+ "%s: AS External undersized (%u B) LSA header",
+ __func__, lsalen);
return MSG_NG;
}
/* forwarding address */
I.e.,
this check does not include any IPv6 prefix fields. */
if (exp_length > lsalen) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: undersized (%u B) LSA header",
- __func__, lsalen);
+ zlog_warn(
+ "%s: AS External undersized (%u B) LSA header",
+ __func__, lsalen);
return MSG_NG;
}
/* The last call completely covers the remainder (IPv6 prefix).
while (length) {
uint16_t lsalen;
if (length < OSPF6_LSA_HEADER_SIZE) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug(
- "%s: undersized (%zu B) trailing (#%u) LSA header",
- __func__, length, counted_lsas);
+ zlog_warn(
+ "%s: undersized (%zu B) trailing (#%u) LSA header",
+ __func__, length, counted_lsas);
return MSG_NG;
}
/* save on ntohs() calls here and in the LSA validator */
lsalen = OSPF6_LSA_SIZE(lsah);
if (lsalen < OSPF6_LSA_HEADER_SIZE) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug(
- "%s: malformed LSA header #%u, declared length is %u B",
- __func__, counted_lsas, lsalen);
+ zlog_warn(
+ "%s: malformed LSA header #%u, declared length is %u B",
+ __func__, counted_lsas, lsalen);
return MSG_NG;
}
if (headeronly) {
/* less checks here and in ospf6_lsa_examin() */
if (MSG_OK != ospf6_lsa_examin(lsah, lsalen, 1)) {
- if (IS_OSPF6_DEBUG_MESSAGE(
- OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug(
- "%s: anomaly in header-only %s LSA #%u",
- __func__,
- ospf6_lstype_name(lsah->type),
- counted_lsas);
+ zlog_warn(
+ "%s: anomaly in header-only %s LSA #%u",
+ __func__, ospf6_lstype_name(lsah->type),
+ counted_lsas);
return MSG_NG;
}
lsah = (struct ospf6_lsa_header
/* make sure the input buffer is deep enough before
* further checks */
if (lsalen > length) {
- if (IS_OSPF6_DEBUG_MESSAGE(
- OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug(
- "%s: anomaly in %s LSA #%u: declared length is %u B, buffered length is %zu B",
- __func__,
- ospf6_lstype_name(lsah->type),
- counted_lsas, lsalen, length);
+ zlog_warn(
+ "%s: anomaly in %s LSA #%u: declared length is %u B, buffered length is %zu B",
+ __func__, ospf6_lstype_name(lsah->type),
+ counted_lsas, lsalen, length);
return MSG_NG;
}
if (MSG_OK != ospf6_lsa_examin(lsah, lsalen, 0)) {
- if (IS_OSPF6_DEBUG_MESSAGE(
- OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug(
- "%s: anomaly in %s LSA #%u",
- __func__,
- ospf6_lstype_name(lsah->type),
- counted_lsas);
+ zlog_warn("%s: anomaly in %s LSA #%u", __func__,
+ ospf6_lstype_name(lsah->type),
+ counted_lsas);
return MSG_NG;
}
lsah = (struct ospf6_lsa_header *)((caddr_t)lsah
}
if (declared_num_lsas && counted_lsas != declared_num_lsas) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug(
- "%s: #LSAs declared (%u) does not match actual (%u)",
- __func__, declared_num_lsas, counted_lsas);
+ zlog_warn("%s: #LSAs declared (%u) does not match actual (%u)",
+ __func__, declared_num_lsas, counted_lsas);
return MSG_NG;
}
return MSG_OK;
/* length, 1st approximation */
if (bytesonwire < OSPF6_HEADER_SIZE) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: undersized (%u B) packet", __func__,
- bytesonwire);
+ zlog_warn("%s: undersized (%u B) packet", __func__,
+ bytesonwire);
return MSG_NG;
}
/* Now it is safe to access header fields. */
if (bytesonwire != ntohs(oh->length)) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug(
- "%s: %s packet length error (%u real, %u declared)",
- __func__, lookup_msg(ospf6_message_type_str,
- oh->type, NULL),
- bytesonwire, ntohs(oh->length));
+ zlog_warn("%s: %s packet length error (%u real, %u declared)",
+ __func__,
+ lookup_msg(ospf6_message_type_str, oh->type, NULL),
+ bytesonwire, ntohs(oh->length));
return MSG_NG;
}
/* version check */
if (oh->version != OSPFV3_VERSION) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: invalid (%u) protocol version",
- __func__, oh->version);
+ zlog_warn("%s: invalid (%u) protocol version", __func__,
+ oh->version);
return MSG_NG;
}
/* length, 2nd approximation */
if (oh->type < OSPF6_MESSAGE_TYPE_ALL && ospf6_packet_minlen[oh->type]
&& bytesonwire
< OSPF6_HEADER_SIZE + ospf6_packet_minlen[oh->type]) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: undersized (%u B) %s packet", __func__,
- bytesonwire,
- lookup_msg(ospf6_message_type_str, oh->type,
- NULL));
+ zlog_warn("%s: undersized (%u B) %s packet", __func__,
+ bytesonwire,
+ lookup_msg(ospf6_message_type_str, oh->type, NULL));
return MSG_NG;
}
/* type-specific deeper validation */
== (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_HELLO_MIN_SIZE)
% 4)
return MSG_OK;
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: alignment error in %s packet", __func__,
- lookup_msg(ospf6_message_type_str, oh->type,
- NULL));
+ zlog_warn("%s: alignment error in %s packet", __func__,
+ lookup_msg(ospf6_message_type_str, oh->type, NULL));
return MSG_NG;
case OSPF6_MESSAGE_TYPE_DBDESC:
/* RFC5340 A.3.3, packet header + OSPF6_DB_DESC_MIN_SIZE bytes
== (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_REQ_MIN_SIZE)
% OSPF6_LSREQ_LSDESC_FIX_SIZE)
return MSG_OK;
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: alignment error in %s packet", __func__,
- lookup_msg(ospf6_message_type_str, oh->type,
- NULL));
+ zlog_warn("%s: alignment error in %s packet", __func__,
+ lookup_msg(ospf6_message_type_str, oh->type, NULL));
return MSG_NG;
case OSPF6_MESSAGE_TYPE_LSUPDATE:
/* RFC5340 A.3.5, packet header + OSPF6_LS_UPD_MIN_SIZE bytes
1, 0);
break;
default:
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
- RECV_HDR))
- zlog_debug("%s: invalid (%u) message type", __func__,
- oh->type);
+ zlog_warn("%s: invalid (%u) message type", __func__, oh->type);
return MSG_NG;
}
- if (test != MSG_OK
- && IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV_HDR))
- zlog_debug("%s: anomaly in %s packet", __func__,
- lookup_msg(ospf6_message_type_str, oh->type, NULL));
+ if (test != MSG_OK)
+ zlog_warn("%s: anomaly in %s packet", __func__,
+ lookup_msg(ospf6_message_type_str, oh->type, NULL));
return test;
}
iobuflen = 0;
}
-int ospf6_receive(struct thread *thread)
+enum ospf6_read_return_enum {
+ OSPF6_READ_ERROR,
+ OSPF6_READ_CONTINUE,
+};
+
+static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6)
{
- int sockfd;
- unsigned int len;
+ int len;
struct in6_addr src, dst;
ifindex_t ifindex;
struct iovec iovector[2];
struct ospf6_interface *oi;
struct ospf6_header *oh;
- struct ospf6 *ospf6;
-
- /* add next read thread */
- ospf6 = THREAD_ARG(thread);
- sockfd = THREAD_FD(thread);
-
- thread_add_read(master, ospf6_receive, ospf6, ospf6->fd,
- &ospf6->t_ospf6_receive);
/* initialize */
memset(&src, 0, sizeof(src));
/* receive message */
len = ospf6_recvmsg(&src, &dst, &ifindex, iovector, sockfd);
- if (len > iobuflen) {
+ if (len < 0)
+ return OSPF6_READ_ERROR;
+
+ if ((uint)len > iobuflen) {
flog_err(EC_LIB_DEVELOPMENT, "Excess message read");
- return 0;
+ return OSPF6_READ_ERROR;
}
oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id);
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
RECV_HDR))
zlog_debug("Message received on disabled interface");
- return 0;
+ return OSPF6_READ_CONTINUE;
}
if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)) {
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
RECV_HDR))
zlog_debug("%s: Ignore message on passive interface %s",
__func__, oi->interface->name);
- return 0;
+ return OSPF6_READ_CONTINUE;
}
oh = (struct ospf6_header *)recvbuf;
if (ospf6_rxpacket_examin(oi, oh, len) != MSG_OK)
- return 0;
+ return OSPF6_READ_CONTINUE;
/* Being here means, that no sizing/alignment issues were detected in
the input packet. This renders the additional checks performed below
assert(0);
}
+ return OSPF6_READ_CONTINUE;
+}
+
+int ospf6_receive(struct thread *thread)
+{
+ int sockfd;
+ struct ospf6 *ospf6;
+ int count = 0;
+
+ /* add next read thread */
+ ospf6 = THREAD_ARG(thread);
+ sockfd = THREAD_FD(thread);
+
+ thread_add_read(master, ospf6_receive, ospf6, ospf6->fd,
+ &ospf6->t_ospf6_receive);
+
+ while (count < ospf6->write_oi_count) {
+ count++;
+ switch (ospf6_read_helper(sockfd, ospf6)) {
+ case OSPF6_READ_ERROR:
+ return 0;
+ case OSPF6_READ_CONTINUE:
+ break;
+ }
+ }
+
return 0;
}
-static void ospf6_send(struct in6_addr *src, struct in6_addr *dst,
- struct ospf6_interface *oi, struct ospf6_header *oh)
+static void ospf6_make_header(uint8_t type, struct ospf6_interface *oi,
+ struct stream *s)
{
- unsigned int len;
- char srcname[64];
- struct iovec iovector[2];
+ struct ospf6_header *oh;
- /* initialize */
- iovector[0].iov_base = (caddr_t)oh;
- iovector[0].iov_len = ntohs(oh->length);
- iovector[1].iov_base = NULL;
- iovector[1].iov_len = 0;
+ oh = (struct ospf6_header *)STREAM_DATA(s);
+
+ oh->version = (uint8_t)OSPFV3_VERSION;
+ oh->type = type;
- /* fill OSPF header */
- oh->version = OSPFV3_VERSION;
- /* message type must be set before */
- /* message length must be set before */
oh->router_id = oi->area->ospf6->router_id;
oh->area_id = oi->area->area_id;
- /* checksum is calculated by kernel */
oh->instance_id = oi->instance_id;
oh->reserved = 0;
+ stream_forward_endp(s, OSPF6_HEADER_SIZE);
+}
- /* Log */
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) {
- if (src)
- inet_ntop(AF_INET6, src, srcname, sizeof(srcname));
- else
- memset(srcname, 0, sizeof(srcname));
- zlog_debug("%s send on %s",
- lookup_msg(ospf6_message_type_str, oh->type, NULL),
- oi->interface->name);
- zlog_debug(" src: %s", srcname);
- zlog_debug(" dst: %pI6", dst);
+static void ospf6_fill_header(struct ospf6_interface *oi, struct stream *s,
+ uint16_t length)
+{
+ struct ospf6_header *oh;
+
+ oh = (struct ospf6_header *)STREAM_DATA(s);
+
+ oh->length = htons(length);
+}
+
+static void ospf6_fill_lsupdate_header(struct stream *s, uint32_t lsa_num)
+{
+ struct ospf6_header *oh;
+ struct ospf6_lsupdate *lsu;
+
+ oh = (struct ospf6_header *)STREAM_DATA(s);
+
+ lsu = (struct ospf6_lsupdate *)((caddr_t)oh
+ + sizeof(struct ospf6_header));
+ lsu->lsa_number = htonl(lsa_num);
+}
+
+static uint32_t ospf6_packet_max(struct ospf6_interface *oi)
+{
+ assert(oi->ifmtu > sizeof(struct ip6_hdr));
+ return oi->ifmtu - (sizeof(struct ip6_hdr));
+}
+
+static uint16_t ospf6_make_hello(struct ospf6_interface *oi, struct stream *s)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_neighbor *on;
+ uint16_t length = OSPF6_HELLO_MIN_SIZE;
+
+ stream_putl(s, oi->interface->ifindex);
+ stream_putc(s, oi->priority);
+ stream_putc(s, oi->area->options[0]);
+ stream_putc(s, oi->area->options[1]);
+ stream_putc(s, oi->area->options[2]);
+ stream_putw(s, oi->hello_interval);
+ stream_putw(s, oi->dead_interval);
+ stream_put_ipv4(s, oi->drouter);
+ stream_put_ipv4(s, oi->bdrouter);
+
+ for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) {
+ if (on->state < OSPF6_NEIGHBOR_INIT)
+ continue;
+
+ if ((length + sizeof(uint32_t) + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(oi)) {
+ if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO,
+ SEND))
+ zlog_debug(
+ "sending Hello message: exceeds I/F MTU");
+ break;
+ }
+
+ stream_put_ipv4(s, on->router_id);
+ length += sizeof(uint32_t);
+ }
+
+ return length;
+}
+static int ospf6_write(struct thread *thread)
+{
+ struct ospf6 *ospf6 = THREAD_ARG(thread);
+ struct ospf6_interface *oi;
+ struct ospf6_interface *last_serviced_oi = NULL;
+ struct ospf6_header *oh;
+ struct ospf6_packet *op;
+ struct listnode *node;
+ char srcname[64], dstname[64];
+ struct iovec iovector[2];
+ int pkt_count = 0;
+ int len;
+ int64_t latency = 0;
+ struct timeval timestamp;
+
+ if (ospf6->fd < 0) {
+ zlog_warn("ospf6_write failed to send, fd %d", ospf6->fd);
+ return -1;
+ }
+
+ node = listhead(ospf6->oi_write_q);
+ assert(node);
+ oi = listgetdata(node);
+
+ while ((pkt_count < ospf6->write_oi_count) && oi
+ && (last_serviced_oi != oi)) {
+
+ op = ospf6_fifo_head(oi->obuf);
+ assert(op);
+ assert(op->length >= OSPF6_HEADER_SIZE);
+
+ iovector[0].iov_base = (caddr_t)stream_pnt(op->s);
+ iovector[0].iov_len = op->length;
+ iovector[1].iov_base = NULL;
+ iovector[1].iov_len = 0;
+
+ oh = (struct ospf6_header *)STREAM_DATA(op->s);
+
+ len = ospf6_sendmsg(oi->linklocal_addr, &op->dst,
+ oi->interface->ifindex, iovector,
+ ospf6->fd);
+ if (len != op->length)
+ flog_err(EC_LIB_DEVELOPMENT,
+ "Could not send entire message");
+
+ if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)) {
+ inet_ntop(AF_INET6, &op->dst, dstname, sizeof(dstname));
+ inet_ntop(AF_INET6, oi->linklocal_addr, srcname,
+ sizeof(srcname));
+ zlog_debug("%s send on %s",
+ lookup_msg(ospf6_message_type_str, oh->type,
+ NULL),
+ oi->interface->name);
+ zlog_debug(" src: %s", srcname);
+ zlog_debug(" dst: %s", dstname);
+ }
switch (oh->type) {
case OSPF6_MESSAGE_TYPE_HELLO:
- ospf6_hello_print(oh, OSPF6_ACTION_RECV);
+ monotime(×tamp);
+ if (oi->hello_out)
+ latency = monotime_since(&oi->last_hello, NULL)
+ - (oi->hello_interval * 1000000);
+
+ /* log if latency exceeds the hello period */
+ if (latency > (oi->hello_interval * 1000000))
+ zlog_warn("%s hello TX high latency %" PRId64
+ "us.",
+ __func__, latency);
+ oi->last_hello = timestamp;
+ oi->hello_out++;
+ ospf6_hello_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_DBDESC:
- ospf6_dbdesc_print(oh, OSPF6_ACTION_RECV);
+ oi->db_desc_out++;
+ ospf6_dbdesc_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_LSREQ:
- ospf6_lsreq_print(oh, OSPF6_ACTION_RECV);
+ oi->ls_req_out++;
+ ospf6_lsreq_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_LSUPDATE:
- ospf6_lsupdate_print(oh, OSPF6_ACTION_RECV);
+ oi->ls_upd_out++;
+ ospf6_lsupdate_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_LSACK:
- ospf6_lsack_print(oh, OSPF6_ACTION_RECV);
+ oi->ls_ack_out++;
+ ospf6_lsack_print(oh, OSPF6_ACTION_SEND);
break;
default:
zlog_debug("Unknown message");
assert(0);
break;
}
- }
+ /* Now delete packet from queue. */
+ ospf6_packet_delete(oi);
+
+ /* Move this interface to the tail of write_q to
+ serve everyone in a round robin fashion */
+ list_delete_node(ospf6->oi_write_q, node);
+ if (ospf6_fifo_head(oi->obuf) == NULL) {
+ oi->on_write_q = 0;
+ last_serviced_oi = NULL;
+ oi = NULL;
+ } else {
+ listnode_add(ospf6->oi_write_q, oi);
+ }
- /* send message */
- if (oi->area->ospf6->fd != -1) {
- len = ospf6_sendmsg(src, dst, oi->interface->ifindex, iovector,
- oi->area->ospf6->fd);
- if (len != ntohs(oh->length))
- flog_err(EC_LIB_DEVELOPMENT,
- "Could not send entire message");
+ /* Setup to service from the head of the queue again */
+ if (!list_isempty(ospf6->oi_write_q)) {
+ node = listhead(ospf6->oi_write_q);
+ oi = listgetdata(node);
+ }
}
-}
-static uint32_t ospf6_packet_max(struct ospf6_interface *oi)
-{
- assert(oi->ifmtu > sizeof(struct ip6_hdr));
- return oi->ifmtu - (sizeof(struct ip6_hdr));
+ /* If packets still remain in queue, call write thread. */
+ if (!list_isempty(ospf6->oi_write_q))
+ thread_add_write(master, ospf6_write, ospf6, ospf6->fd,
+ &ospf6->t_write);
+
+ return 0;
}
int ospf6_hello_send(struct thread *thread)
{
struct ospf6_interface *oi;
- struct ospf6_header *oh;
- struct ospf6_hello *hello;
- uint8_t *p;
- struct listnode *node, *nnode;
- struct ospf6_neighbor *on;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
oi = (struct ospf6_interface *)THREAD_ARG(thread);
oi->thread_send_hello = (struct thread *)NULL;
return 0;
}
- if (iobuflen == 0) {
- zlog_debug("Unable to send Hello on interface %s iobuflen is 0",
- oi->interface->name);
+ op = ospf6_packet_new(oi->ifmtu);
+
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_HELLO, oi, op->s);
+
+ /* Prepare OSPF Hello body */
+ length += ospf6_make_hello(oi, op->s);
+ if (length == OSPF6_HEADER_SIZE) {
+ /* Hello overshooting MTU */
+ ospf6_packet_free(op);
return 0;
}
- /* set next thread */
- thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval,
- &oi->thread_send_hello);
+ /* Fill OSPF header. */
+ ospf6_fill_header(oi, op->s, length);
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- hello = (struct ospf6_hello *)((caddr_t)oh
- + sizeof(struct ospf6_header));
+ /* Set packet length. */
+ op->length = length;
- hello->interface_id = htonl(oi->interface->ifindex);
- hello->priority = oi->priority;
- hello->options[0] = oi->area->options[0];
- hello->options[1] = oi->area->options[1];
- hello->options[2] = oi->area->options[2];
- hello->hello_interval = htons(oi->hello_interval);
- hello->dead_interval = htons(oi->dead_interval);
- hello->drouter = oi->drouter;
- hello->bdrouter = oi->bdrouter;
+ op->dst = allspfrouters6;
- p = (uint8_t *)((caddr_t)hello + sizeof(struct ospf6_hello));
+ /* Add packet to the top of the interface output queue, so that they
+ * can't get delayed by things like long queues of LS Update packets
+ */
+ ospf6_packet_add_top(oi, op);
- for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) {
- if (on->state < OSPF6_NEIGHBOR_INIT)
- continue;
-
- if (p - sendbuf + sizeof(uint32_t) > ospf6_packet_max(oi)) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO,
- SEND_HDR))
- zlog_debug(
- "sending Hello message: exceeds I/F MTU");
- break;
- }
-
- memcpy(p, &on->router_id, sizeof(uint32_t));
- p += sizeof(uint32_t);
- }
-
- oh->type = OSPF6_MESSAGE_TYPE_HELLO;
- oh->length = htons(p - sendbuf);
+ /* set next thread */
+ thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval,
+ &oi->thread_send_hello);
- oi->hello_out++;
+ OSPF6_MESSAGE_WRITE_ON(oi);
- ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh);
return 0;
}
-int ospf6_dbdesc_send(struct thread *thread)
+static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s)
{
- struct ospf6_neighbor *on;
- struct ospf6_header *oh;
- struct ospf6_dbdesc *dbdesc;
- uint8_t *p;
+ uint16_t length = OSPF6_DB_DESC_MIN_SIZE;
struct ospf6_lsa *lsa, *lsanext;
- struct in6_addr *dst;
-
- on = (struct ospf6_neighbor *)THREAD_ARG(thread);
- on->thread_send_dbdesc = (struct thread *)NULL;
-
- if (on->state < OSPF6_NEIGHBOR_EXSTART) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND_HDR))
- zlog_debug(
- "Quit to send DbDesc to neighbor %s state %s",
- on->name, ospf6_neighbor_state_str[on->state]);
- return 0;
- }
-
- /* set next thread if master */
- if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT))
- thread_add_timer(master, ospf6_dbdesc_send, on,
- on->ospf6_if->rxmt_interval,
- &on->thread_send_dbdesc);
-
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh
- + sizeof(struct ospf6_header));
/* if this is initial one, initialize sequence number for DbDesc */
if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)
on->dbdesc_seqnum = monotime(NULL);
}
- dbdesc->options[0] = on->ospf6_if->area->options[0];
- dbdesc->options[1] = on->ospf6_if->area->options[1];
- dbdesc->options[2] = on->ospf6_if->area->options[2];
- dbdesc->ifmtu = htons(on->ospf6_if->ifmtu);
- dbdesc->bits = on->dbdesc_bits;
- dbdesc->seqnum = htonl(on->dbdesc_seqnum);
+ /* reserved */
+ stream_putc(s, 0); /* reserved 1 */
+ stream_putc(s, on->ospf6_if->area->options[0]);
+ stream_putc(s, on->ospf6_if->area->options[1]);
+ stream_putc(s, on->ospf6_if->area->options[2]);
+ stream_putw(s, on->ospf6_if->ifmtu);
+ stream_putc(s, 0); /* reserved 2 */
+ stream_putc(s, on->dbdesc_bits);
+ stream_putl(s, on->dbdesc_seqnum);
/* if this is not initial one, set LSA headers in dbdesc */
- p = (uint8_t *)((caddr_t)dbdesc + sizeof(struct ospf6_dbdesc));
if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)) {
for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) {
ospf6_lsa_age_update_to_send(lsa,
on->ospf6_if->transdelay);
/* MTU check */
- if (p - sendbuf + sizeof(struct ospf6_lsa_header)
+ if ((length + sizeof(struct ospf6_lsa_header)
+ + OSPF6_HEADER_SIZE)
> ospf6_packet_max(on->ospf6_if)) {
ospf6_lsa_unlock(lsa);
if (lsanext)
ospf6_lsa_unlock(lsanext);
break;
}
- memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header));
- p += sizeof(struct ospf6_lsa_header);
+ stream_put(s, lsa->header,
+ sizeof(struct ospf6_lsa_header));
+ length += sizeof(struct ospf6_lsa_header);
}
}
+ return length;
+}
+
+int ospf6_dbdesc_send(struct thread *thread)
+{
+ struct ospf6_neighbor *on;
+ uint16_t length = OSPF6_HEADER_SIZE;
+ struct ospf6_packet *op;
+
+ on = (struct ospf6_neighbor *)THREAD_ARG(thread);
+ on->thread_send_dbdesc = (struct thread *)NULL;
+
+ if (on->state < OSPF6_NEIGHBOR_EXSTART) {
+ if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND))
+ zlog_debug(
+ "Quit to send DbDesc to neighbor %s state %s",
+ on->name, ospf6_neighbor_state_str[on->state]);
+ return 0;
+ }
+
+ /* set next thread if master */
+ if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT))
+ thread_add_timer(master, ospf6_dbdesc_send, on,
+ on->ospf6_if->rxmt_interval,
+ &on->thread_send_dbdesc);
+
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_DBDESC, on->ospf6_if, op->s);
- oh->type = OSPF6_MESSAGE_TYPE_DBDESC;
- oh->length = htons(p - sendbuf);
+ length += ospf6_make_dbdesc(on, op->s);
+ ospf6_fill_header(on->ospf6_if, op->s, length);
+ /* Set packet length. */
+ op->length = length;
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
- dst = &allspfrouters6;
+ op->dst = allspfrouters6;
else
- dst = &on->linklocal_addr;
+ op->dst = on->linklocal_addr;
- on->ospf6_if->db_desc_out++;
+ ospf6_packet_add(on->ospf6_if, op);
- ospf6_send(on->ospf6_if->linklocal_addr, dst, on->ospf6_if, oh);
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
return 0;
}
size = sizeof(struct ospf6_lsa_header) + sizeof(struct ospf6_dbdesc);
for (ALL_LSDB(on->summary_list, lsa, lsanext)) {
/* if stub area then don't advertise AS-External LSAs */
- if (IS_AREA_STUB(on->ospf6_if->area)
+ if ((IS_AREA_STUB(on->ospf6_if->area)
+ || IS_AREA_NSSA(on->ospf6_if->area))
&& ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) {
ospf6_lsdb_remove(lsa, on->summary_list);
continue;
return 0;
}
+static uint16_t ospf6_make_lsreq(struct ospf6_neighbor *on, struct stream *s)
+{
+ uint16_t length = 0;
+ struct ospf6_lsa *lsa, *lsanext, *last_req = NULL;
+
+ for (ALL_LSDB(on->request_list, lsa, lsanext)) {
+ if ((length + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(on->ospf6_if)) {
+ ospf6_lsa_unlock(lsa);
+ if (lsanext)
+ ospf6_lsa_unlock(lsanext);
+ break;
+ }
+ stream_putw(s, 0); /* reserved */
+ stream_putw(s, ntohs(lsa->header->type));
+ stream_putl(s, ntohl(lsa->header->id));
+ stream_putl(s, ntohl(lsa->header->adv_router));
+ length += sizeof(struct ospf6_lsreq_entry);
+ last_req = lsa;
+ }
+
+ if (last_req != NULL) {
+ if (on->last_ls_req != NULL)
+ on->last_ls_req = ospf6_lsa_unlock(on->last_ls_req);
+
+ ospf6_lsa_lock(last_req);
+ on->last_ls_req = last_req;
+ }
+
+ return length;
+}
+
+static uint16_t ospf6_make_lsack_neighbor(struct ospf6_neighbor *on,
+ struct ospf6_packet **op)
+{
+ uint16_t length = 0;
+ struct ospf6_lsa *lsa, *lsanext;
+ int lsa_cnt = 0;
+
+ for (ALL_LSDB(on->lsack_list, lsa, lsanext)) {
+ if ((length + sizeof(struct ospf6_lsa_header)
+ + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(on->ospf6_if)) {
+ /* if we run out of packet size/space here,
+ better to try again soon. */
+ if (lsa_cnt) {
+ ospf6_fill_header(on->ospf6_if, (*op)->s,
+ length + OSPF6_HEADER_SIZE);
+
+ (*op)->length = length + OSPF6_HEADER_SIZE;
+ (*op)->dst = on->linklocal_addr;
+ ospf6_packet_add(on->ospf6_if, *op);
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
+ /* new packet */
+ *op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK,
+ on->ospf6_if, (*op)->s);
+ length = 0;
+ lsa_cnt = 0;
+ }
+ }
+ ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
+ stream_put((*op)->s, lsa->header,
+ sizeof(struct ospf6_lsa_header));
+ length += sizeof(struct ospf6_lsa_header);
+
+ assert(lsa->lock == 2);
+ ospf6_lsdb_remove(lsa, on->lsack_list);
+ lsa_cnt++;
+ }
+ return length;
+}
+
int ospf6_lsreq_send(struct thread *thread)
{
struct ospf6_neighbor *on;
- struct ospf6_header *oh;
- struct ospf6_lsreq_entry *e;
- uint8_t *p;
- struct ospf6_lsa *lsa, *lsanext, *last_req;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
on = (struct ospf6_neighbor *)THREAD_ARG(thread);
on->thread_send_lsreq = (struct thread *)NULL;
return 0;
}
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- last_req = NULL;
-
- /* set Request entries in lsreq */
- p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header));
- for (ALL_LSDB(on->request_list, lsa, lsanext)) {
- /* MTU check */
- if (p - sendbuf + sizeof(struct ospf6_lsreq_entry)
- > ospf6_packet_max(on->ospf6_if)) {
- ospf6_lsa_unlock(lsa);
- if (lsanext)
- ospf6_lsa_unlock(lsanext);
- break;
- }
-
- e = (struct ospf6_lsreq_entry *)p;
- e->type = lsa->header->type;
- e->id = lsa->header->id;
- e->adv_router = lsa->header->adv_router;
- p += sizeof(struct ospf6_lsreq_entry);
- last_req = lsa;
- }
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSREQ, on->ospf6_if, op->s);
- if (last_req != NULL) {
- if (on->last_ls_req != NULL)
- on->last_ls_req = ospf6_lsa_unlock(on->last_ls_req);
+ length += ospf6_make_lsreq(on, op->s);
- ospf6_lsa_lock(last_req);
- on->last_ls_req = last_req;
+ if (length == OSPF6_HEADER_SIZE) {
+ /* Hello overshooting MTU */
+ ospf6_packet_free(op);
+ return 0;
}
- oh->type = OSPF6_MESSAGE_TYPE_LSREQ;
- oh->length = htons(p - sendbuf);
+ /* Fill OSPF header. */
+ ospf6_fill_header(on->ospf6_if, op->s, length);
- on->ospf6_if->ls_req_out++;
+ /* Set packet length */
+ op->length = length;
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
- ospf6_send(on->ospf6_if->linklocal_addr, &allspfrouters6,
- on->ospf6_if, oh);
+ op->dst = allspfrouters6;
else
- ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr,
- on->ospf6_if, oh);
+ op->dst = on->linklocal_addr;
+
+ ospf6_packet_add(on->ospf6_if, op);
+
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
/* set next thread */
if (on->request_list->count != 0) {
static void ospf6_send_lsupdate(struct ospf6_neighbor *on,
struct ospf6_interface *oi,
- struct ospf6_header *oh)
+ struct ospf6_packet *op)
{
if (on) {
- on->ospf6_if->ls_upd_out++;
if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
|| (on->ospf6_if->state == OSPF6_INTERFACE_DR)
- || (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) {
- ospf6_send(on->ospf6_if->linklocal_addr,
- &allspfrouters6, on->ospf6_if, oh);
- } else {
- ospf6_send(on->ospf6_if->linklocal_addr,
- &on->linklocal_addr, on->ospf6_if, oh);
- }
+ || (on->ospf6_if->state == OSPF6_INTERFACE_BDR))
+ op->dst = allspfrouters6;
+ else
+ op->dst = on->linklocal_addr;
+ oi = on->ospf6_if;
} else if (oi) {
-
- oi->ls_upd_out++;
-
if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT)
|| (oi->state == OSPF6_INTERFACE_DR)
- || (oi->state == OSPF6_INTERFACE_BDR)) {
- ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh);
- } else {
- ospf6_send(oi->linklocal_addr, &alldrouters6, oi, oh);
+ || (oi->state == OSPF6_INTERFACE_BDR))
+ op->dst = allspfrouters6;
+ else
+ op->dst = alldrouters6;
+ }
+ if (oi) {
+ ospf6_packet_add(oi, op);
+ OSPF6_MESSAGE_WRITE_ON(oi);
+ }
+}
+
+static uint16_t ospf6_make_lsupdate_list(struct ospf6_neighbor *on,
+ struct ospf6_packet **op, int *lsa_cnt)
+{
+ uint16_t length = OSPF6_LS_UPD_MIN_SIZE;
+ struct ospf6_lsa *lsa, *lsanext;
+
+ /* skip over fixed header */
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
+
+ for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) {
+ if ((length + (unsigned int)OSPF6_LSA_SIZE(lsa->header)
+ + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(on->ospf6_if)) {
+ ospf6_fill_header(on->ospf6_if, (*op)->s,
+ length + OSPF6_HEADER_SIZE);
+ (*op)->length = length + OSPF6_HEADER_SIZE;
+ ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt);
+ ospf6_send_lsupdate(on, NULL, *op);
+
+ /* refresh packet */
+ *op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ length = OSPF6_LS_UPD_MIN_SIZE;
+ *lsa_cnt = 0;
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE,
+ on->ospf6_if, (*op)->s);
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
+ }
+ ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
+ stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header));
+ (*lsa_cnt)++;
+ length += OSPF6_LSA_SIZE(lsa->header);
+ assert(lsa->lock == 2);
+ ospf6_lsdb_remove(lsa, on->lsupdate_list);
+ }
+ return length;
+}
+
+static uint16_t ospf6_make_ls_retrans_list(struct ospf6_neighbor *on,
+ struct ospf6_packet **op,
+ int *lsa_cnt)
+{
+ uint16_t length = OSPF6_LS_UPD_MIN_SIZE;
+ struct ospf6_lsa *lsa, *lsanext;
+
+ /* skip over fixed header */
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
+
+ for (ALL_LSDB(on->retrans_list, lsa, lsanext)) {
+ if ((length + (unsigned int)OSPF6_LSA_SIZE(lsa->header)
+ + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(on->ospf6_if)) {
+ ospf6_fill_header(on->ospf6_if, (*op)->s,
+ length + OSPF6_HEADER_SIZE);
+ (*op)->length = length + OSPF6_HEADER_SIZE;
+ ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt);
+ if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
+ (*op)->dst = allspfrouters6;
+ else
+ (*op)->dst = on->linklocal_addr;
+
+ ospf6_packet_add(on->ospf6_if, *op);
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
+
+ /* refresh packet */
+ *op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ length = OSPF6_LS_UPD_MIN_SIZE;
+ *lsa_cnt = 0;
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE,
+ on->ospf6_if, (*op)->s);
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
}
+ ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
+ stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header));
+ (*lsa_cnt)++;
+ length += OSPF6_LSA_SIZE(lsa->header);
}
+ return length;
}
int ospf6_lsupdate_send_neighbor(struct thread *thread)
{
struct ospf6_neighbor *on;
- struct ospf6_header *oh;
- struct ospf6_lsupdate *lsupdate;
- uint8_t *p;
- int lsa_cnt;
- struct ospf6_lsa *lsa, *lsanext;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
+ int lsa_cnt = 0;
on = (struct ospf6_neighbor *)THREAD_ARG(thread);
on->thread_send_lsupdate = (struct thread *)NULL;
return 0;
}
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
- + sizeof(struct ospf6_header));
-
- p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
- lsa_cnt = 0;
-
- /* lsupdate_list lists those LSA which doesn't need to be
- retransmitted. remove those from the list */
- for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) {
- /* MTU check */
- if ((p - sendbuf + (unsigned int)OSPF6_LSA_SIZE(lsa->header))
- > ospf6_packet_max(on->ospf6_if)) {
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
-
- ospf6_send_lsupdate(on, NULL, oh);
-
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate
- *)((caddr_t)oh
- + sizeof(struct
- ospf6_header));
-
- p = (uint8_t *)((caddr_t)lsupdate
- + sizeof(struct
- ospf6_lsupdate));
- lsa_cnt = 0;
- }
- }
-
- ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
- memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
- p += OSPF6_LSA_SIZE(lsa->header);
- lsa_cnt++;
-
- assert(lsa->lock == 2);
- ospf6_lsdb_remove(lsa, on->lsupdate_list);
- }
-
+ /* first do lsupdate_list */
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s);
+ length += ospf6_make_lsupdate_list(on, &op, &lsa_cnt);
if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
- ospf6_send_lsupdate(on, NULL, oh);
- }
-
- /* The addresses used for retransmissions are different from those sent
- the
- first time and so we need to separate them here.
- */
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
- + sizeof(struct ospf6_header));
- p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
- lsa_cnt = 0;
-
- for (ALL_LSDB(on->retrans_list, lsa, lsanext)) {
- /* MTU check */
- if ((p - sendbuf + (unsigned int)OSPF6_LSA_SIZE(lsa->header))
- > ospf6_packet_max(on->ospf6_if)) {
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
-
- if (on->ospf6_if->state
- == OSPF6_INTERFACE_POINTTOPOINT) {
- ospf6_send(on->ospf6_if->linklocal_addr,
- &allspfrouters6,
- on->ospf6_if, oh);
- } else {
- ospf6_send(on->ospf6_if->linklocal_addr,
- &on->linklocal_addr,
- on->ospf6_if, oh);
- }
-
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate
- *)((caddr_t)oh
- + sizeof(struct
- ospf6_header));
- p = (uint8_t *)((caddr_t)lsupdate
- + sizeof(struct
- ospf6_lsupdate));
- lsa_cnt = 0;
- }
- }
-
- ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
- memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
- p += OSPF6_LSA_SIZE(lsa->header);
- lsa_cnt++;
- }
-
+ /* Fill OSPF header. */
+ ospf6_fill_header(on->ospf6_if, op->s, length);
+ ospf6_fill_lsupdate_header(op->s, lsa_cnt);
+ op->length = length;
+ ospf6_send_lsupdate(on, NULL, op);
+
+ /* prepare new packet */
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ length = OSPF6_HEADER_SIZE;
+ lsa_cnt = 0;
+ } else {
+ stream_reset(op->s);
+ length = OSPF6_HEADER_SIZE;
+ }
+
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s);
+ /* now do retransmit list */
+ length += ospf6_make_ls_retrans_list(on, &op, &lsa_cnt);
if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
-
+ ospf6_fill_header(on->ospf6_if, op->s, length);
+ ospf6_fill_lsupdate_header(op->s, lsa_cnt);
+ op->length = length;
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
- ospf6_send(on->ospf6_if->linklocal_addr,
- &allspfrouters6, on->ospf6_if, oh);
+ op->dst = allspfrouters6;
else
- ospf6_send(on->ospf6_if->linklocal_addr,
- &on->linklocal_addr, on->ospf6_if, oh);
- }
+ op->dst = on->linklocal_addr;
+ ospf6_packet_add(on->ospf6_if, op);
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
+ } else
+ ospf6_packet_free(op);
if (on->lsupdate_list->count != 0) {
on->thread_send_lsupdate = NULL;
int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on,
struct ospf6_lsa *lsa)
{
- struct ospf6_header *oh;
- struct ospf6_lsupdate *lsupdate;
- uint8_t *p;
- int lsa_cnt = 0;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
- + sizeof(struct ospf6_header));
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s);
- p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
+ /* skip over fixed header */
+ stream_forward_endp(op->s, OSPF6_LS_UPD_MIN_SIZE);
ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
- memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
- p += OSPF6_LSA_SIZE(lsa->header);
- lsa_cnt++;
-
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
+ stream_put(op->s, lsa->header, OSPF6_LSA_SIZE(lsa->header));
+ length = OSPF6_HEADER_SIZE + OSPF6_LS_UPD_MIN_SIZE
+ + OSPF6_LSA_SIZE(lsa->header);
+ ospf6_fill_header(on->ospf6_if, op->s, length);
+ ospf6_fill_lsupdate_header(op->s, 1);
+ op->length = length;
if (IS_OSPF6_DEBUG_FLOODING
|| IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND_HDR))
zlog_debug("%s: Send lsupdate with lsa %s (age %u)", __func__,
lsa->name, ntohs(lsa->header->age));
- ospf6_send_lsupdate(on, NULL, oh);
+ ospf6_send_lsupdate(on, NULL, op);
return 0;
}
+static uint16_t ospf6_make_lsupdate_interface(struct ospf6_interface *oi,
+ struct ospf6_packet **op,
+ int *lsa_cnt)
+{
+ uint16_t length = OSPF6_LS_UPD_MIN_SIZE;
+ struct ospf6_lsa *lsa, *lsanext;
+
+ /* skip over fixed header */
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
+
+ for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) {
+ if (length + (unsigned int)OSPF6_LSA_SIZE(lsa->header)
+ + OSPF6_HEADER_SIZE
+ > ospf6_packet_max(oi)) {
+ ospf6_fill_header(oi, (*op)->s,
+ length + OSPF6_HEADER_SIZE);
+ (*op)->length = length + OSPF6_HEADER_SIZE;
+ ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt);
+ ospf6_send_lsupdate(NULL, oi, *op);
+
+ /* refresh packet */
+ *op = ospf6_packet_new(oi->ifmtu);
+ length = OSPF6_LS_UPD_MIN_SIZE;
+ *lsa_cnt = 0;
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi,
+ (*op)->s);
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
+ }
+
+ ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
+ stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header));
+ (*lsa_cnt)++;
+ length += OSPF6_LSA_SIZE(lsa->header);
+
+ assert(lsa->lock == 2);
+ ospf6_lsdb_remove(lsa, oi->lsupdate_list);
+ }
+ return length;
+}
+
int ospf6_lsupdate_send_interface(struct thread *thread)
{
struct ospf6_interface *oi;
- struct ospf6_header *oh;
- struct ospf6_lsupdate *lsupdate;
- uint8_t *p;
- int lsa_cnt;
- struct ospf6_lsa *lsa, *lsanext;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
+ int lsa_cnt = 0;
oi = (struct ospf6_interface *)THREAD_ARG(thread);
oi->thread_send_lsupdate = (struct thread *)NULL;
if (oi->lsupdate_list->count == 0)
return 0;
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
- + sizeof(struct ospf6_header));
-
- p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
- lsa_cnt = 0;
-
- for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) {
- /* MTU check */
- if ((p - sendbuf + ((unsigned int)OSPF6_LSA_SIZE(lsa->header)))
- > ospf6_packet_max(oi)) {
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
-
- ospf6_send_lsupdate(NULL, oi, oh);
- if (IS_OSPF6_DEBUG_MESSAGE(
- OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
- zlog_debug("%s: LSUpdate length %d",
- __func__, ntohs(oh->length));
-
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate
- *)((caddr_t)oh
- + sizeof(struct
- ospf6_header));
-
- p = (uint8_t *)((caddr_t)lsupdate
- + sizeof(struct
- ospf6_lsupdate));
- lsa_cnt = 0;
- }
- }
-
- ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
- memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
- p += OSPF6_LSA_SIZE(lsa->header);
- lsa_cnt++;
-
- assert(lsa->lock == 2);
- ospf6_lsdb_remove(lsa, oi->lsupdate_list);
- }
-
+ op = ospf6_packet_new(oi->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi, op->s);
+ length += ospf6_make_lsupdate_interface(oi, &op, &lsa_cnt);
if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
-
- ospf6_send_lsupdate(NULL, oi, oh);
- }
+ /* Fill OSPF header. */
+ ospf6_fill_header(oi, op->s, length);
+ ospf6_fill_lsupdate_header(op->s, lsa_cnt);
+ op->length = length;
+ ospf6_send_lsupdate(NULL, oi, op);
+ } else
+ ospf6_packet_free(op);
if (oi->lsupdate_list->count > 0) {
oi->thread_send_lsupdate = NULL;
int ospf6_lsack_send_neighbor(struct thread *thread)
{
struct ospf6_neighbor *on;
- struct ospf6_header *oh;
- uint8_t *p;
- struct ospf6_lsa *lsa, *lsanext;
- int lsa_cnt = 0;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
on = (struct ospf6_neighbor *)THREAD_ARG(thread);
on->thread_send_lsack = (struct thread *)NULL;
if (on->lsack_list->count == 0)
return 0;
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
-
- p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header));
-
- for (ALL_LSDB(on->lsack_list, lsa, lsanext)) {
- /* MTU check */
- if (p - sendbuf + sizeof(struct ospf6_lsa_header)
- > ospf6_packet_max(on->ospf6_if)) {
- /* if we run out of packet size/space here,
- better to try again soon. */
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSACK;
- oh->length = htons(p - sendbuf);
-
- on->ospf6_if->ls_ack_out++;
-
- ospf6_send(on->ospf6_if->linklocal_addr,
- &on->linklocal_addr, on->ospf6_if,
- oh);
-
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- p = (uint8_t *)((caddr_t)oh
- + sizeof(struct ospf6_header));
- lsa_cnt = 0;
- }
- }
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, on->ospf6_if, op->s);
- ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
- memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header));
- p += sizeof(struct ospf6_lsa_header);
+ length += ospf6_make_lsack_neighbor(on, &op);
- assert(lsa->lock == 2);
- ospf6_lsdb_remove(lsa, on->lsack_list);
- lsa_cnt++;
+ if (length == OSPF6_HEADER_SIZE) {
+ ospf6_packet_free(op);
+ return 0;
}
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSACK;
- oh->length = htons(p - sendbuf);
-
- on->ospf6_if->ls_ack_out++;
+ /* Fill OSPF header. */
+ ospf6_fill_header(on->ospf6_if, op->s, length);
- ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr,
- on->ospf6_if, oh);
- }
+ /* Set packet length, dst and queue to FIFO. */
+ op->length = length;
+ op->dst = on->linklocal_addr;
+ ospf6_packet_add(on->ospf6_if, op);
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
if (on->lsack_list->count > 0)
thread_add_event(master, ospf6_lsack_send_neighbor, on, 0,
return 0;
}
+static uint16_t ospf6_make_lsack_interface(struct ospf6_interface *oi,
+ struct ospf6_packet *op)
+{
+ uint16_t length = 0;
+ struct ospf6_lsa *lsa, *lsanext;
+
+ for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) {
+ if ((length + sizeof(struct ospf6_lsa_header)
+ + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(oi)) {
+ /* if we run out of packet size/space here,
+ better to try again soon. */
+ THREAD_OFF(oi->thread_send_lsack);
+ thread_add_event(master, ospf6_lsack_send_interface, oi,
+ 0, &oi->thread_send_lsack);
+
+ ospf6_lsa_unlock(lsa);
+ if (lsanext)
+ ospf6_lsa_unlock(lsanext);
+ break;
+ }
+ ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
+ stream_put(op->s, lsa->header, sizeof(struct ospf6_lsa_header));
+ length += sizeof(struct ospf6_lsa_header);
+
+ assert(lsa->lock == 2);
+ ospf6_lsdb_remove(lsa, oi->lsack_list);
+ }
+ return length;
+}
+
int ospf6_lsack_send_interface(struct thread *thread)
{
struct ospf6_interface *oi;
- struct ospf6_header *oh;
- uint8_t *p;
- struct ospf6_lsa *lsa, *lsanext;
- int lsa_cnt = 0;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
oi = (struct ospf6_interface *)THREAD_ARG(thread);
oi->thread_send_lsack = (struct thread *)NULL;
if (oi->lsack_list->count == 0)
return 0;
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
-
- p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header));
-
- for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) {
- /* MTU check */
- if (p - sendbuf + sizeof(struct ospf6_lsa_header)
- > ospf6_packet_max(oi)) {
- /* if we run out of packet size/space here,
- better to try again soon. */
- THREAD_OFF(oi->thread_send_lsack);
- thread_add_event(master, ospf6_lsack_send_interface, oi,
- 0, &oi->thread_send_lsack);
-
- ospf6_lsa_unlock(lsa);
- if (lsanext)
- ospf6_lsa_unlock(lsanext);
- break;
- }
+ op = ospf6_packet_new(oi->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, oi, op->s);
- ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
- memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header));
- p += sizeof(struct ospf6_lsa_header);
+ length += ospf6_make_lsack_interface(oi, op);
- assert(lsa->lock == 2);
- ospf6_lsdb_remove(lsa, oi->lsack_list);
- lsa_cnt++;
+ if (length == OSPF6_HEADER_SIZE) {
+ ospf6_packet_free(op);
+ return 0;
}
+ /* Fill OSPF header. */
+ ospf6_fill_header(oi, op->s, length);
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSACK;
- oh->length = htons(p - sendbuf);
+ /* Set packet length, dst and queue to FIFO. */
+ op->length = length;
+ if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT)
+ || (oi->state == OSPF6_INTERFACE_DR)
+ || (oi->state == OSPF6_INTERFACE_BDR))
+ op->dst = allspfrouters6;
+ else
+ op->dst = alldrouters6;
- if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT)
- || (oi->state == OSPF6_INTERFACE_DR)
- || (oi->state == OSPF6_INTERFACE_BDR))
- ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh);
- else
- ospf6_send(oi->linklocal_addr, &alldrouters6, oi, oh);
- }
+ ospf6_packet_add(oi, op);
+ OSPF6_MESSAGE_WRITE_ON(oi);
if (oi->lsack_list->count > 0)
thread_add_event(master, ospf6_lsack_send_interface, oi, 0,
return 0;
}
-
/* Commands */
DEFUN(debug_ospf6_message, debug_ospf6_message_cmd,
"debug ospf6 message <unknown|hello|dbdesc|lsreq|lsupdate|lsack|all> [<send|recv|send-hdr|recv-hdr>]",
#define OSPF6_MESSAGE_TYPE_LSACK 0x5 /* Flooding acknowledgment */
#define OSPF6_MESSAGE_TYPE_ALL 0x6 /* For debug option */
+struct ospf6_packet {
+ struct ospf6_packet *next;
+
+ /* Pointer to data stream. */
+ struct stream *s;
+
+ /* IP destination address. */
+ struct in6_addr dst;
+
+ /* OSPF6 packet length. */
+ uint16_t length;
+};
+
+/* OSPF packet queue structure. */
+struct ospf6_fifo {
+ unsigned long count;
+
+ struct ospf6_packet *head;
+ struct ospf6_packet *tail;
+};
+
/* OSPFv3 packet header */
#define OSPF6_HEADER_SIZE 16U
struct ospf6_header {
extern void ospf6_lsupdate_print(struct ospf6_header *, int action);
extern void ospf6_lsack_print(struct ospf6_header *, int action);
+extern struct ospf6_fifo *ospf6_fifo_new(void);
+extern void ospf6_fifo_flush(struct ospf6_fifo *fifo);
+extern void ospf6_fifo_free(struct ospf6_fifo *fifo);
+
extern int ospf6_iobuf_size(unsigned int size);
extern void ospf6_message_terminate(void);
extern int ospf6_receive(struct thread *thread);
THREAD_OFF(on->inactivity_timer);
+ THREAD_OFF(on->last_dbdesc_release_timer);
+
THREAD_OFF(on->thread_send_dbdesc);
THREAD_OFF(on->thread_send_lsreq);
THREAD_OFF(on->thread_send_lsupdate);
return 0;
}
+static int ospf6_neighbor_last_dbdesc_release(struct thread *thread)
+{
+ struct ospf6_neighbor *on = THREAD_ARG(thread);
+
+ assert(on);
+ memset(&on->dbdesc_last, 0, sizeof(struct ospf6_dbdesc));
+
+ return 0;
+}
+
int exchange_done(struct thread *thread)
{
struct ospf6_neighbor *on;
THREAD_OFF(on->thread_send_dbdesc);
ospf6_lsdb_remove_all(on->dbdesc_list);
- /* XXX
- thread_add_timer (master, ospf6_neighbor_last_dbdesc_release, on,
- on->ospf6_if->dead_interval);
- */
+ /* RFC 2328 (10.8): Release the last dbdesc after dead_interval */
+ if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) {
+ THREAD_OFF(on->last_dbdesc_release_timer);
+ thread_add_timer(master, ospf6_neighbor_last_dbdesc_release, on,
+ on->ospf6_if->dead_interval,
+ &on->last_dbdesc_release_timer);
+ }
if (on->request_list->count == 0)
ospf6_neighbor_state_change(OSPF6_NEIGHBOR_FULL, on,
}
}
-static void ospf6_neighbor_show_detail_common(struct vty *vty, int argc,
- struct cmd_token **argv,
- struct ospf6 *ospf6, int idx_type,
- int detail_idx, int json_idx)
+static void ospf6_neighbor_show_detail_common(struct vty *vty,
+ struct ospf6 *ospf6, bool uj,
+ bool detail, bool drchoice)
{
struct ospf6_neighbor *on;
struct ospf6_interface *oi;
struct listnode *i, *j, *k;
json_object *json = NULL;
json_object *json_array = NULL;
- bool uj = use_json(argc, argv);
void (*showfunc)(struct vty *, struct ospf6_neighbor *,
json_object *json, bool use_json);
- showfunc = ospf6_neighbor_show;
-
- if ((uj && argc == detail_idx) || (!uj && argc == json_idx)) {
- if (!strncmp(argv[idx_type]->arg, "de", 2))
- showfunc = ospf6_neighbor_show_detail;
- else if (!strncmp(argv[idx_type]->arg, "dr", 2))
- showfunc = ospf6_neighbor_show_drchoice;
- }
+ if (detail)
+ showfunc = ospf6_neighbor_show_detail;
+ else if (drchoice)
+ showfunc = ospf6_neighbor_show_drchoice;
+ else
+ showfunc = ospf6_neighbor_show;
if (uj) {
json = json_object_new_object();
"Display details\n"
"Display DR choices\n" JSON_STR)
{
- int idx_type = 4;
- int detail_idx = 5;
- int json_idx = 6;
struct ospf6 *ospf6;
struct listnode *node;
const char *vrf_name = NULL;
bool all_vrf = false;
int idx_vrf = 0;
+ int idx_type = 4;
+ bool uj = use_json(argc, argv);
+ bool detail = false;
+ bool drchoice = false;
OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
- if (idx_vrf > 0) {
- idx_type += 2;
- detail_idx += 2;
- json_idx += 2;
- }
+
+ if (argv_find(argv, argc, "detail", &idx_type))
+ detail = true;
+ else if (argv_find(argv, argc, "drchoice", &idx_type))
+ drchoice = true;
for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) {
if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) {
- ospf6_neighbor_show_detail_common(vty, argc, argv,
- ospf6, idx_type,
- detail_idx, json_idx);
+ ospf6_neighbor_show_detail_common(vty, ospf6, uj,
+ detail, drchoice);
if (!all_vrf)
break;
}
uint32_t state_change;
struct timeval last_changed;
+ /* last received hello */
+ struct timeval last_hello;
+ uint32_t hello_in;
+
/* Neighbor Router ID */
in_addr_t router_id;
/* Inactivity timer */
struct thread *inactivity_timer;
+ /* Timer to release the last dbdesc packet */
+ struct thread *last_dbdesc_release_timer;
+
/* Thread for sending message */
struct thread *thread_send_dbdesc;
struct thread *thread_send_lsreq;
rmsghdr.msg_control = (caddr_t)cmsgbuf;
rmsghdr.msg_controllen = sizeof(cmsgbuf);
- retval = recvmsg(ospf6_sock, &rmsghdr, 0);
- if (retval < 0)
- zlog_warn("recvmsg failed: %s", safe_strerror(errno));
- else if (retval == iov_totallen(message))
+ retval = recvmsg(ospf6_sock, &rmsghdr, MSG_DONTWAIT);
+ if (retval < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ zlog_warn("stream_recvmsg failed: %s",
+ safe_strerror(errno));
+ return retval;
+ } else if (retval == iov_totallen(message))
zlog_warn("recvmsg read full buffer size: %d", retval);
/* source address */
ifindex_t *ifindex, struct iovec *message,
int ospf6_sock);
+#define OSPF6_MESSAGE_WRITE_ON(oi) \
+ do { \
+ bool list_was_empty = \
+ list_isempty(oi->area->ospf6->oi_write_q); \
+ if ((oi)->on_write_q == 0) { \
+ listnode_add(oi->area->ospf6->oi_write_q, (oi)); \
+ (oi)->on_write_q = 1; \
+ } \
+ if (list_was_empty \
+ && !list_isempty(oi->area->ospf6->oi_write_q)) \
+ thread_add_write(master, ospf6_write, oi->area->ospf6, \
+ oi->area->ospf6->fd, \
+ &oi->area->ospf6->t_write); \
+ } while (0)
+
#endif /* OSPF6_NETWORK_H */
--- /dev/null
+/*
+ * OSPFv3 Not So Stubby Area implementation.
+ *
+ * Copyright (C) 2021 Kaushik Nath
+ * Copyright (C) 2021 Soman K.S
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <zebra.h>
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "linklist.h"
+#include "command.h"
+#include "thread.h"
+#include "plist.h"
+#include "filter.h"
+
+#include "ospf6_proto.h"
+#include "ospf6_route.h"
+#include "ospf6_lsa.h"
+#include "ospf6_route.h"
+#include "ospf6_lsdb.h"
+#include "ospf6_message.h"
+#include "ospf6_zebra.h"
+
+#include "ospf6_top.h"
+#include "ospf6_area.h"
+#include "ospf6_interface.h"
+#include "ospf6_neighbor.h"
+
+#include "ospf6_flood.h"
+#include "ospf6_intra.h"
+#include "ospf6_abr.h"
+#include "ospf6_asbr.h"
+#include "ospf6d.h"
+#include "ospf6_nssa.h"
+
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA");
+unsigned char config_debug_ospf6_nssa = 0;
+/* Determine whether this router is elected translator or not for area */
+static int ospf6_abr_nssa_am_elected(struct ospf6_area *oa)
+{
+ struct ospf6_lsa *lsa;
+ struct ospf6_router_lsa *router_lsa;
+ in_addr_t *best = NULL;
+ uint16_t type;
+
+ type = htons(OSPF6_LSTYPE_ROUTER);
+
+ /* Verify all the router LSA to compare the router ID */
+ for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) {
+
+ router_lsa = (struct ospf6_router_lsa
+ *)((caddr_t)lsa->header
+ + sizeof(struct ospf6_lsa_header));
+
+ /* ignore non-ABR routers */
+ if (!CHECK_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B))
+ continue;
+
+ /* Router has Nt flag - always translate */
+ if (CHECK_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: router %pI4 asserts Nt",
+ __func__, &lsa->header->id);
+ return 1;
+ }
+
+ if (best == NULL)
+ best = &lsa->header->adv_router;
+ else if (IPV4_ADDR_CMP(best, &lsa->header->adv_router) < 0)
+ best = &lsa->header->adv_router;
+ }
+
+ if (best == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: best electable ABR not found",
+ __func__);
+ return 0;
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: best electable ABR is: %pI4", __func__, best);
+
+ if (IPV4_ADDR_CMP(best, &oa->ospf6->router_id) <= 0) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: elected ABR is: %pI4", __func__, best);
+ return 1;
+ } else {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: not elected best %pI4, router ID %pI4",
+ __func__, best, &oa->ospf6->router_id);
+ return 0;
+ }
+}
+
+/* Flush the translated LSA when translation is disabled */
+static void ospf6_flush_translated_lsa(struct ospf6_area *area)
+{
+ uint16_t type;
+ struct ospf6_lsa *type7;
+ struct ospf6_lsa *type5;
+ struct ospf6 *ospf6 = area->ospf6;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: start area %s", __func__, area->name);
+
+ type = htons(OSPF6_LSTYPE_TYPE_7);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, type7)) {
+ type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ type7->external_lsa_id,
+ ospf6->router_id, ospf6->lsdb);
+ if (type5 && CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT))
+ ospf6_lsa_premature_aging(type5);
+ }
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: finish area %s", __func__, area->name);
+}
+
+/* Check NSSA status for all nssa areas*/
+void ospf6_abr_nssa_check_status(struct ospf6 *ospf6)
+{
+ struct ospf6_area *area;
+ struct listnode *lnode, *nnode;
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, nnode, area)) {
+ uint8_t old_state = area->NSSATranslatorState;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: checking area %s flag %x", __func__,
+ area->name, area->flag);
+
+ if (!IS_AREA_NSSA(area))
+ continue;
+
+ if (!CHECK_FLAG(area->ospf6->flag, OSPF6_FLAG_ABR)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: not ABR", __func__);
+ area->NSSATranslatorState =
+ OSPF6_NSSA_TRANSLATE_DISABLED;
+ ospf6_flush_translated_lsa(area);
+ } else {
+ /* Router is ABR */
+ if (area->NSSATranslatorRole == OSPF6_NSSA_ROLE_ALWAYS)
+ area->NSSATranslatorState =
+ OSPF6_NSSA_TRANSLATE_ENABLED;
+ else {
+ /* We are a candidate for Translation */
+ if (ospf6_abr_nssa_am_elected(area) > 0) {
+ area->NSSATranslatorState =
+ OSPF6_NSSA_TRANSLATE_ENABLED;
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s: elected translator",
+ __func__);
+ } else {
+ area->NSSATranslatorState =
+ OSPF6_NSSA_TRANSLATE_DISABLED;
+ ospf6_flush_translated_lsa(area);
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: not elected",
+ __func__);
+ }
+ }
+ }
+
+ /* RFC3101, 3.1:
+ * All NSSA border routers must set the E-bit in the Type-1
+ * router-LSAs of their directly attached non-stub areas, even
+ * when they are not translating.
+ */
+ if (old_state != area->NSSATranslatorState) {
+ if (old_state == OSPF6_NSSA_TRANSLATE_DISABLED)
+ ospf6_asbr_status_update(ospf6,
+ ++ospf6->redist_count);
+ else
+ ospf6_asbr_status_update(ospf6,
+ --ospf6->redist_count);
+ }
+ }
+}
+
+/* Mark the summary LSA's as unapproved, when ABR status changes.*/
+static void ospf6_abr_unapprove_summaries(struct ospf6 *ospf6)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_area *area;
+ struct ospf6_lsa *lsa;
+ uint16_t type;
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Start", __func__);
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : considering area %pI4", __func__,
+ &area->area_id);
+ /* Inter area router LSA */
+ type = htons(OSPF6_LSTYPE_INTER_ROUTER);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id,
+ lsa)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s : approved unset on summary link id %pI4",
+ __func__, &lsa->header->id);
+ SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED);
+ }
+ /* Inter area prefix LSA */
+ type = htons(OSPF6_LSTYPE_INTER_PREFIX);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id,
+ lsa)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s : approved unset on asbr-summary link id %pI4",
+ __func__, &lsa->header->id);
+ SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED);
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Stop", __func__);
+}
+
+/* Re-advertise inter-area router LSA's */
+void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6)
+{
+ struct ospf6_route *brouter;
+ struct listnode *node, *nnode;
+ struct ospf6_area *oa;
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("Re-examining Inter-Router prefixes");
+
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) {
+ for (brouter = ospf6_route_head(oa->ospf6->brouter_table);
+ brouter; brouter = ospf6_route_next(brouter))
+ ospf6_abr_originate_summary_to_area(brouter, oa);
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("Finished re-examining Inter-Router prefixes");
+}
+
+/* Advertise prefixes configured using area <area-id> range command */
+static void ospf6_abr_announce_aggregates(struct ospf6 *ospf6)
+{
+ struct ospf6_area *area;
+ struct ospf6_route *range;
+ struct listnode *node, *nnode;
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("ospf6_abr_announce_aggregates(): Start");
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) {
+ for (range = ospf6_route_head(area->range_table); range;
+ range = ospf6_route_next(range))
+ ospf6_abr_range_update(range, ospf6);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "ospf_abr_announce_aggregates(): looking at area %pI4",
+ &area->area_id);
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("ospf6_abr_announce_aggregates(): Stop");
+}
+
+/* Flush the summary LSA's which are not approved.*/
+void ospf6_abr_remove_unapproved_summaries(struct ospf6 *ospf6)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_area *area;
+ struct ospf6_lsa *lsa;
+ uint16_t type;
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Start", __func__);
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : looking at area %pI4", __func__,
+ &area->area_id);
+
+ /* Inter area router LSA */
+ type = htons(OSPF6_LSTYPE_INTER_ROUTER);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id,
+ lsa)) {
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) {
+ lsa->header->age = htons(OSPF_LSA_MAXAGE);
+ THREAD_OFF(lsa->refresh);
+ thread_execute(master, ospf6_lsa_expire, lsa,
+ 0);
+ }
+ }
+
+ /* Inter area prefix LSA */
+ type = htons(OSPF6_LSTYPE_INTER_PREFIX);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id,
+ lsa)) {
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) {
+ lsa->header->age = htons(OSPF_LSA_MAXAGE);
+ THREAD_OFF(lsa->refresh);
+ thread_execute(master, ospf6_lsa_expire, lsa,
+ 0);
+ }
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Stop", __func__);
+}
+
+/*
+ * This is the function taking care about ABR stuff, i.e.
+ * summary-LSA origination and flooding.
+ */
+static void ospf6_abr_task(struct ospf6 *ospf6)
+{
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Start", __func__);
+
+ if (ospf6->route_table == NULL || ospf6->brouter_table == NULL) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Routing tables are not yet ready",
+ __func__);
+ return;
+ }
+
+ ospf6_abr_unapprove_summaries(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : prepare aggregates", __func__);
+
+ ospf6_abr_range_reset_cost(ospf6);
+
+ if (IS_OSPF6_ABR(ospf6)) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : process network RT", __func__);
+ ospf6_abr_prefix_resummarize(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : process router RT", __func__);
+ ospf6_asbr_prefix_readvertise(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : announce aggregates", __func__);
+ ospf6_abr_announce_aggregates(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : announce stub defaults", __func__);
+ ospf6_abr_defaults_to_stub(ospf6);
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : remove unapproved summaries", __func__);
+ ospf6_abr_remove_unapproved_summaries(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : Stop", __func__);
+}
+
+/* For NSSA Translations
+ * Mark the translated LSA's as unapproved. */
+static void ospf6_abr_unapprove_translates(struct ospf6 *ospf6)
+{
+ struct ospf6_lsa *lsa;
+ uint16_t type;
+ struct ospf6_area *oa;
+ struct listnode *node;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_unapprove_translates(): Start");
+
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
+ for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) {
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT)) {
+ SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED);
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : approved unset on link id %pI4",
+ __func__, &lsa->header->id);
+ }
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_unapprove_translates(): Stop");
+}
+
+/* Generate the translated external lsa from NSSA lsa */
+static struct ospf6_lsa *ospf6_lsa_translated_nssa_new(struct ospf6_area *area,
+ struct ospf6_lsa *type7)
+{
+ char *buffer;
+ struct ospf6_lsa *lsa;
+ struct ospf6_as_external_lsa *ext, *extnew;
+ struct ospf6_lsa_header *lsa_header;
+ caddr_t old_ptr, new_ptr;
+ struct ospf6_as_external_lsa *nssa;
+ struct prefix prefix;
+ struct ospf6_route *match;
+ struct ospf6 *ospf6 = area->ospf6;
+ ptrdiff_t tag_offset = 0;
+ route_tag_t network_order;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Start", __func__);
+
+ if (area->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: Translation disabled for area %s",
+ __func__, area->name);
+ return NULL;
+ }
+
+ buffer = XCALLOC(MTYPE_OSPF6_LSA, OSPF6_MAX_LSASIZE);
+ lsa_header = (struct ospf6_lsa_header *)buffer;
+ extnew = (struct ospf6_as_external_lsa
+ *)((caddr_t)lsa_header
+ + sizeof(struct ospf6_lsa_header));
+ ext = (struct ospf6_as_external_lsa
+ *)((caddr_t)(type7->header)
+ + sizeof(struct ospf6_lsa_header));
+ old_ptr =
+ (caddr_t)((caddr_t)ext + sizeof(struct ospf6_as_external_lsa));
+ new_ptr = (caddr_t)((caddr_t)extnew
+ + sizeof(struct ospf6_as_external_lsa));
+
+ memcpy(extnew, ext, sizeof(struct ospf6_as_external_lsa));
+
+ /* find the translated Type-5 for this Type-7 */
+ nssa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ type7->header);
+
+ prefix.family = AF_INET6;
+ prefix.prefixlen = nssa->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, nssa, &nssa->prefix);
+
+ /* Find the LSA from the external route */
+ match = ospf6_route_lookup(&prefix, area->route_table);
+ if (match == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : no matching route %pFX", __func__,
+ &prefix);
+ return NULL;
+ }
+
+ /* set Prefix */
+ memcpy(new_ptr, old_ptr, OSPF6_PREFIX_SPACE(ext->prefix.prefix_length));
+ ospf6_prefix_apply_mask(&extnew->prefix);
+ new_ptr += OSPF6_PREFIX_SPACE(extnew->prefix.prefix_length);
+
+ tag_offset =
+ sizeof(*ext) + OSPF6_PREFIX_SPACE(ext->prefix.prefix_length);
+
+ /* Forwarding address */
+ if (CHECK_FLAG(ext->bits_metric, OSPF6_ASBR_BIT_F)) {
+ memcpy(new_ptr, (caddr_t)ext + tag_offset,
+ sizeof(struct in6_addr));
+ new_ptr += sizeof(struct in6_addr);
+ tag_offset += sizeof(struct in6_addr);
+ }
+ /* External Route Tag */
+ if (CHECK_FLAG(ext->bits_metric, OSPF6_ASBR_BIT_T)) {
+ memcpy(&network_order, (caddr_t)ext + tag_offset,
+ sizeof(network_order));
+ network_order = htonl(network_order);
+ memcpy(new_ptr, &network_order, sizeof(network_order));
+ new_ptr += sizeof(network_order);
+ }
+
+ /* Fill LSA Header */
+ lsa_header->age = 0;
+ lsa_header->type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ lsa_header->id = htonl(ospf6->external_id);
+ ospf6->external_id++;
+ lsa_header->adv_router = ospf6->router_id;
+ lsa_header->seqnum =
+ ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
+ lsa_header->adv_router, ospf6->lsdb);
+ lsa_header->length = htons((caddr_t)new_ptr - (caddr_t)lsa_header);
+ type7->external_lsa_id = lsa_header->id;
+
+ /* LSA checksum */
+ ospf6_lsa_checksum(lsa_header);
+
+ /* create LSA */
+ lsa = ospf6_lsa_create(lsa_header);
+
+ SET_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT);
+ UNSET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED);
+
+ /* Originate */
+ ospf6_lsa_originate_process(lsa, ospf6);
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: Originated type5 LSA id %pI4", __func__,
+ &lsa_header->id);
+ return lsa;
+}
+
+/* Delete LSA from retransmission list */
+static void ospf6_ls_retransmit_delete_nbr_as(struct ospf6 *ospf6,
+ struct ospf6_lsa *lsa)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_area *area;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : start lsa %s", __func__, lsa->name);
+
+ /*The function ospf6_flood_clear_area removes LSA from
+ * retransmit list.
+ */
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area))
+ ospf6_flood_clear_area(lsa, area);
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : finish lsa %s", __func__, lsa->name);
+}
+
+/* Refresh translated AS-external-LSA. */
+struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *area,
+ struct ospf6_lsa *type7,
+ struct ospf6_lsa *type5)
+{
+ struct ospf6_lsa *new = NULL;
+ struct ospf6_as_external_lsa *ext_lsa;
+ struct prefix prefix;
+ struct ospf6 *ospf6 = area->ospf6;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : start area %s", __func__, area->name);
+
+ /* Sanity checks. */
+ assert(type7);
+
+ /* Find the AS external LSA */
+ if (type5 == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s: No translated Type-5 found for Type-7 with Id %pI4",
+ __func__, &type7->header->id);
+
+ /* find the translated Type-5 for this Type-7 */
+ ext_lsa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ type7->header);
+
+ prefix.family = AF_INET6;
+ prefix.prefixlen = ext_lsa->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, ext_lsa,
+ &ext_lsa->prefix);
+
+ /* Find the AS external LSA from Type-7 LSA */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: try to find external LSA id %d",
+ __func__, type7->external_lsa_id);
+ type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ type7->external_lsa_id,
+ ospf6->router_id, ospf6->lsdb);
+ }
+
+ if (type5) {
+ if (CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT)) {
+ /* Delete LSA from neighbor retransmit-list. */
+ ospf6_ls_retransmit_delete_nbr_as(ospf6, type5);
+
+ /* Flush the LSA */
+ ospf6_lsa_premature_aging(type5);
+ } else {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: Invalid translated LSA %s",
+ __func__, type5->name);
+ return NULL;
+ }
+ }
+
+ /* create new translated LSA */
+ if (ospf6_lsa_age_current(type7) != OSPF_LSA_MAXAGE) {
+ if ((new = ospf6_lsa_translated_nssa_new(area, type7))
+ == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s: Could not translate Type-7 for %pI4",
+ __func__, &type7->header->id);
+ return NULL;
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: finish", __func__);
+
+ return new;
+}
+
+/* Originate Translated Type-5 for supplied Type-7 NSSA LSA */
+struct ospf6_lsa *ospf6_translated_nssa_originate(struct ospf6_area *oa,
+ struct ospf6_lsa *type7)
+{
+ struct ospf6_lsa *new;
+
+ if (ntohs(type7->header->type) != OSPF6_LSTYPE_TYPE_7)
+ return NULL;
+
+ if ((new = ospf6_lsa_translated_nssa_new(oa, type7)) == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : Could not translate Type-7, Id %pI4, to Type-5",
+ __func__, &type7->header->id);
+ return NULL;
+ }
+
+ return new;
+}
+
+int ospf6_abr_translate_nssa(struct ospf6_area *area, struct ospf6_lsa *lsa)
+{
+ /* Incoming Type-7 or later aggregated Type-7
+ *
+ * LSA is skipped if P-bit is off.
+ * LSA is aggregated if within range.
+ *
+ * The Type-7 is translated, Installed/Approved as a Type-5 into
+ * global LSDB, then Flooded through AS
+ *
+ * Later, any Unapproved Translated Type-5's are flushed/discarded
+ */
+
+ struct ospf6_lsa *old = NULL, *new = NULL;
+ struct ospf6_as_external_lsa *nssa_lsa;
+ struct prefix prefix;
+ struct ospf6_route *match;
+ struct ospf6 *ospf6;
+
+ ospf6 = area->ospf6;
+ nssa_lsa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ lsa->header);
+
+ if (!CHECK_FLAG(nssa_lsa->prefix.prefix_options,
+ OSPF6_PREFIX_OPTION_P)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : LSA Id %pI4, P-bit off, NO Translation",
+ __func__, &lsa->header->id);
+ return 1;
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : LSA Id %pI4 external ID %pI4, Translating type 7 to 5",
+ __func__, &lsa->header->id, &lsa->external_lsa_id);
+
+ prefix.family = AF_INET6;
+ prefix.prefixlen = nssa_lsa->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, nssa_lsa, &nssa_lsa->prefix);
+
+ if (!CHECK_FLAG(nssa_lsa->bits_metric, OSPF6_ASBR_BIT_F)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : LSA Id %pI4, Forward address is 0, NO Translation",
+ __func__, &lsa->header->id);
+ return 1;
+ }
+
+ /* Find the existing AS-External LSA for this prefix */
+ match = ospf6_route_lookup(&prefix, ospf6->external_table);
+ if (match) {
+ old = ospf6_lsdb_lookup(OSPF6_LSTYPE_AS_EXTERNAL,
+ match->path.origin.id, ospf6->router_id,
+ ospf6->lsdb);
+ }
+
+ /* Check Type 5 LSA using the matching external ID */
+ if (old == NULL) {
+ old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ lsa->external_lsa_id, ospf6->router_id,
+ ospf6->lsdb);
+ }
+
+ if (old) {
+ /* Do not continue if type 5 LSA not approved */
+ if (CHECK_FLAG(old->flag, OSPF6_LSA_UNAPPROVED)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : LSA Id %pI4 type 5 is not approved",
+ __func__, &old->header->id);
+ return 1;
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : found old translated LSA Id %pI4, refreshing",
+ __func__, &old->header->id);
+
+ /* refresh */
+ new = ospf6_translated_nssa_refresh(area, lsa, old);
+ if (!new) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : could not refresh translated LSA Id %pI4",
+ __func__, &old->header->id);
+ }
+ } else {
+ /* no existing external route for this LSA Id
+ * originate translated LSA
+ */
+
+ if (ospf6_translated_nssa_originate(area, lsa) == NULL) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "%s : Could not translate Type-7 for %pI4 to Type-5",
+ __func__, &lsa->header->id);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void ospf6_abr_process_nssa_translates(struct ospf6 *ospf6)
+{
+ /* Scan through all NSSA_LSDB records for all areas;
+ * If P-bit is on, translate all Type-7's to 5's and aggregate or\
+ * flood install as approved in Type-5 LSDB with XLATE Flag on
+ * later, do same for all aggregates... At end, DISCARD all
+ * remaining UNAPPROVED Type-5's (Aggregate is for future ) */
+
+ struct listnode *node;
+ struct ospf6_area *oa;
+ struct ospf6_lsa *lsa;
+ int type;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Start", __func__);
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
+
+ /* skip if not translator */
+ if (oa->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED) {
+ zlog_debug("%s area %pI4 NSSATranslatorState %d",
+ __func__, &oa->area_id,
+ oa->NSSATranslatorState);
+ continue;
+ }
+
+ /* skip if not Nssa Area */
+ if (!IS_AREA_NSSA(oa)) {
+ zlog_debug("%s area %pI4 Flag %x", __func__,
+ &oa->area_id, oa->flag);
+ continue;
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : looking at area %pI4", __func__,
+ &oa->area_id);
+
+ type = htons(OSPF6_LSTYPE_TYPE_7);
+ for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) {
+ zlog_debug("%s : lsa %s , id %pI4 , adv router %pI4",
+ lsa->name, __func__, &lsa->header->id,
+ &lsa->header->adv_router);
+ ospf6_abr_translate_nssa(oa, lsa);
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Stop", __func__);
+}
+
+/* Generate translated type-5 LSA from the configured area ranges*/
+static void ospf6_abr_translate_nssa_range(struct ospf6 *ospf6)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_area *oa;
+ struct ospf6_route *range;
+ struct ospf6_lsa *lsa;
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) {
+ for (range = ospf6_route_head(oa->range_table); range;
+ range = ospf6_route_next(range)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "Translating range %pFX of area %pI4",
+ &range->prefix, &oa->area_id);
+ if (CHECK_FLAG(range->flag,
+ OSPF6_ROUTE_DO_NOT_ADVERTISE))
+ continue;
+
+ /* Find the NSSA LSA from the route */
+ /* Generate and flood external LSA */
+ lsa = ospf6_lsdb_lookup(OSPF6_LSTYPE_TYPE_7,
+ range->path.origin.id,
+ ospf6->router_id, oa->lsdb);
+ if (lsa)
+ ospf6_abr_translate_nssa(oa, lsa);
+ }
+ }
+}
+
+static void ospf6_abr_send_nssa_aggregates(struct ospf6 *ospf6)
+{
+ struct listnode *node;
+ struct ospf6_area *area;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Start", __func__);
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) {
+ if (area->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED)
+ continue;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : looking at area %pI4", __func__,
+ &area->area_id);
+
+ ospf6_abr_translate_nssa_range(ospf6);
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Stop", __func__);
+}
+
+/*Flood max age LSA's for the unapproved LSA's */
+static int ospf6_abr_remove_unapproved_translates_apply(struct ospf6_lsa *lsa)
+{
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT)
+ && CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) {
+ zlog_debug("%s : removing unapproved translates, lsa : %s",
+ __func__, lsa->name);
+
+ /* FLUSH THROUGHOUT AS */
+ ospf6_lsa_premature_aging(lsa);
+ }
+ return 0;
+}
+
+static void ospf6_abr_remove_unapproved_translates(struct ospf6 *ospf6)
+{
+ struct ospf6_lsa *lsa;
+ uint16_t type;
+
+ /* All AREA PROCESS should have APPROVED necessary LSAs */
+ /* Remove any left over and not APPROVED */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_remove_unapproved_translates(): Start");
+
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ for (ALL_LSDB_TYPED(ospf6->lsdb, type, lsa))
+ ospf6_abr_remove_unapproved_translates_apply(lsa);
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf_abr_remove_unapproved_translates(): Stop");
+}
+
+static void ospf6_abr_nssa_task(struct ospf6 *ospf6)
+{
+ /* called only if any_nssa */
+ struct ospf6_route *range;
+ struct ospf6_area *area;
+ struct listnode *node, *nnode;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("Check for NSSA-ABR Tasks():");
+
+ if (!IS_OSPF6_ABR(ospf6)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s Not ABR", __func__);
+ return;
+ }
+
+ if (!ospf6->anyNSSA) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s Not NSSA", __func__);
+ return;
+ }
+
+ /* Each area must confirm TranslatorRole */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): Start");
+
+ /* For all Global Entries flagged "local-translate", unset APPROVED */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): unapprove translates");
+
+ ospf6_abr_unapprove_translates(ospf6);
+
+ /* RESET all Ranges in every Area, same as summaries */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): NSSA initialize aggregates");
+ ospf6_abr_range_reset_cost(ospf6);
+
+ /* For all NSSAs, Type-7s, translate to 5's, INSTALL/FLOOD, or
+ * Aggregate as Type-7
+ * Install or Approve in Type-5 Global LSDB
+ */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): process translates");
+ ospf6_abr_process_nssa_translates(ospf6);
+
+ /* Translate/Send any "ranged" aggregates, and also 5-Install and
+ * Approve
+ * Scan Type-7's for aggregates, translate to Type-5's,
+ * Install/Flood/Approve
+ */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): send NSSA aggregates");
+ ospf6_abr_send_nssa_aggregates(ospf6); /*TURNED OFF FOR NOW */
+
+ /* Flush any unapproved previous translates from Global Data Base */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "ospf6_abr_nssa_task(): remove unapproved translates");
+ ospf6_abr_remove_unapproved_translates(ospf6);
+
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) {
+ for (range = ospf6_route_head(area->range_table); range;
+ range = ospf6_route_next(range)) {
+ if (CHECK_FLAG(range->flag,
+ OSPF6_ROUTE_DO_NOT_ADVERTISE))
+ ospf6_zebra_delete_discard(range, ospf6);
+ else
+ ospf6_zebra_add_discard(range, ospf6);
+ }
+ }
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("ospf6_abr_nssa_task(): Stop");
+}
+
+int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route,
+ int type)
+{
+ route_map_result_t ret;
+ struct prefix *prefix;
+ struct ospf6_redist *red;
+
+ if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id))
+ return 0;
+
+ prefix = &route->prefix;
+
+ red = ospf6_redist_lookup(ospf6, type, 0);
+ if (!red)
+ return 0;
+
+ /* Change to new redist structure */
+ if (ROUTEMAP_NAME(red)) {
+ if (ROUTEMAP(red) == NULL)
+ ospf6_asbr_routemap_update(NULL);
+ if (ROUTEMAP(red) == NULL) {
+ zlog_warn(
+ "route-map \"%s\" not found, suppress redistributing",
+ ROUTEMAP_NAME(red));
+ return 0;
+ }
+ }
+
+ /* Change to new redist structure */
+ if (ROUTEMAP(red)) {
+ ret = route_map_apply(ROUTEMAP(red), prefix, route);
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("Denied by route-map \"%s\"",
+ ROUTEMAP_NAME(red));
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void ospf6_external_lsa_refresh_type(struct ospf6 *ospf6, uint8_t type,
+ unsigned short instance, int force)
+{
+ struct ospf6_route *route;
+ struct ospf6_external_info *info;
+ struct ospf6_lsa *lsa;
+
+ if (type == ZEBRA_ROUTE_MAX)
+ return;
+
+ for (route = ospf6_route_head(ospf6->external_table); route;
+ route = ospf6_route_next(route)) {
+ info = route->route_option;
+
+ /* Find the external LSA in the database */
+ if (!is_default_prefix(&route->prefix)) {
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id),
+ ospf6->router_id, ospf6->lsdb);
+
+ if (lsa) {
+ THREAD_OFF(lsa->refresh);
+
+ /* LSA is maxage, immediate refresh */
+ if (OSPF6_LSA_IS_MAXAGE(lsa))
+ ospf6_flood(NULL, lsa);
+ else
+ thread_add_timer(master,
+ ospf6_lsa_refresh, lsa,
+ OSPF_LS_REFRESH_TIME,
+ &lsa->refresh);
+ } else {
+ /* LSA not found in the database
+ * Verify and originate external LSA
+ */
+ if (ospf6_redistribute_check(ospf6, route,
+ type))
+ ospf6_as_external_lsa_originate(route,
+ ospf6);
+ }
+ }
+ }
+}
+
+/* Refresh default route */
+static void ospf6_external_lsa_refresh_default(struct ospf6 *ospf6)
+{
+ struct ospf6_route *route;
+ struct ospf6_external_info *info;
+ struct ospf6_lsa *lsa;
+
+ for (route = ospf6_route_head(ospf6->external_table); route;
+ route = ospf6_route_next(route)) {
+ if (is_default_prefix(&route->prefix)) {
+ info = route->route_option;
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ htonl(info->id),
+ ospf6->router_id, ospf6->lsdb);
+
+ if (lsa) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "LSA[Type5:0.0.0.0]: Refresh AS-external-LSA %p",
+ (void *)lsa);
+ if (OSPF6_LSA_IS_MAXAGE(lsa))
+ ospf6_flood(NULL, lsa);
+ else
+ thread_add_timer(master,
+ ospf6_lsa_refresh, lsa,
+ OSPF_LS_REFRESH_TIME,
+ &lsa->refresh);
+ } else if (!lsa) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "LSA[Type5:0.0.0.0]: Originate AS-external-LSA");
+ ospf6_as_external_lsa_originate(route, ospf6);
+ }
+ }
+ }
+}
+
+/* If there's redistribution configured, we need to refresh external
+ * LSAs in order to install Type-7 and flood to all NSSA Areas
+ */
+void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6)
+{
+ int type;
+ struct ospf6_redist *red;
+
+ for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
+ red = ospf6_redist_lookup(ospf6, type, 0);
+ if (!red)
+ return;
+
+ ospf6_external_lsa_refresh_type(ospf6, type, red->instance,
+ LSA_REFRESH_IF_CHANGED);
+ }
+ ospf6_external_lsa_refresh_default(ospf6);
+}
+
+/* This function performs ABR related processing */
+static int ospf6_abr_task_timer(struct thread *thread)
+{
+ struct ospf6 *ospf6 = THREAD_ARG(thread);
+
+ ospf6->t_abr_task = NULL;
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("Running ABR task on timer");
+
+ (void)ospf6_check_and_set_router_abr(ospf6);
+ ospf6_abr_nssa_check_status(ospf6);
+ ospf6_abr_task(ospf6);
+ /* if nssa-abr, then scan Type-7 LSDB */
+ ospf6_abr_nssa_task(ospf6);
+ ospf6_asbr_nssa_redist_task(ospf6);
+
+ return 0;
+}
+
+void ospf6_schedule_abr_task(struct ospf6 *ospf6)
+{
+ if (ospf6->t_abr_task) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("ABR task already scheduled");
+ return;
+ }
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("Scheduling ABR task");
+
+ thread_add_timer(master, ospf6_abr_task_timer, ospf6,
+ OSPF6_ABR_TASK_DELAY, &ospf6->t_abr_task);
+}
+
+/* Flush the NSSA LSAs from the area */
+static void ospf6_nssa_flush_area(struct ospf6_area *area)
+{
+ uint16_t type;
+ struct ospf6_lsa *lsa = NULL, *type5 = NULL;
+ struct ospf6 *ospf6 = area->ospf6;
+ const struct route_node *rt = NULL;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s: area %s", __func__, area->name);
+
+ /* Flush the NSSA LSA */
+ type = htons(OSPF6_LSTYPE_TYPE_7);
+ rt = ospf6_lsdb_head(area->lsdb_self, 0, type, ospf6->router_id, &lsa);
+ while (lsa) {
+ lsa->header->age = htons(OSPF_LSA_MAXAGE);
+ SET_FLAG(lsa->flag, OSPF6_LSA_FLUSH);
+ ospf6_flood(NULL, lsa);
+ /* Flush the translated LSA */
+ if (ospf6_check_and_set_router_abr(ospf6)) {
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ type5 = ospf6_lsdb_lookup(
+ htons(type), lsa->external_lsa_id,
+ ospf6->router_id, ospf6->lsdb);
+ if (type5
+ && CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT)) {
+ type5->header->age = htons(OSPF_LSA_MAXAGE);
+ SET_FLAG(type5->flag, OSPF6_LSA_FLUSH);
+ ospf6_flood(NULL, type5);
+ }
+ }
+ lsa = ospf6_lsdb_next(rt, lsa);
+ }
+}
+
+static void ospf6_area_nssa_update(struct ospf6_area *area)
+{
+ struct ospf6_route *route;
+
+ if (IS_AREA_NSSA(area)) {
+ if (!ospf6_check_and_set_router_abr(area->ospf6))
+ OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_E);
+ area->ospf6->anyNSSA++;
+ OSPF6_OPT_SET(area->options, OSPF6_OPT_N);
+ area->NSSATranslatorRole = OSPF6_NSSA_ROLE_CANDIDATE;
+ } else if (IS_AREA_ENABLED(area)) {
+ if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER))
+ zlog_debug("Normal area for if %s", area->name);
+ OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_N);
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ OSPF6_OPT_SET(area->options, OSPF6_OPT_E);
+ area->ospf6->anyNSSA--;
+ area->NSSATranslatorState = OSPF6_NSSA_TRANSLATE_DISABLED;
+ }
+
+ /* Refresh router LSA */
+ if (IS_AREA_NSSA(area)) {
+ OSPF6_ROUTER_LSA_SCHEDULE(area);
+
+ /* Check if router is ABR */
+ if (ospf6_check_and_set_router_abr(area->ospf6)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("Router is ABR area %s", area->name);
+ ospf6_schedule_abr_task(area->ospf6);
+ } else {
+ /* Router is not ABR */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("NSSA area %s", area->name);
+
+ /* Originate NSSA LSA */
+ for (route = ospf6_route_head(
+ area->ospf6->external_table);
+ route; route = ospf6_route_next(route))
+ ospf6_nssa_lsa_originate(route, area);
+ }
+ } else {
+ /* Disable NSSA */
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("Normal area %s", area->name);
+ ospf6_nssa_flush_area(area);
+ ospf6_area_disable(area);
+ ospf6_area_delete(area);
+ }
+}
+
+int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area)
+{
+
+ if (!IS_AREA_NSSA(area)) {
+ SET_FLAG(area->flag, OSPF6_AREA_NSSA);
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("area %s nssa set", area->name);
+ ospf6_area_nssa_update(area);
+ }
+
+ return 1;
+}
+
+int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area)
+{
+ if (IS_AREA_NSSA(area)) {
+ UNSET_FLAG(area->flag, OSPF6_AREA_NSSA);
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("area %s nssa reset", area->name);
+ ospf6_area_nssa_update(area);
+ }
+
+ return 1;
+}
+
+/* Find the NSSA forwarding address */
+static struct in6_addr *ospf6_get_nssa_fwd_addr(struct ospf6_area *oa)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_interface *oi;
+
+ for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) {
+ if (if_is_operative(oi->interface))
+ if (oi->area && IS_AREA_NSSA(oi->area))
+ return ospf6_interface_get_global_address(
+ oi->interface);
+ }
+ return NULL;
+}
+
+void ospf6_nssa_lsa_originate(struct ospf6_route *route,
+ struct ospf6_area *area)
+{
+ char buffer[OSPF6_MAX_LSASIZE];
+ struct ospf6_lsa_header *lsa_header;
+ struct ospf6_lsa *lsa;
+ struct ospf6_external_info *info = route->route_option;
+ struct in6_addr *fwd_addr;
+
+ struct ospf6_as_external_lsa *as_external_lsa;
+ char buf[PREFIX2STR_BUFFER];
+ caddr_t p;
+
+ if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL)) {
+ prefix2str(&route->prefix, buf, sizeof(buf));
+ zlog_debug("Originate AS-External-LSA for %s", buf);
+ }
+
+ /* prepare buffer */
+ memset(buffer, 0, sizeof(buffer));
+ lsa_header = (struct ospf6_lsa_header *)buffer;
+ as_external_lsa = (struct ospf6_as_external_lsa
+ *)((caddr_t)lsa_header
+ + sizeof(struct ospf6_lsa_header));
+ p = (caddr_t)((caddr_t)as_external_lsa
+ + sizeof(struct ospf6_as_external_lsa));
+
+ /* Fill AS-External-LSA */
+ /* Metric type */
+ if (route->path.metric_type == OSPF6_PATH_TYPE_EXTERNAL2)
+ SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
+ else
+ UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
+
+ /* external route tag */
+ if (info->tag)
+ SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
+ else
+ UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
+
+ /* Set metric */
+ OSPF6_ASBR_METRIC_SET(as_external_lsa, route->path.cost);
+
+ /* prefixlen */
+ as_external_lsa->prefix.prefix_length = route->prefix.prefixlen;
+
+ /* PrefixOptions */
+ as_external_lsa->prefix.prefix_options = route->path.prefix_options;
+
+ /* Set the P bit */
+ as_external_lsa->prefix.prefix_options |= OSPF6_PREFIX_OPTION_P;
+
+ /* don't use refer LS-type */
+ as_external_lsa->prefix.prefix_refer_lstype = htons(0);
+
+ /* set Prefix */
+ memcpy(p, &route->prefix.u.prefix6,
+ OSPF6_PREFIX_SPACE(route->prefix.prefixlen));
+ ospf6_prefix_apply_mask(&as_external_lsa->prefix);
+ p += OSPF6_PREFIX_SPACE(route->prefix.prefixlen);
+
+ /* Forwarding address */
+ fwd_addr = ospf6_get_nssa_fwd_addr(area);
+ if (fwd_addr) {
+ memcpy(p, fwd_addr, sizeof(struct in6_addr));
+ p += sizeof(struct in6_addr);
+ SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F);
+ } else
+ UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F);
+
+ /* External Route Tag */
+ if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) {
+ route_tag_t network_order = htonl(info->tag);
+
+ memcpy(p, &network_order, sizeof(network_order));
+ p += sizeof(network_order);
+ }
+
+ /* Fill LSA Header */
+ lsa_header->age = 0;
+ lsa_header->type = htons(OSPF6_LSTYPE_TYPE_7);
+ lsa_header->id = route->path.origin.id;
+ lsa_header->adv_router = area->ospf6->router_id;
+ lsa_header->seqnum =
+ ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
+ lsa_header->adv_router, area->ospf6->lsdb);
+ lsa_header->length = htons((caddr_t)p - (caddr_t)lsa_header);
+
+ /* LSA checksum */
+ ospf6_lsa_checksum(lsa_header);
+ /* create LSA */
+ lsa = ospf6_lsa_create(lsa_header);
+
+ /* Originate */
+ ospf6_lsa_originate_area(lsa, area);
+}
+
+void ospf6_abr_check_translate_nssa(struct ospf6_area *area,
+ struct ospf6_lsa *lsa)
+{
+ struct ospf6_lsa *type5 = NULL;
+ struct ospf6 *ospf6 = area->ospf6;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : start", __func__);
+
+ type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL),
+ lsa->external_lsa_id, ospf6->router_id,
+ ospf6->lsdb);
+
+ if (ospf6_check_and_set_router_abr(ospf6) && (type5 == NULL)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("%s : Originating type5 LSA", __func__);
+ ospf6_lsa_translated_nssa_new(area, lsa);
+ }
+}
+
+DEFUN(debug_ospf6_nssa, debug_ospf6_nssa_cmd,
+ "debug ospf6 nssa",
+ DEBUG_STR
+ OSPF6_STR
+ "Debug OSPFv3 NSSA function\n")
+{
+ OSPF6_DEBUG_NSSA_ON();
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_debug_ospf6_nssa, no_debug_ospf6_nssa_cmd,
+ "no debug ospf6 nssa",
+ NO_STR
+ DEBUG_STR
+ OSPF6_STR
+ "Debug OSPFv3 NSSA function\n")
+{
+ OSPF6_DEBUG_NSSA_OFF();
+ return CMD_SUCCESS;
+}
+
+void config_write_ospf6_debug_nssa(struct vty *vty)
+{
+ if (IS_OSPF6_DEBUG_NSSA)
+ vty_out(vty, "debug ospf6 nssa\n");
+}
+
+void install_element_ospf6_debug_nssa(void)
+{
+ install_element(ENABLE_NODE, &debug_ospf6_nssa_cmd);
+ install_element(ENABLE_NODE, &no_debug_ospf6_nssa_cmd);
+ install_element(CONFIG_NODE, &debug_ospf6_nssa_cmd);
+ install_element(CONFIG_NODE, &no_debug_ospf6_nssa_cmd);
+}
--- /dev/null
+/*
+ * OSPFv3 Not So Stubby Area implementation.
+ *
+ * Copyright (C) 2021 Kaushik Nath
+ * Copyright (C) 2021 Soman K.S
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef OSPF6_NSSA_H
+#define OSPF6_NSSA_H
+
+#define OSPF6_OPTION_NP 0x08
+#define OSPF6_LS_INFINITY 0xffffff
+
+#define OSPF6_OPT_N (1 << 3) /* Handling Type-7 LSA Capability */
+
+#define OSPF6_DEBUG_NSSA 0x09
+/* Debug option */
+extern unsigned char config_debug_ospf6_nssa;
+
+#define OSPF6_DEBUG_NSSA_ON() (config_debug_ospf6_nssa = 1)
+#define OSPF6_DEBUG_NSSA_OFF() (config_debug_ospf6_nssa = 0)
+#define IS_OSPF6_DEBUG_NSSA config_debug_ospf6_nssa
+
+#define CHECK_LSA_TYPE_1_TO_5_OR_7(type) \
+ ((type == OSPF6_ROUTER_LSA_MIN_SIZE) \
+ || (type == OSPF6_NETWORK_LSA_MIN_SIZE) \
+ || (type == OSPF6_LINK_LSA_MIN_SIZE) \
+ || (type == OSPF6_INTRA_PREFIX_LSA_MIN_SIZE) \
+ || (type == OSPF6_AS_NSSA_LSA))
+
+#define OSPF6_LSA_APPROVED 0x08
+#define OSPF6_LSA_LOCAL_XLT 0x40
+
+#define OSPF6_ABR_TASK_DELAY 7
+
+int ospf6_area_nssa_no_summary_set(struct ospf6 *ospf6, struct in_addr area_id);
+int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area);
+int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area);
+
+extern void ospf6_nssa_lsa_flush(struct ospf6 *ospf6, struct prefix_ipv6 *p);
+extern struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *,
+ struct ospf6_lsa *,
+ struct ospf6_lsa *);
+extern struct ospf6_lsa *ospf6_translated_nssa_originate(struct ospf6_area *,
+ struct ospf6_lsa *);
+
+extern void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6);
+
+extern void ospf6_schedule_abr_task(struct ospf6 *ospf6);
+void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6);
+extern void ospf6_nssa_lsa_originate(struct ospf6_route *route,
+ struct ospf6_area *area);
+extern void install_element_ospf6_debug_nssa(void);
+int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route,
+ int type);
+extern int ospf6_abr_translate_nssa(struct ospf6_area *area,
+ struct ospf6_lsa *lsa);
+extern void ospf6_abr_check_translate_nssa(struct ospf6_area *area,
+ struct ospf6_lsa *lsa);
+extern void ospf6_abr_nssa_check_status(struct ospf6 *ospf6);
+extern void config_write_ospf6_debug_nssa(struct vty *vty);
+#endif /* OSPF6_NSSA_H */
#define OSPF6_ROUTER_BIT_V (1 << 2)
#define OSPF6_ROUTER_BIT_E (1 << 1)
#define OSPF6_ROUTER_BIT_B (1 << 0)
+#define OSPF6_ROUTER_BIT_NT (1 << 4)
+
/* OSPF options */
/* present in HELLO, DD, LSA */
if (node->info == old) {
node->info = route;
SET_FLAG(route->flag, OSPF6_ROUTE_BEST);
+ if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
+ zlog_debug("%s: replace old route %s",
+ __func__, buf);
}
if (old->prev)
UNSET_FLAG(next->flag, OSPF6_ROUTE_BEST);
SET_FLAG(route->flag, OSPF6_ROUTE_BEST);
if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
- zlog_info(
+ zlog_debug(
"%s %p: route add %p cost %u: replacing previous best: %p cost %u",
ospf6_route_table_name(table),
(void *)table, (void *)route,
- route->path.cost,
- (void *)next, next->path.cost);
+ route->path.cost, (void *)next,
+ next->path.cost);
}
route->installed = now;
if (route->next && route->next->rnode == node) {
node->info = route->next;
SET_FLAG(route->next->flag, OSPF6_ROUTE_BEST);
+ if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
+ zlog_debug("%s: remove route %s", __func__,
+ buf);
} else {
node->info = NULL;
route->rnode = NULL;
return SNMP_INTEGER(3);
case OSPFv3AREABDRRTRSTATUS:
if (ospf6)
- return SNMP_INTEGER(ospf6_is_router_abr(ospf6)
- ? SNMP_TRUE
- : SNMP_FALSE);
+ return SNMP_INTEGER(
+ ospf6_check_and_set_router_abr(ospf6)
+ ? SNMP_TRUE
+ : SNMP_FALSE);
return SNMP_INTEGER(SNMP_FALSE);
case OSPFv3ASBDRRTRSTATUS:
if (ospf6)
#include "ospf6_area.h"
#include "ospf6_proto.h"
#include "ospf6_abr.h"
+#include "ospf6_asbr.h"
#include "ospf6_spf.h"
#include "ospf6_intra.h"
#include "ospf6_interface.h"
#include "ospf6d.h"
#include "ospf6_abr.h"
+#include "ospf6_nssa.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex");
oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id);
if (oi == NULL) {
- if (IS_OSPF6_DEBUG_SPF(PROCESS))
- zlog_debug("Can't find interface in SPF: ifindex %d",
- ifindex);
+ zlog_warn("Can't find interface in SPF: ifindex %d", ifindex);
return;
}
/* construct root vertex */
lsa = ospf6_create_single_router_lsa(oa, oa->lsdb_self, router_id);
if (lsa == NULL) {
- if (IS_OSPF6_DEBUG_SPF(PROCESS))
- zlog_debug("%s: No router LSA for area %s", __func__,
- oa->name);
+ zlog_warn("%s: No router LSA for area %s", __func__, oa->name);
return;
}
monotime(&start);
ospf6->ts_spf = start;
- if (ospf6_is_router_abr(ospf6))
+ if (ospf6_check_and_set_router_abr(ospf6))
ospf6_abr_range_reset_cost(ospf6);
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
areas_processed++;
}
- if (ospf6_is_router_abr(ospf6))
+ /* External LSA calculation */
+ ospf6_ase_calculate_timer_add(ospf6);
+
+ if (ospf6_check_and_set_router_abr(ospf6))
ospf6_abr_defaults_to_stub(ospf6);
monotime(&end);
ospf6_lsdb_remove(lsa, area->temp_router_lsa_lsdb);
}
}
+
+int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa,
+ struct ospf6_area *area)
+{
+ struct ospf6_route *route;
+ struct ospf6_as_external_lsa *external;
+ struct prefix prefix;
+ void (*hook_add)(struct ospf6_route *) = NULL;
+ void (*hook_remove)(struct ospf6_route *) = NULL;
+
+ assert(lsa);
+
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s : start", __func__);
+
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7)
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: Processing Type-7", __func__);
+
+ /* Stay away from any Local Translated Type-7 LSAs */
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT)) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: Rejecting Local translated LSA",
+ __func__);
+ return 0;
+ }
+
+ external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
+ lsa->header);
+ prefix.family = AF_INET6;
+ prefix.prefixlen = external->prefix.prefix_length;
+ ospf6_prefix_in6_addr(&prefix.u.prefix6, external, &external->prefix);
+
+ if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) {
+ hook_add = ospf6->route_table->hook_add;
+ hook_remove = ospf6->route_table->hook_remove;
+ ospf6->route_table->hook_add = NULL;
+ ospf6->route_table->hook_remove = NULL;
+
+ if (!OSPF6_LSA_IS_MAXAGE(lsa))
+ ospf6_asbr_lsa_add(lsa);
+
+ ospf6->route_table->hook_add = hook_add;
+ ospf6->route_table->hook_remove = hook_remove;
+
+ route = ospf6_route_lookup(&prefix, ospf6->route_table);
+ if (route == NULL) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: no external route %pFX",
+ __func__, &prefix);
+ return 0;
+ }
+
+ if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)
+ && CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)) {
+ UNSET_FLAG(route->flag, OSPF6_ROUTE_REMOVE);
+ UNSET_FLAG(route->flag, OSPF6_ROUTE_ADD);
+ }
+
+ if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE))
+ ospf6_route_remove(route, ospf6->route_table);
+ else if (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)
+ || CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE)) {
+ if (hook_add) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug(
+ "%s: add external route %pFX",
+ __func__, &prefix);
+ (*hook_add)(route);
+ }
+ }
+ } else if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) {
+ hook_add = area->route_table->hook_add;
+ hook_remove = area->route_table->hook_remove;
+ area->route_table->hook_add = NULL;
+ area->route_table->hook_remove = NULL;
+
+ if (!OSPF6_LSA_IS_MAXAGE(lsa))
+ ospf6_asbr_lsa_add(lsa);
+
+ area->route_table->hook_add = hook_add;
+ area->route_table->hook_remove = hook_remove;
+
+ route = ospf6_route_lookup(&prefix, area->route_table);
+ if (route == NULL) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: no route %pFX, area %s",
+ __func__, &prefix, area->name);
+ return 0;
+ }
+
+ if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)
+ && CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)) {
+ UNSET_FLAG(route->flag, OSPF6_ROUTE_REMOVE);
+ UNSET_FLAG(route->flag, OSPF6_ROUTE_ADD);
+ }
+
+ if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s : remove route %pFX, area %s",
+ __func__, &prefix, area->name);
+ ospf6_route_remove(route, area->route_table);
+ } else if (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)
+ || CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE)) {
+ if (hook_add) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug(
+ "%s: add nssa route %pFX, area %s",
+ __func__, &prefix, area->name);
+ (*hook_add)(route);
+ }
+ ospf6_abr_check_translate_nssa(area, lsa);
+ }
+ }
+ return 0;
+}
+
+static int ospf6_ase_calculate_timer(struct thread *t)
+{
+ struct ospf6 *ospf6;
+ struct ospf6_lsa *lsa;
+ struct listnode *node, *nnode;
+ struct ospf6_area *area;
+ uint16_t type;
+
+ ospf6 = THREAD_ARG(t);
+ ospf6->t_ase_calc = NULL;
+
+ /* Calculate external route for each AS-external-LSA */
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ for (ALL_LSDB_TYPED(ospf6->lsdb, type, lsa))
+ ospf6_ase_calculate_route(ospf6, lsa, NULL);
+
+ /* This version simple adds to the table all NSSA areas */
+ if (ospf6->anyNSSA) {
+ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s : looking at area %s", __func__,
+ area->name);
+
+ if (IS_OSPF6_DEBUG_SPF(PROCESS)) {
+ type = htons(OSPF6_LSTYPE_TYPE_7);
+ for (ALL_LSDB_TYPED(area->lsdb, type, lsa))
+ ospf6_ase_calculate_route(ospf6, lsa,
+ area);
+ }
+ }
+ }
+ return 0;
+}
+
+void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6)
+{
+ if (ospf6 == NULL)
+ return;
+
+ thread_add_timer(master, ospf6_ase_calculate_timer, ospf6,
+ OSPF6_ASE_CALC_INTERVAL, &ospf6->t_ase_calc);
+}
#define IS_OSPF6_DEBUG_SPF(level) \
(conf_debug_ospf6_spf & OSPF6_DEBUG_SPF_##level)
+#define OSPF6_ASE_CALC_INTERVAL 1
+
PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue);
/* Transit Vertex */
struct ospf6_vertex {
struct ospf6_lsdb *lsdb,
uint32_t adv_router);
extern void ospf6_remove_temp_router_lsa(struct ospf6_area *area);
-
+extern void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6);
+extern int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa,
+ struct ospf6_area *area);
#endif /* OSPF6_SPF_H */
#include "ospf6_spf.h"
#include "ospf6d.h"
#include "lib/json.h"
+#include "ospf6_nssa.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_TOP, "OSPF6 top");
{
vrf_init(ospf6_vrf_new, ospf6_vrf_enable, ospf6_vrf_disable,
ospf6_vrf_delete, ospf6_vrf_enable);
+
+ vrf_cmd_init(NULL, &ospf6d_privs);
}
static void ospf6_top_lsdb_hook_add(struct ospf6_lsa *lsa)
static void ospf6_top_route_hook_add(struct ospf6_route *route)
{
- struct ospf6 *ospf6 = route->table->scope;
+ struct ospf6 *ospf6 = NULL;
+ struct ospf6_area *oa = NULL;
+
+ if (route->table->scope_type == OSPF6_SCOPE_TYPE_GLOBAL)
+ ospf6 = route->table->scope;
+ else if (route->table->scope_type == OSPF6_SCOPE_TYPE_AREA) {
+ oa = (struct ospf6_area *)route->table->scope;
+ ospf6 = oa->ospf6;
+ } else {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)
+ || IS_OSPF6_DEBUG_BROUTER)
+ zlog_debug(
+ "%s: Route is not GLOBAL or scope is not of TYPE_AREA: %pFX",
+ __func__, &route->prefix);
+ return;
+ }
ospf6_abr_originate_summary(route, ospf6);
ospf6_zebra_route_update_add(route, ospf6);
static void ospf6_top_route_hook_remove(struct ospf6_route *route)
{
- struct ospf6 *ospf6 = route->table->scope;
+ struct ospf6 *ospf6 = NULL;
+ struct ospf6_area *oa = NULL;
+
+ if (route->table->scope_type == OSPF6_SCOPE_TYPE_GLOBAL)
+ ospf6 = route->table->scope;
+ else if (route->table->scope_type == OSPF6_SCOPE_TYPE_AREA) {
+ oa = (struct ospf6_area *)route->table->scope;
+ ospf6 = oa->ospf6;
+ } else {
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)
+ || IS_OSPF6_DEBUG_BROUTER)
+ zlog_debug(
+ "%s: Route is not GLOBAL or scope is not of TYPE_AREA: %pFX",
+ __func__, &route->prefix);
+ return;
+ }
route->flag |= OSPF6_ROUTE_REMOVE;
ospf6_abr_originate_summary(route, ospf6);
o->external_id_table = route_table_init();
+ o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT;
o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH;
o->distance_table = route_table_init();
o->max_multipath = MULTIPATH_NUM;
+ o->oi_write_q = list_new();
+
QOBJ_REG(o, ospf6);
/* Make ospf protocol socket. */
struct ospf6 *ospf6_instance_create(const char *name)
{
struct ospf6 *ospf6;
+ struct vrf *vrf;
+ struct interface *ifp;
ospf6 = ospf6_create(name);
if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES)
if (ospf6->router_id == 0)
ospf6_router_id_update(ospf6);
ospf6_add(ospf6);
+ if (ospf6->vrf_id != VRF_UNKNOWN) {
+ vrf = vrf_lookup_by_id(ospf6->vrf_id);
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ if (ifp->info)
+ ospf6_interface_start(ifp->info);
+ }
+ }
if (ospf6->fd < 0)
return ospf6;
ospf6_distance_reset(o);
route_table_finish(o->distance_table);
+ list_delete(&o->oi_write_q);
if (o->vrf_id != VRF_UNKNOWN) {
vrf = vrf_lookup_by_id(o->vrf_id);
return CMD_SUCCESS;
}
-DEFUN (ospf6_interface_area,
+DEFUN_HIDDEN (ospf6_interface_area,
ospf6_interface_area_cmd,
"interface IFNAME area <A.B.C.D|(0-4294967295)>",
"Enable routing on an IPv6 interface\n"
struct interface *ifp;
vrf_id_t vrf_id = VRF_DEFAULT;
int ipv6_count = 0;
+ uint32_t area_id;
+ int format;
+
+ vty_out(vty,
+ "This command is deprecated, because it is not VRF-aware.\n");
+ vty_out(vty,
+ "Please, use \"ipv6 ospf6 area\" on an interface instead.\n");
if (ospf6->vrf_id != VRF_UNKNOWN)
vrf_id = ospf6->vrf_id;
return CMD_WARNING_CONFIG_FAILED;
}
- /* parse Area-ID */
- OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, oa, ospf6);
+ if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) {
+ vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ oi->area_id = area_id;
+ oi->area_id_format = format;
+
+ oa = ospf6_area_lookup(area_id, ospf6);
+ if (oa == NULL)
+ oa = ospf6_area_create(area_id, ospf6, format);
/* attach interface to area */
listnode_add(oa->if_list, oi); /* sort ?? */
ospf6_interface_enable(oi);
/* If the router is ABR, originate summary routes */
- if (ospf6_is_router_abr(ospf6))
+ if (ospf6_check_and_set_router_abr(ospf6)) {
ospf6_abr_enable_area(oa);
+ ospf6_schedule_abr_task(oa->ospf6);
+ }
return CMD_SUCCESS;
}
-DEFUN (no_ospf6_interface_area,
+DEFUN_HIDDEN (no_ospf6_interface_area,
no_ospf6_interface_area_cmd,
"no interface IFNAME area <A.B.C.D|(0-4294967295)>",
NO_STR
uint32_t area_id;
vrf_id_t vrf_id = VRF_DEFAULT;
+ vty_out(vty,
+ "This command is deprecated, because it is not VRF-aware.\n");
+ vty_out(vty,
+ "Please, use \"no ipv6 ospf6 area\" on an interface instead.\n");
+
if (ospf6->vrf_id != VRF_UNKNOWN)
vrf_id = ospf6->vrf_id;
ospf6_abr_disable_area(oa);
}
+ oi->area_id = 0;
+ oi->area_id_format = OSPF6_AREA_FMT_UNSET;
+
return CMD_SUCCESS;
}
/* OSPF configuration write function. */
static int config_write_ospf6(struct vty *vty)
{
- struct listnode *j, *k;
- struct ospf6_area *oa;
- struct ospf6_interface *oi;
struct ospf6 *ospf6;
struct listnode *node, *nnode;
vty_out(vty, " auto-cost reference-bandwidth %d\n",
ospf6->ref_bandwidth);
+ if (ospf6->write_oi_count
+ != OSPF6_WRITE_INTERFACE_COUNT_DEFAULT)
+ vty_out(vty, " write-multiplier %d\n",
+ ospf6->write_oi_count);
+
/* LSA timers print. */
if (ospf6->lsa_minarrival != OSPF_MIN_LS_ARRIVAL)
vty_out(vty, " timers lsa min-arrival %d\n",
ospf6_distance_config_write(vty, ospf6);
ospf6_distribute_config_write(vty, ospf6);
- for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, j, oa)) {
- for (ALL_LIST_ELEMENTS_RO(oa->if_list, k, oi))
- vty_out(vty, " interface %s area %s\n",
- oi->interface->name, oa->name);
- }
vty_out(vty, "!\n");
}
return 0;
struct list *redist[ZEBRA_ROUTE_MAX + 1];
uint8_t flag;
+#define OSPF6_FLAG_ABR 0x04
+#define OSPF6_FLAG_ASBR 0x08
int redistribute; /* Num of redistributed protocols. */
struct thread *maxage_remover;
struct thread *t_distribute_update; /* Distirbute update timer. */
struct thread *t_ospf6_receive; /* OSPF6 receive timer */
+#define OSPF6_WRITE_INTERFACE_COUNT_DEFAULT 20
+ struct thread *t_write;
+ int write_oi_count; /* Num of packets sent per thread invocation */
uint32_t ref_bandwidth;
/* Distance parameters */
* to support ECMP.
*/
uint16_t max_multipath;
+ /* Count of NSSA areas */
+ uint8_t anyNSSA;
+ struct thread *t_abr_task; /* ABR task timer. */
+ struct list *oi_write_q;
+ uint32_t redist_count;
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(ospf6);
#define OSPF6_DISABLED 0x01
#define OSPF6_STUB_ROUTER 0x02
-#define OSPF6_FLAG_ASBR 0x04
#define OSPF6_MAX_IF_ADDRS 100
#define OSPF6_MAX_IF_ADDRS_JUMBO 200
#define OSPF6_DEFAULT_MTU 1500
#include "ospf6d.h"
#include "ospf6_bfd.h"
#include "lib/json.h"
+#include "ospf6_nssa.h"
DEFINE_MGROUP(OSPF6D, "ospf6d");
config_write_ospf6_debug_asbr(vty);
config_write_ospf6_debug_abr(vty);
config_write_ospf6_debug_flood(vty);
+ config_write_ospf6_debug_nssa(vty);
return 0;
}
type = htons(OSPF6_LSTYPE_INTER_PREFIX);
else if (strmatch(argv[idx_lsa]->text, "link"))
type = htons(OSPF6_LSTYPE_LINK);
+ else if (strmatch(argv[idx_lsa]->text, "type-7"))
+ type = htons(OSPF6_LSTYPE_TYPE_7);
}
return type;
install_element_ospf6_debug_asbr();
install_element_ospf6_debug_abr();
install_element_ospf6_debug_flood();
+ install_element_ospf6_debug_nssa();
install_element_ospf6_clear_interface();
noinst_LIBRARIES += ospf6d/libospf6.a
sbin_PROGRAMS += ospf6d/ospf6d
vtysh_scan += \
+ ospf6d/ospf6_nssa.c \
ospf6d/ospf6_abr.c \
ospf6d/ospf6_asbr.c \
ospf6d/ospf6_area.c \
endif
ospf6d_libospf6_a_SOURCES = \
+ ospf6d/ospf6_nssa.c \
ospf6d/ospf6_abr.c \
ospf6d/ospf6_area.c \
ospf6d/ospf6_asbr.c \
# end
noinst_HEADERS += \
+ ospf6d/ospf6_nssa.h \
ospf6d/ospf6_abr.h \
ospf6d/ospf6_area.h \
ospf6d/ospf6_asbr.h \
rn->info = NULL;
route_unlock_node(rn);
- route_unlock_node(rn);
}
struct ospf_external_aggr_rt *
rn = route_node_lookup(ospf->rt_aggr_tbl, (struct prefix *)p);
if (!rn)
return OSPF_INVALID;
+ route_unlock_node(rn);
aggr = rn->info;
rn = route_node_lookup(ospf->rt_aggr_tbl, (struct prefix *)p);
if (!rn)
return OSPF_INVALID;
+ route_unlock_node(rn);
aggr = rn->info;
Designated Router, chances are that all the neighbors have
received the LSA already. */
if (NBR_IS_DR(inbr) || NBR_IS_BDR(inbr)) {
- if (IS_DEBUG_OSPF_NSSA)
+ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug("%s: DR/BDR NOT SEND to int %s (%s)",
__func__, IF_NAME(oi),
ospf_get_name(oi->ospf));
end up retransmitting the updates. */
if (oi->state == ISM_Backup) {
- if (IS_DEBUG_OSPF_NSSA)
+ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
"%s: ISM_Backup NOT SEND to int %s (%s)",
__func__, IF_NAME(oi),
(which must be > 0) when it is copied into the outgoing Link
State Update packet (until the LS age field reaches the maximum
value of MaxAge). */
- /* XXX HASSO: Is this IS_DEBUG_OSPF_NSSA really correct? */
- if (IS_DEBUG_OSPF_NSSA)
+ if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug("%s: DR/BDR sending upd to int %s (%s)", __func__,
IF_NAME(oi), ospf_get_name(oi->ospf));
static const char * const ospf_exit_reason_desc[] = {
"Unknown reason",
- "Helper inprogress",
+ "Helper in progress",
"Topology Change",
"Grace timer expiry",
"Successful graceful restart",
* Returns:
* Nothing
*/
-void ospf_gr_helper_init(struct ospf *ospf)
+void ospf_gr_helper_instance_init(struct ospf *ospf)
{
- int rc;
-
if (IS_DEBUG_OSPF_GR_HELPER)
zlog_debug("%s, GR Helper init.", __func__);
ospf->enable_rtr_list =
hash_create(ospf_enable_rtr_hash_key, ospf_enable_rtr_hash_cmp,
"OSPF enable router hash");
+}
+
+/*
+ * De-Initialize GR helper config data structures.
+ *
+ * OSPF
+ * OSPF pointer
+ *
+ * Returns:
+ * Nothing
+ */
+void ospf_gr_helper_instance_stop(struct ospf *ospf)
+{
+ if (IS_DEBUG_OSPF_GR_HELPER)
+ zlog_debug("%s, GR helper deinit.", __func__);
+
+ ospf_enable_rtr_hash_destroy(ospf);
+}
+
+/*
+ * Initialize GR helper config data structures.
+ *
+ * Returns:
+ * Nothing
+ */
+void ospf_gr_helper_init(void)
+{
+ int rc;
+
+ if (IS_DEBUG_OSPF_GR_HELPER)
+ zlog_debug("%s, GR Helper init.", __func__);
rc = ospf_register_opaque_functab(
OSPF_OPAQUE_LINK_LSA, OPAQUE_TYPE_GRACE_LSA, NULL, NULL, NULL,
/*
* De-Initialize GR helper config data structures.
*
- * OSPF
- * OSPF pointer
- *
* Returns:
* Nothing
*/
-void ospf_gr_helper_stop(struct ospf *ospf)
+void ospf_gr_helper_stop(void)
{
if (IS_DEBUG_OSPF_GR_HELPER)
zlog_debug("%s, GR helper deinit.", __func__);
- ospf_enable_rtr_hash_destroy(ospf);
-
ospf_delete_opaque_functab(OSPF_OPAQUE_LINK_LSA, OPAQUE_TYPE_GRACE_LSA);
}
* Grace LSA received from RESTARTER.
*
* nbr
- * ospf neighbour which requets the router to act as
+ * OSPF neighbour which requests the router to act as
* HELPER.
*
* Returns:
if (IS_DEBUG_OSPF_GR_HELPER)
zlog_debug(
- "%s, Grace LSA received from %pI4, grace interval:%u, restartreason :%s",
+ "%s, Grace LSA received from %pI4, grace interval:%u, restart reason:%s",
__func__, &restart_addr, grace_interval,
ospf_restart_reason2str(restart_reason));
- /* Incase of broadcast links, if RESTARTER is DR_OTHER,
+ /* In case of broadcast links, if RESTARTER is DR_OTHER,
* grace LSA might be received from DR, so need to get
* actual neighbour info , here RESTARTER.
*/
return OSPF_GR_NOT_HELPER;
}
- /* Check the retranmission list of this
+ /* Check the retransmission list of this
* neighbour, check any change in lsas.
*/
if (ospf->strict_lsa_check && !ospf_ls_retransmit_isempty(restarter)
if (ntohs(lsa->data->ls_age) >= grace_interval) {
if (IS_DEBUG_OSPF_GR_HELPER)
zlog_debug(
- "%s, Grace LSA age(%d) is more than the graceinterval(%d)",
+ "%s, Grace LSA age(%d) is more than the grace interval(%d)",
__func__, lsa->data->ls_age, grace_interval);
restarter->gr_helper_info.rejected_reason =
OSPF_HELPER_LSA_AGE_MORE;
restarter->gr_helper_info.gr_restart_reason = restart_reason;
restarter->gr_helper_info.rejected_reason = OSPF_HELPER_REJECTED_NONE;
- /* Incremnet the active restarer count */
+ /* Increment the active restarter count */
ospf->active_restarter_cnt++;
if (IS_DEBUG_OSPF_GR_HELPER)
* retransmission list.
*
* nbr
- * ospf neighbor
+ * OSPF neighbor
*
* Returns:
* TRUE - if any change in the lsa.
* ospf
* ospf pointer
* lsa
- * topo change occured due to this lsa type (1 to 5 and 7)
+ * topo change occurred due to this lsa type (1 to 5 and 7)
*
* Returns:
* Nothing
return;
/* Topo change not required to be handled if strict
- * LSA check is disbaled for this router.
+ * LSA check is disabled for this router.
*/
if (!ospf->strict_lsa_check)
return;
ospf->active_restarter_cnt--;
/* If the exit not triggered due to grace timer
- * expairy , stop the grace timer.
+ * expiry, stop the grace timer.
*/
if (reason != OSPF_GR_HELPER_GRACE_TIMEOUT)
THREAD_OFF(nbr->gr_helper_info.t_grace_timer);
/* check exit triggered due to successful completion
* of graceful restart.
- * If no, bringdown the neighbour.
+ * If no, bring down the neighbour.
*/
if (reason != OSPF_GR_HELPER_COMPLETED) {
if (IS_DEBUG_OSPF_GR_HELPER)
zlog_debug(
"%s, Failed GR exit, so bringing down the neighbour",
__func__);
- OSPF_NSM_EVENT_EXECUTE(nbr, NSM_KillNbr);
+ OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_KillNbr);
}
/*Recalculate the DR for the network segment */
- ospf_dr_election(oi);
+ if (oi->type == OSPF_IFTYPE_BROADCAST || oi->type == OSPF_IFTYPE_NBMA)
+ ospf_dr_election(oi);
/* Originate a router LSA */
ospf_router_lsa_update_area(oi->area);
}
/*
- * Process Maxage Grace LSA.
+ * Process MaxAge Grace LSA.
* It is a indication for successful completion of GR.
* If router acting as HELPER, It exits from helper role.
*
* Grace LSA received from RESTARTER.
*
* nbr
- * ospf neighbour which requets the router to act as
+ * OSPF neighbour which requests the router to act as
* HELPER.
*
* Returns:
* Disable/Enable HELPER support on router level.
*
* ospf
- * OSPFpointer.
+ * OSPF pointer.
*
* status
* TRUE/FALSE
lookup.advRtrAddr.s_addr =
nbr->router_id.s_addr;
/* check if helper support enabled for the
- * correspodning routerid.If enabled, dont
+ * corresponding routerid.If enabled, dont
* dont exit from helper role.
*/
if (hash_lookup(ospf->enable_rtr_list, &lookup))
lsah = (struct lsa_header *)lsa->data;
if (lsa->size <= OSPF_LSA_HEADER_SIZE) {
- vty_out(vty, "%% Invalid LSA length: %d\n", length);
+ if (vty)
+ vty_out(vty, "%% Invalid LSA length: %d\n", length);
+ else
+ zlog_debug("%% Invalid LSA length: %d", length);
return;
}
length = lsa->size - OSPF_LSA_HEADER_SIZE;
- vty_out(vty, " TLV info:\n");
+ if (vty)
+ vty_out(vty, " TLV info:\n");
+ else
+ zlog_debug(" TLV info:");
for (tlvh = TLV_HDR_TOP(lsah); sum < length && tlvh;
tlvh = TLV_HDR_NEXT(tlvh)) {
/* Check TLV len */
if (sum + TLV_SIZE(tlvh) > length) {
- vty_out(vty, "%% Invalid TLV length: %u\n",
- TLV_SIZE(tlvh));
+ if (vty)
+ vty_out(vty, "%% Invalid TLV length: %u\n",
+ TLV_SIZE(tlvh));
+ else
+ zlog_debug("%% Invalid TLV length: %u",
+ TLV_SIZE(tlvh));
return;
}
switch (ntohs(tlvh->type)) {
case GRACE_PERIOD_TYPE:
- if (TLV_SIZE(tlvh) <
- sizeof(struct grace_tlv_graceperiod)) {
- vty_out(vty,
- "%% Invalid grace TLV length %u\n",
- TLV_SIZE(tlvh));
+ if (TLV_SIZE(tlvh)
+ < sizeof(struct grace_tlv_graceperiod)) {
+ if (vty)
+ vty_out(vty,
+ "%% Invalid grace TLV length %u\n",
+ TLV_SIZE(tlvh));
+ else
+ zlog_debug(
+ "%% Invalid grace TLV length %u",
+ TLV_SIZE(tlvh));
return;
}
gracePeriod = (struct grace_tlv_graceperiod *)tlvh;
sum += TLV_SIZE(tlvh);
- vty_out(vty, " Grace period:%d\n",
- ntohl(gracePeriod->interval));
+ if (vty)
+ vty_out(vty, " Grace period:%d\n",
+ ntohl(gracePeriod->interval));
+ else
+ zlog_debug(" Grace period:%d",
+ ntohl(gracePeriod->interval));
break;
case RESTART_REASON_TYPE:
- if (TLV_SIZE(tlvh) <
- sizeof(struct grace_tlv_restart_reason)) {
- vty_out(vty,
- "%% Invalid reason TLV length %u\n",
- TLV_SIZE(tlvh));
+ if (TLV_SIZE(tlvh)
+ < sizeof(struct grace_tlv_restart_reason)) {
+ if (vty)
+ vty_out(vty,
+ "%% Invalid reason TLV length %u\n",
+ TLV_SIZE(tlvh));
+ else
+ zlog_debug(
+ "%% Invalid reason TLV length %u",
+ TLV_SIZE(tlvh));
return;
}
grReason = (struct grace_tlv_restart_reason *)tlvh;
sum += TLV_SIZE(tlvh);
- vty_out(vty, " Restart reason:%s\n",
- ospf_restart_reason2str(grReason->reason));
+ if (vty)
+ vty_out(vty, " Restart reason:%s\n",
+ ospf_restart_reason2str(
+ grReason->reason));
+ else
+ zlog_debug(" Restart reason:%s",
+ ospf_restart_reason2str(
+ grReason->reason));
break;
case RESTARTER_IP_ADDR_TYPE:
- if (TLV_SIZE(tlvh) <
- sizeof(struct grace_tlv_restart_addr)) {
- vty_out(vty,
- "%% Invalid addr TLV length %u\n",
- TLV_SIZE(tlvh));
+ if (TLV_SIZE(tlvh)
+ < sizeof(struct grace_tlv_restart_addr)) {
+ if (vty)
+ vty_out(vty,
+ "%% Invalid addr TLV length %u\n",
+ TLV_SIZE(tlvh));
+ else
+ zlog_debug(
+ "%% Invalid addr TLV length %u",
+ TLV_SIZE(tlvh));
return;
}
restartAddr = (struct grace_tlv_restart_addr *)tlvh;
sum += TLV_SIZE(tlvh);
- vty_out(vty, " Restarter address:%pI4\n",
- &restartAddr->addr);
+ if (vty)
+ vty_out(vty, " Restarter address:%pI4\n",
+ &restartAddr->addr);
+ else
+ zlog_debug(" Restarter address:%pI4",
+ &restartAddr->addr);
break;
default:
- vty_out(vty, " Unknown TLV type %d\n",
- ntohs(tlvh->type));
+ if (vty)
+ vty_out(vty, " Unknown TLV type %d\n",
+ ntohs(tlvh->type));
+ else
+ zlog_debug(" Unknown TLV type %d",
+ ntohs(tlvh->type));
break;
}
const char *ospf_restart_reason2str(unsigned int reason);
const char *ospf_rejected_reason2str(unsigned int reason);
-extern void ospf_gr_helper_init(struct ospf *ospf);
-extern void ospf_gr_helper_stop(struct ospf *ospf);
+extern void ospf_gr_helper_instance_init(struct ospf *ospf);
+extern void ospf_gr_helper_instance_stop(struct ospf *ospf);
+extern void ospf_gr_helper_init(void);
+extern void ospf_gr_helper_stop(void);
extern int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
struct ospf_neighbor *nbr);
extern void ospf_gr_helper_exit(struct ospf_neighbor *nbr,
{
struct external_info *ei;
struct ospf_external_aggr_rt *aggr;
+ struct ospf_lsa *lsa = NULL;
+ int force;
int type;
for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
continue;
if (is_prefix_default(
- (struct prefix_ipv4 *)&ei->p))
+ (struct prefix_ipv4 *)&ei->p))
continue;
- if (!ospf_redistribute_check(ospf, ei, NULL))
- continue;
+ lsa = ospf_external_info_find_lsa(ospf, &ei->p);
aggr = ospf_external_aggr_match(ospf, &ei->p);
if (aggr) {
+
+ if (!ospf_redistribute_check(ospf, ei,
+ NULL))
+ continue;
+
if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
zlog_debug(
"Originate Summary LSA after reset/router-ID change");
+
/* Here the LSA is originated as new */
ospf_originate_summary_lsa(ospf, aggr,
ei);
- } else if (!ospf_external_lsa_originate(ospf,
- ei))
- flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
- "LSA: AS-external-LSA was not originated.");
+ } else if (lsa) {
+ /* LSA needs to be refreshed even if
+ * there is no change in the route
+ * params if the LSA is in maxage.
+ */
+ if (IS_LSA_MAXAGE(lsa))
+ force = LSA_REFRESH_FORCE;
+ else
+ force = LSA_REFRESH_IF_CHANGED;
+
+ ospf_external_lsa_refresh(ospf, lsa,
+ ei, force, 0);
+ } else {
+ if (!ospf_redistribute_check(ospf, ei,
+ NULL))
+ continue;
+
+ if (!ospf_external_lsa_originate(ospf,
+ NULL))
+ flog_warn(
+ EC_OSPF_LSA_INSTALL_FAILURE,
+ "LSA: AS-external-LSA was not originated.");
+ }
}
}
}
if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) {
zlog_debug(
- "ospf_lsa_install() Premature Aging lsa 0x%p, seqnum 0x%x",
- (void *)lsa,
+ "%s() Premature Aging lsa %p, seqnum 0x%x",
+ __func__, lsa,
ntohl(lsa->data->ls_seqnum));
ospf_lsa_header_dump(lsa->data);
}
} else {
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
zlog_debug(
- "ospf_lsa_install() got an lsa with seq 0x80000000 that was not self originated. Ignoring");
+ "%s() got an lsa with seq 0x80000000 that was not self originated. Ignoring",
+ __func__);
ospf_lsa_header_dump(lsa->data);
}
return old;
*/
if (IS_LSA_MAXAGE(new)) {
if (IS_DEBUG_OSPF(lsa, LSA_INSTALL))
- zlog_debug("LSA[Type%d:%pI4]: Install LSA 0x%p, MaxAge",
- new->data->type, &new->data->id,
- (void *)lsa);
+ zlog_debug("LSA[Type%d:%pI4]: Install LSA %p, MaxAge",
+ new->data->type, &new->data->id, lsa);
ospf_lsa_maxage(ospf, lsa);
}
if (CHECK_FLAG(lsa->flags, OSPF_LSA_PREMATURE_AGE)) {
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
- "originating new lsa for lsa 0x%p",
- (void *)lsa);
+ "originating new lsa for lsa %p",
+ lsa);
ospf_lsa_refresh(ospf, lsa);
}
ospf_route_map_init();
ospf_opaque_init();
+ ospf_gr_helper_init();
/* OSPF errors init */
ospf_error_init();
}
}
+ /* Refresh/Re-originate external LSAs (Type-7 and Type-5).*/
+ ospf_external_lsa_rid_change(top);
+
return;
}
goto out;
}
+ /* This lsa will be flushed and removed eventually. */
+ ospf_lsa_flush(top, lsa);
+
/* Dequeue listnode entry from the list. */
listnode_delete(oipt->id_list, oipi);
- /* Disassociate internal control information with the given lsa. */
- free_opaque_info_per_id((void *)oipi);
-
- /* Force given lsa's age to MaxAge. */
- lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
-
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
"Schedule Type-%u Opaque-LSA to FLUSH: [opaque-type=%u, opaque-id=%x]",
GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)),
GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)));
- /* This lsa will be flushed and removed eventually. */
- ospf_lsa_flush(top, lsa);
+ /* Disassociate internal control information with the given lsa. */
+ free_opaque_info_per_id((void *)oipi);
out:
return;
return;
}
- /* Get list of LSAs from Link State Update packet. - Also perorms Stages
- * 1 (validate LSA checksum) and 2 (check for LSA consistent type)
- * of section 13.
+ /* Get list of LSAs from Link State Update packet. - Also performs
+ * Stages 1 (validate LSA checksum) and 2 (check for LSA consistent
+ * type) of section 13.
*/
lsas = ospf_ls_upd_list_lsa(nbr, s, oi, size);
struct ospf_lsa *ls_ret, *current;
int ret = 1;
- if (IS_DEBUG_OSPF_NSSA)
+ if (IS_DEBUG_OSPF(lsa, LSA))
zlog_debug("LSA Type-%d from %pI4, ID: %pI4, ADV: %pI4",
lsa->data->type, &ospfh->router_id,
&lsa->data->id, &lsa->data->adv_router);
/* Keep old route-map. */
struct route_map *old = ROUTEMAP(red);
- if (!old) {
- /* Route-map creation */
- /* Update route-map. */
- ROUTEMAP(red) =
- route_map_lookup_by_name(
- ROUTEMAP_NAME(red));
-
- route_map_counter_increment(
- ROUTEMAP(red));
- } else {
- /* Route-map deletion */
- ROUTEMAP(red) = NULL;
- }
+ ROUTEMAP(red) =
+ route_map_lookup_by_name(
+ ROUTEMAP_NAME(red));
+
+ if (!old)
+ route_map_counter_increment(
+ ROUTEMAP(red));
+
/* No update for this distribute type.
*/
if (old == NULL
rbuf[0] = '\0';
if (spf_reason_flags) {
- if (spf_reason_flags & SPF_FLAG_ROUTER_LSA_INSTALL)
+ if (spf_reason_flags & (1 << SPF_FLAG_ROUTER_LSA_INSTALL))
strlcat(rbuf, "R, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_NETWORK_LSA_INSTALL)
+ if (spf_reason_flags & (1 << SPF_FLAG_NETWORK_LSA_INSTALL))
strlcat(rbuf, "N, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_SUMMARY_LSA_INSTALL)
+ if (spf_reason_flags & (1 << SPF_FLAG_SUMMARY_LSA_INSTALL))
strlcat(rbuf, "S, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL)
+ if (spf_reason_flags & (1 << SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL))
strlcat(rbuf, "AS, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_ABR_STATUS_CHANGE)
+ if (spf_reason_flags & (1 << SPF_FLAG_ABR_STATUS_CHANGE))
strlcat(rbuf, "ABR, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_ASBR_STATUS_CHANGE)
+ if (spf_reason_flags & (1 << SPF_FLAG_ASBR_STATUS_CHANGE))
strlcat(rbuf, "ASBR, ", sizeof(rbuf));
- if (spf_reason_flags & SPF_FLAG_MAXAGE)
+ if (spf_reason_flags & (1 << SPF_FLAG_MAXAGE))
strlcat(rbuf, "M, ", sizeof(rbuf));
size_t rbuflen = strlen(rbuf);
}
-static void ospf_passive_interface_default(struct ospf *ospf, uint8_t newval)
+static void ospf_passive_interface_default_update(struct ospf *ospf,
+ uint8_t newval)
{
- struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
struct listnode *ln;
- struct interface *ifp;
struct ospf_interface *oi;
ospf->passive_interface_default = newval;
- FOR_ALL_INTERFACES (vrf, ifp) {
- if (ifp && OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp),
- passive_interface))
- UNSET_IF_PARAM(IF_DEF_PARAMS(ifp), passive_interface);
- }
- for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, ln, oi)) {
- if (OSPF_IF_PARAM_CONFIGURED(oi->params, passive_interface))
- UNSET_IF_PARAM(oi->params, passive_interface);
- /* update multicast memberships */
+ /* update multicast memberships */
+ for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, ln, oi))
ospf_if_set_multicast(oi);
- }
}
-static void ospf_passive_interface_update_addr(struct ospf *ospf,
- struct interface *ifp,
- struct ospf_if_params *params,
- uint8_t value,
- struct in_addr addr)
+static void ospf_passive_interface_update(struct interface *ifp)
{
- uint8_t dflt;
+ struct route_node *rn;
- params->passive_interface = value;
- if (params != IF_DEF_PARAMS(ifp)) {
- if (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp),
- passive_interface))
- dflt = IF_DEF_PARAMS(ifp)->passive_interface;
- else
- dflt = ospf->passive_interface_default;
+ /*
+ * XXX We should call ospf_if_set_multicast on exactly those
+ * interfaces for which the passive property changed. It is too much
+ * work to determine this set, so we do this for every interface.
+ * This is safe and reasonable because ospf_if_set_multicast uses a
+ * record of joined groups to avoid systems calls if the desired
+ * memberships match the current memership.
+ */
- if (value != dflt)
- SET_IF_PARAM(params, passive_interface);
- else
- UNSET_IF_PARAM(params, passive_interface);
+ for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
+ struct ospf_interface *oi = rn->info;
- ospf_free_if_params(ifp, addr);
- ospf_if_update_params(ifp, addr);
+ if (oi)
+ ospf_if_set_multicast(oi);
}
+
+ /*
+ * XXX It is not clear what state transitions the interface needs to
+ * undergo when going from active to passive and vice versa. Fixing
+ * this will require precise identification of interfaces having such a
+ * transition.
+ */
}
-static void ospf_passive_interface_update(struct ospf *ospf,
- struct interface *ifp,
- struct ospf_if_params *params,
- uint8_t value)
+DEFUN (ospf_passive_interface_default,
+ ospf_passive_interface_default_cmd,
+ "passive-interface default",
+ "Suppress routing updates on an interface\n"
+ "Suppress routing updates on interfaces by default\n")
{
- params->passive_interface = value;
- if (params == IF_DEF_PARAMS(ifp)) {
- if (value != ospf->passive_interface_default)
- SET_IF_PARAM(params, passive_interface);
- else
- UNSET_IF_PARAM(params, passive_interface);
- }
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ ospf_passive_interface_default_update(ospf, OSPF_IF_PASSIVE);
+
+ return CMD_SUCCESS;
}
-DEFUN (ospf_passive_interface,
+DEFUN_HIDDEN (ospf_passive_interface_addr,
ospf_passive_interface_addr_cmd,
- "passive-interface <IFNAME [A.B.C.D]|default>",
+ "passive-interface IFNAME [A.B.C.D]",
"Suppress routing updates on an interface\n"
"Interface's name\n"
- "IPv4 address\n"
- "Suppress routing updates on interfaces by default\n")
+ "IPv4 address\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx_ipv4 = 2;
struct interface *ifp = NULL;
struct in_addr addr = {.s_addr = INADDR_ANY};
- int ret;
struct ospf_if_params *params;
- struct route_node *rn;
+ int ret;
+
+ vty_out(vty,
+ "This command is deprecated, because it is not VRF-aware.\n");
+ vty_out(vty,
+ "Please, use \"ip ospf passive\" on an interface instead.\n");
- if (strmatch(argv[1]->text, "default")) {
- ospf_passive_interface_default(ospf, OSPF_IF_PASSIVE);
- return CMD_SUCCESS;
- }
if (ospf->vrf_id != VRF_UNKNOWN)
ifp = if_get_by_name(argv[1]->arg, ospf->vrf_id);
return CMD_WARNING_CONFIG_FAILED;
}
- params = IF_DEF_PARAMS(ifp);
-
if (argc == 3) {
ret = inet_aton(argv[idx_ipv4]->arg, &addr);
if (!ret) {
params = ospf_get_if_params(ifp, addr);
ospf_if_update_params(ifp, addr);
- ospf_passive_interface_update_addr(ospf, ifp, params,
- OSPF_IF_PASSIVE, addr);
+ } else {
+ params = IF_DEF_PARAMS(ifp);
}
- ospf_passive_interface_update(ospf, ifp, params, OSPF_IF_PASSIVE);
+ params->passive_interface = OSPF_IF_PASSIVE;
+ SET_IF_PARAM(params, passive_interface);
- /* XXX We should call ospf_if_set_multicast on exactly those
- * interfaces for which the passive property changed. It is too much
- * work to determine this set, so we do this for every interface.
- * This is safe and reasonable because ospf_if_set_multicast uses a
- * record of joined groups to avoid systems calls if the desired
- * memberships match the current memership.
- */
+ ospf_passive_interface_update(ifp);
- for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
- struct ospf_interface *oi = rn->info;
+ return CMD_SUCCESS;
+}
- if (oi && (OSPF_IF_PARAM(oi, passive_interface)
- == OSPF_IF_PASSIVE))
- ospf_if_set_multicast(oi);
- }
- /*
- * XXX It is not clear what state transitions the interface needs to
- * undergo when going from active to passive. Fixing this will
- * require precise identification of interfaces having such a
- * transition.
- */
+DEFUN (no_ospf_passive_interface_default,
+ no_ospf_passive_interface_default_cmd,
+ "no passive-interface default",
+ NO_STR
+ "Allow routing updates on an interface\n"
+ "Allow routing updates on interfaces by default\n")
+{
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ ospf_passive_interface_default_update(ospf, OSPF_IF_ACTIVE);
return CMD_SUCCESS;
}
-DEFUN (no_ospf_passive_interface,
+DEFUN_HIDDEN (no_ospf_passive_interface,
no_ospf_passive_interface_addr_cmd,
- "no passive-interface <IFNAME [A.B.C.D]|default>",
+ "no passive-interface IFNAME [A.B.C.D]",
NO_STR
"Allow routing updates on an interface\n"
"Interface's name\n"
- "IPv4 address\n"
- "Allow routing updates on interfaces by default\n")
+ "IPv4 address\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx_ipv4 = 3;
struct in_addr addr = {.s_addr = INADDR_ANY};
struct ospf_if_params *params;
int ret;
- struct route_node *rn;
- if (strmatch(argv[2]->text, "default")) {
- ospf_passive_interface_default(ospf, OSPF_IF_ACTIVE);
- return CMD_SUCCESS;
- }
+ vty_out(vty,
+ "This command is deprecated, because it is not VRF-aware.\n");
+ vty_out(vty,
+ "Please, use \"no ip ospf passive\" on an interface instead.\n");
if (ospf->vrf_id != VRF_UNKNOWN)
ifp = if_get_by_name(argv[2]->arg, ospf->vrf_id);
return CMD_WARNING_CONFIG_FAILED;
}
- params = IF_DEF_PARAMS(ifp);
-
if (argc == 4) {
ret = inet_aton(argv[idx_ipv4]->arg, &addr);
if (!ret) {
"Please specify interface address by A.B.C.D\n");
return CMD_WARNING_CONFIG_FAILED;
}
-
params = ospf_lookup_if_params(ifp, addr);
if (params == NULL)
return CMD_SUCCESS;
- ospf_passive_interface_update_addr(ospf, ifp, params,
- OSPF_IF_ACTIVE, addr);
+ } else {
+ params = IF_DEF_PARAMS(ifp);
}
- ospf_passive_interface_update(ospf, ifp, params, OSPF_IF_ACTIVE);
- /* XXX We should call ospf_if_set_multicast on exactly those
- * interfaces for which the passive property changed. It is too much
- * work to determine this set, so we do this for every interface.
- * This is safe and reasonable because ospf_if_set_multicast uses a
- * record of joined groups to avoid systems calls if the desired
- * memberships match the current memership.
- */
- for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
- struct ospf_interface *oi = rn->info;
-
- if (oi
- && (OSPF_IF_PARAM(oi, passive_interface) == OSPF_IF_ACTIVE))
- ospf_if_set_multicast(oi);
+ params->passive_interface = OSPF_IF_ACTIVE;
+ UNSET_IF_PARAM(params, passive_interface);
+ if (params != IF_DEF_PARAMS(ifp)) {
+ ospf_free_if_params(ifp, addr);
+ ospf_if_update_params(ifp, addr);
}
+ ospf_passive_interface_update(ifp);
+
return CMD_SUCCESS;
}
ospf_nbr_state_message(nbr, msgbuf, 16);
- long time_store;
-
- time_store =
- monotime_until(
- &nbr->t_inactivity->u.sands,
- NULL)
- / 1000LL;
-
json_object_int_add(json_neighbor, "priority",
nbr->priority);
json_object_string_add(json_neighbor, "state",
msgbuf);
- json_object_int_add(json_neighbor,
- "deadTimeMsecs",
- time_store);
+
+ if (nbr->t_inactivity) {
+ long time_store;
+
+ time_store = monotime_until(
+ &nbr->t_inactivity
+ ->u.sands,
+ NULL)
+ / 1000LL;
+ json_object_int_add(json_neighbor,
+ "deadTimeMsecs",
+ time_store);
+ } else {
+ json_object_string_add(json_neighbor,
+ "deadTimeMsecs",
+ "inactive");
+ }
json_object_string_add(
json_neighbor, "address",
inet_ntop(AF_INET, &nbr->src,
return CMD_SUCCESS;
}
+DEFUN (ip_ospf_passive,
+ ip_ospf_passive_cmd,
+ "ip ospf passive [A.B.C.D]",
+ "IP Information\n"
+ "OSPF interface commands\n"
+ "Suppress routing updates on an interface\n"
+ "Address of interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ int idx_ipv4 = 3;
+ struct in_addr addr;
+ struct ospf_if_params *params;
+ int ret;
+
+ if (argc == 4) {
+ ret = inet_aton(argv[idx_ipv4]->arg, &addr);
+ if (!ret) {
+ vty_out(vty,
+ "Please specify interface address by A.B.C.D\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ params = ospf_get_if_params(ifp, addr);
+ ospf_if_update_params(ifp, addr);
+ } else {
+ params = IF_DEF_PARAMS(ifp);
+ }
+
+ params->passive_interface = OSPF_IF_PASSIVE;
+ SET_IF_PARAM(params, passive_interface);
+
+ ospf_passive_interface_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_ospf_passive,
+ no_ip_ospf_passive_cmd,
+ "no ip ospf passive [A.B.C.D]",
+ NO_STR
+ "IP Information\n"
+ "OSPF interface commands\n"
+ "Enable routing updates on an interface\n"
+ "Address of interface\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ int idx_ipv4 = 4;
+ struct in_addr addr;
+ struct ospf_if_params *params;
+ int ret;
+
+ if (argc == 5) {
+ ret = inet_aton(argv[idx_ipv4]->arg, &addr);
+ if (!ret) {
+ vty_out(vty,
+ "Please specify interface address by A.B.C.D\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ params = ospf_lookup_if_params(ifp, addr);
+ if (params == NULL)
+ return CMD_SUCCESS;
+ } else {
+ params = IF_DEF_PARAMS(ifp);
+ }
+
+ params->passive_interface = OSPF_IF_ACTIVE;
+ UNSET_IF_PARAM(params, passive_interface);
+ if (params != IF_DEF_PARAMS(ifp)) {
+ ospf_free_if_params(ifp, addr);
+ ospf_if_update_params(ifp, addr);
+ }
+
+ ospf_passive_interface_update(ifp);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (ospf_redistribute_source,
ospf_redistribute_source_cmd,
"redistribute " FRR_REDIST_STR_OSPFD " [{metric (0-16777214)|metric-type (1-2)|route-map WORD}]",
vty_out(vty, "\n");
}
+ if (OSPF_IF_PARAM_CONFIGURED(params,
+ passive_interface)) {
+ vty_out(vty, " ip ospf passive");
+ if (params != IF_DEF_PARAMS(ifp) && rn)
+ vty_out(vty, " %pI4", &rn->p.u.prefix4);
+ vty_out(vty, "\n");
+ }
+
/* LDP-Sync print */
if (params && params->ldp_sync_info)
ospf_ldp_sync_if_write_config(vty, params);
static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
{
- struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
- struct interface *ifp;
- struct ospf_interface *oi;
- struct listnode *node = NULL;
int write = 0;
/* `router ospf' print. */
vty_out(vty, " no proactive-arp\n");
}
- FOR_ALL_INTERFACES (vrf, ifp)
- if (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp),
- passive_interface)
- && IF_DEF_PARAMS(ifp)->passive_interface
- != ospf->passive_interface_default) {
- vty_out(vty, " %spassive-interface %s\n",
- IF_DEF_PARAMS(ifp)->passive_interface ? ""
- : "no ",
- ifp->name);
- }
- for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
- if (!OSPF_IF_PARAM_CONFIGURED(oi->params, passive_interface))
- continue;
- if (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(oi->ifp),
- passive_interface)) {
- if (oi->params->passive_interface
- == IF_DEF_PARAMS(oi->ifp)->passive_interface)
- continue;
- } else if (oi->params->passive_interface
- == ospf->passive_interface_default)
- continue;
-
- vty_out(vty, " %spassive-interface %s %pI4\n",
- oi->params->passive_interface ? "" : "no ",
- oi->ifp->name, &oi->address->u.prefix4);
- }
-
/* TI-LFA print. */
if (ospf->ti_lfa_enabled) {
if (ospf->ti_lfa_protection_type == OSPF_TI_LFA_NODE_PROTECTION)
install_element(INTERFACE_NODE, &ip_ospf_area_cmd);
install_element(INTERFACE_NODE, &no_ip_ospf_area_cmd);
+ /* "ip ospf passive" commands. */
+ install_element(INTERFACE_NODE, &ip_ospf_passive_cmd);
+ install_element(INTERFACE_NODE, &no_ip_ospf_passive_cmd);
+
/* These commands are compatibitliy for previous version. */
install_element(INTERFACE_NODE, &ospf_authentication_key_cmd);
install_element(INTERFACE_NODE, &ospf_message_digest_key_cmd);
install_element(OSPF_NODE, &no_ospf_router_id_cmd);
/* "passive-interface" commands. */
+ install_element(OSPF_NODE, &ospf_passive_interface_default_cmd);
install_element(OSPF_NODE, &ospf_passive_interface_addr_cmd);
+ install_element(OSPF_NODE, &no_ospf_passive_interface_default_cmd);
install_element(OSPF_NODE, &no_ospf_passive_interface_addr_cmd);
/* "ospf abr-type" commands. */
install_element(OSPF_NODE, &ospf_max_multipath_cmd);
install_element(OSPF_NODE, &no_ospf_max_multipath_cmd);
+ vrf_cmd_init(NULL, &ospfd_privs);
+
/* Init interface related vty commands. */
ospf_vty_if_init();
new->proactive_arp = OSPF_PROACTIVE_ARP_DEFAULT;
- ospf_gr_helper_init(new);
+ ospf_gr_helper_instance_init(new);
ospf_asbr_external_aggregator_init(new);
for (ALL_LIST_ELEMENTS(om->ospf, node, nnode, ospf))
ospf_finish(ospf);
+ /* Cleanup GR */
+ ospf_gr_helper_stop();
+
/* Cleanup route maps */
route_map_finish();
struct route_node *rn;
struct ospf_nbr_nbma *nbr_nbma;
struct ospf_lsa *lsa;
- struct interface *ifp;
struct ospf_interface *oi;
struct ospf_area *area;
struct ospf_vl_data *vl_data;
if (ospf->vrf_id == VRF_DEFAULT)
ospf_ldp_sync_gbl_exit(ospf, true);
- /* Remove ospf interface config params: only passive-interface */
- FOR_ALL_INTERFACES (vrf, ifp) {
- struct ospf_if_params *params;
-
- params = IF_DEF_PARAMS(ifp);
- if (OSPF_IF_PARAM_CONFIGURED(params, passive_interface))
- UNSET_IF_PARAM(params, passive_interface);
- }
-
/* Reset interface. */
for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi))
ospf_if_free(oi);
if ((lsa = rn->info) != NULL) {
ospf_lsa_unlock(&lsa);
rn->info = NULL;
+ route_unlock_node(rn);
}
- route_unlock_node(rn);
}
route_table_finish(ospf->maxage_lsa);
list_delete(&ospf->oi_write_q);
/* Reset GR helper data structers */
- ospf_gr_helper_stop(ospf);
+ ospf_gr_helper_instance_stop(ospf);
close(ospf->fd);
stream_free(ospf->ibuf);
.description = "The PCEP module failed to connected to configured PCE",
.suggestion = "Check the connectivity between the PCC and the PCE"
},
+ {
+ .code = EC_PATH_PCEP_PROTOCOL_ERROR,
+ .title = "PCEP protocol error",
+ .description = "The PCE did not respect the PCEP protocol",
+ .suggestion = "Open an Issue with all relevant log files"
+ },
{
.code = EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
.title = "PCC connection error",
EC_PATH_PCEP_PCC_FINI,
EC_PATH_PCEP_PCC_CONF_UPDATE,
EC_PATH_PCEP_LIB_CONNECT,
+ EC_PATH_PCEP_PROTOCOL_ERROR,
EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
color = yang_dnode_get_uint32(args->dnode, "./color");
yang_dnode_get_ip(&endpoint, args->dnode, "./endpoint");
- policy = srte_policy_add(color, &endpoint);
+ policy = srte_policy_add(color, &endpoint, SRTE_ORIGIN_LOCAL, NULL);
nb_running_set_entry(args->dnode, policy);
SET_FLAG(policy->flags, F_POLICY_NEW);
policy = nb_running_get_entry(args->dnode, NULL, true);
preference = yang_dnode_get_uint32(args->dnode, "./preference");
- candidate = srte_candidate_add(policy, preference);
+ candidate =
+ srte_candidate_add(policy, preference, SRTE_ORIGIN_LOCAL, NULL);
nb_running_set_entry(args->dnode, candidate);
SET_FLAG(candidate->flags, F_CANDIDATE_NEW);
#include "pathd/path_pcep_controller.h"
#include "pathd/path_pcep_lib.h"
#include "pathd/path_pcep_config.h"
+#include "pathd/path_pcep_debug.h"
DEFINE_MTYPE(PATHD, PCEP, "PCEP module");
void *payload);
static int pcep_main_event_start_sync(int pcc_id);
static int pcep_main_event_start_sync_cb(struct path *path, void *arg);
+static int pcep_main_event_initiate_candidate(struct path *path);
static int pcep_main_event_update_candidate(struct path *path);
static int pcep_main_event_remove_candidate_segments(const char *originator,
bool force);
static struct path_metric *pcep_copy_metrics(struct path_metric *metric);
static struct path_hop *pcep_copy_hops(struct path_hop *hop);
+/* Other static functions */
+static void notify_status(struct path *path, bool not_changed);
+
/* Module Functions */
static int pcep_module_finish(void);
static int pcep_module_late_init(struct thread_master *tm);
XFREE(MTYPE_PCEP, path);
}
+/* ------------ Other Static Functions ------------ */
+
+void notify_status(struct path *path, bool not_changed)
+{
+ struct path *resp = NULL;
+
+ if ((resp = path_pcep_config_get_path(&path->nbkey))) {
+ resp->srp_id = path->srp_id;
+ flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ "(%s) Send report for candidate path %s", __func__,
+ path->name);
+ pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp,
+ not_changed);
+ }
+}
/* ------------ Main Thread Even Handler ------------ */
case PCEP_MAIN_EVENT_START_SYNC:
ret = pcep_main_event_start_sync(pcc_id);
break;
+ case PCEP_MAIN_EVENT_INITIATE_CANDIDATE:
+ assert(payload != NULL);
+ ret = pcep_main_event_initiate_candidate(
+ (struct path *)payload);
+ break;
case PCEP_MAIN_EVENT_UPDATE_CANDIDATE:
assert(payload != NULL);
ret = pcep_main_event_update_candidate((struct path *)payload);
return 1;
}
+int pcep_main_event_initiate_candidate(struct path *path)
+{
+ int ret = 0;
+
+ ret = path_pcep_config_initiate_path(path);
+ if (path->do_remove) {
+ struct pcep_error *error;
+ error = XCALLOC(MTYPE_PCEP, sizeof(*error));
+ error->path = path;
+ error->error_type = PCEP_ERRT_INVALID_OPERATION;
+ switch (ret) {
+ case ERROR_19_1:
+ error->error_value =
+ PCEP_ERRV_LSP_UPDATE_FOR_NON_DELEGATED_LSP;
+ break;
+ case ERROR_19_3:
+ error->error_value =
+ PCEP_ERRV_LSP_UPDATE_UNKNOWN_PLSP_ID;
+ break;
+ case ERROR_19_9:
+ error->error_value = PCEP_ERRV_LSP_NOT_PCE_INITIATED;
+ break;
+ default:
+ zlog_warn("(%s)PCE tried to REMOVE unknown error!",
+ __func__);
+ XFREE(MTYPE_PCEP, error);
+ pcep_free_path(path);
+ return ret;
+ break;
+ }
+ pcep_ctrl_send_error(pcep_g->fpt, path->pcc_id, error);
+ } else if (ret != PATH_NB_ERR && path->srp_id != 0)
+ notify_status(path, ret == PATH_NB_NO_CHANGE);
+ return ret;
+}
+
int pcep_main_event_update_candidate(struct path *path)
{
- struct path *resp = NULL;
int ret = 0;
ret = path_pcep_config_update_path(path);
- if (ret != PATH_NB_ERR && path->srp_id != 0) {
- if ((resp = path_pcep_config_get_path(&path->nbkey))) {
- resp->srp_id = path->srp_id;
- pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp,
- ret == PATH_NB_NO_CHANGE);
- }
- }
+ if (ret != PATH_NB_ERR && path->srp_id != 0)
+ notify_status(path, ret == PATH_NB_NO_CHANGE);
return ret;
}
extern struct pcep_glob *pcep_g;
+struct pcep_error {
+ struct path *path;
+ int error_type;
+ int error_value;
+ /* Rfc 8281 PcInitiated error on bad values */
+#define ERROR_19_1 1
+#define ERROR_19_3 2
+#define ERROR_19_9 3
+};
+
/* Path Helper Functions */
struct path *pcep_new_path(void);
struct path_hop *pcep_new_hop(void);
#define MAX_FLOAT_LEN 22
#define INETADDR4_MAXLEN 16
#define INETADDR6_MAXLEN 40
+#define INITIATED_CANDIDATE_PREFERENCE 255
+#define INITIATED_POLICY_COLOR 1
static void copy_candidate_objfun_info(struct srte_candidate *candidate,
.plsp_id = 0,
.name = name,
.type = candidate->type,
- .srp_id = 0,
+ .srp_id = policy->srp_id,
.req_id = 0,
.binding_sid = policy->binding_sid,
.status = status,
return hop;
}
+int path_pcep_config_initiate_path(struct path *path)
+{
+ struct srte_policy *policy;
+ struct srte_candidate *candidate;
+
+ if (path->do_remove) {
+ zlog_warn("PCE %s tried to REMOVE pce-initiate a path ",
+ path->originator);
+ candidate = lookup_candidate(&path->nbkey);
+ if (candidate) {
+ if (!path->is_delegated) {
+ zlog_warn(
+ "(%s)PCE tried to REMOVE but it's not Delegated!",
+ __func__);
+ return ERROR_19_1;
+ }
+ if (candidate->type != SRTE_CANDIDATE_TYPE_DYNAMIC) {
+ zlog_warn(
+ "(%s)PCE tried to REMOVE but it's not PCE origin!",
+ __func__);
+ return ERROR_19_9;
+ }
+ zlog_warn(
+ "(%s)PCE tried to REMOVE found canidate!, let's remove",
+ __func__);
+ candidate->policy->srp_id = path->srp_id;
+ SET_FLAG(candidate->policy->flags, F_POLICY_DELETED);
+ SET_FLAG(candidate->flags, F_CANDIDATE_DELETED);
+ } else {
+ zlog_warn("(%s)PCE tried to REMOVE not existing LSP!",
+ __func__);
+ return ERROR_19_3;
+ }
+ srte_apply_changes();
+ } else {
+ assert(!IS_IPADDR_NONE(&path->nbkey.endpoint));
+
+ if (path->nbkey.preference == 0)
+ path->nbkey.preference = INITIATED_CANDIDATE_PREFERENCE;
+
+ if (path->nbkey.color == 0)
+ path->nbkey.color = INITIATED_POLICY_COLOR;
+
+ candidate = lookup_candidate(&path->nbkey);
+ if (!candidate) {
+ policy = srte_policy_add(
+ path->nbkey.color, &path->nbkey.endpoint,
+ SRTE_ORIGIN_PCEP, path->originator);
+ strlcpy(policy->name, path->name, sizeof(policy->name));
+ policy->binding_sid = path->binding_sid;
+ SET_FLAG(policy->flags, F_POLICY_NEW);
+ candidate = srte_candidate_add(
+ policy, path->nbkey.preference,
+ SRTE_ORIGIN_PCEP, path->originator);
+ strlcpy(candidate->name, path->name,
+ sizeof(candidate->name));
+ SET_FLAG(candidate->flags, F_CANDIDATE_NEW);
+ } else {
+ policy = candidate->policy;
+ if ((path->originator != candidate->originator)
+ || (path->originator != policy->originator)) {
+ /* There is already an initiated path from
+ * another PCE, show a warning and regect the
+ * initiated path */
+ zlog_warn(
+ "PCE %s tried to initiate a path already initiated by PCE %s",
+ path->originator,
+ candidate->originator);
+ return 1;
+ }
+ if ((policy->protocol_origin != SRTE_ORIGIN_PCEP)
+ || (candidate->protocol_origin
+ != SRTE_ORIGIN_PCEP)) {
+ /* There is already an initiated path from
+ * another PCE, show a warning and regect the
+ * initiated path */
+ zlog_warn(
+ "PCE %s tried to initiate a path created localy",
+ path->originator);
+ return 1;
+ }
+ }
+ return path_pcep_config_update_path(path);
+ }
+ return 0;
+}
+
int path_pcep_config_update_path(struct path *path)
{
assert(path != NULL);
char *candidate_name(struct srte_candidate *candidate)
{
- return asprintfrr(MTYPE_PCEP, "%s-%s", candidate->policy->name,
- candidate->name);
+ if (candidate->protocol_origin == SRTE_ORIGIN_PCEP
+ || candidate->protocol_origin == SRTE_ORIGIN_BGP)
+ return asprintfrr(MTYPE_PCEP, "%s", candidate->policy->name);
+ else
+ return asprintfrr(MTYPE_PCEP, "%s-%s", candidate->policy->name,
+ candidate->name);
}
enum pcep_lsp_operational_status
void path_pcep_refine_path(struct path *path);
struct path *path_pcep_config_get_path(struct lsp_nb_key *key);
void path_pcep_config_list_path(path_list_cb_t cb, void *arg);
+int path_pcep_config_initiate_path(struct path *path);
int path_pcep_config_update_path(struct path *path);
struct path *candidate_to_path(struct srte_candidate *candidate);
EV_PCEPLIB_EVENT,
EV_RESET_PCC_SESSION,
EV_SEND_REPORT,
+ EV_SEND_ERROR,
EV_PATH_REFINED
};
}
+int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id,
+ struct pcep_error *error)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, pcc_id, EV_SEND_ERROR, 0, error);
+}
+
+
/* ------------ Internal Functions Called from Main Thread ------------ */
int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res)
path);
}
+void pcep_thread_initiate_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path)
+{
+ send_to_main(ctrl_state, pcc_id, PCEP_MAIN_EVENT_INITIATE_CANDIDATE,
+ path);
+}
+
void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state)
{
struct pcep_refine_path_event_data *refine_data = NULL;
struct path *path_copy = NULL;
+ struct pcep_error *error = NULL;
switch (type) {
case EV_UPDATE_PCC_OPTS:
refine_data = (struct pcep_refine_path_event_data *)payload;
pcep_thread_path_refined_event(ctrl_state, refine_data);
break;
+ case EV_SEND_ERROR:
+ assert(payload != NULL);
+ error = (struct pcep_error *)payload;
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_pcc_send_error(ctrl_state, pcc_state, error,
+ (bool)sub_type);
+ break;
default:
flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
"Unexpected event received in controller thread: %u",
enum pcep_main_event_type {
PCEP_MAIN_EVENT_UNDEFINED = 0,
PCEP_MAIN_EVENT_START_SYNC,
+ PCEP_MAIN_EVENT_INITIATE_CANDIDATE,
PCEP_MAIN_EVENT_UPDATE_CANDIDATE,
PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP,
};
int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
struct path *path, bool is_stable);
+int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id,
+ struct pcep_error *error);
+
/* Functions called from the controller thread */
void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id);
void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id,
struct path *path);
+void pcep_thread_initiate_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path);
void pcep_thread_cancel_timer(struct thread **thread);
void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id,
int retry_count, struct thread **thread);
switch (tlv_type) {
case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR:
return "NO_PATH_VECTOR";
+ case PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST:
+ return "OBJECTIVE_FUNCTION_LIST";
+ case PCEP_OBJ_TLV_TYPE_VENDOR_INFO:
+ return "VENDOR_INFO";
case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
return "STATEFUL_PCE_CAPABILITY";
case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
return "PATH_SETUP_TYPE";
case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
return "PATH_SETUP_TYPE_CAPABILITY";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID:
+ return "SRPOLICY_POL_ID";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME:
+ return "SRPOLICY_POL_NAME";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID:
+ return "SRPOLICY_CPATH_ID";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE:
+ return "SRPOLICY_CPATH_PREFERENCE";
+ case PCEP_OBJ_TLV_TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case PCEP_OBJ_TLV_TYPE_ARBITRARY:
+ return "ARBITRARY";
default:
return "UNKNOWN";
}
#define DEFAULT_LSAP_SETUP_PRIO 4
#define DEFAULT_LSAP_HOLDING_PRIO 4
#define DEFAULT_LSAP_LOCAL_PRETECTION false
+#define MAX_PATH_NAME_SIZE 255
/* pceplib logging callback */
static int pceplib_logging_cb(int level, const char *fmt, va_list args);
static void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp);
static void pcep_lib_parse_lspa(struct path *path,
struct pcep_object_lspa *lspa);
+static void pcep_lib_parse_lsp_symbolic_name(
+ struct path *path, struct pcep_object_tlv_symbolic_path_name *tlv);
static void pcep_lib_parse_metric(struct path *path,
struct pcep_object_metric *obj);
+static void
+pcep_lib_parse_endpoints_ipv4(struct path *path,
+ struct pcep_object_endpoints_ipv4 *obj);
+static void
+pcep_lib_parse_endpoints_ipv6(struct path *path,
+ struct pcep_object_endpoints_ipv6 *obj);
+static void pcep_lib_parse_vendor_info(struct path *path,
+ struct pcep_object_vendor_info *obj);
static void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero);
static struct path_hop *pcep_lib_parse_ero_sr(struct path_hop *next,
struct pcep_ro_subobj_sr *sr);
}
config->support_stateful_pce_lsp_update = true;
- config->support_pce_lsp_instantiation = false;
+ config->support_pce_lsp_instantiation = pcep_options->pce_initiated;
config->support_include_db_version = false;
config->support_lsp_triggered_resync = false;
config->support_lsp_delta_sync = false;
}
}
-struct pcep_message *pcep_lib_format_error(int error_type, int error_value)
+struct pcep_message *pcep_lib_format_error(int error_type, int error_value,
+ struct path *path)
{
- return pcep_msg_create_error(error_type, error_value);
+ double_linked_list *objs, *srp_tlvs;
+ struct pcep_object_srp *srp;
+ struct pcep_object_tlv_header *tlv;
+
+ if ((path == NULL) || (path->srp_id == 0))
+ return pcep_msg_create_error(error_type, error_value);
+
+ objs = dll_initialize();
+ srp_tlvs = dll_initialize();
+ tlv = (struct pcep_object_tlv_header *)pcep_tlv_create_path_setup_type(
+ SR_TE_PST);
+ dll_append(srp_tlvs, tlv);
+ srp = pcep_obj_create_srp(path->do_remove, path->srp_id, srp_tlvs);
+ dll_append(objs, srp);
+ return pcep_msg_create_error_with_objects(error_type, error_value,
+ objs);
}
struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid)
struct pcep_object_metric *metric = NULL;
struct pcep_object_bandwidth *bandwidth = NULL;
struct pcep_object_objective_function *of = NULL;
+ struct pcep_object_endpoints_ipv4 *epv4 = NULL;
+ struct pcep_object_endpoints_ipv6 *epv6 = NULL;
+ struct pcep_object_vendor_info *vendor_info = NULL;
path = pcep_new_path();
path->has_pce_objfun = true;
path->pce_objfun = of->of_code;
break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV4):
+ epv4 = (struct pcep_object_endpoints_ipv4 *)obj;
+ pcep_lib_parse_endpoints_ipv4(path, epv4);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV6):
+ epv6 = (struct pcep_object_endpoints_ipv6 *)obj;
+ pcep_lib_parse_endpoints_ipv6(path, epv6);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_VENDOR_INFO,
+ PCEP_OBJ_TYPE_VENDOR_INFO):
+ vendor_info = (struct pcep_object_vendor_info *)obj;
+ pcep_lib_parse_vendor_info(path, vendor_info);
+ break;
default:
flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT,
"Unexpected PCEP object %s (%u) / %s (%u)",
tlv = (struct pcep_object_tlv_header *)
pcep_tlv_create_tlv_arbitrary(
binding_sid_lsp_tlv_data,
- sizeof(binding_sid_lsp_tlv_data), 65505);
+ sizeof(binding_sid_lsp_tlv_data),
+ PCEP_OBJ_TYPE_CISCO_BSID);
assert(tlv != NULL);
dll_append(lsp_tlvs, tlv);
}
double_linked_list_node *node;
struct pcep_object_tlv_header *tlv;
+ if (tlvs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty RP's TLV plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
+ return;
+ }
/* We ignore the other flags and priority for now */
path->req_id = rp->request_id;
path->has_pce_objfun = false;
path->do_remove = srp->flag_lsp_remove;
path->srp_id = srp->srp_id_number;
+ if (tlvs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty SRP's TLV plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
+ return;
+ }
for (node = tlvs->head; node != NULL; node = node->next_node) {
tlv = (struct pcep_object_tlv_header *)node->data;
switch (tlv->type) {
double_linked_list *tlvs = lsp->header.tlv_list;
double_linked_list_node *node;
struct pcep_object_tlv_header *tlv;
+ struct pcep_object_tlv_symbolic_path_name *name;
+ struct pcep_object_tlv_arbitrary *arb_tlv;
path->plsp_id = lsp->plsp_id;
path->status = lsp->operational_status;
path->is_synching = lsp->flag_s;
path->is_delegated = lsp->flag_d;
- if (tlvs == NULL)
+ if (tlvs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty LSP's TLV plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
return;
+ }
for (node = tlvs->head; node != NULL; node = node->next_node) {
tlv = (struct pcep_object_tlv_header *)node->data;
switch (tlv->type) {
+ case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
+ name = (struct pcep_object_tlv_symbolic_path_name *)tlv;
+ pcep_lib_parse_lsp_symbolic_name(path, name);
+ break;
+ case PCEP_OBJ_TYPE_CISCO_BSID:
+ arb_tlv = (struct pcep_object_tlv_arbitrary *)tlv;
+ memcpy(&path->binding_sid, arb_tlv->data + 2,
+ sizeof(path->binding_sid));
+ path->binding_sid = ntohl(path->binding_sid);
+ path->binding_sid = (path->binding_sid >> 12);
+ break;
default:
flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
"Unexpected LSP TLV %s (%u)",
}
}
+void pcep_lib_parse_lsp_symbolic_name(
+ struct path *path, struct pcep_object_tlv_symbolic_path_name *tlv)
+{
+ uint16_t size = tlv->symbolic_path_name_length;
+ assert(path->name == NULL);
+ size = size > MAX_PATH_NAME_SIZE ? MAX_PATH_NAME_SIZE : size;
+ path->name = XCALLOC(MTYPE_PCEP, size);
+ strlcpy((char *)path->name, tlv->symbolic_path_name, size + 1);
+}
+
void pcep_lib_parse_lspa(struct path *path, struct pcep_object_lspa *lspa)
{
path->has_affinity_filters = true;
path->first_metric = metric;
}
+void pcep_lib_parse_endpoints_ipv4(struct path *path,
+ struct pcep_object_endpoints_ipv4 *obj)
+{
+ SET_IPADDR_V4(&path->pcc_addr);
+ path->pcc_addr.ipaddr_v4 = obj->src_ipv4;
+ SET_IPADDR_V4(&path->nbkey.endpoint);
+ path->nbkey.endpoint.ipaddr_v4 = obj->dst_ipv4;
+}
+
+void pcep_lib_parse_endpoints_ipv6(struct path *path,
+ struct pcep_object_endpoints_ipv6 *obj)
+{
+ SET_IPADDR_V6(&path->pcc_addr);
+ path->pcc_addr.ipaddr_v6 = obj->src_ipv6;
+ SET_IPADDR_V6(&path->nbkey.endpoint);
+ path->nbkey.endpoint.ipaddr_v6 = obj->dst_ipv6;
+}
+
+void pcep_lib_parse_vendor_info(struct path *path,
+ struct pcep_object_vendor_info *obj)
+{
+ if (obj->enterprise_number == ENTERPRISE_NUMBER_CISCO
+ && obj->enterprise_specific_info == ENTERPRISE_COLOR_CISCO)
+ path->nbkey.color = obj->enterprise_specific_info1;
+ else
+ path->nbkey.color = 0;
+}
+
void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero)
{
struct path_hop *hop = NULL;
double_linked_list_node *node;
struct pcep_object_ro_subobj *obj;
+ if (objs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty ERO's sub_obj plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
+ return;
+ }
for (node = objs->tail; node != NULL; node = node->prev_node) {
obj = (struct pcep_object_ro_subobj *)node->data;
switch (obj->ro_subobj_type) {
struct path *path);
struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid);
-struct pcep_message *pcep_lib_format_error(int error_type, int error_value);
+struct pcep_message *pcep_lib_format_error(int error_type, int error_value,
+ struct path *path);
struct path *pcep_lib_parse_path(struct pcep_message *msg);
void pcep_lib_parse_capabilities(struct pcep_message *msg,
struct pcep_caps *caps);
struct pcep_message *msg);
static void send_pcep_error(struct pcc_state *pcc_state,
enum pcep_error_type error_type,
- enum pcep_error_value error_value);
+ enum pcep_error_value error_value,
+ struct path *trigger_path);
static void send_report(struct pcc_state *pcc_state, struct path *path);
static void send_comp_request(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
return;
}
- PCEP_DEBUG("%s Send report for candidate path %s", pcc_state->tag,
- path->name);
+ PCEP_DEBUG("(%s)%s Send report for candidate path %s", __func__,
+ pcc_state->tag, path->name);
/* ODL and Cisco requires the first reported
* LSP to have a DOWN status, the later status changes
/* If no update is expected and the real status wasn't down, we need to
* send a second report with the real status */
if (is_stable && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) {
+ PCEP_DEBUG("(%s)%s Send report for candidate path (!DOWN) %s",
+ __func__, pcc_state->tag, path->name);
path->srp_id = 0;
path->status = real_status;
send_report(pcc_state, path);
}
+void pcep_pcc_send_error(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct pcep_error *error,
+ bool sub_type)
+{
+
+ PCEP_DEBUG("(%s) Send error after PcInitiated ", __func__);
+
+
+ send_pcep_error(pcc_state, error->error_type, error->error_value,
+ error->path);
+ pcep_free_path(error->path);
+ XFREE(MTYPE_PCEP, error);
+}
/* ------------ Timeout handler ------------ */
void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state,
PCEP_DEBUG("%s Candidate path %s removed", pcc_state->tag,
path->name);
path->was_removed = true;
+ /* Removed as response to a PcInitiated 'R'emove*/
+ /* RFC 8281 #5.4 LSP Deletion*/
+ path->do_remove = path->was_removed;
if (pcc_state->caps.is_stateful)
send_report(pcc_state, path);
return;
struct pcc_state *pcc_state,
struct pcep_message *msg)
{
- PCEP_DEBUG("%s Received LSP initiate, not supported yet",
- pcc_state->tag);
+ char err[MAX_ERROR_MSG_SIZE] = "";
+ struct path *path;
+
+ path = pcep_lib_parse_path(msg);
+
+ if (!pcc_state->pce_opts->config_opts.pce_initiated) {
+ /* PCE Initiated is not enabled */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "Not allowed PCE initiated path received: %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state, PCEP_ERRT_LSP_INSTANTIATE_ERROR,
+ PCEP_ERRV_UNACCEPTABLE_INSTANTIATE_ERROR, path);
+ return;
+ }
- /* TODO when we support both PCC and PCE initiated sessions,
- * we should first check the session type before
- * rejecting this message. */
- send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
- PCEP_ERRV_LSP_NOT_PCE_INITIATED);
+ if (path->do_remove) {
+ // lookup in nbkey sequential as no endpoint
+ struct nbkey_map_data *key;
+ char endpoint[46];
+
+ frr_each (nbkey_map, &pcc_state->nbkey_map, key) {
+ ipaddr2str(&key->nbkey.endpoint, endpoint,
+ sizeof(endpoint));
+ flog_warn(
+ EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "FOR_EACH nbkey [color (%d) endpoint (%s)] path [plsp_id (%d)] ",
+ key->nbkey.color, endpoint, path->plsp_id);
+ if (path->plsp_id == key->plspid) {
+ flog_warn(
+ EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "FOR_EACH MATCH nbkey [color (%d) endpoint (%s)] path [plsp_id (%d)] ",
+ key->nbkey.color, endpoint,
+ path->plsp_id);
+ path->nbkey = key->nbkey;
+ break;
+ }
+ }
+ } else {
+ if (path->first_hop == NULL /*ero sets first_hop*/) {
+ /* If the PCC receives a PCInitiate message without an
+ * ERO and the R flag in the SRP object != zero, then it
+ * MUST send a PCErr message with Error-type=6
+ * (Mandatory Object missing) and Error-value=9 (ERO
+ * object missing). */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "ERO object missing or incomplete : %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state,
+ PCEP_ERRT_LSP_INSTANTIATE_ERROR,
+ PCEP_ERRV_INTERNAL_ERROR, path);
+ return;
+ }
+
+ if (path->plsp_id != 0) {
+ /* If the PCC receives a PCInitiate message with a
+ * non-zero PLSP-ID and the R flag in the SRP object set
+ * to zero, then it MUST send a PCErr message with
+ * Error-type=19 (Invalid Operation) and Error-value=8
+ * (Non-zero PLSP-ID in the LSP Initiate Request) */
+ flog_warn(
+ EC_PATH_PCEP_PROTOCOL_ERROR,
+ "PCE initiated path with non-zero PLSP ID: %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_INIT_NON_ZERO_PLSP_ID,
+ path);
+ return;
+ }
+
+ if (path->name == NULL) {
+ /* If the PCC receives a PCInitiate message without a
+ * SYMBOLIC-PATH-NAME TLV, then it MUST send a PCErr
+ * message with Error-type=10 (Reception of an invalid
+ * object) and Error-value=8 (SYMBOLIC-PATH-NAME TLV
+ * missing) */
+ flog_warn(
+ EC_PATH_PCEP_PROTOCOL_ERROR,
+ "PCE initiated path without symbolic name: %s",
+ format_pcep_message(msg));
+ send_pcep_error(
+ pcc_state, PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_SYMBOLIC_PATH_NAME_TLV_MISSING, path);
+ return;
+ }
+ }
+
+ /* TODO: If there is a conflict with the symbolic path name of an
+ * existing LSP, the PCC MUST send a PCErr message with Error-type=23
+ * (Bad Parameter value) and Error-value=1 (SYMBOLIC-PATH-NAME in
+ * use) */
+
+ specialize_incoming_path(pcc_state, path);
+ /* TODO: Validate the PCC address received from the PCE is valid */
+ PCEP_DEBUG("%s Received LSP initiate", pcc_state->tag);
+ PCEP_DEBUG_PATH("%s", format_path(path));
+
+ if (validate_incoming_path(pcc_state, path, err, sizeof(err))) {
+ pcep_thread_initiate_path(ctrl_state, pcc_state->id, path);
+ } else {
+ /* FIXME: Monitor the amount of errors from the PCE and
+ * possibly disconnect and blacklist */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "Unsupported PCEP protocol feature: %s", err);
+ pcep_free_path(path);
+ send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_NOT_PCE_INITIATED, path);
+ }
}
void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
pcc_state->tag, path->req_id);
PCEP_DEBUG_PATH("%s", format_path(path));
send_pcep_error(pcc_state, PCEP_ERRT_UNKNOWN_REQ_REF,
- PCEP_ERRV_UNASSIGNED);
+ PCEP_ERRV_UNASSIGNED, NULL);
return;
}
void send_pcep_error(struct pcc_state *pcc_state,
enum pcep_error_type error_type,
- enum pcep_error_value error_value)
+ enum pcep_error_value error_value,
+ struct path *trigger_path)
{
struct pcep_message *msg;
PCEP_DEBUG("%s Sending PCEP error type %s (%d) value %s (%d)",
pcc_state->tag, pcep_error_type_name(error_type), error_type,
pcep_error_value_name(error_type, error_value), error_value);
- msg = pcep_lib_format_error(error_type, error_value);
+ msg = pcep_lib_format_error(error_type, error_value, trigger_path);
send_pcep_message(pcc_state, msg);
}
/* Updates the path for the PCC */
void specialize_incoming_path(struct pcc_state *pcc_state, struct path *path)
{
- set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr);
+ if (IS_IPADDR_NONE(&path->pcc_addr))
+ set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr);
path->sender = pcc_state->pce_opts->addr;
path->pcc_id = pcc_state->id;
path->update_origin = SRTE_ORIGIN_PCEP;
}
if (err_type != 0) {
- send_pcep_error(pcc_state, err_type, err_value);
+ send_pcep_error(pcc_state, err_type, err_value, NULL);
return false;
}
if (!pcc_state->is_best) {
return;
}
- /* TODO: Add a timer to retry the computation request ? */
specialize_outgoing_path(pcc_state, req->path);
send_pcep_message(pcc_state, msg);
req->was_sent = true;
- /* TODO: Enable this back when the pcep config changes are merged back
- */
- // timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds;
- timeout = 30;
+ timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds;
pcep_thread_schedule_timeout(ctrl_state, pcc_state->id,
TO_COMPUTATION_REQUEST, timeout,
(void *)req, &req->t_retry);
}
}
-
/* ------------ Data Structure Helper Functions ------------ */
void lookup_plspid(struct pcc_state *pcc_state, struct path *path)
void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state, struct path *path,
bool is_stable);
+void pcep_pcc_send_error(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct pcep_error *path,
+ bool is_stable);
int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id,
struct pcc_state **pcc_state_list);
int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state,
* @param endpoint The IP address of the policy endpoint
* @return The created policy
*/
-struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint)
+struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint,
+ enum srte_protocol_origin origin,
+ const char *originator)
{
struct srte_policy *policy;
policy->color = color;
policy->endpoint = *endpoint;
policy->binding_sid = MPLS_LABEL_NONE;
+ policy->protocol_origin = origin;
+ if (originator != NULL)
+ strlcpy(policy->originator, originator,
+ sizeof(policy->originator));
+
RB_INIT(srte_candidate_head, &policy->candidate_paths);
RB_INSERT(srte_policy_head, &srte_policies, policy);
* @return The added candidate path
*/
struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
- uint32_t preference)
+ uint32_t preference,
+ enum srte_protocol_origin origin,
+ const char *originator)
{
struct srte_candidate *candidate;
struct srte_lsp *lsp;
candidate->preference = preference;
candidate->policy = policy;
candidate->type = SRTE_CANDIDATE_TYPE_UNDEFINED;
- candidate->discriminator = frr_weak_random();
+ candidate->discriminator = rand();
+ candidate->protocol_origin = origin;
+ if (originator != NULL) {
+ strlcpy(candidate->originator, originator,
+ sizeof(candidate->originator));
+ lsp->protocol_origin = origin;
+ }
+ if (candidate->protocol_origin == SRTE_ORIGIN_PCEP
+ || candidate->protocol_origin == SRTE_ORIGIN_BGP) {
+ candidate->type = SRTE_CANDIDATE_TYPE_DYNAMIC;
+ }
lsp->candidate = candidate;
candidate->lsp = lsp;
/* Binding SID */
mpls_label_t binding_sid;
+ /* The Protocol-Origin. */
+ enum srte_protocol_origin protocol_origin;
+
+ /* The Originator */
+ char originator[64];
+
/* Operational Status of the policy */
enum srte_policy_status status;
#define F_POLICY_NEW 0x0002
#define F_POLICY_MODIFIED 0x0004
#define F_POLICY_DELETED 0x0008
+ /* SRP id for PcInitiated support */
+ int srp_id;
};
RB_HEAD(srte_policy_head, srte_policy);
RB_PROTOTYPE(srte_policy_head, srte_policy, entry, srte_policy_compare)
void srte_segment_set_local_modification(struct srte_segment_list *s_list,
struct srte_segment_entry *s_entry,
uint32_t ted_sid);
-struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint);
+struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint,
+ enum srte_protocol_origin origin,
+ const char *originator);
void srte_policy_del(struct srte_policy *policy);
struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint);
int srte_policy_update_ted_sid(void);
void srte_clean_zebra(void);
void srte_policy_apply_changes(struct srte_policy *policy);
struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
- uint32_t preference);
+ uint32_t preference,
+ enum srte_protocol_origin origin,
+ const char *originator);
void srte_candidate_del(struct srte_candidate *candidate);
void srte_candidate_set_bandwidth(struct srte_candidate *candidate,
float bandwidth, bool required);
static const struct frr_yang_module_info *const pbrd_yang_modules[] = {
&frr_filter_info,
&frr_interface_info,
+ &frr_vrf_info,
};
FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT,
}
};
+extern struct zebra_privs_t pbr_privs;
+
void pbr_vty_init(void)
{
cmd_variable_handler_register(pbr_map_name);
+ vrf_cmd_init(NULL, &pbr_privs);
+
install_node(&interface_node);
if_cmd_init();
bool flag_c;
};
+#define ENTERPRISE_NUMBER_CISCO 9
+#define ENTERPRISE_COLOR_CISCO 65540
/* RFC 7470 */
struct pcep_object_vendor_info {
struct pcep_object_header header;
uint32_t enterprise_number;
uint32_t enterprise_specific_info;
+ uint32_t enterprise_specific_info1; /* cisco sends color for PcInit */
+ uint32_t enterprise_specific_info2;
+ uint32_t enterprise_specific_info3;
};
/* RFC 8282 */
struct pcep_object_vendor_info *obj =
(struct pcep_object_vendor_info *)common_object_create(
hdr, sizeof(struct pcep_object_vendor_info));
+
obj->enterprise_number = ntohl(*((uint32_t *)(obj_buf)));
obj->enterprise_specific_info = ntohl(*((uint32_t *)(obj_buf + 4)));
+ if (obj->enterprise_number == ENTERPRISE_NUMBER_CISCO
+ && obj->enterprise_specific_info == ENTERPRISE_COLOR_CISCO)
+ obj->enterprise_specific_info1 =
+ ntohl(*((uint32_t *)(obj_buf + 8)));
+ else
+ obj->enterprise_specific_info1 = 0;
return (struct pcep_object_header *)obj;
}
* https://www.iana.org/assignments/pcep/pcep.xhtml */
enum pcep_object_tlv_types {
PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR = 1,
- PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST = 4, /* RFC 5541 */
+ PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST = 4, /* RFC 5541 */
PCEP_OBJ_TLV_TYPE_VENDOR_INFO = 7, /* RFC 7470 */
PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY = 16, /* RFC 8231 */
- PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME = 17, /* RFC 8232 */
- PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS = 18, /* RFC 8231 */
- PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS = 19, /* RFC 8231 */
+ PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME = 17, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS = 18, /* RFC 8231 */
+ PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS = 19, /* RFC 8231 */
PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE = 20, /* RFC 8232 */
PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC = 21, /* RFC 8232 */
PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION = 23, /* RFC 8232 */
- PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID = 24, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID = 24, /* RFC 8232 */
PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY =
26, /* draft-ietf-pce-segment-routing-16 */
PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE = 28, /* RFC 8408 */
PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE =
63, /*TDB5 draft-barth-pce-segment-routing-policy-cp-04 */
PCEP_OBJ_TLV_TYPE_UNKNOWN = 128,
- PCEP_OBJ_TLV_TYPE_ARBITRARY =
- 65533 /* Max IANA To write arbitrary data */
+ PCEP_OBJ_TYPE_CISCO_BSID = 65505,
+ /* Max IANA To write arbitrary data */
+ PCEP_OBJ_TLV_TYPE_ARBITRARY = 65533
};
+
struct pcep_object_tlv_header {
enum pcep_object_tlv_types type;
/* Pointer into encoded_message field from the pcep_message */
return NULL;
}
- tlv_decoder_funcptr tlv_decoder = tlv_decoders[tlv_hdr.type];
+ tlv_decoder_funcptr tlv_decoder = NULL;
+ if (tlv_hdr.type == PCEP_OBJ_TYPE_CISCO_BSID) {
+ pcep_log(LOG_INFO,
+ "%s: Cisco BSID TLV decoder found for TLV type [%d]",
+ __func__, tlv_hdr.type);
+ tlv_decoder = tlv_decoders[PCEP_OBJ_TLV_TYPE_ARBITRARY];
+ } else {
+ tlv_decoder = tlv_decoders[tlv_hdr.type];
+ }
if (tlv_decoder == NULL) {
pcep_log(LOG_INFO, "%s: No TLV decoder found for TLV type [%d]",
__func__, tlv_hdr.type);
CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP);
CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 60);
CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list);
- /* The TLV with ID 65505 is not recognized, and its not in the list */
- CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 2);
+ /* The TLV with ID 65505 is now recognized, and its in the list */
+ CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 3);
CU_ASSERT_EQUAL(lsp->plsp_id, 524303);
CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN);
CU_ASSERT_TRUE(lsp->flag_a);
return yang_dnode_get_string(vrf_node, "./name");
}
+/**
+ * Compatibility function to keep the legacy mesh group CLI behavior:
+ * Delete group when there are no more configurations in it.
+ *
+ * NOTE:
+ * Don't forget to call `nb_cli_apply_changes` after this.
+ */
+static void pim_cli_legacy_mesh_group_behavior(struct vty *vty,
+ const char *gname)
+{
+ const char *vrfname;
+ char xpath_value[XPATH_MAXLEN];
+ char xpath_member_value[XPATH_MAXLEN];
+ const struct lyd_node *member_dnode;
+
+ vrfname = pim_cli_get_vrf_name(vty);
+ if (vrfname == NULL)
+ return;
+
+ /* Get mesh group base XPath. */
+ snprintf(xpath_value, sizeof(xpath_value),
+ FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']",
+ "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname);
+ /* Group must exists, otherwise just quit. */
+ if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value))
+ return;
+
+ /* Group members check: */
+ strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value));
+ strlcat(xpath_member_value, "/members", sizeof(xpath_member_value));
+ if (yang_dnode_exists(vty->candidate_config->dnode,
+ xpath_member_value)) {
+ member_dnode = yang_dnode_get(vty->candidate_config->dnode,
+ xpath_member_value);
+ if (!yang_is_last_list_dnode(member_dnode))
+ return;
+ }
+
+ /* Source address check: */
+ strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value));
+ strlcat(xpath_member_value, "/source", sizeof(xpath_member_value));
+ if (yang_dnode_exists(vty->candidate_config->dnode, xpath_member_value))
+ return;
+
+ /* No configurations found: delete it. */
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
+}
+
DEFUN (clear_ip_interfaces,
clear_ip_interfaces_cmd,
"clear ip interfaces [vrf NAME]",
return nb_cli_apply_changes(vty, NULL);
}
-DEFUN (ip_msdp_mesh_group_member,
- ip_msdp_mesh_group_member_cmd,
- "ip msdp mesh-group WORD member A.B.C.D",
- IP_STR
- CFG_MSDP_STR
- "Configure MSDP mesh-group\n"
- "mesh group name\n"
- "mesh group member\n"
- "peer ip address\n")
+DEFPY(ip_msdp_mesh_group_member,
+ ip_msdp_mesh_group_member_cmd,
+ "ip msdp mesh-group WORD$gname member A.B.C.D$maddr",
+ IP_STR
+ CFG_MSDP_STR
+ "Configure MSDP mesh-group\n"
+ "Mesh group name\n"
+ "Mesh group member\n"
+ "Peer IP address\n")
{
const char *vrfname;
- char msdp_mesh_group_name_xpath[XPATH_MAXLEN];
- char msdp_mesh_group_member_xpath[XPATH_MAXLEN];
+ char xpath_value[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
- snprintf(msdp_mesh_group_name_xpath, sizeof(msdp_mesh_group_name_xpath),
- FRR_PIM_AF_XPATH,
- "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
- strlcat(msdp_mesh_group_name_xpath, "/msdp-mesh-group/mesh-group-name",
- sizeof(msdp_mesh_group_name_xpath));
- snprintf(msdp_mesh_group_member_xpath,
- sizeof(msdp_mesh_group_member_xpath),
- FRR_PIM_AF_XPATH,
- "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
- strlcat(msdp_mesh_group_member_xpath, "/msdp-mesh-group/member-ip",
- sizeof(msdp_mesh_group_member_xpath));
+ /* Create mesh group. */
+ snprintf(xpath_value, sizeof(xpath_value),
+ FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']",
+ "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);
- nb_cli_enqueue_change(vty, msdp_mesh_group_name_xpath, NB_OP_MODIFY,
- argv[3]->arg);
- nb_cli_enqueue_change(vty, msdp_mesh_group_member_xpath, NB_OP_CREATE,
- argv[5]->arg);
+ /* Create mesh group member. */
+ strlcat(xpath_value, "/members[address='", sizeof(xpath_value));
+ strlcat(xpath_value, maddr_str, sizeof(xpath_value));
+ strlcat(xpath_value, "']", sizeof(xpath_value));
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);
return nb_cli_apply_changes(vty, NULL);
}
-DEFUN (no_ip_msdp_mesh_group_member,
- no_ip_msdp_mesh_group_member_cmd,
- "no ip msdp mesh-group WORD member A.B.C.D",
- NO_STR
- IP_STR
- CFG_MSDP_STR
- "Delete MSDP mesh-group member\n"
- "mesh group name\n"
- "mesh group member\n"
- "peer ip address\n")
+DEFPY(no_ip_msdp_mesh_group_member,
+ no_ip_msdp_mesh_group_member_cmd,
+ "no ip msdp mesh-group WORD$gname member A.B.C.D$maddr",
+ NO_STR
+ IP_STR
+ CFG_MSDP_STR
+ "Delete MSDP mesh-group member\n"
+ "Mesh group name\n"
+ "Mesh group member\n"
+ "Peer IP address\n")
{
const char *vrfname;
- char pim_af_xpath[XPATH_MAXLEN];
- char mesh_group_xpath[XPATH_MAXLEN + 32];
- char group_member_list_xpath[XPATH_MAXLEN + 64];
- char group_member_xpath[XPATH_MAXLEN + 128];
- char source_xpath[XPATH_MAXLEN + 64];
- char mesh_group_name_xpath[XPATH_MAXLEN + 64];
- const char *mesh_group_name;
- const struct lyd_node *member_dnode;
+ char xpath_value[XPATH_MAXLEN];
+ char xpath_member_value[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
- snprintf(pim_af_xpath, sizeof(pim_af_xpath), FRR_PIM_AF_XPATH,
- "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
+ /* Get mesh group base XPath. */
+ snprintf(xpath_value, sizeof(xpath_value),
+ FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']",
+ "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname);
- snprintf(mesh_group_xpath, sizeof(mesh_group_xpath),
- "%s/msdp-mesh-group", pim_af_xpath);
-
- snprintf(group_member_list_xpath, sizeof(group_member_list_xpath),
- "%s/msdp-mesh-group/member-ip", pim_af_xpath);
-
- snprintf(group_member_xpath, sizeof(group_member_xpath), "%s[.='%s']",
- group_member_list_xpath, argv[6]->arg);
-
- snprintf(source_xpath, sizeof(source_xpath),
- "%s/msdp-mesh-group/source-ip", pim_af_xpath);
-
- snprintf(mesh_group_name_xpath, sizeof(mesh_group_name_xpath),
- "%s/msdp-mesh-group/mesh-group-name", pim_af_xpath);
-
- if (yang_dnode_exists(running_config->dnode, mesh_group_name_xpath)
- == true) {
- mesh_group_name = yang_dnode_get_string(running_config->dnode,
- mesh_group_name_xpath);
- if (strcmp(mesh_group_name, argv[4]->arg)) {
- vty_out(vty, "%% mesh-group does not exist\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value)) {
+ vty_out(vty, "%% mesh-group does not exist\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
- if (yang_dnode_exists(vty->candidate_config->dnode,
- group_member_xpath)) {
- if (!yang_dnode_exists(vty->candidate_config->dnode,
- source_xpath)) {
- member_dnode = yang_dnode_get(
- vty->candidate_config->dnode,
- group_member_xpath);
- if (yang_is_last_list_dnode(member_dnode)) {
- nb_cli_enqueue_change(vty, mesh_group_xpath,
- NB_OP_DESTROY, NULL);
- return nb_cli_apply_changes(vty, NULL);
- }
- nb_cli_enqueue_change(vty, group_member_list_xpath,
- NB_OP_DESTROY, argv[6]->arg);
- return nb_cli_apply_changes(vty, NULL);
- }
- nb_cli_enqueue_change(vty, group_member_list_xpath,
- NB_OP_DESTROY, argv[6]->arg);
- return nb_cli_apply_changes(vty, NULL);
+ /* Remove mesh group member. */
+ strlcpy(xpath_member_value, xpath_value, sizeof(xpath_member_value));
+ strlcat(xpath_member_value, "/members[address='",
+ sizeof(xpath_member_value));
+ strlcat(xpath_member_value, maddr_str, sizeof(xpath_member_value));
+ strlcat(xpath_member_value, "']", sizeof(xpath_member_value));
+ if (!yang_dnode_exists(vty->candidate_config->dnode,
+ xpath_member_value)) {
+ vty_out(vty, "%% mesh-group member does not exist\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
- vty_out(vty, "%% mesh-group member does not exist\n");
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
- return CMD_SUCCESS;
+ /*
+ * If this is the last member, then we must remove the group altogether
+ * to not break legacy CLI behaviour.
+ */
+ pim_cli_legacy_mesh_group_behavior(vty, gname);
+
+ return nb_cli_apply_changes(vty, NULL);
}
-DEFUN (ip_msdp_mesh_group_source,
- ip_msdp_mesh_group_source_cmd,
- "ip msdp mesh-group WORD source A.B.C.D",
- IP_STR
- CFG_MSDP_STR
- "Configure MSDP mesh-group\n"
- "mesh group name\n"
- "mesh group local address\n"
- "source ip address for the TCP connection\n")
+DEFPY(ip_msdp_mesh_group_source,
+ ip_msdp_mesh_group_source_cmd,
+ "ip msdp mesh-group WORD$gname source A.B.C.D$saddr",
+ IP_STR
+ CFG_MSDP_STR
+ "Configure MSDP mesh-group\n"
+ "Mesh group name\n"
+ "Mesh group local address\n"
+ "Source IP address for the TCP connection\n")
{
const char *vrfname;
- char msdp_mesh_source_ip_xpath[XPATH_MAXLEN];
- char msdp_mesh_group_name_xpath[XPATH_MAXLEN];
+ char xpath_value[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
- snprintf(msdp_mesh_group_name_xpath, sizeof(msdp_mesh_group_name_xpath),
- FRR_PIM_AF_XPATH,
- "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
- strlcat(msdp_mesh_group_name_xpath, "/msdp-mesh-group/mesh-group-name",
- sizeof(msdp_mesh_group_name_xpath));
-
- snprintf(msdp_mesh_source_ip_xpath, sizeof(msdp_mesh_source_ip_xpath),
- FRR_PIM_AF_XPATH,
- "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
- strlcat(msdp_mesh_source_ip_xpath, "/msdp-mesh-group/source-ip",
- sizeof(msdp_mesh_source_ip_xpath));
+ /* Create mesh group. */
+ snprintf(xpath_value, sizeof(xpath_value),
+ FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']",
+ "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);
- nb_cli_enqueue_change(vty, msdp_mesh_group_name_xpath, NB_OP_MODIFY,
- argv[3]->arg);
- nb_cli_enqueue_change(vty, msdp_mesh_source_ip_xpath, NB_OP_MODIFY,
- argv[5]->arg);
+ /* Create mesh group member. */
+ strlcat(xpath_value, "/source", sizeof(xpath_value));
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, saddr_str);
return nb_cli_apply_changes(vty, NULL);
}
-DEFUN (no_ip_msdp_mesh_group_source,
- no_ip_msdp_mesh_group_source_cmd,
- "no ip msdp mesh-group WORD source [A.B.C.D]",
- NO_STR
- IP_STR
- CFG_MSDP_STR
- "Delete MSDP mesh-group source\n"
- "mesh group name\n"
- "mesh group source\n"
- "mesh group local address\n")
+DEFPY(no_ip_msdp_mesh_group_source,
+ no_ip_msdp_mesh_group_source_cmd,
+ "no ip msdp mesh-group WORD$gname source [A.B.C.D]",
+ NO_STR
+ IP_STR
+ CFG_MSDP_STR
+ "Delete MSDP mesh-group source\n"
+ "Mesh group name\n"
+ "Mesh group source\n"
+ "Mesh group local address\n")
{
const char *vrfname;
- char msdp_mesh_xpath[XPATH_MAXLEN];
- char source_xpath[XPATH_MAXLEN];
- char group_member_xpath[XPATH_MAXLEN];
- char mesh_group_name_xpath[XPATH_MAXLEN];
- const char *mesh_group_name;
+ char xpath_value[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
- snprintf(msdp_mesh_xpath, sizeof(msdp_mesh_xpath),
- FRR_PIM_AF_XPATH,
- "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
- strlcat(msdp_mesh_xpath, "/msdp-mesh-group", sizeof(msdp_mesh_xpath));
+ /* Get mesh group base XPath. */
+ snprintf(xpath_value, sizeof(xpath_value),
+ FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']",
+ "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);
- snprintf(source_xpath, sizeof(source_xpath),
- FRR_PIM_AF_XPATH,
- "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
- strlcat(source_xpath, "/msdp-mesh-group/source-ip",
- sizeof(source_xpath));
+ /* Create mesh group member. */
+ strlcat(xpath_value, "/source", sizeof(xpath_value));
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
- snprintf(group_member_xpath,
- sizeof(group_member_xpath),
- FRR_PIM_AF_XPATH,
- "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
- strlcat(group_member_xpath, "/msdp-mesh-group/member-ip",
- sizeof(group_member_xpath));
+ /*
+ * If this is the last member, then we must remove the group altogether
+ * to not break legacy CLI behaviour.
+ */
+ pim_cli_legacy_mesh_group_behavior(vty, gname);
- snprintf(mesh_group_name_xpath, sizeof(mesh_group_name_xpath),
- FRR_PIM_AF_XPATH,
- "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
- strlcat(mesh_group_name_xpath, "/msdp-mesh-group/mesh-group-name",
- sizeof(mesh_group_name_xpath));
-
- if (yang_dnode_exists(running_config->dnode, mesh_group_name_xpath)
- == true) {
- mesh_group_name = yang_dnode_get_string(running_config->dnode,
- mesh_group_name_xpath);
- if (strcmp(mesh_group_name, argv[4]->arg)) {
- vty_out(vty, "%% mesh-group does not exist\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- }
-
- if (!yang_dnode_exists(vty->candidate_config->dnode,
- group_member_xpath)) {
- nb_cli_enqueue_change(vty, msdp_mesh_xpath, NB_OP_DESTROY,
- NULL);
- return nb_cli_apply_changes(vty, NULL);
- }
- nb_cli_enqueue_change(vty, source_xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
-DEFUN (no_ip_msdp_mesh_group,
- no_ip_msdp_mesh_group_cmd,
- "no ip msdp mesh-group [WORD]",
- NO_STR
- IP_STR
- CFG_MSDP_STR
- "Delete MSDP mesh-group\n"
- "mesh group name")
+DEFPY(no_ip_msdp_mesh_group,
+ no_ip_msdp_mesh_group_cmd,
+ "no ip msdp mesh-group WORD$gname",
+ NO_STR
+ IP_STR
+ CFG_MSDP_STR
+ "Delete MSDP mesh-group\n"
+ "Mesh group name")
{
const char *vrfname;
- const char *mesh_group_name;
- char xpath[XPATH_MAXLEN];
- char msdp_mesh_xpath[XPATH_MAXLEN];
+ char xpath_value[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
- if (argc == 5) {
- snprintf(xpath, sizeof(xpath), FRR_PIM_AF_XPATH, "frr-pim:pimd",
- "pim", vrfname, "frr-routing:ipv4");
- strlcat(xpath, "/msdp-mesh-group/mesh-group-name",
- sizeof(xpath));
-
- if (yang_dnode_exists(running_config->dnode, xpath) == true) {
- mesh_group_name =
- yang_dnode_get_string(running_config->dnode,
- xpath);
-
- if (strcmp(mesh_group_name, argv[4]->arg)) {
- vty_out(vty, "%% mesh-group does not exist\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- }
- }
-
- snprintf(msdp_mesh_xpath, sizeof(msdp_mesh_xpath),
- FRR_PIM_AF_XPATH,
- "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4");
- strlcat(msdp_mesh_xpath, "/msdp-mesh-group", sizeof(msdp_mesh_xpath));
+ /* Get mesh group base XPath. */
+ snprintf(xpath_value, sizeof(xpath_value),
+ FRR_PIM_AF_XPATH "/msdp-mesh-groups[name='%s']",
+ "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname);
+ if (!yang_dnode_exists(vty->candidate_config->dnode, xpath_value))
+ return CMD_SUCCESS;
- nb_cli_enqueue_change(vty, msdp_mesh_xpath, NB_OP_DESTROY, NULL);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
-static void print_empty_json_obj(struct vty *vty)
-{
- json_object *json;
- json = json_object_new_object();
- vty_out(vty, "%s\n",
- json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY));
- json_object_free(json);
-}
-
-static void ip_msdp_show_mesh_group(struct pim_instance *pim, struct vty *vty,
- bool uj)
+static void ip_msdp_show_mesh_group(struct vty *vty, struct pim_msdp_mg *mg,
+ struct json_object *json)
{
struct listnode *mbrnode;
struct pim_msdp_mg_mbr *mbr;
- struct pim_msdp_mg *mg = pim->msdp.mg;
char mbr_str[INET_ADDRSTRLEN];
char src_str[INET_ADDRSTRLEN];
char state_str[PIM_MSDP_STATE_STRLEN];
enum pim_msdp_peer_state state;
- json_object *json = NULL;
json_object *json_mg_row = NULL;
json_object *json_members = NULL;
json_object *json_row = NULL;
- if (!mg) {
- if (uj)
- print_empty_json_obj(vty);
- return;
- }
-
pim_inet4_dump("<source?>", mg->src_ip, src_str, sizeof(src_str));
- if (uj) {
- json = json_object_new_object();
+ if (json) {
/* currently there is only one mesh group but we should still
* make
* it a dict with mg-name as key */
state = PIM_MSDP_DISABLED;
}
pim_msdp_state_dump(state, state_str, sizeof(state_str));
- if (uj) {
+ if (json) {
json_row = json_object_new_object();
json_object_string_add(json_row, "member", mbr_str);
json_object_string_add(json_row, "state", state_str);
}
}
- if (uj) {
+ if (json)
json_object_object_add(json, mg->mesh_group_name, json_mg_row);
- vty_out(vty, "%s\n", json_object_to_json_string_ext(
- json, JSON_C_TO_STRING_PRETTY));
- json_object_free(json);
- }
}
DEFUN (show_ip_msdp_mesh_group,
{
bool uj = use_json(argc, argv);
int idx = 2;
+ struct pim_msdp_mg *mg;
struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+ struct pim_instance *pim = vrf->info;
+ struct json_object *json = NULL;
if (!vrf)
return CMD_WARNING;
- ip_msdp_show_mesh_group(vrf->info, vty, uj);
+ /* Quick case: list is empty. */
+ if (SLIST_EMPTY(&pim->msdp.mglist)) {
+ if (uj)
+ vty_out(vty, "{}\n");
+
+ return CMD_SUCCESS;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+
+ SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry)
+ ip_msdp_show_mesh_group(vty, mg, json);
+
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
return CMD_SUCCESS;
}
JSON_STR)
{
bool uj = use_json(argc, argv);
+ struct json_object *json = NULL, *vrf_json = NULL;
+ struct pim_instance *pim;
+ struct pim_msdp_mg *mg;
struct vrf *vrf;
- bool first = true;
if (uj)
- vty_out(vty, "{ ");
+ json = json_object_new_object();
+
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (uj) {
- if (!first)
- vty_out(vty, ", ");
- vty_out(vty, " \"%s\": ", vrf->name);
- first = false;
+ vrf_json = json_object_new_object();
+ json_object_object_add(json, vrf->name, vrf_json);
} else
vty_out(vty, "VRF: %s\n", vrf->name);
- ip_msdp_show_mesh_group(vrf->info, vty, uj);
+
+ pim = vrf->info;
+ SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry)
+ ip_msdp_show_mesh_group(vty, mg, vrf_json);
+ }
+
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
}
- if (uj)
- vty_out(vty, "}\n");
return CMD_SUCCESS;
}
enum pim_msdp_sa_flags flags);
static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2);
static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr);
-static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg,
- struct pim_msdp_mg_mbr *mbr);
/************************ SA cache management ******************************/
static void pim_msdp_sa_timer_expiry_log(struct pim_msdp_sa *sa,
}
/************************** Mesh group management **************************/
-static void pim_msdp_mg_free(struct pim_instance *pim)
+void pim_msdp_mg_free(struct pim_instance *pim, struct pim_msdp_mg **mgp)
{
- struct pim_msdp_mg *mg = pim->msdp.mg;
+ struct pim_msdp_mg_mbr *mbr;
+ struct listnode *n, *nn;
- /* If the mesh-group has valid member or src_ip don't delete it */
- if (!mg || mg->mbr_cnt || (mg->src_ip.s_addr != INADDR_ANY)) {
+ if (*mgp == NULL)
return;
- }
+
+ /* SIP is being removed - tear down all active peer sessions */
+ for (ALL_LIST_ELEMENTS((*mgp)->mbr_list, n, nn, mbr))
+ pim_msdp_mg_mbr_del((*mgp), mbr);
if (PIM_DEBUG_MSDP_EVENTS) {
- zlog_debug("MSDP mesh-group %s deleted", mg->mesh_group_name);
+ zlog_debug("MSDP mesh-group %s deleted",
+ (*mgp)->mesh_group_name);
}
- XFREE(MTYPE_PIM_MSDP_MG_NAME, mg->mesh_group_name);
- if (mg->mbr_list)
- list_delete(&mg->mbr_list);
+ XFREE(MTYPE_PIM_MSDP_MG_NAME, (*mgp)->mesh_group_name);
+
+ if ((*mgp)->mbr_list)
+ list_delete(&(*mgp)->mbr_list);
- XFREE(MTYPE_PIM_MSDP_MG, pim->msdp.mg);
+ XFREE(MTYPE_PIM_MSDP_MG, (*mgp));
}
-static struct pim_msdp_mg *pim_msdp_mg_new(const char *mesh_group_name)
+struct pim_msdp_mg *pim_msdp_mg_new(struct pim_instance *pim,
+ const char *mesh_group_name)
{
struct pim_msdp_mg *mg;
if (PIM_DEBUG_MSDP_EVENTS) {
zlog_debug("MSDP mesh-group %s created", mg->mesh_group_name);
}
- return mg;
-}
-
-enum pim_msdp_err pim_msdp_mg_del(struct pim_instance *pim,
- const char *mesh_group_name)
-{
- struct pim_msdp_mg *mg = pim->msdp.mg;
- struct pim_msdp_mg_mbr *mbr;
-
- if (!mg
- || (mesh_group_name
- && strcmp(mg->mesh_group_name, mesh_group_name))) {
- return PIM_MSDP_ERR_NO_MG;
- }
-
- /* delete all the mesh-group members */
- while (!list_isempty(mg->mbr_list)) {
- mbr = listnode_head(mg->mbr_list);
- pim_msdp_mg_mbr_do_del(mg, mbr);
- }
-
- /* clear src ip */
- mg->src_ip.s_addr = INADDR_ANY;
-
- /* free up the mesh-group */
- pim_msdp_mg_free(pim);
- return PIM_MSDP_ERR_NONE;
-}
-
-static enum pim_msdp_err pim_msdp_mg_add(struct pim_instance *pim,
- const char *mesh_group_name)
-{
- if (pim->msdp.mg) {
- if (!strcmp(pim->msdp.mg->mesh_group_name, mesh_group_name)) {
- return PIM_MSDP_ERR_NONE;
- }
- /* currently only one mesh-group can exist at a time */
- return PIM_MSDP_ERR_MAX_MESH_GROUPS;
- }
- pim->msdp.mg = pim_msdp_mg_new(mesh_group_name);
- if (!pim->msdp.mg) {
- return PIM_MSDP_ERR_OOM;
- }
+ SLIST_INSERT_HEAD(&pim->msdp.mglist, mg, mg_entry);
- return PIM_MSDP_ERR_NONE;
+ return mg;
}
static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2)
XFREE(MTYPE_PIM_MSDP_MG_MBR, mbr);
}
-static struct pim_msdp_mg_mbr *pim_msdp_mg_mbr_find(struct pim_instance *pim,
- struct in_addr mbr_ip)
-{
- struct pim_msdp_mg_mbr *mbr;
- struct listnode *mbr_node;
-
- if (!pim->msdp.mg) {
- return NULL;
- }
- /* we can move this to a hash but considering that number of peers in
- * a mesh-group that seems like bit of an overkill */
- for (ALL_LIST_ELEMENTS_RO(pim->msdp.mg->mbr_list, mbr_node, mbr)) {
- if (mbr->mbr_ip.s_addr == mbr_ip.s_addr) {
- return mbr;
- }
- }
- return mbr;
-}
-
-enum pim_msdp_err pim_msdp_mg_mbr_add(struct pim_instance *pim,
- const char *mesh_group_name,
- struct in_addr mbr_ip)
-{
- int rc;
- struct pim_msdp_mg_mbr *mbr;
- struct pim_msdp_mg *mg;
-
- rc = pim_msdp_mg_add(pim, mesh_group_name);
- if (rc != PIM_MSDP_ERR_NONE) {
- return rc;
- }
-
- mg = pim->msdp.mg;
- mbr = pim_msdp_mg_mbr_find(pim, mbr_ip);
- if (mbr) {
- return PIM_MSDP_ERR_MG_MBR_EXISTS;
- }
-
- mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr));
- mbr->mbr_ip = mbr_ip;
- listnode_add_sort(mg->mbr_list, mbr);
-
- /* if valid SIP has been configured add peer session */
- if (mg->src_ip.s_addr != INADDR_ANY) {
- pim_msdp_peer_add(pim, mbr_ip, mg->src_ip, mesh_group_name,
- &mbr->mp);
- }
-
- if (PIM_DEBUG_MSDP_EVENTS) {
- char ip_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<mbr?>", mbr->mbr_ip, ip_str, sizeof(ip_str));
- zlog_debug("MSDP mesh-group %s mbr %s created",
- mg->mesh_group_name, ip_str);
- }
- ++mg->mbr_cnt;
- return PIM_MSDP_ERR_NONE;
-}
-
-static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg,
- struct pim_msdp_mg_mbr *mbr)
+void pim_msdp_mg_mbr_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr)
{
/* Delete active peer session if any */
if (mbr->mp) {
}
}
-enum pim_msdp_err pim_msdp_mg_mbr_del(struct pim_instance *pim,
- const char *mesh_group_name,
- struct in_addr mbr_ip)
-{
- struct pim_msdp_mg_mbr *mbr;
- struct pim_msdp_mg *mg = pim->msdp.mg;
-
- if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
- return PIM_MSDP_ERR_NO_MG;
- }
-
- mbr = pim_msdp_mg_mbr_find(pim, mbr_ip);
- if (!mbr) {
- return PIM_MSDP_ERR_NO_MG_MBR;
- }
-
- pim_msdp_mg_mbr_do_del(mg, mbr);
- /* if there are no references to the mg free it */
- pim_msdp_mg_free(pim);
-
- return PIM_MSDP_ERR_NONE;
-}
-
-static void pim_msdp_mg_src_do_del(struct pim_instance *pim)
+static void pim_msdp_src_del(struct pim_msdp_mg *mg)
{
struct pim_msdp_mg_mbr *mbr;
struct listnode *mbr_node;
- struct pim_msdp_mg *mg = pim->msdp.mg;
/* SIP is being removed - tear down all active peer sessions */
for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) {
}
}
-enum pim_msdp_err pim_msdp_mg_src_del(struct pim_instance *pim,
- const char *mesh_group_name)
-{
- struct pim_msdp_mg *mg = pim->msdp.mg;
-
- if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
- return PIM_MSDP_ERR_NO_MG;
- }
-
- if (mg->src_ip.s_addr != INADDR_ANY) {
- mg->src_ip.s_addr = INADDR_ANY;
- pim_msdp_mg_src_do_del(pim);
- /* if there are no references to the mg free it */
- pim_msdp_mg_free(pim);
- }
- return PIM_MSDP_ERR_NONE;
-}
-
-enum pim_msdp_err pim_msdp_mg_src_add(struct pim_instance *pim,
- const char *mesh_group_name,
- struct in_addr src_ip)
-{
- int rc;
- struct pim_msdp_mg_mbr *mbr;
- struct listnode *mbr_node;
- struct pim_msdp_mg *mg;
-
- if (src_ip.s_addr == INADDR_ANY) {
- pim_msdp_mg_src_del(pim, mesh_group_name);
- return PIM_MSDP_ERR_NONE;
- }
-
- rc = pim_msdp_mg_add(pim, mesh_group_name);
- if (rc != PIM_MSDP_ERR_NONE) {
- return rc;
- }
-
- mg = pim->msdp.mg;
- if (mg->src_ip.s_addr != INADDR_ANY) {
- pim_msdp_mg_src_do_del(pim);
- }
- mg->src_ip = src_ip;
-
- for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) {
- pim_msdp_peer_add(pim, mbr->mbr_ip, mg->src_ip, mesh_group_name,
- &mbr->mp);
- }
-
- if (PIM_DEBUG_MSDP_EVENTS) {
- char ip_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", mg->src_ip, ip_str, sizeof(ip_str));
- zlog_debug("MSDP mesh-group %s src %s set", mg->mesh_group_name,
- ip_str);
- }
- return PIM_MSDP_ERR_NONE;
-}
-
/*********************** MSDP feature APIs *********************************/
int pim_msdp_config_write(struct pim_instance *pim, struct vty *vty,
const char *spaces)
{
+ struct pim_msdp_mg *mg;
struct listnode *mbrnode;
struct pim_msdp_mg_mbr *mbr;
- struct pim_msdp_mg *mg = pim->msdp.mg;
char mbr_str[INET_ADDRSTRLEN];
char src_str[INET_ADDRSTRLEN];
int count = 0;
- if (!mg) {
+ if (SLIST_EMPTY(&pim->msdp.mglist))
return count;
- }
- if (mg->src_ip.s_addr != INADDR_ANY) {
- pim_inet4_dump("<src?>", mg->src_ip, src_str, sizeof(src_str));
- vty_out(vty, "%sip msdp mesh-group %s source %s\n", spaces,
- mg->mesh_group_name, src_str);
- ++count;
- }
+ SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry) {
+ if (mg->src_ip.s_addr != INADDR_ANY) {
+ pim_inet4_dump("<src?>", mg->src_ip, src_str,
+ sizeof(src_str));
+ vty_out(vty, "%sip msdp mesh-group %s source %s\n",
+ spaces, mg->mesh_group_name, src_str);
+ ++count;
+ }
- for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) {
- pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, sizeof(mbr_str));
- vty_out(vty, "%sip msdp mesh-group %s member %s\n", spaces,
- mg->mesh_group_name, mbr_str);
- ++count;
+ for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) {
+ pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str,
+ sizeof(mbr_str));
+ vty_out(vty, "%sip msdp mesh-group %s member %s\n",
+ spaces, mg->mesh_group_name, mbr_str);
+ ++count;
+ }
}
+
return count;
}
/* counterpart to MSDP init; XXX: unused currently */
void pim_msdp_exit(struct pim_instance *pim)
{
- pim_msdp_sa_adv_timer_setup(pim, false);
+ struct pim_msdp_mg *mg;
- /* XXX: stop listener and delete all peer sessions */
+ pim_msdp_sa_adv_timer_setup(pim, false);
- pim_msdp_mg_free(pim);
+ /* Stop listener and delete all peer sessions */
+ while ((mg = SLIST_FIRST(&pim->msdp.mglist)) != NULL)
+ pim_msdp_mg_free(pim, &mg);
if (pim->msdp.peer_hash) {
hash_clean(pim->msdp.peer_hash, NULL);
stream_free(pim->msdp.work_obuf);
pim->msdp.work_obuf = NULL;
}
+
+void pim_msdp_mg_src_add(struct pim_instance *pim, struct pim_msdp_mg *mg,
+ struct in_addr *ai)
+{
+ struct pim_msdp_mg_mbr *mbr;
+ struct listnode *mbr_node;
+
+ /* Stop all connections and remove data structures. */
+ pim_msdp_src_del(mg);
+
+ /* Set new address. */
+ mg->src_ip = *ai;
+
+ /* No new address, disable everyone. */
+ if (ai->s_addr == INADDR_ANY) {
+ if (PIM_DEBUG_MSDP_EVENTS)
+ zlog_debug("MSDP mesh-group %s src unset",
+ mg->mesh_group_name);
+ return;
+ }
+
+ /* Create data structures and start TCP connection. */
+ for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr))
+ pim_msdp_peer_add(pim, mbr->mbr_ip, mg->src_ip,
+ mg->mesh_group_name, &mbr->mp);
+
+ if (PIM_DEBUG_MSDP_EVENTS)
+ zlog_debug("MSDP mesh-group %s src %pI4 set",
+ mg->mesh_group_name, &mg->src_ip);
+}
+
+struct pim_msdp_mg_mbr *pim_msdp_mg_mbr_add(struct pim_instance *pim,
+ struct pim_msdp_mg *mg,
+ struct in_addr *ia)
+{
+ struct pim_msdp_mg_mbr *mbr;
+
+ mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr));
+ mbr->mbr_ip = *ia;
+ listnode_add_sort(mg->mbr_list, mbr);
+
+ /* if valid SIP has been configured add peer session */
+ if (mg->src_ip.s_addr != INADDR_ANY)
+ pim_msdp_peer_add(pim, mbr->mbr_ip, mg->src_ip,
+ mg->mesh_group_name, &mbr->mp);
+
+ if (PIM_DEBUG_MSDP_EVENTS)
+ zlog_debug("MSDP mesh-group %s mbr %pI4 created",
+ mg->mesh_group_name, &mbr->mbr_ip);
+
+ ++mg->mbr_cnt;
+
+ return mbr;
+}
#ifndef PIM_MSDP_H
#define PIM_MSDP_H
+#include "lib/openbsd-queue.h"
+
enum pim_msdp_peer_state {
PIM_MSDP_DISABLED,
PIM_MSDP_INACTIVE,
struct in_addr src_ip;
uint32_t mbr_cnt;
struct list *mbr_list;
+
+ /** Belongs to PIM instance list. */
+ SLIST_ENTRY(pim_msdp_mg) mg_entry;
};
+SLIST_HEAD(pim_mesh_group_list, pim_msdp_mg);
+
enum pim_msdp_flags {
PIM_MSDPF_NONE = 0,
PIM_MSDPF_ENABLE = (1 << 0),
struct in_addr originator_id;
- /* currently only one mesh-group is supported - so just stash it here */
- struct pim_msdp_mg *mg;
+ /** List of mesh groups. */
+ struct pim_mesh_group_list mglist;
};
#define PIM_MSDP_PEER_READ_ON(mp) \
void pim_msdp_up_join_state_changed(struct pim_instance *pim,
struct pim_upstream *xg_up);
void pim_msdp_up_del(struct pim_instance *pim, struct prefix_sg *sg);
-enum pim_msdp_err pim_msdp_mg_mbr_add(struct pim_instance *pim,
- const char *mesh_group_name,
- struct in_addr mbr_ip);
-enum pim_msdp_err pim_msdp_mg_mbr_del(struct pim_instance *pim,
- const char *mesh_group_name,
- struct in_addr mbr_ip);
-enum pim_msdp_err pim_msdp_mg_src_del(struct pim_instance *pim,
- const char *mesh_group_name);
-enum pim_msdp_err pim_msdp_mg_src_add(struct pim_instance *pim,
- const char *mesh_group_name,
- struct in_addr src_ip);
enum pim_msdp_err pim_msdp_mg_del(struct pim_instance *pim,
const char *mesh_group_name);
+
+/**
+ * Allocates a new mesh group data structure under PIM instance.
+ */
+struct pim_msdp_mg *pim_msdp_mg_new(struct pim_instance *pim,
+ const char *mesh_group_name);
+/**
+ * Deallocates mesh group data structure under PIM instance.
+ */
+void pim_msdp_mg_free(struct pim_instance *pim, struct pim_msdp_mg **mgp);
+
+/**
+ * Change the source address of a mesh group peers. It will do the following:
+ * - Close all peers TCP connections
+ * - Recreate peers data structure
+ * - Start TCP connections with new local address.
+ */
+void pim_msdp_mg_src_add(struct pim_instance *pim, struct pim_msdp_mg *mg,
+ struct in_addr *ai);
+
+/**
+ * Add new peer to mesh group and starts the connection if source address is
+ * configured.
+ */
+struct pim_msdp_mg_mbr *pim_msdp_mg_mbr_add(struct pim_instance *pim,
+ struct pim_msdp_mg *mg,
+ struct in_addr *ia);
+
+/**
+ * Stops the connection and removes the peer data structures.
+ */
+void pim_msdp_mg_mbr_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr);
+
#endif
}
},
{
- .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group",
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups",
.cbs = {
- .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_create,
- .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_destroy,
+ .create = pim_msdp_mesh_group_create,
+ .destroy = pim_msdp_mesh_group_destroy,
}
},
{
- .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/mesh-group-name",
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/source",
.cbs = {
- .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_modify,
- .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_destroy,
+ .modify = pim_msdp_mesh_group_source_modify,
+ .destroy = pim_msdp_mesh_group_source_destroy,
}
},
{
- .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/member-ip",
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/members",
.cbs = {
- .create = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_create,
- .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_destroy,
- }
- },
- {
- .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/source-ip",
- .cbs = {
- .modify = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_modify,
- .destroy = routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_destroy,
+ .create = pim_msdp_mesh_group_members_create,
+ .destroy = pim_msdp_mesh_group_members_destroy,
}
},
{
struct nb_cb_create_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ssm_pingd_source_ip_destroy(
struct nb_cb_destroy_args *args);
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_create(
- struct nb_cb_create_args *args);
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_destroy(
- struct nb_cb_destroy_args *args);
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_modify(
- struct nb_cb_modify_args *args);
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_destroy(
- struct nb_cb_destroy_args *args);
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_create(
- struct nb_cb_create_args *args);
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_destroy(
- struct nb_cb_destroy_args *args);
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_modify(
- struct nb_cb_modify_args *args);
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_destroy(
- struct nb_cb_destroy_args *args);
+int pim_msdp_mesh_group_create(struct nb_cb_create_args *args);
+int pim_msdp_mesh_group_destroy(struct nb_cb_destroy_args *args);
+int pim_msdp_mesh_group_members_create(struct nb_cb_create_args *args);
+int pim_msdp_mesh_group_members_destroy(struct nb_cb_destroy_args *args);
+int pim_msdp_mesh_group_source_modify(struct nb_cb_modify_args *args);
+int pim_msdp_mesh_group_source_destroy(struct nb_cb_destroy_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_create(
struct nb_cb_create_args *args);
int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_peer_destroy(
return ret;
}
-static int ip_no_msdp_mesh_group_cmd_worker(struct pim_instance *pim,
- const char *mg,
- char *errmsg, size_t errmsg_len)
-{
- enum pim_msdp_err result;
-
- result = pim_msdp_mg_del(pim, mg);
-
- switch (result) {
- case PIM_MSDP_ERR_NONE:
- break;
- case PIM_MSDP_ERR_NO_MG:
- snprintf(errmsg, errmsg_len,
- "%% mesh-group does not exist");
- break;
- default:
- snprintf(errmsg, errmsg_len,
- "mesh-group source del failed");
- }
-
- return result ? NB_ERR : NB_OK;
-}
-
-static int ip_msdp_mesh_group_member_cmd_worker(struct pim_instance *pim,
- const char *mg,
- struct in_addr mbr_ip,
- char *errmsg, size_t errmsg_len)
-{
- enum pim_msdp_err result;
- int ret = NB_OK;
-
- result = pim_msdp_mg_mbr_add(pim, mg, mbr_ip);
-
- switch (result) {
- case PIM_MSDP_ERR_NONE:
- break;
- case PIM_MSDP_ERR_OOM:
- ret = NB_ERR;
- snprintf(errmsg, errmsg_len,
- "%% Out of memory");
- break;
- case PIM_MSDP_ERR_MG_MBR_EXISTS:
- ret = NB_ERR;
- snprintf(errmsg, errmsg_len,
- "%% mesh-group member exists");
- break;
- case PIM_MSDP_ERR_MAX_MESH_GROUPS:
- ret = NB_ERR;
- snprintf(errmsg, errmsg_len,
- "%% Only one mesh-group allowed currently");
- break;
- default:
- ret = NB_ERR;
- snprintf(errmsg, errmsg_len,
- "%% member add failed");
- }
-
- return ret;
-}
-
-static int ip_no_msdp_mesh_group_member_cmd_worker(struct pim_instance *pim,
- const char *mg,
- struct in_addr mbr_ip,
- char *errmsg,
- size_t errmsg_len)
-{
- enum pim_msdp_err result;
-
- result = pim_msdp_mg_mbr_del(pim, mg, mbr_ip);
-
- switch (result) {
- case PIM_MSDP_ERR_NONE:
- break;
- case PIM_MSDP_ERR_NO_MG:
- snprintf(errmsg, errmsg_len,
- "%% mesh-group does not exist");
- break;
- case PIM_MSDP_ERR_NO_MG_MBR:
- snprintf(errmsg, errmsg_len,
- "%% mesh-group member does not exist");
- break;
- default:
- snprintf(errmsg, errmsg_len,
- "%% mesh-group member del failed");
- }
-
- return result ? NB_ERR : NB_OK;
-}
-
-static int ip_msdp_mesh_group_source_cmd_worker(struct pim_instance *pim,
- const char *mg,
- struct in_addr src_ip,
- char *errmsg, size_t errmsg_len)
-{
- enum pim_msdp_err result;
-
- result = pim_msdp_mg_src_add(pim, mg, src_ip);
-
- switch (result) {
- case PIM_MSDP_ERR_NONE:
- break;
- case PIM_MSDP_ERR_OOM:
- snprintf(errmsg, errmsg_len,
- "%% Out of memory");
- break;
- case PIM_MSDP_ERR_MAX_MESH_GROUPS:
- snprintf(errmsg, errmsg_len,
- "%% Only one mesh-group allowed currently");
- break;
- default:
- snprintf(errmsg, errmsg_len,
- "%% source add failed");
- }
-
- return result ? NB_ERR : NB_OK;
-}
-
-static int ip_no_msdp_mesh_group_source_cmd_worker(struct pim_instance *pim,
- const char *mg,
- char *errmsg,
- size_t errmsg_len)
-{
- enum pim_msdp_err result;
-
- result = pim_msdp_mg_src_del(pim, mg);
-
- switch (result) {
- case PIM_MSDP_ERR_NONE:
- break;
- case PIM_MSDP_ERR_NO_MG:
- snprintf(errmsg, errmsg_len,
- "%% mesh-group does not exist");
- break;
- default:
- snprintf(errmsg, errmsg_len,
- "%% mesh-group source del failed");
- }
-
- return result ? NB_ERR : NB_OK;
-}
-
static int ip_msdp_peer_cmd_worker(struct pim_instance *pim,
struct in_addr peer_addr,
struct in_addr local_addr,
}
/*
- * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups
*/
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_create(
- struct nb_cb_create_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- break;
- }
-
- return NB_OK;
-}
-
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_destroy(
- struct nb_cb_destroy_args *args)
+int pim_msdp_mesh_group_create(struct nb_cb_create_args *args)
{
+ struct pim_msdp_mg *mg;
struct vrf *vrf;
- struct pim_instance *pim;
- const char *mesh_group_name;
- int result;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
- pim = vrf->info;
- mesh_group_name = yang_dnode_get_string(args->dnode, "mesh-group-name");
-
- result = ip_no_msdp_mesh_group_cmd_worker(pim, mesh_group_name,
- args->errmsg,
- args->errmsg_len);
-
- if (result != PIM_MSDP_ERR_NONE)
- return NB_ERR_INCONSISTENCY;
-
+ mg = pim_msdp_mg_new(vrf->info, yang_dnode_get_string(
+ args->dnode, "./name"));
+ nb_running_set_entry(args->dnode, mg);
break;
}
return NB_OK;
}
-/*
- * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/mesh-group-name
- */
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_modify(
- struct nb_cb_modify_args *args)
+int pim_msdp_mesh_group_destroy(struct nb_cb_destroy_args *args)
{
- const char *mesh_group_name;
- const char *mesh_group_name_old;
- char xpath[XPATH_MAXLEN];
+ struct pim_msdp_mg *mg;
+ struct vrf *vrf;
switch (args->event) {
case NB_EV_VALIDATE:
- mesh_group_name = yang_dnode_get_string(args->dnode, ".");
- yang_dnode_get_path(args->dnode, xpath, sizeof(xpath));
-
- if (yang_dnode_exists(running_config->dnode, xpath) == false)
- break;
-
- mesh_group_name_old = yang_dnode_get_string(
- running_config->dnode,
- xpath);
- if (strcmp(mesh_group_name, mesh_group_name_old)) {
- /* currently only one mesh-group can exist at a time */
- snprintf(args->errmsg, args->errmsg_len,
- "Only one mesh-group allowed currently");
- return NB_ERR_VALIDATION;
- }
- break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
- case NB_EV_APPLY:
break;
- }
-
- return NB_OK;
-}
-
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_mesh_group_name_destroy(
- struct nb_cb_destroy_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
case NB_EV_APPLY:
+ mg = nb_running_unset_entry(args->dnode);
+ vrf = nb_running_get_entry(args->dnode, NULL, true);
+ pim_msdp_mg_free(vrf->info, &mg);
break;
}
}
/*
- * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/member-ip
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/source
*/
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_create(
- struct nb_cb_create_args *args)
+int pim_msdp_mesh_group_source_modify(struct nb_cb_modify_args *args)
{
+ const struct lyd_node *vrf_dnode;
+ struct pim_msdp_mg *mg;
struct vrf *vrf;
- struct pim_instance *pim;
- const char *mesh_group_name;
- struct ipaddr mbr_ip;
- enum pim_msdp_err result;
+ struct ipaddr ip;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
- vrf = nb_running_get_entry(args->dnode, NULL, true);
- pim = vrf->info;
- mesh_group_name = yang_dnode_get_string(args->dnode,
- "../mesh-group-name");
- yang_dnode_get_ip(&mbr_ip, args->dnode, NULL);
-
- result = ip_msdp_mesh_group_member_cmd_worker(
- pim, mesh_group_name, mbr_ip.ip._v4_addr,
- args->errmsg, args->errmsg_len);
-
- if (result != PIM_MSDP_ERR_NONE)
- return NB_ERR_INCONSISTENCY;
+ mg = nb_running_get_entry(args->dnode, NULL, true);
+ vrf_dnode =
+ yang_dnode_get_parent(args->dnode, "address-family");
+ vrf = nb_running_get_entry(vrf_dnode, "../../", true);
+ yang_dnode_get_ip(&ip, args->dnode, NULL);
+ pim_msdp_mg_src_add(vrf->info, mg, &ip.ip._v4_addr);
break;
}
-
return NB_OK;
}
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_member_ip_destroy(
- struct nb_cb_destroy_args *args)
+int pim_msdp_mesh_group_source_destroy(struct nb_cb_destroy_args *args)
{
+ const struct lyd_node *vrf_dnode;
+ struct pim_msdp_mg *mg;
struct vrf *vrf;
- struct pim_instance *pim;
- const char *mesh_group_name;
- struct ipaddr mbr_ip;
- enum pim_msdp_err result;
+ struct in_addr addr;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
- vrf = nb_running_get_entry(args->dnode, NULL, true);
- pim = vrf->info;
- mesh_group_name = yang_dnode_get_string(args->dnode,
- "../mesh-group-name");
- yang_dnode_get_ip(&mbr_ip, args->dnode, NULL);
-
- result = ip_no_msdp_mesh_group_member_cmd_worker(
- pim, mesh_group_name, mbr_ip.ip._v4_addr,
- args->errmsg, args->errmsg_len);
-
- if (result != PIM_MSDP_ERR_NONE)
- return NB_ERR_INCONSISTENCY;
+ mg = nb_running_get_entry(args->dnode, NULL, true);
+ vrf_dnode =
+ yang_dnode_get_parent(args->dnode, "address-family");
+ vrf = nb_running_get_entry(vrf_dnode, "../../", true);
+ addr.s_addr = INADDR_ANY;
+ pim_msdp_mg_src_add(vrf->info, mg, &addr);
break;
}
-
return NB_OK;
}
+
/*
- * XPath: /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-group/source-ip
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-pim:pim/address-family/msdp-mesh-groups/members
*/
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_modify(
- struct nb_cb_modify_args *args)
+int pim_msdp_mesh_group_members_create(struct nb_cb_create_args *args)
{
+ const struct lyd_node *vrf_dnode;
+ struct pim_msdp_mg_mbr *mbr;
+ struct pim_msdp_mg *mg;
struct vrf *vrf;
- struct pim_instance *pim;
- const char *mesh_group_name;
- struct ipaddr src_ip;
- enum pim_msdp_err result;
+ struct ipaddr ip;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
- vrf = nb_running_get_entry(args->dnode, NULL, true);
- pim = vrf->info;
- mesh_group_name = yang_dnode_get_string(args->dnode,
- "../mesh-group-name");
- yang_dnode_get_ip(&src_ip, args->dnode, NULL);
-
- result = ip_msdp_mesh_group_source_cmd_worker(
- pim, mesh_group_name, src_ip.ip._v4_addr,
- args->errmsg, args->errmsg_len);
-
- if (result != PIM_MSDP_ERR_NONE)
- return NB_ERR_INCONSISTENCY;
+ mg = nb_running_get_entry(args->dnode, NULL, true);
+ vrf_dnode =
+ yang_dnode_get_parent(args->dnode, "address-family");
+ vrf = nb_running_get_entry(vrf_dnode, "../../", true);
+ yang_dnode_get_ip(&ip, args->dnode, "address");
+ mbr = pim_msdp_mg_mbr_add(vrf->info, mg, &ip.ip._v4_addr);
+ nb_running_set_entry(args->dnode, mbr);
break;
}
+
return NB_OK;
}
-int routing_control_plane_protocols_control_plane_protocol_pim_address_family_msdp_mesh_group_source_ip_destroy(
- struct nb_cb_destroy_args *args)
+int pim_msdp_mesh_group_members_destroy(struct nb_cb_destroy_args *args)
{
- struct vrf *vrf;
- struct pim_instance *pim;
- const char *mesh_group_name;
- enum pim_msdp_err result;
+ struct pim_msdp_mg_mbr *mbr;
+ struct pim_msdp_mg *mg;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
- vrf = nb_running_get_entry(args->dnode, NULL, true);
- pim = vrf->info;
- mesh_group_name = yang_dnode_get_string(args->dnode,
- "../mesh-group-name");
-
- result = ip_no_msdp_mesh_group_source_cmd_worker(
- pim, mesh_group_name, args->errmsg,
- args->errmsg_len);
-
- if (result != PIM_MSDP_ERR_NONE)
- return NB_ERR_INCONSISTENCY;
+ mbr = nb_running_get_entry(args->dnode, NULL, true);
+ mg = nb_running_get_entry(args->dnode, "../", true);
+ pim_msdp_mg_mbr_del(mg, mbr);
break;
}
+
return NB_OK;
}
{
vrf_init(rip_vrf_new, rip_vrf_enable, rip_vrf_disable, rip_vrf_delete,
rip_vrf_enable);
+
+ vrf_cmd_init(NULL, &ripd_privs);
}
void rip_vrf_terminate(void)
{
vrf_init(ripng_vrf_new, ripng_vrf_enable, ripng_vrf_disable,
ripng_vrf_delete, ripng_vrf_enable);
+
+ vrf_cmd_init(NULL, &ripngd_privs);
}
void ripng_vrf_terminate(void)
#ifndef _STATIC_DEBUG_H
#define _STATIC_DEBUG_H
-
#include <zebra.h>
#include "lib/debug.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* staticd debugging records */
extern struct debug static_dbg_events;
extern struct debug static_dbg_route;
*/
void static_debug_set(int vtynode, bool onoff, bool events, bool route);
+#ifdef __cplusplus
+}
+#endif
#endif /* _STATIC_DEBUG_H */
#ifndef _FRR_STATIC_NB_H_
#define _FRR_STATIC_NB_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern const struct frr_yang_module_info frr_staticd_info;
/* Mandatory callbacks. */
FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \
FRR_STATIC_ROUTE_NH_KEY_XPATH
+#ifdef __cplusplus
+}
+#endif
+
#endif
#ifndef __STATIC_NHT_H__
#define __STATIC_NHT_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/*
* When we get notification that nexthop tracking has an answer for
* us call this function to find the nexthop we are tracking so it
*/
extern void static_get_nh_str(struct static_nexthop *nh, char *nexthop,
size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
#include "table.h"
#include "memory.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
DECLARE_MGROUP(STATIC);
/* Static route label information */
*/
extern void static_get_nh_str(struct static_nexthop *nh, char *nexthop,
size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
#ifndef __STATIC_VRF_H__
#define __STATIC_VRF_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct static_vrf {
struct vrf *vrf;
struct static_vrf *svrf);
extern void static_vrf_terminate(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif
#ifndef __STATIC_VTY_H__
#define __STATIC_VTY_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
int static_config(struct vty *vty, struct static_vrf *svrf,
afi_t afi, safi_t safi, const char *cmd);
void static_vty_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
"Static Nexthop Tracking hash");
}
+/* static_zebra_stop used by tests/lib/test_grpc.cpp */
+void static_zebra_stop(void)
+{
+ if (!zclient)
+ return;
+ zclient_stop(zclient);
+ zclient_free(zclient);
+ zclient = NULL;
+}
+
void static_zebra_vrf_register(struct vrf *vrf)
{
if (vrf->vrf_id == VRF_DEFAULT)
#ifndef __STATIC_ZEBRA_H__
#define __STATIC_ZEBRA_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern struct thread_master *master;
extern void static_zebra_nht_register(struct route_node *rn,
struct static_path *pn, safi_t safi,
bool install);
extern void static_zebra_init(void);
+/* static_zebra_stop used by tests/lib/test_grpc.cpp */
+extern void static_zebra_stop(void);
extern void static_zebra_vrf_register(struct vrf *vrf);
extern void static_zebra_vrf_unregister(struct vrf *vrf);
extern int static_zebra_nh_update(struct route_node *rn,
struct static_nexthop *nh);
+#ifdef __cplusplus
+}
+#endif
+
#endif
{
int ret;
- isis_dynhn_insert(tnode->sysid, tnode->hostname, tnode->level);
+ isis_dynhn_insert(area->isis, tnode->sysid, tnode->hostname,
+ tnode->level);
for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
if ((tnode->level & level) == 0)
if (sysid2buff(fail_id, fail_sysid_str) == 0) {
struct isis_dynhn *dynhn;
- dynhn = dynhn_find_by_name(fail_sysid_str);
+ dynhn = dynhn_find_by_name(area->isis, fail_sysid_str);
if (dynhn == NULL) {
vty_out(vty, "Invalid system id %s\n",
fail_sysid_str);
/* Cleanup IS-IS area. */
isis_area_destroy(area);
- /* Cleanup hostnames. */
- dyn_cache_cleanup_all();
-
return CMD_SUCCESS;
}
--- /dev/null
+/*
+ * May 16 2021, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2021, LabN Consulting, L.L.C
+ *
+ * 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 <time.h>
+#include <unistd.h>
+#include <zebra.h>
+
+#include "filter.h"
+#include "frr_pthread.h"
+#include "libfrr.h"
+#include "routing_nb.h"
+#include "northbound_cli.h"
+#include "thread.h"
+#include "vrf.h"
+#include "vty.h"
+
+#include "staticd/static_debug.h"
+#include "staticd/static_nb.h"
+#include "staticd/static_vrf.h"
+#include "staticd/static_vty.h"
+#include "staticd/static_zebra.h"
+
+// GRPC C++ includes
+#include <string>
+#include <sstream>
+#include <grpc/grpc.h>
+#include <grpcpp/channel.h>
+#include <grpcpp/client_context.h>
+#include <grpcpp/create_channel.h>
+#include <grpcpp/security/credentials.h>
+#include "grpc/frr-northbound.grpc.pb.h"
+
+DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm));
+DEFINE_KOOH(frr_fini, (), ());
+
+struct vty *vty;
+
+bool mpls_enabled;
+struct thread_master *master;
+struct zebra_privs_t static_privs = {0};
+struct frrmod_runtime *grpc_module;
+char binpath[2 * MAXPATHLEN + 1];
+
+extern const char *json_expect1;
+extern const char *json_expect2;
+extern const char *json_expect3;
+extern const char *json_loadconf1;
+
+int test_dbg = 1;
+
+void inline test_debug(const std::string &s)
+{
+ if (test_dbg)
+ std::cout << s << std::endl;
+}
+
+// static struct option_chain modules[] = {{ .arg = "grpc:50051" }]
+// static struct option_chain **modnext = modules->next;
+
+static const struct frr_yang_module_info *const staticd_yang_modules[] = {
+ &frr_interface_info, &frr_filter_info, &frr_routing_info,
+ &frr_staticd_info, &frr_vrf_info,
+};
+
+static int grpc_thread_stop(struct thread *thread);
+
+static void static_startup(void)
+{
+ // struct frrmod_runtime module;
+ // static struct option_chain *oc;
+ char moderr[256] = {};
+ cmd_init(1);
+
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+ zprivs_preinit(&static_privs);
+ zprivs_init(&static_privs);
+
+ /* Load the server side module -- check libtool path first */
+ std::string modpath = std::string(binpath) + std::string("../../../lib/.libs");
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(), moderr, sizeof(moderr));
+ if (!grpc_module) {
+ modpath = std::string(binpath) + std::string("../../lib");
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(), moderr,
+ sizeof(moderr));
+ }
+ if (!grpc_module) {
+ std::cout << "Failed to load grpc module:" << moderr
+ << std::endl;
+ exit(1);
+ }
+
+ static_debug_init();
+
+ master = thread_master_create(NULL);
+ nb_init(master, staticd_yang_modules, array_size(staticd_yang_modules),
+ false);
+
+ static_zebra_init();
+ vty_init(master, true);
+ static_vrf_init();
+ static_vty_init();
+
+ hook_register(routing_conf_event,
+ routing_control_plane_protocols_name_validate);
+
+ routing_control_plane_protocols_register_vrf_dependency();
+
+ // Add a route
+ vty = vty_new();
+ vty->type = vty::VTY_TERM;
+ vty_config_enter(vty, true, false);
+
+ auto ret = cmd_execute(vty, "ip route 11.0.0.0/8 Null0", NULL, 0);
+ assert(!ret);
+
+ ret = cmd_execute(vty, "end", NULL, 0);
+ assert(!ret);
+
+ nb_cli_pending_commit_check(vty);
+
+ frr_pthread_init();
+
+ // frr_config_fork();
+ hook_call(frr_late_init, master);
+}
+
+static void static_shutdown(void)
+{
+ hook_call(frr_fini);
+ vty_close(vty);
+ vrf_terminate();
+ vty_terminate();
+ cmd_terminate();
+ nb_terminate();
+ yang_terminate();
+ thread_master_free(master);
+ master = NULL;
+}
+
+using frr::Northbound;
+using grpc::Channel;
+using grpc::ClientAsyncResponseReader;
+using grpc::ClientContext;
+using grpc::CompletionQueue;
+using grpc::Status;
+
+class NorthboundClient
+{
+ public:
+ NorthboundClient(std::shared_ptr<Channel> channel)
+ : stub_(frr::Northbound::NewStub(channel))
+ {
+ }
+
+ void Commit(uint32_t candidate_id)
+ {
+ frr::CommitRequest request;
+ frr::CommitResponse reply;
+ ClientContext context;
+ Status status;
+
+ request.set_candidate_id(candidate_id);
+
+ request.set_phase(frr::CommitRequest::ALL);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+#if 0
+ request.set_phase(frr::CommitRequest::VALIDATE);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+
+ request.set_phase(frr::CommitRequest::PREPARE);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+
+ request.set_phase(frr::CommitRequest::APPLY);
+ status = stub_->Commit(&context, request, &reply);
+ _throw_if_not_ok(status);
+#endif
+ }
+
+ uint32_t CreateCandidate()
+ {
+ frr::CreateCandidateRequest request;
+ frr::CreateCandidateResponse reply;
+ ClientContext context;
+ Status status;
+
+ status = stub_->CreateCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ return reply.candidate_id();
+ }
+
+ void DeleteCandidate(uint32_t candidate_id)
+ {
+ frr::DeleteCandidateRequest request;
+ frr::DeleteCandidateResponse reply;
+ ClientContext context;
+ Status status;
+
+ request.set_candidate_id(candidate_id);
+ status = stub_->DeleteCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ }
+
+ void EditCandidate(uint32_t candidate_id, const std::string &path,
+ const std::string &value)
+ {
+ frr::EditCandidateRequest request;
+ frr::EditCandidateResponse reply;
+ ClientContext context;
+
+ request.set_candidate_id(candidate_id);
+ frr::PathValue *pv = request.add_update();
+ pv->set_path(path);
+ pv->set_value(value);
+
+ Status status = stub_->EditCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ }
+
+ std::string Get(const std::string &path,
+ frr::GetRequest::DataType dtype, frr::Encoding enc,
+ bool with_defaults)
+ {
+ frr::GetRequest request;
+ frr::GetResponse reply;
+ ClientContext context;
+ std::ostringstream ss;
+
+ request.set_type(dtype);
+ request.set_encoding(enc);
+ request.set_with_defaults(with_defaults);
+ request.add_path(path);
+
+ auto stream = stub_->Get(&context, request);
+ while (stream->Read(&reply)) {
+ ss << reply.data().data() << std::endl;
+ }
+ auto status = stream->Finish();
+ _throw_if_not_ok(status);
+ return ss.str();
+ }
+
+ std::string GetCapabilities()
+ {
+ frr::GetCapabilitiesRequest request;
+ frr::GetCapabilitiesResponse reply;
+ ClientContext context;
+
+ Status status =
+ stub_->GetCapabilities(&context, request, &reply);
+ _throw_if_not_ok(status);
+
+ std::ostringstream ss;
+ ss << "Capabilities:" << std::endl
+ << "\tVersion: " << reply.frr_version() << std::endl
+ << "\tRollback Support: " << reply.rollback_support()
+ << std::endl
+ << "\tSupported Modules:";
+
+ for (int i = 0; i < reply.supported_modules_size(); i++) {
+ auto sm = reply.supported_modules(i);
+ ss << std::endl
+ << "\t\tName: \"" << sm.name()
+ << "\" Revision: " << sm.revision() << " Org: \""
+ << sm.organization() << "\"";
+ }
+
+ ss << std::endl << "\tSupported Encodings:";
+
+ for (int i = 0; i < reply.supported_encodings_size(); i++) {
+ auto se = reply.supported_encodings(i);
+ auto desc =
+ google::protobuf::GetEnumDescriptor<decltype(
+ se)>();
+ ss << std::endl
+ << "\t\t" << desc->FindValueByNumber(se)->name();
+ }
+
+ ss << std::endl;
+
+ return ss.str();
+ }
+
+ void LoadToCandidate(uint32_t candidate_id, bool is_replace,
+ bool is_json, const std::string &data)
+ {
+ frr::LoadToCandidateRequest request;
+ frr::LoadToCandidateResponse reply;
+ frr::DataTree *dt = new frr::DataTree;
+ ClientContext context;
+
+ request.set_candidate_id(candidate_id);
+ request.set_type(is_replace
+ ? frr::LoadToCandidateRequest::REPLACE
+ : frr::LoadToCandidateRequest::MERGE);
+ dt->set_encoding(is_json ? frr::JSON : frr::XML);
+ dt->set_data(data);
+ request.set_allocated_config(dt);
+
+ Status status =
+ stub_->LoadToCandidate(&context, request, &reply);
+ _throw_if_not_ok(status);
+ }
+
+ std::string ListTransactions()
+ {
+ frr::ListTransactionsRequest request;
+ frr::ListTransactionsResponse reply;
+ ClientContext context;
+ std::ostringstream ss;
+
+ auto stream = stub_->ListTransactions(&context, request);
+
+ while (stream->Read(&reply)) {
+ ss << "Tx ID: " << reply.id()
+ << " client: " << reply.client()
+ << " date: " << reply.date()
+ << " comment: " << reply.comment() << std::endl;
+ }
+
+ auto status = stream->Finish();
+ _throw_if_not_ok(status);
+ return ss.str();
+ }
+
+ private:
+ std::unique_ptr<frr::Northbound::Stub> stub_;
+
+ void _throw_if_not_ok(Status &status)
+ {
+ if (!status.ok())
+ throw std::runtime_error(
+ std::to_string(status.error_code()) + ": "
+ + status.error_message());
+ }
+};
+
+
+bool stop = false;
+
+int grpc_client_test_stop(struct frr_pthread *fpt, void **result)
+{
+ test_debug("client: STOP pthread");
+
+ assert(fpt->running);
+ atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
+
+ test_debug("client: joining pthread");
+ pthread_join(fpt->thread, result);
+
+ test_debug("client: joined pthread");
+ return 0;
+}
+
+int find_first_diff(const std::string &s1, const std::string &s2)
+{
+ int s1len = s1.length();
+ int s2len = s2.length();
+ int mlen = std::min(s1len, s2len);
+
+ for (int i = 0; i < mlen; i++)
+ if (s1[i] != s2[i])
+ return i;
+ return s1len == s2len ? -1 : mlen;
+}
+
+void assert_no_diff(const std::string &s1, const std::string &s2)
+{
+ int pos = find_first_diff(s1, s2);
+ if (pos == -1)
+ return;
+ std::cout << "not ok" << std::endl;
+ std::cout << "Same: " << s1.substr(0, pos) << std::endl;
+ std::cout << "Diff s1: " << s1.substr(pos) << std::endl;
+ std::cout << "Diff s2: " << s2.substr(pos) << std::endl;
+ assert(false);
+}
+
+void assert_config_same(NorthboundClient &client, const std::string &compare)
+{
+ std::string confs = client.Get("/frr-routing:routing",
+ frr::GetRequest::ALL, frr::JSON, true);
+ assert_no_diff(confs, compare);
+ std::cout << "ok" << std::endl;
+}
+
+void grpc_client_run_test(void)
+{
+ NorthboundClient client(grpc::CreateChannel(
+ "localhost:50051", grpc::InsecureChannelCredentials()));
+
+ std::string reply = client.GetCapabilities();
+
+ uint32_t cid;
+ cid = client.CreateCandidate();
+ std::cout << "CreateCandidate -> " << cid << std::endl;
+ assert(cid == 1);
+ client.DeleteCandidate(cid);
+ std::cout << "DeleteCandidate(" << cid << ")" << std::endl;
+ cid = client.CreateCandidate();
+ assert(cid == 2);
+ std::cout << "CreateCandidate -> " << cid << std::endl;
+
+ /*
+ * Get initial configuration
+ */
+ std::cout << "Comparing initial config...";
+ assert_config_same(client, json_expect1);
+
+ /*
+ * Add config using EditCandidate
+ */
+
+ char xpath_buf[1024];
+ strlcpy(xpath_buf,
+ "/frr-routing:routing/control-plane-protocols/"
+ "control-plane-protocol[type='frr-staticd:staticd']"
+ "[name='staticd'][vrf='default']/frr-staticd:staticd/route-list",
+ sizeof(xpath_buf));
+ int slen = strlen(xpath_buf);
+ for (int i = 0; i < 4; i++) {
+ snprintf(xpath_buf + slen, sizeof(xpath_buf) - slen,
+ "[prefix='13.0.%d.0/24']"
+ "[afi-safi='frr-routing:ipv4-unicast']/"
+ "path-list[table-id='0'][distance='1']/"
+ "frr-nexthops/nexthop[nh-type='blackhole']"
+ "[vrf='default'][gateway=''][interface='(null)']",
+ i);
+ client.EditCandidate(cid, xpath_buf, "");
+ }
+ client.Commit(cid);
+ std::cout << "Comparing EditCandidate config...";
+ assert_config_same(client, json_expect2);
+
+ client.DeleteCandidate(cid);
+ std::cout << "DeleteCandidate(" << cid << ")" << std::endl;
+
+ /*
+ * Add config using LoadToCandidate
+ */
+
+ cid = client.CreateCandidate();
+ std::cout << "CreateCandidate -> " << cid << std::endl;
+
+ client.LoadToCandidate(cid, false, true, json_loadconf1);
+ client.Commit(cid);
+
+ std::cout << "Comparing LoadToCandidate config...";
+ assert_config_same(client, json_expect3);
+
+ client.DeleteCandidate(cid);
+ std::cout << "DeleteCandidate(" << cid << ")" << std::endl;
+
+ std::string ltxreply = client.ListTransactions();
+ // std::cout << "client: pthread received: " << ltxreply << std::endl;
+}
+
+void *grpc_client_test_start(void *arg)
+{
+ struct frr_pthread *fpt = (struct frr_pthread *)arg;
+ fpt->master->owner = pthread_self();
+ frr_pthread_set_name(fpt);
+ frr_pthread_notify_running(fpt);
+
+ try {
+ grpc_client_run_test();
+ std::cout << "TEST PASSED" << std::endl;
+ } catch (std::exception &e) {
+ std::cout << "Exception in test: " << e.what() << std::endl;
+ }
+
+ // Signal FRR event loop to stop
+ test_debug("client: pthread: adding event to stop us");
+ thread_add_event(master, grpc_thread_stop, NULL, 0, NULL);
+
+ test_debug("client: pthread: DONE (returning)");
+
+ return NULL;
+}
+
+static int grpc_thread_start(struct thread *thread)
+{
+ struct frr_pthread_attr client = {
+ .start = grpc_client_test_start,
+ .stop = grpc_client_test_stop,
+ };
+
+ auto pth = frr_pthread_new(&client, "GRPC Client thread", "grpc");
+ frr_pthread_run(pth, NULL);
+ frr_pthread_wait_running(pth);
+
+ return 0;
+}
+
+static int grpc_thread_stop(struct thread *thread)
+{
+ std::cout << __func__ << ": frr_pthread_stop_all" << std::endl;
+ frr_pthread_stop_all();
+ std::cout << __func__ << ": static_shutdown" << std::endl;
+ static_shutdown();
+ std::cout << __func__ << ": exit cleanly" << std::endl;
+ exit(0);
+}
+
+/*
+ * return abs path to this binary with trailing `/`. Does not parse path
+ * environment to find in path, which should not matter for unit testing.
+ */
+static int get_binpath(const char *argv0, char cwd[2 * MAXPATHLEN + 1])
+{
+ const char *rch;
+ if (argv0[0] == '/') {
+ *cwd = 0;
+ rch = strrchr(argv0, '/');
+ strlcpy(cwd, argv0, MIN(rch - argv0 + 2, 2 * MAXPATHLEN + 1));
+ return 0;
+ }
+ if (!(rch = strrchr(argv0, '/'))) {
+ /* Does not handle using PATH, shouldn't matter for test */
+ errno = EINVAL;
+ return -1;
+ }
+ if (!getcwd(cwd, MAXPATHLEN))
+ return -1;
+ int len = strlen(cwd);
+ cwd[len++] = '/';
+ strlcpy(cwd + len, argv0, MIN(rch - argv0 + 2, 2 * MAXPATHLEN + 1));
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ assert(argc >= 1);
+ if (get_binpath(argv[0], binpath) < 0)
+ exit(1);
+
+ static_startup();
+
+ thread_add_event(master, grpc_thread_start, NULL, 0, NULL);
+
+ /* Event Loop */
+ struct thread thread;
+ while (thread_fetch(master, &thread))
+ thread_call(&thread);
+ return 0;
+}
+
+// clang-format off
+
+const char *json_expect1 = R"NONCE({
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "11.0.0.0/8",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default",
+ "state": {
+ "active": false
+ }
+ }
+ ]
+ }
+}
+
+)NONCE";
+
+const char *json_loadconf1 = R"NONCE(
+{
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "10.0.0.0/13",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)"
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default"
+ }
+ ]
+ }
+})NONCE";
+
+const char *json_expect2 = R"NONCE({
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "11.0.0.0/8",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.0.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.1.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.2.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.3.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default",
+ "state": {
+ "active": false
+ }
+ }
+ ]
+ }
+}
+
+)NONCE";
+
+const char *json_expect3 = R"NONCE({
+ "frr-routing:routing": {
+ "control-plane-protocols": {
+ "control-plane-protocol": [
+ {
+ "type": "frr-staticd:staticd",
+ "name": "staticd",
+ "vrf": "default",
+ "frr-staticd:staticd": {
+ "route-list": [
+ {
+ "prefix": "11.0.0.0/8",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.0.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.1.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.2.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "13.0.3.0/24",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "prefix": "10.0.0.0/13",
+ "afi-safi": "frr-routing:ipv4-unicast",
+ "path-list": [
+ {
+ "table-id": 0,
+ "distance": 1,
+ "tag": 0,
+ "frr-nexthops": {
+ "nexthop": [
+ {
+ "nh-type": "blackhole",
+ "vrf": "default",
+ "gateway": "",
+ "interface": "(null)",
+ "bh-type": "null",
+ "onlink": false
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "frr-vrf:lib": {
+ "vrf": [
+ {
+ "name": "default",
+ "state": {
+ "active": false
+ }
+ }
+ ]
+ }
+}
+
+)NONCE";
--- /dev/null
+import inspect
+import os
+import subprocess
+import pytest
+import frrtest
+
+class TestGRPC(object):
+ program = "./test_grpc"
+
+ @pytest.mark.skipif(
+ 'S["GRPC_TRUE"]=""\n' not in open("../config.status").readlines(),
+ reason="GRPC not enabled",
+ )
+ def test_exits_cleanly(self):
+ basedir = os.path.dirname(inspect.getsourcefile(type(self)))
+ program = os.path.join(basedir, self.program)
+ proc = subprocess.Popen(
+ [frrtest.binpath(program)], stdin=subprocess.PIPE, stdout=subprocess.PIPE
+ )
+ output, _ = proc.communicate()
+ self.exitcode = proc.wait()
+ if self.exitcode != 0:
+ raise frrtest.TestExitNonzero(self)
$(TESTS_ZEBRA) \
# end
+if GRPC
+check_PROGRAMS += \
+ tests/lib/test_grpc \
+ #end
+endif
+
if ZEROMQ
check_PROGRAMS += \
tests/lib/test_zmq \
# end
# note no -Werror
+TESTS_CXXFLAGS = \
+ $(AC_CXXFLAGS) \
+ $(LIBYANG_CFLAGS) \
+ $(SAN_FLAGS) \
+ # end
+# note no -Werror
+
ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP)
BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm
ISISD_TEST_LDADD = isisd/libisis.a $(ALL_TESTS_LDADD)
+if GRPC
+GRPC_TESTS_LDADD = staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm
+endif
OSPFD_TEST_LDADD = ospfd/libfrrospf.a $(ALL_TESTS_LDADD)
OSPF6_TEST_LDADD = ospf6d/libospf6.a $(ALL_TESTS_LDADD)
ZEBRA_TEST_LDADD = zebra/label_manager.o $(ALL_TESTS_LDADD)
tests_lib_northbound_test_oper_data_LDADD = $(ALL_TESTS_LDADD)
tests_lib_northbound_test_oper_data_SOURCES = tests/lib/northbound/test_oper_data.c
nodist_tests_lib_northbound_test_oper_data_SOURCES = yang/frr-test-module.yang.c
+if GRPC
+tests_lib_test_grpc_CXXFLAGS = $(WERROR) $(TESTS_CXXFLAGS)
+tests_lib_test_grpc_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_grpc_LDADD = $(GRPC_TESTS_LDADD)
+tests_lib_test_grpc_SOURCES = tests/lib/test_grpc.cpp
+endif
tests_lib_test_assert_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_assert_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_assert_LDADD = $(ALL_TESTS_LDADD)
# Read expected result from file
expected_original = open(refTableFile).read().rstrip()
- for filter in ["", "remote-as internal", "remote-as external",
+ for arguments in ["", "remote-as internal", "remote-as external",
"remote-as 100", "remote-as 123",
"neighbor 192.168.7.10", "neighbor 192.168.7.10",
"neighbor fc00:0:0:8::1000",
- "neighbor 10.0.0.1"]:
+ "neighbor 10.0.0.1",
+ "terse",
+ "remote-as internal terse",
+ "remote-as external terse",
+ "remote-as 100 terse", "remote-as 123 terse",
+ "neighbor 192.168.7.10 terse", "neighbor 192.168.7.10 terse",
+ "neighbor fc00:0:0:8::1000 terse",
+ "neighbor 10.0.0.1 terse"]:
# Actual output from router
actual = (
net["r%s" % i]
- .cmd('vtysh -c "show ip bgp summary ' + filter + '" 2> /dev/null')
+ .cmd('vtysh -c "show ip bgp summary ' + arguments + '" 2> /dev/null')
.rstrip()
)
+
# Mask out "using XXiXX bytes" portion. They are random...
actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
# Mask out "using XiXXX KiB" portion. They are random...
actual = re.sub(r"Total number.*", "", actual)
actual = re.sub(r"Displayed.*", "", actual)
# Remove IPv4 Unicast Summary (Title only)
- actual = re.sub(r"IPv4 Unicast Summary:", "", actual)
+ actual = re.sub(r"IPv4 Unicast Summary \(VRF default\):", "", actual)
# Remove IPv4 Multicast Summary (all of it)
- actual = re.sub(r"IPv4 Multicast Summary:", "", actual)
+ actual = re.sub(r"IPv4 Multicast Summary \(VRF default\):", "", actual)
actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual)
# Remove IPv4 VPN Summary (all of it)
- actual = re.sub(r"IPv4 VPN Summary:", "", actual)
+ actual = re.sub(r"IPv4 VPN Summary \(VRF default\):", "", actual)
actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual)
# Remove IPv4 Encap Summary (all of it)
- actual = re.sub(r"IPv4 Encap Summary:", "", actual)
+ actual = re.sub(r"IPv4 Encap Summary \(VRF default\):", "", actual)
actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual)
# Remove Unknown Summary (all of it)
- actual = re.sub(r"Unknown Summary:", "", actual)
+ actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual)
actual = re.sub(r"No Unknown neighbor is configured", "", actual)
- actual = re.sub(r"IPv4 labeled-unicast Summary:", "", actual)
+ actual = re.sub(r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual)
actual = re.sub(
r"No IPv4 labeled-unicast neighbor is configured", "", actual
)
expected = expected_original
- # apply filters on expected output
- if "internal" in filter or "remote-as 100" in filter:
+ # apply argumentss on expected output
+ if "internal" in arguments or "remote-as 100" in arguments:
expected = re.sub(r".+\s+200\s+.+", "", expected)
- elif "external" in filter:
+ elif "external" in arguments:
expected = re.sub(r".+\s+100\s+.+Active.+", "", expected)
- elif "remote-as 123" in filter:
+ elif "remote-as 123" in arguments:
expected = re.sub(
r"(192.168.7.(1|2)0|fc00:0:0:8::(1|2)000).+Active.+",
"", expected
)
- elif "192.168.7.10" in filter:
+ expected = re.sub(r"\nNeighbor.+Desc", "", expected)
+ expected = expected + "% No matching neighbor\n"
+ elif "192.168.7.10" in arguments:
expected = re.sub(
r"(192.168.7.20|fc00:0:0:8::(1|2)000).+Active.+",
"", expected
)
- elif "fc00:0:0:8::1000" in filter:
+ elif "fc00:0:0:8::1000" in arguments:
expected = re.sub(
r"(192.168.7.(1|2)0|fc00:0:0:8::2000).+Active.+",
"", expected
)
- elif "10.0.0.1" in filter:
- expected = "No such neighbor in this view/vrf"
+ elif "10.0.0.1" in arguments:
+ expected = "No such neighbor in VRF default"
+
+ if "terse" in arguments:
+ expected = re.sub(r"BGP table version .+", "", expected)
+ expected = re.sub(r"RIB entries .+", "", expected)
+ expected = re.sub(r"Peers [0-9]+, using .+", "", expected)
# Strip empty lines
actual = actual.lstrip().rstrip()
expected = re.sub(r"\n+", "\n", expected)
# reapply initial formatting
- actual = re.sub(r"KiB of memory\n", "KiB of memory\n\n", actual)
- expected = re.sub(r"KiB of memory\n", "KiB of memory\n\n", expected)
+ if "terse" in arguments:
+ actual = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", actual)
+ expected = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", expected)
+ else:
+ actual = re.sub(r"KiB of memory\n", "KiB of memory\n\n", actual)
+ expected = re.sub(r"KiB of memory\n", "KiB of memory\n\n", expected)
# realign expected neighbor columns if needed
try:
- idx_actual = re.search(r"\n(Neighbor\s+V\s+)", actual).group(1).find("V")
- idx_expected = re.search(r"\n(Neighbor\s+V\s+)", expected).group(1).find("V")
+ idx_actual = re.search(r"(Neighbor\s+V\s+)", actual).group(1).find("V")
+ idx_expected = re.search(r"(Neighbor\s+V\s+)", expected).group(1).find("V")
idx_diff = idx_expected - idx_actual
if idx_diff > 0:
# Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
diff = topotest.get_textdiff(
actual,
expected,
- title1="actual SHOW IP BGP SUMMARY " + filter.upper() ,
- title2="expected SHOW IP BGP SUMMARY " + filter.upper(),
+ title1="actual SHOW IP BGP SUMMARY " + arguments.upper() ,
+ title2="expected SHOW IP BGP SUMMARY " + arguments.upper(),
)
# Empty string if it matches, otherwise diff contains unified diff
diff,
)
- # Actual output from router
- actual = (
- net["r%s" % i]
- .cmd('vtysh -c "show ip bgp summary" 2> /dev/null')
- .rstrip()
- )
-
# Make sure that all daemons are running
for i in range(1, 2):
fatal_error = net["r%s" % i].checkRouterRunning()
actual = re.sub(r"Total number.*", "", actual)
actual = re.sub(r"Displayed.*", "", actual)
# Remove IPv4 Unicast Summary (Title only)
- actual = re.sub(r"IPv6 Unicast Summary:", "", actual)
+ actual = re.sub(r"IPv6 Unicast Summary \(VRF default\):", "", actual)
# Remove IPv4 Multicast Summary (all of it)
- actual = re.sub(r"IPv6 Multicast Summary:", "", actual)
+ actual = re.sub(r"IPv6 Multicast Summary \(VRF default\):", "", actual)
actual = re.sub(r"No IPv6 Multicast neighbor is configured", "", actual)
# Remove IPv4 VPN Summary (all of it)
- actual = re.sub(r"IPv6 VPN Summary:", "", actual)
+ actual = re.sub(r"IPv6 VPN Summary \(VRF default\):", "", actual)
actual = re.sub(r"No IPv6 VPN neighbor is configured", "", actual)
# Remove IPv4 Encap Summary (all of it)
- actual = re.sub(r"IPv6 Encap Summary:", "", actual)
+ actual = re.sub(r"IPv6 Encap Summary \(VRF default\):", "", actual)
actual = re.sub(r"No IPv6 Encap neighbor is configured", "", actual)
# Remove Unknown Summary (all of it)
- actual = re.sub(r"Unknown Summary:", "", actual)
+ actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual)
actual = re.sub(r"No Unknown neighbor is configured", "", actual)
# Remove Labeled Unicast Summary (all of it)
- actual = re.sub(r"IPv6 labeled-unicast Summary:", "", actual)
+ actual = re.sub(r"IPv6 labeled-unicast Summary \(VRF default\):", "", actual)
actual = re.sub(
r"No IPv6 labeled-unicast neighbor is configured", "", actual
)
{
"distance": 110,
"protocol": "ospf6",
- "metric": 10,
+ "metric": 20,
"selected": true,
"installed": true,
"prefix": "2001:db8:1::/64",
--- /dev/null
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]": null,
+ "[3]:[0]:[32]:[10.100.0.1]": null,
+ "[3]:[0]:[32]:[10.100.0.2]": null
+}
\ No newline at end of file
--- /dev/null
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"50.0.1.11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
\ No newline at end of file
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"50.0.1.11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
\ No newline at end of file
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": null } }
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"50:0:1::11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global"
+ }
+ ]
+ }
+] } }
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"50:0:1::11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global"
+ }
+ ]
+ }
+] } }
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": null } }
\ No newline at end of file
--- /dev/null
+router bgp 101
+ bgp router-id 10.100.0.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.1.2 remote-as 102
+ !
+ address-family l2vpn evpn
+ neighbor 10.0.1.2 activate
+ advertise-all-vni
+ exit-address-family
+!
+router bgp 101 vrf vrf-blue
+ bgp router-id 10.100.0.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 50.0.1.11 remote-as 111
+ neighbor 50:0:1::11 remote-as 111
+ !
+ address-family ipv4 unicast
+ no neighbor 50:0:1::11 activate
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ neighbor 50:0:1::11 activate
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ advertise ipv4 unicast gateway-ip
+ advertise ipv6 unicast gateway-ip
+ exit-address-family
\ No newline at end of file
--- /dev/null
+!
+log file zebra.log
+!
+ip route 10.100.0.2/32 10.0.1.2
+!
+vrf vrf-blue
+ vni 1000 prefix-routes-only
+ exit-vrf
+!
+interface lo
+ ip address 10.100.0.1/32
+interface PE1-eth0
+ ip address 10.0.1.1/24
+!
--- /dev/null
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32":[
+ {
+ "prefix":"100.0.0.21\/32",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32":[
+ {
+ "prefix":"100.0.0.21\/32",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32": null
+}
--- /dev/null
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128":[
+ {
+ "prefix":"100::21\/128",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128":[
+ {
+ "prefix":"100::21\/128",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128": null
+}
\ No newline at end of file
--- /dev/null
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]": null,
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
\ No newline at end of file
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":null,
+ "bestpath":null,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": null } }
\ No newline at end of file
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global",
+ "used":true
+ }
+ ]
+ }
+] } }
\ No newline at end of file
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":null,
+ "bestpath":null,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global",
+ "used":true
+ }
+ ]
+ }
+] } }
--- /dev/null
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": null } }
\ No newline at end of file
--- /dev/null
+router bgp 102
+ bgp router-id 10.100.0.2
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.1.1 remote-as 101
+ !
+ address-family l2vpn evpn
+ neighbor 10.0.1.1 activate
+ advertise-all-vni
+ enable-resolve-overlay-index
+ exit-address-family
+!
+router bgp 101 vrf vrf-blue
+ bgp router-id 10.100.0.2
--- /dev/null
+!
+log file zebra.log
+!
+ip route 10.100.0.1/32 10.0.1.1
+!
+vrf vrf-blue
+ vni 1000 prefix-routes-only
+ exit-vrf
+!
+interface lo
+ ip address 10.100.0.2/32
+interface PE2-eth0
+ ip address 10.0.1.2/24
+!
--- /dev/null
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32":[
+ {
+ "prefix":"100.0.0.21\/32",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":40,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32": null
+}
\ No newline at end of file
--- /dev/null
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32": null
+}
\ No newline at end of file
--- /dev/null
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128":[
+ {
+ "prefix":"100::21\/128",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":40,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
--- /dev/null
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128": null
+}
\ No newline at end of file
--- /dev/null
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128": null
+}
\ No newline at end of file
--- /dev/null
+router bgp 111
+ bgp router-id 10.100.0.11
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 50.0.1.1 remote-as 101
+ neighbor 50:0:1::1 remote-as 101
+ !
+ address-family ipv4 unicast
+ network 100.0.0.21/32
+ no neighbor 50:0:1::1 activate
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ network 100::21/128
+ neighbor 50:0:1::1 activate
+ exit-address-family
+
+
--- /dev/null
+!
+int host1-eth0
+ ip address 50.0.1.11/24
+ ipv6 address 50:0:1::11/48
--- /dev/null
+!
+int host1-eth0
+ ip address 50.0.1.21/24
+ ipv6 address 50:0:1::21/48
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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_bgp_evpn_overlay_index_gateway.py: Test EVPN gateway IP overlay index functionality
+Following functionality is covered:
+
+ +--------+ BGP +--------+ BGP +--------+ +--------+
+ SN1 | | IPv4/v6 | | EVPN | | | |
+ ======+ Host1 +---------+ PE1 +------+ PE2 +------+ Host2 +
+ | | | | | | | |
+ +--------+ +--------+ +--------+ +--------+
+
+ Host1 is connected to PE1 and host2 is connected to PE2
+ Host1 and PE1 have IPv4/v6 BGP sessions.
+ PE1 and PE2 gave EVPN session.
+ Host1 advertises IPv4/v6 prefixes to PE1.
+ PE1 advertises these prefixes to PE2 as EVPN type-5 routes.
+ Gateway IP for these EVPN type-5 routes is host1 IP.
+ Host1 MAC/IP is advertised by PE1 as EVPN type-2 route
+
+Following testcases are covered:
+TC_1:
+Check BGP and zebra states for above topology at PE1 and PE2.
+
+TC_2:
+Stop advertising prefixes from host1. It should withdraw type-5 routes. Check states at PE1 and PE2
+Advertise the prefixes again. Check states.
+
+TC_3:
+Shut down VxLAN interface at PE1. This should withdraw type-2 routes. Check states at PE1 and PE2.
+Enable VxLAN interface again. Check states.
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import pytest
+import time
+import platform
+
+#Current Working Directory
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.common_config import (
+ step,
+ write_test_header,
+ write_test_footer,
+ generate_support_bundle,
+)
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+#Global variables
+PES = ['PE1', 'PE2']
+HOSTS = ['host1', 'host2']
+PE_SUFFIX = {'PE1': '1', 'PE2': '2'}
+HOST_SUFFIX = {'host1': '1', 'host2': '2'}
+TRIGGERS = ["base", "no_rt5", "no_rt2"]
+
+
+class TemplateTopo(Topo):
+ """Test topology builder"""
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # This function only purpose is to define allocation and relationship
+ # between routers and add links.
+
+ # Create routers
+ for pe in PES:
+ tgen.add_router(pe)
+ for host in HOSTS:
+ tgen.add_router(host)
+
+ krel = platform.release()
+ logger.info('Kernel version ' + krel)
+
+ #Add links
+ tgen.add_link(tgen.gears['PE1'], tgen.gears['PE2'], 'PE1-eth0', 'PE2-eth0')
+ tgen.add_link(tgen.gears['PE1'], tgen.gears['host1'], 'PE1-eth1', 'host1-eth0')
+ tgen.add_link(tgen.gears['PE2'], tgen.gears['host2'], 'PE2-eth1', 'host2-eth0')
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15. Kernel present {}".format(kernelv))
+ return
+
+ if topotest.version_cmp(kernelv, '4.15') == 0:
+ l3mdev_accept = 1
+ logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept))
+ else:
+ l3mdev_accept = 0
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ tgen.start_topology()
+
+ # Configure MAC address for hosts as these MACs are advertised with EVPN type-2 routes
+ for (name, host) in tgen.gears.items():
+ if name not in HOSTS:
+ continue
+
+ host_mac = "1a:2b:3c:4d:5e:6{}".format(HOST_SUFFIX[name])
+ host.run("ip link set dev {}-eth0 down").format(name)
+ host.run("ip link set dev {0}-eth0 address {1}".format(name, host_mac))
+ host.run("ip link set dev {}-eth0 up").format(name)
+
+ # Configure PE VxLAN and Bridge interfaces
+ for (name, pe) in tgen.gears.items():
+ if name not in PES:
+ continue
+ vtep_ip = "10.100.0.{}".format(PE_SUFFIX[name])
+ bridge_ip = "50.0.1.{}/24".format(PE_SUFFIX[name])
+ bridge_ipv6 = "50:0:1::{}/48".format(PE_SUFFIX[name])
+
+ pe.run("ip link add vrf-blue type vrf table 10")
+ pe.run("ip link set dev vrf-blue up")
+ pe.run("ip link add vxlan100 type vxlan id 100 dstport 4789 local {}".format(vtep_ip))
+ pe.run("ip link add name br100 type bridge stp_state 0")
+ pe.run("ip link set dev vxlan100 master br100")
+ pe.run("ip link set dev {}-eth1 master br100".format(name))
+ pe.run("ip addr add {} dev br100".format(bridge_ip))
+ pe.run("ip link set up dev br100")
+ pe.run("ip link set up dev vxlan100")
+ pe.run("ip link set up dev {}-eth1".format(name))
+ pe.run("ip link set dev br100 master vrf-blue")
+ pe.run("ip -6 addr add {} dev br100".format(bridge_ipv6))
+
+ pe.run("ip link add vxlan1000 type vxlan id 1000 dstport 4789 local {}".format(vtep_ip))
+ pe.run("ip link add name br1000 type bridge stp_state 0")
+ pe.run("ip link set dev vxlan1000 master br100")
+ pe.run("ip link set up dev br1000")
+ pe.run("ip link set up dev vxlan1000")
+ pe.run("ip link set dev br1000 master vrf-blue")
+
+ pe.run("sysctl -w net.ipv4.ip_forward=1")
+ pe.run("sysctl -w net.ipv6.conf.all.forwarding=1")
+ pe.run("sysctl -w net.ipv4.udp_l3mdev_accept={}".format(l3mdev_accept))
+ pe.run("sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept))
+
+ # For all registred routers, load the zebra configuration file
+ for (name, router) in tgen.routers().items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(name))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(name))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ logger.info("Running setup_module() done")
+ topotest.sleep(200)
+
+
+def teardown_module(mod):
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def evpn_gateway_ip_show_op_check(trigger=" "):
+ """
+ This function checks CLI O/P for commands mentioned in show_commands for a given trigger
+ :param trigger: Should be a trigger present in TRIGGERS
+ :return: Returns a tuple (result: None for success, retmsg: Log message to be printed on failure)
+ """
+ tgen = get_topogen()
+
+ if trigger not in TRIGGERS:
+ return "Unexpected trigger", "Unexpected trigger {}".format(trigger)
+
+ show_commands = {'bgp_vni_routes': 'show bgp l2vpn evpn route vni 100 json',
+ 'bgp_vrf_ipv4' : 'show bgp vrf vrf-blue ipv4 json',
+ 'bgp_vrf_ipv6' : 'show bgp vrf vrf-blue ipv6 json',
+ 'zebra_vrf_ipv4': 'show ip route vrf vrf-blue json',
+ 'zebra_vrf_ipv6': 'show ipv6 route vrf vrf-blue json'}
+
+ for (name, pe) in tgen.gears.items():
+ if name not in PES:
+ continue
+
+ for (cmd_key, command) in show_commands.items():
+ expected_op_file = "{0}/{1}/{2}_{3}.json".format(CWD, name, cmd_key, trigger)
+ expected_op = json.loads(open(expected_op_file).read())
+
+ test_func = partial(topotest.router_json_cmp, pe, command, expected_op)
+ ret, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{0}" JSON output mismatch for {1}'.format(name, command)
+ if result is not None:
+ return result, assertmsg
+
+ return None, "Pass"
+
+
+def test_evpn_gateway_ip_basic_topo(request):
+ """
+ Tets EVPN overlay index gateway IP functionality. VErify show O/Ps on PE1 and PE2
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15")
+ write_test_footer(tc_name)
+ return
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Check O/Ps for EVPN gateway IP overlay Index functionality at PE1 and PE2")
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("base")
+
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_gateway_ip_flap_rt5(request):
+ """
+ Withdraw EVPN type-5 routes and check O/Ps at PE1 and PE2
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15")
+ write_test_footer(tc_name)
+ return
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ h1 = tgen.gears['host1']
+
+ step("Withdraw type-5 routes")
+
+ h1.run('vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv4" \
+ -c "no network 100.0.0.21/32"')
+ h1.run('vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv6" \
+ -c "no network 100::21/128"')
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("no_rt5")
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ step("Advertise type-5 routes again")
+
+ h1.run('vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv4" \
+ -c "network 100.0.0.21/32"')
+ h1.run('vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv6" \
+ -c "network 100::21/128"')
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("base")
+ if result is not None:
+ generate_support_bundle()
+
+ assert result is None, assertmsg
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_gateway_ip_flap_rt2(request):
+ """
+ Withdraw EVPN type-2 routes and check O/Ps at PE1 and PE2
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15")
+ write_test_footer(tc_name)
+ return
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+
+ step("Shut down VxLAN interface at PE1 which results in withdraw of type-2 routes")
+
+ pe1 = tgen.gears['PE1']
+
+ pe1.run('ip link set dev vxlan100 down')
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("no_rt2")
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ step("Bring up VxLAN interface at PE1 and advertise type-2 routes again")
+
+ pe1.run('ip link set dev vxlan100 up')
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("base")
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ write_test_footer(tc_name)
+
+
+def test_memory_leak():
+ """Run the memory leak test and report results"""
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
--- /dev/null
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.255.2 default-originate route-map default
+ exit-address-family
+!
+ip prefix-list r2 permit 10.0.0.0/22
+!
+route-map default permit 10
+ match ip address prefix-list r2
+!
--- /dev/null
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
--- /dev/null
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
--- /dev/null
+!
+interface lo
+ ip address 10.0.0.1/22
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
--- /dev/null
+#!/usr/bin/env python
+
+# Copyright (c) 2021 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 default-originate works with conditional match.
+If 10.0.0.0/22 is recived from r2, then we announce 0.0.0.0/0
+to r2.
+"""
+
+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
+from lib.common_config import step
+
+
+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.items(), 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_is_valid(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
+ expected = {"paths": [{"valid": True}]}
+ return topotest.json_cmp(output, expected)
+
+ step("Converge network")
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see bgp convergence at r2"
+
+ step("Withdraw 10.0.0.0/22 from R2")
+ router.vtysh_cmd(
+ "conf t\nrouter bgp\naddress-family ipv4\nno redistribute connected"
+ )
+
+ step("Check if we don't have 0.0.0.0/0 at R2")
+ test_func = functools.partial(_bgp_default_route_is_valid, router)
+ success, result = topotest.run_and_expect(test_func, not None, count=30, wait=0.5)
+ assert result is not None, "0.0.0.0/0 exists at r2"
+
+ step("Announce 10.0.0.0/22 from R2")
+ router.vtysh_cmd("conf t\nrouter bgp\naddress-family ipv4\nredistribute connected")
+
+ step("Check if we have 0.0.0.0/0 at R2")
+ test_func = functools.partial(_bgp_default_route_is_valid, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "0.0.0.0/0 does not exist at r2"
+
+ step("Withdraw 10.0.0.0/22 from R2 again")
+ router.vtysh_cmd(
+ "conf t\nrouter bgp\naddress-family ipv4\nno redistribute connected"
+ )
+
+ step("Check if we don't have 0.0.0.0/0 at R2 again")
+ test_func = functools.partial(_bgp_default_route_is_valid, router)
+ success, result = topotest.run_and_expect(test_func, not None, count=30, wait=0.5)
+ assert result is not None, "0.0.0.0/0 exists at r2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
route-map default permit 10
match community default
set metric 123
+ set as-path prepend 65000 65000 65000
!
route-map internal permit 10
set community 65000:1
def _bgp_default_route_has_metric(router):
output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
- expected = {"paths": [{"metric": 123}]}
+ expected = {
+ "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}]
+ }
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_converge, router)
!
route-map default permit 10
set metric 123
+ set as-path prepend 65000 65000 65000
!
def _bgp_default_route_has_metric(router):
output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
- expected = {"paths": [{"metric": 123}]}
+ expected = {
+ "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}]
+ }
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_converge, router)
--- /dev/null
+log timestamp precision 3
--- /dev/null
+log timestamp precision 3
+
+ip prefix-list ANY permit 0.0.0.0/0 le 32
+ipv6 prefix-list ANY seq 10 permit any
+
+route-map RM-NONE4 deny 10
+exit-route-map
+
+route-map RM-NONE6 deny 10
+exit-route-map
+
+interface r1-eth0
+ ip address 100.0.0.1/24
+ ipv6 address 2102::1/64
+exit
+
+ip protocol static route-map RM-NONE4
+ipv6 protocol static route-map RM-NONE6
--- /dev/null
+#!/usr/bin/env python
+#
+# June 2 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+# Copyright (c) 2019-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 the timing of config operations.
+
+The initial add of 10k routes is used as a baseline for timing and all future
+operations are expected to complete in under 2 times that baseline. This is a
+lot of slop; however, the pre-batching code some of these operations (e.g.,
+adding the same set of 10k routes) would take 100 times longer, so the intention
+is to catch those types of regressions.
+"""
+
+import datetime
+import ipaddress
+import math
+import os
+import sys
+import pytest
+
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.staticd]
+
+class TimingTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+ tgen.add_router("r1")
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+
+def setup_module(mod):
+ tgen = Topogen(TimingTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)),
+ )
+ router.load_config(
+ TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+def get_ip_networks(super_prefix, count):
+ count_log2 = math.log(count, 2)
+ if count_log2 != int(count_log2):
+ count_log2 = int(count_log2) + 1
+ else:
+ count_log2 = int(count_log2)
+ network = ipaddress.ip_network(super_prefix)
+ return tuple(network.subnets(count_log2))[0:count]
+
+def test_static_timing():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def do_config(
+ count, bad_indices, base_delta, d_multiplier, add=True, do_ipv6=False, super_prefix=None, en_dbg=False
+ ):
+ router_list = tgen.routers()
+ tot_delta = float(0)
+
+ optype = "adding" if add else "removing"
+ iptype = "IPv6" if do_ipv6 else "IPv4"
+ if super_prefix is None:
+ super_prefix = u"2001::/48" if do_ipv6 else u"10.0.0.0/8"
+ via = u"lo"
+ optyped = "added" if add else "removed"
+
+ for rname, router in router_list.items():
+ router.logger.info("{} {} static {} routes".format(
+ optype, count, iptype)
+ )
+
+ # Generate config file.
+ config_file = os.path.join(
+ router.logdir, rname, "{}-routes-{}.conf".format(
+ iptype.lower(), optype
+ )
+ )
+ with open(config_file, "w") as f:
+ for i, net in enumerate(get_ip_networks(super_prefix, count)):
+ if i in bad_indices:
+ if add:
+ f.write("ip route {} {} bad_input\n".format(net, via))
+ else:
+ f.write("no ip route {} {} bad_input\n".format(net, via))
+ elif add:
+ f.write("ip route {} {}\n".format(net, via))
+ else:
+ f.write("no ip route {} {}\n".format(net, via))
+
+ # Enable debug
+ if en_dbg:
+ router.vtysh_cmd("debug northbound callbacks configuration")
+
+ # Load config file.
+ load_command = 'vtysh -f "{}"'.format(config_file)
+ tstamp = datetime.datetime.now()
+ output = router.run(load_command)
+ delta = (datetime.datetime.now() - tstamp).total_seconds()
+ tot_delta += delta
+
+ router.logger.info(
+ "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format(
+ load_command, output, delta
+ )
+ )
+
+ limit_delta = base_delta * d_multiplier
+ logger.info(
+ "{} {} {} static routes under {} in {}s (limit: {}s)".format(
+ optyped, count, iptype.lower(), super_prefix, tot_delta, limit_delta
+ )
+ )
+ if limit_delta:
+ assert tot_delta <= limit_delta
+
+ return tot_delta
+
+ # Number of static routes
+ prefix_count = 10000
+ prefix_base = [[u"10.0.0.0/8", u"11.0.0.0/8"],
+ [u"2100:1111:2220::/44", u"2100:3333:4440::/44"]]
+
+ bad_indices = []
+ for ipv6 in [False, True]:
+ base_delta = do_config(prefix_count, bad_indices, 0, 0, True, ipv6, prefix_base[ipv6][0])
+
+ # Another set of same number of prefixes
+ do_config(prefix_count, bad_indices, base_delta, 2, True, ipv6, prefix_base[ipv6][1])
+
+ # Duplicate config
+ do_config(prefix_count, bad_indices, base_delta, 2, True, ipv6, prefix_base[ipv6][0])
+
+ # Remove 1/2 of duplicate
+ do_config(prefix_count / 2, bad_indices, base_delta, 2, False, ipv6, prefix_base[ipv6][0])
+
+ # Add all back in so 1/2 replicate 1/2 new
+ do_config(prefix_count, bad_indices, base_delta, 2, True, ipv6, prefix_base[ipv6][0])
+
+ # remove all
+ delta = do_config(prefix_count, bad_indices, base_delta, 2, False, ipv6, prefix_base[ipv6][0])
+ delta += do_config(prefix_count, bad_indices, base_delta, 2, False, ipv6, prefix_base[ipv6][1])
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
Topotest conftest.py file.
"""
+import glob
import os
import pdb
+import re
import pytest
from lib.topogen import get_topogen, diagnose_env
from lib.topotest import g_extra_config as topotest_extra_config
from lib.topolog import logger
+try:
+ from _pytest._code.code import ExceptionInfo
+ leak_check_ok = True
+except ImportError:
+ leak_check_ok = False
+
def pytest_addoption(parser):
"""
help="Only set up this topology, don't run tests",
)
+ parser.addoption(
+ "--valgrind-extra",
+ action="store_true",
+ help="Generate suppression file, and enable more precise (slower) valgrind checks",
+ )
+
+ parser.addoption(
+ "--valgrind-memleaks",
+ action="store_true",
+ help="Run all daemons under valgrind for memleak detection",
+ )
+
parser.addoption(
"--vtysh",
metavar="ROUTER[,ROUTER...]",
)
+def check_for_memleaks():
+ if not topotest_extra_config["valgrind_memleaks"]:
+ return
+
+ leaks = []
+ tgen = get_topogen()
+ latest = []
+ existing = []
+ if tgen is not None:
+ logdir = "/tmp/topotests/{}".format(tgen.modname)
+ if hasattr(tgen, "valgrind_existing_files"):
+ existing = tgen.valgrind_existing_files
+ latest = glob.glob(os.path.join(logdir, "*.valgrind.*"))
+
+ for vfile in latest:
+ if vfile in existing:
+ continue
+ with open(vfile) as vf:
+ vfcontent = vf.read()
+ match = re.search(r"ERROR SUMMARY: (\d+) errors", vfcontent)
+ if match and match.group(1) != "0":
+ emsg = '{} in {}'.format(match.group(1), vfile)
+ leaks.append(emsg)
+
+ if leaks:
+ if leak_check_ok:
+ pytest.fail("Memleaks found:\n\t" + "\n\t".join(leaks))
+ else:
+ logger.error("Memleaks found:\n\t" + "\n\t".join(leaks))
+
+
def pytest_runtest_call():
"""
This function must be run after setup_module(), it does standarized post
shell_on_error = config.getoption("--shell-on-error")
topotest_extra_config["shell_on_error"] = shell_on_error
+ topotest_extra_config["valgrind_extra"] = config.getoption("--valgrind-extra")
+ topotest_extra_config["valgrind_memleaks"] = config.getoption("--valgrind-memleaks")
+
vtysh = config.getoption("--vtysh")
topotest_extra_config["vtysh"] = vtysh.split(",") if vtysh else []
else:
pause = False
+ if call.excinfo is None and call.when == "call":
+ try:
+ check_for_memleaks()
+ except:
+ call.excinfo = ExceptionInfo()
+
if call.excinfo is None:
error = False
else:
# Verification APIs
#############################################
@retry(attempts=4, wait=2, return_is_str=True)
-def verify_router_id(tgen, topo, input_dict):
+def verify_router_id(tgen, topo, input_dict, expected=True):
"""
Running command "show ip bgp json" for DUT and reading router-id
from input_dict and verifying with command output.
* `topo`: input json file data
* `input_dict`: input dictionary, have details of Device Under Test, for
which user wants to test the data
+ * `expected` : expected results from API, by-default True
+
Usage
-----
# Verify if router-id for r1 is 12.12.12.12
@retry(attempts=50, wait=3, return_is_str=True)
-def verify_bgp_convergence(tgen, topo, dut=None):
+def verify_bgp_convergence(tgen, topo, dut=None, expected=True):
"""
API will verify if BGP is converged with in the given time frame.
Running "show bgp summary json" command and verify bgp neighbor
* `tgen`: topogen object
* `topo`: input json file data
* `dut`: device under test
+ * `expected` : expected results from API, by-default True
+
Usage
-----
# To veriry is BGP is converged for all the routers used in
@retry(attempts=4, wait=4, return_is_str=True)
def verify_bgp_community(
- tgen, addr_type, router, network, input_dict=None, vrf=None, bestpath=False
+ tgen, addr_type, router, network, input_dict=None, vrf=None, bestpath=False, expected=True
):
"""
API to veiryf BGP large community is attached in route for any given
values needs to be verified
* `vrf`: VRF name
* `bestpath`: To check best path cli
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=4, wait=2, return_is_str=True)
-def verify_as_numbers(tgen, topo, input_dict):
+def verify_as_numbers(tgen, topo, input_dict, expected=True):
"""
This API is to verify AS numbers for given DUT by running
"show ip bgp neighbor json" command. Local AS and Remote AS
* `topo`: input json file data
* `addr_type` : ip type, ipv4/ipv6
* `input_dict`: defines - for which router, AS numbers needs to be verified
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=50, wait=3, return_is_str=True)
-def verify_bgp_convergence_from_running_config(tgen, dut=None):
+def verify_bgp_convergence_from_running_config(tgen, dut=None, expected=True):
"""
API to verify BGP convergence b/w loopback and physical interface.
This API would be used when routers have BGP neighborship is loopback
----------
* `tgen`: topogen object
* `dut`: device under test
+ * `expected` : expected results from API, by-default True
Usage
-----
input_dict=None,
seq_id=None,
nexthop=None,
+ expected=True
):
"""
API will verify BGP attributes set by Route-map for given prefix and
* `rmap_name`: route map name for which set criteria needs to be verified
* `input_dict`: defines for which router, AS numbers needs
* `seq_id`: sequence number of rmap, default is None
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=4, wait=2, return_is_str=True)
def verify_best_path_as_per_bgp_attribute(
- tgen, addr_type, router, input_dict, attribute
+ tgen, addr_type, router, input_dict, attribute, expected=True
):
"""
API is to verify best path according to BGP attributes for given routes.
* `attribute` : calculate best path using this attribute
* `input_dict`: defines different routes to calculate for which route
best path is selected
+ * `expected` : expected results from API, by-default True
+
Usage
-----
# To verify best path for routes 200.50.2.0/32 and 200.60.2.0/32 from
@retry(attempts=5, wait=2, return_is_str=True)
def verify_best_path_as_per_admin_distance(
- tgen, addr_type, router, input_dict, attribute
+ tgen, addr_type, router, input_dict, attribute, expected=True
):
"""
API is to verify best path according to admin distance for given
* `attribute` : calculate best path using admin distance
* `input_dict`: defines different routes with different admin distance
to calculate for which route best path is selected
+ * `expected` : expected results from API, by-default True
+
Usage
-----
# To verify best path for route 200.50.2.0/32 from router r2 to
@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2)
def verify_bgp_rib(
- tgen, addr_type, dut, input_dict, next_hop=None, aspath=None, multi_nh=None
+ tgen, addr_type, dut, input_dict, next_hop=None, aspath=None, multi_nh=None, expected=True
):
"""
This API is to verify whether bgp rib has any
* `next_hop`[optional]: next_hop which needs to be verified,
default = static
* 'aspath'[optional]: aspath which needs to be verified
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=5, wait=2, return_is_str=True)
-def verify_graceful_restart(tgen, topo, addr_type, input_dict, dut, peer):
+def verify_graceful_restart(tgen, topo, addr_type, input_dict, dut, peer, expected=True):
"""
This API is to verify verify_graceful_restart configuration of DUT and
cross verify the same from the peer bgp routerrouter.
which user wants to test the data
* `dut`: input dut router name
* `peer`: input peer router name
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=5, wait=2, return_is_str=True)
-def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer):
+def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer, expected=True):
"""
This API is to verify r_bit in the BGP gr capability advertised
by the neighbor router
which user wants to test the data
* `dut`: input dut router name
* `peer`: peer name
+ * `expected` : expected results from API, by-default True
+
Usage
-----
input_dict = {
@retry(attempts=5, wait=2, return_is_str=True)
-def verify_eor(tgen, topo, addr_type, input_dict, dut, peer):
+def verify_eor(tgen, topo, addr_type, input_dict, dut, peer, expected=True):
"""
This API is to verify EOR
@retry(attempts=4, wait=2, return_is_str=True)
-def verify_f_bit(tgen, topo, addr_type, input_dict, dut, peer):
+def verify_f_bit(tgen, topo, addr_type, input_dict, dut, peer, expected=True):
"""
This API is to verify f_bit in the BGP gr capability advertised
by the neighbor router
which user wants to test the data
* `dut`: input dut router name
* `peer`: peer name
+ * `expected` : expected results from API, by-default True
Usage
-----
for which user wants to test the data
* `dut`: input dut router name
* `peer`: peer name
+ * `expected` : expected results from API, by-default True
+
Usage
-----
# Configure graceful-restart
@retry(attempts=4, wait=2, return_is_str=True)
-def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut):
+def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut, expected=True):
"""
This API is to verify gr_address_family in the BGP gr capability advertised
by the neighbor router
* `addr_type` : ip type ipv4/ipv6
* `addr_type` : ip type IPV4 Unicast/IPV6 Unicast
* `dut`: input dut router name
+ * `expected` : expected results from API, by-default True
Usage
-----
ipLen=None,
rd_peer=None,
rt_peer=None,
+ expected=True
):
"""
API to verify rd and rt value using "sh bgp l2vpn evpn 10.1.1.1"
* `ipLen` : IP prefix length
* `rd_peer` : Peer name from which RD will be auto-generated
* `rt_peer` : Peer name from which RT will be auto-generated
+ * `expected` : expected results from API, by-default True
+
Usage
-----
input_dict_1 = {
@retry(attempts=5, wait=2, return_is_str=True)
def verify_evpn_routes(
- tgen, topo, dut, input_dict, routeType=5, EthTag=0, next_hop=None
+ tgen, topo, dut, input_dict, routeType=5, EthTag=0, next_hop=None, expected=True
):
"""
API to verify evpn routes using "sh bgp l2vpn evpn"
* `route_type` : Route type 5 is supported as of now
* `EthTag` : Ethernet tag, by-default is 0
* `next_hop` : Prefered nexthop for the evpn routes
+ * `expected` : expected results from API, by-default True
+
Usage
-----
input_dict_1 = {
"debug ospf te",
"debug ospf zebra",
],
+ "ospf6": [
+ "debug ospf6 event",
+ "debug ospf6 ism",
+ "debug ospf6 lsa",
+ "debug ospf6 nsm",
+ "debug ospf6 nssa",
+ "debug ospf6 packet all",
+ "debug ospf6 sr",
+ "debug ospf6 te",
+ "debug ospf6 zebra",
+ ],
}
if config.has_option("topogen", "verbosity"):
daemons.append("zebra")
if "pimd" in result:
daemons.append("pimd")
-
+ if "ospfd" in result:
+ daemons.append("ospfd")
+ if "ospf6d" in result:
+ daemons.append("ospf6d")
rnode.startDaemons(daemons)
except Exception as e:
for val in topo["routers"][rtr]["links"].values():
if "pim" in val and "pimd" not in daemon_list:
daemon_list.append("pimd")
+ if "ospf" in val and "ospfd" not in daemon_list:
+ daemon_list.append("ospfd")
+ if "ospf6" in val and "ospf6d" not in daemon_list:
+ daemon_list.append("ospf6d")
break
return daemon_list
--- /dev/null
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# 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 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.
+
+"""
+Subscribe to a multicast group so that the kernel sends an IGMP JOIN
+for the multicast group we subscribed to.
+"""
+
+import argparse
+import os
+import json
+import socket
+import subprocess
+import struct
+import sys
+import time
+
+#
+# Functions
+#
+def interface_name_to_index(name):
+ "Gets the interface index using its name. Returns None on failure."
+ interfaces = json.loads(
+ subprocess.check_output('ip -j link show', shell=True))
+
+ for interface in interfaces:
+ if interface['ifname'] == name:
+ return interface['ifindex']
+
+ return None
+
+
+def multicast_join(sock, ifindex, group, port):
+ "Joins a multicast group."
+ mreq = struct.pack(
+ "=4sLL", socket.inet_aton(args.group), socket.INADDR_ANY, ifindex
+ )
+
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind((group, port))
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
+
+
+#
+# Main code.
+#
+parser = argparse.ArgumentParser(description="Multicast RX utility")
+parser.add_argument('socket', help='Point to topotest UNIX socket')
+parser.add_argument('group', help='Multicast IP')
+parser.add_argument('interface', help='Interface name')
+parser.add_argument(
+ '--send',
+ help='Transmit instead of join with interval (defaults to 0.7 sec)',
+ type=float, default=0)
+args = parser.parse_args()
+
+ttl = 16
+port = 1000
+
+# Get interface index/validate.
+ifindex = interface_name_to_index(args.interface)
+if ifindex is None:
+ sys.stderr.write('Interface {} does not exists\n'.format(args.interface))
+ sys.exit(1)
+
+# We need root privileges to set up multicast.
+if os.geteuid() != 0:
+ sys.stderr.write("ERROR: You must have root privileges\n")
+ sys.exit(1)
+
+# Wait for topotest to synchronize with us.
+toposock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+while True:
+ try:
+ toposock.connect(args.socket)
+ break
+ except ConnectionRefusedError:
+ time.sleep(1)
+ continue
+
+msock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+if args.send > 0:
+ # Prepare multicast bit in that interface.
+ msock.setsockopt(
+ socket.SOL_SOCKET, 25,
+ struct.pack("%ds" % len(args.interface),
+ args.interface.encode('utf-8')))
+ # Set packets TTL.
+ msock.setsockopt(
+ socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack("b", ttl))
+ # Block to ensure packet send.
+ msock.setblocking(True)
+ # Set topotest socket non blocking so we can multiplex the main loop.
+ toposock.setblocking(False)
+else:
+ multicast_join(msock, ifindex, args.group, port)
+
+counter = 0
+while True:
+ if args.send > 0:
+ msock.sendto(b"test %d" % counter, (args.group, port))
+ counter += 1
+ time.sleep(args.send)
+
+ try:
+ data = toposock.recv(1)
+ if data == b'':
+ print(' -> Connection closed')
+ break
+ except BlockingIOError:
+ continue
+
+msock.close()
+
+sys.exit(0)
# OF THIS SOFTWARE.
#
-from copy import deepcopy
import traceback
+import ipaddr
+import ipaddress
+import sys
+
+from copy import deepcopy
from time import sleep
from lib.topolog import logger
-import ipaddr
from lib.topotest import frr_unicode
-
+from ipaddress import IPv6Address
# Import common_config to use commomnly used APIs
from lib.common_config import (
create_common_configuration,
logger.debug("Router %s: 'ospf' not present in input_dict", router)
continue
- result = __create_ospf_global(tgen, input_dict, router, build, load_config)
+ result = __create_ospf_global(
+ tgen, input_dict, router, build, load_config)
if result is True:
ospf_data = input_dict[router]["ospf"]
+ for router in input_dict.keys():
+ if "ospf6" not in input_dict[router]:
+ logger.debug("Router %s: 'ospf6' not present in input_dict", router)
+ continue
+
+ result = __create_ospf_global(
+ tgen, input_dict, router, build, load_config, ospf='ospf6')
+ if result is True:
+ ospf_data = input_dict[router]["ospf6"]
+
logger.debug("Exiting lib API: create_router_ospf()")
return result
config_data.append(cmd)
+
# router id
router_id = ospf_data.setdefault("router_id", None)
del_router_id = ospf_data.setdefault("del_router_id", False)
if router_id:
config_data.append("{} router-id {}".format(ospf, router_id))
+ # log-adjacency-changes
+ log_adj_changes = ospf_data.setdefault("log_adj_changes", None)
+ del_log_adj_changes = ospf_data.setdefault("del_log_adj_changes", False)
+ if del_log_adj_changes:
+ config_data.append("no log-adjacency-changes detail")
+ if log_adj_changes:
+ config_data.append("log-adjacency-changes {}".format(
+ log_adj_changes))
+
+ # aggregation timer
+ aggr_timer = ospf_data.setdefault("aggr_timer", None)
+ del_aggr_timer = ospf_data.setdefault("del_aggr_timer", False)
+ if del_aggr_timer:
+ config_data.append("no aggregation timer")
+ if aggr_timer:
+ config_data.append("aggregation timer {}".format(
+ aggr_timer))
+
+ # maximum path information
+ ecmp_data = ospf_data.setdefault("maximum-paths", {})
+ if ecmp_data:
+ cmd = "maximum-paths {}".format(ecmp_data)
+ del_action = ospf_data.setdefault("del_max_path", False)
+ if del_action:
+ cmd = "no maximum-paths"
+ config_data.append(cmd)
+
# redistribute command
redistribute_data = ospf_data.setdefault("redistribute", {})
if redistribute_data:
cmd = "no {}".format(cmd)
config_data.append(cmd)
+ #def route information
+ def_rte_data = ospf_data.setdefault("default-information", {})
+ if def_rte_data:
+ if "originate" not in def_rte_data:
+ logger.debug("Router %s: 'originate key' not present in "
+ "input_dict", router)
+ else:
+ cmd = "default-information originate"
+
+ if "always" in def_rte_data:
+ cmd = cmd + " always"
+
+ if "metric" in def_rte_data:
+ cmd = cmd + " metric {}".format(def_rte_data["metric"])
+
+ if "metric-type" in def_rte_data:
+ cmd = cmd + " metric-type {}".format(def_rte_data[
+ "metric-type"])
+
+ if "route-map" in def_rte_data:
+ cmd = cmd + " route-map {}".format(def_rte_data[
+ "route-map"])
+
+ del_action = def_rte_data.setdefault("delete", False)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
# area interface information for ospf6d only
if ospf == "ospf6":
area_iface = ospf_data.setdefault("neighbors", {})
cmd = "no {}".format(cmd)
config_data.append(cmd)
+ try:
+ if "area" in input_dict[router]['links'][neighbor][
+ 'ospf6']:
+ iface = input_dict[router]["links"][neighbor]["interface"]
+ cmd = "interface {} area {}".format(
+ iface, input_dict[router]['links'][neighbor][
+ 'ospf6']['area'])
+ if input_dict[router]['links'][neighbor].setdefault(
+ "delete", False):
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+ except KeyError:
+ pass
+
+
# summary information
summary_data = ospf_data.setdefault("summary-address", {})
if summary_data:
result = create_common_configuration(
tgen, router, config_data, "interface_config", build=build
)
- logger.debug("Exiting lib API: create_igmp_config()")
+ logger.debug("Exiting lib API: config_ospf_interface()")
return result
-def clear_ospf(tgen, router):
+def clear_ospf(tgen, router, ospf=None):
"""
This API is to clear ospf neighborship by running
clear ip ospf interface * command,
return False
rnode = tgen.routers()[router]
-
# Clearing OSPF
- logger.info("Clearing ospf process for router %s..", router)
+ if ospf:
+ version = "ipv6"
+ else:
+ version = "ip"
- run_frr_cmd(rnode, "clear ip ospf interface ")
+ cmd = "clear {} ospf interface".format(version)
+ logger.info(
+ "Clearing ospf process on router %s.. using command '%s'", router, cmd)
+ run_frr_cmd(rnode, cmd)
logger.debug("Exiting lib API: clear_ospf()")
# Verification procs
################################
@retry(attempts=40, wait=2, return_is_str=True)
-def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
+def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False, expected=True):
"""
This API is to verify ospf neighborship by running
show ip ospf neighbour command,
* `dut`: device under test
* `input_dict` : Input dict data, required when configuring from testcase
* `lan` : verify neighbors in lan topology
+ * `expected` : expected results from API, by-default True
Usage
-----
################################
# Verification procs
################################
-@retry(attempts=40, wait=2, return_is_str=True)
-def verify_ospf6_neighbor(tgen, topo):
+@retry(attempts=10, wait=2, return_is_str=True)
+def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
"""
This API is to verify ospf neighborship by running
- show ip ospf neighbour command,
+ show ipv6 ospf neighbour command,
Parameters
----------
* `tgen` : Topogen object
* `topo` : json file data
+ * `dut`: device under test
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `lan` : verify neighbors in lan topology
Usage
-----
- Check FULL neighbors.
- verify_ospf_neighbor(tgen, topo)
+ 1. To check FULL neighbors.
+ verify_ospf_neighbor(tgen, topo, dut=dut)
- result = verify_ospf_neighbor(tgen, topo)
+ 2. To check neighbors with their roles.
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "neighbors": {
+ "r1": {
+ "state": "Full",
+ "role": "DR"
+ },
+ "r2": {
+ "state": "Full",
+ "role": "DROther"
+ },
+ "r3": {
+ "state": "Full",
+ "role": "DROther"
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf6_neighbor(tgen, topo, dut, input_dict, lan=True)
Returns
-------
True or False (Error Message)
"""
-
- logger.debug("Entering lib API: verify_ospf6_neighbor()")
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
result = False
- for router, rnode in tgen.routers().items():
- if "ospf6" not in topo["routers"][router]:
- continue
- logger.info("Verifying OSPF6 neighborship on router %s:", router)
- show_ospf_json = run_frr_cmd(
- rnode, "show ipv6 ospf6 neighbor json", isjson=True
- )
+ if input_dict:
+ for router, rnode in tgen.routers().items():
+ if 'ospf6' not in topo['routers'][router]:
+ continue
- if not show_ospf_json:
- return "OSPF6 is not running"
-
- ospf_nbr_list = topo["routers"][router]["ospf6"]["neighbors"]
- no_of_peer = 0
- for ospf_nbr in ospf_nbr_list:
- ospf_nbr_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
- for neighbor in show_ospf_json["neighbors"]:
- if neighbor["neighborId"] == ospf_nbr_rid:
- nh_state = neighbor["state"]
- break
- else:
- return "[DUT: {}] OSPF6 peer {} missing".format(router, ospf_nbr_rid)
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying OSPF neighborship on router %s:", router)
+ show_ospf_json = run_frr_cmd(rnode,
+ "show ipv6 ospf neighbor json", isjson=True)
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF6 is not running"
+ return errormsg
+
+ ospf_data_list = input_dict[router]["ospf6"]
+ ospf_nbr_list = ospf_data_list['neighbors']
+
+ for ospf_nbr, nbr_data in ospf_nbr_list.items():
+ data_ip = data_rid = topo['routers'][ospf_nbr]['ospf6']['router_id']
+ if ospf_nbr in data_ip:
+ nbr_details = nbr_data[ospf_nbr]
+ elif lan:
+ for switch in topo['switches']:
+ if 'ospf6' in topo['switches'][switch]['links'][router]:
+ neighbor_ip = data_ip
+ else:
+ continue
+ else:
+ neighbor_ip = data_ip[router]['ipv6'].split("/")[0]
- if nh_state == "Full":
- no_of_peer += 1
+ nh_state = None
+ neighbor_ip = neighbor_ip.lower()
+ nbr_rid = data_rid
+ get_index_val = dict((d['neighborId'], dict( \
+ d, index=index)) for (index, d) in enumerate( \
+ show_ospf_json['neighbors']))
+ try:
+ nh_state = get_index_val.get(neighbor_ip)['state']
+ intf_state = get_index_val.get(neighbor_ip)['ifState']
+ except TypeError:
+ errormsg = "[DUT: {}] OSPF peer {} missing,from "\
+ "{} ".format(router,
+ nbr_rid, ospf_nbr)
+ return errormsg
- if no_of_peer == len(ospf_nbr_list):
- logger.info("[DUT: {}] OSPF6 is Converged".format(router))
- result = True
- else:
- return "[DUT: {}] OSPF6 is not Converged".format(router)
+ nbr_state = nbr_data.setdefault("state",None)
+ nbr_role = nbr_data.setdefault("role",None)
- logger.debug("Exiting API: verify_ospf6_neighbor()")
+ if nbr_state:
+ if nbr_state == nh_state:
+ logger.info("[DUT: {}] OSPF6 Nbr is {}:{} State {}".format
+ (router, ospf_nbr, nbr_rid, nh_state))
+ result = True
+ else:
+ errormsg = ("[DUT: {}] OSPF6 is not Converged, neighbor"
+ " state is {} , Expected state is {}".format(router,
+ nh_state, nbr_state))
+ return errormsg
+ if nbr_role:
+ if nbr_role == intf_state:
+ logger.info("[DUT: {}] OSPF6 Nbr is {}: {} Role {}".format(
+ router, ospf_nbr, nbr_rid, nbr_role))
+ else:
+ errormsg = ("[DUT: {}] OSPF6 is not Converged with rid"
+ "{}, role is {}, Expected role is {}".format(router,
+ nbr_rid, intf_state, nbr_role))
+ return errormsg
+ continue
+ else:
+
+ for router, rnode in tgen.routers().items():
+ if 'ospf6' not in topo['routers'][router]:
+ continue
+
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying OSPF6 neighborship on router %s:", router)
+ show_ospf_json = run_frr_cmd(rnode,
+ "show ipv6 ospf neighbor json", isjson=True)
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF6 is not running"
+ return errormsg
+
+ ospf_data_list = topo["routers"][router]["ospf6"]
+ ospf_neighbors = ospf_data_list['neighbors']
+ total_peer = 0
+ total_peer = len(ospf_neighbors.keys())
+ no_of_ospf_nbr = 0
+ ospf_nbr_list = ospf_data_list['neighbors']
+ no_of_peer = 0
+ for ospf_nbr, nbr_data in ospf_nbr_list.items():
+ data_ip = data_rid = topo['routers'][ospf_nbr]['ospf6']['router_id']
+ if ospf_nbr in data_ip:
+ nbr_details = nbr_data[ospf_nbr]
+ elif lan:
+ for switch in topo['switches']:
+ if 'ospf6' in topo['switches'][switch]['links'][router]:
+ neighbor_ip = data_ip
+ else:
+ continue
+ else:
+ neighbor_ip = data_ip
+
+ nh_state = None
+ neighbor_ip = neighbor_ip.lower()
+ nbr_rid = data_rid
+ get_index_val = dict((d['neighborId'], dict( \
+ d, index=index)) for (index, d) in enumerate( \
+ show_ospf_json['neighbors']))
+ try:
+ nh_state = get_index_val.get(neighbor_ip)['state']
+ intf_state = get_index_val.get(neighbor_ip)['ifState']
+ except TypeError:
+ errormsg = "[DUT: {}] OSPF peer {} missing,from "\
+ "{} ".format(router,
+ nbr_rid, ospf_nbr)
+ return errormsg
+
+ if nh_state == 'Full':
+ no_of_peer += 1
+
+ if no_of_peer == total_peer:
+ logger.info("[DUT: {}] OSPF6 is Converged".format(router))
+ result = True
+ else:
+ errormsg = ("[DUT: {}] OSPF6 is not Converged".format(router))
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
@retry(attempts=21, wait=2, return_is_str=True)
def verify_ospf_rib(
- tgen, dut, input_dict, next_hop=None, tag=None, metric=None, fib=None
+ tgen, dut, input_dict, next_hop=None, tag=None, metric=None, fib=None, expected=True
):
"""
This API is to verify ospf routes by running
* `tag` : tag to be verified
* `metric` : metric to be verified
* `fib` : True if the route is installed in FIB.
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=10, wait=2, return_is_str=True)
-def verify_ospf_interface(tgen, topo, dut=None, lan=False, input_dict=None):
+def verify_ospf_interface(tgen, topo, dut=None, lan=False, input_dict=None, expected=True):
"""
This API is to verify ospf routes by running
show ip ospf interface command.
* `dut`: device under test
* `lan`: if set to true this interface belongs to LAN.
* `input_dict` : Input dict data, required when configuring from testcase
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=11, wait=2, return_is_str=True)
-def verify_ospf_database(tgen, topo, dut, input_dict):
+def verify_ospf_database(tgen, topo, dut, input_dict, expected=True):
"""
This API is to verify ospf lsa's by running
show ip ospf database command.
* `dut`: device under test
* `input_dict` : Input dict data, required when configuring from testcase
* `topo` : next to be verified
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=10, wait=2, return_is_str=True)
-def verify_ospf_summary(tgen, topo, dut, input_dict):
+def verify_ospf_summary(tgen, topo, dut, input_dict, expected=True):
"""
This API is to verify ospf routes by running
show ip ospf interface command.
* `topo` : topology descriptions
* `dut`: device under test
* `input_dict` : Input dict data, required when configuring from testcase
+ * `expected` : expected results from API, by-default True
Usage
-----
logger.debug("Exiting API: verify_ospf_summary()")
return result
+
+
+
+@retry(attempts=10, wait=3, return_is_str=True)
+def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
+ tag=None, metric=None, fib=None):
+ """
+ This API is to verify ospf routes by running
+ show ip ospf route command.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `dut`: device under test
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `next_hop` : next to be verified
+ * `tag` : tag to be verified
+ * `metric` : metric to be verified
+ * `fib` : True if the route is installed in FIB.
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": ip_net,
+ "no_of_ip": 1,
+ "routeType": "N"
+ }
+ ]
+ }
+ }
+
+ result = verify_ospf6_rib(tgen, dut, input_dict,next_hop=nh)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+ router_list = tgen.routers()
+ additional_nexthops_in_required_nhs = []
+ found_hops = []
+ for routerInput in input_dict.keys():
+ for router, rnode in router_list.iteritems():
+ if router != dut:
+ continue
+
+ logger.info("Checking router %s RIB:", router)
+
+ # Verifying RIB routes
+ command = "show ipv6 ospf route"
+
+ found_routes = []
+ missing_routes = []
+
+ if "static_routes" in input_dict[routerInput] or \
+ "prefix" in input_dict[routerInput]:
+ if "prefix" in input_dict[routerInput]:
+ static_routes = input_dict[routerInput]["prefix"]
+ else:
+ static_routes = input_dict[routerInput]["static_routes"]
+
+
+ for static_route in static_routes:
+ cmd = "{}".format(command)
+
+ cmd = "{} json".format(cmd)
+
+ ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Fix for PR 2644182
+ try:
+ ospf_rib_json = ospf_rib_json['routes']
+ except KeyError:
+ pass
+
+ # Verifying output dictionary ospf_rib_json is not empty
+ if bool(ospf_rib_json) is False:
+ errormsg = "[DUT: {}] No routes found in OSPF6 route " \
+ "table".format(router)
+ return errormsg
+
+ network = static_route["network"]
+ no_of_ip = static_route.setdefault("no_of_ip", 1)
+ _tag = static_route.setdefault("tag", None)
+ _rtype = static_route.setdefault("routeType", None)
+
+
+ # Generating IPs for verification
+ ip_list = generate_ips(network, no_of_ip)
+ st_found = False
+ nh_found = False
+ for st_rt in ip_list:
+ st_rt = str(ipaddress.ip_network(frr_unicode(st_rt)))
+
+ _addr_type = validate_ip_address(st_rt)
+ if _addr_type != 'ipv6':
+ continue
+
+ if st_rt in ospf_rib_json:
+
+ st_found = True
+ found_routes.append(st_rt)
+
+ if fib and next_hop:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+
+ for mnh in range(0, len(ospf_rib_json[st_rt])):
+ if 'fib' in ospf_rib_json[st_rt][
+ mnh]["nextHops"][0]:
+ found_hops.append([rib_r[
+ "ip"] for rib_r in ospf_rib_json[
+ st_rt][mnh]["nextHops"]])
+
+ if found_hops[0]:
+ missing_list_of_nexthops = \
+ set(found_hops[0]).difference(next_hop)
+ additional_nexthops_in_required_nhs = \
+ set(next_hop).difference(found_hops[0])
+
+ if additional_nexthops_in_required_nhs:
+ logger.info(
+ "Nexthop "
+ "%s is not active for route %s in "
+ "RIB of router %s\n",
+ additional_nexthops_in_required_nhs,
+ st_rt, dut)
+ errormsg = (
+ "Nexthop {} is not active"
+ " for route {} in RIB of router"
+ " {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt, dut))
+ return errormsg
+ else:
+ nh_found = True
+
+ elif next_hop and fib is None:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+ found_hops = [rib_r['nextHop'] for rib_r in
+ ospf_rib_json[st_rt][
+ "nextHops"]]
+
+ if found_hops:
+ missing_list_of_nexthops = \
+ set(found_hops).difference(next_hop)
+ additional_nexthops_in_required_nhs = \
+ set(next_hop).difference(found_hops)
+ if additional_nexthops_in_required_nhs:
+ logger.info(
+ "Missing nexthop %s for route"\
+ " %s in RIB of router %s\n", \
+ additional_nexthops_in_required_nhs, \
+ st_rt, dut)
+ errormsg=("Nexthop {} is Missing for "\
+ "route {} in RIB of router {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt, dut))
+ return errormsg
+ else:
+ nh_found = True
+ if _rtype:
+ if "destinationType" not in ospf_rib_json[
+ st_rt]:
+ errormsg = ("[DUT: {}]: destinationType missing"
+ "for route {} in OSPF RIB \n".\
+ format(dut, st_rt))
+ return errormsg
+ elif _rtype != ospf_rib_json[st_rt][
+ "destinationType"]:
+ errormsg = ("[DUT: {}]: destinationType mismatch"
+ "for route {} in OSPF RIB \n".\
+ format(dut, st_rt))
+ return errormsg
+ else:
+ logger.info("DUT: {}]: Found destinationType {}"
+ "for route {}".\
+ format(dut, _rtype, st_rt))
+ if tag:
+ if "tag" not in ospf_rib_json[
+ st_rt]:
+ errormsg = ("[DUT: {}]: tag is not"
+ " present for"
+ " route {} in RIB \n".\
+ format(dut, st_rt
+ ))
+ return errormsg
+
+ if _tag != ospf_rib_json[
+ st_rt]["tag"]:
+ errormsg = ("[DUT: {}]: tag value {}"
+ " is not matched for"
+ " route {} in RIB \n".\
+ format(dut, _tag, st_rt,
+ ))
+ return errormsg
+
+ if metric is not None:
+ if "type2cost" not in ospf_rib_json[
+ st_rt]:
+ errormsg = ("[DUT: {}]: metric is"
+ " not present for"
+ " route {} in RIB \n".\
+ format(dut, st_rt))
+ return errormsg
+
+ if metric != ospf_rib_json[
+ st_rt]["type2cost"]:
+ errormsg = ("[DUT: {}]: metric value "
+ "{} is not matched for "
+ "route {} in RIB \n".\
+ format(dut, metric, st_rt,
+ ))
+ return errormsg
+
+ else:
+ missing_routes.append(st_rt)
+
+ if nh_found:
+ logger.info("[DUT: {}]: Found next_hop {} for all OSPF"
+ " routes in RIB".format(router, next_hop))
+
+ if len(missing_routes) > 0:
+ errormsg = ("[DUT: {}]: Missing route in RIB, "
+ "routes: {}".\
+ format(dut, missing_routes))
+ return errormsg
+
+ if found_routes:
+ logger.info("[DUT: %s]: Verified routes in RIB, found"
+ " routes are: %s\n", dut, found_routes)
+ result = True
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+@retry(attempts=3, wait=2, return_is_str=True)
+def verify_ospf6_interface(tgen, topo, dut=None,lan=False, input_dict=None):
+ """
+ This API is to verify ospf routes by running
+ show ip ospf interface command.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : topology descriptions
+ * `dut`: device under test
+ * `lan`: if set to true this interface belongs to LAN.
+ * `input_dict` : Input dict data, required when configuring from testcase
+
+ Usage
+ -----
+ input_dict= {
+ 'r0': {
+ 'links':{
+ 's1': {
+ 'ospf6':{
+ 'priority':98,
+ 'timerDeadSecs': 4,
+ 'area': '0.0.0.3',
+ 'mcastMemberOspfDesignatedRouters': True,
+ 'mcastMemberOspfAllRouters': True,
+ 'ospfEnabled': True,
+
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+
+ for router, rnode in tgen.routers().iteritems():
+ if 'ospf6' not in topo['routers'][router]:
+ continue
+
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying OSPF interface on router %s:", router)
+ show_ospf_json = run_frr_cmd(rnode, "show ipv6 ospf interface json",
+ isjson=True)
+
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF6 is not running"
+ return errormsg
+
+ # To find neighbor ip type
+ ospf_intf_data = input_dict[router]["links"]
+ for ospf_intf, intf_data in ospf_intf_data.items():
+ intf = topo['routers'][router]['links'][ospf_intf]['interface']
+ if intf in show_ospf_json:
+ for intf_attribute in intf_data['ospf6']:
+ if intf_data['ospf6'][intf_attribute] is not list:
+ if intf_data['ospf6'][intf_attribute] == show_ospf_json[
+ intf][intf_attribute]:
+ logger.info("[DUT: %s] OSPF6 interface %s: %s is %s",
+ router, intf, intf_attribute, intf_data['ospf6'][
+ intf_attribute])
+ elif intf_data['ospf6'][intf_attribute] is list:
+ for addr_list in len(show_ospf_json[intf][intf_attribute]):
+ if show_ospf_json[intf][intf_attribute][addr_list][
+ 'address'].split('/')[0] == intf_data['ospf6'][
+ 'internetAddress'][0]['address']:
+ break
+ else:
+ errormsg= "[DUT: {}] OSPF6 interface {}: {} is {}, \
+ Expected is {}".format(router, intf, intf_attribute,
+ intf_data['ospf6'][intf_attribute], intf_data['ospf6'][
+ intf_attribute])
+ return errormsg
+ else:
+ errormsg= "[DUT: {}] OSPF6 interface {}: {} is {}, \
+ Expected is {}".format(router, intf, intf_attribute,
+ intf_data['ospf6'][intf_attribute], intf_data['ospf6'][
+ intf_attribute])
+ return errormsg
+ result = True
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+@retry(attempts=11, wait=2, return_is_str=True)
+def verify_ospf6_database(tgen, topo, dut, input_dict):
+ """
+ This API is to verify ospf lsa's by running
+ show ip ospf database command.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `dut`: device under test
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `topo` : next to be verified
+
+ Usage
+ -----
+ input_dict = {
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": {
+ "100.1.1.0-100.1.1.0": {
+ "LSID": "100.1.1.0",
+ "Advertised router": "100.1.1.0",
+ "LSA Age": 130,
+ "Sequence Number": "80000006",
+ "Checksum": "a703",
+ "Router links": 3
+ }
+ },
+ "networkLinkStates": {
+ "10.0.0.2-100.1.1.1": {
+ "LSID": "10.0.0.2",
+ "Advertised router": "100.1.1.1",
+ "LSA Age": 137,
+ "Sequence Number": "80000001",
+ "Checksum": "9583"
+ }
+ },
+ },
+ }
+ }
+ result = verify_ospf_database(tgen, topo, dut, input_dict)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ result = False
+ router = dut
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if 'ospf' not in topo['routers'][dut]:
+ errormsg = "[DUT: {}] OSPF is not configured on the router.".format(
+ dut)
+ return errormsg
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("Verifying OSPF interface on router %s:", dut)
+ show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json",
+ isjson=True)
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF is not running"
+ return errormsg
+
+ # for inter and inter lsa's
+ ospf_db_data = input_dict.setdefault("areas", None)
+ ospf_external_lsa = input_dict.setdefault(
+ 'asExternalLinkStates', None)
+
+ if ospf_db_data:
+ for ospf_area, area_lsa in ospf_db_data.items():
+ if ospf_area in show_ospf_json['areas']:
+ if 'routerLinkStates' in area_lsa:
+ for lsa in area_lsa['routerLinkStates']:
+ for rtrlsa in show_ospf_json['areas'][ospf_area][
+ 'routerLinkStates']:
+ if lsa['lsaId'] == rtrlsa['lsaId'] and \
+ lsa['advertisedRouter'] == rtrlsa[
+ 'advertisedRouter']:
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Router "
+ "LSA %s", router, ospf_area, lsa)
+ break
+ else:
+ errormsg = \
+ "[DUT: {}] OSPF LSDB area {}: expected" \
+ " Router LSA is {}".format(router, ospf_area, lsa)
+ return errormsg
+
+ if 'networkLinkStates' in area_lsa:
+ for lsa in area_lsa['networkLinkStates']:
+ for netlsa in show_ospf_json['areas'][ospf_area][
+ 'networkLinkStates']:
+ if lsa in show_ospf_json['areas'][ospf_area][
+ 'networkLinkStates']:
+ if lsa['lsaId'] == netlsa['lsaId'] and \
+ lsa['advertisedRouter'] == netlsa[
+ 'advertisedRouter']:
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Network "
+ "LSA %s", router, ospf_area, lsa)
+ break
+ else:
+ errormsg = \
+ "[DUT: {}] OSPF LSDB area {}: expected" \
+ " Network LSA is {}".format(router, ospf_area, lsa)
+ return errormsg
+
+ if 'summaryLinkStates' in area_lsa:
+ for lsa in area_lsa['summaryLinkStates']:
+ for t3lsa in show_ospf_json['areas'][ospf_area][
+ 'summaryLinkStates']:
+ if lsa['lsaId'] == t3lsa['lsaId'] and \
+ lsa['advertisedRouter'] == t3lsa[
+ 'advertisedRouter']:
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Summary "
+ "LSA %s", router, ospf_area, lsa)
+ break
+ else:
+ errormsg = \
+ "[DUT: {}] OSPF LSDB area {}: expected" \
+ " Summary LSA is {}".format(router, ospf_area, lsa)
+ return errormsg
+
+ if 'nssaExternalLinkStates' in area_lsa:
+ for lsa in area_lsa['nssaExternalLinkStates']:
+ for t7lsa in show_ospf_json['areas'][ospf_area][
+ 'nssaExternalLinkStates']:
+ if lsa['lsaId'] == t7lsa['lsaId'] and \
+ lsa['advertisedRouter'] == t7lsa[
+ 'advertisedRouter']:
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Type7 "
+ "LSA %s", router, ospf_area, lsa)
+ break
+ else:
+ errormsg = \
+ "[DUT: {}] OSPF LSDB area {}: expected" \
+ " Type7 LSA is {}".format(router, ospf_area, lsa)
+ return errormsg
+
+ if 'asbrSummaryLinkStates' in area_lsa:
+ for lsa in area_lsa['asbrSummaryLinkStates']:
+ for t4lsa in show_ospf_json['areas'][ospf_area][
+ 'asbrSummaryLinkStates']:
+ if lsa['lsaId'] == t4lsa['lsaId'] and \
+ lsa['advertisedRouter'] == t4lsa[
+ 'advertisedRouter']:
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:ASBR Summary "
+ "LSA %s", router, ospf_area, lsa)
+ result = True
+ else:
+ errormsg = \
+ "[DUT: {}] OSPF LSDB area {}: expected" \
+ " ASBR Summary LSA is {}".format(
+ router, ospf_area, lsa)
+ return errormsg
+
+ if 'linkLocalOpaqueLsa' in area_lsa:
+ for lsa in area_lsa['linkLocalOpaqueLsa']:
+ try:
+ for lnklsa in show_ospf_json['areas'][ospf_area][
+ 'linkLocalOpaqueLsa']:
+ if lsa['lsaId'] in lnklsa['lsaId'] and \
+ 'linkLocalOpaqueLsa' in show_ospf_json[
+ 'areas'][ospf_area]:
+ logger.info((
+ "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA"
+ "%s", ospf_area, lsa))
+ result = True
+ else:
+ errormsg = ("[DUT: FRR] OSPF LSDB area: {} "
+ "expected Opaque-LSA is {}, Found is {}".format(
+ ospf_area, lsa, show_ospf_json))
+ raise ValueError (errormsg)
+ return errormsg
+ except KeyError:
+ errormsg = ("[DUT: FRR] linkLocalOpaqueLsa Not "
+ "present")
+ return errormsg
+
+ if ospf_external_lsa:
+ for lsa in ospf_external_lsa:
+ try:
+ for t5lsa in show_ospf_json['asExternalLinkStates']:
+ if lsa['lsaId'] == t5lsa['lsaId'] and \
+ lsa['advertisedRouter'] == t5lsa[
+ 'advertisedRouter']:
+ result = True
+ break
+ except KeyError:
+ result = False
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB:External LSA %s",
+ router, lsa)
+ result = True
+ else:
+ errormsg = \
+ "[DUT: {}] OSPF LSDB : expected" \
+ " External LSA is {}".format(router, lsa)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+
+def config_ospf6_interface (tgen, topo, input_dict=None, build=False,
+ load_config=True):
+ """
+ API to configure ospf on router.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `build` : Only for initial setup phase this is set as True.
+ * `load_config` : Loading the config to router this is set as True.
+
+ Usage
+ -----
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": 'message-digest',
+ "authentication-key": "ospf",
+ "message-digest-key": "10"
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf_auth)
+
+ Returns
+ -------
+ True or False
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+ if not input_dict:
+ input_dict = deepcopy(topo)
+ else:
+ input_dict = deepcopy(input_dict)
+ for router in input_dict.keys():
+ config_data = []
+ for lnk in input_dict[router]['links'].keys():
+ if "ospf6" not in input_dict[router]['links'][lnk]:
+ logger.debug("Router %s: ospf6 configs is not present in"
+ "input_dict, passed input_dict", router,
+ input_dict)
+ continue
+ ospf_data = input_dict[router]['links'][lnk]['ospf6']
+ data_ospf_area = ospf_data.setdefault("area", None)
+ data_ospf_auth = ospf_data.setdefault("authentication", None)
+ data_ospf_dr_priority = ospf_data.setdefault("priority", None)
+ data_ospf_cost = ospf_data.setdefault("cost", None)
+ data_ospf_mtu = ospf_data.setdefault("mtu_ignore", None)
+
+ try:
+ intf = topo['routers'][router]['links'][lnk]['interface']
+ except KeyError:
+ intf = topo['switches'][router]['links'][lnk]['interface']
+
+ # interface
+ cmd = "interface {}".format(intf)
+
+ config_data.append(cmd)
+ # interface area config
+ if data_ospf_area:
+ cmd = "ipv6 ospf area {}".format(data_ospf_area)
+ config_data.append(cmd)
+
+ # interface ospf dr priority
+ if data_ospf_dr_priority:
+ cmd = "ipv6 ospf priority {}".format(
+ ospf_data["priority"])
+ if 'del_action' in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # interface ospf cost
+ if data_ospf_cost:
+ cmd = "ipv6 ospf cost {}".format(
+ ospf_data["cost"])
+ if 'del_action' in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # interface ospf mtu
+ if data_ospf_mtu:
+ cmd = "ipv6 ospf mtu-ignore"
+ if 'del_action' in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if build:
+ return config_data
+ else:
+ result = create_common_configuration(tgen, router, config_data,
+ "interface_config",
+ build=build)
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
# Verification APIs
#############################################
@retry(attempts=6, wait=2, return_is_str=True)
-def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None):
+def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None, expected=True):
"""
Verify all PIM neighbors are up and running, config is verified
using "show ip pim neighbor" cli
* `dut` : dut info
* `iface` : link for which PIM nbr need to check
* `nbr_ip` : neighbor ip of interface
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=21, wait=2, return_is_str=True)
-def verify_igmp_groups(tgen, dut, interface, group_addresses):
+def verify_igmp_groups(tgen, dut, interface, group_addresses, expected=True):
"""
Verify IGMP groups are received from an intended interface
by running "show ip igmp groups" command
* `dut`: device under test
* `interface`: interface, from which IGMP groups would be received
* `group_addresses`: IGMP group address
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=31, wait=2, return_is_str=True)
def verify_upstream_iif(
- tgen, dut, iif, src_address, group_addresses, joinState=None, refCount=1
+ tgen, dut, iif, src_address, group_addresses, joinState=None, refCount=1, expected=True
):
"""
Verify upstream inbound interface is updated correctly
* `group_addresses`: IGMP group address
* `joinState`: upstream join state
* `refCount`: refCount value
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=6, wait=2, return_is_str=True)
-def verify_join_state_and_timer(tgen, dut, iif, src_address, group_addresses):
+def verify_join_state_and_timer(tgen, dut, iif, src_address, group_addresses, expected=True):
"""
Verify join state is updated correctly and join timer is
running with the help of "show ip pim upstream" cli
* `iif`: inbound interface
* `src_address`: source address
* `group_addresses`: IGMP group address
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=41, wait=2, return_is_dict=True)
def verify_ip_mroutes(
- tgen, dut, src_address, group_addresses, iif, oil, return_uptime=False, mwait=0
+ tgen, dut, src_address, group_addresses, iif, oil, return_uptime=False, mwait=0, expected=True
):
"""
Verify ip mroutes and make sure (*, G)/(S, G) is present in mroutes
* `oil`: Outgoing interface
* `return_uptime`: If True, return uptime dict, default is False
* `mwait`: Wait time, default is 0
-
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=31, wait=2, return_is_str=True)
def verify_pim_rp_info(
- tgen, topo, dut, group_addresses, oif=None, rp=None, source=None, iamrp=None
+ tgen, topo, dut, group_addresses, oif=None, rp=None, source=None, iamrp=None, expected=True
):
"""
Verify pim rp info by running "show ip pim rp-info" cli
* `rp`: RP address
* `source`: Source of RP
* `iamrp`: User defined RP
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=31, wait=2, return_is_str=True)
def verify_pim_state(
- tgen, dut, iif, oil, group_addresses, src_address=None, installed_fl=None
+ tgen, dut, iif, oil, group_addresses, src_address=None, installed_fl=None, expected=True
):
"""
Verify pim state by running "show ip pim state" cli
* `group_addresses`: IGMP group address
* `src_address`: source address, default = None
* installed_fl` : Installed flag
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=21, wait=2, return_is_str=True)
-def verify_pim_interface(tgen, topo, dut, interface=None, interface_ip=None):
+def verify_pim_interface(tgen, topo, dut, interface=None, interface_ip=None, expected=True):
"""
Verify all PIM interface are up and running, config is verified
using "show ip pim interface" cli
* `dut` : device under test
* `interface` : interface name
* `interface_ip` : interface ip address
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=10, wait=2, return_is_str=True)
-def clear_ip_mroute_verify(tgen, dut):
+def clear_ip_mroute_verify(tgen, dut, expected=True):
"""
Clear ip mroute by running "clear ip mroute" cli and verify
mroutes are up again after mroute clear
----------
* `tgen`: topogen object
* `dut`: Device Under Test
+ * `expected` : expected results from API, by-default True
+
Usage
-----
@retry(attempts=6, wait=2, return_is_str=True)
-def verify_pim_grp_rp_source(tgen, topo, dut, grp_addr, rp_source, rpadd=None):
+def verify_pim_grp_rp_source(tgen, topo, dut, grp_addr, rp_source, rpadd=None, expected=True):
"""
Verify pim rp info by running "show ip pim rp-info" cli
* `grp_addr`: IGMP group address
* 'rp_source': source from which rp installed
* 'rpadd': rp address
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=31, wait=2, return_is_str=True)
-def verify_pim_bsr(tgen, topo, dut, bsr_ip):
+def verify_pim_bsr(tgen, topo, dut, bsr_ip, expected=True):
"""
Verify all PIM interface are up and running, config is verified
using "show ip pim interface" cli
* `topo` : json file data
* `dut` : device under test
* 'bsr' : bsr ip to be verified
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=31, wait=2, return_is_str=True)
-def verify_ip_pim_upstream_rpf(tgen, topo, dut, interface, group_addresses, rp=None):
+def verify_ip_pim_upstream_rpf(tgen, topo, dut, interface, group_addresses, rp=None, expected=True):
"""
Verify IP PIM upstream rpf, config is verified
using "show ip pim neighbor" cli
* `group_addresses` : list of group address for which upstream info
needs to be checked
* `rp` : RP address
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=31, wait=2, return_is_str=True)
-def verify_ip_pim_join(tgen, topo, dut, interface, group_addresses, src_address=None):
+def verify_ip_pim_join(tgen, topo, dut, interface, group_addresses, src_address=None, expected=True):
"""
Verify ip pim join by running "show ip pim join" cli
* `interface`: interface name, from which PIM join would come
* `group_addresses`: IGMP group address
* `src_address`: Source address
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=31, wait=2, return_is_dict=True)
-def verify_igmp_config(tgen, input_dict, stats_return=False):
+def verify_igmp_config(tgen, input_dict, stats_return=False, expected=True):
"""
Verify igmp interface details, verifying following configs:
timerQueryInterval
* `input_dict` : Input dict data, required to verify
timer
* `stats_return`: If user wants API to return statistics
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=31, wait=2, return_is_str=True)
-def verify_pim_config(tgen, input_dict):
+def verify_pim_config(tgen, input_dict, expected=True):
"""
Verify pim interface details, verifying following configs:
drPriority
* `tgen`: topogen object
* `input_dict` : Input dict data, required to verify
timer
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=21, wait=2, return_is_dict=True)
-def verify_multicast_traffic(tgen, input_dict, return_traffic=False):
+def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=True):
"""
Verify multicast traffic by running
"show multicast traffic count json" cli
* `input_dict(dict)`: defines DUT, what and for which interfaces
traffic needs to be verified
* `return_traffic`: returns traffic stats
+ * `expected` : expected results from API, by-default True
+
Usage
-----
input_dict = {
@retry(attempts=21, wait=2, return_is_str=True)
-def verify_multicast_flag_state(tgen, dut, src_address, group_addresses, flag):
+def verify_multicast_flag_state(tgen, dut, src_address, group_addresses, flag, expected=True):
"""
Verify flag state for mroutes and make sure (*, G)/(S, G) are having
coorect flags by running "show ip mroute" cli
* `src_address`: source address
* `group_addresses`: IGMP group address
* `flag`: flag state, needs to be verified
+ * `expected` : expected results from API, by-default True
Usage
-----
@retry(attempts=21, wait=2, return_is_str=True)
-def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip):
+def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip, expected=True):
"""
Verify all IGMP interface are up and running, config is verified
using "show ip igmp interface" cli
* `dut` : device under test
* `igmp_iface` : interface name
* `interface_ip` : interface ip address
+ * `expected` : expected results from API, by-default True
Usage
-----
self.peern += 1
return self.gears[name]
+ def add_host(self, name, ip, defaultRoute):
+ """
+ Adds a new host to the topology. This function has the following
+ parameters:
+ * `ip`: the peer address (e.g. '1.2.3.4/24')
+ * `defaultRoute`: the peer default route (e.g. 'via 1.2.3.1')
+ """
+ if name is None:
+ name = "host{}".format(self.peern)
+ if name in self.gears:
+ raise KeyError("host already exists")
+
+ self.gears[name] = TopoHost(self, name, ip=ip, defaultRoute=defaultRoute)
+ self.peern += 1
+ return self.gears[name]
+
def add_link(self, node1, node2, ifname1=None, ifname2=None):
"""
Creates a connection between node1 and node2. The nodes can be the
# Try to find relevant old logfiles in /tmp and delete them
map(os.remove, glob.glob("{}/{}/*.log".format(self.logdir, self.name)))
+ # Remove old valgrind files
+ map(os.remove, glob.glob("{}/{}.valgrind.*".format(self.logdir, self.name)))
# Remove old core files
map(os.remove, glob.glob("{}/{}/*.dmp".format(self.logdir, self.name)))
gdb_breakpoints = g_extra_config["gdb_breakpoints"]
gdb_daemons = g_extra_config["gdb_daemons"]
gdb_routers = g_extra_config["gdb_routers"]
+ valgrind_extra = g_extra_config["valgrind_extra"]
+ valgrind_memleaks = g_extra_config["valgrind_memleaks"]
bundle_data = ""
) + "/var/run/{}/snmpd.pid -x /etc/frr/agentx".format(self.routertype)
else:
binary = os.path.join(self.daemondir, daemon)
+
cmdenv = "ASAN_OPTIONS=log_path={0}.asan".format(daemon)
+ if valgrind_memleaks:
+ this_dir = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
+ supp_file = os.path.abspath(os.path.join(this_dir, "../../../tools/valgrind.supp"))
+ cmdenv += " /usr/bin/valgrind --num-callers=50 --log-file={1}/{2}.valgrind.{0}.%p --leak-check=full --suppressions={3}".format(daemon, self.logdir, self.name, supp_file)
+ if valgrind_extra:
+ cmdenv += "--gen-suppressions=all --expensive-definedness-checks=yes"
cmdopt = "{} --log file:{}.log --log-level debug".format(
daemon_opts, daemon
)
--- /dev/null
+router bgp 65000
+ neighbor 10.254.254.2 remote-as 65000
+ neighbor 10.254.254.2 update-source 10.254.254.1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
--- /dev/null
+interface r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ redistribute connected
+!
--- /dev/null
+interface lo
+ ip pim
+ ip pim use-source 10.254.254.1
+!
+interface r1-eth0
+ ip pim
+!
+interface r1-eth1
+ ip pim
+ ip igmp
+!
+ip pim rp 10.254.254.1
+ip msdp mesh-group mg-1 source 10.254.254.1
+ip msdp mesh-group mg-1 member 10.254.254.2
+ip msdp mesh-group mg-1 member 10.254.254.3
--- /dev/null
+ip forwarding
+!
+interface lo
+ ip address 10.254.254.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.2/24
+!
+interface r1-eth1
+ ip address 192.168.10.1/24
+!
--- /dev/null
+router bgp 65000
+ neighbor pg-1 peer-group
+ neighbor pg-1 update-source 10.254.254.1
+ neighbor pg-1 remote-as 65000
+ neighbor 10.254.254.1 peer-group pg-1
+ neighbor 10.254.254.3 peer-group pg-1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
--- /dev/null
+interface r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 192.168.2.0/24 area 0.0.0.0
+ redistribute connected
+!
--- /dev/null
+interface lo
+ ip pim
+ ip pim use-source 10.254.254.2
+!
+interface r2-eth0
+ ip pim
+!
+interface r2-eth1
+ ip pim
+!
+ip pim rp 10.254.254.2
+ip msdp mesh-group mg-1 source 10.254.254.2
+ip msdp mesh-group mg-1 member 10.254.254.1
+ip msdp mesh-group mg-1 member 10.254.254.3
--- /dev/null
+ip forwarding
+!
+interface lo
+ ip address 10.254.254.2/32
+!
+interface r2-eth0
+ ip address 192.168.1.1/24
+!
+interface r2-eth1
+ ip address 192.168.2.1/24
+!
--- /dev/null
+router bgp 65000
+ neighbor 192.168.2.1 remote-as 65000
+ neighbor 192.168.2.1 update-source 10.254.254.3
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
--- /dev/null
+interface r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ network 192.168.2.0/24 area 0.0.0.0
+ redistribute connected
+!
--- /dev/null
+interface lo
+ ip pim
+ ip pim use-source 10.254.254.3
+!
+interface r3-eth0
+ ip pim
+!
+interface r3-eth1
+ ip pim
+ ip igmp
+!
+ip pim rp 10.254.254.3
+ip msdp mesh-group mg-1 source 10.254.254.3
+ip msdp mesh-group mg-1 member 10.254.254.1
+ip msdp mesh-group mg-1 member 10.254.254.2
--- /dev/null
+ip forwarding
+!
+interface lo
+ ip address 10.254.254.3/32
+!
+interface r3-eth0
+ ip address 192.168.2.2/24
+!
+interface r3-eth1
+ ip address 192.168.30.1/24
+!
--- /dev/null
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## Switches: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #fcb314 dark yellow
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+##### Colors (see http://www.color-hex.com/)
+
+graph template {
+ label="msdp_mesh_topo1";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ h1 [
+ shape=doubleoctagon
+ label="h1",
+ fillcolor="#4f4f4f",
+ style=filled,
+ ];
+ h2 [
+ shape=doubleoctagon
+ label="h2",
+ fillcolor="#4f4f4f",
+ style=filled,
+ ];
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="sw1\n192.168.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="sw2\n192.168.2.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ shape=oval,
+ label="sw3\n192.168.10.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ shape=oval,
+ label="sw3\n192.168.30.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- s1 [label="eth0\n.2"];
+ r2 -- s1 [label="eth0\n.1"];
+
+ r2 -- s2 [label="eth1\n.1"];
+ r3 -- s2 [label="eth0\n.2"];
+
+ r1 -- s3 [label="eth1\n.1"];
+ h1 -- s3 [label="eth0\n.2"];
+
+ r3 -- s4 [label="eth1\n.1"];
+ h2 -- s4 [label="eth0\n.2"];
+}
--- /dev/null
+#!/usr/bin/env python
+
+#
+# test_msdp_mesh_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (C) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# 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_msdp_mesh_topo1.py: Test the FRR PIM MSDP mesh groups.
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import pytest
+import socket
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.pimd]
+
+#
+# Test global variables:
+# They are used to handle communicating with external application.
+#
+APP_SOCK_PATH = '/tmp/topotests/apps.sock'
+HELPER_APP_PATH = os.path.join(CWD, "../lib/mcast-tester.py")
+app_listener = None
+app_clients = {}
+
+def listen_to_applications():
+ "Start listening socket to connect with applications."
+ # Remove old socket.
+ try:
+ os.unlink(APP_SOCK_PATH)
+ except OSError:
+ pass
+
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ sock.bind(APP_SOCK_PATH)
+ sock.listen(10)
+ global app_listener
+ app_listener = sock
+
+def accept_host(host):
+ "Accept connection from application running in hosts."
+ global app_listener, app_clients
+ conn = app_listener.accept()
+ app_clients[host] = {
+ 'fd': conn[0],
+ 'address': conn[1]
+ }
+
+def close_applications():
+ "Signal applications to stop and close all sockets."
+ global app_listener, app_clients
+
+ # Close listening socket.
+ app_listener.close()
+
+ # Remove old socket.
+ try:
+ os.unlink(APP_SOCK_PATH)
+ except OSError:
+ pass
+
+ # Close all host connections.
+ for host in ["h1", "h2"]:
+ if app_clients.get(host) is None:
+ continue
+ app_clients["h1"]["fd"].close()
+
+
+class MSDPMeshTopo1(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # Create 3 routers
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+ # Create stub networks for multicast traffic.
+ tgen.add_host("h1", "192.168.10.2/24", "192.168.10.1")
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["h1"])
+
+ tgen.add_host("h2", "192.168.30.2/24", "192.168.30.1")
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["h2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(MSDPMeshTopo1, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ daemon_file = "{}/{}/zebra.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_ZEBRA, daemon_file)
+
+ daemon_file = "{}/{}/bgpd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_BGP, daemon_file)
+
+ daemon_file = "{}/{}/ospfd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_OSPF, daemon_file)
+
+ daemon_file = "{}/{}/pimd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_PIM, daemon_file)
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ # Start applications socket.
+ listen_to_applications()
+
+
+def test_wait_ospf_convergence():
+ "Wait for OSPF to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_loopback_route(router, iptype, route, proto):
+ "Wait until route is present on RIB for protocol."
+ logger.info("waiting route {} in {}".format(route, router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show {} route json".format(iptype),
+ {route: [{"protocol": proto}]}
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=1)
+ assertmsg = '"{}" OSPF convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ # Wait for R1 <-> R2 convergence.
+ expect_loopback_route("r1", "ip", "10.254.254.2/32", "ospf")
+ # Wait for R1 <-> R3 convergence.
+ expect_loopback_route("r1", "ip", "10.254.254.3/32", "ospf")
+
+ # Wait for R2 <-> R1 convergence.
+ expect_loopback_route("r2", "ip", "10.254.254.1/32", "ospf")
+ # Wait for R2 <-> R3 convergence.
+ expect_loopback_route("r2", "ip", "10.254.254.3/32", "ospf")
+
+ # Wait for R3 <-> R1 convergence.
+ expect_loopback_route("r3", "ip", "10.254.254.1/32", "ospf")
+ # Wait for R3 <-> R2 convergence.
+ expect_loopback_route("r3", "ip", "10.254.254.2/32", "ospf")
+
+
+def test_wait_msdp_convergence():
+ "Wait for MSDP to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("test MSDP convergence")
+
+ tgen.gears["h1"].run("{} --send='0.7' '{}' '{}' '{}' &".format(
+ HELPER_APP_PATH, APP_SOCK_PATH, '229.0.1.10', 'h1-eth0'))
+ accept_host("h1")
+
+ tgen.gears["h2"].run("{} '{}' '{}' '{}' &".format(
+ HELPER_APP_PATH, APP_SOCK_PATH, '229.0.1.10', 'h2-eth0'))
+ accept_host("h2")
+
+ def expect_msdp_peer(router, peer, sa_count=0):
+ "Expect MSDP peer connection to be established with SA amount."
+ logger.info("waiting MSDP connection from peer {} on router {}".format(peer, router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ip msdp peer json",
+ {peer: {"state": "established", "saCount": sa_count}}
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{}" MSDP connection failure'.format(router)
+ assert result is None, assertmsg
+
+ # R1 peers.
+ expect_msdp_peer("r1", "10.254.254.2")
+ expect_msdp_peer("r1", "10.254.254.3")
+
+ # R2 peers.
+ expect_msdp_peer("r2", "10.254.254.1", 1)
+ expect_msdp_peer("r2", "10.254.254.3")
+
+ # R3 peers.
+ expect_msdp_peer("r3", "10.254.254.1", 1)
+ expect_msdp_peer("r3", "10.254.254.2")
+
+
+def test_msdp_sa_configuration():
+ "Expect the multicast traffic SA to be created"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("test MSDP SA")
+
+ def expect_msdp_sa(router, source, group, local, rp, spt_setup):
+ "Expect MSDP SA."
+ logger.info("waiting MSDP SA on router {}".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ip msdp sa json",
+ {group: {source: {"local": local, "rp": rp, "sptSetup": spt_setup}}}
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{}" MSDP SA failure'.format(router)
+ assert result is None, assertmsg
+
+ source = "192.168.10.2"
+ group = "229.0.1.10"
+ rp = "10.254.254.1"
+
+ # R1 SA.
+ expect_msdp_sa("r1", source, group, "yes", "-", "-")
+
+ # R2 SA.
+ expect_msdp_sa("r2", source, group, "no", rp, "no")
+
+ # R3 peers.
+ expect_msdp_sa("r3", source, group, "no", rp, "yes")
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ close_applications()
+ tgen.stop_topology()
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, weight 1, XX:XX:XX
O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
-O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
-O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, weight 1, XX:XX:XX
O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
-O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
-O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, weight 1, XX:XX:XX
O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, weight 1, XX:XX:XX
-O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
-O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
-O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
+O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, weight 1, XX:XX:XX
O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
-O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
)
+def test_ospfv3_routingTable_write_multiplier():
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # For debugging, uncomment the next line
+ # tgen.mininet_cli()
+
+ # Modify R1 write muliplier and reset the interfaces
+ r1 = tgen.gears["r1"]
+
+ r1.vtysh_cmd("conf t\nrouter ospf6\n write-multiplier 100")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5")
+
+ # Verify OSPFv3 Routing Table
+ for router, rnode in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', router)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ipv6, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
+ assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
+
+
def test_shutdown_check_stderr():
tgen = get_topogen()
hostname r1
!
interface r1-stubnet vrf r1-cust1
+ ipv6 ospf6 area 0.0.0.0
ipv6 address fc00:1:1:1::1/64
ipv6 ospf6 network broadcast
!
interface r1-sw5 vrf r1-cust1
+ ipv6 ospf6 area 0.0.0.0
ipv6 address fc00:a:a:a::1/64
ipv6 ospf6 network broadcast
!
router-id 10.0.0.1
log-adjacency-changes detail
redistribute static
- interface r1-stubnet area 0.0.0.0
- interface r1-sw5 area 0.0.0.0
!
ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234 vrf r1-cust1
hostname r3
!
interface r3-stubnet vrf r3-cust1
+ ipv6 ospf6 area 0.0.0.0
ipv6 address fc00:3:3:3::3/64
ipv6 ospf6 network broadcast
!
interface r3-sw5 vrf r3-cust1
+ ipv6 ospf6 area 0.0.0.0
ipv6 address fc00:a:a:a::3/64
ipv6 ospf6 network broadcast
!
interface r3-sw6 vrf r3-cust1
+ ipv6 ospf6 area 0.0.0.1
ipv6 address fc00:b:b:b::3/64
ipv6 ospf6 network broadcast
!
router-id 10.0.0.3
log-adjacency-changes detail
redistribute static
- interface r3-stubnet area 0.0.0.0
- interface r3-sw5 area 0.0.0.0
- interface r3-sw6 area 0.0.0.1
!
ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234 vrf r3-cust1
debug ospf6 route table
debug ospf6 flooding
!
-interface r1-stubnet vrf r1-cust1
+interface r1-stubnet
+ ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 network broadcast
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
!
-interface r1-sw5 vrf r1-cust1
+interface r1-sw5
+ ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 network broadcast
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
ospf6 router-id 10.0.0.1
log-adjacency-changes detail
redistribute static
- interface r1-stubnet area 0.0.0.0
- interface r1-sw5 area 0.0.0.0
!
line vty
exec-timeout 0 0
O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, weight 1, XX:XX:XX
O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
-O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
-O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
debug ospf6 route table
debug ospf6 flooding
!
-interface r2-stubnet vrf r2-cust1
+interface r2-stubnet
+ ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 network broadcast
ipv6 ospf6 dead-interval 10
ipv6 ospf6 hello-interval 2
!
-interface r2-sw5 vrf r2-cust1
+interface r2-sw5
+ ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 network broadcast
ipv6 ospf6 dead-interval 10
ipv6 ospf6 hello-interval 2
ospf6 router-id 10.0.0.2
log-adjacency-changes detail
redistribute static
- interface r2-stubnet area 0.0.0.0
- interface r2-sw5 area 0.0.0.0
!
line vty
exec-timeout 0 0
O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, weight 1, XX:XX:XX
O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
-O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
-O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
debug ospf6 route table
debug ospf6 flooding
!
-interface r3-stubnet vrf r3-cust1
+interface r3-stubnet
+ ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 network broadcast
ipv6 ospf6 dead-interval 10
ipv6 ospf6 hello-interval 2
!
-interface r3-sw5 vrf r3-cust1
+interface r3-sw5
+ ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 network broadcast
ipv6 ospf6 dead-interval 10
ipv6 ospf6 hello-interval 2
!
-interface r3-sw6 vrf r3-cust1
+interface r3-sw6
+ ipv6 ospf6 area 0.0.0.1
ipv6 ospf6 network broadcast
ipv6 ospf6 dead-interval 10
ipv6 ospf6 hello-interval 2
ospf6 router-id 10.0.0.3
log-adjacency-changes detail
redistribute static
- interface r3-stubnet area 0.0.0.0
- interface r3-sw5 area 0.0.0.0
- interface r3-sw6 area 0.0.0.1
!
line vty
exec-timeout 0 0
O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, weight 1, XX:XX:XX
O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, weight 1, XX:XX:XX
-O>* fc00:1111:1111:1111::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
-O>* fc00:2222:2222:2222::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
-O>* fc00:4444:4444:4444::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
+O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
debug ospf6 route table
debug ospf6 flooding
!
-interface r4-stubnet vrf r4-cust1
+interface r4-stubnet
+ ipv6 ospf6 area 0.0.0.1
ipv6 ospf6 network broadcast
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
!
-interface r4-sw6 vrf r4-cust1
+interface r4-sw6
+ ipv6 ospf6 area 0.0.0.1
ipv6 ospf6 network broadcast
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
ospf6 router-id 10.0.0.4
log-adjacency-changes detail
redistribute static
- interface r4-stubnet area 0.0.0.1
- interface r4-sw6 area 0.0.0.1
!
line vty
exec-timeout 0 0
O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, weight 1, XX:XX:XX
O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
-O>* fc00:3333:3333:3333::/64 [110/10] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
# Part of NetDEF Topology Tests
#
# Copyright (c) 2021 by Niral Networks, Inc. ("Niral Networks")
-# Used Copyright (c) 2016 by Network Device Education Foundation,
+# Used Copyright (c) 2016 by Network Device Education Foundation,
# Inc. ("NetDEF") in this file.
#
# Permission to use, copy, modify, and/or distribute this software
"ip link set {0}-stubnet master {0}-cust1",
]
- cmds1 = [
- "ip link set {0}-sw5 master {0}-cust1",
- ]
+ cmds1 = ["ip link set {0}-sw5 master {0}-cust1"]
- cmds2 = [
- "ip link set {0}-sw6 master {0}-cust1",
- ]
+ cmds2 = ["ip link set {0}-sw6 master {0}-cust1"]
# For all registered routers, load the zebra configuration file
for rname, router in tgen.routers().items():
tgen = get_topogen()
tgen.stop_topology()
+
def test_wait_protocol_convergence():
"Wait for OSPFv3 to converge"
tgen = get_topogen()
# Use the vtysh output, with some masking to make comparison easy
vrf_name = "{0}-cust1".format(rname)
current = topotest.ip6_route_zebra(tgen.gears[rname], vrf_name)
-
+
# Use just the 'O'spf lines of the output
linearr = []
for line in current.splitlines():
for i in range(1, 5):
# Actual output from router
- actual = tgen.gears["r{}".format(i)].run("ip -6 route show vrf r{}-cust1".format(i)).rstrip()
+ actual = (
+ tgen.gears["r{}".format(i)]
+ .run("ip -6 route show vrf r{}-cust1".format(i))
+ .rstrip()
+ )
if "nhid" in actual:
refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i))
else:
"unreachable fe80::/64 "
):
continue
- if 'anycast' in line:
+ if "anycast" in line:
continue
- if 'multicast' in line:
+ if "multicast" in line:
continue
filtered_lines.append(line)
actual = "\n".join(filtered_lines).splitlines(1)
)
+def test_ospfv3_routingTable_write_multiplier():
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # For debugging, uncomment the next line
+ # tgen.mininet_cli()
+ # Modify R1 write muliplier and reset the interfaces
+ r1 = tgen.gears["r1"]
+
+ r1.vtysh_cmd("conf t\nrouter ospf6 vrf r1-cust1 \n write-multiplier 100")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5")
+
+ # Verify OSPFv3 Routing Table
+ for router, rnode in tgen.routers().iteritems():
+ logger.info('Waiting for router "%s" convergence', router)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/show_ipv6_vrf_route.ref".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ipv6_vrf, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
+ assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
+
+
def test_shutdown_check_stderr():
tgen = get_topogen()
ipv6 ospf6 hello-interval 2
ipv6 ospf6 dead-interval 10
!
+interface r2-eth2
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
router ospf6
ospf6 router-id 10.254.254.2
redistribute connected
redistribute static
default-information originate always metric 123
area 0.0.0.1 stub
+ area 0.0.0.2 nssa
interface r2-eth0 area 0.0.0.1
interface r2-eth1 area 0.0.0.0
+ interface r2-eth2 area 0.0.0.2
!
interface r2-eth1
ipv6 address 2001:db8:2::2/64
!
+interface r2-eth2
+ ipv6 address 2001:db8:3::1/64
+!
--- /dev/null
+interface r4-eth0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.4
+ area 0.0.0.2 nssa
+ interface r4-eth0 area 0.0.0.2
+!
--- /dev/null
+ipv6 forwarding
+!
+interface r4-eth0
+ ipv6 address 2001:db8:3::2/64
+!
fillcolor="#f08080",
style=filled,
];
+ r4 [
+ shape=doubleoctagon
+ label="r4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
# Switches
sw1 [
}
subgraph cluster1 {
+ label="area 0.0.0.2";
+ r4 -- sw3 [label="eth0\n.2"];
+ }
+
+ subgraph cluster2 {
label="area 0.0.0.0";
r2 -- sw1 [label="eth0\n.1"];
r2 -- sw2 [label="eth1\n.2"];
+ r2 -- sw3 [label="eth2\n.1"];
+
r3 -- sw2 [label="eth0\n.1"];
- r3 -- sw3 [label="eth1\n.2"];
}
}
pytestmark = [pytest.mark.ospf6d]
+def expect_lsas(router, area, lsas, wait=5, extra_params=""):
+ """
+ Run the OSPFv3 show LSA database command and expect the supplied LSAs.
+
+ Optional parameters:
+ * `wait`: amount of seconds to wait.
+ * `extra_params`: extra LSA database parameters.
+ * `inverse`: assert the inverse of the expected.
+ """
+ tgen = get_topogen()
+
+ command = "show ipv6 ospf6 database {} json".format(extra_params)
+
+ logger.info("waiting OSPFv3 router '{}' LSA".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ command,
+ {"areaScopedLinkStateDb": [{"areaId": area, "lsa": lsas}]},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=wait, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+
+ assert result is None, assertmsg
+
+
+def expect_ospfv3_routes(router, routes, wait=5):
+ "Run command `ipv6 ospf6 route` and expect route with type."
+ tgen = get_topogen()
+
+ logger.info("waiting OSPFv3 router '{}' route".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 ospf6 route json",
+ {"routes": routes}
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=wait, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+
+ assert result is None, assertmsg
+
+
class OSPFv3Topo2(Topo):
"Test topology builder"
"Build function"
tgen = get_topogen(self)
- # Create 3 routers
- for routern in range(1, 4):
+ # Create 4 routers
+ for routern in range(1, 5):
tgen.add_router("r{}".format(routern))
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r4"])
+
def setup_module(mod):
"Sets up the pytest environment"
expect_neighbor_full("r1", "10.254.254.2")
expect_neighbor_full("r2", "10.254.254.1")
expect_neighbor_full("r2", "10.254.254.3")
+ expect_neighbor_full("r2", "10.254.254.4")
expect_neighbor_full("r3", "10.254.254.2")
+ expect_neighbor_full("r4", "10.254.254.2")
+
+
+def test_ospfv3_expected_route_types():
+ "Test routers route type to determine if NSSA/Stub is working as expected."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_ospf6_route_types(router, expected_summary):
+ "Expect the correct route types."
+ logger.info("waiting OSPFv3 router '{}'".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 ospf6 route summary json",
+ expected_summary,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ # Stub router: no external routes.
+ expect_ospf6_route_types(
+ "r1",
+ {
+ "numberOfIntraAreaRoutes": 1,
+ "numberOfInterAreaRoutes": 3,
+ "numberOfExternal1Routes": 0,
+ "numberOfExternal2Routes": 0,
+ },
+ )
+ # NSSA router: no external routes.
+ expect_ospf6_route_types(
+ "r4",
+ {
+ "numberOfIntraAreaRoutes": 1,
+ "numberOfInterAreaRoutes": 2,
+ "numberOfExternal1Routes": 0,
+ "numberOfExternal2Routes": 0,
+ },
+ )
def test_ospf6_default_route():
assertmsg = '"{}" convergence failure'.format(router)
assert result is None, assertmsg
- def expect_lsa(router, area, prefix, metric):
- "Test OSPF6 LSA existence."
- logger.info("waiting OSPFv3 router '{}' LSA".format(router))
- test_func = partial(
- topotest.router_json_cmp,
- tgen.gears[router],
- "show ipv6 ospf6 database inter-prefix detail json",
- {
- "areaScopedLinkStateDb": [
- {
- "areaId": area,
- "lsa": [
- {
- "prefix": prefix,
- "metric": metric,
- }
- ],
- }
- ]
- },
- )
- _, result = topotest.run_and_expect(test_func, None, count=4, wait=1)
- assertmsg = '"{}" convergence failure'.format(router)
- assert result is None, assertmsg
-
metric = 123
- expect_lsa("r1", "0.0.0.1", "::/0", metric)
+ expect_lsas(
+ "r1",
+ "0.0.0.1",
+ [{"prefix": "::/0", "metric": metric}],
+ extra_params="inter-prefix detail",
+ )
expect_route("r1", "::/0", metric + 10)
+def test_nssa_lsa_type7():
+ """
+ Test that static route gets announced as external route when redistributed
+ and gets removed when redistribution stops.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Add new static route and check if it gets announced as LSA Type-7.
+ #
+ config = """
+ configure terminal
+ ipv6 route 2001:db8:100::/64 Null0
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ lsas = [
+ {
+ "type": "NSSA",
+ "advertisingRouter": "10.254.254.2",
+ "prefix": "2001:db8:100::/64",
+ "forwardingAddress": "2001:db8:3::1",
+ }
+ ]
+ route = {
+ "2001:db8:100::/64": {
+ "pathType": "E1",
+ "nextHops": [
+ {"nextHop": "::", "interfaceName": "r4-eth0"}
+ ]
+ }
+ }
+
+ logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to show up")
+ expect_lsas("r4", "0.0.0.2", lsas, wait=30, extra_params="type-7 detail")
+ expect_ospfv3_routes("r4", route, wait=30)
+
+ #
+ # Remove static route and check for LSA Type-7 removal.
+ #
+ config = """
+ configure terminal
+ no ipv6 route 2001:db8:100::/64 Null0
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ def dont_expect_lsa(unexpected_lsa):
+ "Specialized test function to expect LSA go missing"
+ output = tgen.gears["r4"].vtysh_cmd("show ipv6 ospf6 database type-7 detail json", isjson=True)
+ for lsa in output['areaScopedLinkStateDb'][0]['lsa']:
+ if lsa["prefix"] == unexpected_lsa["prefix"]:
+ if lsa["forwardingAddress"] == unexpected_lsa["forwardingAddress"]:
+ return lsa
+ return None
+
+ def dont_expect_route(unexpected_route):
+ "Specialized test function to expect route go missing"
+ output = tgen.gears["r4"].vtysh_cmd("show ipv6 ospf6 route json", isjson=True)
+ if output["routes"].has_key(unexpected_route):
+ return output["routes"][unexpected_route]
+ return None
+
+
+ logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to go away")
+
+ # Test that LSA doesn't exist.
+ test_func = partial(dont_expect_lsa, lsas[0])
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" LSA still exists'.format("r4")
+ assert result is None, assertmsg
+
+ # Test that route doesn't exist.
+ test_func = partial(dont_expect_route, "2001:db8:100::/64")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" route still exists'.format("r4")
+ assert result is None, assertmsg
+
+
def teardown_module(_mod):
"Teardown the pytest environment"
tgen = get_topogen()
--- /dev/null
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r0-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR0"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+}
--- /dev/null
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""OSPF Summarisation Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+from time import sleep
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ kill_router_daemons,
+ write_test_footer,
+ reset_config_on_routers,
+ stop_router,
+ start_router,
+ verify_rib,
+ create_static_routes,
+ step,
+ start_router_daemons,
+ create_route_maps,
+ shutdown_bringup_interface,
+ topo_daemons,
+ create_prefix_lists,
+ create_route_maps,
+ create_interfaces_cfg,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_rib,
+ create_router_ospf,
+ verify_ospf_summary,
+)
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospf_asbr_summary_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+NETWORK_11 = {"ipv4": ["11.0.20.6/32", "11.0.20.7/32"]}
+
+NETWORK2 = {
+ "ipv4": [
+ "12.0.20.1/32",
+ "12.0.20.2/32",
+ "12.0.20.3/32",
+ "12.0.20.4/32",
+ "12.0.20.5/32",
+ ]
+}
+SUMMARY = {"ipv4": ["11.0.0.0/8", "12.0.0.0/8", "11.0.0.0/24"]}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A0 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A0
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A0 +---+
+
+TESTCASES =
+1. OSPF summarisation functionality.
+2. OSPF summarisation with metric type 2.
+3. OSPF summarisation with Tag option
+4. OSPF summarisation with advertise and no advertise option
+5. OSPF summarisation Chaos.
+6. OSPF summarisation with route map filtering.
+7. OSPF summarisation with route map modification of metric type.
+8. OSPF CLI Show verify ospf ASBR summary config and show commands behaviours.
+"""
+
+
+class CreateTopo(Topo):
+ """
+ Test topology builder.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def red_static(dut, config=True):
+ """
+ Local 'def' for Redstribute static routes inside ospf.
+
+ Parameters
+ ----------
+ * `dut` : DUT on which configs have to be made.
+ * `config` : True or False, True by default for configure, set False for
+ unconfiguration.
+ """
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """
+ Local 'def' for Redstribute connected routes inside ospf
+
+ Parameters
+ ----------
+ * `dut` : DUT on which configs have to be made.
+ * `config` : True or False, True by default for configure, set False for
+ unconfiguration.
+ """
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf": {"redistribute": [{"redist_type": "connected", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_type5_summary_tc43_p0(request):
+ """OSPF summarisation with metric type 2."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change the summary address mask to lower match (ex - 16 to 8)")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "16"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "11.0.0.0/16": {
+ "Summary address": "11.0.0.0/16",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Change the summary address mask to higher match (ex - 8 to 24)")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "24"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "11.0.0.0/16": {
+ "Summary address": "11.0.0.0/24",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+ step("Configure 2 summary address with different mask of same network.")
+ step(
+ "Verify that external routes(static / connected) are summarised "
+ "to configured summary address with highest match."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(" Un configure one of the summary address.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "24"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised "
+ "to configured summary address with highest match."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc48_p0(request):
+ """OSPF summarisation with route map modification of metric type."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Configure route map and & rule to permit configured summary address,"
+ " redistribute static & connected routes with the route map."
+ )
+ step("Configure prefixlist to permit the static routes, add to route map.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure metric type as 1 in route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [{"action": "permit", "set": {"metric-type": "type-1"}}]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with metric type 2."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Un configure metric type from route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "set": {"metric-type": "type-1"},
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with metric type 2."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change rule from permit to deny in prefix list.")
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "deny"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
*N IA 2001:db8:1::/64 :: r1-eth0 00:02:11
*N IA 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06
- N E1 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06
+ N E2 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06
*N IA 2001:db8:3::/64 :: r1-eth1 00:02:11
- N E1 2001:db8:3::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
- N E1 2001:db8:3::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06
+ N E2 2001:db8:3::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
+ N E2 2001:db8:3::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06
*N IA 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
- N E1 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
+ N E2 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
*N IE 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
- N E1 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
- N E1 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04
+ N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
+ N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04
*N IE 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04
- N E1 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04
+ N E2 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04
*N IA 2001:db8:1::/64 :: r1-eth0 00:01:51
*N IA 2001:db8:2::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52
- N E1 2001:db8:2::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52
+ N E2 2001:db8:2::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52
*N IA 2001:db8:3::/64 :: r1-eth1 00:00:52
- N E1 2001:db8:3::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52
+ N E2 2001:db8:3::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52
*N IA 2001:db8:1::/64 :: r1-eth0 00:06:13
*N IA 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r1-eth1 00:06:08
- N E1 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r1-eth1 00:06:08
+ N E2 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r1-eth1 00:06:08
*N IA 2001:db8:3::/64 :: r1-eth1 00:06:13
- N E1 2001:db8:3::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
+ N E2 2001:db8:3::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
fe80::e8bb:62ff:fee8:7022 r1-eth1
*N IA 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
- N E1 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
+ N E2 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
*N IE 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
- N E1 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
- N E1 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07
+ N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
+ N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07
*N IE 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07
- N E1 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07
+ N E2 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07
*N IA 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34
- N E1 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34
+ N E2 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34
*N IA 2001:db8:2::/64 :: r2-eth0 00:03:39
*N IA 2001:db8:3::/64 :: r2-eth1 00:03:34
- N E1 2001:db8:3::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34
- N E1 2001:db8:3::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
+ N E2 2001:db8:3::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34
+ N E2 2001:db8:3::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
*N IA 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
- N E1 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
+ N E2 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
*N IE 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
- N E1 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
- N E1 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32
+ N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
+ N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32
*N IE 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32
- N E1 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32
+ N E2 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32
*N IA 2001:db8:1::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19
- N E1 2001:db8:1::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19
+ N E2 2001:db8:1::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19
*N IA 2001:db8:2::/64 :: r2-eth0 00:07:17
*N IA 2001:db8:3::/64 :: r2-eth1 00:06:27
- N E1 2001:db8:3::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19
+ N E2 2001:db8:3::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19
*N IA 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r2-eth1 00:07:04
- N E1 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r2-eth1 00:07:04
+ N E2 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r2-eth1 00:07:04
*N IA 2001:db8:2::/64 :: r2-eth0 00:07:09
*N IA 2001:db8:3::/64 :: r2-eth1 00:07:04
- N E1 2001:db8:3::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
+ N E2 2001:db8:3::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
fe80::98cd:28ff:fe5e:3d93 r2-eth1
*N IA 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
- N E1 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
+ N E2 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
*N IE 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
- N E1 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
- N E1 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03
+ N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
+ N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03
*N IE 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03
- N E1 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03
+ N E2 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03
*N IA 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03
- N E1 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03
+ N E2 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03
*N IA 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03
- N E1 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03
+ N E2 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03
*N IA 2001:db8:3::/64 :: r3-eth0 00:04:08
- N E1 2001:db8:3::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03
- N E1 2001:db8:3::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03
+ N E2 2001:db8:3::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03
+ N E2 2001:db8:3::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03
*N IA 2001:db8:100::/64 :: r3-eth1 00:04:08
*N IA 2001:db8:200::/64 :: r3-eth2 00:04:05
- N E1 2001:db8:200::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00
+ N E2 2001:db8:200::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00
*N IA 2001:db8:300::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00
- N E1 2001:db8:300::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00
+ N E2 2001:db8:300::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00
*N IA 2001:db8:100::/64 :: r3-eth1 00:08:06
*N IA 2001:db8:200::/64 :: r3-eth2 00:08:04
- N E1 2001:db8:200::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59
+ N E2 2001:db8:200::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59
*N IA 2001:db8:300::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59
- N E1 2001:db8:300::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59
+ N E2 2001:db8:300::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59
*N IA 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58
- N E1 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58
+ N E2 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58
*N IA 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r3-eth0 00:08:58
- N E1 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r3-eth0 00:08:58
+ N E2 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r3-eth0 00:08:58
*N IA 2001:db8:3::/64 :: r3-eth0 00:09:03
- N E1 2001:db8:3::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58
+ N E2 2001:db8:3::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58
fe80::e8bb:62ff:fee8:7022 r3-eth0
*N IA 2001:db8:100::/64 :: r3-eth1 00:09:03
*N IA 2001:db8:200::/64 :: r3-eth2 00:09:02
- N E1 2001:db8:200::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57
+ N E2 2001:db8:200::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57
*N IA 2001:db8:300::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57
- N E1 2001:db8:300::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57
+ N E2 2001:db8:300::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57
*N IE 2001:db8:1::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
- N E1 2001:db8:1::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:1::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
*N IE 2001:db8:2::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
- N E1 2001:db8:2::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:2::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
*N IE 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
- N E1 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
- N E1 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
- N E1 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
*N IE 2001:db8:100::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
- N E1 2001:db8:100::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:100::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
*N IA 2001:db8:200::/64 :: r4-eth0 00:04:30
- N E1 2001:db8:200::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:200::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
*N IA 2001:db8:300::/64 :: r4-eth1 00:04:30
*N IE 2001:db8:100::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45
- N E1 2001:db8:100::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45
+ N E2 2001:db8:100::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45
*N IA 2001:db8:200::/64 :: r4-eth0 00:01:50
- N E1 2001:db8:200::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45
+ N E2 2001:db8:200::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45
*N IA 2001:db8:300::/64 :: r4-eth1 00:01:50
*N IE 2001:db8:1::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
- N E1 2001:db8:1::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:1::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
*N IE 2001:db8:2::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
- N E1 2001:db8:2::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:2::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
*N IE 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
- N E1 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
- N E1 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
*N IE 2001:db8:100::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
- N E1 2001:db8:100::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:100::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
*N IA 2001:db8:200::/64 :: r4-eth0 00:09:17
- N E1 2001:db8:200::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:200::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
*N IA 2001:db8:300::/64 :: r4-eth1 00:09:18
--- /dev/null
+{
+ "feature": [
+ "bgp"
+ ],
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf6": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "1.0.4.17",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+from copy import deepcopy
+from ipaddress import IPv4Address
+from lib.topotest import frr_unicode
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+from lib.bgp import verify_bgp_convergence, create_router_bgp
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ topo_daemons,
+ get_frr_ipv6_linklocal,
+)
+
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf6_rib,
+ create_router_ospf,
+ verify_ospf6_interface,
+ verify_ospf6_database,
+ config_ospf6_interface,
+)
+
+from ipaddress import IPv6Address
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospfv3_rte_calc.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+NETWORK = {
+ "ipv6": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+TOPOOLOGY = """
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+"""
+
+TESTCASES = """
+1. OSPF Cost - verifying ospf interface cost functionality
+"""
+
+
+class CreateTopo(Topo):
+ """
+ Test topology builder.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a perticular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a perticular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+def red_static(dut, config=True):
+ """Local def for Redstribute static routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """Local def for Redstribute connected routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "connected", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+def test_ospfv3_cost_tc52_p0(request):
+ """OSPF Cost - verifying ospf interface cost functionality"""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure ospf cost as 20 on interface between R0 and R1. "
+ "Configure ospf cost as 30 between interface between R0 and R2."
+ )
+
+ r0_ospf_cost = {
+ "r0": {"links": {"r1": {"ospf6": {"cost": 20}}, "r2": {"ospf6": {"cost": 30}}}}
+ }
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that cost is updated in the ospf interface between"
+ " r0 and r1 as 30 and r0 and r2 as 20"
+ )
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=r0_ospf_cost)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Swap the costs between interfaces on r0, between r0 and r1 to 30"
+ ", r0 and r2 to 20"
+ )
+
+ r0_ospf_cost = {
+ "r0": {"links": {"r1": {"ospf6": {"cost": 30}}, "r2": {"ospf6": {"cost": 20}}}}
+ }
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that cost is updated in the ospf interface between r0 "
+ "and r1 as 30 and r0 and r2 as 20."
+ )
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=r0_ospf_cost)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" Un configure cost from the interface r0 - r1.")
+
+ r0_ospf_cost = {
+ "r0": {"links": {"r1": {"ospf6": {"cost": 30, "del_action": True}}}}
+ }
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {"links": {"r1": {"ospf6": {"cost": 10}}, "r2": {"ospf6": {"cost": 20}}}}
+ }
+ step(
+ "Verify that cost is updated in the ospf interface between r0"
+ " and r1 as 10 and r0 and r2 as 20."
+ )
+
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" Un configure cost from the interface r0 - r2.")
+
+ r0_ospf_cost = {
+ "r0": {"links": {"r2": {"ospf6": {"cost": 20, "del_action": True}}}}
+ }
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that cost is updated in the ospf interface between r0"
+ "and r1 as 10 and r0 and r2 as 10"
+ )
+
+ input_dict = {
+ "r0": {"links": {"r1": {"ospf6": {"cost": 10}}, "r2": {"ospf6": {"cost": 10}}}}
+ }
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
--- /dev/null
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+from copy import deepcopy
+from ipaddress import IPv4Address
+from lib.topotest import frr_unicode
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf6_rib,
+ create_router_ospf,
+ verify_ospf6_interface,
+ verify_ospf6_database,
+ config_ospf6_interface,
+)
+
+from ipaddress import IPv6Address
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospfv3_single_area.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+1. OSPF IFSM -Verify state change events on p2p network.
+2. OSPF Timers - Verify OSPF interface timer hello interval functionality
+3. OSPF Timers - Verify OSPF interface timer dead interval functionality
+4. Verify ospf show commands with json output.
+5. Verify NFSM events when ospf nbr changes with different MTU values.
+ """
+
+
+class CreateTopo(Topo):
+ """
+ Test topology builder.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_p2p_tc3_p0(request):
+ """OSPF IFSM -Verify state change events on p2p network."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ step(
+ "Verify that OSPF is subscribed to multi cast services "
+ "(All SPF, all DR Routers)."
+ )
+ step("Verify that interface is enabled in ospf.")
+ step("Verify that config is successful.")
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r3": {"ospf6": {"ospf6Enabled": True}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"] = str(
+ IPv6Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ospf6": {
+ "internetAddress": [
+ {
+ "type": "inet6",
+ "address": topo_modify_change_ip["routers"]["r0"][
+ "links"
+ ]["r3"]["ipv6"].split("/")[0],
+ }
+ ],
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Modify the mask on the R0 interface")
+ ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"]
+ mask = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"]
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": ip_addr,
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"] = str(
+ IPv6Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(int(intf_ip.split("/")[1]) + 1)
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ospf6": {
+ "internetAddress": [
+ {
+ "type": "inet6",
+ "address": topo_modify_change_ip["routers"]["r0"][
+ "links"
+ ]["r3"]["ipv6"].split("/")[0],
+ }
+ ],
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv6"
+ ],
+ "interface": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "interface"
+ ],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step("Change the area id on the interface")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf6": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r3": {"ospf6": {"ospf6Enabled": True}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf6": {"area": "0.0.0.1"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r3": {"ospf6": {"ospf6Enabled": True}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify the all neighbors are up after clearing the process.")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr, ospf="ospf6")
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
show bgp martian next-hop
show bgp nexthop
+show bgp vrf all summary
+show bgp vrf all ipv4
+show bgp vrf all ipv6
+show bgp vrf all neighbors
+
show bgp evpn route
+show bgp l2vpn evpn route vni all
+show bgp l2vpn evpn vni
+show bgp l2vpn evpn import-rt
+show bgp l2vpn evpn vrf-import-rt
+show bgp l2vpn evpn all overlay
+show bgp l2vpn evpn summary
+show bgp l2vpn evpn route detail
+show bgp l2vpn evpn vni remote-ip-hash
+show bgp l2vpn evpn vni-svi-hash
+
+show evpn
+show evpn arp-cache vni all detail
+show evpn mac vni all detail
+show evpn next-hops vni all
+show evpn rmac vni all
+show evpn vni detail
+
CMD_LIST_END
# Zebra Support Bundle Command List
"ip ",
"ipv6 ",
"log ",
+ "mac access-list ",
"mpls lsp",
"mpls label",
"no ",
"password ",
+ "pbr ",
"ptm-enable",
"router-id ",
"service ",
if found_add_bfd_nbr:
lines_to_del_to_del.append((ctx_keys, line))
+ """
+ Neighbor changes of route-maps need to be accounted for in that we
+ do not want to do a `no route-map...` `route-map ....` when changing
+ a route-map. This is bad mojo as that we will send/receive
+ data we don't want.
+ Additionally we need to ensure that if we have different afi/safi
+ variants that they actually match and if we are going from a very
+ old style command such that the neighbor command is under the
+ `router bgp ..` node that we need to handle that appropriately
+ """
+ re_nbr_rm = re.search("neighbor(.*)route-map(.*)(in|out)$", line)
+ if re_nbr_rm:
+ adjust_for_bgp_node = 0
+ neighbor_name = re_nbr_rm.group(1)
+ rm_name_del = re_nbr_rm.group(2)
+ dir = re_nbr_rm.group(3)
+ search = "neighbor%sroute-map(.*)%s" % (neighbor_name, dir)
+ save_line = "EMPTY"
+ for (ctx_keys_al, add_line) in lines_to_add:
+ if ctx_keys_al[0].startswith("router bgp"):
+ if add_line:
+ rm_match = re.search(search, add_line)
+ if rm_match:
+ rm_name_add = rm_match.group(1)
+ if rm_name_add == rm_name_del:
+ continue
+ if len(ctx_keys_al) == 1:
+ save_line = line
+ adjust_for_bgp_node = 1
+ else:
+ if (
+ len(ctx_keys) > 1
+ and len(ctx_keys_al) > 1
+ and ctx_keys[1] == ctx_keys_al[1]
+ ):
+ lines_to_del_to_del.append((ctx_keys_al, line))
+
+ if adjust_for_bgp_node == 1:
+ for (ctx_keys_dl, dl_line) in lines_to_del:
+ if (
+ ctx_keys_dl[0].startswith("router bgp")
+ and len(ctx_keys_dl) > 1
+ and ctx_keys_dl[1] == "address-family ipv4 unicast"
+ ):
+ if save_line == dl_line:
+ lines_to_del_to_del.append((ctx_keys_dl, save_line))
+
"""
We changed how we display the neighbor interface command. Older
versions of frr would display the following:
install_node(&debug_node);
install_node(&interface_node);
install_node(&vrrp_node);
+ vrf_cmd_init(NULL, &vrrp_privs);
if_cmd_init();
install_element(VIEW_NODE, &vrrp_vrid_show_cmd);
*/
while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON
&& ret != CMD_WARNING && ret != CMD_WARNING_CONFIG_FAILED
+ && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
&& vty->node > CONFIG_NODE) {
vty->node = node_parent(vty->node);
ret = cmd_execute(vty, line, &cmd, 1);
*/
while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON
&& ret != CMD_WARNING && ret != CMD_WARNING_CONFIG_FAILED
+ && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
&& vty->node > CONFIG_NODE) {
vty->node = node_parent(vty->node);
ret = cmd_execute_command_strict(vline, vty, &cmd);
.prompt = "%s(config-pw)# ",
};
-#if defined(HAVE_PATHD)
static struct cmd_node segment_routing_node = {
.name = "segment-routing",
.node = SEGMENT_ROUTING_NODE,
.prompt = "%s(config-sr)# ",
};
+#if defined(HAVE_PATHD)
static struct cmd_node sr_traffic_eng_node = {
.name = "sr traffic-eng",
.node = SR_TRAFFIC_ENG_NODE,
}
#endif /* HAVE_FABRICD */
-#if defined(HAVE_PATHD)
DEFUNSH(VTYSH_SR, segment_routing, segment_routing_cmd,
"segment-routing",
"Configure segment routing\n")
return CMD_SUCCESS;
}
+#if defined (HAVE_PATHD)
DEFUNSH(VTYSH_PATHD, sr_traffic_eng, sr_traffic_eng_cmd,
"traffic-eng",
"Configure SR traffic engineering\n")
return vtysh_exit_keys(self, vty, argc, argv);
}
+DEFUNSH(VTYSH_SR, vtysh_exit_sr, vtysh_exit_sr_cmd, "exit",
+ "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit(vty);
+}
+
+DEFUNSH(VTYSH_SR, vtysh_quit_sr, vtysh_quit_sr_cmd, "quit",
+ "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit(vty);
+}
+
#if defined(HAVE_PATHD)
DEFUNSH(VTYSH_PATHD, vtysh_exit_pathd, vtysh_exit_pathd_cmd, "exit",
"Exit current mode and down to previous mode\n")
install_element(BFD_PROFILE_NODE, &vtysh_end_all_cmd);
#endif /* HAVE_BFDD */
-#if defined(HAVE_PATHD)
install_node(&segment_routing_node);
+ install_element(SEGMENT_ROUTING_NODE, &vtysh_exit_sr_cmd);
+ install_element(SEGMENT_ROUTING_NODE, &vtysh_quit_sr_cmd);
+ install_element(SEGMENT_ROUTING_NODE, &vtysh_end_all_cmd);
+
+#if defined(HAVE_PATHD)
install_node(&sr_traffic_eng_node);
install_node(&srte_segment_list_node);
install_node(&srte_policy_node);
install_node(&srte_candidate_dyn_node);
- install_element(SEGMENT_ROUTING_NODE, &vtysh_exit_pathd_cmd);
- install_element(SEGMENT_ROUTING_NODE, &vtysh_quit_pathd_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &vtysh_exit_pathd_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &vtysh_quit_pathd_cmd);
install_element(SR_SEGMENT_LIST_NODE, &vtysh_exit_pathd_cmd);
install_element(SR_CANDIDATE_DYN_NODE, &vtysh_exit_pathd_cmd);
install_element(SR_CANDIDATE_DYN_NODE, &vtysh_quit_pathd_cmd);
- install_element(SEGMENT_ROUTING_NODE, &vtysh_end_all_cmd);
+
install_element(SR_TRAFFIC_ENG_NODE, &vtysh_end_all_cmd);
install_element(SR_SEGMENT_LIST_NODE, &vtysh_end_all_cmd);
install_element(SR_POLICY_NODE, &vtysh_end_all_cmd);
#define VTYSH_ACL VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA
#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD
#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_VRF VTYSH_INTERFACE|VTYSH_STATICD
#define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD
/* Daemons who can process nexthop-group configs */
#define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD
"Set BGP large community list (for deletion)";
}
+ identity set-evpn-gateway-ip-ipv4 {
+ base frr-route-map:rmap-set-type;
+ description
+ "Set EVPN gateway IP overlay index IPv4";
+ }
+
+ identity set-evpn-gateway-ip-ipv6 {
+ base frr-route-map:rmap-set-type;
+ description
+ "Set EVPN gateway IP overlay index IPv6";
+ }
+
grouping extcommunity-non-transitive-types {
leaf two-octet-as-specific {
type boolean;
type bgp-filter:bgp-list-name;
}
}
+ case evpn-gateway-ip-ipv4 {
+ when
+ "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action,
+ 'frr-bgp-route-map:set-evpn-gateway-ip-ipv4')";
+ description
+ "Set EVPN gateway IP overlay index IPv4";
+ leaf evpn-gateway-ip-ipv4 {
+ type inet:ipv4-address;
+ }
+ }
+ case evpn-gateway-ip-ipv6 {
+ when
+ "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action,
+ 'frr-bgp-route-map:set-evpn-gateway-ip-ipv6')";
+ description
+ "Set EVPN gateway IP overlay index IPv6";
+ leaf evpn-gateway-ip-ipv6 {
+ type inet:ipv6-address;
+ }
+ }
}
}
"Enable ssmpingd operation.";
}
- container msdp-mesh-group {
- presence
- "Configure MSDP mesh-group.";
+ list msdp-mesh-groups {
+ key "name";
+ description
+ "RFC 3618 Section 10.2. MSDP mesh-group semantics
- leaf mesh-group-name {
- type string;
+ Groups multiple MSDP peers to reduce SA flooding typically used
+ in intra-domain settings.";
+
+ leaf name {
+ type string {
+ length 1..64;
+ }
description
- "MSDP mesh group name.";
+ "The mesh group name.";
}
- leaf-list member-ip {
+ leaf source {
type inet:ip-address;
description
- "Peer ip address.";
+ "Source IP address for the TCP connections.";
}
- leaf source-ip {
- type inet:ip-address;
- description
- "Source ip address for the TCP connection.";
+ list members {
+ key "address";
+
+ leaf address {
+ type inet:ip-address;
+ description
+ "Peer member IP address.";
+ }
}
}
static void zread_table_manager_request(ZAPI_HANDLER_ARGS)
{
- /* to avoid sending other messages like ZERBA_INTERFACE_UP */
+ /* to avoid sending other messages like ZEBRA_INTERFACE_UP */
if (hdr->command == ZEBRA_TABLE_MANAGER_CONNECT)
zread_table_manager_connect(client, msg, zvrf_id(zvrf));
else {
if (!client->proto) {
flog_err(
EC_ZEBRA_TM_ALIENS,
- "Got table request from an unidentified client");
+ "Got SRv6 request from an unidentified client");
return;
}
if (hdr->command == ZEBRA_GET_TABLE_CHUNK)
if (json == NULL) {
vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name);
vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex);
+ vty_out(vty, " SVI interface: %s\n",
+ (zevpn->svi_if ? zevpn->svi_if->name : ""));
+ vty_out(vty, " SVI ifIndex: %u\n",
+ (zevpn->svi_if ? zevpn->svi_if->ifindex : 0));
vty_out(vty, " Local VTEP IP: %pI4\n",
&zevpn->local_vtep_ip);
vty_out(vty, " Mcast group: %pI4\n",
json_object_string_add(json, "vxlanInterface",
zevpn->vxlan_if->name);
json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex);
+ if (zevpn->svi_if) {
+ json_object_string_add(json, "sviInterface",
+ zevpn->svi_if->name);
+ json_object_int_add(json, "sviIfindex",
+ zevpn->svi_if->ifindex);
+ }
json_object_string_add(json, "vtepIp",
inet_ntop(AF_INET, &zevpn->local_vtep_ip,
buf, sizeof(buf)));
zvrf = zebra_vrf_get_evpn();
assert(zvrf);
+ zevpn->svi_if = NULL;
+
/* Free the neighbor hash table. */
hash_free(zevpn->neigh_table);
zevpn->neigh_table = NULL;
{
struct zserv *client;
struct stream *s;
+ ifindex_t svi_index;
int rc;
client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
if (!client)
return 0;
+ svi_index = zevpn->svi_if ? zevpn->svi_if->ifindex : 0;
+
s = stream_new(ZEBRA_MAX_PACKET_SIZ);
zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id());
stream_put_in_addr(s, &zevpn->local_vtep_ip);
stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
stream_put_in_addr(s, &zevpn->mcast_grp);
+ stream_put(s, &svi_index, sizeof(ifindex_t));
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Send EVPN_ADD %u %pI4 tenant vrf %s to %s", zevpn->vni,
- &zevpn->local_vtep_ip,
- vrf_id_to_name(zevpn->vrf_id),
- zebra_route_string(client->proto));
+ zlog_debug(
+ "Send EVPN_ADD %u %pI4 tenant vrf %s(%u) SVI index %u to %s",
+ zevpn->vni, &zevpn->local_vtep_ip,
+ vrf_id_to_name(zevpn->vrf_id), zevpn->vrf_id,
+ (zevpn->svi_if ? zevpn->svi_if->ifindex : 0),
+ zebra_route_string(client->proto));
client->vniadd_cnt++;
rc = zserv_send_message(client, s);
/* Corresponding VxLAN interface. */
struct interface *vxlan_if;
+ /* Corresponding SVI interface. */
+ struct interface *svi_if;
+
/* List of remote VTEPs */
zebra_vtep_t *vteps;
#include "zebra/zebra_fpm_private.h"
#include "zebra/zebra_vxlan_private.h"
+#include "zebra/interface.h"
/*
* af_addr_size
{
struct netlink_nh_info nhi;
union g_addr *src;
- zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_vrf *zvrf = NULL;
+ struct interface *ifp = NULL, *link_if = NULL;
+ struct zebra_if *zif = NULL;
+ vni_t vni = 0;
memset(&nhi, 0, sizeof(nhi));
src = NULL;
if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) {
nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN;
- zl3vni = zl3vni_from_vrf(nexthop->vrf_id);
- if (zl3vni && is_l3vni_oper_up(zl3vni)) {
-
- /* Add VNI to VxLAN encap info */
- nhi.encap_info.vxlan_encap.vni = zl3vni->vni;
+ /* Extract VNI id for the nexthop SVI interface */
+ zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
+ if (zvrf) {
+ ifp = if_lookup_by_index_per_ns(zvrf->zns,
+ nexthop->ifindex);
+ if (ifp) {
+ zif = (struct zebra_if *)ifp->info;
+ if (zif) {
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ link_if = ifp;
+ else if (IS_ZEBRA_IF_VLAN(ifp))
+ link_if =
+ if_lookup_by_index_per_ns(
+ zvrf->zns,
+ zif->link_ifindex);
+ if (link_if)
+ vni = vni_id_from_svi(ifp,
+ link_if);
+ }
+ }
}
+
+ nhi.encap_info.vxlan_encap.vni = vni;
}
/*
}
/*
- * A helper function to delte and destory client info.
+ * A helper function to delete and destroy client info.
*/
static void zebra_gr_client_info_delte(struct zserv *client,
struct client_gr_info *info)
client->restart_time = monotime(&tv);
- /* For all the GR instance start the starle removal timer. */
+ /* For all the GR instance start the stale removal timer. */
TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) {
if (ZEBRA_CLIENT_GR_ENABLED(info->capabilities)
&& (info->t_stale_removal == NULL)) {
/*
* Update the graceful restart information
* for the client instance.
- * This function handles all the capabilties that are received.
+ * This function handles all the capabilities that are received.
*/
static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api)
{
if (!info)
info = zebra_gr_client_info_create(client);
- /* Udpate other parameters */
+ /* Update other parameters */
if (!info->gr_enable) {
client->gr_instance_count++;
cnt = zebra_gr_delete_stale_routes(info);
- /* Retsart the timer */
+ /* Restart the timer */
if (cnt > 0) {
LOG_GR("%s: Client %s processed %d routes. Start timer again",
__func__, zebra_route_string(client->proto), cnt);
&info->t_stale_removal);
} else {
/* No routes to delete for the VRF */
- LOG_GR("%s: Client %s all starle routes processed", __func__,
+ LOG_GR("%s: Client %s all stale routes processed", __func__,
zebra_route_string(client->proto));
XFREE(MTYPE_TMP, info->current_prefix);
* Cancel the stale timer and process the routes
*/
if (info->t_stale_removal) {
- LOG_GR("%s: Client %s cancled stale delete timer vrf %d",
+ LOG_GR("%s: Client %s canceled stale delete timer vrf %d",
__func__, zebra_route_string(client->proto),
info->vrf_id);
THREAD_OFF(info->t_stale_removal);
struct stream *s = NULL;
int msg_type = 0;
- s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ s = stream_new(ZEBRA_MLAG_BUF_LIMIT);
/*
* Place holder we need the message type first
*/
case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_ADD_BULK: {
ZebraMlagMrouteAddBulk *Bulk_msg = NULL;
ZebraMlagMrouteAdd *msg = NULL;
- size_t i;
+ size_t i, length_spot;
Bulk_msg = zebra_mlag_mroute_add_bulk__unpack(
NULL, hdr->data.len, hdr->data.data);
stream_putw(s, (Bulk_msg->n_mroute_add
* sizeof(struct mlag_mroute_add)));
/* No. of msgs in Batch */
- stream_putw(s, Bulk_msg->n_mroute_add);
+ length_spot = stream_putw(s, Bulk_msg->n_mroute_add);
/* Actual Data */
for (i = 0; i < Bulk_msg->n_mroute_add; i++) {
+ if (STREAM_SIZE(s)
+ < VRF_NAMSIZ + 22 + INTERFACE_NAMSIZ) {
+ zlog_warn(
+ "We have received more messages than we can parse at this point in time: %zu",
+ Bulk_msg->n_mroute_add);
+ break;
+ }
msg = Bulk_msg->mroute_add[i];
else
stream_put(s, NULL, INTERFACE_NAMSIZ);
}
+
+ stream_putw_at(s, length_spot, i + 1);
+
zebra_mlag_mroute_add_bulk__free_unpacked(Bulk_msg,
NULL);
} break;
case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_DEL_BULK: {
ZebraMlagMrouteDelBulk *Bulk_msg = NULL;
ZebraMlagMrouteDel *msg = NULL;
- size_t i;
+ size_t i, length_spot;
Bulk_msg = zebra_mlag_mroute_del_bulk__unpack(
NULL, hdr->data.len, hdr->data.data);
stream_putw(s, (Bulk_msg->n_mroute_del
* sizeof(struct mlag_mroute_del)));
/* No. of msgs in Batch */
- stream_putw(s, Bulk_msg->n_mroute_del);
+ length_spot = stream_putw(s, Bulk_msg->n_mroute_del);
/* Actual Data */
for (i = 0; i < Bulk_msg->n_mroute_del; i++) {
+ if (STREAM_SIZE(s)
+ < VRF_NAMSIZ + 16 + INTERFACE_NAMSIZ) {
+ zlog_warn(
+ "We have received more messages than we can parse at this time");
+ break;
+ }
msg = Bulk_msg->mroute_del[i];
else
stream_put(s, NULL, INTERFACE_NAMSIZ);
}
+
+ stream_putw_at(s, length_spot, i + 1);
+
zebra_mlag_mroute_del_bulk__free_unpacked(Bulk_msg,
NULL);
} break;
stream_putw_at(s, 0, stream_get_endp(s));
client->nh_last_upd_time = monotime(NULL);
- client->last_write_cmd = cmd;
return zserv_send_message(client, s);
failure:
stream_putw_at(s, 0, stream_get_endp(s));
client->nh_last_upd_time = monotime(NULL);
- client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE;
return zserv_send_message(client, s);
failure:
loc->status_up = false;
chunk = srv6_locator_chunk_alloc();
- chunk->proto = 0;
+ chunk->proto = NO_PROTO;
listnode_add(loc->chunks, chunk);
zebra_srv6_locator_add(loc);
}
for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) {
- if (chunk->proto != 0 && chunk->proto != proto)
+ if (chunk->proto != NO_PROTO && chunk->proto != proto)
continue;
chunk_found = true;
break;
}
chunk->proto = proto;
+ chunk->instance = instance;
+ chunk->session_id = session_id;
return loc;
}
vxl->access_vlan,
zif->brslave_info.br_if);
if (vlan_if) {
+ zevpn->svi_if = vlan_if;
zevpn->vrf_id = vlan_if->vrf_id;
zl3vni = zl3vni_from_vrf(
vlan_if->vrf_id);
return zl3vni;
}
+vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if)
+{
+ vni_t vni = 0;
+ zebra_evpn_t *zevpn = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* Check if an L3VNI belongs to this SVI interface.
+ * If not, check if an L2VNI belongs to this SVI interface.
+ */
+ zl3vni = zl3vni_from_svi(ifp, br_if);
+ if (zl3vni)
+ vni = zl3vni->vni;
+ else {
+ zevpn = zebra_evpn_from_svi(ifp, br_if);
+ if (zevpn)
+ vni = zevpn->vni;
+ }
+
+ return vni;
+}
+
static inline void zl3vni_get_vrr_rmac(zebra_l3vni_t *zl3vni,
struct ethaddr *rmac)
{
zevpn = zebra_evpn_from_svi(ifp, link_if);
if (zevpn) {
+ zevpn->svi_if = NULL;
zevpn->vrf_id = VRF_DEFAULT;
/* update the tenant vrf in BGP */
vrf_id_to_name(ifp->vrf_id));
/* update the vrf information for l2-vni and inform bgp */
+ zevpn->svi_if = ifp;
zevpn->vrf_id = ifp->vrf_id;
if (if_is_operative(zevpn->vxlan_if))
vlan_if = zvni_map_to_svi(vxl->access_vlan,
zif->brslave_info.br_if);
if (vlan_if) {
+ zevpn->svi_if = vlan_if;
zevpn->vrf_id = vlan_if->vrf_id;
zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
if (zl3vni)
struct zebra_l2info_vxlan *vxl = NULL;
zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
+ struct interface *vlan_if = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
&& (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
/* Delete from client, remove all remote VTEPs */
/* Also, free up all MACs and neighbors. */
+ zevpn->svi_if = NULL;
zebra_evpn_send_del_to_client(zevpn);
zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH);
zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC);
zebra_evpn_es_set_base_evpn(zevpn);
}
zevpn_vxlan_if_set(zevpn, ifp, true /* set */);
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if)
+ zevpn->svi_if = vlan_if;
+
/* Take further actions needed.
* Note that if we are here, there is a change of interest.
*/
vlan_if = zvni_map_to_svi(vxl->access_vlan,
zif->brslave_info.br_if);
if (vlan_if) {
+ zevpn->svi_if = vlan_if;
zevpn->vrf_id = vlan_if->vrf_id;
zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
if (zl3vni)
extern struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni);
extern struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni);
extern zebra_l3vni_t *zl3vni_lookup(vni_t vni);
+extern vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if);
DECLARE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
bool delete, const char *reason), (rmac, zl3vni, delete, reason));
0, client->redist_v4_del_cnt);
vty_out(vty, "Redist:v6 %-12u%-12u%-12u\n", client->redist_v6_add_cnt,
0, client->redist_v6_del_cnt);
+ vty_out(vty, "VRF %-12u%-12u%-12u\n", client->vrfadd_cnt, 0,
+ client->vrfdel_cnt);
vty_out(vty, "Connected %-12u%-12u%-12u\n", client->ifadd_cnt, 0,
client->ifdel_cnt);
+ vty_out(vty, "Interface %-12u%-12u%-12u\n", client->ifup_cnt, 0,
+ client->ifdown_cnt);
+ vty_out(vty, "Intf Addr %-12u%-12u%-12u\n",
+ client->connected_rt_add_cnt, 0, client->connected_rt_del_cnt);
vty_out(vty, "BFD peer %-12u%-12u%-12u\n", client->bfd_peer_add_cnt,
client->bfd_peer_upd8_cnt, client->bfd_peer_del_cnt);
vty_out(vty, "NHT v4 %-12u%-12u%-12u\n",
client->v6_nh_watch_add_cnt, 0, client->v6_nh_watch_rem_cnt);
vty_out(vty, "VxLAN SG %-12u%-12u%-12u\n", client->vxlan_sg_add_cnt,
0, client->vxlan_sg_del_cnt);
- vty_out(vty, "Interface Up Notifications: %u\n", client->ifup_cnt);
- vty_out(vty, "Interface Down Notifications: %u\n", client->ifdown_cnt);
- vty_out(vty, "VNI add notifications: %u\n", client->vniadd_cnt);
- vty_out(vty, "VNI delete notifications: %u\n", client->vnidel_cnt);
- vty_out(vty, "L3-VNI add notifications: %u\n", client->l3vniadd_cnt);
- vty_out(vty, "L3-VNI delete notifications: %u\n", client->l3vnidel_cnt);
- vty_out(vty, "MAC-IP add notifications: %u\n", client->macipadd_cnt);
- vty_out(vty, "MAC-IP delete notifications: %u\n", client->macipdel_cnt);
- vty_out(vty, "ES add notifications: %u\n", client->local_es_add_cnt);
- vty_out(vty, "ES delete notifications: %u\n", client->local_es_del_cnt);
- vty_out(vty, "ES-EVI add notifications: %u\n",
- client->local_es_evi_add_cnt);
- vty_out(vty, "ES-EVI delete notifications: %u\n",
- client->local_es_evi_del_cnt);
+ vty_out(vty, "VNI %-12u%-12u%-12u\n", client->vniadd_cnt, 0,
+ client->vnidel_cnt);
+ vty_out(vty, "L3-VNI %-12u%-12u%-12u\n", client->l3vniadd_cnt, 0,
+ client->l3vnidel_cnt);
+ vty_out(vty, "MAC-IP %-12u%-12u%-12u\n", client->macipadd_cnt, 0,
+ client->macipdel_cnt);
+ vty_out(vty, "ES %-12u%-12u%-12u\n", client->local_es_add_cnt,
+ 0, client->local_es_del_cnt);
+ vty_out(vty, "ES-EVI %-12u%-12u%-12u\n",
+ client->local_es_evi_add_cnt, 0, client->local_es_evi_del_cnt);
+ vty_out(vty, "Errors: %u\n", client->error_cnt);
TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) {
vty_out(vty, "VRF : %s\n", vrf_id_to_name(info->vrf_id));