((c-mode . ((indent-tabs-mode . t)
(show-trailing-whitespace . t)
(c-basic-offset . 8)))
- (json-mode . ((js-indent-level 4)))
- (python-mode . ((python-formatter . black)
- (python-fill-column . 88))))
+ (json-mode . ((js-indent-level 4))))
.emacs.desktop*
/test-suite.log
+pceplib/test/*.log
+pceplib/test/*.trs
control_new(csock);
- bglobal.bg_csockev = NULL;
thread_add_read(master, control_accept, NULL, sd, &bglobal.bg_csockev);
return 0;
key = jhash(&l3vpn->sid, 16, key);
key = jhash_1word(l3vpn->sid_flags, key);
key = jhash_1word(l3vpn->endpoint_behavior, key);
+ key = jhash_1word(l3vpn->loc_block_len, key);
+ key = jhash_1word(l3vpn->loc_node_len, key);
+ key = jhash_1word(l3vpn->func_len, key);
+ key = jhash_1word(l3vpn->arg_len, key);
+ key = jhash_1word(l3vpn->transposition_len, key);
+ key = jhash_1word(l3vpn->transposition_offset, key);
return key;
}
return sid_same(&l3vpn1->sid, &l3vpn2->sid)
&& l3vpn1->sid_flags == l3vpn2->sid_flags
- && l3vpn1->endpoint_behavior == l3vpn2->endpoint_behavior;
+ && l3vpn1->endpoint_behavior == l3vpn2->endpoint_behavior
+ && l3vpn1->loc_block_len == l3vpn2->loc_block_len
+ && l3vpn1->loc_node_len == l3vpn2->loc_node_len
+ && l3vpn1->func_len == l3vpn2->func_len
+ && l3vpn1->arg_len == l3vpn2->arg_len
+ && l3vpn1->transposition_len == l3vpn2->transposition_len
+ && l3vpn1->transposition_offset == l3vpn2->transposition_offset;
}
static bool srv6_l3vpn_same(const struct bgp_attr_srv6_l3vpn *h1,
key = jhash(attr->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key);
MIX3(attr->nh_ifindex, attr->nh_lla_ifindex, attr->distance);
MIX(attr->rmap_table_id);
+ MIX(attr->nh_type);
+ MIX(attr->bh_type);
return key;
}
&& attr1->distance == attr2->distance
&& srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn)
&& srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn)
- && attr1->srte_color == attr2->srte_color)
+ && attr1->srte_color == attr2->srte_color
+ && attr1->nh_type == attr2->nh_type
+ && attr1->bh_type == attr2->bh_type)
return true;
}
return 0;
}
+
+/* SRv6 Service Data Sub-Sub-TLV attribute
+ * draft-ietf-bess-srv6-services-07
+ */
+static bgp_attr_parse_ret_t
+bgp_attr_srv6_service_data(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ uint8_t type, loc_block_len, loc_node_len, func_len, arg_len,
+ transposition_len, transposition_offset;
+ uint16_t length;
+ size_t headersz = sizeof(type) + sizeof(length);
+
+ if (STREAM_READABLE(peer->curr) < headersz) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Data Sub-Sub-TLV attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)",
+ headersz, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ type = stream_getc(peer->curr);
+ length = stream_getw(peer->curr);
+
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Data Sub-Sub-TLV attribute - insufficent data (need %hu for attribute data, have %zu remaining in UPDATE)",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE) {
+ loc_block_len = stream_getc(peer->curr);
+ loc_node_len = stream_getc(peer->curr);
+ func_len = stream_getc(peer->curr);
+ arg_len = stream_getc(peer->curr);
+ transposition_len = stream_getc(peer->curr);
+ transposition_offset = stream_getc(peer->curr);
+
+ /* Log SRv6 Service Data Sub-Sub-TLV */
+ if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
+ zlog_debug(
+ "%s: srv6-l3-srv-data loc-block-len=%u, loc-node-len=%u func-len=%u, arg-len=%u, transposition-len=%u, transposition-offset=%u",
+ __func__, loc_block_len, loc_node_len, func_len,
+ arg_len, transposition_len,
+ transposition_offset);
+ }
+
+ attr->srv6_l3vpn->loc_block_len = loc_block_len;
+ attr->srv6_l3vpn->loc_node_len = loc_node_len;
+ attr->srv6_l3vpn->func_len = func_len;
+ attr->srv6_l3vpn->arg_len = arg_len;
+ attr->srv6_l3vpn->transposition_len = transposition_len;
+ attr->srv6_l3vpn->transposition_offset = transposition_offset;
+ }
+
+ else {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "%s attr SRv6 Service Data Sub-Sub-TLV sub-sub-type=%u is not supported, skipped",
+ peer->host, type);
+
+ stream_forward_getp(peer->curr, length);
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* SRv6 Service Sub-TLV attribute
+ * draft-ietf-bess-srv6-services-07
+ */
+static bgp_attr_parse_ret_t
+bgp_attr_srv6_service(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ struct in6_addr ipv6_sid;
+ uint8_t type, sid_flags;
+ uint16_t length, endpoint_behavior;
+ size_t headersz = sizeof(type) + sizeof(length);
+ bgp_attr_parse_ret_t err;
+ char buf[BUFSIZ];
+
+ if (STREAM_READABLE(peer->curr) < headersz) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Sub-TLV attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)",
+ headersz, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ type = stream_getc(peer->curr);
+ length = stream_getw(peer->curr);
+
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Sub-TLV attribute - insufficent data (need %hu for attribute data, have %zu remaining in UPDATE)",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO) {
+ stream_getc(peer->curr);
+ stream_get(&ipv6_sid, peer->curr, sizeof(ipv6_sid));
+ sid_flags = stream_getc(peer->curr);
+ endpoint_behavior = stream_getw(peer->curr);
+ stream_getc(peer->curr);
+
+ /* Log SRv6 Service Sub-TLV */
+ if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
+ inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf));
+ zlog_debug(
+ "%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x",
+ __func__, buf, sid_flags, endpoint_behavior);
+ }
+
+ /* Configure from Info */
+ if (attr->srv6_l3vpn) {
+ flog_err(EC_BGP_ATTRIBUTE_REPEATED,
+ "Prefix SID SRv6 L3VPN field repeated");
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_MAL_ATTR, args->total);
+ }
+ attr->srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN,
+ sizeof(struct bgp_attr_srv6_l3vpn));
+ sid_copy(&attr->srv6_l3vpn->sid, &ipv6_sid);
+ attr->srv6_l3vpn->sid_flags = sid_flags;
+ attr->srv6_l3vpn->endpoint_behavior = endpoint_behavior;
+ attr->srv6_l3vpn->loc_block_len = 0;
+ attr->srv6_l3vpn->loc_node_len = 0;
+ attr->srv6_l3vpn->func_len = 0;
+ attr->srv6_l3vpn->arg_len = 0;
+ attr->srv6_l3vpn->transposition_len = 0;
+ attr->srv6_l3vpn->transposition_offset = 0;
+
+ // Sub-Sub-TLV found
+ if (length > BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH) {
+ err = bgp_attr_srv6_service_data(args);
+
+ if (err != BGP_ATTR_PARSE_PROCEED)
+ return err;
+ }
+
+ attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn);
+ }
+
+ /* Placeholder code for unsupported type */
+ else {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "%s attr SRv6 Service Sub-TLV sub-type=%u is not supported, skipped",
+ peer->host, type);
+
+ stream_forward_getp(peer->curr, length);
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
/*
* Read an individual SID value returning how much data we have read
* Returns 0 if there was an error that needs to be passed up the stack
uint32_t srgb_range;
int srgb_count;
uint8_t sid_type, sid_flags;
- uint16_t endpoint_behavior;
char buf[BUFSIZ];
if (type == BGP_PREFIX_SID_LABEL_INDEX) {
/* Placeholder code for the SRv6 L3 Service type */
else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) {
- if (STREAM_READABLE(peer->curr) < length
- || length != BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH) {
- flog_err(EC_BGP_ATTR_LEN,
- "Prefix SID SRv6 L3-Service length is %hu instead of %u",
- length, BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH);
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Prefix SID SRv6 L3-Service length is %hu, but only %zu bytes remain",
+ length, STREAM_READABLE(peer->curr));
return bgp_attr_malformed(args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
args->total);
}
- /* Parse L3-SERVICE Sub-TLV */
- stream_getc(peer->curr); /* reserved */
- stream_get(&ipv6_sid, peer->curr,
- sizeof(ipv6_sid)); /* sid_value */
- sid_flags = stream_getc(peer->curr); /* sid_flags */
- endpoint_behavior = stream_getw(peer->curr); /* endpoint */
- stream_getc(peer->curr); /* reserved */
-
- /* Log L3-SERVICE Sub-TLV */
- if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
- inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf));
- zlog_debug(
- "%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x",
- __func__, buf, sid_flags, endpoint_behavior);
- }
+ /* ignore reserved */
+ stream_getc(peer->curr);
- /* Configure from Info */
- if (attr->srv6_l3vpn) {
- flog_err(EC_BGP_ATTRIBUTE_REPEATED,
- "Prefix SID SRv6 L3VPN field repeated");
- return bgp_attr_malformed(
- args, BGP_NOTIFY_UPDATE_MAL_ATTR, args->total);
- }
- attr->srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN,
- sizeof(struct bgp_attr_srv6_l3vpn));
- attr->srv6_l3vpn->sid_flags = sid_flags;
- attr->srv6_l3vpn->endpoint_behavior = endpoint_behavior;
- sid_copy(&attr->srv6_l3vpn->sid, &ipv6_sid);
- attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn);
+ return bgp_attr_srv6_service(args);
}
/* Placeholder code for Unsupported TLV */
/* SRv6 Service Information Attribute. */
if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_MPLS_VPN) {
if (attr->srv6_l3vpn) {
+ uint8_t subtlv_len =
+ BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH
+ + BGP_ATTR_MIN_LEN
+ + BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH;
+ uint8_t tlv_len = subtlv_len + BGP_ATTR_MIN_LEN + 1;
+ uint8_t attr_len = tlv_len + BGP_ATTR_MIN_LEN;
stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
| BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_PREFIX_SID);
- stream_putc(s, 24); /* tlv len */
+ stream_putc(s, attr_len);
stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE);
- stream_putw(s, 21); /* sub-tlv len */
+ stream_putw(s, tlv_len);
+ stream_putc(s, 0); /* reserved */
+ stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO);
+ stream_putw(s, subtlv_len);
stream_putc(s, 0); /* reserved */
stream_put(s, &attr->srv6_l3vpn->sid,
sizeof(attr->srv6_l3vpn->sid)); /* sid */
stream_putc(s, 0); /* sid_flags */
stream_putw(s, 0xffff); /* endpoint */
stream_putc(s, 0); /* reserved */
+ stream_putc(
+ s,
+ BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE);
+ stream_putw(
+ s,
+ BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH);
+ stream_putc(s, attr->srv6_l3vpn->loc_block_len);
+ stream_putc(s, attr->srv6_l3vpn->loc_node_len);
+ stream_putc(s, attr->srv6_l3vpn->func_len);
+ stream_putc(s, attr->srv6_l3vpn->arg_len);
+ stream_putc(s, attr->srv6_l3vpn->transposition_len);
+ stream_putc(s, attr->srv6_l3vpn->transposition_offset);
} else if (attr->srv6_vpn) {
stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
| BGP_ATTR_FLAG_TRANS);
#define BGP_PREFIX_SID_IPV6_LENGTH 19
#define BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH 6
#define BGP_PREFIX_SID_VPN_SID_LENGTH 19
-#define BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH 21
+
+/* SRv6 Service Sub-TLV types */
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO 1
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH 21
+
+/* SRv6 Service Data Sub-Sub-TLV types */
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE 1
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH 6
+
+/* SRv6 SID Structure default values */
+#define BGP_PREFIX_SID_SRV6_LOCATOR_BLOCK_LENGTH 40
+#define BGP_PREFIX_SID_SRV6_LOCATOR_NODE_LENGTH 24
+#define BGP_PREFIX_SID_SRV6_FUNCTION_LENGTH 16
+#define BGP_PREFIX_SID_SRV6_ARGUMENT_LENGTH 0
+#define BGP_PREFIX_SID_SRV6_TRANSPOSITION_LENGTH 16
+#define BGP_PREFIX_SID_SRV6_TRANSPOSITION_OFFSET 64
#define BGP_ATTR_NH_AFI(afi, attr) \
((afi != AFI_L2VPN) ? afi : \
uint8_t sid_flags;
uint16_t endpoint_behavior;
struct in6_addr sid;
+ uint8_t loc_block_len;
+ uint8_t loc_node_len;
+ uint8_t func_len;
+ uint8_t arg_len;
+ uint8_t transposition_len;
+ uint8_t transposition_offset;
};
/* BGP core attribute structure. */
/* EVPN DF preference and algorithm for DF election on local ESs */
uint16_t df_pref;
uint8_t df_alg;
+
+ /* Nexthop type */
+ enum nexthop_types_t nh_type;
+
+ /* If NEXTHOP_TYPE_BLACKHOLE, then blackhole type */
+ enum blackhole_type bh_type;
};
/* rmap_change_flags definition */
return entry->direct == COMMUNITY_PERMIT;
if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) {
- if (lcommunity_cmp(lcom, entry->u.com))
+ if (lcommunity_cmp(lcom, entry->u.lcom))
return entry->direct == COMMUNITY_PERMIT;
} else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) {
if (lcommunity_regexp_match(lcom, entry->reg))
RESET_FLAG(dummy_attr.rmap_change_flags);
ret = route_map_apply(rmap, dest_p, &path);
- if (ret != RMAP_PERMITMATCH)
- bgp_attr_flush(&dummy_attr);
- else {
+ bgp_attr_flush(&dummy_attr);
+
+ if (ret == RMAP_PERMITMATCH) {
bgp_dest_unlock_node(dest);
if (BGP_DEBUG(update, UPDATE_OUT))
zlog_debug(
struct update_subgroup *subgrp;
struct attr dummy_attr = {0}, attr = {0};
struct bgp_path_info_extra path_extra = {0};
+ route_map_result_t ret;
paf = peer_af_find(peer, afi, safi);
if (!paf)
RESET_FLAG(dummy_attr.rmap_change_flags);
- if (route_map_apply(rmap, dest_p, &path)
- != RMAP_PERMITMATCH) {
- bgp_attr_flush(&dummy_attr);
+ ret = route_map_apply(rmap, dest_p, &path);
+ bgp_attr_flush(&dummy_attr);
+
+ if (ret != RMAP_PERMITMATCH)
continue;
- }
if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)
|| (addpath_capable
interval = interval
- secs_into_day % interval; /* always > 0 */
}
- bgp_dump->t_interval = NULL;
thread_add_timer(bm->master, bgp_dump_interval_func, bgp_dump,
interval, &bgp_dump->t_interval);
} else {
/* One-off dump: execute immediately, don't affect any scheduled
* dumps */
- bgp_dump->t_interval = NULL;
thread_add_event(bm->master, bgp_dump_interval_func, bgp_dump,
0, &bgp_dump->t_interval);
}
{
struct bgp_dump *bgp_dump;
bgp_dump = THREAD_ARG(t);
- bgp_dump->t_interval = NULL;
/* Reschedule dump even if file couldn't be opened this time... */
if (bgp_dump_open_file(bgp_dump) != NULL) {
#include "bgpd/bgp_mac.h"
#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_trace.h"
/*
* Definitions and external declarations.
&p->prefix.macip_addr.mac, &p->prefix.macip_addr.ip,
flags, seq, &remote_vtep_ip);
+ frrtrace(5, frr_bgp, evpn_mac_ip_zsend, add, vpn, p, remote_vtep_ip,
+ esi);
+
return zclient_send_message(zclient);
}
add ? "ADD" : "DEL", vpn->vni,
&p->prefix.imet_addr.ip.ipaddr_v4);
+ frrtrace(3, frr_bgp, evpn_bum_vtep_zsend, add, vpn, p);
+
return zclient_send_message(zclient);
}
}
}
+/*
+ * Handle autort change for L3VNI.
+ */
+static void update_autort_l3vni(struct bgp *bgp)
+{
+ if ((CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+ && (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)))
+ return;
+
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
+ if (is_l3vni_live(bgp))
+ uninstall_routes_for_vrf(bgp);
+
+ /* Cleanup the RT to VRF mapping */
+ bgp_evpn_unmap_vrf_from_its_rts(bgp);
+
+ /* Remove auto generated RT */
+ evpn_auto_rt_import_delete_for_vrf(bgp);
+
+ list_delete_all_node(bgp->vrf_import_rtl);
+
+ /* Map auto derive or configured RTs */
+ evpn_auto_rt_import_add_for_vrf(bgp);
+ }
+
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ list_delete_all_node(bgp->vrf_export_rtl);
+
+ evpn_auto_rt_export_delete_for_vrf(bgp);
+
+ evpn_auto_rt_export_add_for_vrf(bgp);
+
+ if (is_l3vni_live(bgp))
+ bgp_evpn_map_vrf_to_its_rts(bgp);
+ }
+
+ if (!is_l3vni_live(bgp))
+ return;
+
+ /* advertise type-5 routes if needed */
+ update_advertise_vrf_routes(bgp);
+
+ /* install all remote routes belonging to this l3vni
+ * into corresponding vrf
+ */
+ install_routes_for_vrf(bgp);
+}
+
/*
* Public functions.
*/
(void (*)(struct hash_bucket *,
void*))update_autort_vni,
bgp);
+ if (bgp->l3vni)
+ update_autort_l3vni(bgp);
}
/*
* type-5 routes. It may be tweaked later on for other routes, or
* even removed completely when all routes are handled.
*/
- if (pfx && pfx->family == AF_EVPN &&
- (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ||
- evp->prefix.route_type == BGP_EVPN_IMET_ROUTE ||
- evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
+ if (pfx && pfx->family == AF_EVPN
+ && (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_AD_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_ES_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
return true;
return false;
#include "bgpd/bgp_label.h"
#include "bgpd/bgp_nht.h"
#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_trace.h"
static void bgp_evpn_local_es_down(struct bgp *bgp,
struct bgp_evpn_es *es);
es_vtep->es = es;
es_vtep->vtep_ip.s_addr = vtep_ip.s_addr;
+ inet_ntop(AF_INET, &es_vtep->vtep_ip, es_vtep->vtep_str,
+ sizeof(es_vtep->vtep_str));
listnode_init(&es_vtep->es_listnode, es_vtep);
listnode_add_sort(es->es_vtep_list, &es_vtep->es_listnode);
zlog_debug("Tx %s Remote ESI %s VTEP %pI4", add ? "ADD" : "DEL",
es->esi_str, &es_vtep->vtep_ip);
+ frrtrace(3, frr_bgp, evpn_mh_vtep_zsend, add, es, es_vtep);
+
return zclient_send_message(zclient);
}
es_vrf->bgp_vrf->vrf_id,
v4_nhg ? "v4_nhg" : "v6_nhg", nhg_id);
+ frrtrace(4, frr_bgp, evpn_mh_nhg_zsend, true, v4_nhg, nhg_id, es_vrf);
+
/* only the gateway ip changes for each NH. rest of the params
* are constant
*/
zlog_debug("nhg %u vtep %pI4 l3-svi %d", api_nhg.id,
&es_vtep->vtep_ip,
es_vrf->bgp_vrf->l3vni_svi_ifindex);
+
+ frrtrace(3, frr_bgp, evpn_mh_nh_zsend, nhg_id, es_vtep, es_vrf);
}
if (!api_nhg.nexthop_num)
es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id,
v4_nhg ? "v4_nhg" : "v6_nhg", api_nhg.id);
+
+ frrtrace(4, frr_bgp, evpn_mh_nhg_zsend, false, v4_nhg, api_nhg.id,
+ es_vrf);
+
zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg);
}
nh->bgp_vrf->name, nh->nh_str);
}
+ frrtrace(2, frr_bgp, evpn_mh_nh_rmac_zsend, add, nh);
+
zclient_send_message(zclient);
}
struct bgp_evpn_es *es; /* parent ES */
struct in_addr vtep_ip;
+ char vtep_str[INET6_ADDRSTRLEN];
+
uint32_t flags;
/* Rxed a Type4 route from this PE */
#define BGP_EVPNES_VTEP_ESR (1 << 0)
"Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n");
vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n");
vty_out(vty,
- "EVPN type-1 prefix: [1]:[ESI]:[EthTag]:[IPlen]:[VTEP-IP]\n");
+ "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]\n");
vty_out(vty,
"EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n");
vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
/* RD header and legend - once overall. */
if (rd_header && !json) {
vty_out(vty,
- "EVPN type-1 prefix: [1]:[ESI]:[EthTag]:[IPlen]:[VTEP-IP]\n");
+ "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]\n");
vty_out(vty,
"EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]\n");
vty_out(vty,
* if index != 0: try to allocate as index-mode
* else: try to allocate as auto-mode
*/
-static bool alloc_new_sid(struct bgp *bgp, uint32_t index,
- struct in6_addr *sid)
+static uint32_t alloc_new_sid(struct bgp *bgp, uint32_t index,
+ struct in6_addr *sid)
{
struct listnode *node;
struct prefix_ipv6 *chunk;
struct in6_addr sid_buf;
bool alloced = false;
+ int label;
if (!bgp || !sid)
return false;
for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, chunk)) {
sid_buf = chunk->prefix;
if (index != 0) {
- sid_buf.s6_addr[15] = index;
+ label = index << 12;
+ transpose_sid(&sid_buf, label, 64, 16);
if (sid_exist(bgp, &sid_buf))
return false;
alloced = true;
}
for (size_t i = 1; i < 255; i++) {
- sid_buf.s6_addr[15] = (i & 0xff00) >> 8;
- sid_buf.s6_addr[14] = (i & 0x00ff);
-
+ label = i << 12;
+ transpose_sid(&sid_buf, label, 64, 16);
if (sid_exist(bgp, &sid_buf))
continue;
alloced = true;
}
if (!alloced)
- return false;
+ return 0;
sid_register(bgp, &sid_buf, bgp->srv6_locator_name);
*sid = sid_buf;
- return true;
+ return label;
}
void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi)
{
int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
- bool alloced = false;
char buf[256];
struct in6_addr *sid;
- uint32_t tovpn_sid_index = 0;
+ uint32_t tovpn_sid_index = 0, tovpn_sid_transpose_label;
bool tovpn_sid_auto = false;
if (debug)
}
sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr));
- alloced = alloc_new_sid(bgp_vpn, tovpn_sid_index, sid);
- if (!alloced) {
+ tovpn_sid_transpose_label =
+ alloc_new_sid(bgp_vpn, tovpn_sid_index, sid);
+ if (tovpn_sid_transpose_label == 0) {
zlog_debug("%s: not allocated new sid for vrf %s: afi %s",
__func__, bgp_vrf->name_pretty, afi2str(afi));
return;
__func__, buf, bgp_vrf->name_pretty,
afi2str(afi));
}
+ bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label =
+ tovpn_sid_transpose_label;
bgp_vrf->vpn_policy[afi].tovpn_sid = sid;
}
+void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset,
+ uint8_t len)
+{
+ for (uint8_t idx = 0; idx < len; idx++) {
+ uint8_t tidx = offset + idx;
+ sid->s6_addr[tidx / 8] &= ~(0x1 << (7 - tidx % 8));
+ if (label >> (19 - idx) & 0x1)
+ sid->s6_addr[tidx / 8] |= 0x1 << (7 - tidx % 8);
+ }
+}
+
static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
{
uint32_t i, j;
extra = bgp_path_info_extra_get(bpi);
for (i = 0; i < num_sids; i++)
- memcpy(&extra->sid[i], &sid[i], sizeof(struct in6_addr));
+ memcpy(&extra->sid[i].sid, &sid[i], sizeof(struct in6_addr));
extra->num_sids = num_sids;
}
+static void unsetsids(struct bgp_path_info *bpi)
+{
+ struct bgp_path_info_extra *extra;
+
+ extra = bgp_path_info_extra_get(bpi);
+ extra->num_sids = 0;
+ memset(extra->sid, 0, sizeof(extra->sid));
+}
+
/*
* returns pointer to new bgp_path_info upon success
*/
struct bgp_path_info *bpi;
struct bgp_path_info *bpi_ultimate;
struct bgp_path_info *new;
+ struct bgp_path_info_extra *extra;
uint32_t num_sids = 0;
if (new_attr->srv6_l3vpn || new_attr->srv6_vpn)
* rewrite sid
*/
if (num_sids) {
- if (new_attr->srv6_l3vpn)
+ if (new_attr->srv6_l3vpn) {
setsids(bpi, &new_attr->srv6_l3vpn->sid,
num_sids);
- else if (new_attr->srv6_vpn)
+
+ extra = bgp_path_info_extra_get(bpi);
+
+ extra->sid[0].loc_block_len =
+ new_attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ new_attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len =
+ new_attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len =
+ new_attr->srv6_l3vpn->arg_len;
+
+ if (new_attr->srv6_l3vpn->transposition_len
+ != 0)
+ transpose_sid(
+ &extra->sid[0].sid,
+ decode_label(label),
+ new_attr->srv6_l3vpn
+ ->transposition_offset,
+ new_attr->srv6_l3vpn
+ ->transposition_len);
+ } else if (new_attr->srv6_vpn)
setsids(bpi, &new_attr->srv6_vpn->sid,
num_sids);
- }
+ } else
+ unsetsids(bpi);
if (nexthop_self_flag)
bgp_path_info_set_flag(bn, bpi, BGP_PATH_ANNC_NH_SELF);
nh_valid = bgp_find_or_add_nexthop(
bgp, bgp_nexthop, afi, safi, bpi, NULL, 0, p);
+ /*
+ * If you are using SRv6 VPN instead of MPLS, it need to check
+ * the SID allocation. If the sid is not allocated, the rib
+ * will be invalid.
+ */
+ if (bgp->srv6_enabled
+ && (!new_attr->srv6_l3vpn && !new_attr->srv6_vpn)) {
+ bgp_path_info_unset_flag(bn, bpi, BGP_PATH_VALID);
+ nh_valid = false;
+ }
+
if (debug)
zlog_debug("%s: nexthop is %svalid (in vrf %s)",
__func__, (nh_valid ? "" : "not "),
* rewrite sid
*/
if (num_sids) {
- if (new_attr->srv6_l3vpn)
+ if (new_attr->srv6_l3vpn) {
setsids(new, &new_attr->srv6_l3vpn->sid, num_sids);
- else if (new_attr->srv6_vpn)
+
+ extra = bgp_path_info_extra_get(new);
+
+ extra->sid[0].loc_block_len =
+ new_attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ new_attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len = new_attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len = new_attr->srv6_l3vpn->arg_len;
+
+ if (new_attr->srv6_l3vpn->transposition_len != 0)
+ transpose_sid(&extra->sid[0].sid,
+ decode_label(label),
+ new_attr->srv6_l3vpn
+ ->transposition_offset,
+ new_attr->srv6_l3vpn
+ ->transposition_len);
+ } else if (new_attr->srv6_vpn)
setsids(new, &new_attr->srv6_vpn->sid, num_sids);
- }
+ } else
+ unsetsids(new);
if (num_labels)
setlabels(new, label, num_labels);
nh_valid = bgp_find_or_add_nexthop(bgp, bgp_nexthop, afi, safi,
new, NULL, 0, p);
+ /*
+ * If you are using SRv6 VPN instead of MPLS, it need to check
+ * the SID allocation. If the sid is not allocated, the rib
+ * will be invalid.
+ */
+ if (bgp->srv6_enabled
+ && (!new->attr->srv6_l3vpn && !new->attr->srv6_vpn)) {
+ bgp_path_info_unset_flag(bn, new, BGP_PATH_VALID);
+ nh_valid = false;
+ }
+
if (debug)
zlog_debug("%s: nexthop is %svalid (in vrf %s)",
__func__, (nh_valid ? "" : "not "),
/* Set SID for SRv6 VPN */
if (bgp_vrf->vpn_policy[afi].tovpn_sid) {
+ encode_label(bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label,
+ &label);
static_attr.srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN,
sizeof(struct bgp_attr_srv6_l3vpn));
static_attr.srv6_l3vpn->sid_flags = 0x00;
static_attr.srv6_l3vpn->endpoint_behavior = 0xffff;
+ static_attr.srv6_l3vpn->loc_block_len =
+ BGP_PREFIX_SID_SRV6_LOCATOR_BLOCK_LENGTH;
+ static_attr.srv6_l3vpn->loc_node_len =
+ BGP_PREFIX_SID_SRV6_LOCATOR_NODE_LENGTH;
+ static_attr.srv6_l3vpn->func_len =
+ BGP_PREFIX_SID_SRV6_FUNCTION_LENGTH;
+ static_attr.srv6_l3vpn->arg_len =
+ BGP_PREFIX_SID_SRV6_ARGUMENT_LENGTH;
+ static_attr.srv6_l3vpn->transposition_len =
+ BGP_PREFIX_SID_SRV6_TRANSPOSITION_LENGTH;
+ static_attr.srv6_l3vpn->transposition_offset =
+ BGP_PREFIX_SID_SRV6_TRANSPOSITION_OFFSET;
memcpy(&static_attr.srv6_l3vpn->sid,
bgp_vrf->vpn_policy[afi].tovpn_sid,
sizeof(static_attr.srv6_l3vpn->sid));
extern void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi);
extern int vpn_leak_label_callback(mpls_label_t label, void *lblid, bool alloc);
extern void ensure_vrf_tovpn_sid(struct bgp *vpn, struct bgp *vrf, afi_t afi);
+extern void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset,
+ uint8_t size);
extern void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
afi_t afi, safi_t safi);
void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
if (!bgp_vrf->vpn_policy[afi].tovpn_sid)
ensure_vrf_tovpn_sid(bgp_vpn, bgp_vrf, afi);
+ if (!bgp_vrf->vpn_policy[afi].tovpn_sid
+ && bgp_vrf->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent)
+ vpn_leak_zebra_vrf_sid_withdraw(bgp_vrf, afi);
+
if (sid_diff(bgp_vrf->vpn_policy[afi].tovpn_sid,
bgp_vrf->vpn_policy[afi]
.tovpn_zebra_vrf_sid_last_sent)) {
#include "bgpd/bgp_errors.h"
#include "bgpd/bgp_network.h"
#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_nht.h"
extern struct zebra_privs_t bgpd_privs;
BGP_EVENT_ADD(peer, TCP_connection_open);
}
+ /*
+ * If we are doing nht for a peer that is v6 LL based
+ * massage the event system to make things happy
+ */
+ bgp_nht_interface_events(peer);
+
return 0;
}
struct peer_af *paf;
struct bpacket *next_pkt;
struct update_subgroup *subgrp;
+ enum bgp_af_index index;
- FOREACH_AFI_SAFI (afi, safi) {
- paf = peer_af_find(peer, afi, safi);
+ for (index = BGP_AF_START; index < BGP_AF_MAX; index++) {
+ paf = peer->peer_af_array[index];
if (!paf)
continue;
+
subgrp = paf->subgroup;
if (!subgrp)
continue;
return;
}
+ afi = paf->afi;
+ safi = paf->safi;
+
/* No packets to send, see if EOR is pending */
if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) {
if (!subgrp->t_coalesce && peer->afc_nego[afi][safi]
return 0;
do {
+ enum bgp_af_index index;
+
s = NULL;
- FOREACH_AFI_SAFI (afi, safi) {
- paf = peer_af_find(peer, afi, safi);
+ for (index = BGP_AF_START; index < BGP_AF_MAX; index++) {
+ paf = peer->peer_af_array[index];
if (!paf || !PAF_SUBGRP(paf))
continue;
+
+ afi = paf->afi;
+ safi = paf->safi;
next_pkt = paf->next_pkt_to_send;
/*
struct update_group *updgrp;
struct peer *updgrp_peer;
uint8_t subtype;
+ bool force_update = false;
bgp_size_t msg_length =
size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE);
/* Avoid supressing duplicate routes later
* when processing in subgroup_announce_table().
*/
- SET_FLAG(paf->subgroup->sflags, SUBGRP_STATUS_FORCE_UPDATES);
+ force_update = true;
/* If the peer is configured for default-originate clear the
* SUBGRP_STATUS_DEFAULT_ORIGINATE flag so that we will
}
/* Perform route refreshment to the peer */
- bgp_announce_route(peer, afi, safi);
+ bgp_announce_route(peer, afi, safi, force_update);
/* No FSM action necessary */
return BGP_PACKET_NOOP;
peer->afc_recv[afi][safi] = 1;
if (peer->afc[afi][safi]) {
peer->afc_nego[afi][safi] = 1;
- bgp_announce_route(peer, afi, safi);
+ bgp_announce_route(peer, afi, safi,
+ false);
}
} else {
peer->afc_recv[afi][safi] = 0;
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s [Event] BGP error %d on fd %d",
- peer->host, peer->fd, code);
+ peer->host, code, peer->fd);
/* Closed connection or error on the socket */
if (peer_established(peer)) {
/* Update SRv6 SID */
if (attr->srv6_l3vpn) {
extra = bgp_path_info_extra_get(pi);
- if (sid_diff(&extra->sid[0], &attr->srv6_l3vpn->sid)) {
- sid_copy(&extra->sid[0],
+ if (sid_diff(&extra->sid[0].sid,
+ &attr->srv6_l3vpn->sid)) {
+ sid_copy(&extra->sid[0].sid,
&attr->srv6_l3vpn->sid);
extra->num_sids = 1;
+
+ extra->sid[0].loc_block_len = 0;
+ extra->sid[0].loc_node_len = 0;
+ extra->sid[0].func_len = 0;
+ extra->sid[0].arg_len = 0;
+
+ if (attr->srv6_l3vpn->loc_block_len != 0) {
+ extra->sid[0].loc_block_len =
+ attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len =
+ attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len =
+ attr->srv6_l3vpn->arg_len;
+ }
+
+ /*
+ * draft-ietf-bess-srv6-services-07
+ * The part of SRv6 SID may be encoded as MPLS
+ * Label for the efficient packing.
+ */
+ if (attr->srv6_l3vpn->transposition_len != 0)
+ transpose_sid(
+ &extra->sid[0].sid,
+ decode_label(label),
+ attr->srv6_l3vpn
+ ->transposition_offset,
+ attr->srv6_l3vpn
+ ->transposition_len);
}
} else if (attr->srv6_vpn) {
extra = bgp_path_info_extra_get(pi);
- if (sid_diff(&extra->sid[0], &attr->srv6_vpn->sid)) {
- sid_copy(&extra->sid[0], &attr->srv6_vpn->sid);
+ if (sid_diff(&extra->sid[0].sid,
+ &attr->srv6_vpn->sid)) {
+ sid_copy(&extra->sid[0].sid,
+ &attr->srv6_vpn->sid);
extra->num_sids = 1;
}
}
if (safi == SAFI_MPLS_VPN) {
extra = bgp_path_info_extra_get(new);
if (attr->srv6_l3vpn) {
- sid_copy(&extra->sid[0], &attr->srv6_l3vpn->sid);
+ sid_copy(&extra->sid[0].sid, &attr->srv6_l3vpn->sid);
extra->num_sids = 1;
+
+ extra->sid[0].loc_block_len =
+ attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len = attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len = attr->srv6_l3vpn->arg_len;
+
+ /*
+ * draft-ietf-bess-srv6-services-07
+ * The part of SRv6 SID may be encoded as MPLS Label for
+ * the efficient packing.
+ */
+ if (attr->srv6_l3vpn->transposition_len != 0)
+ transpose_sid(
+ &extra->sid[0].sid, decode_label(label),
+ attr->srv6_l3vpn->transposition_offset,
+ attr->srv6_l3vpn->transposition_len);
} else if (attr->srv6_vpn) {
- sid_copy(&extra->sid[0], &attr->srv6_vpn->sid);
+ sid_copy(&extra->sid[0].sid, &attr->srv6_vpn->sid);
extra->num_sids = 1;
}
}
* bgp_announce_route
*
* *Triggers* announcement of routes of a given AFI/SAFI to a peer.
+ *
+ * if force is true we will force an update even if the update
+ * limiting code is attempted to kick in.
*/
-void bgp_announce_route(struct peer *peer, afi_t afi, safi_t safi)
+void bgp_announce_route(struct peer *peer, afi_t afi, safi_t safi, bool force)
{
struct peer_af *paf;
struct update_subgroup *subgrp;
if (!subgrp || paf->t_announce_route)
return;
+ if (force)
+ SET_FLAG(subgrp->sflags, SUBGRP_STATUS_FORCE_UPDATES);
+
/*
* Start a timer to stagger/delay the announce. This serves
* two purposes - announcement can potentially be combined for
safi_t safi;
FOREACH_AFI_SAFI (afi, safi)
- bgp_announce_route(peer, afi, safi);
+ bgp_announce_route(peer, afi, safi, false);
}
/* Flag or unflag bgp_dest to determine whether it should be treated by
table->soft_reconfig_peers,
peer);
bgp_announce_route(peer, table->afi,
- table->safi);
+ table->safi, false);
if (list_isempty(
table->soft_reconfig_peers)) {
list_delete(
*/
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);
+ bgp_announce_route(peer, table->afi, table->safi, false);
}
list_delete(&table->soft_reconfig_peers);
void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
const union g_addr *nexthop, ifindex_t ifindex,
enum nexthop_types_t nhtype, uint8_t distance,
- uint32_t metric, uint8_t type,
- unsigned short instance, route_tag_t tag)
+ enum blackhole_type bhtype, uint32_t metric,
+ uint8_t type, unsigned short instance,
+ route_tag_t tag)
{
struct bgp_path_info *new;
struct bgp_path_info *bpi;
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
break;
}
+ attr.bh_type = bhtype;
break;
}
+ attr.nh_type = nhtype;
attr.nh_ifindex = ifindex;
attr.med = metric;
vty_out(vty, "\n");
if (safi == SAFI_EVPN) {
- struct bgp_path_es_info *path_es_info = NULL;
-
if (bgp_evpn_is_esi_valid(&attr->esi)) {
/* XXX - add these params to the json out */
vty_out(vty, "%*s", 20, " ");
esi_to_str(&attr->esi, esi_buf,
sizeof(esi_buf)));
- if (path->extra && path->extra->mh_info)
- path_es_info =
- path->extra->mh_info->es_info;
-
- if (path_es_info && path_es_info->es)
- vty_out(vty, " VNI: %u",
- path_es_info->vni);
vty_out(vty, "\n");
}
if (attr->flag &
vty_out(vty, "notag/%d", label);
vty_out(vty, "\n");
}
- }
+ } else if (!json)
+ vty_out(vty, "\n");
}
void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
/* Remote SID */
if (path->extra && path->extra->num_sids > 0 && safi != SAFI_EVPN) {
- inet_ntop(AF_INET6, &path->extra->sid, buf, sizeof(buf));
+ inet_ntop(AF_INET6, &path->extra->sid[0].sid, buf, sizeof(buf));
if (json_paths)
json_object_string_add(json_path, "remoteSid", buf);
else
path.attr = &dummy_attr;
ret = route_map_apply(rmap, dest_p, &path);
+ bgp_attr_flush(&dummy_attr);
if (ret == RMAP_DENYMATCH)
continue;
}
return CMD_WARNING;
}
+ /* Labeled-unicast routes live in the unicast table. */
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
table = bgp->rib[afi][safi];
/* use MPLS and ENCAP specific shows until they are merged */
if (safi == SAFI_MPLS_VPN) {
output_arg, use_json,
1, NULL, NULL);
}
- /* labeled-unicast routes live in the unicast table */
- else if (safi == SAFI_LABELED_UNICAST)
- safi = SAFI_UNICAST;
return bgp_show_table(vty, bgp, safi, table, type, output_arg, NULL, 1,
NULL, NULL, &json_header_depth, show_flags,
DEFUN (show_ip_bgp_neighbor_received_prefix_filter,
show_ip_bgp_neighbor_received_prefix_filter_cmd,
- "show [ip] bgp [<ipv4|ipv6> [unicast]] neighbors <A.B.C.D|X:X::X:X|WORD> received prefix-filter [json]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [<ipv4|ipv6> [unicast]] neighbors <A.B.C.D|X:X::X:X|WORD> received prefix-filter [json]",
SHOW_STR
IP_STR
BGP_STR
+ BGP_INSTANCE_HELP_STR
"Address Family\n"
"Address Family\n"
"Address Family modifier\n"
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
char *peerstr = NULL;
-
char name[BUFSIZ];
- union sockunion su;
struct peer *peer;
- int count, ret;
-
+ int count;
int idx = 0;
+ struct bgp *bgp = NULL;
+ bool uj = use_json(argc, argv);
+
+ if (uj)
+ argc--;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
- /* show [ip] bgp */
- if (argv_find(argv, argc, "ip", &idx))
- afi = AFI_IP;
- /* [<ipv4|ipv6> [unicast]] */
- if (argv_find(argv, argc, "ipv4", &idx))
- afi = AFI_IP;
- if (argv_find(argv, argc, "ipv6", &idx))
- afi = AFI_IP6;
/* neighbors <A.B.C.D|X:X::X:X|WORD> */
argv_find(argv, argc, "neighbors", &idx);
peerstr = argv[++idx]->arg;
- bool uj = use_json(argc, argv);
-
- ret = str2sockunion(peerstr, &su);
- if (ret < 0) {
- peer = peer_lookup_by_conf_if(NULL, peerstr);
- if (!peer) {
- if (uj)
- vty_out(vty, "{}\n");
- else
- vty_out(vty,
- "%% Malformed address or name: %s\n",
- peerstr);
- return CMD_WARNING;
- }
- } else {
- peer = peer_lookup(NULL, &su);
- if (!peer) {
- if (uj)
- vty_out(vty, "{}\n");
- else
- vty_out(vty, "No peer\n");
- return CMD_WARNING;
- }
- }
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer)
+ return CMD_WARNING;
snprintf(name, sizeof(name), "%s.%d.%d", peer->host, afi, safi);
count = prefix_bgp_show_prefix_list(NULL, afi, name, uj);
struct bgp_path_evpn_nh_info *nh_info;
};
+struct bgp_sid_info {
+ struct in6_addr sid;
+ uint8_t loc_block_len;
+ uint8_t loc_node_len;
+ uint8_t func_len;
+ uint8_t arg_len;
+};
+
/* Ancillary information to struct bgp_path_info,
* used for uncommonly used data (aggregation, MPLS, etc.)
* and lazily allocated to save memory.
#define BGP_EVPN_MACIP_TYPE_SVI_IP (1 << 0)
/* SRv6 SID(s) for SRv6-VPN */
- struct in6_addr sid[BGP_MAX_SIDS];
+ struct bgp_sid_info sid[BGP_MAX_SIDS];
uint32_t num_sids;
#ifdef ENABLE_BGP_VNC
extern void bgp_route_init(void);
extern void bgp_route_finish(void);
extern void bgp_cleanup_routes(struct bgp *);
-extern void bgp_announce_route(struct peer *, afi_t, safi_t);
+extern void bgp_announce_route(struct peer *peer, afi_t afi, safi_t safi,
+ bool force);
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_redistribute_add(struct bgp *bgp, struct prefix *p,
const union g_addr *nexthop, ifindex_t ifindex,
enum nexthop_types_t nhtype, uint8_t distance,
- uint32_t metric, uint8_t type,
- unsigned short instance, route_tag_t tag);
+ enum blackhole_type bhtype, uint32_t metric,
+ uint8_t type, unsigned short instance,
+ route_tag_t tag);
extern void bgp_redistribute_delete(struct bgp *, struct prefix *, uint8_t,
unsigned short);
extern void bgp_redistribute_withdraw(struct bgp *, afi_t, int, unsigned short);
char *argstr;
const char *condition;
route_map_event_t event;
+ int ret;
/* Add configuration. */
rhc = nb_running_get_entry(args->dnode, NULL, true);
rhc->rhc_event = RMAP_EVENT_ECLIST_DELETED;
}
- bgp_route_match_add(rhc->rhc_rmi, rhc->rhc_rule, argstr, event,
- args->errmsg, args->errmsg_len);
+ ret = bgp_route_match_add(rhc->rhc_rmi, rhc->rhc_rule, argstr, event,
+ args->errmsg, args->errmsg_len);
+ /*
+ * At this point if this is not a successful operation
+ * bgpd is about to crash. Let's just cut to the
+ * chase and do it.
+ */
+ assert(ret == RMAP_COMPILE_SUCCESS);
if (argstr != value)
XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr);
const char *asn;
const char *addr;
char *argstr;
+ int ret;
/* Add configuration. */
rhc = nb_running_get_entry(args->dnode, NULL, true);
rhc->rhc_rule = "aggregator as";
rhc->rhc_event = RMAP_EVENT_SET_DELETED;
- generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, argstr,
- args->errmsg, args->errmsg_len);
+ ret = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, argstr, args->errmsg,
+ args->errmsg_len);
+ /*
+ * At this point if this is not a successful operation
+ * bgpd is about to crash. Let's just cut to the
+ * chase and do it.
+ */
+ assert(ret == CMD_SUCCESS);
+
XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr);
}
/*
enum ecommunity_lb_type lb_type;
char str[VTY_BUFSIZ];
uint16_t bandwidth;
+ int ret;
/* Add configuration. */
rhc = nb_running_get_entry(args->dnode, NULL, true);
if (yang_dnode_get_bool(args->dnode, "./two-octet-as-specific"))
strlcat(str, " non-transitive", sizeof(str));
- generic_set_add(rhc->rhc_rmi,
- "extcommunity bandwidth", str,
- args->errmsg, args->errmsg_len);
+ ret = generic_set_add(rhc->rhc_rmi, "extcommunity bandwidth", str,
+ args->errmsg, args->errmsg_len);
+ /*
+ * At this point if this is not a successful operation
+ * bgpd is about to crash. Let's just cut to the
+ * chase and do it.
+ */
+ assert(ret == CMD_SUCCESS);
}
/*
#include "bgpd/bgpd.h"
#include "bgpd/bgp_table.h"
#include "bgp_addpath.h"
+#include "bgp_trace.h"
void bgp_table_lock(struct bgp_table *rt)
{
}
}
+/*
+ * bgp_dest_unlock_node
+ */
+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));
+}
+
+/*
+ * bgp_dest_lock_node
+ */
+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);
+}
+
+/*
+ * bgp_dest_get_prefix_str
+ */
+const char *bgp_dest_get_prefix_str(struct bgp_dest *dest)
+{
+ const struct prefix *p = NULL;
+ char str[PREFIX_STRLEN] = {0};
+
+ p = bgp_dest_get_prefix(dest);
+ if (p)
+ return prefix2str(p, str, sizeof(str));
+
+ return NULL;
+}
+
/*
* bgp_node_create
*/
#include "linklist.h"
#include "bgpd.h"
#include "bgp_advertise.h"
-#include "bgpd/bgp_trace.h"
struct bgp_table {
/* table belongs to this instance */
extern void bgp_table_lock(struct bgp_table *);
extern void bgp_table_unlock(struct bgp_table *);
extern void bgp_table_finish(struct bgp_table **);
+extern void bgp_dest_unlock_node(struct bgp_dest *dest);
+extern struct bgp_dest *bgp_dest_lock_node(struct bgp_dest *dest);
+extern const char *bgp_dest_get_prefix_str(struct bgp_dest *dest);
/*
return bgp_dest_from_rnode(rn);
}
-/*
- * bgp_dest_unlock_node
- */
-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));
-}
-
/*
* bgp_table_top_nolock
*
return bgp_dest_from_rnode(rn);
}
-/*
- * bgp_dest_lock_node
- */
-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);
-}
-
/*
* bgp_node_match
*/
#include <lttng/tracepoint.h>
#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
#include "lib/stream.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
+
/* clang-format off */
TRACEPOINT_LOGLEVEL(frr_bgp, bmp_process, TRACE_DEBUG)
+/*
+ * bgp_dest_lock/bgp_dest_unlock
+ */
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bgp_dest_lock,
+ TP_ARGS(struct bgp_dest *, dest),
+ TP_FIELDS(
+ ctf_string(prefix, bgp_dest_get_prefix_str(dest))
+ ctf_integer(unsigned int, count, bgp_dest_get_lock_count(dest))
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, bgp_dest_lock, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bgp_dest_unlock,
+ TP_ARGS(struct bgp_dest *, dest),
+ TP_FIELDS(
+ ctf_string(prefix, bgp_dest_get_prefix_str(dest))
+ ctf_integer(unsigned int, count, bgp_dest_get_lock_count(dest))
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, bgp_dest_unlock, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mac_ip_zsend,
+ TP_ARGS(int, add, struct bgpevpn *, vpn,
+ const struct prefix_evpn *, pfx,
+ struct in_addr, vtep, esi_t *, esi),
+ TP_FIELDS(
+ ctf_string(action, add ? "add" : "del")
+ ctf_integer(vni_t, vni, vpn->vni)
+ ctf_array(unsigned char, mac, &pfx->prefix.macip_addr.mac,
+ sizeof(struct ethaddr))
+ ctf_array(unsigned char, ip, &pfx->prefix.macip_addr.ip,
+ sizeof(struct ipaddr))
+ ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr)
+ ctf_array(unsigned char, esi, esi, sizeof(esi_t))
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mac_ip_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_bum_vtep_zsend,
+ TP_ARGS(int, add, struct bgpevpn *, vpn,
+ const struct prefix_evpn *, pfx),
+ TP_FIELDS(
+ ctf_string(action, add ? "add" : "del")
+ ctf_integer(vni_t, vni, vpn->vni)
+ ctf_integer_network_hex(unsigned int, vtep,
+ pfx->prefix.imet_addr.ip.ipaddr_v4.s_addr)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_bum_vtep_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_vtep_zsend,
+ TP_ARGS(bool, add, struct bgp_evpn_es *, es,
+ struct bgp_evpn_es_vtep *, es_vtep),
+ TP_FIELDS(
+ ctf_string(action, add ? "add" : "del")
+ ctf_string(esi, es->esi_str)
+ ctf_string(vtep, es_vtep->vtep_str)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_vtep_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_nhg_zsend,
+ TP_ARGS(bool, add, bool, type_v4, uint32_t, nhg_id,
+ struct bgp_evpn_es_vrf *, es_vrf),
+ TP_FIELDS(
+ ctf_string(action, add ? "add" : "del")
+ ctf_string(type, type_v4 ? "v4" : "v6")
+ ctf_integer(unsigned int, nhg, nhg_id)
+ ctf_string(esi, es_vrf->es->esi_str)
+ ctf_integer(int, vrf, es_vrf->bgp_vrf->vrf_id)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_nhg_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_nh_zsend,
+ TP_ARGS(uint32_t, nhg_id, struct bgp_evpn_es_vtep *, vtep,
+ struct bgp_evpn_es_vrf *, es_vrf),
+ TP_FIELDS(
+ ctf_integer(unsigned int, nhg, nhg_id)
+ ctf_string(vtep, vtep->vtep_str)
+ ctf_integer(int, svi, es_vrf->bgp_vrf->l3vni_svi_ifindex)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_nh_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_nh_rmac_zsend,
+ TP_ARGS(bool, add, struct bgp_evpn_nh *, nh),
+ TP_FIELDS(
+ ctf_string(action, add ? "add" : "del")
+ ctf_integer(int, vrf, nh->bgp_vrf->vrf_id)
+ ctf_string(nh, nh->nh_str)
+ ctf_array(unsigned char, rmac, &nh->rmac,
+ sizeof(struct ethaddr))
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_nh_rmac_zsend, TRACE_INFO)
/* clang-format on */
#include <lttng/tracepoint-event.h>
bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
+ /* make coverity happy */
+ assert(attr.aspath);
+
attr.local_pref = bgp->default_local_pref;
if ((afi == AFI_IP6) || peer_cap_enhe(peer, afi, safi)) {
}
if (peer->default_rmap[afi][safi].name) {
+ struct bgp_path_info tmp_pi = {0};
+
+ tmp_pi.peer = bgp->peer_self;
+
SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT);
/* Iterate over the RIB to see if we can announce
for (pi = bgp_dest_get_bgp_path_info(dest); pi;
pi = pi->next) {
- struct attr tmp_attr;
- struct bgp_path_info tmp_pi;
- struct bgp_path_info_extra tmp_pie;
+ struct attr tmp_attr = attr;
- tmp_attr = *pi->attr;
- tmp_attr.aspath = attr.aspath;
+ tmp_pi.attr = &tmp_attr;
- prep_for_rmap_apply(&tmp_pi, &tmp_pie, dest, pi,
- pi->peer, &tmp_attr);
-
- ret = route_map_apply(
+ ret = route_map_apply_ext(
peer->default_rmap[afi][safi].map,
- bgp_dest_get_prefix(dest), &tmp_pi);
+ bgp_dest_get_prefix(dest), pi, &tmp_pi);
if (ret == RMAP_DENYMATCH) {
- /* The aspath belongs to 'attr' */
- tmp_attr.aspath = NULL;
- bgp_attr_flush(&tmp_attr);
+ bgp_attr_undup(&tmp_attr, &attr);
continue;
} else {
new_attr = bgp_attr_intern(&tmp_attr);
subgroup_default_update_packet(subgrp, new_attr, from);
}
}
+
+ aspath_unintern(&attr.aspath);
}
/*
return "Unknown";
}
+/* unset srv6 locator */
+static int bgp_srv6_locator_unset(struct bgp *bgp)
+{
+ int ret;
+ struct listnode *node, *nnode;
+ struct prefix_ipv6 *chunk;
+ struct bgp_srv6_function *func;
+ struct bgp *bgp_vrf;
+ struct in6_addr *tovpn_sid;
+
+ /* release chunk notification via ZAPI */
+ ret = bgp_zebra_srv6_manager_release_locator_chunk(
+ bgp->srv6_locator_name);
+ if (ret < 0)
+ return -1;
+
+ /* refresh chunks */
+ for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk))
+ listnode_delete(bgp->srv6_locator_chunks, chunk);
+
+ /* refresh functions */
+ for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func))
+ listnode_delete(bgp->srv6_functions, func);
+
+ /* refresh tovpn_sid */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
+ if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ /* refresh vpnv4 tovpn_sid */
+ tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid;
+ if (tovpn_sid)
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid);
+
+ /* refresh vpnv6 tovpn_sid */
+ tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid;
+ if (tovpn_sid)
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid);
+ }
+
+ /* update vpn bgp processes */
+ vpn_leak_postchange_all();
+
+ /* clear locator name */
+ memset(bgp->srv6_locator_name, 0, sizeof(bgp->srv6_locator_name));
+
+ return 0;
+}
+
/* Utility function to get address family from current node. */
afi_t bgp_node_afi(struct vty *vty)
{
if ((afi == AFI_UNSPEC) && (safi == SAFI_UNSPEC)) {
afi_t tmp_afi;
safi_t tmp_safi;
+ enum bgp_af_index index;
+
+ for (index = BGP_AF_START; index < BGP_AF_MAX; index++) {
+ paf = peer->peer_af_array[index];
+ if (!paf)
+ continue;
- FOREACH_AFI_SAFI (tmp_afi, tmp_safi) {
- paf = peer_af_find(peer, tmp_afi, tmp_safi);
if (paf && paf->subgroup)
SET_FLAG(paf->subgroup->sflags,
SUBGRP_STATUS_FORCE_UPDATES);
+ tmp_afi = paf->afi;
+ tmp_safi = paf->safi;
if (!peer->afc[tmp_afi][tmp_safi])
continue;
return CMD_SUCCESS;
}
+DEFUN (no_bgp_segment_routing_srv6,
+ no_bgp_segment_routing_srv6_cmd,
+ "no segment-routing srv6",
+ NO_STR
+ "Segment-Routing configuration\n"
+ "Segment-Routing SRv6 configuration\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (strlen(bgp->srv6_locator_name) > 0)
+ if (bgp_srv6_locator_unset(bgp) < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ bgp->srv6_enabled = false;
+ return CMD_SUCCESS;
+}
+
DEFPY (bgp_srv6_locator,
bgp_srv6_locator_cmd,
"locator NAME$name",
return CMD_SUCCESS;
}
+DEFPY (no_bgp_srv6_locator,
+ no_bgp_srv6_locator_cmd,
+ "no locator NAME$name",
+ NO_STR
+ "Specify SRv6 locator\n"
+ "Specify SRv6 locator\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* when locator isn't configured, do nothing */
+ if (strlen(bgp->srv6_locator_name) < 1)
+ return CMD_SUCCESS;
+
+ /* name validation */
+ if (strcmp(name, bgp->srv6_locator_name) != 0) {
+ vty_out(vty, "%% No srv6 locator is configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* unset locator */
+ if (bgp_srv6_locator_unset(bgp) < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return CMD_SUCCESS;
+}
+
DEFPY (show_bgp_srv6,
show_bgp_srv6_cmd,
"show bgp segment-routing srv6",
}
}
-/* If the peer's description includes whitespaces
- * then return the first occurrence. Also strip description
- * to the given size if needed.
- */
+/* Strip peer's description to the given size. */
static char *bgp_peer_description_stripped(char *desc, uint32_t size)
{
static char stripped[BUFSIZ];
- char *pnt;
uint32_t len = size > strlen(desc) ? strlen(desc) : size;
- pnt = strchr(desc, ' ');
- if (pnt)
- len = size > (uint32_t)(pnt - desc) ? (uint32_t)(pnt - desc)
- : size;
-
strlcpy(stripped, desc, len + 1);
return stripped;
return false;
}
-/* Show BGP peer's summary information. */
+/* Show BGP peer's summary information.
+ *
+ * Peer's description is stripped according to if `wide` option is given
+ * or not.
+ *
+ * When adding new columns to `show bgp summary` output, please make
+ * sure `Desc` is the lastest column to show because it can contain
+ * whitespaces and the whole output will be tricky.
+ */
static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
struct peer *fpeer, int as_type, as_t as,
uint16_t show_flags)
vty_out(vty, " %8u", 0);
}
+ /* Make sure `Desc` column is the lastest in
+ * the output.
+ */
if (peer->desc)
vty_out(vty, " %s",
bgp_peer_description_stripped(
/* srv6 commands */
install_element(VIEW_NODE, &show_bgp_srv6_cmd);
install_element(BGP_NODE, &bgp_segment_routing_srv6_cmd);
+ install_element(BGP_NODE, &no_bgp_segment_routing_srv6_cmd);
install_element(BGP_SRV6_NODE, &bgp_srv6_locator_cmd);
+ install_element(BGP_SRV6_NODE, &no_bgp_srv6_locator_cmd);
install_element(BGP_IPV4_NODE, &af_sid_vpn_export_cmd);
install_element(BGP_IPV6_NODE, &af_sid_vpn_export_cmd);
}
static int bgp_interface_address_delete(ZAPI_CALLBACK_ARGS)
{
+ struct listnode *node, *nnode;
struct connected *ifc;
+ struct peer *peer;
struct bgp *bgp;
+ struct prefix *addr;
bgp = bgp_lookup_by_vrf_id(vrf_id);
bgp_connected_delete(bgp, ifc);
}
+ addr = ifc->address;
+
+ if (bgp) {
+ /*
+ * When we are using the v6 global as part of the peering
+ * nexthops and we are removing it, then we need to
+ * clear the peer data saved for that nexthop and
+ * cause a re-announcement of the route. Since
+ * we do not want the peering to bounce.
+ */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ afi_t afi;
+ safi_t safi;
+
+ if (addr->family == AF_INET)
+ continue;
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&addr->u.prefix6)
+ && memcmp(&peer->nexthop.v6_global,
+ &addr->u.prefix6, 16)
+ == 0) {
+ memset(&peer->nexthop.v6_global, 0, 16);
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_announce_route(peer, afi, safi,
+ true);
+ }
+ }
+ }
+
connected_free(&ifc);
return 0;
static int zebra_read_route(ZAPI_CALLBACK_ARGS)
{
enum nexthop_types_t nhtype;
+ enum blackhole_type bhtype = BLACKHOLE_UNSPEC;
struct zapi_route api;
- union g_addr nexthop;
+ union g_addr nexthop = {};
ifindex_t ifindex;
int add, i;
struct bgp *bgp;
&& IN6_IS_ADDR_LINKLOCAL(&api.prefix.u.prefix6))
return 0;
- nexthop = api.nexthops[0].gate;
ifindex = api.nexthops[0].ifindex;
nhtype = api.nexthops[0].type;
+ /* api_nh structure has union of gate and bh_type */
+ if (nhtype == NEXTHOP_TYPE_BLACKHOLE) {
+ /* bh_type is only applicable if NEXTHOP_TYPE_BLACKHOLE*/
+ bhtype = api.nexthops[0].bh_type;
+ } else
+ nexthop = api.nexthops[0].gate;
+
add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD);
if (add) {
/*
/* Now perform the add/update. */
bgp_redistribute_add(bgp, &api.prefix, &nexthop, ifindex,
- nhtype, api.distance, api.metric, api.type,
- api.instance, api.tag);
+ nhtype, bhtype, api.distance, api.metric,
+ api.type, api.instance, api.tag);
} else {
bgp_redistribute_delete(bgp, &api.prefix, api.type,
api.instance);
* a VRF (which are programmed as onlink on l3-vni SVI) as well as
* connected routes leaked into a VRF.
*/
- if (is_evpn) {
-
+ if (attr->nh_type == NEXTHOP_TYPE_BLACKHOLE) {
+ api_nh->type = attr->nh_type;
+ api_nh->bh_type = attr->bh_type;
+ } else if (is_evpn) {
/*
* If the nexthop is EVPN overlay index gateway IP,
* treat the nexthop as NEXTHOP_TYPE_IPV4
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) {
+ } else if (nh_othervrf && api_nh->gate.ipv4.s_addr == INADDR_ANY) {
api_nh->type = NEXTHOP_TYPE_IFINDEX;
api_nh->ifindex = attr->nh_ifindex;
} else
attr = pi->attr;
api_nh->vrf_id = nh_bgp->vrf_id;
- if (is_evpn) {
-
+ if (attr->nh_type == NEXTHOP_TYPE_BLACKHOLE) {
+ api_nh->type = attr->nh_type;
+ api_nh->bh_type = attr->bh_type;
+ } else if (is_evpn) {
/*
* If the nexthop is EVPN overlay index gateway IP,
* treat the nexthop as NEXTHOP_TYPE_IPV4
api_nh->ifindex = 0;
}
}
- if (nexthop)
+ /* api_nh structure has union of gate and bh_type */
+ if (nexthop && api_nh->type != NEXTHOP_TYPE_BLACKHOLE)
api_nh->gate.ipv6 = *nexthop;
return true;
struct zapi_nexthop *api_nh;
int nh_family;
unsigned int valid_nh_count = 0;
- int has_valid_label = 0;
bool allow_recursion = false;
- int has_valid_sid = 0;
uint8_t distance;
struct peer *peer;
struct bgp_path_info *mpinfo;
if (mpinfo->extra
&& bgp_is_valid_label(&mpinfo->extra->label[0])
&& !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) {
- has_valid_label = 1;
label = label_pton(&mpinfo->extra->label[0]);
SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
api_nh->weight = nh_weight;
- if (mpinfo->extra
- && !sid_zero(&mpinfo->extra->sid[0])
+ if (mpinfo->extra && !sid_zero(&mpinfo->extra->sid[0].sid)
&& !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) {
- has_valid_sid = 1;
- memcpy(&api_nh->seg6_segs, &mpinfo->extra->sid[0],
+ memcpy(&api_nh->seg6_segs, &mpinfo->extra->sid[0].sid,
sizeof(api_nh->seg6_segs));
+
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6);
}
valid_nh_count++;
}
- if (has_valid_sid && !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)))
- SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6);
-
is_add = (valid_nh_count || nhg_id) ? true : false;
if (is_add && CHECK_FLAG(bm->flags, BM_FLAG_SEND_EXTRA_DATA_TO_ZEBRA)) {
label_buf[0] = '\0';
eth_buf[0] = '\0';
segs_buf[0] = '\0';
- if (has_valid_label
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)
&& !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))
snprintf(label_buf, sizeof(label_buf),
"label %u", api_nh->labels[0]);
- if (has_valid_sid
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6)
&& !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) {
inet_ntop(AF_INET6, &api_nh->seg6_segs,
sid_buf, sizeof(sid_buf));
vpn_leak_postchange_all();
}
+static int bgp_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS)
+{
+ struct srv6_locator loc = {};
+ struct bgp *bgp = bgp_get_default();
+ const char *loc_name = bgp->srv6_locator_name;
+
+ if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0)
+ return -1;
+
+ if (!bgp || !bgp->srv6_enabled)
+ return 0;
+
+ if (bgp_zebra_srv6_manager_get_locator_chunk(loc_name) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS)
+{
+ struct srv6_locator loc = {};
+ struct bgp *bgp = bgp_get_default();
+ struct listnode *node, *nnode;
+ struct prefix_ipv6 *chunk;
+ struct bgp_srv6_function *func;
+ struct bgp *bgp_vrf;
+ struct in6_addr *tovpn_sid;
+ struct prefix_ipv6 tmp_prefi;
+
+ if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0)
+ return -1;
+
+ // refresh chunks
+ for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk))
+ if (prefix_match((struct prefix *)&loc.prefix,
+ (struct prefix *)chunk))
+ listnode_delete(bgp->srv6_locator_chunks, chunk);
+
+ // refresh functions
+ for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) {
+ tmp_prefi.family = AF_INET6;
+ tmp_prefi.prefixlen = 128;
+ tmp_prefi.prefix = func->sid;
+ if (prefix_match((struct prefix *)&loc.prefix,
+ (struct prefix *)&tmp_prefi))
+ listnode_delete(bgp->srv6_functions, func);
+ }
+
+ // refresh tovpn_sid
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
+ if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ // refresh vpnv4 tovpn_sid
+ tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid;
+ if (tovpn_sid) {
+ tmp_prefi.family = AF_INET6;
+ tmp_prefi.prefixlen = 128;
+ tmp_prefi.prefix = *tovpn_sid;
+ if (prefix_match((struct prefix *)&loc.prefix,
+ (struct prefix *)&tmp_prefi))
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid);
+ }
+
+ // refresh vpnv6 tovpn_sid
+ tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid;
+ if (tovpn_sid) {
+ tmp_prefi.family = AF_INET6;
+ tmp_prefi.prefixlen = 128;
+ tmp_prefi.prefix = *tovpn_sid;
+ if (prefix_match((struct prefix *)&loc.prefix,
+ (struct prefix *)&tmp_prefi))
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid);
+ }
+ }
+
+ vpn_leak_postchange_all();
+ return 0;
+}
+
void bgp_zebra_init(struct thread_master *master, unsigned short instance)
{
zclient_num_connects = 0;
zclient->iptable_notify_owner = iptable_notify_owner;
zclient->route_notify_owner = bgp_zebra_route_notify_owner;
zclient->instance = instance;
+ zclient->srv6_locator_add = bgp_zebra_process_srv6_locator_add;
+ zclient->srv6_locator_delete = bgp_zebra_process_srv6_locator_delete;
zclient->process_srv6_locator_chunk =
bgp_zebra_process_srv6_locator_chunk;
}
{
return srv6_manager_get_locator_chunk(zclient, name);
}
+
+int bgp_zebra_srv6_manager_release_locator_chunk(const char *name)
+{
+ return srv6_manager_release_locator_chunk(zclient, name);
+}
extern int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type);
extern int bgp_zebra_stale_timer_update(struct bgp *bgp);
extern int bgp_zebra_srv6_manager_get_locator_chunk(const char *name);
+extern int bgp_zebra_srv6_manager_release_locator_chunk(const char *name);
#endif /* _QUAGGA_BGP_ZEBRA_H */
CAPABILITY_ACTION_SET);
if (peer->afc_recv[afi][safi]) {
peer->afc_nego[afi][safi] = 1;
- bgp_announce_route(peer, afi, safi);
+ bgp_announce_route(peer, afi, safi,
+ false);
}
} else {
peer->last_reset = PEER_DOWN_AF_ACTIVATE;
SUBGRP_STATUS_FORCE_UPDATES);
update_group_adjust_peer(paf);
- bgp_announce_route(peer, afi, safi);
+ bgp_announce_route(peer, afi, safi, false);
}
}
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);
+ bgp_announce_route(peer, afi, safi, false);
}
/* Skip peer-group mechanics for regular peers. */
update_group_adjust_peer(
peer_af_find(member, afi, safi));
bgp_default_originate(member, afi, safi, 0);
- bgp_announce_route(member, afi, safi);
+ bgp_announce_route(member, afi, safi, false);
}
}
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);
+ bgp_announce_route(peer, afi, safi, false);
}
/* Skip peer-group mechanics for regular peers. */
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);
+ bgp_announce_route(member, afi, safi, false);
}
}
if (outbound) {
update_group_adjust_peer(peer_af_find(peer, afi, safi));
if (peer_established(peer))
- bgp_announce_route(peer, afi, safi);
+ bgp_announce_route(peer, afi, safi, false);
} else {
if (!peer_established(peer))
return;
for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
if (access->name)
- update_group_policy_update(bgp, BGP_POLICY_FILTER_LIST,
+ update_group_policy_update(bgp,
+ BGP_POLICY_DISTRIBUTE_LIST,
access->name, 0, 0);
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
FOREACH_AFI_SAFI (afi, safi) {
UNSET_FLAG(paf->subgroup->sflags,
SUBGRP_STATUS_DEFAULT_ORIGINATE);
- bgp_announce_route(peer, afi, safi);
+ bgp_announce_route(peer, afi, safi, false);
}
if (stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) {
json_no, JSON_C_TO_STRING_PRETTY));
json_object_free(json_no);
} else
- vty_out(vty, "No such neighbor in %s\n",
- bgp->name_pretty);
+ vty_out(vty, "No such neighbor in this view/vrf\n");
return NULL;
}
*/
uint32_t tovpn_sid_index; /* unset => set to 0 */
struct in6_addr *tovpn_sid;
+ uint32_t tovpn_sid_transpose_label;
struct in6_addr *tovpn_zebra_vrf_sid_last_sent;
};
char buf[BUFSIZ];
vty_out(vty, " sid=%s",
- inet_ntop(AF_INET6, &bpi->extra->sid[0], buf,
- sizeof(buf)));
+ inet_ntop(AF_INET6, &bpi->extra->sid[0].sid,
+ buf, sizeof(buf)));
+
+ if (bpi->extra->sid[0].loc_block_len != 0) {
+ vty_out(vty, " sid_structure=[%d,%d,%d,%d]",
+ bpi->extra->sid[0].loc_block_len,
+ bpi->extra->sid[0].loc_node_len,
+ bpi->extra->sid[0].func_len,
+ bpi->extra->sid[0].arg_len);
+ }
}
}
##
AC_PREREQ([2.69])
-AC_INIT([frr], [8.1-dev], [https://github.com/frrouting/frr/issues])
+AC_INIT([frr], [8.2-dev], [https://github.com/frrouting/frr/issues])
PACKAGE_URL="https://frrouting.org/"
AC_SUBST([PACKAGE_URL])
PACKAGE_FULLNAME="FRRouting"
dnl LTTng
dnl -----
if test "$enable_lttng" = "yes"; then
- PKG_CHECK_MODULES([UST], [lttng-ust >= 2.12.0], [
+ PKG_CHECK_MODULES([UST], [lttng-ust >= 2.9.0], [
AC_DEFINE([HAVE_LTTNG], [1], [Enable LTTng support])
LTTNG=true
], [
AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket])
AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket])
AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information])
+AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information])
AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory])
AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory])
selector: "<" `selector_seq_seq` ">" `varname_token`
: "{" `selector_seq_seq` "}" `varname_token`
: "[" `selector_seq_seq` "]" `varname_token`
+ : "![" `selector_seq_seq` "]" `varname_token`
selector_seq_seq: `selector_seq_seq` "|" `selector_token_seq`
: `selector_token_seq`
selector_token_seq: `selector_token_seq` `selector_token`
provide mutual exclusion. User input matches at most one option.
- ``[square brackets]`` -- Contains sequences of tokens that can be omitted.
``[<a|b>]`` can be shortened to ``[a|b]``.
+- ``![exclamation square brackets]`` -- same as ``[square brackets]``, but
+ only allow skipping the contents if the command input starts with ``no``.
+ (For cases where the positive command needs a parameter, but the parameter
+ is optional for the negative case.)
- ``{curly|braces}`` -- similar to angle brackets, but instead of mutual
exclusion, curly braces indicate that one or more of the pipe-separated
sequences may be provided in any order.
``ip`` partially matches ``ipv6`` but exactly matches ``ip``, so ``ip`` will
win.
+Adding a CLI Node
+-----------------
+
+To add a new CLI node, you should:
+
+- define a new numerical node constant
+- define a node structure in the relevant daemon
+- call ``install_node()`` in the relevant daemon
+- define and install the new node in vtysh
+- define corresponding node entry commands in daemon and vtysh
+- add a new entry to the ``ctx_keywords`` dictionary in ``tools/frr-reload.py``
+
+Defining the numerical node constant
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Add your new node value to the enum before ``NODE_TYPE_MAX`` in
+``lib/command.h``:
+
+.. code-block:: c
+
+ enum node_type {
+ AUTH_NODE, // Authentication mode of vty interface.
+ VIEW_NODE, // View node. Default mode of vty interface.
+ [...]
+ MY_NEW_NODE,
+ NODE_TYPE_MAX, // maximum
+ };
+
+Defining a node structure
+^^^^^^^^^^^^^^^^^^^^^^^^^
+In your daemon-specific code where you define your new commands that
+attach to the new node, add a node definition:
+
+.. code-block:: c
+
+ static struct cmd_node my_new_node = {
+ .name = "my new node name",
+ .node = MY_NEW_NODE, // enum node_type lib/command.h
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(my-new-node-prompt)# ",
+ .config_write = my_new_node_config_write,
+ };
+
+You will need to define ``my_new_node_config_write(struct vty \*vty)``
+(or omit this field if you have no relevant configuration to save).
+
+Calling ``install_node()``
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+In the daemon's initialization function, before installing your new commands
+with ``install_element()``, add a call ``install_node(&my_new_node)``.
+
+Defining and installing the new node in vtysh
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The build tools automatically collect command definitions for vtysh.
+However, new nodes must be coded in vtysh specifically.
+
+In ``vtysh/vtysh.c``, define a stripped-down node structure and
+call ``install_node()``:
+
+.. code-block:: c
+
+ static struct cmd_node my_new_node = {
+ .name = "my new node name",
+ .node = MY_NEW_NODE, /* enum node_type lib/command.h */
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(my-new-node-prompt)# ",
+ };
+ [...]
+ void vtysh_init_vty(void)
+ {
+ [...]
+ install_node(&my_new_node)
+ [...]
+ }
+
+Defining corresponding node entry commands in daemon and vtysh
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The command that descends into the new node is typically programmed
+with ``VTY_PUSH_CONTEXT`` or equivalent in the daemon's CLI handler function.
+(If the CLI has been updated to use the new northbound architecture,
+``VTY_PUSH_XPATH`` is used instead.)
+
+In vtysh, you must implement a corresponding node change so that vtysh
+tracks the daemon's movement through the node tree.
+
+Although the build tools typically scan daemon code for CLI definitions
+to replicate their parsing in vtysh, the node-descent function in the
+daemon must be blocked from this replication so that a hand-coded
+skeleton can be written in ``vtysh.c``.
+
+Accordingly, use one of the ``*_NOSH`` macros such as ``DEFUN_NOSH``,
+``DEFPY_NOSH``, or ``DEFUN_YANG_NOSH`` for the daemon's node-descent
+CLI definition, and use ``DEFUNSH`` in ``vtysh.c`` for the vtysh equivalent.
+
+.. seealso:: :ref:`vtysh-special-defuns`
+
+Examples:
+
+``zebra_whatever.c``
+
+.. code-block:: c
+
+ DEFPY_NOSH(my_new_node,
+ my_new_node_cmd,
+ "my-new-node foo",
+ "New Thing\n"
+ "A foo\n")
+ {
+ [...]
+ VTY_PUSH_CONTEXT(MY_NEW_NODE, bar);
+ [...]
+ }
+
+
+``ripd_whatever.c``
+
+.. code-block:: c
+
+ DEFPY_YANG_NOSH(my_new_node,
+ my_new_node_cmd,
+ "my-new-node foo",
+ "New Thing\n"
+ "A foo\n")
+ {
+ [...]
+ VTY_PUSH_XPATH(MY_NEW_NODE, xbar);
+ [...]
+ }
+
+
+``vtysh.c``
+
+.. code-block:: c
+
+ DEFUNSH(VTYSH_ZEBRA, my_new_node,
+ my_new_node_cmd,
+ "my-new-node foo",
+ "New Thing\n"
+ "A foo\n")
+ {
+ vty->node = MY_NEW_NODE;
+ return CMD_SUCCESS;
+ }
+ [...]
+ install_element(CONFIG_NODE, &my_new_node_cmd);
+
+
+Adding a new entry to the ``ctx_keywords`` dictionary
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+In file ``tools/frr-reload.py``, the ``ctx_keywords`` dictionary
+describes the various node relationships.
+Add a new node entry at the appropriate level in this dictionary.
+
+.. code-block:: python
+
+ ctx_keywords = {
+ [...]
+ "key chain ": {
+ "key ": {}
+ },
+ [...]
+ "my-new-node": {},
+ [...]
+ }
+
+
+
Inspection & Debugging
----------------------
Generating FRR northbound bindings for C++ example:
::
+
# Install gRPC (e.g., on Ubuntu 20.04)
sudo apt-get install libgrpc++-dev libgrpc-dev
``%pNHs``: :frrfmtout:`1.2.3.4 if 15` — same as :c:func:`nexthop2str()`
+ ``%pNHcg``: :frrfmtout:`1.2.3.4` — compact gateway only
+
+ ``%pNHci``: :frrfmtout:`eth0` — compact interface only
+
.. frrfmt:: %pBD (struct bgp_dest *)
:frrfmtout:`fe80::1234/64`
Executing Tests
---------------
+Configure your sudo environment
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Topotests must be run as root. Normally this will be accomplished through the
+use of the ``sudo`` command. In order for topotests to be able to open new
+windows (either XTerm or byobu/screen/tmux windows) certain environment
+variables must be passed through the sudo command. One way to do this is to
+specify the :option:`-E` flag to ``sudo``. This will carry over most if not all
+your environment variables include ``PATH``. For example:
+
+.. code:: shell
+
+ sudo -E python3 -m pytest -s -v
+
+If you do not wish to use :option:`-E` (e.g., to avoid ``sudo`` inheriting
+``PATH``) you can modify your `/etc/sudoers` config file to specifically pass
+the environment variables required by topotests. Add the following commands to
+your ``/etc/sudoers`` config file.
+
+.. code:: shell
+
+ Defaults env_keep="TMUX"
+ Defaults env_keep+="TMUX_PANE"
+ Defaults env_keep+="STY"
+ Defaults env_keep+="DISPLAY"
+
+If there was already an ``env_keep`` configuration there be sure to use the
+``+=`` rather than ``=`` on the first line above as well.
+
+
Execute all tests in distributed test mode
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code:: shell
- py.test -s -v -nauto --dist=loadfile
+ sudo -E pytest -s -v -nauto --dist=loadfile
The above command must be executed from inside the topotests directory.
Here we see that 4 tests have failed. We an dig deeper by displaying the
captured logs and errors. First let's redisplay the results enumerated by adding
-the ``-E`` flag
+the :option:`-E` flag
.. code:: shell
be launched using that windowing program, otherwise ``xterm`` will be attempted
to launch the given programs.
+NOTE: you must run the topotest (pytest) such that your DISPLAY, STY or TMUX
+environment variables are carried over. You can do this by passing the
+:option:`-E` flag to ``sudo`` or you can modify your ``/etc/sudoers`` config to
+automatically pass that environment variable through to the ``sudo``
+environment.
+
.. _screen: https://www.gnu.org/software/screen/
.. _tmux: https://github.com/tmux/tmux/wiki
.. code:: shell
- pytest --cli-on-error all-protocol-startup
+ sudo -E pytest --cli-on-error all-protocol-startup
The debugging CLI can run shell or vtysh commands on any combination of routers
It can also open shells or vtysh in their own windows for any combination of
.. code:: shell
- pytest --vtysh=rt1,rt2 all-protocol-startup
+ sudo -E pytest --vtysh=rt1,rt2 all-protocol-startup
Debugging with GDB
""""""""""""""""""
.. code:: shell
- pytest --gdb-routers=r1 \
+ sudo -E pytest --gdb-routers=r1 \
--gdb-daemons=bgpd,zebra \
--gdb-breakpoints=nb_config_diff \
all-protocol-startup
.. code:: shell
- pytest --valgrind-memleaks all-protocol-startup
+ sudo -E pytest --valgrind-memleaks all-protocol-startup
.. _topotests_docker:
$ # Change to the top level directory of topotests.
$ cd path/to/topotests
$ # Tests must be run as root, since micronet requires it.
- $ sudo pytest
+ $ sudo -E pytest
In order to run a specific test, you can use the following command:
.. code:: shell
$ # running a specific topology
- $ sudo pytest ospf-topo1/
+ $ sudo -E pytest ospf-topo1/
$ # or inside the test folder
$ cd ospf-topo1
- $ sudo pytest # to run all tests inside the directory
- $ sudo pytest test_ospf_topo1.py # to run a specific test
+ $ sudo -E pytest # to run all tests inside the directory
+ $ sudo -E pytest test_ospf_topo1.py # to run a specific test
$ # or outside the test folder
$ cd ..
- $ sudo pytest ospf-topo1/test_ospf_topo1.py # to run a specific one
+ $ sudo -E pytest ospf-topo1/test_ospf_topo1.py # to run a specific one
The output of the tested daemons will be available at the temporary folder of
your machine:
.. code:: shell
$ # Set the environment variable to apply to a specific test...
- $ sudo env TOPOTESTS_CHECK_MEMLEAK="/tmp/memleak_report_" pytest ospf-topo1/test_ospf_topo1.py
+ $ sudo -E env TOPOTESTS_CHECK_MEMLEAK="/tmp/memleak_report_" pytest ospf-topo1/test_ospf_topo1.py
$ # ...or apply to all tests adding this line to the configuration file
$ echo 'memleak_path = /tmp/memleak_report_' >> pytest.ini
$ # You can also use your editor
- Using sleep is almost never appropriate. As an example: if the test resets the
peers in BGP, the test should look for the peers re-converging instead of just
sleeping an arbitrary amount of time and continuing on. See
- `verify_bgp_convergence` as a good example of this. In particular look at it's
- use of the `@retry` decorator. If you are having troubles figuring out what to
- look for, please do not be afraid to ask.
+ ``verify_bgp_convergence`` as a good example of this. In particular look at
+ it's use of the ``@retry`` decorator. If you are having troubles figuring out
+ what to look for, please do not be afraid to ask.
- Don't duplicate effort. There exists many protocol utility functions that can
- be found in their eponymous module under `tests/topotests/lib/` (e.g.,
- `ospf.py`)
+ be found in their eponymous module under ``tests/topotests/lib/`` (e.g.,
+ ``ospf.py``)
.. code:: shell
$ # Running your bootstraped topology
- $ sudo pytest -s --topology-only new-topo/test_new_topo.py
+ $ sudo -E pytest -s --topology-only new-topo/test_new_topo.py
$ # Running the test_template.py topology
- $ sudo pytest -s --topology-only example-test/test_template.py
+ $ sudo -E pytest -s --topology-only example-test/test_template.py
$ # Running the ospf_topo1.py topology
- $ sudo pytest -s --topology-only ospf-topo1/test_ospf_topo1.py
+ $ sudo -E pytest -s --topology-only ospf-topo1/test_ospf_topo1.py
Parameters explanation:
.. code:: shell
- frr/tests/topotests# sudo pytest -s --topology-only ospf_topo1/test_ospf_topo1.py
+ frr/tests/topotests# sudo -E pytest -s --topology-only ospf_topo1/test_ospf_topo1.py
============================= test session starts ==============================
platform linux -- Python 3.9.2, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /home/chopps/w/frr/tests/topotests, configfile: pytest.ini
"""""""""""""
Test topologies should always be bootstrapped from
-:file:`tests/topotests/example-test/test_template.py` because it contains
+:file:`tests/topotests/example_test/test_template.py` because it contains
important boilerplate code that can't be avoided, like:
Example:
.. code:: py
- # For all registered routers, load the zebra configuration file
- CWD = os.path.dirname(os.path.realpath(__file__))
- for rname, router in router_list.items():
- router.load_config(
- TopoRouter.RD_ZEBRA,
- os.path.join(CWD, '{}/zebra.conf'.format(rname))
- )
- # os.path.join() joins the CWD string with arguments adding the necessary
- # slashes ('/'). Arguments must not begin with '/'.
+ # For all routers arrange for:
+ # - starting zebra using config file from <rtrname>/zebra.conf
+ # - starting ospfd using an empty config file.
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+
- The topology definition or build function
# topology build code
...
-- pytest ``setup_module()`` and ``teardown_module()`` to start the topology
+- pytest setup/teardown fixture to start the topology and supply ``tgen``
+ argument to tests.
.. code:: py
- def setup_module(module):
+
+ @pytest.fixture(scope="module")
+ def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
tgen = Topogen(topodef, module.__name__)
# or
tgen = Topogen(build_topo, module.__name__)
- tgen.start_topology('debug')
+ ...
- def teardown_module(_m):
- tgen = get_topogen()
- tgen.stop_topology()
+ # Start and configure the router daemons
+ tgen.start_router()
-- ``__main__`` initialization code (to support running the script directly)
+ # Provide tgen as argument to each test function
+ yield tgen
-.. code:: py
+ # Teardown after last test runs
+ tgen.stop_topology()
- if __name__ == '__main__':
- sys.exit(pytest.main(["-s"]))
Requirements:
However, these rules are not applied as-is; some of them expressly collide
with established practice.
+
+Container implementations
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In particular to gain defensive coding benefits from better compiler type
+checks, there is a set of replacement container data structures to be found
+in :file:`lib/typesafe.h`. They're documented under :ref:`lists`.
+
+Unfortunately, the FRR codebase is quite large, and migrating existing code to
+use these new structures is a tedious and far-reaching process (even if it
+can be automated with coccinelle, the patches would touch whole swaths of code
+and create tons of merge conflicts for ongoing work.) Therefore, little
+existing code has been migrated.
+
+However, both **new code and refactors of existing code should use the new
+containers**. If there are any reasons this can't be done, please work to
+remove these reasons (e.g. by adding necessary features to the new containers)
+rather than falling back to the old code.
+
+In order of likelyhood of removal, these are the old containers:
+
+- :file:`nhrpd/list.*`, ``hlist_*`` ⇒ ``DECLARE_LIST``
+- :file:`nhrpd/list.*`, ``list_*`` ⇒ ``DECLARE_DLIST``
+- :file:`lib/skiplist.*`, ``skiplist_*`` ⇒ ``DECLARE_SKIPLIST``
+- :file:`lib/*_queue.h` (BSD), ``SLIST_*`` ⇒ ``DECLARE_LIST``
+- :file:`lib/*_queue.h` (BSD), ``LIST_*`` ⇒ ``DECLARE_DLIST``
+- :file:`lib/*_queue.h` (BSD), ``STAILQ_*`` ⇒ ``DECLARE_LIST``
+- :file:`lib/*_queue.h` (BSD), ``TAILQ_*`` ⇒ ``DECLARE_DLIST``
+- :file:`lib/hash.*`, ``hash_*`` ⇒ ``DECLARE_HASH``
+- :file:`lib/linklist.*`, ``list_*`` ⇒ ``DECLARE_DLIST``
+- open-coded linked lists ⇒ ``DECLARE_LIST``/``DECLARE_DLIST``
+
+
Code Formatting
---------------
When number is used for BGP community list name, the number has
special meanings. Community list number in the range from 1 and 99 is
standard community list. Community list number in the range from 100
-to 199 is expanded community list. These community lists are called
+to 500 is expanded community list. These community lists are called
as numbered community lists. On the other hand normal community lists
is called as named community lists.
This command defines a new community list. The argument to (1-99) defines
the list identifier.
-.. clicmd:: bgp community-list (100-199) permit|deny COMMUNITY
+.. clicmd:: bgp community-list (100-500) permit|deny COMMUNITY
This command defines a new expanded community list. The argument to
- (100-199) defines the list identifier.
+ (100-500) defines the list identifier.
.. _bgp-community-alias:
The ``received-routes`` keyword displays all routes belonging to this
address-family (prior to inbound policy) that were received by this peer.
+.. clicmd:: show bgp [<view|vrf> VIEWVRFNAME] [afi] [safi] neighbors PEER received prefix-filter [json]
+
+ Display Address Prefix ORFs received from this peer.
+
.. clicmd:: show bgp [afi] [safi] [all] dampening dampened-paths [wide|json]
Display paths suppressed due to dampening of the selected afi and safi
clear the Node flag that is set by default for Prefix-SIDs associated to
loopback addresses. This option is necessary to configure Anycast-SIDs.
-.. clicmd:: show isis segment-routing prefix-sids
-
- Show detailed information about all learned Segment Routing Prefix-SIDs.
-
.. clicmd:: show isis segment-routing nodes
Show detailed information about all learned Segment Routing Nodes.
.. clicmd:: area A.B.C.D nssa [no-summary]
-.. clicmd:: area (0-4294967295) nssa [no-summary]
+.. clicmd:: area (0-4294967295) nssa [no-summary] [default-information-originate [metric-type (1-2)] [metric (0-16777214)]]
Configure the area to be a NSSA (Not-So-Stubby Area).
advertisement of summaries into the area. In that case, a single Type-3 LSA
containing a default route is originated into the NSSA.
+ NSSA ABRs and ASBRs can be configured with `default-information-originate`
+ option to originate a Type-7 default route into the NSSA area. In the case
+ of NSSA ASBRs, the origination of the default route is conditioned to the
+ existence of a default route in the RIB that wasn't learned via the OSPF
+ protocol.
+
+.. clicmd:: area A.B.C.D export-list NAME
+
+.. clicmd:: area (0-4294967295) export-list NAME
+
+ Filter Type-3 summary-LSAs announced to other areas originated from intra-
+ area paths from specified area.
+
+ .. code-block:: frr
+
+ router ospf6
+ area 0.0.0.10 export-list foo
+ !
+ ipv6 access-list foo permit 2001:db8:1000::/64
+ ipv6 access-list foo deny any
+
+ With example above any intra-area paths from area 0.0.0.10 and from range
+ 2001:db8::/32 (for example 2001:db8:1::/64 and 2001:db8:2::/64) are announced
+ into other areas as Type-3 summary-LSA's, but any others (for example
+ 2001:200::/48) aren't.
+
+ This command is only relevant if the router is an ABR for the specified
+ area.
+
+.. clicmd:: area A.B.C.D import-list NAME
+
+.. clicmd:: area (0-4294967295) import-list NAME
+
+ Same as export-list, but it applies to paths announced into specified area
+ as Type-3 summary-LSAs.
+
+.. clicmd:: area A.B.C.D filter-list prefix NAME in
+
+.. clicmd:: area A.B.C.D filter-list prefix NAME out
+
+.. clicmd:: area (0-4294967295) filter-list prefix NAME in
+
+.. clicmd:: area (0-4294967295) filter-list prefix NAME out
+
+ Filtering Type-3 summary-LSAs to/from area using prefix lists. This command
+ makes sense in ABR only.
+
.. _ospf6-interface:
OSPF6 interface
argument injects the default route regardless of it being present in the
router. Metric values and route-map can also be specified optionally.
-Graceful Restart Helper
-=======================
+Graceful Restart
+================
+
+.. clicmd:: graceful-restart [grace-period (1-1800)]
+
-.. clicmd:: graceful-restart helper-only [A.B.C.D]
+ Configure Graceful Restart (RFC 5187) restarting support.
+ When enabled, the default grace period is 120 seconds.
+
+ To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf"
+ EXEC-level command needs to be issued before restarting the ospf6d daemon.
+
+.. clicmd:: graceful-restart helper enable [A.B.C.D]
Configure Graceful Restart (RFC 5187) helper support.
restarts. By default, it supports both planned and
unplanned outages.
+.. clicmd:: graceful-restart prepare ipv6 ospf
+
+
+ Initiate a graceful restart for all OSPFv3 instances configured with the
+ "graceful-restart" command. The ospf6d daemon should be restarted during
+ the instance-specific grace period, otherwise the graceful restart will fail.
+
+ This is an EXEC-level command.
+
+
.. _showing-ospf6-information:
Showing OSPF6 information
Toggle OSPFv3 ASBR debugging messages.
-.. clicmd:: debug ospf6 border-routers
+.. clicmd:: debug ospf6 border-routers {router-id [A.B.C.D] | area-id [A.B.C.D]}
- Toggle OSPFv3 border router debugging messages.
+ Toggle OSPFv3 border router debugging messages. This can be specified for a
+ router with specific Router-ID/Area-ID.
.. clicmd:: debug ospf6 flooding
the 'always' keyword is given then the default is always advertised, even
when there is no default present in the routing table.
-.. clicmd:: distribute-list NAME out (kernel|connected|static|rip|ospf
-
-
.. _ospf-distribute-list:
+.. clicmd:: distribute-list NAME out <kernel|connected|static|rip|isis|bgp|eigrp|nhrp|table|vnc|babel|openfabric>
+
Apply the access-list filter, NAME, to redistributed routes of the given
- type before allowing the routes to redistributed into OSPF
+ type before allowing the routes to be redistributed into OSPF
(:ref:`ospf redistribution <ospf-redistribute>`).
.. clicmd:: default-metric (0-16777214)
To perform a graceful shutdown, the "graceful-restart prepare ip ospf"
EXEC-level command needs to be issued before restarting the ospfd daemon.
-.. clicmd:: graceful-restart helper-only [A.B.C.D]
+.. clicmd:: graceful-restart helper enable [A.B.C.D]
Configure Graceful Restart (RFC 3623) helper support.
Show Router Capabilities PCE parameters.
-.. _debugging-ospf:
-
Segment Routing
===============
Note that so far only P2P interfaces are supported.
+.. _debugging-ospf:
+
Debugging OSPF
==============
Set the BGP AS path to prepend.
+.. clicmd:: set as-path exclude AS-NUMBER...
+
+ Drop AS-NUMBER from the BGP AS path.
+
.. clicmd:: set community COMMUNITY
Set the BGP community attribute.
This section covers a few common operational tasks and how to perform them.
+Interactive Shell
+^^^^^^^^^^^^^^^^^
+FRR offers an IOS-like interactive shell called ``vtysh`` where a user can run
+individual configuration or show commands. To get into this shell, issue the
+``vtysh`` command from either a privilege user (root, or with sudo) or a user
+account that is part of the ``frrvty`` group.
+e.g.
+
+.. code-block:: console
+
+ root@ub18:~# vtysh
+
+ Hello, this is FRRouting (version 8.1-dev).
+ Copyright 1996-2005 Kunihiro Ishiguro, et al.
+
+ ub18#
+
+.. note::
+ The default install location for vtysh is /usr/bin/vtysh
+
+
Restarting
^^^^^^^^^^
.. _zebra-sysctl:
-Expected sysctl settings
-========================
+sysctl settings
+===============
The linux kernel has a variety of sysctl's that affect it's operation as a router. This
section is meant to act as a starting point for those sysctl's that must be used in
order to provide FRR with smooth operation as a router. This section is not meant
as the full documentation for sysctl's. The operator must use the sysctl documentation
-with the linux kernel for that.
+with the linux kernel for that. The following link has helpful references to many relevant
+sysctl values: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
+
+Expected sysctl settings
+------------------------
.. option:: net.ipv4.ip_forward = 1
- This option allows the linux kernel to forward ipv4 packets incoming from one interface
- to an outgoing interface. Without this no forwarding will take place from off box packets.
+ This global option allows the linux kernel to forward (route) ipv4 packets incoming from one
+ interface to an outgoing interface. If this is set to 0, the system will not route transit
+ ipv4 packets, i.e. packets that are not sent to/from a process running on the local system.
-.. option:: net.ipv6.conf.all_forwarding=1
+.. option:: net.ipv4.conf.{all,default,<interface>}.forwarding = 1
- This option allows the linux kernel to forward ipv6 packets incoming from one interface
- to an outgoing interface. Without this no forwarding will take place from off box packets.
+ The linux kernel can selectively enable forwarding (routing) of ipv4 packets on a per
+ interface basis. The forwarding check in the kernel dataplane occurs against the ingress
+ Layer 3 interface, i.e. if the ingress L3 interface has forwarding set to 0, packets will not
+ be routed.
-.. option:: net.ipv6.conf.all.keep_addr_on_down=1
+.. option:: net.ipv6.conf.{all,default,<interface>}.forwarding = 1
+
+ This per interface option allows the linux kernel to forward (route) transit ipv6 packets
+ i.e. incoming from one Layer 3 interface to an outgoing Layer 3 interface.
+ The forwarding check in the kernel dataplane occurs against the ingress Layer 3 interface,
+ i.e. if the ingress L3 interface has forwarding set to 0, packets will not be routed.
+
+.. option:: net.ipv6.conf.all.keep_addr_on_down = 1
When an interface is taken down, do not remove the v6 addresses associated with the interface.
This option is recommended because this is the default behavior for v4 as well.
-.. option:: net.ipv6.route.skip_notify_on_dev_down=1
+.. option:: net.ipv6.route.skip_notify_on_dev_down = 1
When an interface is taken down, the linux kernel will not notify, via netlink, about routes
that used that interface being removed from the FIB. This option is recommended because this
is the default behavior for v4 as well.
+Optional sysctl settings
+------------------------
+
+.. option:: net.ipv4.conf.{all,default,<interface>}.bc_forwarding = 0
+
+ This per interface option allows the linux kernel to optionally allow Directed Broadcast
+ (i.e. Routed Broadcast or Subnet Broadcast) packets to be routed onto the connected network
+ segment where the subnet exists.
+ If the local router receives a routed packet destined for a broadcast address of a connected
+ subnet, setting bc_forwarding to 1 on the interface with the target subnet assigned to it will
+ allow non locally-generated packets to be routed via the broadcast route.
+ If bc_forwarding is set to 0, routed packets destined for a broadcast route will be dropped.
+ e.g.
+ Host1 (SIP:192.0.2.10, DIP:10.0.0.255) -> (eth0:192.0.2.1/24) Router1 (eth1:10.0.0.1/24) -> BC
+ If net.ipv4.conf.{all,default,<interface>}.bc_forwarding=1, then Router1 will forward each
+ packet destined to 10.0.0.255 onto the eth1 interface with a broadcast DMAC (ff:ff:ff:ff:ff:ff).
+
+.. option:: net.ipv4.conf.{all,default,<interface>}.arp_accept = 1
+
+ This per interface option allows the linux kernel to optionally skip the creation of ARP
+ entries upon the receipt of a Gratuitous ARP (GARP) frame carrying an IP that is not already
+ present in the ARP cache. Setting arp_accept to 0 on an interface will ensure NEW ARP entries
+ are not created due to the arrival of a GARP frame.
+ Note: This does not impact how the kernel reacts to GARP frames that carry a "known" IP
+ (that is already in the ARP cache) -- an existing ARP entry will always be updated
+ when a GARP for that IP is received.
+
+.. option:: net.ipv4.conf.{all,default,<interface>}.arp_ignore = 0
+
+ This per interface option allows the linux kernel to control what conditions must be met in
+ order for an ARP reply to be sent in response to an ARP request targeting a local IP address.
+ When arp_ignore is set to 0, the kernel will send ARP replies in response to any ARP Request
+ with a Target-IP matching a local address.
+ When arp_ignore is set to 1, the kernel will send ARP replies if the Target-IP in the ARP
+ Request matches an IP address on the interface the Request arrived at.
+ When arp_ignore is set to 2, the kernel will send ARP replies only if the Target-IP matches an
+ IP address on the interface where the Request arrived AND the Sender-IP falls within the subnet
+ assigned to the local IP/interface.
+
+.. option:: net.ipv4.conf.{all,default,<interface>}.arp_notify = 1
+
+ This per interface option allows the linux kernel to decide whether to send a Gratuitious ARP
+ (GARP) frame when the Layer 3 interface comes UP.
+ When arp_notify is set to 0, no GARP is sent.
+ When arp_notify is set to 1, a GARP is sent when the interface comes UP.
+
+.. option:: net.ipv6.conf.{all,default,<interface>}.ndisc_notify = 1
+
+ This per interface option allows the linux kernel to decide whether to send an Unsolicited
+ Neighbor Advertisement (U-NA) frame when the Layer 3 interface comes UP.
+ When ndisc_notify is set to 0, no U-NA is sent.
+ When ndisc_notify is set to 1, a U-NA is sent when the interface comes UP.
+
Debugging
=========
install-info build-essential libsnmp-dev perl \
libcap-dev python2 libelf-dev \
sudo gdb curl iputils-ping time \
- libgrpc++-dev libgrpc-dev protobuf-compiler-grpc \
lua5.3 liblua5.3-dev \
net-tools iproute2 && \
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output /tmp/get-pip.py && \
--sbindir=/usr/lib/frr \
--sysconfdir=/etc/frr \
--enable-vtysh \
- --enable-grpc \
--enable-pimd \
--enable-sharpd \
--enable-multipath=64 \
/* Cancel GR scheduled */
thread_cancel(&(ei->t_distribute));
/* schedule Graceful restart for interface in 10sec */
- e->t_distribute = NULL;
thread_add_timer(master, eigrp_distribute_timer_interface, ei, 10,
- &e->t_distribute);
+ &ei->t_distribute);
}
/*
struct eigrp *eigrp;
eigrp = THREAD_ARG(thread);
- eigrp->t_distribute = NULL;
/* execute GR for whole process */
eigrp_update_send_process_GR(eigrp, EIGRP_GR_FILTER, NULL);
struct eigrp_interface *ei;
ei = THREAD_ARG(thread);
- ei->t_hello = NULL;
if (IS_DEBUG_EIGRP(0, TIMERS))
zlog_debug("Start Hello Timer (%s) Expire [%u]", IF_NAME(ei),
eigrp_hello_send(ei, EIGRP_HELLO_NORMAL, NULL);
/* Hello timer set. */
- ei->t_hello = NULL;
thread_add_timer(master, eigrp_hello_timer, ei, ei->params.v_hello,
&ei->t_hello);
/* Set multicast memberships appropriately for new state. */
eigrp_if_set_multicast(ei);
- thread_add_event(master, eigrp_hello_timer, ei, (1), NULL);
+ thread_add_event(master, eigrp_hello_timer, ei, (1), &ei->t_hello);
/*Prepare metrics*/
metric.bandwidth = eigrp_bandwidth_to_scaled(ei->params.bandwidth);
{
struct eigrp *eigrp;
const char *vrf;
+ struct vrf *pVrf;
vrf_id_t vrfid;
switch (args->event) {
break;
case NB_EV_PREPARE:
vrf = yang_dnode_get_string(args->dnode, "./vrf");
- vrfid = vrf_name_to_id(vrf);
+
+ pVrf = vrf_lookup_by_name(vrf);
+ if (pVrf)
+ vrfid = pVrf->vrf_id;
+ else
+ vrfid = VRF_DEFAULT;
eigrp = eigrp_get(yang_dnode_get_uint16(args->dnode, "./asn"),
vrfid);
struct eigrp *eigrp;
uint32_t proto;
vrf_id_t vrfid;
+ struct vrf *pVrf;
switch (args->event) {
case NB_EV_VALIDATE:
proto = yang_dnode_get_enum(args->dnode, "./protocol");
vrfname = yang_dnode_get_string(args->dnode, "../vrf");
- vrfid = vrf_name_to_id(vrfname);
+
+ pVrf = vrf_lookup_by_name(vrfname);
+ if (pVrf)
+ vrfid = pVrf->vrf_id;
+ else
+ vrfid = VRF_DEFAULT;
+
if (vrf_bitmap_check(zclient->redist[AFI_IP][proto], vrfid))
return NB_ERR_INCONSISTENCY;
break;
#endif /* WANT_EIGRP_WRITE_FRAGMENT */
#define EIGRP_WRITE_IPHL_SHIFT 2
- eigrp->t_write = NULL;
-
node = listhead(eigrp->oi_write_q);
assert(node);
ei = listgetdata(node);
/* If packets still remain in queue, call write thread. */
if (!list_isempty(eigrp->oi_write_q)) {
- eigrp->t_write = NULL;
thread_add_write(master, eigrp_write, eigrp, eigrp->fd,
&eigrp->t_write);
}
eigrp = THREAD_ARG(thread);
/* prepare for next packet. */
- eigrp->t_read = NULL;
thread_add_read(master, eigrp_read, eigrp, eigrp->fd, &eigrp->t_read);
stream_reset(eigrp->ibuf);
return eigrp_retrans_count_exceeded(ep, nbr);
/*Start retransmission timer*/
- ep->t_retrans_timer = NULL;
thread_add_timer(master, eigrp_unack_packet_retrans, nbr,
EIGRP_PACKET_RETRANS_TIME,
&ep->t_retrans_timer);
return eigrp_retrans_count_exceeded(ep, nbr);
/*Start retransmission timer*/
- ep->t_retrans_timer = NULL;
thread_add_timer(master, eigrp_unack_multicast_packet_retrans,
nbr, EIGRP_PACKET_RETRANS_TIME,
&ep->t_retrans_timer);
/* get argument from thread */
nbr = THREAD_ARG(thread);
/* remove this thread pointer */
- nbr->t_nbr_send_gr = NULL;
/* if there is packet waiting in queue,
* schedule this thread again with small delay */
if (nbr->retrans_queue->count > 0) {
- nbr->t_nbr_send_gr = NULL;
thread_add_timer_msec(master, eigrp_update_send_GR_thread, nbr,
10, &nbr->t_nbr_send_gr);
return 0;
/* if it wasn't last chunk, schedule this thread again */
if (nbr->nbr_gr_packet_type != EIGRP_PACKET_PART_LAST) {
thread_execute(master, eigrp_update_send_GR_thread, nbr, 0);
- nbr->t_nbr_send_gr = NULL;
}
return 0;
nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_FIRST;
/* execute packet sending in thread */
thread_execute(master, eigrp_update_send_GR_thread, nbr, 0);
- nbr->t_nbr_send_gr = NULL;
}
/**
eigrp->ibuf = stream_new(EIGRP_PACKET_MAX_LEN + 1);
- eigrp->t_read = NULL;
thread_add_read(master, eigrp_read, eigrp, eigrp->fd, &eigrp->t_read);
eigrp->oi_write_q = list_new();
{
struct fabricd *f = THREAD_ARG(thread);
uint8_t tier = ISIS_TIER_UNDEFINED;
- f->tier_calculation_timer = NULL;
tier = fabricd_calculate_fabric_tier(f->area);
if (tier == ISIS_TIER_UNDEFINED)
* XPath: /frr-isisd:isis/instance/redistribute
*/
DEFPY_YANG(isis_redistribute, isis_redistribute_cmd,
- "[no] redistribute <ipv4|ipv6>$ip " PROTO_REDIST_STR
- "$proto <level-1|level-2>$level [{metric (0-16777215)|route-map WORD}]",
+ "[no] redistribute <ipv4$ip " PROTO_IP_REDIST_STR "$proto|ipv6$ip "
+ PROTO_IP6_REDIST_STR "$proto> <level-1|level-2>$level"
+ "[{metric (0-16777215)|route-map WORD}]",
NO_STR REDIST_STR
"Redistribute IPv4 routes\n"
- "Redistribute IPv6 routes\n" PROTO_REDIST_HELP
+ PROTO_IP_REDIST_HELP
+ "Redistribute IPv6 routes\n"
+ PROTO_IP6_REDIST_HELP
"Redistribute into level-1\n"
"Redistribute into level-2\n"
"Metric for redistributed routes\n"
#ifdef FABRICD
DEFUN (isis_redistribute,
isis_redistribute_cmd,
- "redistribute <ipv4|ipv6> " PROTO_REDIST_STR
+ "redistribute <ipv4 " PROTO_IP_REDIST_STR "|ipv6 " PROTO_IP6_REDIST_STR ">"
" [{metric (0-16777215)|route-map WORD}]",
REDIST_STR
"Redistribute IPv4 routes\n"
+ PROTO_IP_REDIST_HELP
"Redistribute IPv6 routes\n"
- PROTO_REDIST_HELP
+ PROTO_IP6_REDIST_HELP
"Metric for redistributed routes\n"
"ISIS default metric\n"
"Route map reference\n"
DEFUN (no_isis_redistribute,
no_isis_redistribute_cmd,
- "no redistribute <ipv4|ipv6> " PROTO_REDIST_STR,
+ "no redistribute <ipv4 " PROTO_IP_REDIST_STR "|ipv6 " PROTO_IP6_REDIST_STR ">",
NO_STR
REDIST_STR
"Redistribute IPv4 routes\n"
+ PROTO_IP_REDIST_HELP
"Redistribute IPv6 routes\n"
- PROTO_REDIST_HELP)
+ PROTO_IP6_REDIST_HELP)
{
int idx_afi = 2;
int idx_protocol = 3;
*
* 2. I could be replaced in unit test environment
*/
-#ifndef ISIS_SNMP_HAVE_TIME_FUNC
-static uint32_t isis_snmp_time(void)
-{
- return (uint32_t)time(NULL);
-}
-
-#endif
/* ISIS-MIB instances. */
static oid isis_oid[] = {ISIS_MIB};
struct isis_circuit *circuit;
uint32_t up_ticks;
uint32_t delta_ticks;
- uint32_t now_time;
+ time_t now_time;
int res;
*write_method = NULL;
return SNMP_INTEGER(0);
up_ticks = (uint32_t)netsnmp_get_agent_uptime();
- now_time = isis_snmp_time();
+ now_time = time(NULL);
if (circuit->last_uptime >= now_time)
return SNMP_INTEGER(up_ticks);
oid *oid_idx;
size_t oid_idx_len;
int res;
- uint32_t val;
+ time_t val;
struct isis_adjacency *adj;
uint32_t up_ticks;
uint32_t delta_ticks;
- uint32_t now_time;
+ time_t now_time;
*write_method = NULL;
* It seems that we want remaining timer
*/
if (adj->last_upd != 0) {
- val = isis_snmp_time();
+ val = time(NULL);
if (val < (adj->last_upd + adj->hold_time))
return SNMP_INTEGER(adj->last_upd
+ adj->hold_time - val);
up_ticks = (uint32_t)netsnmp_get_agent_uptime();
- now_time = isis_snmp_time();
+ now_time = time(NULL);
if (adj->last_flap >= now_time)
return SNMP_INTEGER(up_ticks);
if (isis == NULL || !isis->snmp_notifications || !smux_enabled())
return 0;
- time_now = isis_snmp_time();
+ time_now = time(NULL);
if ((isis_snmp_trap_timestamp[trap_id] + 5) > time_now)
/* Throttle trap rate at 1 in 5 secs */
struct isis_tx_queue_entry *e = THREAD_ARG(thread);
struct isis_tx_queue *queue = e->queue;
- e->retry = NULL;
thread_add_timer(master, tx_queue_send_event, e, 5, &e->retry);
if (e->is_retry)
#define PROTO_NAME "openfabric"
#define PROTO_HELP "OpenFabric routing protocol\n"
#define PROTO_REDIST_STR FRR_REDIST_STR_FABRICD
+#define PROTO_IP_REDIST_STR FRR_IP_REDIST_STR_FABRICD
+#define PROTO_IP6_REDIST_STR FRR_IP6_REDIST_STR_FABRICD
#define PROTO_REDIST_HELP FRR_REDIST_HELP_STR_FABRICD
+#define PROTO_IP_REDIST_HELP FRR_IP_REDIST_HELP_STR_FABRICD
+#define PROTO_IP6_REDIST_HELP FRR_IP6_REDIST_HELP_STR_FABRICD
#define ROUTER_NODE OPENFABRIC_NODE
#else
static const bool fabricd = false;
#define PROTO_NAME "isis"
#define PROTO_HELP "IS-IS routing protocol\n"
#define PROTO_REDIST_STR FRR_REDIST_STR_ISISD
+#define PROTO_IP_REDIST_STR FRR_IP_REDIST_STR_ISISD
+#define PROTO_IP6_REDIST_STR FRR_IP6_REDIST_STR_ISISD
#define PROTO_REDIST_HELP FRR_REDIST_HELP_STR_ISISD
+#define PROTO_IP_REDIST_HELP FRR_IP_REDIST_HELP_STR_ISISD
+#define PROTO_IP6_REDIST_HELP FRR_IP6_REDIST_HELP_STR_ISISD
#define ROUTER_NODE ISIS_NODE
extern void isis_cli_init(void);
#endif
void isis_master_init(struct thread_master *master);
void isis_vrf_link(struct isis *isis, struct vrf *vrf);
void isis_vrf_unlink(struct isis *isis, struct vrf *vrf);
-void isis_global_instance_create(const char *vrf_name);
struct isis *isis_lookup_by_vrfid(vrf_id_t vrf_id);
struct isis *isis_lookup_by_vrfname(const char *vrfname);
struct isis *isis_lookup_by_sysid(const uint8_t *sysid);
av->arg = arg;
LIST_INSERT_HEAD(&accept_queue.queue, av, entry);
- av->ev = NULL;
thread_add_read(master, accept_cb, av, av->fd, &av->ev);
log_debug("%s: accepting on fd %d", __func__, fd);
{
log_debug(__func__);
accept_unarm();
- accept_queue.evt = NULL;
thread_add_timer(master, accept_timeout, NULL, 1, &accept_queue.evt);
}
{
struct accept_ev *av;
LIST_FOREACH(av, &accept_queue.queue, entry) {
- av->ev = NULL;
thread_add_read(master, accept_cb, av, av->fd, &av->ev);
}
}
accept_cb(struct thread *thread)
{
struct accept_ev *av = THREAD_ARG(thread);
- av->ev = NULL;
thread_add_read(master, accept_cb, av, av->fd, &av->ev);
av->accept_cb(thread);
if_start_hello_timer(struct iface_af *ia)
{
thread_cancel(&ia->hello_timer);
- ia->hello_timer = NULL;
thread_add_timer(master, if_hello_timer, ia, if_get_hello_interval(ia),
&ia->hello_timer);
}
fatal(NULL);
imsg_init(&iev_main->ibuf, LDPD_FD_ASYNC);
iev_main->handler_read = lde_dispatch_parent;
- iev_main->ev_read = NULL;
thread_add_read(master, iev_main->handler_read, iev_main, iev_main->ibuf.fd,
&iev_main->ev_read);
iev_main->handler_write = ldp_write_handler;
fatal(NULL);
imsg_init(&iev_ldpe->ibuf, fd);
iev_ldpe->handler_read = lde_dispatch_imsg;
- iev_ldpe->ev_read = NULL;
thread_add_read(master, iev_ldpe->handler_read, iev_ldpe, iev_ldpe->ibuf.fd,
&iev_ldpe->ev_read);
iev_ldpe->handler_write = ldp_write_handler;
lde_gc_start_timer(void)
{
thread_cancel(&gc_timer);
- gc_timer = NULL;
thread_add_timer(master, lde_gc_timer, NULL, LDE_GC_INTERVAL,
&gc_timer);
}
{
const char *dir;
dir = ldpd_di.module_path ? ldpd_di.module_path : frr_moduledir;
- char moderr[256];
struct frrmod_runtime *module;
- module = frrmod_load(name, dir, moderr, sizeof(moderr));
+ module = frrmod_load(name, dir, NULL,NULL);
if (!module) {
fprintf(stderr, "%s: failed to load %s", __func__, name);
log_warnx("%s: failed to load %s", __func__, name);
fatal(NULL);
imsg_init(&iev_ldpe->ibuf, pipe_parent2ldpe[0]);
iev_ldpe->handler_read = main_dispatch_ldpe;
- iev_ldpe->ev_read = NULL;
thread_add_read(master, iev_ldpe->handler_read, iev_ldpe, iev_ldpe->ibuf.fd,
&iev_ldpe->ev_read);
iev_ldpe->handler_write = ldp_write_handler;
imsg_init(&iev_ldpe_sync->ibuf, pipe_parent2ldpe_sync[0]);
iev_ldpe_sync->handler_read = main_dispatch_ldpe;
- iev_ldpe_sync->ev_read = NULL;
thread_add_read(master, iev_ldpe_sync->handler_read, iev_ldpe_sync, iev_ldpe_sync->ibuf.fd,
&iev_ldpe_sync->ev_read);
iev_ldpe_sync->handler_write = ldp_write_handler;
imsg_init(&iev_lde->ibuf, pipe_parent2lde[0]);
iev_lde->handler_read = main_dispatch_lde;
- iev_lde->ev_read = NULL;
thread_add_read(master, iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd,
&iev_lde->ev_read);
iev_lde->handler_write = ldp_write_handler;
imsg_init(&iev_lde_sync->ibuf, pipe_parent2lde_sync[0]);
iev_lde_sync->handler_read = main_dispatch_lde;
- iev_lde_sync->ev_read = NULL;
thread_add_read(master, iev_lde_sync->handler_read, iev_lde_sync, iev_lde_sync->ibuf.fd,
&iev_lde_sync->ev_read);
iev_lde_sync->handler_write = ldp_write_handler;
fatal(NULL);
imsg_init(&iev_main->ibuf, LDPD_FD_ASYNC);
iev_main->handler_read = ldpe_dispatch_main;
- iev_main->ev_read = NULL;
thread_add_read(master, iev_main->handler_read, iev_main, iev_main->ibuf.fd,
&iev_main->ev_read);
iev_main->handler_write = ldp_write_handler;
/* This socket must be open before dropping privileges. */
global.pfkeysock = pfkey_init();
if (sysdep.no_pfkey == 0) {
- pfkey_ev = NULL;
thread_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock,
&pfkey_ev);
}
fatal(NULL);
imsg_init(&iev_lde->ibuf, fd);
iev_lde->handler_read = ldpe_dispatch_lde;
- iev_lde->ev_read = NULL;
thread_add_read(master, iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd,
&iev_lde->ev_read);
iev_lde->handler_write = ldp_write_handler;
{
int fd = THREAD_FD(thread);
- pfkey_ev = NULL;
thread_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock,
&pfkey_ev);
/* discovery socket */
af_global->ldp_disc_socket = disc_socket;
- af_global->disc_ev = NULL;
thread_add_read(master, disc_recv_packet, &af_global->disc_ev, af_global->ldp_disc_socket,
&af_global->disc_ev);
/* extended discovery socket */
af_global->ldp_edisc_socket = edisc_socket;
- af_global->edisc_ev = NULL;
thread_add_read(master, disc_recv_packet, &af_global->edisc_ev, af_global->ldp_edisc_socket,
&af_global->edisc_ev);
struct in_addr lsr_id;
/* reschedule read */
- *threadp = NULL;
thread_add_read(master, disc_recv_packet, threadp, fd, threadp);
/* setup buffer */
uint16_t pdu_len, msg_len, msg_size, max_pdu_len;
int ret;
- tcp->rev = NULL;
thread_add_read(master, session_read, nbr, fd, &tcp->rev);
if ((n = read(fd, tcp->rbuf->buf + tcp->rbuf->wpos,
if ((tcp->rbuf = calloc(1, sizeof(struct ibuf_read))) == NULL)
fatal(__func__);
- tcp->rev = NULL;
thread_add_read(master, session_read, nbr, tcp->fd, &tcp->rev);
tcp->nbr = nbr;
}
item(JOIN_TKN),
item(START_TKN),
item(END_TKN),
+ item(NEG_ONLY_TKN),
{0},
};
/* clang-format on */
#define CMD_WARNING_CONFIG_FAILED 13
#define CMD_NOT_MY_INSTANCE 14
#define CMD_NO_LEVEL_UP 15
+#define CMD_ERR_NO_DAEMON 16
/* Argc max counts. */
#define CMD_ARGC_MAX 256
case START_TKN:
case JOIN_TKN:
+ case NEG_ONLY_TKN:
/* "<foo|bar> WORD" -> word is not "bar" or "foo" */
prevname = NULL;
break;
case JOIN_TKN:
color = "#ddaaff";
break;
+ case NEG_ONLY_TKN:
+ color = "#ffddaa";
+ break;
case WORD_TKN:
color = "#ffffff";
break;
JOIN_TKN, // marks subgraph end
START_TKN, // first token in line
END_TKN, // last token in line
+ NEG_ONLY_TKN, // filter token, match if "no ..." command
SPECIAL_TKN = FORK_TKN,
};
CMD_ATTR_YANG,
};
-/* Comamand token struct. */
+/* Command token struct. */
struct cmd_token {
enum cmd_token_type type; // token type
uint8_t attr; // token attributes
- bool allowrepeat; // matcher allowed to match token repetively?
+ bool allowrepeat; // matcher allowed to match token repetitively?
uint32_t refcnt;
char *text; // token text
{VARIABLE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return VARIABLE;}
{WORD} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return WORD;}
{RANGE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return RANGE;}
+!\[ {yylval->string = NULL; return EXCL_BRACKET;}
. {return yytext[0];}
%%
/* matcher helper prototypes */
static int add_nexthops(struct list *, struct graph_node *,
- struct graph_node **, size_t);
+ struct graph_node **, size_t, bool);
static enum matcher_rv command_match_r(struct graph_node *, vector,
unsigned int, struct graph_node **,
static enum match_type match_mac(const char *, bool);
+static bool is_neg(vector vline, size_t idx)
+{
+ if (idx >= vector_active(vline) || !vector_slot(vline, idx))
+ return false;
+ return !strcmp(vector_slot(vline, idx), "no");
+}
+
enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
struct list **argv, const struct cmd_element **el)
{
// get all possible nexthops
struct list *next = list_new();
- add_nexthops(next, start, NULL, 0);
+ add_nexthops(next, start, NULL, 0, is_neg(vline, 1));
// determine the best match
for (ALL_LIST_ELEMENTS_RO(next, ln, gn)) {
{
// pointer to next input token to match
char *input_token;
+ bool neg = is_neg(vline, 0);
struct list *
current =
// add all children of start node to list
struct graph_node *start = vector_slot(graph->nodes, 0);
- add_nexthops(next, start, &start, 0);
+ add_nexthops(next, start, &start, 0, neg);
unsigned int idx;
for (idx = 0; idx < vector_active(vline) && next->count > 0; idx++) {
listnode_add(next, newstack);
} else if (matchtype >= minmatch)
add_nexthops(next, gstack[0], gstack,
- idx + 1);
+ idx + 1, neg);
break;
default:
trace_matcher("no_match\n");
* output, instead of direct node pointers!
*/
static int add_nexthops(struct list *list, struct graph_node *node,
- struct graph_node **stack, size_t stackpos)
+ struct graph_node **stack, size_t stackpos, bool neg)
{
int added = 0;
struct graph_node *child;
if (j != stackpos)
continue;
}
+
+ if (token->type == NEG_ONLY_TKN && !neg)
+ continue;
+
if (token->type >= SPECIAL_TKN && token->type != END_TKN) {
- added += add_nexthops(list, child, stack, stackpos);
+ added +=
+ add_nexthops(list, child, stack, stackpos, neg);
} else {
if (stack) {
nextstack = XMALLOC(
%token <string> MAC
%token <string> MAC_PREFIX
+/* special syntax, value is irrelevant */
+%token <string> EXCL_BRACKET
+
/* union types for parsed rules */
%type <node> start
%type <node> literal_token
}
;
+/* ![option] productions */
+selector: EXCL_BRACKET selector_seq_seq ']' varname_token
+{
+ struct graph_node *neg_only = new_token_node (ctx, NEG_ONLY_TKN, NULL, NULL);
+
+ $$ = $2;
+ graph_add_edge ($$.start, neg_only);
+ graph_add_edge (neg_only, $$.end);
+ cmd_token_varname_set ($2.end->data, $4);
+ XFREE (MTYPE_LEX, $4);
+}
+;
+
%%
#undef scanner
if (gn->data) {
struct cmd_token *tok = gn->data;
switch (tok->type) {
-#define item(x) case x: wrap->type = #x; break;
- item(WORD_TKN) // words
- item(VARIABLE_TKN) // almost anything
- item(RANGE_TKN) // integer range
- item(IPV4_TKN) // IPV4 addresses
- item(IPV4_PREFIX_TKN) // IPV4 network prefixes
- item(IPV6_TKN) // IPV6 prefixes
- item(IPV6_PREFIX_TKN) // IPV6 network prefixes
- item(MAC_TKN) // MAC address
- item(MAC_PREFIX_TKN) // MAC address with mask
-
- /* plumbing types */
- item(FORK_TKN) item(JOIN_TKN) item(START_TKN)
- item(END_TKN) default
- : wrap->type = "???";
+#define item(x) \
+ case x: \
+ wrap->type = #x; \
+ break /* no semicolon */
+
+ item(WORD_TKN); // words
+ item(VARIABLE_TKN); // almost anything
+ item(RANGE_TKN); // integer range
+ item(IPV4_TKN); // IPV4 addresses
+ item(IPV4_PREFIX_TKN); // IPV4 network prefixes
+ item(IPV6_TKN); // IPV6 prefixes
+ item(IPV6_PREFIX_TKN); // IPV6 network prefixes
+ item(MAC_TKN); // MAC address
+ item(MAC_PREFIX_TKN); // MAC address with mask
+
+ /* plumbing types */
+ item(FORK_TKN);
+ item(JOIN_TKN);
+ item(START_TKN);
+ item(END_TKN);
+ item(NEG_ONLY_TKN);
+#undef item
+ default:
+ wrap->type = "???";
}
wrap->deprecated = (tok->attr == CMD_ATTR_DEPRECATED);
break;
if (cb->read.cb_msg) {
+ cb->in_cb = true;
cb->read.cb_msg(cb->read.arg, cb->zmqsock);
+ cb->in_cb = false;
+
read = 1;
if (cb->read.cancelled) {
ZMQ_POLLOUT);
cb->read.thread = NULL;
if (cb->write.cancelled && !cb->write.thread)
- XFREE(MTYPE_ZEROMQ_CB, cb);
+ XFREE(MTYPE_ZEROMQ_CB, *cbp);
+
return 0;
}
continue;
}
read = 1;
+ cb->in_cb = true;
cb->read.cb_part(cb->read.arg, cb->zmqsock, &msg,
partno);
+ cb->in_cb = false;
+
if (cb->read.cancelled) {
zmq_msg_close(&msg);
frrzmq_check_events(cbp, &cb->write,
ZMQ_POLLOUT);
cb->read.thread = NULL;
if (cb->write.cancelled && !cb->write.thread)
- XFREE(MTYPE_ZEROMQ_CB, cb);
+ XFREE(MTYPE_ZEROMQ_CB, *cbp);
+
return 0;
}
cb = *cbp;
else {
cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb));
-
cb->write.cancelled = true;
*cbp = cb;
}
cb->read.cb_part = partfunc;
cb->read.cb_error = errfunc;
cb->read.cancelled = false;
+ cb->in_cb = false;
if (events & ZMQ_POLLIN) {
thread_cancel(&cb->read.thread);
break;
if (cb->write.cb_msg) {
+ cb->in_cb = true;
cb->write.cb_msg(cb->write.arg, cb->zmqsock);
+ cb->in_cb = false;
+
written = 1;
if (cb->write.cancelled) {
frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN);
cb->write.thread = NULL;
if (cb->read.cancelled && !cb->read.thread)
- XFREE(MTYPE_ZEROMQ_CB, cb);
+ XFREE(MTYPE_ZEROMQ_CB, *cbp);
+
return 0;
}
continue;
cb = *cbp;
else {
cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb));
-
cb->read.cancelled = true;
*cbp = cb;
}
cb->write.cb_part = NULL;
cb->write.cb_error = errfunc;
cb->write.cancelled = false;
+ cb->in_cb = false;
if (events & ZMQ_POLLOUT) {
thread_cancel(&cb->write.thread);
core->cancelled = true;
thread_cancel(&core->thread);
- /*
- * Looking at this code one would assume that FRR
- * would want a `!(*cb)->write.thread. This was
- * attempted in e08165def1c62beee0e87385 but this
- * change caused `make check` to stop working
- * which was not noticed because our CI system
- * does not build with zeromq. Put this back
- * to the code as written in 2017. e08165de..
- * was introduced in 2021. So someone was ok
- * with frrzmq_thread_cancel for 4 years. This will
- * allow those people doing `make check` to continue
- * working. In the meantime if the people using
- * this code see an issue they can fix it
+ /* If cancelled from within a callback, don't try to free memory
+ * in this path.
*/
+ if ((*cb)->in_cb)
+ return;
+
+ /* Ok to free the callback context if no more ... context. */
if ((*cb)->read.cancelled && !(*cb)->read.thread
- && (*cb)->write.cancelled && (*cb)->write.thread)
+ && (*cb)->write.cancelled && ((*cb)->write.thread == NULL))
XFREE(MTYPE_ZEROMQ_CB, *cb);
}
unsigned partnum);
void (*cb_error)(void *arg, void *zmqsock);
};
+
struct frrzmq_cb {
void *zmqsock;
int fd;
+ bool in_cb; /* This context is in a read or write callback. */
+
struct cb_core read;
struct cb_core write;
};
strerror(errno));
}
+static void _err_print(const void *cookie, const char *errstr)
+{
+ const char *prefix = (const char *)cookie;
+
+ fprintf(stderr, "%s: %s\n", prefix, errstr);
+}
+
static struct thread_master *master;
struct thread_master *frr_init(void)
{
struct option_chain *oc;
struct frrmod_runtime *module;
struct zprivs_ids_t ids;
- char moderr[256];
char p_instance[16] = "", p_pathspace[256] = "";
const char *dir;
dir = di->module_path ? di->module_path : frr_moduledir;
frrmod_init(di->module);
while (modules) {
modules = (oc = modules)->next;
- module = frrmod_load(oc->arg, dir, moderr, sizeof(moderr));
- if (!module) {
- fprintf(stderr, "%s\n", moderr);
+ module = frrmod_load(oc->arg, dir, _err_print, __func__);
+ if (!module)
exit(1);
- }
XFREE(MTYPE_TMP, oc);
}
#include "module.h"
#include "memory.h"
#include "lib/version.h"
+#include "printfrr.h"
DEFINE_MTYPE_STATIC(LIB, MODULE_LOADNAME, "Module loading name");
DEFINE_MTYPE_STATIC(LIB, MODULE_LOADARGS, "Module loading arguments");
+DEFINE_MTYPE_STATIC(LIB, MODULE_LOAD_ERR, "Module loading error");
static struct frrmod_info frrmod_default_info = {
.name = "libfrr",
execname = modinfo->info->name;
}
-struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, char *err,
- size_t err_len)
+/*
+ * If caller wants error strings, it should define non-NULL pFerrlog
+ * which will be called with 0-terminated error messages. These
+ * messages will NOT contain newlines, and the (*pFerrlog)() function
+ * could be called multiple times for a single call to frrmod_load().
+ *
+ * The (*pFerrlog)() function may copy these strings if needed, but
+ * should expect them to be freed by frrmod_load() before frrmod_load()
+ * returns.
+ *
+ * frrmod_load() is coded such that (*pFerrlog)() will be called only
+ * in the case where frrmod_load() returns an error.
+ */
+struct frrmod_runtime *frrmod_load(const char *spec, const char *dir,
+ void (*pFerrlog)(const void *, const char *),
+ const void *pErrlogCookie)
{
void *handle = NULL;
char name[PATH_MAX], fullpath[PATH_MAX * 2], *args;
struct frrmod_runtime *rtinfo, **rtinfop;
const struct frrmod_info *info;
+#define FRRMOD_LOAD_N_ERRSTR 10
+ char *aErr[FRRMOD_LOAD_N_ERRSTR];
+ unsigned int iErr = 0;
+
+ memset(aErr, 0, sizeof(aErr));
+
+#define ERR_RECORD(...) \
+ do { \
+ if (pFerrlog && (iErr < FRRMOD_LOAD_N_ERRSTR)) { \
+ aErr[iErr++] = asprintfrr(MTYPE_MODULE_LOAD_ERR, \
+ __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define ERR_REPORT \
+ do { \
+ if (pFerrlog) { \
+ unsigned int i; \
+ \
+ for (i = 0; i < iErr; ++i) { \
+ (*pFerrlog)(pErrlogCookie, aErr[i]); \
+ } \
+ } \
+ } while (0)
+
+#define ERR_FREE \
+ do { \
+ unsigned int i; \
+ \
+ for (i = 0; i < iErr; ++i) { \
+ XFREE(MTYPE_MODULE_LOAD_ERR, aErr[i]); \
+ aErr[i] = 0; \
+ } \
+ iErr = 0; \
+ } while (0)
+
snprintf(name, sizeof(name), "%s", spec);
args = strchr(name, ':');
if (args)
snprintf(fullpath, sizeof(fullpath), "%s/%s_%s.so", dir,
execname, name);
handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
+ if (!handle)
+ ERR_RECORD("loader error: dlopen(%s): %s",
+ fullpath, dlerror());
}
if (!handle) {
snprintf(fullpath, sizeof(fullpath), "%s/%s.so", dir,
name);
handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
+ if (!handle)
+ ERR_RECORD("loader error: dlopen(%s): %s",
+ fullpath, dlerror());
}
}
if (!handle) {
snprintf(fullpath, sizeof(fullpath), "%s", name);
handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
+ if (!handle)
+ ERR_RECORD("loader error: dlopen(%s): %s", fullpath,
+ dlerror());
}
if (!handle) {
- if (err)
- snprintf(err, err_len,
- "loading module \"%s\" failed: %s", name,
- dlerror());
+ ERR_REPORT;
+ ERR_FREE;
return NULL;
}
+ /* previous dlopen() errors are no longer relevant */
+ ERR_FREE;
+
rtinfop = dlsym(handle, "frr_module");
if (!rtinfop) {
dlclose(handle);
- if (err)
- snprintf(err, err_len,
- "\"%s\" is not an FRR module: %s", name,
- dlerror());
+ ERR_RECORD("\"%s\" is not an FRR module: %s", name, dlerror());
+ ERR_REPORT;
+ ERR_FREE;
return NULL;
}
rtinfo = *rtinfop;
if (rtinfo->finished_loading) {
dlclose(handle);
- if (err)
- snprintf(err, err_len, "module \"%s\" already loaded",
- name);
+ ERR_RECORD("module \"%s\" already loaded", name);
goto out_fail;
}
if (info->init && info->init()) {
dlclose(handle);
- if (err)
- snprintf(err, err_len,
- "module \"%s\" initialisation failed", name);
+ ERR_RECORD("module \"%s\" initialisation failed", name);
goto out_fail;
}
*frrmod_last = rtinfo;
frrmod_last = &rtinfo->next;
+ ERR_FREE;
return rtinfo;
out_fail:
XFREE(MTYPE_MODULE_LOADARGS, rtinfo->load_args);
XFREE(MTYPE_MODULE_LOADNAME, rtinfo->load_name);
+ ERR_REPORT;
+ ERR_FREE;
return NULL;
}
extern void frrmod_init(struct frrmod_runtime *modinfo);
extern struct frrmod_runtime *frrmod_load(const char *spec, const char *dir,
- char *err, size_t err_len);
+ void (*pFerrlog)(const void *,
+ const char *),
+ const void *pErrlogCookie);
#if 0
/* not implemented yet */
extern void frrmod_unload(struct frrmod_runtime *module);
return nexthop;
}
-struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type)
+struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type,
+ vrf_id_t nh_vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
- nexthop->vrf_id = VRF_DEFAULT;
+ nexthop->vrf_id = nh_vrf_id;
nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
nexthop->bh_type = bh_type;
case NEXTHOP_TYPE_BLACKHOLE:
snprintf(str, size, "blackhole");
break;
- default:
- snprintf(str, size, "unknown");
- break;
}
return str;
* unreachable (blackhole)
* %pNHs
* nexthop2str()
+ * %pNHcg
+ * 1.2.3.4
+ * (0-length if no IP address present)
+ * %pNHci
+ * eth0
+ * (0-length if no interface present)
*/
printfrr_ext_autoreg_p("NH", printfrr_nh)
static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea,
case BLACKHOLE_NULL:
ret += bputs(buf, " (blackhole)");
break;
- default:
+ case BLACKHOLE_UNSPEC:
break;
}
break;
- default:
- break;
}
if (do_ifi && nexthop->ifindex)
ret += bprintfrr(buf, ", %s%s", v_viaif,
case NEXTHOP_TYPE_BLACKHOLE:
ret += bputs(buf, "blackhole");
break;
- default:
- ret += bputs(buf, "unknown");
- break;
+ }
+ return ret;
+ case 'c':
+ ea->fmt++;
+ if (*ea->fmt == 'g') {
+ ea->fmt++;
+ if (!nexthop)
+ return bputs(buf, "(null)");
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ ret += bprintfrr(buf, "%pI4",
+ &nexthop->gate.ipv4);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ ret += bprintfrr(buf, "%pI6",
+ &nexthop->gate.ipv6);
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_BLACKHOLE:
+ break;
+ }
+ } else if (*ea->fmt == 'i') {
+ ea->fmt++;
+ if (!nexthop)
+ return bputs(buf, "(null)");
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ ret += bprintfrr(
+ buf, "%s",
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ if (nexthop->ifindex)
+ ret += bprintfrr(
+ buf, "%s",
+ ifindex2ifname(
+ nexthop->ifindex,
+ nexthop->vrf_id));
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ break;
+ }
}
return ret;
}
vrf_id_t vrf_id);
struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6,
ifindex_t ifindex, vrf_id_t vrf_id);
-struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type);
+struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type,
+ vrf_id_t nh_vrf_id);
/*
* Hash a nexthop. Suitable for use with hash tables.
LYD_VALIDATE_NO_STATE, NULL);
}
+static int lyd_node_cmp(struct lyd_node **dnode1, struct lyd_node **dnode2)
+{
+ struct nb_node *nb_node = (*dnode1)->schema->priv;
+
+ return nb_node->cbs.cli_cmp(*dnode1, *dnode2);
+}
+
static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root,
bool with_defaults)
{
* it's time to print the config.
*/
if (sort_node && nb_node != sort_node) {
+ list_sort(sort_list,
+ (int (*)(const void **,
+ const void **))lyd_node_cmp);
+
for (ALL_LIST_ELEMENTS_RO(sort_list, listnode, data))
nb_cli_show_dnode_cmds(vty, data,
with_defaults);
if (!sort_node) {
sort_node = nb_node;
sort_list = list_new();
- sort_list->cmp = (int (*)(void *, void *))
- nb_node->cbs.cli_cmp;
}
- listnode_add_sort(sort_list, child);
+ listnode_add(sort_list, child);
continue;
}
}
if (sort_node) {
+ list_sort(sort_list,
+ (int (*)(const void **, const void **))lyd_node_cmp);
+
for (ALL_LIST_ELEMENTS_RO(sort_list, listnode, data))
nb_cli_show_dnode_cmds(vty, data, with_defaults);
{
struct lyd_node *dnode;
+ if (!yang_dnode_exists(running_config->dnode,
+ path.empty() ? NULL : path.c_str()))
+ return NULL;
+
dnode = yang_dnode_get(running_config->dnode,
path.empty() ? NULL : path.c_str());
if (dnode)
extern char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len);
extern void prefix_evpn_hexdump(const struct prefix_evpn *p);
-static inline int ipv6_martian(struct in6_addr *addr)
+static inline int ipv6_martian(const struct in6_addr *addr)
{
struct in6_addr localhost_addr;
extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p);
/* NOTE: This routine expects the address argument in network byte order. */
-static inline int ipv4_martian(struct in_addr *addr)
+static inline int ipv4_martian(const struct in_addr *addr)
{
in_addr_t ip = ntohl(addr->s_addr);
{
struct resolver_state *r = THREAD_ARG(t);
int fd = THREAD_FD(t);
+ struct thread **t_ptr;
vector_set_index(r->read_threads, fd, THREAD_RUNNING);
ares_process_fd(r->channel, fd, ARES_SOCKET_BAD);
if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) {
- t = NULL;
+ t_ptr = (struct thread **)vector_get_index(r->read_threads, fd);
thread_add_read(r->master, resolver_cb_socket_readable, r, fd,
- &t);
- vector_set_index(r->read_threads, fd, t);
+ t_ptr);
}
resolver_update_timeouts(r);
{
struct resolver_state *r = THREAD_ARG(t);
int fd = THREAD_FD(t);
+ struct thread **t_ptr;
vector_set_index(r->write_threads, fd, THREAD_RUNNING);
ares_process_fd(r->channel, ARES_SOCKET_BAD, fd);
if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) {
- t = NULL;
+ t_ptr = (struct thread **)vector_get_index(r->write_threads, fd);
thread_add_write(r->master, resolver_cb_socket_writable, r, fd,
- &t);
- vector_set_index(r->write_threads, fd, t);
+ t_ptr);
}
resolver_update_timeouts(r);
int writable)
{
struct resolver_state *r = (struct resolver_state *)data;
- struct thread *t;
+ struct thread *t, **t_ptr;
if (readable) {
- t = vector_lookup_ensure(r->read_threads, fd);
+ t = vector_lookup(r->read_threads, fd);
if (!t) {
+ t_ptr = (struct thread **)vector_get_index(
+ r->read_threads, fd);
thread_add_read(r->master, resolver_cb_socket_readable,
- r, fd, &t);
- vector_set_index(r->read_threads, fd, t);
+ r, fd, t_ptr);
}
} else {
t = vector_lookup(r->read_threads, fd);
}
if (writable) {
- t = vector_lookup_ensure(r->write_threads, fd);
+ t = vector_lookup(r->write_threads, fd);
if (!t) {
+ t_ptr = (struct thread **)vector_get_index(
+ r->write_threads, fd);
thread_add_read(r->master, resolver_cb_socket_writable,
- r, fd, &t);
- vector_set_index(r->write_threads, fd, t);
+ r, fd, t_ptr);
}
} else {
t = vector_lookup(r->write_threads, fd);
We need to make sure our route-map processing matches the above
*/
-route_map_result_t route_map_apply(struct route_map *map,
- const struct prefix *prefix, void *object)
+route_map_result_t route_map_apply_ext(struct route_map *map,
+ const struct prefix *prefix,
+ void *match_object, void *set_object)
{
static int recursion = 0;
enum route_map_cmd_result_t match_ret = RMAP_NOMATCH;
if ((!map->optimization_disabled)
&& (map->ipv4_prefix_table || map->ipv6_prefix_table)) {
- index = route_map_get_index(map, prefix, object,
+ index = route_map_get_index(map, prefix, match_object,
(uint8_t *)&match_ret);
if (index) {
index->applied++;
index->applied++;
/* Apply this index. */
match_ret = route_map_apply_match(&index->match_list,
- prefix, object);
+ prefix, match_object);
if (rmap_debug) {
zlog_debug(
"Route-map: %s, sequence: %d, prefix: %pFX, result: %s",
* return code.
*/
(void)(*set->cmd->func_apply)(
- set->value, prefix, object);
+ set->value, prefix, set_object);
/* Call another route-map if available */
if (index->nextrm) {
jump to it */
{
recursion++;
- ret = route_map_apply(
- nextrm, prefix, object);
+ ret = route_map_apply_ext(
+ nextrm, prefix,
+ match_object,
+ set_object);
recursion--;
}
struct route_map *route_map_lookup_warn_noexist(struct vty *vty, const char *name);
/* Apply route map to the object. */
-extern route_map_result_t route_map_apply(struct route_map *map,
- const struct prefix *prefix,
- void *object);
+extern route_map_result_t route_map_apply_ext(struct route_map *map,
+ const struct prefix *prefix,
+ void *match_object,
+ void *set_object);
+#define route_map_apply(map, prefix, object) \
+ route_map_apply_ext(map, prefix, object, object)
extern void route_map_add_hook(void (*func)(const char *));
extern void route_map_delete_hook(void (*func)(const char *));
DEFINE_MTYPE_STATIC(LIB, SKIP_LIST, "Skip List");
DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_NODE, "Skip Node");
+DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_STATS, "Skiplist Counters");
#define BitsInRandom 31
#define MaxNumberOfLevels 16
#define MaxLevel (MaxNumberOfLevels-1)
-#define newNodeOfLevel(l) XCALLOC(MTYPE_SKIP_LIST_NODE, sizeof(struct skiplistnode)+(l)*sizeof(struct skiplistnode *))
+#define newNodeOfLevel(l) \
+ XCALLOC(MTYPE_SKIP_LIST_NODE, \
+ sizeof(struct skiplistnode) \
+ + (l) * sizeof(struct skiplistnode *))
+
+/* XXX must match type of (struct skiplist).level_stats */
+#define newStatsOfLevel(l) \
+ XCALLOC(MTYPE_SKIP_LIST_STATS, ((l) + 1) * sizeof(int))
static int randomsLeft;
static int randomBits;
-#if 1
+#ifdef SKIPLIST_DEBUG
#define CHECKLAST(sl) \
do { \
if ((sl)->header->forward[0] && !(sl)->last) \
new->level = 0;
new->count = 0;
new->header = newNodeOfLevel(MaxNumberOfLevels);
- new->stats = newNodeOfLevel(MaxNumberOfLevels);
+ new->level_stats = newStatsOfLevel(MaxNumberOfLevels);
new->flags = flags;
if (cmp)
p = q;
} while (p);
- XFREE(MTYPE_SKIP_LIST_NODE, l->stats);
+ XFREE(MTYPE_SKIP_LIST_STATS, l->level_stats);
XFREE(MTYPE_SKIP_LIST, l);
}
CHECKLAST(l);
+#ifdef SKIPLIST_DEBUG
/* DEBUG */
if (!key) {
flog_err(EC_LIB_DEVELOPMENT, "%s: key is 0, value is %p",
__func__, value);
}
+#endif
p = l->header;
k = l->level;
q->flags = SKIPLIST_NODE_FLAG_INSERTED; /* debug */
#endif
- ++(l->stats->forward[k]);
+ ++(l->level_stats[k]);
#ifdef SKIPLIST_DEBUG
- zlog_debug("%s: incremented stats @%p:%d, now %ld", __func__, l, k,
- l->stats->forward[k] - (struct skiplistnode *)NULL);
+ zlog_debug("%s: incremented level_stats @%p:%d, now %d", __func__, l, k,
+ l->level_stats[k]);
#endif
do {
k++) {
p->forward[k] = q->forward[k];
}
- --(l->stats->forward[k - 1]);
+ --(l->level_stats[k - 1]);
#ifdef SKIPLIST_DEBUG
- zlog_debug("%s: decremented stats @%p:%d, now %ld",
- __func__, l, k - 1,
- l->stats->forward[k - 1]
- - (struct skiplistnode *)NULL);
+ zlog_debug("%s: decremented level_stats @%p:%d, now %d",
+ __func__, l, k - 1, l->level_stats[k - 1]);
#endif
if (l->del)
(*l->del)(q->value);
l->last = NULL;
}
- --(l->stats->forward[nodelevel]);
+ --(l->level_stats[nodelevel]);
#ifdef SKIPLIST_DEBUG
- zlog_debug("%s: decremented stats @%p:%d, now %ld", __func__, l,
- nodelevel,
- l->stats->forward[nodelevel] - (struct skiplistnode *)NULL);
+ zlog_debug("%s: decremented level_stats @%p:%d, now %d", __func__, l,
+ nodelevel, l->level_stats[nodelevel]);
#endif
if (l->del)
vty_out(vty, "Skiplist %p has max level %d\n", l, l->level);
for (i = l->level; i >= 0; --i)
- vty_out(vty, " @%d: %ld\n", i,
- (long)((l->stats->forward[i])
- - (struct skiplistnode *)NULL));
+ vty_out(vty, " @%d: %d\n", i, l->level_stats[i]);
}
static void *scramble(int i)
int level; /* max lvl (1 + current # of levels in list) */
unsigned int count;
struct skiplistnode *header;
- struct skiplistnode *stats;
+ int *level_stats;
struct skiplistnode
*last; /* last real list item (NULL if empty list) */
extern unsigned int skiplist_count(register struct skiplist *l); /* in */
+struct vty;
extern void skiplist_debug(struct vty *vty, struct skiplist *l);
extern void skiplist_test(struct vty *vty);
return i;
}
+/* Make a specified index slot active and return its address. */
+void **vector_get_index(vector v, unsigned int i)
+{
+ vector_ensure(v, i);
+
+ if (v->active <= i)
+ v->active = i + 1;
+
+ return &v->index[i];
+}
+
/* Look up vector. */
void *vector_lookup(vector v, unsigned int i)
{
extern int vector_empty_slot(vector v);
extern int vector_set(vector v, void *val);
extern int vector_set_index(vector v, unsigned int i, void *val);
+extern void **vector_get_index(vector v, unsigned int i);
extern void vector_unset(vector v, unsigned int i);
extern void vector_unset_value(vector v, void *val);
extern void vector_remove(vector v, unsigned int ix);
return VRF_LOGNAME(vrf);
}
-vrf_id_t vrf_name_to_id(const char *name)
-{
- struct vrf *vrf;
- vrf_id_t vrf_id = VRF_DEFAULT; // Pending: need a way to return invalid
- // id/ routine not used.
-
- if (!name)
- return vrf_id;
- vrf = vrf_lookup_by_name(name);
- if (vrf)
- vrf_id = vrf->vrf_id;
-
- return vrf_id;
-}
-
/* Get the data pointer of the specified VRF. If not found, create one. */
void *vrf_info_get(vrf_id_t vrf_id)
{
extern struct vrf *vrf_get(vrf_id_t, const char *);
extern struct vrf *vrf_update(vrf_id_t new_vrf_id, const char *name);
extern const char *vrf_id_to_name(vrf_id_t vrf_id);
-extern vrf_id_t vrf_name_to_id(const char *);
#define VRF_LOGNAME(V) V ? V->name : "Unknown"
return -1;
}
+int zapi_srv6_locator_encode(struct stream *s, const struct srv6_locator *l)
+{
+ stream_putw(s, strlen(l->name));
+ stream_put(s, l->name, strlen(l->name));
+ stream_putw(s, l->prefix.prefixlen);
+ stream_put(s, &l->prefix.prefix, sizeof(l->prefix.prefix));
+ return 0;
+}
+
+int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l)
+{
+ uint16_t len = 0;
+
+ STREAM_GETW(s, len);
+ if (len > SRV6_LOCNAME_SIZE)
+ goto stream_failure;
+
+ STREAM_GET(l->name, s, len);
+ STREAM_GETW(s, l->prefix.prefixlen);
+ STREAM_GET(&l->prefix.prefix, s, sizeof(l->prefix.prefix));
+ l->prefix.family = AF_INET6;
+ return 0;
+
+stream_failure:
+ return -1;
+}
+
static int zapi_nhg_encode(struct stream *s, int cmd, struct zapi_nhg *api_nhg)
{
int i;
struct zapi_labels *zl);
extern int zapi_labels_decode(struct stream *s, struct zapi_labels *zl);
+extern int zapi_srv6_locator_encode(struct stream *s,
+ const struct srv6_locator *l);
+extern int zapi_srv6_locator_decode(struct stream *s, struct srv6_locator *l);
extern int zapi_srv6_locator_chunk_encode(struct stream *s,
const struct srv6_locator_chunk *c);
extern int zapi_srv6_locator_chunk_decode(struct stream *s,
struct zbuf payload, zb;
struct nlmsghdr *n;
- netlink_log_thread = NULL;
zbuf_init(&zb, buf, sizeof(buf), 0);
while (zbuf_recv(&zb, fd) > 0) {
struct zbuf *ibuf = &evmgr->ibuf;
struct zbuf msg;
- evmgr->t_read = NULL;
if (zbuf_read(ibuf, evmgr->fd, (size_t)-1) < 0) {
evmgr_connection_error(evmgr);
return 0;
struct event_manager *evmgr = THREAD_ARG(t);
int r;
- evmgr->t_write = NULL;
r = zbufq_write(&evmgr->obuf, evmgr->fd);
if (r > 0) {
thread_add_write(master, evmgr_write, evmgr, evmgr->fd,
struct event_manager *evmgr = THREAD_ARG(t);
int fd;
- evmgr->t_reconnect = NULL;
if (evmgr->fd >= 0 || !nhrp_event_socket_path)
return 0;
struct zbuf payload, zb;
struct nlmsghdr *n;
- netlink_mcast_log_thread = NULL;
zbuf_init(&zb, buf, sizeof(buf), 0);
while (zbuf_recv(&zb, fd) > 0) {
struct nhrp_registration *r = THREAD_ARG(t);
struct nhrp_cache *c;
- r->t_register = NULL;
if (r->timeout >= 16 && sockunion_family(&r->proto_addr) != AF_UNSPEC) {
nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid);
struct nhrp_extension_header *ext;
struct nhrp_cie_header *cie;
- r->t_register = NULL;
if (!nhrp_peer_check(r->peer, 2)) {
debugf(NHRP_DEBUG_COMMON, "NHS: Waiting link for %pSU",
&r->peer->vc->remote.nbma);
struct nhrp_registration *reg, *regn;
int i;
- nhs->t_resolve = NULL;
if (n < 0) {
/* Failed, retry in a moment */
thread_add_timer(master, nhrp_nhs_resolve, nhs, 5,
struct interface *ifp = p->ifp;
struct nhrp_interface *nifp = ifp->info;
- p->t_fallback = NULL;
if (p->online)
return 0;
{
struct nhrp_shortcut *s = THREAD_ARG(t);
- s->t_timer = NULL;
thread_add_timer(master, nhrp_shortcut_do_purge, s, s->holding_time / 3,
&s->t_timer);
s->expiring = 1;
struct zbuf *ibuf = &vici->ibuf;
struct zbuf pktbuf;
- vici->t_read = NULL;
if (zbuf_read(ibuf, vici->fd, (size_t)-1) < 0) {
vici_connection_error(vici);
return 0;
struct vici_conn *vici = THREAD_ARG(t);
int r;
- vici->t_write = NULL;
r = zbufq_write(&vici->obuf, vici->fd);
if (r > 0) {
thread_add_write(master, vici_write, vici, vici->fd,
int fd;
char *file_path;
- vici->t_reconnect = NULL;
if (vici->fd >= 0)
return 0;
unsigned char conf_debug_ospf6_abr;
+int ospf6_ls_origin_same(struct ospf6_path *o_path, struct ospf6_path *r_path)
+{
+ if (((o_path->origin.type == r_path->origin.type)
+ && (o_path->origin.id == r_path->origin.id)
+ && (o_path->origin.adv_router == r_path->origin.adv_router)))
+ return 1;
+ else
+ return 0;
+}
+
bool ospf6_check_and_set_router_abr(struct ospf6 *o)
{
struct listnode *node;
return 0;
}
+ if (route->type == OSPF6_DEST_TYPE_NETWORK) {
+ bool filter = false;
+
+ route_area =
+ ospf6_area_lookup(route->path.area_id, area->ospf6);
+ assert(route_area);
+
+ /* Check export-list */
+ if (EXPORT_LIST(route_area)
+ && access_list_apply(EXPORT_LIST(route_area),
+ &route->prefix)
+ == FILTER_DENY) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: prefix %pFX was denied by export-list",
+ __func__, &route->prefix);
+ filter = true;
+ }
+
+ /* Check output prefix-list */
+ if (PREFIX_LIST_OUT(route_area)
+ && prefix_list_apply(PREFIX_LIST_OUT(route_area),
+ &route->prefix)
+ != PREFIX_PERMIT) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: prefix %pFX was denied by prefix-list out",
+ __func__, &route->prefix);
+ filter = true;
+ }
+
+ /* Check import-list */
+ if (IMPORT_LIST(area)
+ && access_list_apply(IMPORT_LIST(area), &route->prefix)
+ == FILTER_DENY) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: prefix %pFX was denied by import-list",
+ __func__, &route->prefix);
+ filter = true;
+ }
+
+ /* Check input prefix-list */
+ if (PREFIX_LIST_IN(area)
+ && prefix_list_apply(PREFIX_LIST_IN(area), &route->prefix)
+ != PREFIX_PERMIT) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: prefix %pFX was denied by prefix-list in",
+ __func__, &route->prefix);
+ filter = true;
+ }
+
+ if (filter) {
+ if (summary) {
+ ospf6_route_remove(summary, summary_table);
+ if (old)
+ ospf6_lsa_purge(old);
+ }
+ 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)
}
}
- /* Check export list */
- if (EXPORT_NAME(area)) {
- if (EXPORT_LIST(area) == NULL)
- EXPORT_LIST(area) =
- access_list_lookup(AFI_IP6, EXPORT_NAME(area));
-
- if (EXPORT_LIST(area))
- if (access_list_apply(EXPORT_LIST(area), &route->prefix)
- == FILTER_DENY) {
- if (is_debug)
- zlog_debug(
- "prefix %pFX was denied by export list",
- &route->prefix);
- ospf6_abr_delete_route(route, summary,
- summary_table, old);
- return 0;
- }
- }
-
- /* Check filter-list */
- if (PREFIX_LIST_OUT(area))
- if (prefix_list_apply(PREFIX_LIST_OUT(area), &route->prefix)
- != PREFIX_PERMIT) {
- if (is_debug)
- zlog_debug(
- "prefix %pFX was denied by filter-list out",
- &route->prefix);
- ospf6_abr_delete_route(route, summary, summary_table,
- old);
-
- return 0;
- }
-
/* the route is going to be originated. store it in area's summary_table
*/
if (summary == NULL) {
struct ospf6_nexthop *nh, *rnh;
for (ALL_LIST_ELEMENTS(old_route->paths, anode, anext, o_path)) {
- if (o_path->area_id != route->path.area_id ||
- (memcmp(&(o_path)->origin, &(route)->path.origin,
- sizeof(struct ospf6_ls_origin)) != 0))
+ if (o_path->area_id != route->path.area_id
+ || !ospf6_ls_origin_same(o_path, &route->path))
continue;
if ((o_path->cost == route->path.cost) &&
return;
}
- /* Check import list */
- if (IMPORT_NAME(oa)) {
- if (IMPORT_LIST(oa) == NULL)
- IMPORT_LIST(oa) =
- access_list_lookup(AFI_IP6, IMPORT_NAME(oa));
-
- if (IMPORT_LIST(oa))
- if (access_list_apply(IMPORT_LIST(oa), &prefix)
- == FILTER_DENY) {
- if (is_debug)
- zlog_debug(
- "Prefix %pFX was denied by import-list",
- &prefix);
- if (old)
- ospf6_route_remove(old, table);
- return;
- }
- }
-
- /* Check input prefix-list */
- if (PREFIX_LIST_IN(oa)) {
- if (prefix_list_apply(PREFIX_LIST_IN(oa), &prefix)
- != PREFIX_PERMIT) {
- if (is_debug)
- zlog_debug(
- "Prefix %pFX was denied by prefix-list in",
- &prefix);
- if (old)
- ospf6_route_remove(old, table);
- return;
- }
- }
-
/* (5),(6): the path preference is handled by the sorting
in the routing table. Always install the path by substituting
old route (if any). */
__func__, &prefix, listcount(old->paths));
}
for (old_route = old; old_route; old_route = old_route->next) {
- if (!ospf6_route_is_same(old_route, route) ||
- (old_route->type != route->type) ||
- (old_route->path.type != route->path.type))
+
+ /* The route linked-list is grouped in batches of prefix.
+ * If the new prefix is not the same as the one of interest
+ * then we have walked over the end of the batch and so we
+ * should break rather than continuing unnecessarily.
+ */
+ if (!ospf6_route_is_same(old_route, route))
+ break;
+ if ((old_route->type != route->type)
+ || (old_route->path.type != route->path.type))
continue;
if ((ospf6_route_cmp(route, old_route) != 0)) {
for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
o_path)) {
- if (o_path->area_id == route->path.area_id &&
- (memcmp(&(o_path)->origin, &(route)->path.origin,
- sizeof(struct ospf6_ls_origin)) == 0))
+ if (o_path->area_id == route->path.area_id
+ && ospf6_ls_origin_same(o_path, &route->path))
break;
}
ospf6_abr_examin_summary(lsa, oa);
}
-void ospf6_abr_reimport(struct ospf6_area *oa)
-{
- struct ospf6_lsa *lsa;
- uint16_t type;
-
- type = htons(OSPF6_LSTYPE_INTER_ROUTER);
- for (ALL_LSDB_TYPED(oa->lsdb, type, lsa))
- ospf6_abr_examin_summary(lsa, oa);
-
- type = htons(OSPF6_LSTYPE_INTER_PREFIX);
- for (ALL_LSDB_TYPED(oa->lsdb, type, lsa))
- ospf6_abr_examin_summary(lsa, oa);
-}
-
-/* export filter removed so determine if we should reoriginate summary LSAs */
-void ospf6_abr_reexport(struct ospf6_area *oa)
-{
- struct ospf6_route *route;
-
- /* if not a ABR return success */
- if (!ospf6_check_and_set_router_abr(oa->ospf6))
- return;
-
- /* Redo summaries if required */
- for (route = ospf6_route_head(oa->ospf6->route_table); route;
- route = ospf6_route_next(route))
- ospf6_abr_originate_summary_to_area(route, oa);
-}
-
void ospf6_abr_prefix_resummarize(struct ospf6 *o)
{
struct ospf6_route *route;
extern void ospf6_abr_examin_brouter(uint32_t router_id,
struct ospf6_route *route,
struct ospf6 *ospf6);
-extern void ospf6_abr_reimport(struct ospf6_area *oa);
-extern void ospf6_abr_reexport(struct ospf6_area *oa);
extern void ospf6_abr_range_reset_cost(struct ospf6 *ospf6);
extern void ospf6_abr_prefix_resummarize(struct ospf6 *ospf6);
struct ospf6_route *route,
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);
+extern int ospf6_ls_origin_same(struct ospf6_path *o_path,
+ struct ospf6_path *r_path);
#endif /*OSPF6_ABR_H*/
#include "ospf6_intra.h"
#include "ospf6_abr.h"
#include "ospf6_asbr.h"
+#include "ospf6_zebra.h"
#include "ospf6d.h"
#include "lib/json.h"
#include "ospf6_nssa.h"
}
}
+static void ospf6_nssa_default_originate_set(struct ospf6 *ospf6,
+ struct ospf6_area *area,
+ int metric, int metric_type)
+{
+ if (!area->nssa_default_originate.enabled) {
+ area->nssa_default_originate.enabled = true;
+ if (++ospf6->nssa_default_import_check.refcnt == 1) {
+ ospf6->nssa_default_import_check.status = false;
+ ospf6_zebra_import_default_route(ospf6, false);
+ }
+ }
+
+ area->nssa_default_originate.metric_value = metric;
+ area->nssa_default_originate.metric_type = metric_type;
+}
+
+static void ospf6_nssa_default_originate_unset(struct ospf6 *ospf6,
+ struct ospf6_area *area)
+{
+ if (area->nssa_default_originate.enabled) {
+ area->nssa_default_originate.enabled = false;
+ if (--ospf6->nssa_default_import_check.refcnt == 0) {
+ ospf6->nssa_default_import_check.status = false;
+ ospf6_zebra_import_default_route(ospf6, true);
+ }
+ area->nssa_default_originate.metric_value = -1;
+ area->nssa_default_originate.metric_type = -1;
+ }
+}
+
/**
* Make new area structure.
*
}
if (IS_AREA_NSSA(oa)) {
vty_out(vty, " area %s nssa", oa->name);
+ if (oa->nssa_default_originate.enabled) {
+ vty_out(vty, " default-information-originate");
+ if (oa->nssa_default_originate.metric_value
+ != -1)
+ vty_out(vty, " metric %d",
+ oa->nssa_default_originate
+ .metric_value);
+ if (oa->nssa_default_originate.metric_type
+ != DEFAULT_METRIC_TYPE)
+ vty_out(vty, " metric-type 1");
+ }
if (oa->no_summary)
vty_out(vty, " no-summary");
vty_out(vty, "\n");
XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_IN(area));
PREFIX_NAME_IN(area) =
XSTRDUP(MTYPE_OSPF6_PLISTNAME, plistname);
- ospf6_abr_reimport(area);
} else {
PREFIX_LIST_OUT(area) = plist;
XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_OUT(area));
PREFIX_NAME_OUT(area) =
XSTRDUP(MTYPE_OSPF6_PLISTNAME, plistname);
-
- /* Redo summaries if required */
- ospf6_abr_reexport(area);
}
+ /* Redo summaries if required */
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
+
return CMD_SUCCESS;
}
PREFIX_LIST_IN(area) = NULL;
XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_IN(area));
- ospf6_abr_reimport(area);
} else {
if (PREFIX_NAME_OUT(area))
if (!strmatch(PREFIX_NAME_OUT(area), plistname))
XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_OUT(area));
PREFIX_LIST_OUT(area) = NULL;
- ospf6_abr_reexport(area);
}
+ /* Redo summaries if required */
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
+
return CMD_SUCCESS;
}
struct ospf6 *ospf6;
for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) {
+ bool update = false;
+
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) {
if (IMPORT_NAME(oa)
- && strcmp(IMPORT_NAME(oa), access->name) == 0)
- ospf6_abr_reimport(oa);
+ && strcmp(IMPORT_NAME(oa), access->name) == 0) {
+ IMPORT_LIST(oa) = access_list_lookup(
+ AFI_IP6, IMPORT_NAME(oa));
+ update = true;
+ }
if (EXPORT_NAME(oa)
- && strcmp(EXPORT_NAME(oa), access->name) == 0)
- ospf6_abr_reexport(oa);
+ && strcmp(EXPORT_NAME(oa), access->name) == 0) {
+ EXPORT_LIST(oa) = access_list_lookup(
+ AFI_IP6, EXPORT_NAME(oa));
+ update = true;
+ }
}
+
+ if (update && ospf6_check_and_set_router_abr(ospf6))
+ ospf6_schedule_abr_task(ospf6);
}
}
-void ospf6_area_plist_update(struct prefix_list *plist, int add)
+void ospf6_plist_update(struct prefix_list *plist)
{
struct listnode *node, *nnode;
struct ospf6_area *oa;
const char *name = prefix_list_name(plist);
struct ospf6 *ospf6 = NULL;
-
- if (!om6->ospf6)
+ if (prefix_list_afi(plist) != AFI_IP6)
return;
for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) {
+ bool update = false;
+
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) {
if (PREFIX_NAME_IN(oa)
&& !strcmp(PREFIX_NAME_IN(oa), name)) {
- PREFIX_LIST_IN(oa) = add ? plist : NULL;
- ospf6_abr_reexport(oa);
+ PREFIX_LIST_IN(oa) = prefix_list_lookup(
+ AFI_IP6, PREFIX_NAME_IN(oa));
+ update = true;
}
if (PREFIX_NAME_OUT(oa)
&& !strcmp(PREFIX_NAME_OUT(oa), name)) {
- PREFIX_LIST_OUT(oa) = add ? plist : NULL;
- ospf6_abr_reexport(oa);
+ PREFIX_LIST_OUT(oa) = prefix_list_lookup(
+ AFI_IP6, PREFIX_NAME_OUT(oa));
+ update = true;
}
}
+
+ if (update && ospf6_check_and_set_router_abr(ospf6))
+ ospf6_schedule_abr_task(ospf6);
}
}
free(IMPORT_NAME(area));
IMPORT_NAME(area) = strdup(argv[idx_name]->arg);
- ospf6_abr_reimport(area);
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
return CMD_SUCCESS;
}
OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, area, ospf6);
- IMPORT_LIST(area) = 0;
+ IMPORT_LIST(area) = NULL;
if (IMPORT_NAME(area))
free(IMPORT_NAME(area));
IMPORT_NAME(area) = NULL;
- ospf6_abr_reimport(area);
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
return CMD_SUCCESS;
}
EXPORT_NAME(area) = strdup(argv[idx_name]->arg);
/* Redo summaries if required */
- ospf6_abr_reexport(area);
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
return CMD_SUCCESS;
}
OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, area, ospf6);
- EXPORT_LIST(area) = 0;
+ EXPORT_LIST(area) = NULL;
if (EXPORT_NAME(area))
free(EXPORT_NAME(area));
EXPORT_NAME(area) = NULL;
- ospf6_abr_reexport(area);
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
return CMD_SUCCESS;
}
int idx_vrf = 0;
bool uj = use_json(argc, argv);
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) {
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_ipv4 += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_ipv4 += 2;
}
DEFPY(ospf6_area_nssa, ospf6_area_nssa_cmd,
- "area <A.B.C.D|(0-4294967295)>$area_str nssa [no-summary$no_summary]",
+ "area <A.B.C.D|(0-4294967295)>$area_str nssa\
+ [{\
+ default-information-originate$dflt_originate [{metric (0-16777214)$mval|metric-type (1-2)$mtype}]\
+ |no-summary$no_summary\
+ }]",
"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"
+ "Originate Type 7 default into NSSA area\n"
+ "OSPFv3 default metric\n"
+ "OSPFv3 metric\n"
+ "OSPFv3 metric type for default routes\n"
+ "Set OSPFv3 External Type 1/2 metrics\n"
"Do not inject inter-area routes into area\n")
{
struct ospf6_area *area;
return CMD_WARNING_CONFIG_FAILED;
}
+ if (dflt_originate) {
+ if (mval_str == NULL)
+ mval = -1;
+ if (mtype_str == NULL)
+ mtype = DEFAULT_METRIC_TYPE;
+ ospf6_nssa_default_originate_set(ospf6, area, mval, mtype);
+ } else
+ ospf6_nssa_default_originate_unset(ospf6, area);
+
if (no_summary)
ospf6_area_no_summary_set(ospf6, area);
else
ospf6_area_no_summary_unset(ospf6, area);
- if (ospf6_check_and_set_router_abr(ospf6))
+
+ if (ospf6_check_and_set_router_abr(ospf6)) {
ospf6_abr_defaults_to_stub(ospf6);
+ ospf6_abr_nssa_type_7_defaults(ospf6);
+ }
return CMD_SUCCESS;
}
DEFPY(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd,
- "no area <A.B.C.D|(0-4294967295)>$area_str nssa [no-summary$no_summary]",
+ "no area <A.B.C.D|(0-4294967295)>$area_str nssa\
+ [{\
+ default-information-originate [{metric (0-16777214)|metric-type (1-2)}]\
+ |no-summary\
+ }]",
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"
+ "Originate Type 7 default into NSSA area\n"
+ "OSPFv3 default metric\n"
+ "OSPFv3 metric\n"
+ "OSPFv3 metric type for default routes\n"
+ "Set OSPFv3 External Type 1/2 metrics\n"
"Do not inject inter-area routes into area\n")
{
struct ospf6_area *area;
ospf6_area_nssa_unset(ospf6, area);
ospf6_area_no_summary_unset(ospf6, area);
+ ospf6_nssa_default_originate_unset(ospf6, area);
return CMD_SUCCESS;
}
struct listnode *node, *nnode;
struct ospf6 *ospf6;
- if (!om6->ospf6)
- return;
for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) {
for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa))
if (listnode_lookup(oa->if_list, oi))
/* Area type */
int no_summary;
+ /* NSSA default-information-originate */
+ struct {
+ bool enabled;
+ int metric_type;
+ int metric_value;
+ } nssa_default_originate;
+
/* Brouter traversal protection */
- int intra_brouter_calc;
+ bool intra_brouter_calc;
/* OSPF interface list */
struct list *if_list;
extern int ospf6_area_cmp(void *va, void *vb);
-extern struct ospf6_area *ospf6_area_create(uint32_t, struct ospf6 *, int);
-extern void ospf6_area_delete(struct ospf6_area *);
-extern struct ospf6_area *ospf6_area_lookup(uint32_t, struct ospf6 *);
+extern struct ospf6_area *ospf6_area_create(uint32_t area_id,
+ struct ospf6 *ospf6, int df);
+extern void ospf6_area_delete(struct ospf6_area *oa);
+extern struct ospf6_area *ospf6_area_lookup(uint32_t area_id,
+ struct ospf6 *ospf6);
extern struct ospf6_area *ospf6_area_lookup_by_area_id(uint32_t area_id);
extern void ospf6_area_stub_unset(struct ospf6 *ospf6, struct ospf6_area *area);
-extern void ospf6_area_enable(struct ospf6_area *);
-extern void ospf6_area_disable(struct ospf6_area *);
+extern void ospf6_area_enable(struct ospf6_area *oa);
+extern void ospf6_area_disable(struct ospf6_area *oa);
-extern void ospf6_area_show(struct vty *, struct ospf6_area *,
+extern void ospf6_area_show(struct vty *vty, struct ospf6_area *oa,
json_object *json_areas, bool use_json);
-extern void ospf6_area_plist_update(struct prefix_list *plist, int add);
+extern void ospf6_plist_update(struct prefix_list *plist);
extern void ospf6_filter_update(struct access_list *access);
extern void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6);
extern void ospf6_area_init(void);
#include "ospf6_abr.h"
#include "ospf6_intra.h"
#include "ospf6_flood.h"
+#include "ospf6_nssa.h"
#include "ospf6d.h"
#include "ospf6_spf.h"
#include "ospf6_nssa.h"
+#include "ospf6_gr.h"
#include "lib/json.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info");
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
if (IS_AREA_NSSA(oa))
- ospf6_nssa_lsa_originate(route, oa);
+ ospf6_nssa_lsa_originate(route, oa, true);
}
return lsa;
struct ospf6_as_external_lsa *as_external_lsa;
caddr_t p;
+ if (ospf6->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Graceful Restart in progress, don't originate LSA");
+ return NULL;
+ }
+
if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL))
zlog_debug("Originate AS-External-LSA for %pFX",
&route->prefix);
next_route = old_route->next;
- if (!ospf6_route_is_same(old_route, route)
- || (old_route->path.type != route->path.type))
+ /* The route linked-list is grouped in batches of prefix.
+ * If the new prefix is not the same as the one of interest
+ * then we have walked over the end of the batch and so we
+ * should break rather than continuing unnecessarily.
+ */
+ if (!ospf6_route_is_same(old_route, route))
+ break;
+ if (old_route->path.type != route->path.type)
continue;
/* Current and New route has same origin,
* origin.
*/
if (o_path->area_id != route->path.area_id
- || (memcmp(&(o_path)->origin, &(route)->path.origin,
- sizeof(struct ospf6_ls_origin))
- != 0))
+ || !ospf6_ls_origin_same(o_path, &route->path))
continue;
/* Cost is not same then delete current path */
/* Add new route */
for (old_route = old; old_route; old_route = old_route->next) {
- /* Current and New Route prefix or route type
- * is not same skip this current node.
+ /* The route linked-list is grouped in batches of prefix.
+ * If the new prefix is not the same as the one of interest
+ * then we have walked over the end of the batch and so we
+ * should break rather than continuing unnecessarily.
*/
- if (!ospf6_route_is_same(old_route, route)
- || (old_route->path.type != route->path.type))
+ if (!ospf6_route_is_same(old_route, route))
+ break;
+ if (old_route->path.type != route->path.type)
continue;
/* Old Route and New Route have Equal Cost, Merge NHs */
for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
o_path)) {
if (o_path->area_id == route->path.area_id
- && (memcmp(&(o_path)->origin,
- &(route)->path.origin,
- sizeof(struct ospf6_ls_origin))
- == 0))
+ && ospf6_ls_origin_same(o_path, &route->path))
break;
}
/* If path is not found in old_route paths's list,
if (IS_OSPF6_DEBUG_ASBR)
zlog_debug("%s: trigger redistribute reset thread", __func__);
- ospf6->t_distribute_update = NULL;
thread_add_timer_msec(master, ospf6_asbr_routemap_update_timer, ospf6,
OSPF_MIN_LS_INTERVAL,
&ospf6->t_distribute_update);
memset(&tinfo, 0, sizeof(tinfo));
if (IS_OSPF6_DEBUG_ASBR)
- zlog_debug("Redistribute %pFX (%s)", prefix, ZROUTE_NAME(type));
+ zlog_debug("Redistribute %pFX (%s)", prefix,
+ type == DEFAULT_ROUTE
+ ? "default-information-originate"
+ : ZROUTE_NAME(type));
/* if route-map was specified but not found, do not advertise */
if (ROUTEMAP_NAME(red)) {
vty_out(vty, " redistribute %s", ZROUTE_NAME(type));
if (red->dmetric.value >= 0)
vty_out(vty, " metric %d", red->dmetric.value);
- if (red->dmetric.type != DEFAULT_METRIC_TYPE)
+ if (red->dmetric.type == 1)
vty_out(vty, " metric-type 1");
if (ROUTEMAP_NAME(red))
vty_out(vty, " route-map %s", ROUTEMAP_NAME(red));
json_object *json_array_routes = NULL;
json_object *json_array_redistribute = NULL;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (uj) {
return lsdb_self;
}
-void ospf6_lsa_originate(struct ospf6_lsa *lsa)
+void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa)
{
struct ospf6_lsa *old;
struct ospf6_lsdb *lsdb_self;
/* if the new LSA does not differ from previous,
suppress this update of the LSA */
- if (old && !OSPF6_LSA_IS_DIFFER(lsa, old)) {
+ if (old && !OSPF6_LSA_IS_DIFFER(lsa, old)
+ && !ospf6->gr_info.finishing_restart) {
if (IS_OSPF6_DEBUG_ORIGINATE_TYPE(lsa->header->type))
zlog_debug("Suppress updating LSA: %s", lsa->name);
ospf6_lsa_delete(lsa);
void ospf6_lsa_originate_process(struct ospf6_lsa *lsa, struct ospf6 *process)
{
lsa->lsdb = process->lsdb;
- ospf6_lsa_originate(lsa);
+ ospf6_lsa_originate(process, lsa);
}
void ospf6_lsa_originate_area(struct ospf6_lsa *lsa, struct ospf6_area *oa)
{
lsa->lsdb = oa->lsdb;
- ospf6_lsa_originate(lsa);
+ ospf6_lsa_originate(oa->ospf6, lsa);
}
void ospf6_lsa_originate_interface(struct ospf6_lsa *lsa,
struct ospf6_interface *oi)
{
lsa->lsdb = oi->lsdb;
- ospf6_lsa_originate(lsa);
+ ospf6_lsa_originate(oi->area->ospf6, lsa);
}
void ospf6_remove_id_from_external_id_table(struct ospf6 *ospf6,
monotime(&now);
if (!OSPF6_LSA_IS_MAXAGE(lsa)) {
- lsa->expire = NULL;
thread_add_timer(master, ospf6_lsa_expire, lsa,
OSPF_LSA_MAXAGE + lsa->birth.tv_sec
- now.tv_sec,
lsa->installed = now;
/* Topo change handling */
- if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type))) {
+ if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type))
+ && !CHECK_FLAG(lsa->flag, OSPF6_LSA_DUPLICATE)) {
/* check if it is new lsa ? or existing lsa got modified ?*/
if (!old || OSPF6_LSA_IS_CHANGED(old, lsa)) {
/* reschedule retransmissions to all neighbors */
for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) {
THREAD_OFF(on->thread_send_lsupdate);
- on->thread_send_lsupdate = NULL;
thread_add_event(master, ospf6_lsupdate_send_neighbor,
on, 0, &on->thread_send_lsupdate);
}
/* if no database copy or received is more recent */
if (old == NULL || ismore_recent < 0) {
+ bool self_originated;
+
/* in case we have no database copy */
ismore_recent = -1;
if (old)
ospf6_flood_clear(old);
- /* (b) immediately flood and (c) remove from all retrans-list */
- /* Prevent self-originated LSA to be flooded. this is to make
- reoriginated instance of the LSA not to be rejected by other
- routers
- due to MinLSArrival. */
- if (new->header->adv_router
- != from->ospf6_if->area->ospf6->router_id)
- ospf6_flood(from, new);
+ self_originated = (new->header->adv_router
+ == from->ospf6_if->area->ospf6->router_id);
- /* Received Grace-LSA */
- if (IS_GRACE_LSA(new)) {
+ /* Received non-self-originated Grace LSA. */
+ if (IS_GRACE_LSA(new) && !self_originated) {
struct ospf6 *ospf6;
ospf6 = ospf6_get_by_lsdb(new);
}
}
+ /* (b) immediately flood and (c) remove from all retrans-list */
+ /* Prevent self-originated LSA to be flooded. this is to make
+ * reoriginated instance of the LSA not to be rejected by other
+ * routers due to MinLSArrival.
+ */
+ if (!self_originated)
+ ospf6_flood(from, new);
+
/* (d), installing lsdb, which may cause routing
table calculation (replacing database copy) */
ospf6_install_lsa(new);
ospf6_acknowledge_lsa(new, ismore_recent, from);
/* (f) Self Originated LSA, section 13.4 */
- if (new->header->adv_router
- == from->ospf6_if->area->ospf6->router_id) {
+ if (self_originated) {
+ if (from->ospf6_if->area->ospf6->gr_info
+ .restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Graceful Restart in progress -- not flushing self-originated LSA: %s",
+ new->name);
+ return;
+ }
+
/* Self-originated LSA (newer than ours) is received
from
another router. We have to make a new instance of the
"Newer instance of the self-originated LSA");
zlog_debug("Schedule reorigination");
}
- new->refresh = NULL;
thread_add_event(master, ospf6_lsa_refresh, new, 0,
&new->refresh);
}
+ struct ospf6 *ospf6 = from->ospf6_if->area->ospf6;
+ struct ospf6_area *area = from->ospf6_if->area;
+ if (ospf6->gr_info.restart_in_progress)
+ ospf6_gr_check_lsdb_consistency(ospf6, area);
+
return;
}
ospf6_lsa_delete(new);
return;
}
- return;
}
}
extern struct ospf6_lsdb *ospf6_get_scoped_lsdb_self(struct ospf6_lsa *lsa);
/* origination & purging */
-extern void ospf6_lsa_originate(struct ospf6_lsa *lsa);
+extern void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa);
extern void ospf6_lsa_originate_process(struct ospf6_lsa *lsa,
struct ospf6 *process);
extern void ospf6_lsa_originate_area(struct ospf6_lsa *lsa,
--- /dev/null
+/*
+ * This is an implementation of RFC 5187 Graceful Restart.
+ *
+ * Copyright 2021 NetDEF (c), All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "command.h"
+#include "table.h"
+#include "vty.h"
+#include "log.h"
+#include "hook.h"
+#include "printfrr.h"
+
+#include "ospf6d/ospf6_lsa.h"
+#include "ospf6d/ospf6_lsdb.h"
+#include "ospf6d/ospf6_route.h"
+#include "ospf6d/ospf6_area.h"
+#include "ospf6d/ospf6_interface.h"
+#include "ospf6d/ospf6d.h"
+#include "ospf6d/ospf6_asbr.h"
+#include "ospf6d/ospf6_zebra.h"
+#include "ospf6d/ospf6_message.h"
+#include "ospf6d/ospf6_neighbor.h"
+#include "ospf6d/ospf6_flood.h"
+#include "ospf6d/ospf6_intra.h"
+#include "ospf6d/ospf6_spf.h"
+#include "ospf6d/ospf6_gr.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "ospf6d/ospf6_gr_clippy.c"
+#endif
+
+static void ospf6_gr_nvm_delete(struct ospf6 *ospf6);
+
+/* Originate and install Grace-LSA for a given interface. */
+static int ospf6_gr_lsa_originate(struct ospf6_interface *oi)
+{
+ struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info;
+ struct ospf6_lsa_header *lsa_header;
+ struct ospf6_grace_lsa *grace_lsa;
+ struct ospf6_lsa *lsa;
+ char buffer[OSPF6_MAX_LSASIZE];
+
+ if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
+ zlog_debug("Originate Link-LSA for Interface %s",
+ oi->interface->name);
+
+ /* prepare buffer */
+ memset(buffer, 0, sizeof(buffer));
+ lsa_header = (struct ospf6_lsa_header *)buffer;
+ grace_lsa =
+ (struct ospf6_grace_lsa *)((caddr_t)lsa_header
+ + sizeof(struct ospf6_lsa_header));
+
+ /* Put grace period. */
+ grace_lsa->tlv_period.header.type = htons(GRACE_PERIOD_TYPE);
+ grace_lsa->tlv_period.header.length = htons(GRACE_PERIOD_LENGTH);
+ grace_lsa->tlv_period.interval = htonl(gr_info->grace_period);
+
+ /* Put restart reason. */
+ grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE);
+ grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
+ if (gr_info->restart_support)
+ grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART;
+ else
+ grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART;
+
+ /* Fill LSA Header */
+ lsa_header->age = 0;
+ lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA);
+ lsa_header->id = htonl(oi->interface->ifindex);
+ lsa_header->adv_router = oi->area->ospf6->router_id;
+ lsa_header->seqnum =
+ ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
+ lsa_header->adv_router, oi->lsdb);
+ lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa));
+
+ /* LSA checksum */
+ ospf6_lsa_checksum(lsa_header);
+
+ /* create LSA */
+ lsa = ospf6_lsa_create(lsa_header);
+
+ /* Originate */
+ ospf6_lsa_originate_interface(lsa, oi);
+
+ return 0;
+}
+
+/* Flush all self-originated Grace-LSAs. */
+static void ospf6_gr_flush_grace_lsas(struct ospf6 *ospf6)
+{
+ struct ospf6_area *area;
+ struct listnode *anode;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
+ struct ospf6_lsa *lsa;
+ struct ospf6_interface *oi;
+ struct listnode *inode;
+
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: flushing self-originated Grace-LSAs [area %pI4]",
+ &area->area_id);
+
+ for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_GRACE_LSA),
+ htonl(oi->interface->ifindex),
+ oi->area->ospf6->router_id,
+ oi->lsdb);
+ if (!lsa) {
+ zlog_warn(
+ "%s: Grace-LSA not found [interface %s] [area %pI4]",
+ __func__, oi->interface->name,
+ &area->area_id);
+ continue;
+ }
+
+ ospf6_lsa_purge(lsa);
+ }
+ }
+}
+
+/* Exit from the Graceful Restart mode. */
+static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
+{
+ struct ospf6_area *area;
+ struct listnode *onode, *anode;
+
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug("GR: exiting graceful restart: %s", reason);
+
+ ospf6->gr_info.restart_in_progress = false;
+ ospf6->gr_info.finishing_restart = true;
+ THREAD_OFF(ospf6->gr_info.t_grace_period);
+
+ /* Record in non-volatile memory that the restart is complete. */
+ ospf6_gr_nvm_delete(ospf6);
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) {
+ struct ospf6_interface *oi;
+
+ /*
+ * 1) The router should reoriginate its router-LSAs for all
+ * attached areas in order to make sure they have the correct
+ * contents.
+ */
+ OSPF6_ROUTER_LSA_EXECUTE(area);
+
+ for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) {
+ OSPF6_LINK_LSA_EXECUTE(oi);
+
+ /*
+ * 2) The router should reoriginate network-LSAs on all
+ * segments where it is the Designated Router.
+ */
+ if (oi->state == OSPF6_INTERFACE_DR)
+ OSPF6_NETWORK_LSA_EXECUTE(oi);
+ }
+ }
+
+ /*
+ * 3) The router reruns its OSPF routing calculations, this time
+ * installing the results into the system forwarding table, and
+ * originating summary-LSAs, Type-7 LSAs and AS-external-LSAs as
+ * necessary.
+ *
+ * 4) Any remnant entries in the system forwarding table that were
+ * installed before the restart, but that are no longer valid,
+ * should be removed.
+ */
+ ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_GR_FINISH);
+
+ /* 6) Any grace-LSAs that the router originated should be flushed. */
+ ospf6_gr_flush_grace_lsas(ospf6);
+}
+
+#define RTR_LSA_MISSING 0
+#define RTR_LSA_ADJ_FOUND 1
+#define RTR_LSA_ADJ_NOT_FOUND 2
+
+/* Check if a Router-LSA exists and if it contains a given link. */
+static int ospf6_router_lsa_contains_adj(struct ospf6_area *area,
+ in_addr_t adv_router,
+ in_addr_t neighbor_router_id)
+{
+ uint16_t type;
+ struct ospf6_lsa *lsa;
+ bool empty = true;
+
+ type = ntohs(OSPF6_LSTYPE_ROUTER);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, adv_router, lsa)) {
+ struct ospf6_router_lsa *router_lsa;
+ char *start, *end, *current;
+
+ empty = false;
+ router_lsa = (struct ospf6_router_lsa
+ *)((char *)lsa->header
+ + sizeof(struct ospf6_lsa_header));
+
+ /* Iterate over all interfaces in the Router-LSA. */
+ start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
+ end = (char *)lsa->header + ntohs(lsa->header->length);
+ for (current = start;
+ current + sizeof(struct ospf6_router_lsdesc) <= end;
+ current += sizeof(struct ospf6_router_lsdesc)) {
+ struct ospf6_router_lsdesc *lsdesc;
+
+ lsdesc = (struct ospf6_router_lsdesc *)current;
+ if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT)
+ continue;
+
+ if (lsdesc->neighbor_router_id == neighbor_router_id)
+ return RTR_LSA_ADJ_FOUND;
+ }
+ }
+
+ if (empty)
+ return RTR_LSA_MISSING;
+
+ return RTR_LSA_ADJ_NOT_FOUND;
+}
+
+static bool ospf6_gr_check_router_lsa_consistency(struct ospf6 *ospf6,
+ struct ospf6_area *area,
+ struct ospf6_lsa *lsa)
+{
+ if (lsa->header->adv_router == ospf6->router_id) {
+ struct ospf6_router_lsa *router_lsa;
+ char *start, *end, *current;
+
+ router_lsa = (struct ospf6_router_lsa
+ *)((char *)lsa->header
+ + sizeof(struct ospf6_lsa_header));
+
+ /* Iterate over all interfaces in the Router-LSA. */
+ start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
+ end = (char *)lsa->header + ntohs(lsa->header->length);
+ for (current = start;
+ current + sizeof(struct ospf6_router_lsdesc) <= end;
+ current += sizeof(struct ospf6_router_lsdesc)) {
+ struct ospf6_router_lsdesc *lsdesc;
+
+ lsdesc = (struct ospf6_router_lsdesc *)current;
+ if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT)
+ continue;
+
+ if (ospf6_router_lsa_contains_adj(
+ area, lsdesc->neighbor_router_id,
+ ospf6->router_id)
+ == RTR_LSA_ADJ_NOT_FOUND)
+ return false;
+ }
+ } else {
+ int adj1, adj2;
+
+ adj1 = ospf6_router_lsa_contains_adj(area, ospf6->router_id,
+ lsa->header->adv_router);
+ adj2 = ospf6_router_lsa_contains_adj(
+ area, lsa->header->adv_router, ospf6->router_id);
+ if ((adj1 == RTR_LSA_ADJ_FOUND && adj2 == RTR_LSA_ADJ_NOT_FOUND)
+ || (adj1 == RTR_LSA_ADJ_NOT_FOUND
+ && adj2 == RTR_LSA_ADJ_FOUND))
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Check for LSAs that are inconsistent with the pre-restart LSAs, and abort the
+ * ongoing graceful restart when that's the case.
+ */
+void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf6,
+ struct ospf6_area *area)
+{
+ uint16_t type;
+ struct ospf6_lsa *lsa;
+
+ type = ntohs(OSPF6_LSTYPE_ROUTER);
+ for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) {
+ if (!ospf6_gr_check_router_lsa_consistency(ospf6, area, lsa)) {
+ char reason[256];
+
+ snprintfrr(reason, sizeof(reason),
+ "detected inconsistent LSA %s [area %pI4]",
+ lsa->name, &area->area_id);
+ ospf6_gr_restart_exit(ospf6, reason);
+ return;
+ }
+ }
+}
+
+/* Check if there's a fully formed adjacency with the given neighbor ID. */
+static bool ospf6_gr_check_adj_id(struct ospf6_area *area,
+ in_addr_t neighbor_router_id)
+{
+ struct ospf6_neighbor *nbr;
+
+ nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id);
+ if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug("GR: missing adjacency to router %pI4",
+ &neighbor_router_id);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ospf6_gr_check_adjs_lsa_transit(struct ospf6_area *area,
+ in_addr_t neighbor_router_id,
+ uint32_t neighbor_interface_id)
+{
+ struct ospf6 *ospf6 = area->ospf6;
+
+ /* Check if we are the DR. */
+ if (neighbor_router_id == ospf6->router_id) {
+ struct ospf6_lsa *lsa;
+ char *start, *end, *current;
+ struct ospf6_network_lsa *network_lsa;
+ struct ospf6_network_lsdesc *lsdesc;
+
+ /* Lookup Network LSA corresponding to this interface. */
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK),
+ neighbor_interface_id,
+ neighbor_router_id, area->lsdb);
+ if (!lsa)
+ return false;
+
+ /* Iterate over all routers present in the network. */
+ network_lsa = (struct ospf6_network_lsa
+ *)((char *)lsa->header
+ + sizeof(struct ospf6_lsa_header));
+ start = (char *)network_lsa + sizeof(struct ospf6_network_lsa);
+ end = (char *)lsa->header + ntohs(lsa->header->length);
+ for (current = start;
+ current + sizeof(struct ospf6_network_lsdesc) <= end;
+ current += sizeof(struct ospf6_network_lsdesc)) {
+ lsdesc = (struct ospf6_network_lsdesc *)current;
+
+ /* Skip self in the pseudonode. */
+ if (lsdesc->router_id == ospf6->router_id)
+ continue;
+
+ /*
+ * Check if there's a fully formed adjacency with this
+ * router.
+ */
+ if (!ospf6_gr_check_adj_id(area, lsdesc->router_id))
+ return false;
+ }
+ } else {
+ struct ospf6_neighbor *nbr;
+
+ /* Check if there's a fully formed adjacency with the DR. */
+ nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id);
+ if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: missing adjacency to DR router %pI4",
+ &neighbor_router_id);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool ospf6_gr_check_adjs_lsa(struct ospf6_area *area,
+ struct ospf6_lsa *lsa)
+{
+ struct ospf6_router_lsa *router_lsa;
+ char *start, *end, *current;
+
+ router_lsa =
+ (struct ospf6_router_lsa *)((char *)lsa->header
+ + sizeof(struct ospf6_lsa_header));
+
+ /* Iterate over all interfaces in the Router-LSA. */
+ start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
+ end = (char *)lsa->header + ntohs(lsa->header->length);
+ for (current = start;
+ current + sizeof(struct ospf6_router_lsdesc) <= end;
+ current += sizeof(struct ospf6_router_lsdesc)) {
+ struct ospf6_router_lsdesc *lsdesc;
+
+ lsdesc = (struct ospf6_router_lsdesc *)current;
+ switch (lsdesc->type) {
+ case OSPF6_ROUTER_LSDESC_POINTTOPOINT:
+ if (!ospf6_gr_check_adj_id(area,
+ lsdesc->neighbor_router_id))
+ return false;
+ break;
+ case OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK:
+ if (!ospf6_gr_check_adjs_lsa_transit(
+ area, lsdesc->neighbor_router_id,
+ lsdesc->neighbor_interface_id))
+ return false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Check if all adjacencies prior to the restart were reestablished.
+ *
+ * This is done using pre-restart Router LSAs and pre-restart Network LSAs
+ * received from the helping neighbors.
+ */
+static bool ospf6_gr_check_adjs(struct ospf6 *ospf6)
+{
+ struct ospf6_area *area;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) {
+ uint16_t type;
+ uint32_t router;
+ struct ospf6_lsa *lsa_self;
+ bool found = false;
+
+ type = ntohs(OSPF6_LSTYPE_ROUTER);
+ router = ospf6->router_id;
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, router,
+ lsa_self)) {
+ found = true;
+ if (!ospf6_gr_check_adjs_lsa(area, lsa_self))
+ return false;
+ }
+ if (!found)
+ return false;
+ }
+
+ return true;
+}
+
+/* Handling of grace period expiry. */
+static int ospf6_gr_grace_period_expired(struct thread *thread)
+{
+ struct ospf6 *ospf6 = THREAD_ARG(thread);
+
+ ospf6->gr_info.t_grace_period = NULL;
+ ospf6_gr_restart_exit(ospf6, "grace period has expired");
+
+ return 0;
+}
+
+/*
+ * Record in non-volatile memory that the given OSPF instance is attempting to
+ * perform a graceful restart.
+ */
+static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
+{
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+ json_object *json_instance;
+
+ inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file((char *)OSPF6D_GR_STATE);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "instances", &json_instances);
+ if (!json_instances) {
+ json_instances = json_object_new_object();
+ json_object_object_add(json, "instances", json_instances);
+ }
+
+ json_object_object_get_ex(json_instances, inst_name, &json_instance);
+ if (!json_instance) {
+ json_instance = json_object_new_object();
+ json_object_object_add(json_instances, inst_name,
+ json_instance);
+ }
+
+ /*
+ * Record not only the grace period, but also a UNIX timestamp
+ * corresponding to the end of that period. That way, once ospf6d is
+ * restarted, it will be possible to take into account the time that
+ * passed while ospf6d wasn't running.
+ */
+ json_object_int_add(json_instance, "gracePeriod",
+ ospf6->gr_info.grace_period);
+ json_object_int_add(json_instance, "timestamp",
+ time(NULL) + ospf6->gr_info.grace_period);
+
+ json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
+ JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/*
+ * Delete GR status information about the given OSPF instance from non-volatile
+ * memory.
+ */
+static void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
+{
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+
+ inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file((char *)OSPF6D_GR_STATE);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "instances", &json_instances);
+ if (!json_instances) {
+ json_instances = json_object_new_object();
+ json_object_object_add(json, "instances", json_instances);
+ }
+
+ json_object_object_del(json_instances, inst_name);
+
+ json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
+ JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/*
+ * Fetch from non-volatile memory whether the given OSPF instance is performing
+ * a graceful shutdown or not.
+ */
+void ospf6_gr_nvm_read(struct ospf6 *ospf6)
+{
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+ json_object *json_instance;
+ json_object *json_timestamp;
+ time_t timestamp = 0;
+
+ inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file((char *)OSPF6D_GR_STATE);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "instances", &json_instances);
+ if (!json_instances) {
+ json_instances = json_object_new_object();
+ json_object_object_add(json, "instances", json_instances);
+ }
+
+ json_object_object_get_ex(json_instances, inst_name, &json_instance);
+ if (!json_instance) {
+ json_instance = json_object_new_object();
+ json_object_object_add(json_instances, inst_name,
+ json_instance);
+ }
+
+ json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
+ if (json_timestamp) {
+ time_t now;
+ unsigned long remaining_time;
+
+ /* Check if the grace period has already expired. */
+ now = time(NULL);
+ timestamp = json_object_get_int(json_timestamp);
+ if (now > timestamp) {
+ ospf6_gr_restart_exit(
+ ospf6, "grace period has expired already");
+ } else {
+ /* Schedule grace period timeout. */
+ ospf6->gr_info.restart_in_progress = true;
+ remaining_time = timestamp - time(NULL);
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: remaining time until grace period expires: %lu(s)",
+ remaining_time);
+ thread_add_timer(master, ospf6_gr_grace_period_expired,
+ ospf6, remaining_time,
+ &ospf6->gr_info.t_grace_period);
+ }
+ }
+
+ json_object_object_del(json_instances, inst_name);
+
+ json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
+ JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/* Prepare to start a Graceful Restart. */
+static void ospf6_gr_prepare(void)
+{
+ struct ospf6 *ospf6;
+ struct ospf6_interface *oi;
+ struct listnode *onode, *anode, *inode;
+
+ for (ALL_LIST_ELEMENTS_RO(om6->ospf6, onode, ospf6)) {
+ struct ospf6_area *area;
+
+ if (!ospf6->gr_info.restart_support
+ || ospf6->gr_info.prepare_in_progress)
+ continue;
+
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]",
+ ospf6->gr_info.grace_period,
+ ospf6_vrf_id_to_name(ospf6->vrf_id));
+
+ /* Freeze OSPF routes in the RIB. */
+ if (ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period)) {
+ zlog_warn(
+ "%s: failed to activate graceful restart: not connected to zebra",
+ __func__);
+ continue;
+ }
+
+ /* Send a Grace-LSA to all neighbors. */
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
+ for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
+ if (oi->state < OSPF6_INTERFACE_POINTTOPOINT)
+ continue;
+ ospf6_gr_lsa_originate(oi);
+ }
+ }
+
+ /* Record end of the grace period in non-volatile memory. */
+ ospf6_gr_nvm_update(ospf6);
+
+ /*
+ * Mark that a Graceful Restart preparation is in progress, to
+ * prevent ospf6d from flushing its self-originated LSAs on
+ * exit.
+ */
+ ospf6->gr_info.prepare_in_progress = true;
+ }
+}
+
+static int ospf6_gr_neighbor_change(struct ospf6_neighbor *on, int next_state,
+ int prev_state)
+{
+ struct ospf6 *ospf6 = on->ospf6_if->area->ospf6;
+
+ if (next_state == OSPF6_NEIGHBOR_FULL
+ && ospf6->gr_info.restart_in_progress) {
+ if (ospf6_gr_check_adjs(ospf6)) {
+ ospf6_gr_restart_exit(
+ ospf6, "all adjacencies were reestablished");
+ } else {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: not all adjacencies were reestablished yet");
+ }
+ }
+
+ return 0;
+}
+
+int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6)
+{
+ if (!ospf6->gr_info.restart_support)
+ return 0;
+
+ if (ospf6->gr_info.grace_period == OSPF6_DFLT_GRACE_INTERVAL)
+ vty_out(vty, " graceful-restart\n");
+ else
+ vty_out(vty, " graceful-restart grace-period %u\n",
+ ospf6->gr_info.grace_period);
+
+ return 0;
+}
+
+DEFPY(ospf6_graceful_restart_prepare, ospf6_graceful_restart_prepare_cmd,
+ "graceful-restart prepare ipv6 ospf",
+ "Graceful Restart commands\n"
+ "Prepare upcoming graceful restart\n" IPV6_STR
+ "Prepare to restart the OSPFv3 process")
+{
+ ospf6_gr_prepare();
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd,
+ "graceful-restart [grace-period (1-1800)$grace_period]",
+ OSPF_GR_STR
+ "Maximum length of the 'grace period'\n"
+ "Maximum length of the 'grace period' in seconds\n")
+{
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6);
+
+ /* Check and get restart period if present. */
+ if (!grace_period_str)
+ grace_period = OSPF6_DFLT_GRACE_INTERVAL;
+
+ ospf6->gr_info.restart_support = true;
+ ospf6->gr_info.grace_period = grace_period;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd,
+ "no graceful-restart [period (1-1800)]",
+ NO_STR OSPF_GR_STR
+ "Maximum length of the 'grace period'\n"
+ "Maximum length of the 'grace period' in seconds\n")
+{
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6);
+
+ if (!ospf6->gr_info.restart_support)
+ return CMD_SUCCESS;
+
+ if (ospf6->gr_info.prepare_in_progress) {
+ vty_out(vty,
+ "%% Error: Graceful Restart preparation in progress\n");
+ return CMD_WARNING;
+ }
+
+ ospf6->gr_info.restart_support = false;
+ ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL;
+
+ return CMD_SUCCESS;
+}
+
+void ospf6_gr_init(void)
+{
+ hook_register(ospf6_neighbor_change, ospf6_gr_neighbor_change);
+
+ install_element(ENABLE_NODE, &ospf6_graceful_restart_prepare_cmd);
+ install_element(OSPF6_NODE, &ospf6_graceful_restart_cmd);
+ install_element(OSPF6_NODE, &ospf6_no_graceful_restart_cmd);
+}
#define OSPF6_MAX_GRACE_INTERVAL 1800
#define OSPF6_MIN_GRACE_INTERVAL 1
+#define OSPF6_DFLT_GRACE_INTERVAL 120
+
+/* Forward declaration(s). */
+struct ospf6_neighbor;
/* Debug option */
extern unsigned char conf_debug_ospf6_gr;
OSPF6_HELPER_NOT_A_VALID_NEIGHBOUR,
OSPF6_HELPER_PLANNED_ONLY_RESTART,
OSPF6_HELPER_TOPO_CHANGE_RTXMT_LIST,
- OSPF6_HELPER_LSA_AGE_MORE
+ OSPF6_HELPER_LSA_AGE_MORE,
+ OSPF6_HELPER_RESTARTING,
};
#ifdef roundup
#define OSPF6_GRACE_LSA_MIN_SIZE \
GRACE_PERIOD_TLV_SIZE + GRACE_RESTART_REASON_TLV_SIZE
+struct ospf6_grace_lsa {
+ struct grace_tlv_graceperiod tlv_period;
+ struct grace_tlv_restart_reason tlv_reason;
+};
+
struct advRtr {
in_addr_t advRtrAddr;
};
struct ospf6_neighbor *nbr);
extern void ospf6_helper_handle_topo_chg(struct ospf6 *ospf6,
struct ospf6_lsa *lsa);
+extern int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6);
extern int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6);
extern int config_write_ospf6_debug_gr_helper(struct vty *vty);
+
+extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf,
+ struct ospf6_area *area);
+extern void ospf6_gr_nvm_read(struct ospf6 *ospf);
+extern void ospf6_gr_init(void);
+
#endif /* OSPF6_GR_H */
return OSPF6_GR_NOT_HELPER;
}
+ if (ospf6->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "%s: router is in the process of graceful restart",
+ __func__);
+ restarter->gr_helper_info.rejected_reason =
+ OSPF6_HELPER_RESTARTING;
+ return OSPF6_GR_NOT_HELPER;
+ }
+
/* check supported grace period configured
* if configured, use this to start the grace
* timer otherwise use the interval received
/* Graceful Restart HELPER config Commands */
DEFPY(ospf6_gr_helper_enable,
ospf6_gr_helper_enable_cmd,
- "graceful-restart helper-only [A.B.C.D$rtr_id]",
+ "graceful-restart helper enable [A.B.C.D$rtr_id]",
"ospf6 graceful restart\n"
+ "ospf6 GR Helper\n"
"Enable Helper support\n"
- "Advertisement RouterId\n")
+ "Advertisement Router-ID\n")
{
VTY_DECLVAR_CONTEXT(ospf6, ospf6);
DEFPY(ospf6_gr_helper_disable,
ospf6_gr_helper_disable_cmd,
- "no graceful-restart helper-only [A.B.C.D$rtr_id]",
+ "no graceful-restart helper enable [A.B.C.D$rtr_id]",
NO_STR
"ospf6 graceful restart\n"
- "Disable Helper support\n"
- "Advertisement RouterId\n")
+ "ospf6 GR Helper\n"
+ "Enable Helper support\n"
+ "Advertisement Router-ID\n")
{
VTY_DECLVAR_CONTEXT(ospf6, ospf6);
bool detail = false;
ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME);
- OSPF6_CMD_CHECK_RUNNING();
+ if (ospf6 == NULL) {
+ vty_out(vty, "OSPFv3 is not configured\n");
+ return CMD_SUCCESS;
+ }
if (argv_find(argv, argc, "detail", &idx))
detail = true;
struct advRtr *rtr = backet->data;
struct vty *vty = (struct vty *)arg;
- vty_out(vty, " graceful-restart helper-only %pI4\n", &rtr->advRtrAddr);
+ vty_out(vty, " graceful-restart helper enable %pI4\n", &rtr->advRtrAddr);
return HASHWALK_CONTINUE;
}
int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6)
{
if (ospf6->ospf6_helper_cfg.is_helper_supported)
- vty_out(vty, " graceful-restart helper-only\n");
+ vty_out(vty, " graceful-restart helper enable\n");
if (!ospf6->ospf6_helper_cfg.strict_lsa_check)
vty_out(vty, " graceful-restart helper lsa-check-disable\n");
/* Schedule Hello */
if (!CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)
&& !if_is_loopback_or_vrf(oi->interface)) {
- oi->thread_send_hello = NULL;
thread_add_event(master, ospf6_hello_send, oi, 0,
&oi->thread_send_hello);
}
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_ifname += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) {
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_ifname += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_prefix += 2;
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_delete(struct ospf6_interface *);
+extern struct ospf6_interface *ospf6_interface_create(struct interface *ifp);
+extern void ospf6_interface_delete(struct ospf6_interface *oi);
-extern void ospf6_interface_enable(struct ospf6_interface *);
-extern void ospf6_interface_disable(struct ospf6_interface *);
+extern void ospf6_interface_enable(struct ospf6_interface *oi);
+extern void ospf6_interface_disable(struct ospf6_interface *oi);
-extern void ospf6_interface_state_update(struct interface *);
-extern void ospf6_interface_connected_route_update(struct interface *);
+extern void ospf6_interface_state_update(struct interface *ifp);
+extern void ospf6_interface_connected_route_update(struct interface *ifp);
extern struct in6_addr *
ospf6_interface_get_global_address(struct interface *ifp);
/* interface event */
-extern int interface_up(struct thread *);
-extern int interface_down(struct thread *);
-extern int wait_timer(struct thread *);
-extern int backup_seen(struct thread *);
-extern int neighbor_change(struct thread *);
+extern int interface_up(struct thread *thread);
+extern int interface_down(struct thread *thread);
+extern int wait_timer(struct thread *thread);
+extern int backup_seen(struct thread *thread);
+extern int neighbor_change(struct thread *thread);
extern void ospf6_interface_init(void);
extern void ospf6_interface_clear(struct interface *ifp);
#include "ospf6_flood.h"
#include "ospf6d.h"
#include "ospf6_spf.h"
+#include "ospf6_gr.h"
unsigned char conf_debug_ospf6_brouter = 0;
uint32_t conf_debug_ospf6_brouter_specific_router_id;
oa = (struct ospf6_area *)THREAD_ARG(thread);
oa->thread_router_lsa = NULL;
+ if (oa->ospf6->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Graceful Restart in progress, don't originate LSA");
+ return 0;
+ }
+
if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER))
zlog_debug("Originate Router-LSA for Area %s", oa->name);
by ospf6_lsa_refresh (), and does not come here. */
assert(oi->area);
+ if (oi->area->ospf6->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Graceful Restart in progress, don't originate LSA");
+ return 0;
+ }
+
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK),
htonl(oi->interface->ifindex),
oi->area->ospf6->router_id, oi->area->lsdb);
assert(oi->area);
+ if (oi->area->ospf6->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Graceful Restart in progress, don't originate LSA");
+ return 0;
+ }
+
+
/* find previous LSA */
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_LINK),
htonl(oi->interface->ifindex),
oa = (struct ospf6_area *)THREAD_ARG(thread);
oa->thread_intra_prefix_lsa = NULL;
+ if (oa->ospf6->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Graceful Restart in progress, don't originate LSA");
+ return 0;
+ }
+
/* find previous LSA */
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX), htonl(0),
oa->ospf6->router_id, oa->lsdb);
assert(oi->area);
+ if (oi->area->ospf6->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Graceful Restart in progress, don't originate LSA");
+ return 0;
+ }
+
/* find previous LSA */
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX),
htonl(oi->interface->ifindex),
struct listnode *anode, *anext;
struct listnode *nnode, *rnode, *rnext;
struct ospf6_nexthop *nh, *rnh;
- char buf[PREFIX2STR_BUFFER];
bool route_found = false;
struct interface *ifp = NULL;
struct ospf6_lsa *lsa;
for (old_route = old; old_route; old_route = old_route->next) {
bool route_updated = false;
- if (!ospf6_route_is_same(old_route, route) ||
- (old_route->path.type != route->path.type))
+ /* The route linked-list is grouped in batches of prefix.
+ * If the new prefix is not the same as the one of interest
+ * then we have walked over the end of the batch and so we
+ * should break rather than continuing unnecessarily.
+ */
+ if (!ospf6_route_is_same(old_route, route))
+ break;
+ if (old_route->path.type != route->path.type)
continue;
/* Current and New route has same origin,
/* Check old route path and route has same
* origin.
*/
- if (o_path->area_id != route->path.area_id ||
- (memcmp(&(o_path)->origin, &(route)->path.origin,
- sizeof(struct ospf6_ls_origin)) != 0))
+ if (o_path->area_id != route->path.area_id
+ || !ospf6_ls_origin_same(o_path, &route->path))
continue;
/* Cost is not same then delete current path */
for (old_route = old; old_route; old_route = old_route->next) {
- if (!ospf6_route_is_same(old_route, route) ||
- (old_route->path.type != route->path.type))
+ /* The route linked-list is grouped in batches of prefix.
+ * If the new prefix is not the same as the one of interest
+ * then we have walked over the end of the batch and so we
+ * should break rather than continuing unnecessarily.
+ */
+ if (!ospf6_route_is_same(old_route, route))
+ break;
+ if (old_route->path.type != route->path.type)
continue;
/* Old Route and New Route have Equal Cost, Merge NHs */
*/
for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
o_path)) {
- if (o_path->area_id == route->path.area_id &&
- (memcmp(&(o_path)->origin,
- &(route)->path.origin,
- sizeof(struct ospf6_ls_origin)) == 0))
+ if (o_path->area_id == route->path.area_id
+ && ospf6_ls_origin_same(o_path, &route->path))
break;
}
/* If path is not found in old_route paths's list,
if (ls_entry == NULL) {
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX))
zlog_debug(
- "%s: ls_prfix %s ls_entry not found.",
- __func__, buf);
+ "%s: ls_prfix %pFX ls_entry not found.",
+ __func__,
+ &o_path->ls_prefix);
continue;
}
lsa = ospf6_lsdb_lookup(o_path->origin.type,
* the table. For an example, ospf6_abr_examin_summary,
* removes brouters which are marked for remove.
*/
- oa->intra_brouter_calc = 1;
+ oa->intra_brouter_calc = true;
ospf6_route_remove(brouter, oa->ospf6->brouter_table);
brouter = NULL;
} else if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD)
UNSET_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE);
}
/* Reset for nbrouter */
- oa->intra_brouter_calc = 0;
+ oa->intra_brouter_calc = false;
}
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) ||
DEFUN (no_debug_ospf6_brouter_router,
no_debug_ospf6_brouter_router_cmd,
- "no debug ospf6 border-routers router-id",
+ "no debug ospf6 border-routers router-id [A.B.C.D]",
NO_STR
DEBUG_STR
OSPF6_STR
"Debug border router\n"
"Debug specific border router\n"
+ "Specify border-router's router-id\n"
)
{
OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_OFF();
DEFUN (no_debug_ospf6_brouter_area,
no_debug_ospf6_brouter_area_cmd,
- "no debug ospf6 border-routers area-id",
+ "no debug ospf6 border-routers area-id [A.B.C.D]",
NO_STR
DEBUG_STR
OSPF6_STR
"Debug border router\n"
"Debug border routers in specific Area\n"
+ "Specify Area-ID\n"
)
{
OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_OFF();
oi, 0, &(oi)->thread_as_extern_lsa); \
} while (0)
+#define OSPF6_ROUTER_LSA_EXECUTE(oa) \
+ do { \
+ if (CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \
+ thread_execute(master, ospf6_router_lsa_originate, oa, \
+ 0); \
+ } while (0)
+
#define OSPF6_NETWORK_LSA_EXECUTE(oi) \
do { \
THREAD_OFF((oi)->thread_network_lsa); \
thread_execute(master, ospf6_network_lsa_originate, oi, 0); \
} while (0)
+#define OSPF6_LINK_LSA_EXECUTE(oi) \
+ do { \
+ if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \
+ thread_execute(master, ospf6_link_lsa_originate, oi, \
+ 0); \
+ } while (0)
+
#define OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi) \
do { \
THREAD_OFF((oi)->thread_intra_prefix_lsa); \
struct ospf6_lsa *lsa);
extern int ospf6_router_is_stub_router(struct ospf6_lsa *lsa);
-extern int ospf6_router_lsa_originate(struct thread *);
-extern int ospf6_network_lsa_originate(struct thread *);
-extern int ospf6_link_lsa_originate(struct thread *);
-extern int ospf6_intra_prefix_lsa_originate_transit(struct thread *);
-extern int ospf6_intra_prefix_lsa_originate_stub(struct thread *);
+extern int ospf6_router_lsa_originate(struct thread *thread);
+extern int ospf6_network_lsa_originate(struct thread *thread);
+extern int ospf6_link_lsa_originate(struct thread *thread);
+extern int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread);
+extern int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread);
extern void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa);
extern void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa);
extern int ospf6_orig_as_external_lsa(struct thread *thread);
new = ospf6_lsa_create(self->header);
new->lsdb = old->lsdb;
- new->refresh = NULL;
thread_add_timer(master, ospf6_lsa_refresh, new, OSPF_LS_REFRESH_TIME,
&new->refresh);
return buf;
}
+DEFPY (debug_ospf6_lsa_all,
+ debug_ospf6_lsa_all_cmd,
+ "[no$no] debug ospf6 lsa all",
+ NO_STR
+ DEBUG_STR
+ OSPF6_STR
+ "Debug Link State Advertisements (LSAs)\n"
+ "Display for all types of LSAs\n")
+{
+ unsigned int i;
+ struct ospf6_lsa_handler *handler = NULL;
+
+ for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) {
+ handler = vector_slot(ospf6_lsa_handler_vector, i);
+ if (handler == NULL)
+ continue;
+ if (!no)
+ SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL);
+ else
+ UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL);
+ }
+ return CMD_SUCCESS;
+}
+
DEFPY (debug_ospf6_lsa_aggregation,
debug_ospf6_lsa_aggregation_cmd,
"[no] debug ospf6 lsa aggregation",
void install_element_ospf6_debug_lsa(void)
{
+ install_element(ENABLE_NODE, &debug_ospf6_lsa_all_cmd);
+ install_element(CONFIG_NODE, &debug_ospf6_lsa_all_cmd);
install_element(ENABLE_NODE, &debug_ospf6_lsa_hex_cmd);
install_element(ENABLE_NODE, &no_debug_ospf6_lsa_hex_cmd);
install_element(CONFIG_NODE, &debug_ospf6_lsa_hex_cmd);
{
unsigned int i;
const struct ospf6_lsa_handler *handler;
+ bool debug_all = true;
+
+ for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) {
+ handler = vector_slot(ospf6_lsa_handler_vector, i);
+ if (handler == NULL)
+ continue;
+ if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL)
+ < OSPF6_LSA_DEBUG_ALL) {
+ debug_all = false;
+ break;
+ }
+ }
+
+ if (debug_all) {
+ vty_out(vty, "debug ospf6 lsa all\n");
+ return 0;
+ }
for (i = 0; i < vector_active(ospf6_lsa_handler_vector); i++) {
handler = vector_slot(ospf6_lsa_handler_vector, i);
#define OSPF6_LSA_DEBUG_ORIGINATE 0x02
#define OSPF6_LSA_DEBUG_EXAMIN 0x04
#define OSPF6_LSA_DEBUG_FLOOD 0x08
+#define OSPF6_LSA_DEBUG_ALL \
+ (OSPF6_LSA_DEBUG | OSPF6_LSA_DEBUG_ORIGINATE | OSPF6_LSA_DEBUG_EXAMIN \
+ | OSPF6_LSA_DEBUG_FLOOD)
#define OSPF6_LSA_DEBUG_AGGR 0x10
/* OSPF LSA Default metric values */
extern int metric_value(struct ospf6 *ospf6, int type, uint8_t instance);
extern int ospf6_lsa_is_differ(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2);
extern int ospf6_lsa_is_changed(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2);
-extern uint16_t ospf6_lsa_age_current(struct ospf6_lsa *);
-extern void ospf6_lsa_age_update_to_send(struct ospf6_lsa *, uint32_t);
-extern void ospf6_lsa_premature_aging(struct ospf6_lsa *);
-extern int ospf6_lsa_compare(struct ospf6_lsa *, struct ospf6_lsa *);
+extern uint16_t ospf6_lsa_age_current(struct ospf6_lsa *lsa);
+extern void ospf6_lsa_age_update_to_send(struct ospf6_lsa *lsa,
+ uint32_t transdelay);
+extern void ospf6_lsa_premature_aging(struct ospf6_lsa *lsa);
+extern int ospf6_lsa_compare(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2);
extern char *ospf6_lsa_printbuf(struct ospf6_lsa *lsa, char *buf, int size);
extern void ospf6_lsa_header_print_raw(struct ospf6_lsa_header *header);
extern struct ospf6_lsa *
ospf6_lsa_create_headeronly(struct ospf6_lsa_header *header);
extern void ospf6_lsa_delete(struct ospf6_lsa *lsa);
-extern struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *);
+extern struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *lsa);
extern struct ospf6_lsa *ospf6_lsa_lock(struct ospf6_lsa *lsa);
extern struct ospf6_lsa *ospf6_lsa_unlock(struct ospf6_lsa *lsa);
-extern int ospf6_lsa_expire(struct thread *);
-extern int ospf6_lsa_refresh(struct thread *);
+extern int ospf6_lsa_expire(struct thread *thread);
+extern int ospf6_lsa_refresh(struct thread *thread);
-extern unsigned short ospf6_lsa_checksum(struct ospf6_lsa_header *);
-extern int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *);
+extern unsigned short ospf6_lsa_checksum(struct ospf6_lsa_header *lsah);
+extern int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *lsah);
extern int ospf6_lsa_prohibited_duration(uint16_t type, uint32_t id,
uint32_t adv_router, void *scope);
return;
}
+ /*
+ * RFC 3623 - Section 2:
+ * "If the restarting router determines that it was the Designated
+ * Router on a given segment prior to the restart, it elects
+ * itself as the Designated Router again. The restarting router
+ * knows that it was the Designated Router if, while the
+ * associated interface is in Waiting state, a Hello packet is
+ * received from a neighbor listing the router as the Designated
+ * Router".
+ */
+ if (oi->area->ospf6->gr_info.restart_in_progress
+ && oi->state == OSPF6_INTERFACE_WAITING
+ && hello->drouter == oi->area->ospf6->router_id)
+ oi->drouter = hello->drouter;
+
/* Schedule interface events */
if (backupseen)
thread_add_event(master, backup_seen, oi, 0, NULL);
zlog_debug("Ignoring LSA of reserved scope");
ospf6_lsa_delete(his);
continue;
- break;
}
if (ntohs(his->header->type) == OSPF6_LSTYPE_AS_EXTERNAL
&& !CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT))
thread_add_event(master, exchange_done, on, 0, NULL);
else {
- on->thread_send_dbdesc = NULL;
thread_add_event(master, ospf6_dbdesc_send_newone, on, 0,
&on->thread_send_dbdesc);
}
zlog_debug(
"Duplicated dbdesc causes retransmit");
THREAD_OFF(on->thread_send_dbdesc);
- on->thread_send_dbdesc = NULL;
thread_add_event(master, ospf6_dbdesc_send, on, 0,
&on->thread_send_dbdesc);
return;
zlog_debug("Ignoring LSA of reserved scope");
ospf6_lsa_delete(his);
continue;
- break;
}
if (OSPF6_LSA_SCOPE(his->header->type) == OSPF6_SCOPE_AS
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
zlog_debug("Ignoring LSA of reserved scope");
continue;
- break;
}
/* Find database copy */
zlog_debug("Ignoring LSA of reserved scope");
ospf6_lsa_delete(his);
continue;
- break;
}
if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV))
oh->version = (uint8_t)OSPFV3_VERSION;
oh->type = type;
-
+ oh->length = 0;
oh->router_id = oi->area->ospf6->router_id;
oh->area_id = oi->area->area_id;
+ oh->checksum = 0;
oh->instance_id = oi->instance_id;
oh->reserved = 0;
+
stream_forward_endp(s, OSPF6_HEADER_SIZE);
}
/* set next thread */
if (on->request_list->count != 0) {
- on->thread_send_lsreq = NULL;
thread_add_timer(master, ospf6_lsreq_send, on,
on->ospf6_if->rxmt_interval,
&on->thread_send_lsreq);
ospf6_packet_free(op);
if (on->lsupdate_list->count != 0) {
- on->thread_send_lsupdate = NULL;
thread_add_event(master, ospf6_lsupdate_send_neighbor, on, 0,
&on->thread_send_lsupdate);
} else if (on->retrans_list->count != 0) {
- on->thread_send_lsupdate = NULL;
thread_add_timer(master, ospf6_lsupdate_send_neighbor, on,
on->ospf6_if->rxmt_interval,
&on->thread_send_lsupdate);
ospf6_packet_free(op);
if (oi->lsupdate_list->count > 0) {
- oi->thread_send_lsupdate = NULL;
thread_add_event(master, ospf6_lsupdate_send_interface, oi, 0,
&oi->thread_send_lsupdate);
}
return (struct ospf6_neighbor *)NULL;
}
+struct ospf6_neighbor *ospf6_area_neighbor_lookup(struct ospf6_area *area,
+ uint32_t router_id)
+{
+ struct ospf6_interface *oi;
+ struct ospf6_neighbor *nbr;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(area->if_list, node, oi)) {
+ nbr = ospf6_neighbor_lookup(router_id, oi);
+ if (nbr)
+ return nbr;
+ }
+
+ return NULL;
+}
+
/* create ospf6_neighbor */
struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id,
struct ospf6_interface *oi)
/* reset Inactivity Timer */
THREAD_OFF(on->inactivity_timer);
- on->inactivity_timer = NULL;
thread_add_timer(master, inactivity_timer, on,
on->ospf6_if->dead_interval, &on->inactivity_timer);
SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT);
THREAD_OFF(on->thread_send_dbdesc);
- on->thread_send_dbdesc = NULL;
thread_add_event(master, ospf6_dbdesc_send, on, 0,
&on->thread_send_dbdesc);
else if (on->last_ls_req == NULL) {
if (on->thread_send_lsreq != NULL)
THREAD_OFF(on->thread_send_lsreq);
- on->thread_send_lsreq = NULL;
thread_add_event(master, ospf6_lsreq_send, on, 0,
&on->thread_send_lsreq);
}
if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT))
zlog_debug("Neighbor Event %s: *InactivityTimer*", on->name);
- on->inactivity_timer = NULL;
on->drouter = on->prev_drouter = 0;
on->bdrouter = on->prev_bdrouter = 0;
bool detail = false;
bool drchoice = false;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (argv_find(argv, argc, "detail", &idx_type))
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_ipv4 += 2;
#include "hook.h"
+/* Forward declaration(s). */
+struct ospf6_area;
+
/* Debug option */
extern unsigned char conf_debug_ospf6_neighbor;
#define OSPF6_DEBUG_NEIGHBOR_STATE 0x01
int ospf6_neighbor_cmp(void *va, void *vb);
void ospf6_neighbor_dbex_init(struct ospf6_neighbor *on);
-struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t,
- struct ospf6_interface *);
-struct ospf6_neighbor *ospf6_neighbor_create(uint32_t,
- struct ospf6_interface *);
-void ospf6_neighbor_delete(struct ospf6_neighbor *);
+struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id,
+ struct ospf6_interface *oi);
+struct ospf6_neighbor *ospf6_area_neighbor_lookup(struct ospf6_area *area,
+ uint32_t router_id);
+struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id,
+ struct ospf6_interface *oi);
+void ospf6_neighbor_delete(struct ospf6_neighbor *on);
/* Neighbor event */
-extern int hello_received(struct thread *);
-extern int twoway_received(struct thread *);
-extern int negotiation_done(struct thread *);
-extern int exchange_done(struct thread *);
-extern int loading_done(struct thread *);
-extern int adj_ok(struct thread *);
-extern int seqnumber_mismatch(struct thread *);
-extern int bad_lsreq(struct thread *);
-extern int oneway_received(struct thread *);
-extern int inactivity_timer(struct thread *);
-extern void ospf6_check_nbr_loading(struct ospf6_neighbor *);
+extern int hello_received(struct thread *thread);
+extern int twoway_received(struct thread *thread);
+extern int negotiation_done(struct thread *thread);
+extern int exchange_done(struct thread *thread);
+extern int loading_done(struct thread *thread);
+extern int adj_ok(struct thread *thread);
+extern int seqnumber_mismatch(struct thread *thread);
+extern int bad_lsreq(struct thread *thread);
+extern int oneway_received(struct thread *thread);
+extern int inactivity_timer(struct thread *thread);
+extern void ospf6_check_nbr_loading(struct ospf6_neighbor *on);
extern void ospf6_neighbor_init(void);
extern int config_write_ospf6_debug_neighbor(struct vty *vty);
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);
- }
+ if (CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED))
+ ospf6_lsa_premature_aging(lsa);
}
/* 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 (CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED))
+ ospf6_lsa_premature_aging(lsa);
}
}
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 : announce NSSA Type-7 defaults",
+ __func__);
+ ospf6_abr_nssa_type_7_defaults(ospf6);
}
if (IS_OSPF6_DEBUG_ABR)
zlog_debug("ospf_abr_remove_unapproved_translates(): Stop");
}
+static void ospf6_abr_nssa_type_7_default_create(struct ospf6 *ospf6,
+ struct ospf6_area *oa)
+{
+ struct ospf6_route *def;
+ int metric;
+ int metric_type;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("Announcing Type-7 default route into NSSA area %s",
+ oa->name);
+
+ def = ospf6_route_create(ospf6);
+ def->type = OSPF6_DEST_TYPE_NETWORK;
+ def->prefix.family = AF_INET6;
+ def->prefix.prefixlen = 0;
+ memset(&def->prefix.u.prefix6, 0, sizeof(struct in6_addr));
+ def->type = OSPF6_DEST_TYPE_NETWORK;
+ def->path.subtype = OSPF6_PATH_SUBTYPE_DEFAULT_RT;
+ if (CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR))
+ def->path.area_id = ospf6->backbone->area_id;
+ else
+ def->path.area_id = oa->area_id;
+
+ /* Compute default route type and metric. */
+ if (oa->nssa_default_originate.metric_value != -1)
+ metric = oa->nssa_default_originate.metric_value;
+ else
+ metric = DEFAULT_DEFAULT_ALWAYS_METRIC;
+ if (oa->nssa_default_originate.metric_type != -1)
+ metric_type = oa->nssa_default_originate.metric_type;
+ else
+ metric_type = DEFAULT_METRIC_TYPE;
+ def->path.metric_type = metric_type;
+ def->path.cost = metric;
+ if (metric_type == 1)
+ def->path.type = OSPF6_PATH_TYPE_EXTERNAL1;
+ else
+ def->path.type = OSPF6_PATH_TYPE_EXTERNAL2;
+
+ ospf6_nssa_lsa_originate(def, oa, false);
+ ospf6_route_delete(def);
+}
+
+static void ospf6_abr_nssa_type_7_default_delete(struct ospf6 *ospf6,
+ struct ospf6_area *oa)
+{
+ struct ospf6_lsa *lsa;
+
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7), 0,
+ oa->ospf6->router_id, oa->lsdb);
+ if (lsa && !OSPF6_LSA_IS_MAXAGE(lsa)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "Withdrawing Type-7 default route from area %s",
+ oa->name);
+
+ ospf6_lsa_purge(lsa);
+ }
+}
+
+/* NSSA Type-7 default route. */
+void ospf6_abr_nssa_type_7_defaults(struct ospf6 *ospf6)
+{
+ struct listnode *node;
+ struct ospf6_area *oa;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
+ if (IS_AREA_NSSA(oa) && oa->nssa_default_originate.enabled
+ && (IS_OSPF6_ABR(ospf6)
+ || (IS_OSPF6_ASBR(ospf6)
+ && ospf6->nssa_default_import_check.status)))
+ ospf6_abr_nssa_type_7_default_create(ospf6, oa);
+ else
+ ospf6_abr_nssa_type_7_default_delete(ospf6, oa);
+ }
+}
+
static void ospf6_abr_nssa_task(struct ospf6 *ospf6)
{
/* called only if any_nssa */
for (route = ospf6_route_head(
area->ospf6->external_table);
route; route = ospf6_route_next(route)) {
- /* This means the Type-5 LSA was originated for this route */
- if (route->path.origin.id != 0)
- ospf6_nssa_lsa_originate(route, area);
+ struct ospf6_external_info *info = route->route_option;
+ /* This means the Type-5 LSA was originated for this route */
+ if (route->path.origin.id != 0 && info->type != DEFAULT_ROUTE)
+ ospf6_nssa_lsa_originate(route, area, true);
}
/* Loop through the aggregation table to originate type-7 LSAs
"Originating Type-7 LSAs for area %s",
area->name);
- ospf6_nssa_lsa_originate(aggr->route, area);
+ ospf6_nssa_lsa_originate(aggr->route, area, true);
}
}
}
void ospf6_nssa_lsa_originate(struct ospf6_route *route,
- struct ospf6_area *area)
+ struct ospf6_area *area, bool p_bit)
{
char buffer[OSPF6_MAX_LSASIZE];
struct ospf6_lsa_header *lsa_header;
/* Fill AS-External-LSA */
/* Metric type */
- if (route->path.metric_type == OSPF6_PATH_TYPE_EXTERNAL2)
+ if (route->path.metric_type == 2)
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)
+ if (info && 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);
as_external_lsa->prefix.prefix_options = route->prefix_options;
/* Set the P bit */
- as_external_lsa->prefix.prefix_options |= OSPF6_PREFIX_OPTION_P;
+ if (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);
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)) {
+ if (info
+ && 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));
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 struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *oa,
+ struct ospf6_lsa *type7,
+ struct ospf6_lsa *type5);
+extern struct ospf6_lsa *
+ospf6_translated_nssa_originate(struct ospf6_area *oa, struct ospf6_lsa *type7);
extern void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6);
extern void ospf6_area_nssa_update(struct ospf6_area *area);
void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6);
extern void ospf6_nssa_lsa_originate(struct ospf6_route *route,
- struct ospf6_area *area);
+ struct ospf6_area *area, bool p_bit);
extern void install_element_ospf6_debug_nssa(void);
+extern void ospf6_abr_nssa_type_7_defaults(struct ospf6 *osof6);
int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route,
int type);
extern int ospf6_abr_translate_nssa(struct ospf6_area *area,
#include "ospf6_interface.h"
#include "ospf6d.h"
#include "ospf6_zebra.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "ospf6d/ospf6_route_clippy.c"
+#endif
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE, "OSPF6 route");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE_TABLE, "OSPF6 route table");
else
return memcmp(&a->address, &b->address,
sizeof(struct in6_addr));
-
- return 0;
}
static int ospf6_path_cmp(struct ospf6_path *a, struct ospf6_path *b)
json_object *json_array_next_hops = NULL;
json_object *json_next_hop;
- if (om6->ospf6 == NULL) {
- vty_out(vty, "OSPFv3 is not running\n");
- return;
- }
-
monotime(&now);
timersub(&now, &route->changed, &res);
timerstring(&res, duration, sizeof(duration));
json_object *json_array_next_hops = NULL;
json_object *json_next_hop;
- if (om6->ospf6 == NULL) {
- vty_out(vty, "OSPFv3 is not running\n");
- return;
- }
-
monotime(&now);
/* destination */
OSPF6_PATH_TYPE_NAME(route->path.type), area);
}
-DEFUN (debug_ospf6_route,
- debug_ospf6_route_cmd,
- "debug ospf6 route <table|intra-area|inter-area|memory>",
- DEBUG_STR
- OSPF6_STR
- "Debug routes\n"
- "Debug route table calculation\n"
- "Debug intra-area route calculation\n"
- "Debug inter-area route calculation\n"
- "Debug route memory use\n"
- )
+DEFPY(debug_ospf6_route,
+ debug_ospf6_route_cmd,
+ "[no$no] debug ospf6 route <all|table|intra-area|inter-area|memory>",
+ NO_STR
+ DEBUG_STR
+ OSPF6_STR
+ "Debug routes\n"
+ "Debug for all types of route calculation\n"
+ "Debug route table calculation\n"
+ "Debug intra-area route calculation\n"
+ "Debug inter-area route calculation\n"
+ "Debug route memory use\n")
{
- int idx_type = 3;
+ int idx_type;
unsigned char level = 0;
- if (!strcmp(argv[idx_type]->text, "table"))
- level = OSPF6_DEBUG_ROUTE_TABLE;
- else if (!strcmp(argv[idx_type]->text, "intra-area"))
- level = OSPF6_DEBUG_ROUTE_INTRA;
- else if (!strcmp(argv[idx_type]->text, "inter-area"))
- level = OSPF6_DEBUG_ROUTE_INTER;
- else if (!strcmp(argv[idx_type]->text, "memory"))
- level = OSPF6_DEBUG_ROUTE_MEMORY;
- OSPF6_DEBUG_ROUTE_ON(level);
- return CMD_SUCCESS;
-}
-
-DEFUN (no_debug_ospf6_route,
- no_debug_ospf6_route_cmd,
- "no debug ospf6 route <table|intra-area|inter-area|memory>",
- NO_STR
- DEBUG_STR
- OSPF6_STR
- "Debug routes\n"
- "Debug route table calculation\n"
- "Debug intra-area route calculation\n"
- "Debug inter-area route calculation\n"
- "Debug route memory use\n")
-{
- int idx_type = 4;
- unsigned char level = 0;
+ idx_type = ((no) ? 4 : 3);
- if (!strcmp(argv[idx_type]->text, "table"))
+ if (!strcmp(argv[idx_type]->text, "all"))
+ level = OSPF6_DEBUG_ROUTE_ALL;
+ else if (!strcmp(argv[idx_type]->text, "table"))
level = OSPF6_DEBUG_ROUTE_TABLE;
else if (!strcmp(argv[idx_type]->text, "intra-area"))
level = OSPF6_DEBUG_ROUTE_INTRA;
level = OSPF6_DEBUG_ROUTE_INTER;
else if (!strcmp(argv[idx_type]->text, "memory"))
level = OSPF6_DEBUG_ROUTE_MEMORY;
- OSPF6_DEBUG_ROUTE_OFF(level);
+
+ if (no)
+ OSPF6_DEBUG_ROUTE_OFF(level);
+ else
+ OSPF6_DEBUG_ROUTE_ON(level);
return CMD_SUCCESS;
}
int config_write_ospf6_debug_route(struct vty *vty)
{
+ if (IS_OSPF6_DEBUG_ROUTE(ALL) == OSPF6_DEBUG_ROUTE_ALL) {
+ vty_out(vty, "debug ospf6 route all\n");
+ return 0;
+ }
if (IS_OSPF6_DEBUG_ROUTE(TABLE))
vty_out(vty, "debug ospf6 route table\n");
if (IS_OSPF6_DEBUG_ROUTE(INTRA))
void install_element_ospf6_debug_route(void)
{
install_element(ENABLE_NODE, &debug_ospf6_route_cmd);
- install_element(ENABLE_NODE, &no_debug_ospf6_route_cmd);
install_element(CONFIG_NODE, &debug_ospf6_route_cmd);
- install_element(CONFIG_NODE, &no_debug_ospf6_route_cmd);
}
#define OSPF6_DEBUG_ROUTE_TABLE 0x01
#define OSPF6_DEBUG_ROUTE_INTRA 0x02
#define OSPF6_DEBUG_ROUTE_INTER 0x04
-#define OSPF6_DEBUG_ROUTE_MEMORY 0x80
+#define OSPF6_DEBUG_ROUTE_MEMORY 0x08
+#define OSPF6_DEBUG_ROUTE_ALL \
+ (OSPF6_DEBUG_ROUTE_TABLE | OSPF6_DEBUG_ROUTE_INTRA \
+ | OSPF6_DEBUG_ROUTE_INTER | OSPF6_DEBUG_ROUTE_MEMORY)
#define OSPF6_DEBUG_ROUTE_ON(level) (conf_debug_ospf6_route |= (level))
#define OSPF6_DEBUG_ROUTE_OFF(level) (conf_debug_ospf6_route &= ~(level))
#define IS_OSPF6_DEBUG_ROUTE(e) (conf_debug_ospf6_route & OSPF6_DEBUG_ROUTE_##e)
ospf6_add_nexthop(route->nh_list, ifindex, addr)
extern struct ospf6_route *ospf6_route_create(struct ospf6 *ospf6);
-extern void ospf6_route_delete(struct ospf6_route *);
+extern void ospf6_route_delete(struct ospf6_route *route);
extern struct ospf6_route *ospf6_route_copy(struct ospf6_route *route);
extern int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb);
json_object *json, bool use_json);
-extern int ospf6_route_table_show(struct vty *, int, int, struct cmd_token **,
- struct ospf6_route_table *, bool use_json);
+extern int ospf6_route_table_show(struct vty *vty, int argc_start, int argc,
+ struct cmd_token **argv,
+ struct ospf6_route_table *table,
+ bool use_json);
extern int ospf6_linkstate_table_show(struct vty *vty, int idx_ipv4, int argc,
struct cmd_token **argv,
struct ospf6_route_table *table);
#include "ospf6d.h"
#include "ospf6_abr.h"
#include "ospf6_nssa.h"
+#include "ospf6_zebra.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex");
}
}
-static const char *const ospf6_spf_reason_str[] = {"R+", "R-", "N+", "N-", "L+",
- "L-", "R*", "N*", "C"};
-
-void ospf6_spf_reason_string(unsigned int reason, char *buf, int size)
+static const char *const ospf6_spf_reason_str[] = {
+ "R+", /* OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED */
+ "R-", /* OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED */
+ "N+", /* OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED */
+ "N-", /* OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED */
+ "L+", /* OSPF6_SPF_FLAGS_NETWORK_LINK_LSA_ADDED */
+ "L-", /* OSPF6_SPF_FLAGS_NETWORK_LINK_LSA_REMOVED */
+ "R*", /* OSPF6_SPF_FLAGS_ROUTER_LSA_ORIGINATED */
+ "N*", /* OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED */
+ "C", /* OSPF6_SPF_FLAGS_CONFIG_CHANGE */
+ "A", /* OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE */
+ "GR", /* OSPF6_SPF_FLAGS_GR_FINISH */
+};
+
+void ospf6_spf_reason_string(uint32_t reason, char *buf, int size)
{
- unsigned int bit;
+ uint32_t bit;
int len = 0;
if (!buf)
/* External LSA calculation */
ospf6_ase_calculate_timer_add(ospf6);
- if (ospf6_check_and_set_router_abr(ospf6))
+ if (ospf6_check_and_set_router_abr(ospf6)) {
ospf6_abr_defaults_to_stub(ospf6);
+ ospf6_abr_nssa_type_7_defaults(ospf6);
+ }
monotime(&end);
timersub(&end, &start, &runtime);
ospf6_ase_calculate_route(ospf6, lsa, area);
}
}
+
+ if (ospf6->gr_info.finishing_restart) {
+ /*
+ * The routing table computation is complete. Uninstall remnant
+ * routes that were installed before the restart, but that are
+ * no longer valid.
+ */
+ ospf6_zebra_gr_disable(ospf6);
+ ospf6->gr_info.finishing_restart = false;
+ }
+
return 0;
}
#define OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED (1 << 7)
#define OSPF6_SPF_FLAGS_CONFIG_CHANGE (1 << 8)
#define OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE (1 << 9)
+#define OSPF6_SPF_FLAGS_GR_FINISH (1 << 10)
static inline void ospf6_set_spf_reason(struct ospf6 *ospf, unsigned int reason)
{
thread_add_read(master, ospf6_receive, ospf6, ospf6->fd,
&ospf6->t_ospf6_receive);
- ospf6_router_id_update(ospf6);
+ ospf6_router_id_update(ospf6, true);
}
}
if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES)
SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES);
if (ospf6->router_id == 0)
- ospf6_router_id_update(ospf6);
+ ospf6_router_id_update(ospf6, true);
ospf6_add(ospf6);
if (ospf6->vrf_id != VRF_UNKNOWN) {
vrf = vrf_lookup_by_id(ospf6->vrf_id);
if (ospf6->fd < 0)
return ospf6;
+ /*
+ * Read from non-volatile memory whether this instance is performing a
+ * graceful restart or not.
+ */
+ ospf6_gr_nvm_read(ospf6);
+
thread_add_read(master, ospf6_receive, ospf6, ospf6->fd,
&ospf6->t_ospf6_receive);
QOBJ_UNREG(o);
ospf6_gr_helper_deinit(o);
- ospf6_flush_self_originated_lsas_now(o);
+ if (!o->gr_info.prepare_in_progress)
+ ospf6_flush_self_originated_lsas_now(o);
ospf6_disable(o);
ospf6_del(o);
THREAD_OFF(o->t_distribute_update);
THREAD_OFF(o->t_ospf6_receive);
THREAD_OFF(o->t_external_aggr);
+ THREAD_OFF(o->gr_info.t_grace_period);
}
}
&o->maxage_remover);
}
-void ospf6_router_id_update(struct ospf6 *ospf6)
+bool ospf6_router_id_update(struct ospf6 *ospf6, bool init)
{
+ in_addr_t new_router_id;
+ struct listnode *node;
+ struct ospf6_area *oa;
+
if (!ospf6)
- return;
+ return true;
if (ospf6->router_id_static != 0)
- ospf6->router_id = ospf6->router_id_static;
+ new_router_id = ospf6->router_id_static;
else
- ospf6->router_id = ospf6->router_id_zebra;
+ new_router_id = ospf6->router_id_zebra;
+
+ if (ospf6->router_id == new_router_id)
+ return true;
+
+ if (!init)
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
+ if (oa->full_nbrs) {
+ zlog_err(
+ "%s: cannot update router-id. Run the \"clear ipv6 ospf6 process\" command",
+ __func__);
+ return false;
+ }
+ }
+
+ ospf6->router_id = new_router_id;
+ return true;
}
/* start ospf6 */
ospf6->inst_shutdown = 0;
ospf6_db_clear(ospf6);
- ospf6_router_id_update(ospf6);
-
ospf6_asbr_redistribute_reset(ospf6);
FOR_ALL_INTERFACES (vrf, ifp)
ospf6_interface_clear(ifp);
vrf_name = name;
ospf6 = ospf6_lookup_by_vrf_name(vrf_name);
- if (ospf6 == NULL)
+ if (ospf6 == NULL) {
vty_out(vty, "OSPFv3 is not configured\n");
- else
+ } else {
+ ospf6_router_id_update(ospf6, true);
ospf6_process_reset(ospf6);
+ }
return CMD_SUCCESS;
}
int ret;
const char *router_id_str;
uint32_t router_id;
- struct ospf6_area *oa;
- struct listnode *node;
argv_find(argv, argc, "A.B.C.D", &idx);
router_id_str = argv[idx]->arg;
o->router_id_static = router_id;
- for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) {
- if (oa->full_nbrs) {
- vty_out(vty,
- "For this router-id change to take effect, run the \"clear ipv6 ospf6 process\" command\n");
- return CMD_SUCCESS;
- }
- }
-
- o->router_id = router_id;
+ if (ospf6_router_id_update(o, false))
+ ospf6_process_reset(o);
+ else
+ vty_out(vty,
+ "For this router-id change to take effect run the \"clear ipv6 ospf6 process\" command\n");
return CMD_SUCCESS;
}
V4NOTATION_STR)
{
VTY_DECLVAR_CONTEXT(ospf6, o);
- struct ospf6_area *oa;
- struct listnode *node;
o->router_id_static = 0;
- for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) {
- if (oa->full_nbrs) {
- vty_out(vty,
- "For this router-id change to take effect, run the \"clear ipv6 ospf6 process\" command\n");
- return CMD_SUCCESS;
- }
- }
- o->router_id = 0;
- if (o->router_id_zebra)
- o->router_id = o->router_id_zebra;
+
+ if (ospf6_router_id_update(o, false))
+ ospf6_process_reset(o);
+ else
+ vty_out(vty,
+ "For this router-id change to take effect run the \"clear ipv6 ospf6 process\" command\n");
return CMD_SUCCESS;
}
bool uj = use_json(argc, argv);
json_object *json = NULL;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) {
int idx_arg_start = 4;
bool uj = use_json(argc, argv);
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_arg_start += 2;
int idx_start_arg = 4;
bool uj = use_json(argc, argv);
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_start_arg += 2;
int idx_start_arg = 4;
bool uj = use_json(argc, argv);
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_start_arg += 2;
int idx_start_arg = 4;
bool uj = use_json(argc, argv);
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_start_arg += 2;
if (uj)
json = json_object_new_object();
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) {
ospf6_distance_config_write(vty, ospf6);
ospf6_distribute_config_write(vty, ospf6);
ospf6_asbr_summary_config_write(vty, ospf6);
+ config_write_ospf6_gr(vty, ospf6);
config_write_ospf6_gr_helper(vty, ospf6);
vty_out(vty, "exit\n");
#define ROUTEMAP(R) (R->route_map.map)
};
+struct ospf6_gr_info {
+ bool restart_support;
+ bool restart_in_progress;
+ bool prepare_in_progress;
+ bool finishing_restart;
+ uint32_t grace_period;
+ struct thread *t_grace_period;
+};
+
struct ospf6_gr_helper {
/* Gracefull restart Helper supported configs*/
/* Supported grace interval*/
/* OSPF6 redistribute configuration */
struct list *redist[ZEBRA_ROUTE_MAX + 1];
+ /* NSSA default-information-originate */
+ struct {
+ /* # of NSSA areas requesting default information */
+ uint16_t refcnt;
+
+ /*
+ * Whether a default route known through non-OSPF protocol is
+ * present in the RIB.
+ */
+ bool status;
+ } nssa_default_import_check;
+
uint8_t flag;
#define OSPF6_FLAG_ABR 0x04
#define OSPF6_FLAG_ASBR 0x08
*/
uint16_t max_multipath;
+ /* OSPF Graceful Restart info (restarting mode) */
+ struct ospf6_gr_info gr_info;
+
/*ospf6 Graceful restart helper info */
struct ospf6_gr_helper ospf6_helper_cfg;
extern void install_element_ospf6_clear_process(void);
extern void ospf6_top_init(void);
extern void ospf6_delete(struct ospf6 *o);
-extern void ospf6_router_id_update(struct ospf6 *ospf6);
+extern bool ospf6_router_id_update(struct ospf6 *ospf6, bool init);
extern void ospf6_maxage_remove(struct ospf6 *o);
extern struct ospf6 *ospf6_instance_create(const char *name);
#include "ospf6_lsa.h"
#include "ospf6_lsdb.h"
#include "ospf6_asbr.h"
+#include "ospf6_nssa.h"
#include "ospf6_zebra.h"
#include "ospf6d.h"
#include "ospf6_area.h"
+#include "ospf6_gr.h"
#include "lib/json.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DISTANCE, "OSPF6 distance");
o->router_id_zebra = router_id.u.prefix4.s_addr;
- ospf6_router_id_update(o);
+ ospf6_router_id_update(o, false);
return 0;
}
AFI_IP6, type, 0, vrf_id);
}
+void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg)
+{
+ struct prefix prefix = {};
+ int command;
+
+ if (zclient->sock < 0) {
+ if (IS_OSPF6_DEBUG_ZEBRA(SEND))
+ zlog_debug(" Not connected to Zebra");
+ return;
+ }
+
+ prefix.family = AF_INET6;
+ prefix.prefixlen = 0;
+
+ if (unreg)
+ command = ZEBRA_IMPORT_ROUTE_UNREGISTER;
+ else
+ command = ZEBRA_IMPORT_ROUTE_REGISTER;
+
+ if (IS_OSPF6_DEBUG_ZEBRA(SEND))
+ zlog_debug("%s: sending cmd %s for %pFX (vrf %u)", __func__,
+ zserv_command_string(command), &prefix,
+ ospf6->vrf_id);
+
+ if (zclient_send_rnh(zclient, command, &prefix, true, ospf6->vrf_id)
+ == ZCLIENT_SEND_FAILURE)
+ flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_send_rnh() failed",
+ __func__);
+}
+
+static int ospf6_zebra_import_check_update(ZAPI_CALLBACK_ARGS)
+{
+ struct ospf6 *ospf6;
+ struct zapi_route nhr;
+
+ ospf6 = ospf6_lookup_by_vrf_id(vrf_id);
+ if (ospf6 == NULL || !IS_OSPF6_ASBR(ospf6))
+ return 0;
+
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) {
+ zlog_err("%s[%u]: Failure to decode route", __func__,
+ ospf6->vrf_id);
+ return -1;
+ }
+
+ if (nhr.prefix.family != AF_INET6 || nhr.prefix.prefixlen != 0
+ || nhr.type == ZEBRA_ROUTE_OSPF6)
+ return 0;
+
+ ospf6->nssa_default_import_check.status = !!nhr.nexthop_num;
+ ospf6_abr_nssa_type_7_defaults(ospf6);
+
+ return 0;
+}
+
static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
return 0;
}
+static int ospf6_zebra_gr_update(struct ospf6 *ospf6, int command,
+ uint32_t stale_time)
+{
+ struct zapi_cap api;
+
+ if (!zclient || zclient->sock < 0 || !ospf6)
+ return 1;
+
+ memset(&api, 0, sizeof(struct zapi_cap));
+ api.cap = command;
+ api.stale_removal_time = stale_time;
+ api.vrf_id = ospf6->vrf_id;
+
+ (void)zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient,
+ &api);
+
+ return 0;
+}
+
+int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time)
+{
+ return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES,
+ stale_time);
+}
+
+int ospf6_zebra_gr_disable(struct ospf6 *ospf6)
+{
+ return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0);
+}
+
static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
void ospf6_zebra_route_update_add(struct ospf6_route *request,
struct ospf6 *ospf6)
{
+ if (ospf6->gr_info.restart_in_progress
+ || ospf6->gr_info.prepare_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not installing %pFX",
+ &request->prefix);
+ return;
+ }
+
ospf6_zebra_route_update(ADD, request, ospf6);
}
void ospf6_zebra_route_update_remove(struct ospf6_route *request,
struct ospf6 *ospf6)
{
+ if (ospf6->gr_info.restart_in_progress
+ || ospf6->gr_info.prepare_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not uninstalling %pFX",
+ &request->prefix);
+ return;
+ }
+
ospf6_zebra_route_update(REM, request, ospf6);
}
struct zapi_route api;
struct prefix *dest = &request->prefix;
+ if (ospf6->gr_info.restart_in_progress
+ || ospf6->gr_info.prepare_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not installing %pFX",
+ &request->prefix);
+ return;
+ }
+
if (!CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) {
memset(&api, 0, sizeof(api));
api.vrf_id = ospf6->vrf_id;
struct zapi_route api;
struct prefix *dest = &request->prefix;
+ if (ospf6->gr_info.restart_in_progress
+ || ospf6->gr_info.prepare_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not uninstalling %pFX",
+ &request->prefix);
+ return;
+ }
+
if (CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) {
memset(&api, 0, sizeof(api));
api.vrf_id = ospf6->vrf_id;
ospf6_zebra_if_address_update_delete;
zclient->redistribute_route_add = ospf6_zebra_read_route;
zclient->redistribute_route_del = ospf6_zebra_read_route;
+ zclient->import_check_update = ospf6_zebra_import_check_update;
/* Install command element for zebra node. */
install_element(VIEW_NODE, &show_ospf6_zebra_cmd);
extern void ospf6_zebra_no_redistribute(int, vrf_id_t vrf_id);
#define ospf6_zebra_is_redistribute(type, vrf_id) \
vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id)
-extern void ospf6_zebra_init(struct thread_master *);
+extern void ospf6_zebra_init(struct thread_master *tm);
+extern void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg);
extern void ospf6_zebra_add_discard(struct ospf6_route *request,
struct ospf6 *ospf6);
extern void ospf6_zebra_delete_discard(struct ospf6_route *request,
struct ospf6 *ospf6);
-extern void ospf6_distance_reset(struct ospf6 *);
-extern uint8_t ospf6_distance_apply(struct prefix_ipv6 *, struct ospf6_route *,
- struct ospf6 *);
+extern void ospf6_distance_reset(struct ospf6 *ospf6);
+extern uint8_t ospf6_distance_apply(struct prefix_ipv6 *p,
+ struct ospf6_route * or,
+ struct ospf6 *ospf6);
-extern int ospf6_distance_set(struct vty *, struct ospf6 *, const char *,
- const char *, const char *);
-extern int ospf6_distance_unset(struct vty *, struct ospf6 *, const char *,
- const char *, const char *);
+extern int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time);
+extern int ospf6_zebra_gr_disable(struct ospf6 *ospf6);
+extern int ospf6_distance_set(struct vty *vty, struct ospf6 *ospf6,
+ const char *distance_str, const char *ip_str,
+ const char *access_list_str);
+extern int ospf6_distance_unset(struct vty *vty, struct ospf6 *ospf6,
+ const char *distance_str, const char *ip_str,
+ const char *access_list_str);
extern int config_write_ospf6_debug_zebra(struct vty *vty);
extern void install_element_ospf6_debug_zebra(void);
int idx_vrf = 0;
bool uj = use_json(argc, argv);
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_level += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_lsa += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (argv[idx_ipv4]->type == IPV4_TKN)
inet_pton(AF_INET, argv[idx_ipv4]->arg, &id);
int idx_vrf = 0;
bool uj = use_json(argc, argv);
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_ipv4 += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_ipv4 += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_lsa += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_lsa += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_ls_id += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_adv_rtr += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_lsa += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_lsa += 2;
uint32_t adv_router = 0;
bool uj = use_json(argc, argv);
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_level += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_lsa += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_lsa += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_lsa += 2;
int idx_vrf = 0;
int idx_argc = 5;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0) {
idx_argc += 2;
bool all_vrf = false;
int idx_vrf = 0;
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_ipv4 += 2;
bool all_vrf = false;
int idx_vrf = 0;
-
- OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
if (idx_vrf > 0)
idx_detail += 2;
return CMD_SUCCESS;
}
-static void ospf6_plist_add(struct prefix_list *plist)
-{
- if (prefix_list_afi(plist) != AFI_IP6)
- return;
- ospf6_area_plist_update(plist, 1);
-}
-
-static void ospf6_plist_del(struct prefix_list *plist)
-{
- if (prefix_list_afi(plist) != AFI_IP6)
- return;
- ospf6_area_plist_update(plist, 0);
-}
-
/* Install ospf related commands. */
void ospf6_init(struct thread_master *master)
{
ospf6_intra_init();
ospf6_asbr_init();
ospf6_abr_init();
+ ospf6_gr_init();
ospf6_gr_helper_config_init();
/* initialize hooks for modifying filter rules */
- prefix_list_add_hook(ospf6_plist_add);
- prefix_list_delete_hook(ospf6_plist_del);
+ prefix_list_add_hook(ospf6_plist_update);
+ prefix_list_delete_hook(ospf6_plist_update);
access_list_add_hook(ospf6_filter_update);
access_list_delete_hook(ospf6_filter_update);
#define OSPF6_ROUTER_ID_STR "Specify Router-ID\n"
#define OSPF6_LS_ID_STR "Specify Link State ID\n"
-#define OSPF6_CMD_CHECK_RUNNING() \
- if (om6->ospf6 == NULL) { \
- vty_out(vty, "OSPFv3 is not running\n"); \
- return CMD_SUCCESS; \
- }
-
#define IS_OSPF6_ASBR(O) ((O)->flag & OSPF6_FLAG_ASBR)
#define OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) \
if (argv_find(argv, argc, "vrf", &idx_vrf)) { \
ospf6d/ospf6_area.c \
ospf6d/ospf6_bfd.c \
ospf6d/ospf6_flood.c \
+ ospf6d/ospf6_gr.c \
ospf6d/ospf6_gr_helper.c \
ospf6d/ospf6_interface.c \
ospf6d/ospf6_intra.c \
ospf6d/ospf6_routemap_nb_config.c \
ospf6d/ospf6_bfd.c \
ospf6d/ospf6_flood.c \
+ ospf6d/ospf6_gr.c \
ospf6d/ospf6_gr_helper.c \
ospf6d/ospf6_interface.c \
ospf6d/ospf6_intra.c \
ospf6d/ospf6_asbr.c \
ospf6d/ospf6_lsa.c \
ospf6d/ospf6_gr_helper.c \
+ ospf6d/ospf6_gr.c \
+ ospf6d/ospf6_route.c \
# end
nodist_ospf6d_ospf6d_SOURCES = \
/* check exit triggered due to successful completion
* of graceful restart.
- * If no, bring down the neighbour.
*/
if (reason != OSPF_GR_HELPER_COMPLETED) {
if (IS_DEBUG_OSPF_GR)
- zlog_debug(
- "%s, Failed GR exit, so bringing down the neighbour",
- __func__);
- OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_KillNbr);
+ zlog_debug("%s, Unsuccessful GR exit", __func__);
}
/*Recalculate the DR for the network segment */
if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
continue;
- if (if_is_loopback(oi->ifp) || if_is_vrf(oi->ifp))
+ if (if_is_loopback_or_vrf(oi->ifp))
continue;
if (CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED))
int ospf_if_is_enable(struct ospf_interface *oi)
{
- if (!(if_is_loopback(oi->ifp) || if_is_vrf(oi->ifp)))
+ if (!(if_is_loopback_or_vrf(oi->ifp)))
if (if_is_up(oi->ifp))
return 1;
{
if (if_is_pointopoint(ifp))
return OSPF_IFTYPE_POINTOPOINT;
- else if (if_is_loopback(ifp) || if_is_vrf(ifp))
+ else if (if_is_loopback_or_vrf(ifp))
return OSPF_IFTYPE_LOOPBACK;
else
return OSPF_IFTYPE_BROADCAST;
*/
if (!OSPF_GR_IS_ACTIVE_HELPER(nbr))
OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer);
- else if (IS_DEBUG_OSPF_GR)
+ else if (IS_DEBUG_OSPF_GR) {
zlog_debug(
- "%s, Acting as HELPER for this neighbour, So inactivitytimer event will not be fired.",
+ "%s, Acting as HELPER for this neighbour, So restart the dead timer",
__func__);
+ OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer,
+ nbr->v_inactivity);
+ }
return 0;
}
/* Graceful Restart HELPER Commands */
DEFPY(ospf_gr_helper_enable, ospf_gr_helper_enable_cmd,
- "graceful-restart helper-only [A.B.C.D]",
+ "graceful-restart helper enable [A.B.C.D$address]",
"OSPF Graceful Restart\n"
+ "OSPF GR Helper\n"
"Enable Helper support\n"
- "Advertising router id\n")
+ "Advertising Router-ID\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
- struct in_addr addr;
- int ret;
-
- if (argc == 3) {
- ret = inet_aton(argv[2]->arg, &addr);
- if (!ret) {
- vty_out(vty,
- "Please specify the valid routerid address.\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- ospf_gr_helper_support_set_per_routerid(ospf, &addr, OSPF_GR_TRUE);
+ if (address_str) {
+ ospf_gr_helper_support_set_per_routerid(ospf, &address,
+ OSPF_GR_TRUE);
return CMD_SUCCESS;
}
DEFPY(no_ospf_gr_helper_enable,
no_ospf_gr_helper_enable_cmd,
- "no graceful-restart helper-only [A.B.C.D]",
+ "no graceful-restart helper enable [A.B.C.D$address]",
NO_STR
"OSPF Graceful Restart\n"
- "Disable Helper support\n"
+ "OSPF GR Helper\n"
+ "Enable Helper support\n"
+ "Advertising Router-ID\n")
+{
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ if (address_str) {
+ ospf_gr_helper_support_set_per_routerid(ospf, &address,
+ OSPF_GR_FALSE);
+ return CMD_SUCCESS;
+ }
+
+ ospf_gr_helper_support_set(ospf, OSPF_GR_FALSE);
+ return CMD_SUCCESS;
+}
+
+#if CONFDATE > 20220921
+CPP_NOTICE(
+ "Time to remove the deprecated \"[no] graceful-restart helper-only\" commands")
+#endif
+
+DEFPY_HIDDEN(ospf_gr_helper_only, ospf_gr_helper_only_cmd,
+ "graceful-restart helper-only [A.B.C.D]",
+ "OSPF Graceful Restart\n"
+ "Enable Helper support\n"
"Advertising router id\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
struct in_addr addr;
int ret;
- if (argc == 4) {
- ret = inet_aton(argv[3]->arg, &addr);
+ vty_out(vty,
+ "%% This command is deprecated. Please, use `graceful-restart helper enable` instead.\n");
+
+ if (argc == 3) {
+ ret = inet_aton(argv[2]->arg, &addr);
if (!ret) {
vty_out(vty,
"Please specify the valid routerid address.\n");
return CMD_WARNING_CONFIG_FAILED;
}
- ospf_gr_helper_support_set_per_routerid(ospf, &addr,
- OSPF_GR_FALSE);
+ ospf_gr_helper_support_set_per_routerid(ospf, &addr, OSPF_GR_TRUE);
return CMD_SUCCESS;
}
- ospf_gr_helper_support_set(ospf, OSPF_GR_FALSE);
+ ospf_gr_helper_support_set(ospf, OSPF_GR_TRUE);
+
return CMD_SUCCESS;
}
+ALIAS_HIDDEN(no_ospf_gr_helper_enable,
+ no_ospf_gr_helper_only_cmd,
+ "no graceful-restart helper-only [A.B.C.D]",
+ NO_STR
+ "OSPF Graceful Restart\n"
+ "Disable Helper support\n"
+ "Advertising router id\n")
+
DEFPY(ospf_gr_helper_enable_lsacheck,
ospf_gr_helper_enable_lsacheck_cmd,
"graceful-restart helper strict-lsa-checking",
struct advRtr *rtr = bucket->data;
struct vty *vty = (struct vty *)arg;
- vty_out(vty, " graceful-restart helper-only %pI4\n",
+ vty_out(vty, " graceful-restart helper enable %pI4\n",
&rtr->advRtrAddr);
return HASHWALK_CONTINUE;
}
static int config_write_ospf_gr_helper(struct vty *vty, struct ospf *ospf)
{
if (ospf->is_helper_supported)
- vty_out(vty, " graceful-restart helper-only\n");
+ vty_out(vty, " graceful-restart helper enable\n");
if (!ospf->strict_lsa_check)
vty_out(vty,
/*Ospf garcefull restart helper configurations */
install_element(OSPF_NODE, &ospf_gr_helper_enable_cmd);
install_element(OSPF_NODE, &no_ospf_gr_helper_enable_cmd);
+ install_element(OSPF_NODE, &ospf_gr_helper_only_cmd);
+ install_element(OSPF_NODE, &no_ospf_gr_helper_only_cmd);
install_element(OSPF_NODE, &ospf_gr_helper_enable_lsacheck_cmd);
install_element(OSPF_NODE, &no_ospf_gr_helper_enable_lsacheck_cmd);
install_element(OSPF_NODE, &ospf_gr_helper_supported_grace_time_cmd);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
"ipv6_adjacency");
node_src_id = adj_src_ipv6_str;
+ } else {
+ /*
+ * This is just to make the compiler happy about
+ * node_src_id not being initialized. This
+ * should never happen unless we change the cli
+ * function.
+ */
+ assert(!"We must have a adj_src_ipv4_str or a adj_src_ipv6_str");
}
+
/* addresses */
snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/local-address",
index_str);
#include "if.h"
-#include "pim_neighbor.h"
-#include "pim_ifchannel.h"
+struct pim_ifchannel;
+struct pim_neighbor;
+
+enum pim_ifassert_state {
+ PIM_IFASSERT_NOINFO,
+ PIM_IFASSERT_I_AM_WINNER,
+ PIM_IFASSERT_I_AM_LOSER
+};
+
+struct pim_assert_metric {
+ uint32_t rpt_bit_flag;
+ uint32_t metric_preference;
+ uint32_t route_metric;
+ struct in_addr ip_address; /* neighbor router that sourced the Assert
+ message */
+};
/*
RFC 4601: 4.11. Timer Values
#include "zclient.h"
#include "pim_instance.h"
+#include "pim_neighbor.h"
#include "pim_cmd.h"
#include "pim_vty.h"
#include "pim_iface.h"
#include "pimd.h"
#include "pim_iface.h"
#include "pim_instance.h"
+#include "pim_neighbor.h"
#include "pim_rpf.h"
#include "pim_hello.h"
#include "pim_pim.h"
pim->igmp_watermark_limit ? "Set" : "Not Set",
pim->igmp_watermark_limit);
vty_out(vty,
- "Interface Address Group Mode Timer Srcs V Uptime \n");
+ "Interface Group Mode Timer Srcs V Uptime \n");
}
/* scan interfaces */
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp = ifp->info;
- struct listnode *sock_node;
- struct igmp_sock *igmp;
+ struct listnode *grpnode;
+ struct igmp_group *grp;
if (!pim_ifp)
continue;
- /* scan igmp sockets */
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node,
- igmp)) {
- char ifaddr_str[INET_ADDRSTRLEN];
- struct listnode *grpnode;
- struct igmp_group *grp;
-
- pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
- sizeof(ifaddr_str));
-
- /* scan igmp groups */
- for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list,
- grpnode, grp)) {
- char group_str[INET_ADDRSTRLEN];
- char hhmmss[10];
- char uptime[10];
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode,
+ grp)) {
+ char group_str[INET_ADDRSTRLEN];
+ char hhmmss[10];
+ char uptime[10];
- pim_inet4_dump("<group?>", grp->group_addr,
- group_str, sizeof(group_str));
- pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss),
- grp->t_group_timer);
- pim_time_uptime(uptime, sizeof(uptime),
- now - grp->group_creation);
+ pim_inet4_dump("<group?>", grp->group_addr, group_str,
+ sizeof(group_str));
+ pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss),
+ grp->t_group_timer);
+ pim_time_uptime(uptime, sizeof(uptime),
+ now - grp->group_creation);
- if (uj) {
- json_object_object_get_ex(
- json, ifp->name, &json_iface);
-
- if (!json_iface) {
- json_iface =
- json_object_new_object();
- json_object_pim_ifp_add(
- json_iface, ifp);
- json_object_object_add(
- json, ifp->name,
- json_iface);
- json_groups =
- json_object_new_array();
- json_object_object_add(
- json_iface,
- "groups",
- json_groups);
- }
+ if (uj) {
+ json_object_object_get_ex(json, ifp->name,
+ &json_iface);
- json_group = json_object_new_object();
- json_object_string_add(json_group,
- "source",
- ifaddr_str);
- json_object_string_add(json_group,
- "group",
- group_str);
-
- if (grp->igmp_version == 3)
- json_object_string_add(
- json_group, "mode",
- grp->group_filtermode_isexcl
+ if (!json_iface) {
+ json_iface = json_object_new_object();
+ json_object_pim_ifp_add(json_iface,
+ ifp);
+ json_object_object_add(json, ifp->name,
+ json_iface);
+ json_groups = json_object_new_array();
+ json_object_object_add(json_iface,
+ "groups",
+ json_groups);
+ }
+
+ json_group = json_object_new_object();
+ json_object_string_add(json_group, "group",
+ group_str);
+
+ if (grp->igmp_version == 3)
+ json_object_string_add(
+ json_group, "mode",
+ grp->group_filtermode_isexcl
? "EXCLUDE"
: "INCLUDE");
- json_object_string_add(json_group,
- "timer", hhmmss);
- json_object_int_add(
- json_group, "sourcesCount",
- grp->group_source_list
- ? listcount(
- grp->group_source_list)
- : 0);
- json_object_int_add(
- json_group, "version",
- grp->igmp_version);
- json_object_string_add(
- json_group, "uptime", uptime);
- json_object_array_add(json_groups,
- json_group);
- } else {
- vty_out(vty,
- "%-16s %-15s %-15s %4s %8s %4d %d %8s\n",
- ifp->name, ifaddr_str,
- group_str,
- grp->igmp_version == 3
+ json_object_string_add(json_group, "timer",
+ hhmmss);
+ json_object_int_add(
+ json_group, "sourcesCount",
+ grp->group_source_list ? listcount(
+ grp->group_source_list)
+ : 0);
+ json_object_int_add(json_group, "version",
+ grp->igmp_version);
+ json_object_string_add(json_group, "uptime",
+ uptime);
+ json_object_array_add(json_groups, json_group);
+ } else {
+ vty_out(vty, "%-16s %-15s %4s %8s %4d %d %8s\n",
+ ifp->name, group_str,
+ grp->igmp_version == 3
? (grp->group_filtermode_isexcl
- ? "EXCL"
- : "INCL")
+ ? "EXCL"
+ : "INCL")
: "----",
- hhmmss,
- grp->group_source_list
- ? listcount(
- grp->group_source_list)
- : 0,
- grp->igmp_version, uptime);
- }
- } /* scan igmp groups */
- } /* scan igmp sockets */
- } /* scan interfaces */
+ hhmmss,
+ grp->group_source_list ? listcount(
+ grp->group_source_list)
+ : 0,
+ grp->igmp_version, uptime);
+ }
+ } /* scan igmp groups */
+ } /* scan interfaces */
if (uj) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
struct interface *ifp;
vty_out(vty,
- "Interface Address Group RetTimer Counter RetSrcs\n");
+ "Interface Group RetTimer Counter RetSrcs\n");
/* scan interfaces */
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp = ifp->info;
- struct listnode *sock_node;
- struct igmp_sock *igmp;
+ struct listnode *grpnode;
+ struct igmp_group *grp;
if (!pim_ifp)
continue;
- /* scan igmp sockets */
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node,
- igmp)) {
- char ifaddr_str[INET_ADDRSTRLEN];
- struct listnode *grpnode;
- struct igmp_group *grp;
-
- pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
- sizeof(ifaddr_str));
-
- /* scan igmp groups */
- for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list,
- grpnode, grp)) {
- char group_str[INET_ADDRSTRLEN];
- char grp_retr_mmss[10];
- struct listnode *src_node;
- struct igmp_source *src;
- int grp_retr_sources = 0;
-
- pim_inet4_dump("<group?>", grp->group_addr,
- group_str, sizeof(group_str));
- pim_time_timer_to_mmss(
- grp_retr_mmss, sizeof(grp_retr_mmss),
- grp->t_group_query_retransmit_timer);
-
-
- /* count group sources with retransmission state
- */
- for (ALL_LIST_ELEMENTS_RO(
- grp->group_source_list, src_node,
- src)) {
- if (src->source_query_retransmit_count
- > 0) {
- ++grp_retr_sources;
- }
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode,
+ grp)) {
+ char group_str[INET_ADDRSTRLEN];
+ char grp_retr_mmss[10];
+ struct listnode *src_node;
+ struct igmp_source *src;
+ int grp_retr_sources = 0;
+
+ pim_inet4_dump("<group?>", grp->group_addr, group_str,
+ sizeof(group_str));
+ pim_time_timer_to_mmss(
+ grp_retr_mmss, sizeof(grp_retr_mmss),
+ grp->t_group_query_retransmit_timer);
+
+
+ /* count group sources with retransmission state
+ */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
+ src_node, src)) {
+ if (src->source_query_retransmit_count > 0) {
+ ++grp_retr_sources;
}
+ }
- vty_out(vty, "%-16s %-15s %-15s %-8s %7d %7d\n",
- ifp->name, ifaddr_str, group_str,
- grp_retr_mmss,
- grp->group_specific_query_retransmit_count,
- grp_retr_sources);
+ vty_out(vty, "%-16s %-15s %-8s %7d %7d\n", ifp->name,
+ group_str, grp_retr_mmss,
+ grp->group_specific_query_retransmit_count,
+ grp_retr_sources);
- } /* scan igmp groups */
- } /* scan igmp sockets */
- } /* scan interfaces */
+ } /* scan igmp groups */
+ } /* scan interfaces */
}
static void igmp_show_sources(struct pim_instance *pim, struct vty *vty)
now = pim_time_monotonic_sec();
vty_out(vty,
- "Interface Address Group Source Timer Fwd Uptime \n");
+ "Interface Group Source Timer Fwd Uptime \n");
/* scan interfaces */
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp = ifp->info;
- struct listnode *sock_node;
- struct igmp_sock *igmp;
+ struct listnode *grpnode;
+ struct igmp_group *grp;
if (!pim_ifp)
continue;
- /* scan igmp sockets */
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node,
- igmp)) {
- char ifaddr_str[INET_ADDRSTRLEN];
- struct listnode *grpnode;
- struct igmp_group *grp;
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode,
+ grp)) {
+ char group_str[INET_ADDRSTRLEN];
+ struct listnode *srcnode;
+ struct igmp_source *src;
+
+ pim_inet4_dump("<group?>", grp->group_addr, group_str,
+ sizeof(group_str));
- pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
- sizeof(ifaddr_str));
+ /* scan group sources */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
+ srcnode, src)) {
+ char source_str[INET_ADDRSTRLEN];
+ char mmss[10];
+ char uptime[10];
- /* scan igmp groups */
- for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list,
- grpnode, grp)) {
- char group_str[INET_ADDRSTRLEN];
- struct listnode *srcnode;
- struct igmp_source *src;
+ pim_inet4_dump("<source?>", src->source_addr,
+ source_str, sizeof(source_str));
- pim_inet4_dump("<group?>", grp->group_addr,
- group_str, sizeof(group_str));
+ pim_time_timer_to_mmss(mmss, sizeof(mmss),
+ src->t_source_timer);
- /* scan group sources */
- for (ALL_LIST_ELEMENTS_RO(
- grp->group_source_list, srcnode,
- src)) {
- char source_str[INET_ADDRSTRLEN];
- char mmss[10];
- char uptime[10];
-
- pim_inet4_dump(
- "<source?>", src->source_addr,
- source_str, sizeof(source_str));
-
- pim_time_timer_to_mmss(
- mmss, sizeof(mmss),
- src->t_source_timer);
-
- pim_time_uptime(
- uptime, sizeof(uptime),
+ pim_time_uptime(uptime, sizeof(uptime),
now - src->source_creation);
- vty_out(vty,
- "%-16s %-15s %-15s %-15s %5s %3s %8s\n",
- ifp->name, ifaddr_str,
- group_str, source_str, mmss,
- IGMP_SOURCE_TEST_FORWARDING(
- src->source_flags)
+ vty_out(vty, "%-16s %-15s %-15s %5s %3s %8s\n",
+ ifp->name, group_str, source_str, mmss,
+ IGMP_SOURCE_TEST_FORWARDING(
+ src->source_flags)
? "Y"
: "N",
- uptime);
+ uptime);
- } /* scan group sources */
- } /* scan igmp groups */
- } /* scan igmp sockets */
- } /* scan interfaces */
+ } /* scan group sources */
+ } /* scan igmp groups */
+ } /* scan interfaces */
}
static void igmp_show_source_retransmission(struct pim_instance *pim,
struct interface *ifp;
vty_out(vty,
- "Interface Address Group Source Counter\n");
+ "Interface Group Source Counter\n");
/* scan interfaces */
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp = ifp->info;
- struct listnode *sock_node;
- struct igmp_sock *igmp;
+ struct listnode *grpnode;
+ struct igmp_group *grp;
if (!pim_ifp)
continue;
- /* scan igmp sockets */
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node,
- igmp)) {
- char ifaddr_str[INET_ADDRSTRLEN];
- struct listnode *grpnode;
- struct igmp_group *grp;
-
- pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
- sizeof(ifaddr_str));
-
- /* scan igmp groups */
- for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list,
- grpnode, grp)) {
- char group_str[INET_ADDRSTRLEN];
- struct listnode *srcnode;
- struct igmp_source *src;
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode,
+ grp)) {
+ char group_str[INET_ADDRSTRLEN];
+ struct listnode *srcnode;
+ struct igmp_source *src;
- pim_inet4_dump("<group?>", grp->group_addr,
- group_str, sizeof(group_str));
+ pim_inet4_dump("<group?>", grp->group_addr, group_str,
+ sizeof(group_str));
- /* scan group sources */
- for (ALL_LIST_ELEMENTS_RO(
- grp->group_source_list, srcnode,
- src)) {
- char source_str[INET_ADDRSTRLEN];
+ /* scan group sources */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
+ srcnode, src)) {
+ char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump(
- "<source?>", src->source_addr,
- source_str, sizeof(source_str));
+ pim_inet4_dump("<source?>", src->source_addr,
+ source_str, sizeof(source_str));
- vty_out(vty,
- "%-16s %-15s %-15s %-15s %7d\n",
- ifp->name, ifaddr_str,
- group_str, source_str,
- src->source_query_retransmit_count);
+ vty_out(vty, "%-16s %-15s %-15s %7d\n",
+ ifp->name, group_str, source_str,
+ src->source_query_retransmit_count);
- } /* scan group sources */
- } /* scan igmp groups */
- } /* scan igmp sockets */
- } /* scan interfaces */
+ } /* scan group sources */
+ } /* scan igmp groups */
+ } /* scan interfaces */
}
static void pim_show_bsr(struct pim_instance *pim,
xpath_member_value)) {
member_dnode = yang_dnode_get(vty->candidate_config->dnode,
xpath_member_value);
- if (!yang_is_last_list_dnode(member_dnode))
+ if (!member_dnode || !yang_is_last_list_dnode(member_dnode))
return;
}
/* scan interfaces */
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp = ifp->info;
- struct listnode *sock_node;
- struct igmp_sock *igmp;
+ struct igmp_group *grp;
struct pim_ifchannel *ch;
if (!pim_ifp)
}
/* clean up all igmp groups */
- /* scan igmp sockets */
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node,
- igmp)) {
-
- struct igmp_group *grp;
- if (igmp->igmp_group_list) {
- while (igmp->igmp_group_list->count) {
- grp = listnode_head(
- igmp->igmp_group_list);
- igmp_group_delete(grp);
- }
+ if (pim_ifp->igmp_group_list) {
+ while (pim_ifp->igmp_group_list->count) {
+ grp = listnode_head(pim_ifp->igmp_group_list);
+ igmp_group_delete(grp);
}
-
}
}
rpnode->info = NULL;
route_unlock_node(rpnode);
route_unlock_node(rpnode);
+ XFREE(MTYPE_PIM_RP, rp_info);
}
- XFREE(MTYPE_PIM_RP, rp_info);
-
pim_free_bsgrp_node(bsgrp->scope->bsrp_table, &bsgrp->group);
pim_free_bsgrp_data(bsgrp);
}
return CMD_WARNING_CONFIG_FAILED;
}
- nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
+ nb_cli_enqueue_change(vty, xpath_member_value, NB_OP_DESTROY, NULL);
/*
* If this is the last member, then we must remove the group altogether
"frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname);
nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);
- /* Create mesh group member. */
+ /* Create mesh group source. */
strlcat(xpath_value, "/source", sizeof(xpath_value));
nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, saddr_str);
"frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", gname);
nb_cli_enqueue_change(vty, xpath_value, NB_OP_CREATE, NULL);
- /* Create mesh group member. */
+ /* Create mesh group source. */
strlcat(xpath_value, "/source", sizeof(xpath_value));
nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, NULL);
PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options);
pim_ifp->igmp_join_list = NULL;
- pim_ifp->igmp_socket_list = NULL;
pim_ifp->pim_neighbor_list = NULL;
pim_ifp->upstream_switch_list = NULL;
pim_ifp->pim_generation_id = 0;
/* list of struct igmp_sock */
- pim_ifp->igmp_socket_list = list_new();
- pim_ifp->igmp_socket_list->del = (void (*)(void *))igmp_sock_free;
+ pim_igmp_if_init(pim_ifp, ifp);
/* list of struct pim_neighbor */
pim_ifp->pim_neighbor_list = list_new();
pim_if_del_vif(ifp);
pim_ifp->pim->mcast_if_count--;
- list_delete(&pim_ifp->igmp_socket_list);
+ pim_igmp_if_fini(pim_ifp);
+
list_delete(&pim_ifp->pim_neighbor_list);
list_delete(&pim_ifp->upstream_switch_list);
list_delete(&pim_ifp->sec_addr_list);
#include "pim_igmp.h"
#include "pim_upstream.h"
+#include "pim_instance.h"
#include "bfd.h"
#define PIM_IF_MASK_PIM (1 << 0)
int igmp_last_member_query_count; /* IGMP last member query count */
struct list *igmp_socket_list; /* list of struct igmp_sock */
struct list *igmp_join_list; /* list of struct igmp_join */
+ struct list *igmp_group_list; /* list of struct igmp_group */
+ struct hash *igmp_group_hash;
int pim_sock_fd; /* PIM socket file descriptor */
struct thread *t_pim_sock_read; /* thread for reading PIM socket */
#include "if.h"
#include "prefix.h"
+#include "pim_assert.h"
+
struct pim_ifchannel;
#include "pim_upstream.h"
PIM_IFJOIN_PRUNE_PENDING_TMP,
};
-enum pim_ifassert_state {
- PIM_IFASSERT_NOINFO,
- PIM_IFASSERT_I_AM_WINNER,
- PIM_IFASSERT_I_AM_LOSER
-};
-
-struct pim_assert_metric {
- uint32_t rpt_bit_flag;
- uint32_t metric_preference;
- uint32_t route_metric;
- struct in_addr ip_address; /* neighbor router that sourced the Assert
- message */
-};
-
/*
Flag to detect change in CouldAssert(S,G,I)
*/
ifaddr_str, query_interval,
startup_mode ? "startup" : "non-startup", igmp->fd);
}
- igmp->t_igmp_query_timer = NULL;
thread_add_timer(router->master, pim_igmp_general_query, igmp,
query_interval, &igmp->t_igmp_query_timer);
}
XFREE(MTYPE_PIM_IGMP_GROUP, group);
}
-static void igmp_group_count_incr(struct igmp_sock *igmp)
+static void igmp_group_count_incr(struct pim_interface *pim_ifp)
{
- struct pim_interface *pim_ifp = igmp->interface->info;
-
- if (!pim_ifp)
- return;
-
++pim_ifp->pim->igmp_group_count;
if (pim_ifp->pim->igmp_group_count
== pim_ifp->pim->igmp_watermark_limit) {
}
}
-static void igmp_group_count_decr(struct igmp_sock *igmp)
+static void igmp_group_count_decr(struct pim_interface *pim_ifp)
{
- struct pim_interface *pim_ifp = igmp->interface->info;
-
- if (!pim_ifp)
- return;
-
if (pim_ifp->pim->igmp_group_count == 0) {
zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
VRF_LOGNAME(pim_ifp->pim->vrf));
struct listnode *src_node;
struct listnode *src_nextnode;
struct igmp_source *src;
+ struct pim_interface *pim_ifp = group->interface->info;
if (PIM_DEBUG_IGMP_TRACE) {
char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str,
sizeof(group_str));
- zlog_debug("Deleting IGMP group %s from socket %d interface %s",
- group_str, group->group_igmp_sock->fd,
- group->group_igmp_sock->interface->name);
+ zlog_debug("Deleting IGMP group %s from interface %s",
+ group_str, group->interface->name);
}
for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
THREAD_OFF(group->t_group_query_retransmit_timer);
group_timer_off(group);
- igmp_group_count_decr(group->group_igmp_sock);
- listnode_delete(group->group_igmp_sock->igmp_group_list, group);
- hash_release(group->group_igmp_sock->igmp_group_hash, group);
+ igmp_group_count_decr(pim_ifp);
+ listnode_delete(pim_ifp->igmp_group_list, group);
+ hash_release(pim_ifp->igmp_group_hash, group);
igmp_group_free(group);
}
assert(!igmp->t_igmp_read);
assert(!igmp->t_igmp_query_timer);
assert(!igmp->t_other_querier_timer);
- assert(igmp->igmp_group_list);
- assert(!listcount(igmp->igmp_group_list));
-
- list_delete(&igmp->igmp_group_list);
- hash_free(igmp->igmp_group_hash);
XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
}
void igmp_sock_delete(struct igmp_sock *igmp)
{
struct pim_interface *pim_ifp;
- struct listnode *grp_node;
- struct listnode *grp_nextnode;
- struct igmp_group *grp;
-
- for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode,
- grp)) {
- igmp_group_delete(grp);
- }
sock_close(igmp);
listnode_delete(pim_ifp->igmp_socket_list, igmp);
igmp_sock_free(igmp);
+
+ if (!listcount(pim_ifp->igmp_socket_list))
+ pim_igmp_if_reset(pim_ifp);
}
void igmp_sock_delete_all(struct interface *ifp)
return false;
}
+void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp)
+{
+ char hash_name[64];
+
+ pim_ifp->igmp_socket_list = list_new();
+ pim_ifp->igmp_socket_list->del = (void (*)(void *))igmp_sock_free;
+
+ pim_ifp->igmp_group_list = list_new();
+ pim_ifp->igmp_group_list->del = (void (*)(void *))igmp_group_free;
+
+ snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
+ pim_ifp->igmp_group_hash = hash_create(
+ igmp_group_hash_key, igmp_group_hash_equal, hash_name);
+}
+
+void pim_igmp_if_reset(struct pim_interface *pim_ifp)
+{
+ struct listnode *grp_node, *grp_nextnode;
+ struct igmp_group *grp;
+
+ for (ALL_LIST_ELEMENTS(pim_ifp->igmp_group_list, grp_node, grp_nextnode,
+ grp)) {
+ igmp_group_delete(grp);
+ }
+}
+
+void pim_igmp_if_fini(struct pim_interface *pim_ifp)
+{
+ pim_igmp_if_reset(pim_ifp);
+
+ assert(pim_ifp->igmp_group_list);
+ assert(!listcount(pim_ifp->igmp_group_list));
+
+ list_delete(&pim_ifp->igmp_group_list);
+ hash_free(pim_ifp->igmp_group_hash);
+
+ list_delete(&pim_ifp->igmp_socket_list);
+}
+
static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
struct interface *ifp, int mtrace_only)
{
struct pim_interface *pim_ifp;
struct igmp_sock *igmp;
- char hash_name[64];
pim_ifp = ifp->info;
igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
- igmp->igmp_group_list = list_new();
- igmp->igmp_group_list->del = (void (*)(void *))igmp_group_free;
-
- snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
- igmp->igmp_group_hash = hash_create(igmp_group_hash_key,
- igmp_group_hash_equal, hash_name);
-
igmp->fd = fd;
igmp->interface = ifp;
igmp->ifaddr = ifaddr;
zlog_debug("Scheduling READ event on IGMP socket fd=%d",
igmp->fd);
}
- igmp->t_igmp_read = NULL;
thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
&igmp->t_igmp_read);
}
pim_inet4_dump("<group?>", group->group_addr, group_str,
sizeof(group_str));
zlog_debug("%s: Timer for group %s on interface %s", __func__,
- group_str, group->group_igmp_sock->interface->name);
+ group_str, group->interface->name);
}
assert(group->group_filtermode_isexcl);
pim_inet4_dump("<group?>", group->group_addr, group_str,
sizeof(group_str));
zlog_debug("Cancelling TIMER event for group %s on %s",
- group_str, group->group_igmp_sock->interface->name);
+ group_str, group->interface->name);
}
THREAD_OFF(group->t_group_timer);
}
struct in_addr group_addr)
{
struct igmp_group lookup;
+ struct pim_interface *pim_ifp = igmp->interface->info;
lookup.group_addr.s_addr = group_addr.s_addr;
- return hash_lookup(igmp->igmp_group_hash, &lookup);
+ return hash_lookup(pim_ifp->igmp_group_hash, &lookup);
}
struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
struct in_addr group_addr)
{
struct igmp_group *group;
+ struct pim_interface *pim_ifp = igmp->interface->info;
group = find_group_by_addr(igmp, group_addr);
if (group) {
group->t_group_query_retransmit_timer = NULL;
group->group_specific_query_retransmit_count = 0;
group->group_addr = group_addr;
- group->group_igmp_sock = igmp;
+ group->interface = igmp->interface;
group->last_igmp_v1_report_dsec = -1;
group->last_igmp_v2_report_dsec = -1;
group->group_creation = pim_time_monotonic_sec();
/* initialize new group as INCLUDE {empty} */
group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
- listnode_add(igmp->igmp_group_list, group);
- group = hash_get(igmp->igmp_group_hash, group, hash_alloc_intern);
+ listnode_add(pim_ifp->igmp_group_list, group);
+ group = hash_get(pim_ifp->igmp_group_hash, group, hash_alloc_intern);
if (PIM_DEBUG_IGMP_TRACE) {
char group_str[INET_ADDRSTRLEN];
group_str, igmp->fd, igmp->interface->name);
}
- igmp_group_count_incr(igmp);
+ igmp_group_count_incr(pim_ifp);
/*
RFC 3376: 6.2.2. Definition of Group Timers
bool mtrace_only;
- struct list *igmp_group_list; /* list of struct igmp_group */
- struct hash *igmp_group_hash;
-
struct igmp_stats rx_stats;
};
+struct pim_interface;
+
+void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp);
+void pim_igmp_if_reset(struct pim_interface *pim_ifp);
+void pim_igmp_if_fini(struct pim_interface *pim_ifp);
+
struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
struct in_addr ifaddr);
struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, int fd);
int group_filtermode_isexcl; /* 0=INCLUDE, 1=EXCLUDE */
struct list *group_source_list; /* list of struct igmp_source */
time_t group_creation;
- struct igmp_sock *group_igmp_sock; /* back pointer */
+ struct interface *interface;
int64_t last_igmp_v1_report_dsec;
int64_t last_igmp_v2_report_dsec;
};
struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
struct in_addr group_addr);
+struct igmp_source *igmp_get_source_by_addr(struct igmp_group *group,
+ struct in_addr src_addr,
+ bool *created);
+
void igmp_group_delete_empty_include(struct igmp_group *group);
void igmp_startup_mode_on(struct igmp_sock *igmp);
void igmp_group_timer_on(struct igmp_group *group, long interval_msec,
const char *ifname);
-struct igmp_source *source_new(struct igmp_group *group,
- struct in_addr src_addr);
-
void igmp_send_query(int igmp_version, struct igmp_group *group, int fd,
const char *ifname, char *query_buf, int query_buf_size,
int num_sources, struct in_addr dst_addr,
}
}
+static inline long igmp_gmi_msec(struct igmp_group *group)
+{
+ struct pim_interface *pim_ifp = group->interface->info;
+ struct igmp_sock *igmp;
+ struct listnode *sock_node;
+
+ long qrv = 0, qqi = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ qrv = MAX(qrv, igmp->querier_robustness_variable);
+ qqi = MAX(qqi, igmp->querier_query_interval);
+ }
+ return PIM_IGMP_GMI_MSEC(qrv, qqi,
+ pim_ifp->igmp_query_max_response_time_dsec);
+}
+
void igmp_group_reset_gmi(struct igmp_group *group)
{
long group_membership_interval_msec;
- struct pim_interface *pim_ifp;
- struct igmp_sock *igmp;
struct interface *ifp;
- igmp = group->group_igmp_sock;
- ifp = igmp->interface;
- pim_ifp = ifp->info;
+ ifp = group->interface;
/*
RFC 3376: 8.4. Group Membership Interval
(1000 * querier_query_interval) +
100 * query_response_interval_dsec;
*/
- group_membership_interval_msec = PIM_IGMP_GMI_MSEC(
- igmp->querier_robustness_variable, igmp->querier_query_interval,
- pim_ifp->igmp_query_max_response_time_dsec);
+ group_membership_interval_msec = igmp_gmi_msec(group);
if (PIM_DEBUG_IGMP_TRACE) {
char group_str[INET_ADDRSTRLEN];
zlog_debug(
"%s: Source timer expired for group %s source %s on %s",
__func__, group_str, source_str,
- group->group_igmp_sock->interface->name);
+ group->interface->name);
}
/*
sizeof(source_str));
zlog_debug(
"Cancelling TIMER event for group %s source %s on %s",
- group_str, source_str,
- group->group_igmp_sock->interface->name);
+ group_str, source_str, group->interface->name);
}
THREAD_OFF(source->t_source_timer);
struct igmp_source *source, long interval_msec)
{
source_timer_off(group, source);
- struct pim_interface *pim_ifp = group->group_igmp_sock->interface->info;
+ struct pim_interface *pim_ifp = group->interface->info;
if (PIM_DEBUG_IGMP_EVENTS) {
char group_str[INET_ADDRSTRLEN];
zlog_debug(
"Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
interval_msec / 1000, interval_msec % 1000, group_str,
- source_str, group->group_igmp_sock->interface->name);
+ source_str, group->interface->name);
}
thread_add_timer_msec(router->master, igmp_source_timer, source,
igmp_source_forward_start(pim_ifp->pim, source);
}
-void igmp_source_reset_gmi(struct igmp_sock *igmp, struct igmp_group *group,
- struct igmp_source *source)
+void igmp_source_reset_gmi(struct igmp_group *group, struct igmp_source *source)
{
long group_membership_interval_msec;
- struct pim_interface *pim_ifp;
struct interface *ifp;
- ifp = igmp->interface;
- pim_ifp = ifp->info;
+ ifp = group->interface;
- group_membership_interval_msec = PIM_IGMP_GMI_MSEC(
- igmp->querier_robustness_variable, igmp->querier_query_interval,
- pim_ifp->igmp_query_max_response_time_dsec);
+ group_membership_interval_msec = igmp_gmi_msec(group);
if (PIM_DEBUG_IGMP_TRACE) {
char group_str[INET_ADDRSTRLEN];
*/
static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
{
- struct pim_interface *pim_ifp = group->group_igmp_sock->interface->info;
+ struct pim_interface *pim_ifp = group->interface->info;
assert(group->group_filtermode_isexcl);
pim_inet4_dump("<source?>", source->source_addr, source_str,
sizeof(source_str));
zlog_debug(
- "Deleting IGMP source %s for group %s from socket %d interface %s c_oil ref_count %d",
- source_str, group_str, group->group_igmp_sock->fd,
- group->group_igmp_sock->interface->name,
+ "Deleting IGMP source %s for group %s from interface %s c_oil ref_count %d",
+ source_str, group_str, group->interface->name,
source->source_channel_oil
? source->source_channel_oil->oil_ref_count
: 0);
pim_inet4_dump("<source?>", source->source_addr, source_str,
sizeof(source_str));
zlog_warn(
- "%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
+ "%s: forwarding=ON(!) IGMP source %s for group %s from interface %s",
__func__, source_str, group_str,
- group->group_igmp_sock->fd,
- group->group_igmp_sock->interface->name);
+ group->interface->name);
/* warning only */
}
return 0;
}
-struct igmp_source *source_new(struct igmp_group *group,
- struct in_addr src_addr)
+struct igmp_source *igmp_get_source_by_addr(struct igmp_group *group,
+ struct in_addr src_addr, bool *new)
{
struct igmp_source *src;
+ if (new)
+ *new = false;
+
+ src = igmp_find_source_by_addr(group, src_addr);
+ if (src)
+ return src;
+
if (PIM_DEBUG_IGMP_TRACE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<source?>", src_addr, source_str,
sizeof(source_str));
zlog_debug(
- "Creating new IGMP source %s for group %s on socket %d interface %s",
- source_str, group_str, group->group_igmp_sock->fd,
- group->group_igmp_sock->interface->name);
+ "Creating new IGMP source %s for group %s on interface %s",
+ source_str, group_str, group->interface->name);
}
src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
/* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
igmp_anysource_forward_stop(group);
-
- return src;
-}
-
-static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
- struct igmp_group *group,
- struct in_addr src_addr)
-{
- struct igmp_source *src;
-
- src = igmp_find_source_by_addr(group, src_addr);
- if (src) {
- return src;
- }
-
- src = source_new(group, src_addr);
-
return src;
}
source = igmp_find_source_by_addr(group, star);
if (source)
- igmp_source_reset_gmi(igmp, group,
- source);
+ igmp_source_reset_gmi(group, source);
}
} else {
igmp_group_delete(group);
src_addr = sources + i;
- source = add_source_by_addr(igmp, group, *src_addr);
- if (!source) {
+ source = igmp_get_source_by_addr(group, *src_addr, NULL);
+ if (!source)
continue;
- }
/*
RFC 3376: 6.4.1. Reception of Current-State Records
igmp_source_reset_gmi() below, resetting the source timers to
GMI, accomplishes this.
*/
- igmp_source_reset_gmi(igmp, group, source);
+ igmp_source_reset_gmi(group, source);
} /* scan received sources */
}
/* scan received sources (A) */
for (i = 0; i < num_sources; ++i) {
struct in_addr *src_addr;
+ bool new;
src_addr = sources + i;
/* E.2: lookup reported source from (A) in (X,Y) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
+ source = igmp_get_source_by_addr(group, *src_addr, &new);
+ if (!source)
+ continue;
+
+ if (!new) {
/* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
IGMP_SOURCE_DONT_DELETE(source->source_flags);
} else {
/* E.4: if not found, create source with timer=GMI:
* (A-X-Y) */
- source = source_new(group, *src_addr);
assert(!source->t_source_timer); /* timer == 0 */
- igmp_source_reset_gmi(group->group_igmp_sock, group,
- source);
+ igmp_source_reset_gmi(group, source);
assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
}
source = igmp_find_source_by_addr(group, star);
if (source) {
IGMP_SOURCE_DONT_DELETE(source->source_flags);
- igmp_source_reset_gmi(group->group_igmp_sock, group,
- source);
+ igmp_source_reset_gmi(group, source);
}
}
for (i = 0; i < num_sources; ++i) {
struct igmp_source *source;
struct in_addr *src_addr;
+ bool new;
src_addr = sources + i;
/* I.2: lookup reported source (B) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
+ source = igmp_get_source_by_addr(group, *src_addr, &new);
+ if (!source)
+ continue;
+
+ if (!new) {
/* I.3: if found, clear deletion flag (A*B) */
IGMP_SOURCE_DONT_DELETE(source->source_flags);
} else {
/* I.4: if not found, create source with timer=0 (B-A)
*/
- source = source_new(group, *src_addr);
assert(!source->t_source_timer); /* (B-A) timer=0 */
}
static void toin_incl(struct igmp_group *group, int num_sources,
struct in_addr *sources)
{
- struct igmp_sock *igmp = group->group_igmp_sock;
int num_sources_tosend = listcount(group->group_source_list);
int i;
for (i = 0; i < num_sources; ++i) {
struct igmp_source *source;
struct in_addr *src_addr;
+ bool new;
src_addr = sources + i;
/* Lookup reported source (B) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
+ source = igmp_get_source_by_addr(group, *src_addr, &new);
+ if (!source)
+ continue;
+
+ if (!new) {
/* If found, clear SEND flag (A*B) */
IGMP_SOURCE_DONT_SEND(source->source_flags);
--num_sources_tosend;
- } else {
- /* If not found, create new source */
- source = source_new(group, *src_addr);
}
/* (B)=GMI */
- igmp_source_reset_gmi(igmp, group, source);
+ igmp_source_reset_gmi(group, source);
}
/* Send sources marked with SEND flag: Q(G,A-B) */
static void toin_excl(struct igmp_group *group, int num_sources,
struct in_addr *sources)
{
- struct igmp_sock *igmp = group->group_igmp_sock;
int num_sources_tosend;
int i;
for (i = 0; i < num_sources; ++i) {
struct igmp_source *source;
struct in_addr *src_addr;
+ bool new;
src_addr = sources + i;
/* Lookup reported source (A) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
- if (source->t_source_timer) {
- /* If found and timer running, clear SEND flag
- * (X*A) */
- IGMP_SOURCE_DONT_SEND(source->source_flags);
- --num_sources_tosend;
- }
- } else {
- /* If not found, create new source */
- source = source_new(group, *src_addr);
+ source = igmp_get_source_by_addr(group, *src_addr, &new);
+ if (!source)
+ continue;
+
+ if (source->t_source_timer) {
+ /* If found and timer running, clear SEND flag
+ * (X*A) */
+ IGMP_SOURCE_DONT_SEND(source->source_flags);
+ --num_sources_tosend;
}
/* (A)=GMI */
- igmp_source_reset_gmi(igmp, group, source);
+ igmp_source_reset_gmi(group, source);
}
/* Send sources marked with SEND flag: Q(G,X-A) */
for (i = 0; i < num_sources; ++i) {
struct igmp_source *source;
struct in_addr *src_addr;
+ bool new;
src_addr = sources + i;
/* Lookup reported source (B) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
+ source = igmp_get_source_by_addr(group, *src_addr, &new);
+ if (!new) {
/* If found, clear deletion flag: (A*B) */
IGMP_SOURCE_DONT_DELETE(source->source_flags);
/* and set SEND flag (A*B) */
IGMP_SOURCE_DO_SEND(source->source_flags);
++num_sources_tosend;
- } else {
- /* If source not found, create source with timer=0:
- * (B-A)=0 */
- source = source_new(group, *src_addr);
- assert(!source->t_source_timer); /* (B-A) timer=0 */
}
} /* Scan received sources (B) */
for (i = 0; i < num_sources; ++i) {
struct igmp_source *source;
struct in_addr *src_addr;
+ bool new;
src_addr = sources + i;
/* lookup reported source (A) in known sources (X,Y) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
+ source = igmp_get_source_by_addr(group, *src_addr, &new);
+ if (!source)
+ continue;
+
+ if (!new) {
/* if found, clear off DELETE flag from reported source
* (A) */
IGMP_SOURCE_DONT_DELETE(source->source_flags);
/* if not found, create source with Group Timer:
* (A-X-Y)=Group Timer */
long group_timer_msec;
- source = source_new(group, *src_addr);
assert(!source->t_source_timer); /* timer == 0 */
group_timer_msec = igmp_group_timer_remain_msec(group);
allow(igmp, from, group_addr, num_sources, sources);
}
+static void igmp_send_query_group(struct igmp_group *group, char *query_buf,
+ size_t query_buf_size, int num_sources,
+ int s_flag)
+{
+ struct interface *ifp = group->interface;
+ struct pim_interface *pim_ifp = ifp->info;
+ struct igmp_sock *igmp;
+ struct listnode *sock_node;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
+ igmp_send_query(
+ pim_ifp->igmp_version, group, igmp->fd, ifp->name,
+ query_buf, query_buf_size, num_sources,
+ group->group_addr, group->group_addr,
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ s_flag, igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
+ }
+}
+
/*
RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
*/
static void group_retransmit_group(struct igmp_group *group)
{
- struct igmp_sock *igmp;
struct pim_interface *pim_ifp;
long lmqc; /* Last Member Query Count */
long lmqi_msec; /* Last Member Query Interval */
int s_flag;
int query_buf_size;
- igmp = group->group_igmp_sock;
- pim_ifp = igmp->interface->info;
+ pim_ifp = group->interface->info;
if (pim_ifp->igmp_version == 3) {
query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
sizeof(group_str));
zlog_debug(
"retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
- group_str, igmp->interface->name, s_flag,
+ group_str, group->interface->name, s_flag,
group->group_specific_query_retransmit_count);
}
interest.
*/
- igmp_send_query(pim_ifp->igmp_version, group, igmp->fd,
- igmp->interface->name, query_buf, sizeof(query_buf),
- 0 /* num_sources_tosend */,
- group->group_addr /* dst_addr */,
- group->group_addr /* group_addr */,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- s_flag, igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query_group(group, query_buf, sizeof(query_buf), 0, s_flag);
}
/*
static int group_retransmit_sources(struct igmp_group *group,
int send_with_sflag_set)
{
- struct igmp_sock *igmp;
struct pim_interface *pim_ifp;
long lmqc; /* Last Member Query Count */
long lmqi_msec; /* Last Member Query Interval */
source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
- igmp = group->group_igmp_sock;
- pim_ifp = igmp->interface->info;
+ pim_ifp = group->interface->info;
lmqc = pim_ifp->igmp_last_member_query_count;
lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
sizeof(group_str));
zlog_debug(
"retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
- group_str, igmp->interface->name, num_sources_tosend1,
+ group_str, group->interface->name, num_sources_tosend1,
num_sources_tosend2, send_with_sflag_set,
num_retransmit_sources_left);
}
zlog_warn(
"%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
__func__, group_str,
- igmp->interface->name,
+ group->interface->name,
num_sources_tosend1, sizeof(query_buf1),
query_buf1_max_sources);
} else {
interest.
*/
- igmp_send_query(
- pim_ifp->igmp_version, group, igmp->fd,
- igmp->interface->name, query_buf1,
- sizeof(query_buf1), num_sources_tosend1,
- group->group_addr, group->group_addr,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- 1 /* s_flag */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query_group(
+ group, query_buf1, sizeof(query_buf1),
+ num_sources_tosend1, 1 /* s_flag */);
}
} /* send_with_sflag_set */
sizeof(group_str));
zlog_warn(
"%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
- __func__, group_str, igmp->interface->name,
+ __func__, group_str, group->interface->name,
num_sources_tosend2, sizeof(query_buf2),
query_buf2_max_sources);
} else {
interest.
*/
- igmp_send_query(
- pim_ifp->igmp_version, group, igmp->fd,
- igmp->interface->name, query_buf2,
- sizeof(query_buf2), num_sources_tosend2,
- group->group_addr, group->group_addr,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- 0 /* s_flag */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query_group(
+ group, query_buf2, sizeof(query_buf2),
+ num_sources_tosend2, 0 /* s_flag */);
}
}
pim_inet4_dump("<group?>", group->group_addr, group_str,
sizeof(group_str));
zlog_debug("group_retransmit_timer: group %s on %s", group_str,
- group->group_igmp_sock->interface->name);
+ group->interface->name);
}
/* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
*/
static void group_retransmit_timer_on(struct igmp_group *group)
{
- struct igmp_sock *igmp;
struct pim_interface *pim_ifp;
long lmqi_msec; /* Last Member Query Interval */
return;
}
- igmp = group->group_igmp_sock;
- pim_ifp = igmp->interface->info;
+ pim_ifp = group->interface->info;
lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
zlog_debug(
"Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
lmqi_msec / 1000, lmqi_msec % 1000, group_str,
- igmp->interface->name);
+ group->interface->name);
}
thread_add_timer_msec(router->master, igmp_group_retransmit, group,
static void group_query_send(struct igmp_group *group)
{
struct pim_interface *pim_ifp;
- struct igmp_sock *igmp;
long lmqc; /* Last Member Query Count */
- igmp = group->group_igmp_sock;
- pim_ifp = igmp->interface->info;
+ pim_ifp = group->interface->info;
lmqc = pim_ifp->igmp_last_member_query_count;
/* lower group timer to lmqt */
static void source_query_send_by_flag(struct igmp_group *group,
int num_sources_tosend)
{
- struct igmp_sock *igmp;
struct pim_interface *pim_ifp;
struct listnode *src_node;
struct igmp_source *src;
assert(num_sources_tosend > 0);
- igmp = group->group_igmp_sock;
- pim_ifp = igmp->interface->info;
+ pim_ifp = group->interface->info;
lmqc = pim_ifp->igmp_last_member_query_count;
lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
for (i = 0; i < num_sources; ++i) {
struct igmp_source *source;
struct in_addr *src_addr;
+ bool new;
src_addr = sources + i;
/* lookup reported source (A) in known sources (X,Y) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (!source) {
+ source = igmp_get_source_by_addr(group, *src_addr, &new);
+ if (!source)
+ continue;
+
+ if (new) {
/* 3: if not found, create source with Group Timer:
* (A-X-Y)=Group Timer */
long group_timer_msec;
- source = source_new(group, *src_addr);
assert(!source->t_source_timer); /* timer == 0 */
group_timer_msec = igmp_group_timer_remain_msec(group);
void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
{
- struct igmp_sock *igmp;
struct interface *ifp;
struct pim_interface *pim_ifp;
char *ifname;
return;
}
- igmp = group->group_igmp_sock;
- ifp = igmp->interface;
+ ifp = group->interface;
pim_ifp = ifp->info;
ifname = ifp->name;
void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
{
struct igmp_group *group;
- struct igmp_sock *igmp;
struct interface *ifp;
struct pim_interface *pim_ifp;
char *ifname;
int lmqt_msec; /* Last Member Query Time */
group = source->source_group;
- igmp = group->group_igmp_sock;
- ifp = igmp->interface;
+ ifp = group->interface;
pim_ifp = ifp->info;
ifname = ifp->name;
#include <zebra.h>
#include "if.h"
+#include "pim_igmp.h"
+
#define IGMP_V3_CHECKSUM_OFFSET (2)
#define IGMP_V3_REPORT_NUMGROUPS_OFFSET (6)
#define IGMP_V3_REPORT_GROUPPRECORD_OFFSET (8)
#define PIM_IGMP_OHPI_DSEC(qrv,qqi,qri_dsec) ((qrv) * (10 * (qqi)) + (qri_dsec))
void igmp_group_reset_gmi(struct igmp_group *group);
-void igmp_source_reset_gmi(struct igmp_sock *igmp, struct igmp_group *group,
+void igmp_source_reset_gmi(struct igmp_group *group,
struct igmp_source *source);
void igmp_source_free(struct igmp_source *source);
void pim_vrf_init(void);
void pim_vrf_terminate(void);
+extern struct pim_router *router;
+
struct pim_instance *pim_get_pim_instance(vrf_id_t vrf_id);
#endif
Above: from <linux/mroute.h>
*/
+struct channel_oil;
+
int pim_mroute_socket_enable(struct pim_instance *pim);
int pim_mroute_socket_disable(struct pim_instance *pim);
/* XXX: this can use a bit of refining and extensions */
bool pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp)
{
- struct pim_nexthop nexthop;
+ struct pim_nexthop nexthop = {0};
if (mp->peer.s_addr == rp.s_addr) {
return true;
/* add accept thread */
listener->fd = sock;
memcpy(&listener->su, &sin, socklen);
- listener->thread = NULL;
thread_add_read(pim->msdp.master, pim_msdp_sock_accept, pim, sock,
&listener->thread);
#include "pim_nb.h"
#include "lib/northbound_cli.h"
#include "pim_igmpv3.h"
+#include "pim_neighbor.h"
#include "pim_pim.h"
#include "pim_mlag.h"
#include "pim_bfd.h"
static void pim_if_membership_refresh(struct interface *ifp)
{
struct pim_interface *pim_ifp;
- struct listnode *sock_node;
- struct igmp_sock *igmp;
+ struct listnode *grpnode;
+ struct igmp_group *grp;
+
pim_ifp = ifp->info;
assert(pim_ifp);
* the interface
*/
- /* scan igmp sockets */
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
- struct listnode *grpnode;
- struct igmp_group *grp;
-
- /* scan igmp groups */
- for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode,
- grp)) {
- struct listnode *srcnode;
- struct igmp_source *src;
-
- /* scan group sources */
- for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
- srcnode, src)) {
-
- if (IGMP_SOURCE_TEST_FORWARDING(
- src->source_flags)) {
- struct prefix_sg sg;
-
- memset(&sg, 0,
- sizeof(struct prefix_sg));
- sg.src = src->source_addr;
- sg.grp = grp->group_addr;
- pim_ifchannel_local_membership_add(
- ifp, &sg, false /*is_vxlan*/);
- }
-
- } /* scan group sources */
- } /* scan igmp groups */
- } /* scan igmp sockets */
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode, grp)) {
+ struct listnode *srcnode;
+ struct igmp_source *src;
+
+ /* scan group sources */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode,
+ src)) {
+
+ if (IGMP_SOURCE_TEST_FORWARDING(src->source_flags)) {
+ struct prefix_sg sg;
+
+ memset(&sg, 0, sizeof(struct prefix_sg));
+ sg.src = src->source_addr;
+ sg.grp = grp->group_addr;
+ pim_ifchannel_local_membership_add(
+ ifp, &sg, false /*is_vxlan*/);
+ }
+
+ } /* scan group sources */
+ } /* scan igmp groups */
/*
* Finally delete every PIM (S,G) entry lacking all state info
{
struct listnode *sock_node;
struct igmp_sock *igmp;
+ struct listnode *grp_node;
+ struct igmp_group *grp;
if (pim_ifp->igmp_query_max_response_time_dsec
== query_max_response_time_dsec)
/* scan all sockets */
for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
- struct listnode *grp_node;
- struct igmp_group *grp;
-
/* reschedule socket general query */
igmp_sock_query_reschedule(igmp);
+ }
- /* scan socket groups */
- for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grp_node,
- grp)) {
- struct listnode *src_node;
- struct igmp_source *src;
-
- /* reset group timers for groups in EXCLUDE mode */
- if (grp->group_filtermode_isexcl)
- igmp_group_reset_gmi(grp);
-
- /* scan group sources */
- for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
- src_node, src)) {
-
- /* reset source timers for sources with running
- * timers
- */
- if (src->t_source_timer)
- igmp_source_reset_gmi(igmp, grp, src);
- }
+ /* scan socket groups */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grp_node, grp)) {
+ struct listnode *src_node;
+ struct igmp_source *src;
+
+ /* reset group timers for groups in EXCLUDE mode */
+ if (grp->group_filtermode_isexcl)
+ igmp_group_reset_gmi(grp);
+
+ /* scan group sources */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node,
+ src)) {
+
+ /* reset source timers for sources with running
+ * timers
+ */
+ if (src->t_source_timer)
+ igmp_source_reset_gmi(grp, src);
}
}
}
{
struct pim_msdp_mg_mbr *mbr;
struct pim_msdp_mg *mg;
+ const struct lyd_node *mg_dnode;
switch (args->event) {
case NB_EV_VALIDATE:
break;
case NB_EV_APPLY:
mbr = nb_running_get_entry(args->dnode, NULL, true);
- mg = nb_running_get_entry(args->dnode, "../", true);
-
+ mg_dnode =
+ yang_dnode_get_parent(args->dnode, "msdp-mesh-groups");
+ mg = nb_running_get_entry(mg_dnode, NULL, true);
pim_msdp_mg_mbr_del(mg, mbr);
+ nb_running_unset_entry(args->dnode);
break;
}
#include "prefix.h"
#include "pim_tlv.h"
+#include "pim_iface.h"
struct pim_neighbor {
int64_t creation; /* timestamp of creation */
#ifndef PIM_OIL_H
#define PIM_OIL_H
+struct pim_interface;
+
#include "pim_mroute.h"
-#include "pim_iface.h"
/*
* Where did we get this (S,G) from?
zlog_debug("Scheduling READ event on PIM socket fd=%d",
pim_ifp->pim_sock_fd);
}
- pim_ifp->t_pim_sock_read = NULL;
thread_add_read(router->master, pim_sock_read, ifp,
pim_ifp->pim_sock_fd, &pim_ifp->t_pim_sock_read);
}
#include "pim_rpf.h"
#include "pim_sock.h"
#include "pim_memory.h"
-#include "pim_iface.h"
+#include "pim_neighbor.h"
#include "pim_msdp.h"
#include "pim_nht.h"
#include "pim_mroute.h"
#include "prefix.h"
#include "vty.h"
#include "plist.h"
-#include "pim_iface.h"
#include "pim_rpf.h"
+struct pim_interface;
+
enum rp_source {
RP_SRC_NONE = 0,
RP_SRC_STATIC,
#include "pim_pim.h"
#include "pim_str.h"
#include "pim_iface.h"
+#include "pim_neighbor.h"
#include "pim_zlookup.h"
#include "pim_ifchannel.h"
#include "pim_time.h"
#include <zebra.h>
-#include "pim_upstream.h"
-#include "pim_neighbor.h"
-
/*
RFC 4601:
#include <prefix.h>
#include "plist.h"
-#include <pimd/pim_rpf.h>
+#include "pim_rpf.h"
#include "pim_str.h"
#include "pim_ifchannel.h"
assert(group->group_filtermode_isexcl);
assert(listcount(group->group_source_list) < 1);
- source = source_new(group, src_addr);
+ source = igmp_get_source_by_addr(group, src_addr, NULL);
if (!source) {
zlog_warn("%s: Failure to create * source", __func__);
return;
sg.src = source->source_addr;
sg.grp = group->group_addr;
- ch = pim_ifchannel_find(group->group_igmp_sock->interface, &sg);
+ ch = pim_ifchannel_find(group->interface, &sg);
if (pim_is_grp_ssm(pim, group->group_addr)) {
/* If SSM group withdraw local membership */
if (ch
zlog_debug(
"local membership del for %s as G is now SSM",
pim_str_sg_dump(&sg));
- pim_ifchannel_local_membership_del(
- group->group_igmp_sock->interface, &sg);
+ pim_ifchannel_local_membership_del(group->interface,
+ &sg);
}
} else {
/* If ASM group add local membership */
"local membership add for %s as G is now ASM",
pim_str_sg_dump(&sg));
pim_ifchannel_local_membership_add(
- group->group_igmp_sock->interface, &sg,
- false /*is_vxlan*/);
+ group->interface, &sg, false /*is_vxlan*/);
}
}
}
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp = ifp->info;
- struct listnode *sock_node;
- struct igmp_sock *igmp;
+ struct listnode *grpnode;
+ struct igmp_group *grp;
if (!pim_ifp)
continue;
- /* scan igmp sockets */
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node,
- igmp)) {
- struct listnode *grpnode;
- struct igmp_group *grp;
-
- /* scan igmp groups */
- for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list,
- grpnode, grp)) {
- struct listnode *srcnode;
- struct igmp_source *src;
-
- /* scan group sources */
- for (ALL_LIST_ELEMENTS_RO(
- grp->group_source_list, srcnode,
- src)) {
- igmp_source_forward_reevaluate_one(pim,
- src);
- } /* scan group sources */
- } /* scan igmp groups */
- } /* scan igmp sockets */
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_group_list, grpnode,
+ grp)) {
+ struct listnode *srcnode;
+ struct igmp_source *src;
+
+ /* scan group sources */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
+ srcnode, src)) {
+ igmp_source_forward_reevaluate_one(pim, src);
+ } /* scan group sources */
+ } /* scan igmp groups */
} /* scan interfaces */
}
sg.grp = source->source_group->group_addr;
if (PIM_DEBUG_IGMP_TRACE) {
- zlog_debug(
- "%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", __func__,
- pim_str_sg_dump(&sg),
- source->source_group->group_igmp_sock->fd,
- source->source_group->group_igmp_sock->interface->name,
- IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
+ zlog_debug("%s: (S,G)=%s oif=%s fwd=%d", __func__,
+ pim_str_sg_dump(&sg),
+ source->source_group->interface->name,
+ IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
}
/* Prevent IGMP interface from installing multicast route multiple
}
group = source->source_group;
- pim_oif = group->group_igmp_sock->interface->info;
+ pim_oif = group->interface->info;
if (!pim_oif) {
if (PIM_DEBUG_IGMP_TRACE) {
zlog_debug("%s: multicast not enabled on oif=%s ?",
__func__,
- source->source_group->group_igmp_sock
- ->interface->name);
+ source->source_group->interface->name);
}
return;
}
*/
if (PIM_DEBUG_IGMP_TRACE) {
zlog_debug(
- "%s: ignoring request for looped MFC entry (S,G)=%s: igmp_sock=%d oif=%s vif_index=%d",
+ "%s: ignoring request for looped MFC entry (S,G)=%s: oif=%s vif_index=%d",
__func__,
pim_str_sg_dump(&sg),
source->source_group
- ->group_igmp_sock
- ->fd,
- source->source_group
- ->group_igmp_sock
->interface->name,
input_iface_vif_index);
}
if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) {
result = pim_channel_add_oif(source->source_channel_oil,
- group->group_igmp_sock->interface,
+ group->interface,
PIM_OIF_FLAG_PROTO_IGMP, __func__);
if (result) {
if (PIM_DEBUG_MROUTE) {
zlog_debug(
"%s: %s was received on %s interface but we are not DR for that interface",
__func__, pim_str_sg_dump(&sg),
- group->group_igmp_sock->interface->name);
+ group->interface->name);
return;
}
Feed IGMPv3-gathered local membership information into PIM
per-interface (S,G) state.
*/
- if (!pim_ifchannel_local_membership_add(
- group->group_igmp_sock->interface, &sg,
+ if (!pim_ifchannel_local_membership_add(group->interface, &sg,
false /*is_vxlan*/)) {
if (PIM_DEBUG_MROUTE)
zlog_warn("%s: Failure to add local membership for %s",
__func__, pim_str_sg_dump(&sg));
pim_channel_del_oif(source->source_channel_oil,
- group->group_igmp_sock->interface,
- PIM_OIF_FLAG_PROTO_IGMP, __func__);
+ group->interface, PIM_OIF_FLAG_PROTO_IGMP,
+ __func__);
return;
}
sg.grp = source->source_group->group_addr;
if (PIM_DEBUG_IGMP_TRACE) {
- zlog_debug(
- "%s: (S,G)=%s igmp_sock=%d oif=%s fwd=%d", __func__,
- pim_str_sg_dump(&sg),
- source->source_group->group_igmp_sock->fd,
- source->source_group->group_igmp_sock->interface->name,
- IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
+ zlog_debug("%s: (S,G)=%s oif=%s fwd=%d", __func__,
+ pim_str_sg_dump(&sg),
+ source->source_group->interface->name,
+ IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
}
/* Prevent IGMP interface from removing multicast route multiple
pim_forward_stop below.
*/
result = pim_channel_del_oif(source->source_channel_oil,
- group->group_igmp_sock->interface,
- PIM_OIF_FLAG_PROTO_IGMP,
- __func__);
+ group->interface, PIM_OIF_FLAG_PROTO_IGMP,
+ __func__);
if (result) {
if (PIM_DEBUG_IGMP_TRACE)
zlog_debug(
Feed IGMPv3-gathered local membership information into PIM
per-interface (S,G) state.
*/
- pim_ifchannel_local_membership_del(group->group_igmp_sock->interface,
- &sg);
+ pim_ifchannel_local_membership_del(group->interface, &sg);
IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
}
#include "pimd.h"
#include "pim_iface.h"
+#include "pim_neighbor.h"
#include "pim_pim.h"
#include "pim_str.h"
#include "pim_oil.h"
extern const char *const PIM_ALL_PIM_ROUTERS;
extern const char *const PIM_ALL_IGMP_ROUTERS;
-extern struct pim_router *router;
extern struct zebra_privs_t pimd_privs;
extern struct in_addr qpim_all_pim_routers_addr;
extern uint8_t qpim_ecmp_enable;
macros.load(os.path.join(basepath, "bgpd/bgp_vty.h"))
# sigh :(
macros["PROTO_REDIST_STR"] = "FRR_REDIST_STR_ISISD"
+ macros["PROTO_IP_REDIST_STR"] = "FRR_IP_REDIST_STR_ISISD"
+ macros["PROTO_IP6_REDIST_STR"] = "FRR_IP6_REDIST_STR_ISISD"
errors = process_file(args.cfile, ofd, dumpfd, args.all_defun, macros)
if errors != 0:
ifp = THREAD_ARG(t);
ri = ifp->info;
- ri->t_wakeup = NULL;
/* Join to multicast group. */
if (rip_multicast_join(ifp, ri->rip->sock) < 0) {
}
/* Update timeout thread. */
- peer->t_timeout = NULL;
thread_add_timer(master, rip_peer_timeout, peer, RIP_PEER_TIMER_DEFAULT,
&peer->t_timeout);
struct route_node *rp;
rinfo = THREAD_ARG(t);
- rinfo->t_garbage_collect = NULL;
/* Off timeout timer. */
RIP_TIMER_OFF(rinfo->t_timeout);
/* Fetch socket then register myself. */
sock = THREAD_FD(t);
- rip->t_read = NULL;
/* Add myself to tne next event */
rip_event(rip, RIP_READ, sock);
{
struct rip *rip = THREAD_ARG(t);
- /* Clear timer pointer. */
- rip->t_update = NULL;
-
if (IS_RIP_DEBUG_EVENT)
zlog_debug("update timer fire!");
{
struct rip *rip = THREAD_ARG(t);
- rip->t_triggered_interval = NULL;
-
if (rip->trigger) {
rip->trigger = 0;
rip_triggered_update(t);
struct rip *rip = THREAD_ARG(t);
int interval;
- /* Clear thred pointer. */
- rip->t_triggered_update = NULL;
-
/* Cancel interval timer. */
RIP_TIMER_OFF(rip->t_triggered_interval);
rip->trigger = 0;
update is triggered when the timer expires. */
interval = (frr_weak_random() % 5) + 1;
- rip->t_triggered_interval = NULL;
thread_add_timer(master, rip_triggered_interval, rip, interval,
&rip->t_triggered_interval);
switch (event) {
case RIP_READ:
- rip->t_read = NULL;
thread_add_read(master, rip_read, rip, sock, &rip->t_read);
break;
case RIP_UPDATE_EVENT:
ifp = THREAD_ARG(t);
ri = ifp->info;
- ri->t_wakeup = NULL;
/* Join to multicast group. */
if (ripng_multicast_join(ifp, ri->ripng->sock) < 0) {
struct agg_node *rp;
rinfo = THREAD_ARG(t);
- rinfo->t_garbage_collect = NULL;
/* Off timeout timer. */
RIPNG_TIMER_OFF(rinfo->t_timeout);
/* Fetch thread data and set read pointer to empty for event
managing. `sock' sould be same as ripng->sock. */
sock = THREAD_FD(thread);
- ripng->t_read = NULL;
/* Add myself to the next event. */
ripng_event(ripng, RIPNG_READ, sock);
struct interface *ifp;
struct ripng_interface *ri;
- /* Clear update timer thread. */
- ripng->t_update = NULL;
-
/* Logging update event. */
if (IS_RIPNG_DEBUG_EVENT)
zlog_debug("RIPng update timer expired!");
{
struct ripng *ripng = THREAD_ARG(t);
- ripng->t_triggered_interval = NULL;
-
if (ripng->trigger) {
ripng->trigger = 0;
ripng_triggered_update(t);
struct ripng_interface *ri;
int interval;
- ripng->t_triggered_update = NULL;
-
/* Cancel interval timer. */
thread_cancel(&ripng->t_triggered_interval);
ripng->trigger = 0;
update is triggered when the timer expires. */
interval = (frr_weak_random() % 5) + 1;
- ripng->t_triggered_interval = NULL;
thread_add_timer(master, ripng_triggered_interval, ripng, interval,
&ripng->t_triggered_interval);
/* Update timer jitter. */
jitter = ripng_update_jitter(ripng->update_time);
- ripng->t_update = NULL;
thread_add_timer(master, ripng_update, ripng,
sock ? 2 : ripng->update_time + jitter,
&ripng->t_update);
/* RIPng threads. */
struct thread *t_read;
- struct thread *t_write;
struct thread *t_update;
- struct thread *t_garbage;
- struct thread *t_zebra;
/* Triggered update hack. */
int trigger;
#include "northbound.h"
#include "libfrr.h"
#include "static_nb.h"
-
+#include "static_vty.h"
/* clang-format off */
const struct frr_yang_module_info frr_staticd_info = {
.name = "frr-staticd",
.nodes = {
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd",
+ .cbs = {
+ .cli_show = static_cli_show,
+ .cli_show_end = static_cli_show_end,
+ }
+ },
{
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list",
.cbs = {
.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_destroy,
+ .cli_cmp = static_route_list_cli_cmp,
}
},
{
.cbs = {
.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_destroy,
+ .cli_cmp = static_path_list_cli_cmp,
}
},
{
.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_destroy,
.pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate,
+ .cli_show = static_nexthop_cli_show,
+ .cli_cmp = static_nexthop_cli_cmp,
}
},
{
.cbs = {
.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy,
+ .cli_cmp = static_src_list_cli_cmp,
}
},
{
.cbs = {
.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy,
+ .cli_cmp = static_path_list_cli_cmp,
}
},
{
.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create,
.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy,
.pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate,
+ .cli_show = static_src_nexthop_cli_show,
+ .cli_cmp = static_nexthop_cli_cmp,
}
},
{
static int nexthop_iter_cb(const struct lyd_node *dnode, void *arg)
{
struct nexthop_iter *iter = arg;
- int nh_type;
+ enum static_nh_type nh_type;
nh_type = yang_dnode_get_enum(dnode, "./nh-type");
struct static_path *pn;
struct ipaddr ipaddr;
struct static_nexthop *nh;
- int nh_type;
+ enum static_nh_type nh_type;
const char *ifname;
const char *nh_vrf;
static int static_nexthop_onlink_modify(struct nb_cb_modify_args *args)
{
struct static_nexthop *nh;
- static_types nh_type;
+ enum static_nh_type nh_type;
switch (args->event) {
case NB_EV_VALIDATE:
static int static_nexthop_bh_type_modify(struct nb_cb_modify_args *args)
{
struct static_nexthop *nh;
- static_types nh_type;
+ enum static_nh_type nh_type;
switch (args->event) {
case NB_EV_VALIDATE:
route_unlock_node(rn);
}
-bool static_add_nexthop_validate(const char *nh_vrf_name, static_types type,
+bool static_add_nexthop_validate(const char *nh_vrf_name,
+ enum static_nh_type type,
struct ipaddr *ipaddr)
{
struct vrf *vrf;
}
struct static_nexthop *static_add_nexthop(struct static_path *pn,
- static_types type,
+ enum static_nh_type type,
struct ipaddr *ipaddr,
const char *ifname,
const char *nh_vrf, uint32_t color)
static_ifindex_update_af(ifp, up, AFI_IP6, SAFI_MULTICAST);
}
-void static_get_nh_type(static_types stype, char *type, size_t size)
+void static_get_nh_type(enum static_nh_type stype, char *type, size_t size)
{
switch (stype) {
case STATIC_IFNAME:
* The order for below macros should be in sync with
* yang model typedef nexthop-type
*/
-typedef enum {
+enum static_nh_type {
STATIC_IFNAME = 1,
STATIC_IPV4_GATEWAY,
STATIC_IPV4_GATEWAY_IFNAME,
STATIC_IPV6_GATEWAY,
STATIC_IPV6_GATEWAY_IFNAME,
STATIC_BLACKHOLE,
-} static_types;
+};
/*
* Route Creation gives us:
enum static_install_states state;
/* Flag for this static route's type. */
- static_types type;
+ enum static_nh_type type;
/*
* Nexthop value.
void static_fixup_vrf_ids(struct static_vrf *svrf);
extern struct static_nexthop *
-static_add_nexthop(struct static_path *pn, static_types type,
+static_add_nexthop(struct static_path *pn, enum static_nh_type type,
struct ipaddr *ipaddr, const char *ifname,
const char *nh_vrf, uint32_t color);
extern void static_install_nexthop(struct static_nexthop *nh);
uint32_t table_id, uint8_t distance);
extern void static_del_path(struct static_path *pn);
-extern void static_get_nh_type(static_types stype, char *type, size_t size);
+extern void static_get_nh_type(enum static_nh_type stype, char *type,
+ size_t size);
extern bool static_add_nexthop_validate(const char *nh_vrf_name,
- static_types type,
+ enum static_nh_type type,
struct ipaddr *ipaddr);
extern struct stable_info *static_get_stable_info(struct route_node *rn);
#include "nexthop.h"
#include "table.h"
#include "srcdest_table.h"
+#include "northbound_cli.h"
#include "static_vrf.h"
#include "static_routes.h"
#include "static_zebra.h"
-#include "static_vty.h"
DEFINE_MTYPE_STATIC(STATIC, STATIC_RTABLE_INFO, "Static Route Table Info");
static int static_vrf_config_write(struct vty *vty)
{
- struct vrf *vrf;
-
- RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
- if (vrf->vrf_id != VRF_DEFAULT)
- vty_frame(vty, "vrf %s\n", vrf->name);
+ struct lyd_node *dnode;
+ int written = 0;
- static_config(vty, vrf->info, AFI_IP,
- SAFI_UNICAST, "ip route");
- static_config(vty, vrf->info, AFI_IP,
- SAFI_MULTICAST, "ip mroute");
- static_config(vty, vrf->info, AFI_IP6,
- SAFI_UNICAST, "ipv6 route");
-
- if (vrf->vrf_id != VRF_DEFAULT)
- vty_endframe(vty, "exit-vrf\n!\n");
+ dnode = yang_dnode_get(running_config->dnode, "/frr-routing:routing");
+ if (dnode) {
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+ written = 1;
}
- return 0;
+ return written;
}
void static_vrf_init(void)
int ret;
struct prefix p, src;
struct in_addr mask;
- uint8_t type;
+ enum static_nh_type type;
const char *bh_type;
char xpath_prefix[XPATH_MAXLEN];
char xpath_nexthop[XPATH_MAXLEN];
table_str, false, NULL);
}
-/* Write static route configuration. */
-int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi,
- safi_t safi, const char *cmd)
-{
- char spacing[100];
- struct route_node *rn;
- struct static_nexthop *nh;
- struct static_path *pn;
- struct route_table *stable;
- struct static_route_info *si;
- char buf[SRCDEST2STR_BUFFER];
- int write = 0;
- struct stable_info *info;
-
- stable = svrf->stable[afi][safi];
- if (stable == NULL)
- return write;
-
- snprintf(spacing, sizeof(spacing), "%s%s",
- (svrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ", cmd);
-
- for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) {
- si = static_route_info_from_rnode(rn);
- if (!si)
- continue;
- info = static_get_stable_info(rn);
- frr_each(static_path_list, &si->path_list, pn) {
- frr_each(static_nexthop_list, &pn->nexthop_list, nh) {
- vty_out(vty, "%s %s", spacing,
- srcdest_rnode2str(rn, buf,
- sizeof(buf)));
-
- switch (nh->type) {
- case STATIC_IPV4_GATEWAY:
- vty_out(vty, " %pI4", &nh->addr.ipv4);
- break;
- case STATIC_IPV6_GATEWAY:
- vty_out(vty, " %s",
- inet_ntop(AF_INET6,
- &nh->addr.ipv6, buf,
- sizeof(buf)));
- break;
- case STATIC_IFNAME:
- vty_out(vty, " %s", nh->ifname);
- break;
- case STATIC_BLACKHOLE:
- switch (nh->bh_type) {
- case STATIC_BLACKHOLE_DROP:
- vty_out(vty, " blackhole");
- break;
- case STATIC_BLACKHOLE_NULL:
- vty_out(vty, " Null0");
- break;
- case STATIC_BLACKHOLE_REJECT:
- vty_out(vty, " reject");
- break;
- }
- break;
- case STATIC_IPV4_GATEWAY_IFNAME:
- vty_out(vty, " %s %s",
- inet_ntop(AF_INET,
- &nh->addr.ipv4, buf,
- sizeof(buf)),
- nh->ifname);
- break;
- case STATIC_IPV6_GATEWAY_IFNAME:
- vty_out(vty, " %s %s",
- inet_ntop(AF_INET6,
- &nh->addr.ipv6, buf,
- sizeof(buf)),
- nh->ifname);
- break;
- }
-
- if (pn->tag)
- vty_out(vty, " tag %" ROUTE_TAG_PRI,
- pn->tag);
-
- if (pn->distance
- != ZEBRA_STATIC_DISTANCE_DEFAULT)
- vty_out(vty, " %u", pn->distance);
-
- /* Label information */
- if (nh->snh_label.num_labels)
- vty_out(vty, " label %s",
- mpls_label2str(
- nh->snh_label
- .num_labels,
- nh->snh_label.label,
- buf, sizeof(buf), 0));
-
- if (!strmatch(nh->nh_vrfname,
- info->svrf->vrf->name))
- vty_out(vty, " nexthop-vrf %s",
- nh->nh_vrfname);
-
- /*
- * table ID from VRF overrides
- * configured
- */
- if (pn->table_id
- && svrf->vrf->data.l.table_id
- == RT_TABLE_MAIN)
- vty_out(vty, " table %u", pn->table_id);
-
- if (nh->onlink)
- vty_out(vty, " onlink");
-
- /*
- * SR-TE color
- */
- if (nh->color != 0)
- vty_out(vty, " color %u", nh->color);
-
- vty_out(vty, "\n");
-
- write = 1;
- }
- }
- }
- return write;
-}
-
/* Static unicast routes for multicast RPF lookup. */
DEFPY_YANG (ip_mroute_dist,
ip_mroute_dist_cmd,
ifname, flag, tag_str, distance_str, label,
table_str, false, color_str);
}
+
+void static_cli_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *vrf;
+
+ vrf = yang_dnode_get_string(dnode, "../vrf");
+ if (strcmp(vrf, VRF_DEFAULT_NAME))
+ vty_out(vty, "vrf %s\n", vrf);
+}
+
+void static_cli_show_end(struct vty *vty, struct lyd_node *dnode)
+{
+ const char *vrf;
+
+ vrf = yang_dnode_get_string(dnode, "../vrf");
+ if (strcmp(vrf, VRF_DEFAULT_NAME))
+ vty_out(vty, "exit-vrf\n");
+}
+
+struct mpls_label_iter {
+ struct vty *vty;
+ bool first;
+};
+
+static int mpls_label_iter_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct mpls_label_iter *iter = arg;
+
+ if (yang_dnode_exists(dnode, "./label")) {
+ if (iter->first)
+ vty_out(iter->vty, " label %s",
+ yang_dnode_get_string(dnode, "./label"));
+ else
+ vty_out(iter->vty, "/%s",
+ yang_dnode_get_string(dnode, "./label"));
+ iter->first = false;
+ }
+
+ return YANG_ITER_CONTINUE;
+}
+
+static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route,
+ const struct lyd_node *src,
+ const struct lyd_node *path,
+ const struct lyd_node *nexthop, bool show_defaults)
+{
+ const char *vrf;
+ const char *afi_safi;
+ afi_t afi;
+ safi_t safi;
+ enum static_nh_type nh_type;
+ enum static_blackhole_type bh_type;
+ uint32_t tag;
+ uint8_t distance;
+ struct mpls_label_iter iter;
+ const char *nexthop_vrf;
+ uint32_t table_id;
+ bool onlink;
+
+ vrf = yang_dnode_get_string(route, "../../vrf");
+
+ afi_safi = yang_dnode_get_string(route, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi, &afi, &safi);
+
+ if (afi == AFI_IP)
+ vty_out(vty, "%sip",
+ strmatch(vrf, VRF_DEFAULT_NAME) ? "" : " ");
+ else
+ vty_out(vty, "%sipv6",
+ strmatch(vrf, VRF_DEFAULT_NAME) ? "" : " ");
+
+ if (safi == SAFI_UNICAST)
+ vty_out(vty, " route");
+ else
+ vty_out(vty, " mroute");
+
+ vty_out(vty, " %s", yang_dnode_get_string(route, "./prefix"));
+
+ if (src)
+ vty_out(vty, " from %s",
+ yang_dnode_get_string(src, "./src-prefix"));
+
+ nh_type = yang_dnode_get_enum(nexthop, "./nh-type");
+ switch (nh_type) {
+ case STATIC_IFNAME:
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./interface"));
+ break;
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./gateway"));
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./gateway"));
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./interface"));
+ break;
+ case STATIC_BLACKHOLE:
+ bh_type = yang_dnode_get_enum(nexthop, "./bh-type");
+ switch (bh_type) {
+ case STATIC_BLACKHOLE_DROP:
+ vty_out(vty, " blackhole");
+ break;
+ case STATIC_BLACKHOLE_NULL:
+ vty_out(vty, " Null0");
+ break;
+ case STATIC_BLACKHOLE_REJECT:
+ vty_out(vty, " reject");
+ break;
+ }
+ break;
+ }
+
+ if (yang_dnode_exists(path, "./tag")) {
+ tag = yang_dnode_get_uint32(path, "./tag");
+ if (tag != 0 || show_defaults)
+ vty_out(vty, " tag %" PRIu32, tag);
+ }
+
+ distance = yang_dnode_get_uint8(path, "./distance");
+ if (distance != ZEBRA_STATIC_DISTANCE_DEFAULT || show_defaults)
+ vty_out(vty, " %" PRIu8, distance);
+
+ iter.vty = vty;
+ iter.first = true;
+ yang_dnode_iterate(mpls_label_iter_cb, &iter, nexthop,
+ "./mpls-label-stack/entry");
+
+ nexthop_vrf = yang_dnode_get_string(nexthop, "./vrf");
+ if (strcmp(vrf, nexthop_vrf))
+ vty_out(vty, " nexthop-vrf %s", nexthop_vrf);
+
+ table_id = yang_dnode_get_uint32(path, "./table-id");
+ if (table_id || show_defaults)
+ vty_out(vty, " table %" PRIu32, table_id);
+
+ if (yang_dnode_exists(nexthop, "./onlink")) {
+ onlink = yang_dnode_get_bool(nexthop, "./onlink");
+ if (onlink)
+ vty_out(vty, " onlink");
+ }
+
+ if (yang_dnode_exists(nexthop, "./srte-color"))
+ vty_out(vty, " color %s",
+ yang_dnode_get_string(nexthop, "./srte-color"));
+
+ vty_out(vty, "\n");
+}
+
+void static_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list");
+ const struct lyd_node *route =
+ yang_dnode_get_parent(path, "route-list");
+
+ nexthop_cli_show(vty, route, NULL, path, dnode, show_defaults);
+}
+
+void static_src_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list");
+ const struct lyd_node *src = yang_dnode_get_parent(path, "src-list");
+ const struct lyd_node *route = yang_dnode_get_parent(src, "route-list");
+
+ nexthop_cli_show(vty, route, src, path, dnode, show_defaults);
+}
+
+int static_nexthop_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2)
+{
+ enum static_nh_type nh_type1, nh_type2;
+ struct prefix prefix1, prefix2;
+ int ret = 0;
+
+ nh_type1 = yang_dnode_get_enum(dnode1, "./nh-type");
+ nh_type2 = yang_dnode_get_enum(dnode2, "./nh-type");
+
+ if (nh_type1 != nh_type2)
+ return (int)nh_type1 - (int)nh_type2;
+
+ switch (nh_type1) {
+ case STATIC_IFNAME:
+ ret = if_cmp_name_func(
+ yang_dnode_get_string(dnode1, "./interface"),
+ yang_dnode_get_string(dnode2, "./interface"));
+ break;
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ yang_dnode_get_prefix(&prefix1, dnode1, "./gateway");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./gateway");
+ ret = prefix_cmp(&prefix1, &prefix2);
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ yang_dnode_get_prefix(&prefix1, dnode1, "./gateway");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./gateway");
+ ret = prefix_cmp(&prefix1, &prefix2);
+ if (!ret)
+ ret = if_cmp_name_func(
+ yang_dnode_get_string(dnode1, "./interface"),
+ yang_dnode_get_string(dnode2, "./interface"));
+ break;
+ case STATIC_BLACKHOLE:
+ /* There's only one blackhole nexthop per route */
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ return if_cmp_name_func(yang_dnode_get_string(dnode1, "./vrf"),
+ yang_dnode_get_string(dnode2, "./vrf"));
+}
+
+int static_route_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2)
+{
+ const char *afi_safi1, *afi_safi2;
+ afi_t afi1, afi2;
+ safi_t safi1, safi2;
+ struct prefix prefix1, prefix2;
+
+ afi_safi1 = yang_dnode_get_string(dnode1, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi1, &afi1, &safi1);
+
+ afi_safi2 = yang_dnode_get_string(dnode2, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi2, &afi2, &safi2);
+
+ if (afi1 != afi2)
+ return (int)afi1 - (int)afi2;
+
+ if (safi1 != safi2)
+ return (int)safi1 - (int)safi2;
+
+ yang_dnode_get_prefix(&prefix1, dnode1, "./prefix");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./prefix");
+
+ return prefix_cmp(&prefix1, &prefix2);
+}
+
+int static_src_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2)
+{
+ struct prefix prefix1, prefix2;
+
+ yang_dnode_get_prefix(&prefix1, dnode1, "./src-prefix");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./src-prefix");
+
+ return prefix_cmp(&prefix1, &prefix2);
+}
+
+int static_path_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2)
+{
+ uint32_t table_id1, table_id2;
+ uint8_t distance1, distance2;
+
+ table_id1 = yang_dnode_get_uint32(dnode1, "./table-id");
+ table_id2 = yang_dnode_get_uint32(dnode2, "./table-id");
+
+ if (table_id1 != table_id2)
+ return (int)table_id1 - (int)table_id2;
+
+ distance1 = yang_dnode_get_uint8(dnode1, "./distance");
+ distance2 = yang_dnode_get_uint8(dnode2, "./distance");
+
+ return (int)distance1 - (int)distance2;
+}
+
DEFPY_YANG(debug_staticd, debug_staticd_cmd,
"[no] debug static [{events$events|route$route}]",
NO_STR DEBUG_STR STATICD_STR
extern "C" {
#endif
-int static_config(struct vty *vty, struct static_vrf *svrf,
- afi_t afi, safi_t safi, const char *cmd);
+void static_cli_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void static_cli_show_end(struct vty *vty, struct lyd_node *dnode);
+void static_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void static_src_nexthop_cli_show(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+int static_nexthop_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2);
+int static_route_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2);
+int static_src_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2);
+int static_path_list_cli_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2);
void static_vty_init(void);
str2prefix("42.1.1.0/24", &test_rn.p);
rt_node = bgp_dest_to_rnode(&test_rn);
memcpy((struct route_table *)&rt_node->table, &rt->route_table,
- sizeof(struct route_table *));
+ sizeof(struct route_table));
setup_bgp_mp_list(t);
for (i = 0; i < test_mp_list_info_count; i++)
bgp_path_info_add(&test_rn, &test_mp_list_info[i]);
DUMMY_DEFUN(cmd13, "alt a X:X::X:X");
DUMMY_DEFUN(cmd14,
"pat g { foo A.B.C.D$foo|foo|bar X:X::X:X$bar| baz } [final]");
+DUMMY_DEFUN(cmd15, "no pat g ![ WORD ]");
+DUMMY_DEFUN(cmd16, "[no] pat h {foo ![A.B.C.D$foo]|bar X:X::X:X$bar} final");
#include "tests/lib/cli/test_cli_clippy.c"
install_element(ENABLE_NODE, &cmd13_cmd);
}
install_element(ENABLE_NODE, &cmd14_cmd);
+ install_element(ENABLE_NODE, &cmd15_cmd);
+ install_element(ENABLE_NODE, &cmd16_cmd);
install_element(ENABLE_NODE, &magic_test_cmd);
}
pat f foo
pat f key
+no pat g
+no pat g test
+no pat g test more
+
+pat h foo ?1.2.3.4 final
+no pat h foo ?1.2.3.4 final
+pat h foo final
+no pat h foo final
+pat h bar final
+no pat h bar final
+pat h bar 1::2 final
+no pat h bar 1::2 final
+pat h bar 1::2 foo final
+no pat h bar 1::2 foo final
+pat h bar 1::2 foo 1.2.3.4 final
+no pat h bar 1::2 foo 1.2.3.4 final
+
alt a a?b
alt a 1 .2?.3.4
alt a 1 :2? ::?3
% Command incomplete.\r
test# pat \r
a b c d e f \r
-g \r
+g h \r
test# pat \r
% Command incomplete.\r
test# \r
[01] f@(null): f\r
[02] key@(null): key\r
test# \r
+test# no pat g\r
+cmd15 with 3 args.\r
+[00] no@(null): no\r
+[01] pat@(null): pat\r
+[02] g@(null): g\r
+test# no pat g test\r
+cmd15 with 4 args.\r
+[00] no@(null): no\r
+[01] pat@(null): pat\r
+[02] g@(null): g\r
+[03] WORD@g: test\r
+test# no pat g test more\r
+% [NONE] Unknown command: no pat g test more\r
+test# \r
+test# pat h foo \r
+ A.B.C.D 04\r
+test# pat h foo 1.2.3.4 final\r
+cmd16 with 5 args.\r
+[00] pat@(null): pat\r
+[01] h@(null): h\r
+[02] foo@(null): foo\r
+[03] A.B.C.D@foo: 1.2.3.4\r
+[04] final@(null): final\r
+test# no pat h foo \r
+ A.B.C.D 04\r
+ bar 05\r
+ final 07\r
+test# no pat h foo 1.2.3.4 final\r
+cmd16 with 6 args.\r
+[00] no@no: no\r
+[01] pat@(null): pat\r
+[02] h@(null): h\r
+[03] foo@(null): foo\r
+[04] A.B.C.D@foo: 1.2.3.4\r
+[05] final@(null): final\r
+test# pat h foo final\r
+% [NONE] Unknown command: pat h foo final\r
+test# no pat h foo final\r
+cmd16 with 5 args.\r
+[00] no@no: no\r
+[01] pat@(null): pat\r
+[02] h@(null): h\r
+[03] foo@(null): foo\r
+[04] final@(null): final\r
+test# pat h bar final\r
+% [NONE] Unknown command: pat h bar final\r
+test# no pat h bar final\r
+% [NONE] Unknown command: no pat h bar final\r
+test# pat h bar 1::2 final\r
+cmd16 with 5 args.\r
+[00] pat@(null): pat\r
+[01] h@(null): h\r
+[02] bar@(null): bar\r
+[03] X:X::X:X@bar: 1::2\r
+[04] final@(null): final\r
+test# no pat h bar 1::2 final\r
+cmd16 with 6 args.\r
+[00] no@no: no\r
+[01] pat@(null): pat\r
+[02] h@(null): h\r
+[03] bar@(null): bar\r
+[04] X:X::X:X@bar: 1::2\r
+[05] final@(null): final\r
+test# pat h bar 1::2 foo final\r
+% [NONE] Unknown command: pat h bar 1::2 foo final\r
+test# no pat h bar 1::2 foo final\r
+cmd16 with 7 args.\r
+[00] no@no: no\r
+[01] pat@(null): pat\r
+[02] h@(null): h\r
+[03] bar@(null): bar\r
+[04] X:X::X:X@bar: 1::2\r
+[05] foo@(null): foo\r
+[06] final@(null): final\r
+test# pat h bar 1::2 foo 1.2.3.4 final\r
+cmd16 with 7 args.\r
+[00] pat@(null): pat\r
+[01] h@(null): h\r
+[02] bar@(null): bar\r
+[03] X:X::X:X@bar: 1::2\r
+[04] foo@(null): foo\r
+[05] A.B.C.D@foo: 1.2.3.4\r
+[06] final@(null): final\r
+test# no pat h bar 1::2 foo 1.2.3.4 final\r
+cmd16 with 8 args.\r
+[00] no@no: no\r
+[01] pat@(null): pat\r
+[02] h@(null): h\r
+[03] bar@(null): bar\r
+[04] X:X::X:X@bar: 1::2\r
+[05] foo@(null): foo\r
+[06] A.B.C.D@foo: 1.2.3.4\r
+[07] final@(null): final\r
+test# \r
test# alt a \r
test# alt a a\r
WORD 02\r
static int grpc_thread_stop(struct thread *thread);
+static void _err_print(const void *cookie, const char *errstr)
+{
+ std::cout << "Failed to load grpc module:" << errstr << std::endl;
+}
+
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);
/* 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));
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(), 0, 0);
if (!grpc_module) {
modpath = std::string(binpath) + std::string("../../lib");
- grpc_module = frrmod_load("grpc:50051", modpath.c_str(), moderr,
- sizeof(moderr));
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(),
+ _err_print, 0);
}
- if (!grpc_module) {
- std::cout << "Failed to load grpc module:" << moderr
- << std::endl;
+ if (!grpc_module)
exit(1);
- }
static_debug_init();
nexthop_free(nh2);
/* Blackhole */
- nh1 = nexthop_from_blackhole(BLACKHOLE_REJECT);
- nh2 = nexthop_from_blackhole(BLACKHOLE_REJECT);
+ nh1 = nexthop_from_blackhole(BLACKHOLE_REJECT, 0);
+ nh2 = nexthop_from_blackhole(BLACKHOLE_REJECT, 0);
ret = nexthop_cmp_basic(nh1, nh2);
assert(ret == 0);
nexthop_free(nh2);
- nh2 = nexthop_from_blackhole(BLACKHOLE_NULL);
+ nh2 = nexthop_from_blackhole(BLACKHOLE_NULL, 0);
ret = nexthop_cmp_basic(nh1, nh2);
assert(ret != 0);
#include "lib/printfrr.h"
#include "lib/memory.h"
#include "lib/prefix.h"
+#include "lib/nexthop.h"
static int errors;
printchk("\"\"", "%pSQqn", (char *)NULL);
printchk("(null)", "%pSQq", (char *)NULL);
+ /*
+ * %pNH<foo> tests
+ *
+ * gateway addresses only for now: interfaces require more setup
+ */
+ printchk("(null)", "%pNHcg", NULL);
+ printchk("(null)", "%pNHci", NULL);
+
+ struct nexthop nh;
+
+ memset(&nh, 0, sizeof(nh));
+
+ nh.type = NEXTHOP_TYPE_IPV4;
+ inet_aton("3.2.1.0", &nh.gate.ipv4);
+ printchk("3.2.1.0", "%pNHcg", &nh);
+
+ nh.type = NEXTHOP_TYPE_IPV6;
+ inet_pton(AF_INET6, "fe2c::34", &nh.gate.ipv6);
+ printchk("fe2c::34", "%pNHcg", &nh);
+
return !!errors;
}
--- /dev/null
+/*
+ * 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 <zebra.h>
+#include <skiplist.h>
+
+static void sl_debug(struct skiplist *l)
+{
+ int i;
+
+ if (!l)
+ return;
+
+ printf("Skiplist %p has max level %d\n", l, l->level);
+ for (i = l->level; i >= 0; --i)
+ printf(" @%d: %d\n", i, l->level_stats[i]);
+}
+
+static void *scramble(int i)
+{
+ uintptr_t result;
+
+ result = (uintptr_t)(i & 0xff) << 24;
+ result |= (uintptr_t)i >> 8;
+
+ return (void *)result;
+}
+#define sampleSize 65536
+static int sl_test(void)
+{
+ struct skiplist *l;
+ register int i, k;
+ void *keys[sampleSize];
+ void *v = NULL;
+ int errors = 0;
+
+ l = skiplist_new(SKIPLIST_FLAG_ALLOW_DUPLICATES, NULL, NULL);
+
+ printf("%s: skiplist_new returned %p\n", __func__, l);
+
+ for (i = 0; i < 4; i++) {
+
+ for (k = 0; k < sampleSize; k++) {
+ if (!(k % 10000))
+ printf("%s: (%d:%d)\n", __func__, i, k);
+ /* keys[k] = (void *)random(); */
+ keys[k] = scramble(k);
+ if (skiplist_insert(l, keys[k], keys[k])) {
+ ++errors;
+ printf("error in insert #%d,#%d\n", i, k);
+ }
+ }
+
+ printf("%s: inserts done\n", __func__);
+ sl_debug(l);
+
+ for (k = 0; k < sampleSize; k++) {
+
+ if (!(k % 10000))
+ printf("[%d:%d]\n", i, k);
+ /* keys[k] = (void *)random(); */
+ if (skiplist_search(l, keys[k], &v)) {
+ ++errors;
+ printf("error in search #%d,#%d\n", i, k);
+ }
+
+ if (v != keys[k]) {
+ ++errors;
+ printf("search returned wrong value\n");
+ }
+ }
+ printf("%s: searches done\n", __func__);
+
+
+ for (k = 0; k < sampleSize; k++) {
+
+ if (!(k % 10000))
+ printf("<%d:%d>\n", i, k);
+ /* keys[k] = (void *)random(); */
+ if (skiplist_delete(l, keys[k], keys[k])) {
+ ++errors;
+ printf("error in delete\n");
+ }
+ keys[k] = scramble(k ^ 0xf0f0f0f0);
+ if (skiplist_insert(l, keys[k], keys[k])) {
+ ++errors;
+ printf("error in insert #%d,#%d\n", i, k);
+ }
+ }
+
+ printf("%s: del+inserts done\n", __func__);
+ sl_debug(l);
+
+ for (k = 0; k < sampleSize; k++) {
+
+ if (!(k % 10000))
+ printf("{%d:%d}\n", i, k);
+ /* keys[k] = (void *)random(); */
+ if (skiplist_delete_first(l)) {
+ ++errors;
+ printf("error in delete_first\n");
+ }
+ }
+ }
+
+ sl_debug(l);
+
+ skiplist_free(l);
+
+ return errors;
+}
+
+int main(int argc, char **argv)
+{
+ int errors = sl_test();
+
+ if (errors)
+ return 1;
+ return 0;
+}
/* Schedule timers to expire in 0..5 seconds */
interval_msec = prng_rand(prng) % 5000;
arg = XMALLOC(MTYPE_TMP, TIMESTR_LEN + 1);
- timers[i] = NULL;
thread_add_timer_msec(master, timer_func, arg, interval_msec,
&timers[i]);
ret = snprintf(arg, TIMESTR_LEN + 1, "%lld.%06lld",
/* create thread structures so they won't be allocated during the
* time measurement */
for (i = 0; i < SCHEDULE_TIMERS; i++) {
- timers[i] = NULL;
thread_add_timer_msec(master, dummy_func, NULL, 0, &timers[i]);
}
for (i = 0; i < SCHEDULE_TIMERS; i++)
long interval_msec;
interval_msec = prng_rand(prng) % (100 * SCHEDULE_TIMERS);
- timers[i] = NULL;
thread_add_timer_msec(master, dummy_func, NULL, interval_msec,
&timers[i]);
}
tests/lib/test_segv \
tests/lib/test_seqlock \
tests/lib/test_sig \
+ tests/lib/test_skiplist \
tests/lib/test_stream \
tests/lib/test_table \
tests/lib/test_timer_correctness \
# note no -Werror
ALL_TESTS_LDADD = lib/libfrr.la $(LIBCAP)
-BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm
+BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) $(UST_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
tests_lib_test_sig_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_sig_LDADD = $(ALL_TESTS_LDADD)
tests_lib_test_sig_SOURCES = tests/lib/test_sig.c
+tests_lib_test_skiplist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_skiplist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_skiplist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_skiplist_SOURCES = tests/lib/test_skiplist.c
tests_lib_test_srcdest_table_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_srcdest_table_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_srcdest_table_LDADD = $(ALL_TESTS_LDADD)
# Remove Unknown Summary (all of it)
actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual)
actual = re.sub(r"No Unknown neighbor is configured", "", actual)
+ # Make Connect/Active/Idle the same (change them all to Active)
+ actual = re.sub(r" Connect ", " Active ", actual)
+ actual = re.sub(r" Idle ", " Active ", actual)
actual = re.sub(
r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual
r"(192.168.7.(1|2)0|fc00:0:0:8::2000).+Active.+", "", expected
)
elif "10.0.0.1" in arguments:
- expected = "No such neighbor in VRF default"
+ expected = "No such neighbor in this view/vrf"
if "terse" in arguments:
expected = re.sub(r"BGP table version .+", "", expected)
# Remove Unknown Summary (all of it)
actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual)
actual = re.sub(r"No Unknown neighbor is configured", "", actual)
+ # Make Connect/Active/Idle the same (change them all to Active)
+ actual = re.sub(r" Connect ", " Active ", actual)
+ actual = re.sub(r" Idle ", " Active ", actual)
# Remove Labeled Unicast Summary (all of it)
actual = re.sub(
password 1
!
interface eth-rt2
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
ipv6 ospf6 network broadcast
ipv6 ospf6 bfd
!
interface eth-rt3
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
ipv6 ospf6 network broadcast
ipv6 ospf6 bfd
!
!
interface lo
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
interface eth-rt2
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
ip ospf bfd
!
interface eth-rt3
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
ip ospf bfd
!
router ospf
password 1
!
interface eth-rt1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
ipv6 ospf6 network broadcast
ipv6 ospf6 bfd
!
interface eth-rt5
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
ipv6 ospf6 network broadcast
!
router ospf6
!
interface lo
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
interface eth-rt1
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
ip ospf bfd
!
interface eth-rt5
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
router ospf
ospf router-id 2.2.2.2
password 1
!
interface eth-rt1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
ipv6 ospf6 network broadcast
ipv6 ospf6 bfd
!
interface eth-rt4
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
ipv6 ospf6 network broadcast
!
router ospf6
!
interface lo
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
interface eth-rt1
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
ip ospf bfd
!
interface eth-rt4
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
router ospf
ospf router-id 3.3.3.3
password 1
!
interface eth-rt3
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
ipv6 ospf6 network broadcast
!
interface eth-rt5
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
ipv6 ospf6 network broadcast
!
router ospf6
!
interface lo
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
interface eth-rt3
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
interface eth-rt5
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
router ospf
ospf router-id 4.4.4.4
!
interface eth-rt2
ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
!
interface eth-rt4
ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
!
router ospf6
ospf6 router-id 5.5.5.5
!
interface lo
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
interface eth-rt2
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
interface eth-rt4
ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
!
router ospf
ospf router-id 5.5.5.5
print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
-def router_compare_json_output(rname, command, reference, count=120, wait=0.5):
+def router_compare_json_output(rname, command, reference, count=40, wait=2):
"Compare router JSON output"
logger.info('Comparing router "%s" "%s" output', rname, command)
filename = "{}/{}/{}".format(CWD, rname, reference)
expected = json.loads(open(filename).read())
- # Run test function until we get an result. Wait at most 60 seconds.
+ # Run test function until we get an result. Wait at most 80 seconds.
test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
_, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait)
assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
# By default BFD provides a recovery time of 900ms plus jitter, so let's wait
# initial 2 seconds to let the CI not suffer.
- # TODO: add check for array size
- sleep(2)
+ topotest.sleep(2, 'Wait for BFD down notification')
+
router_compare_json_output(
"rt1", "show ip route ospf json", "step3/show_ip_route_rt2_down.ref", 1, 0
)
# By default BFD provides a recovery time of 900ms plus jitter, so let's wait
# initial 2 seconds to let the CI not suffer.
- # TODO: add check for array size
- sleep(2)
+ topotest.sleep(2, 'Wait for BFD down notification')
router_compare_json_output(
"rt1", "show ip route ospf json", "step3/show_ip_route_rt3_down.ref", 1, 0
)
def _bgp_default_route_has_metric(router):
output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
expected = {
- "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}]
+ "paths": [
+ {
+ "aspath": {"string": "65000 65000 65000 65000"},
+ "metric": 123,
+ "community": None,
+ }
+ ]
}
return topotest.json_cmp(output, expected)
!
-int host1-eth0
+int host2-eth0
ip address 50.0.1.21/24
ipv6 address 50:0:1::21/48
for cmd in cmds:
cc.doCmd(tgen, rtr, cmd.format(rtr))
cc.doCmd(tgen, rtr, "ip link set dev {0}-eth0 master {0}-cust2".format(rtr))
- if cc.getOutput() != 4:
+ if cc.getOutput() != 0:
InitSuccess = False
logger.info(
"Unexpected output seen ({} times, tests will be skipped".format(
)
)
else:
+ rtrs = ["r1", "r3", "r4", "ce4"]
+ for rtr in rtrs:
+ logger.info("{} configured".format(rtr))
+ cc.doCmd(tgen, rtr, "ip -d link show type vrf")
+ cc.doCmd(tgen, rtr, "ip link show")
InitSuccess = True
logger.info("VRF config successful!")
return InitSuccess
next-hop 2001::2;
extended-community [ target:2:10 ];
label 3;
- attribute [0x28 0xc0 0x0500150020010db800010001000000000000000100ffff00 ];
+ attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ];
}
route 2001:2::/64 {
rd 2:10;
next-hop 2001::2;
extended-community [ target:2:10 ];
label 3;
- attribute [0x28 0xc0 0x0500150020010db800010001000000000000000100ffff00 ];
+ attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ];
}
}
}
r1.run("sysctl -w net.mpls.conf.r1-eth0.input=1")
r1.run("sysctl -w net.mpls.conf.r1-eth1.input=1")
r1.run("sysctl -w net.mpls.conf.r1-eth2.input=1")
- r2.run("sysctl -w net.mpls.conf.r1-eth0.input=1")
- r2.run("sysctl -w net.mpls.conf.r1-eth1.input=1")
- r3.run("sysctl -w net.mpls.conf.r1-eth0.input=1")
- r3.run("sysctl -w net.mpls.conf.r1-eth1.input=1")
- r3.run("sysctl -w net.mpls.conf.r1-eth2.input=1")
- r4.run("sysctl -w net.mpls.conf.r1-eth0.input=1")
- r4.run("sysctl -w net.mpls.conf.r1-eth1.input=1")
router_list = tgen.routers()
--- /dev/null
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "1.1.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 1,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "2001:1::/64": [
+ {
+ "pathFrom": "external",
+ "prefix": "2001:1::",
+ "prefixLen": 64,
+ "network": "2001:1::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "pathFrom": "external",
+ "prefix": "2001:3::",
+ "prefixLen": 64,
+ "network": "2001:3::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "2001:5::/64": [
+ {
+ "pathFrom": "external",
+ "prefix": "2001:5::",
+ "prefixLen": 64,
+ "network": "2001:5::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "1.1.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 1,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "2001:1::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:1::",
+ "prefixLen": 64,
+ "network": "2001:1::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:3::",
+ "prefixLen": 64,
+ "network": "2001:3::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "2001:5::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:5::",
+ "prefixLen": 64,
+ "network": "2001:5::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:2:2::100"
+ "segs": "2001:db8:2:2:100::"
}
}
],
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:2:2::200"
+ "segs": "2001:db8:2:2:200::"
}
}
],
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:2:2::200"
+ "segs": "2001:db8:2:2:200::"
}
}
],
ip forwarding
ipv6 forwarding
!
+ipv6 route 2001:db8:2:1::/64 2001::2
ipv6 route 2001:db8:2:2::/64 2001::2
+ipv6 route 2001:db8:2:3::/64 2001::2
!
line vty
!
--- /dev/null
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "2.2.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 2,
+ "routes": {
+ "routeDistinguishers": {
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "2.2.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 2,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "2001:1::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:1::",
+ "prefixLen": 64,
+ "network": "2001:1::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:3::",
+ "prefixLen": 64,
+ "network": "2001:3::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "2001:5::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:5::",
+ "prefixLen": 64,
+ "network": "2001:5::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:1:1::100"
+ "segs": "2001:db8:1:1:100::"
}
}
],
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:1:1::100"
+ "segs": "2001:db8:1:1:100::"
}
}
],
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:1:1::200"
+ "segs": "2001:db8:1:1:200::"
}
}
],
ipv6 forwarding
!
ipv6 route 2001:db8:1:1::/64 2001::1
+ipv6 route 2001:db8:1:2::/64 2001::1
+ipv6 route 2001:db8:1:3::/64 2001::1
!
line vty
!
tgen.gears["r2"].run("ip link set eth3 master vrf20")
tgen.start_router()
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
def teardown_module(mod):
tgen = get_topogen()
assert False, "Could not read file {}".format(filename)
-def test_rib():
+def check_ping(name, dest_addr, expect_connected):
+ def _check(name, dest_addr, match):
+ tgen = get_topogen()
+ output = tgen.gears[name].run("ping6 {} -c 1 -w 1".format(dest_addr))
+ logger.info(output)
+ assert match in output, "ping fail"
+
+ match = "{} packet loss".format("0%" if expect_connected else "100%")
+ logger.info("[+] check {} {} {}".format(name, dest_addr, match))
+ tgen = get_topogen()
+ func = functools.partial(_check, name, dest_addr, match)
+ success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+ assert result is None, "Failed"
+
+
+def check_rib(name, cmd, expected_file):
def _check(name, cmd, expected_file):
logger.info("polling")
tgen = get_topogen()
expected = open_json_file("{}/{}".format(CWD, expected_file))
return topotest.json_cmp(output, expected)
- def check(name, cmd, expected_file):
- logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file))
- tgen = get_topogen()
- func = functools.partial(_check, name, cmd, expected_file)
- success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
- assert result is None, "Failed"
-
- check("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json")
- check("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json")
- check("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json")
- check("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json")
- check("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json")
- check("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json")
- check("ce1", "show ipv6 route json", "ce1/ipv6_rib.json")
- check("ce2", "show ipv6 route json", "ce2/ipv6_rib.json")
- check("ce3", "show ipv6 route json", "ce3/ipv6_rib.json")
- check("ce4", "show ipv6 route json", "ce4/ipv6_rib.json")
- check("ce5", "show ipv6 route json", "ce5/ipv6_rib.json")
- check("ce6", "show ipv6 route json", "ce6/ipv6_rib.json")
+ logger.info("[+] check {} \"{}\" {}".format(name, cmd, expected_file))
+ tgen = get_topogen()
+ func = functools.partial(_check, name, cmd, expected_file)
+ success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+ assert result is None, "Failed"
+
+
+def test_rib():
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json")
+ check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json")
+ check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json")
+ check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json")
+ check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json")
+ check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json")
+ check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json")
+ check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json")
+ check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json")
+ check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json")
+ check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json")
def test_ping():
- def _check(name, dest_addr, match):
- tgen = get_topogen()
- output = tgen.gears[name].run("ping6 {} -c 1 -w 1".format(dest_addr))
- logger.info(output)
- assert match in output, "ping fail"
+ check_ping("ce1", "2001:2::2", True)
+ check_ping("ce1", "2001:3::2", True)
+ check_ping("ce1", "2001:4::2", False)
+ check_ping("ce1", "2001:5::2", False)
+ check_ping("ce1", "2001:6::2", False)
+ check_ping("ce4", "2001:1::2", False)
+ check_ping("ce4", "2001:2::2", False)
+ check_ping("ce4", "2001:3::2", False)
+ check_ping("ce4", "2001:5::2", True)
+ check_ping("ce4", "2001:6::2", True)
- def check(name, dest_addr, match):
- logger.info("[+] check {} {} {}".format(name, dest_addr, match))
- tgen = get_topogen()
- func = functools.partial(_check, name, dest_addr, match)
- success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
- assert result is None, "Failed"
-
- check("ce1", "2001:2::2", " 0% packet loss")
- check("ce1", "2001:3::2", " 0% packet loss")
- check("ce1", "2001:4::2", " 100% packet loss")
- check("ce1", "2001:5::2", " 100% packet loss")
- check("ce1", "2001:6::2", " 100% packet loss")
- check("ce4", "2001:1::2", " 100% packet loss")
- check("ce4", "2001:2::2", " 100% packet loss")
- check("ce4", "2001:3::2", " 100% packet loss")
- check("ce4", "2001:5::2", " 0% packet loss")
- check("ce4", "2001:6::2", " 0% packet loss")
+
+def test_locator_delete():
+ check_ping("ce1", "2001:2::2", True)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ no locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json")
+ check_ping("ce1", "2001:2::2", False)
+
+
+def test_locator_recreate():
+ check_ping("ce1", "2001:2::2", False)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix 2001:db8:1:1::/64
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json")
+ check_ping("ce1", "2001:2::2", True)
+
+
+def test_bgp_locator_unset():
+ check_ping("ce1", "2001:2::2", True)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ segment-routing srv6
+ no locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json")
+ check_ping("ce1", "2001:2::2", False)
+
+
+def test_bgp_locator_reset():
+ check_ping("ce1", "2001:2::2", False)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ segment-routing srv6
+ locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json")
+ check_ping("ce1", "2001:2::2", True)
+
+
+def test_bgp_srv6_unset():
+ check_ping("ce1", "2001:2::2", True)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ no segment-routing srv6
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json")
+ check_ping("ce1", "2001:2::2", False)
+
+
+def test_bgp_srv6_reset():
+ check_ping("ce1", "2001:2::2", False)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ segment-routing srv6
+ locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json")
+ check_ping("ce1", "2001:2::2", True)
if __name__ == "__main__":
--- /dev/null
+interface r1-eth0
+ ip address 192.168.1.1/24
+
+interface r1-eth1
+ ip address 192.168.2.1/24
+
+interface r1-eth2
+ ip address 192.168.3.1/24
\ No newline at end of file
--- /dev/null
+interface r2-eth0
+ ip address 192.168.1.2/24
+interface r2-eth1
+ ip address 192.168.3.2/24
#!/usr/bin/env python
-
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
#
# <template>.py
# Part of NetDEF Topology Tests
import sys
import pytest
-# Import topogen and topotest helpers
-from lib.topogen import Topogen, TopoRouter, get_topogen
-
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
# TODO: select markers based on daemons used during test
# pytest module level markers
-"""
-pytestmark = pytest.mark.bfdd # single marker
pytestmark = [
- pytest.mark.bgpd,
- pytest.mark.ospfd,
- pytest.mark.ospf6d
-] # multiple markers
-"""
-
-
+ # pytest.mark.babeld,
+ # pytest.mark.bfdd,
+ # pytest.mark.bgpd,
+ # pytest.mark.eigrpd,
+ # pytest.mark.isisd,
+ # pytest.mark.ldpd,
+ # pytest.mark.nhrpd,
+ # pytest.mark.ospf6d,
+ pytest.mark.ospfd,
+ # pytest.mark.pathd,
+ # pytest.mark.pbrd,
+ # pytest.mark.pimd,
+ # pytest.mark.ripd,
+ # pytest.mark.ripngd,
+ # pytest.mark.sharpd,
+ # pytest.mark.staticd,
+ # pytest.mark.vrrpd,
+]
+
+# Function we pass to Topogen to create the topology
def build_topo(tgen):
"Build function"
# Create 2 routers
- for routern in range(1, 3):
- tgen.add_router("r{}".format(routern))
+ r1 = tgen.add_router("r1")
+ r2 = tgen.add_router("r2")
- # Create a switch with just one router connected to it to simulate a
- # empty network.
+ # Create a p2p connection between r1 and r2
+ tgen.add_link(r1, r2)
+
+ # Create a switch with one router connected to it to simulate a empty network.
switch = tgen.add_switch("s1")
- switch.add_link(tgen.gears["r1"])
+ switch.add_link(r1)
- # Create a connection between r1 and r2
+ # Create a p2p connection between r1 and r2
switch = tgen.add_switch("s2")
- switch.add_link(tgen.gears["r1"])
- switch.add_link(tgen.gears["r2"])
+ switch.add_link(r1)
+ switch.add_link(r2)
-def setup_module(mod):
- "Sets up the pytest environment"
+# New form of setup/teardown using pytest fixture
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
# This function initiates the topology build with Topogen...
- tgen = Topogen(build_topo, mod.__name__)
+ tgen = Topogen(build_topo, request.module.__name__)
- # The basic topology above could also have be more easily specified using a
- # dictionary, remove the build_topo function and use the following instead:
+ # A basic topology similar to the above could also have be more easily specified
+ # using a # dictionary, remove the build_topo function and use the following
+ # instead:
#
# topodef = {
# "s1": "r1"
# "s2": ("r1", "r2")
# }
- # tgen = Topogen(topodef, mod.__name__)
+ # tgen = Topogen(topodef, request.module.__name__)
# ... and here it calls initialization functions.
tgen.start_topology()
# This is a sample of configuration loading.
router_list = tgen.routers()
- # For all registred routers, load the zebra configuration file
- # CWD = os.path.dirname(os.path.realpath(__file__))
+ # For all routers arrange for:
+ # - starting zebra using config file from <rtrname>/zebra.conf
+ # - starting ospfd using an empty config file.
for rname, router in router_list.items():
- router.load_config(
- TopoRouter.RD_ZEBRA,
- # Uncomment next line to load configuration from ./router/zebra.conf
- # os.path.join(CWD, '{}/zebra.conf'.format(rname))
- )
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
- # After loading the configurations, this function loads configured daemons.
+ # Start and configure the router daemons
tgen.start_router()
+ # Provide tgen as argument to each test function
+ yield tgen
-def teardown_module(mod):
- "Teardown the pytest environment"
- tgen = get_topogen()
-
- # This function tears down the whole topology.
+ # Teardown after last test runs
tgen.stop_topology()
-def test_call_cli():
- "Dummy test that just calls tgen.cli() so we can interact with the build."
- tgen = get_topogen()
- # Don't run this test if we have any failure.
+# Fixture that executes before each test
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
+ pytest.skip("skipped because of previous test failure")
+
+
+# ===================
+# The tests functions
+# ===================
+
- # logger.info("calling CLI")
- # tgen.cli()
+def test_get_version(tgen):
+ "Test the logs the FRR version"
+
+ r1 = tgen.gears["r1"]
+ version = r1.vtysh_cmd("show version")
+ logger.info("FRR version is: " + version)
+
+
+def test_connectivity(tgen):
+ "Test the logs the FRR version"
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ output = r1.cmd_raises("ping -c1 192.168.1.2")
+ output = r2.cmd_raises("ping -c1 192.168.3.1")
+
+
+@pytest.mark.xfail
+def test_expect_failure(tgen):
+ "A test that is current expected to fail but should be fixed"
+
+ assert False, "Example of temporary expected failure that will eventually be fixed"
+
+
+@pytest.mark.skip
+def test_will_be_skipped(tgen):
+ "A test that will be skipped"
+ assert False
# Memory leak test template
-def test_memory_leak():
+def test_memory_leak(tgen):
"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")
return True
-@retry(retry_timeout=10, initial_wait=2)
+@retry(retry_timeout=30)
def verify_bgp_rib(
tgen,
addr_type,
from lib.topogen import TopoRouter, get_topogen
from lib.topolog import get_logger, logger
from lib.topotest import frr_unicode, interface_set_status, version_cmp
+from lib import topotest
FRRCFG_FILE = "frr_json.conf"
FRRCFG_BKUP_FILE = "frr_json_initial.conf"
self.resetCounts()
def doCmd(self, tgen, rtr, cmd, checkstr=None):
+ logger.info("doCmd: {} {}".format(rtr, cmd))
output = tgen.net[rtr].cmd(cmd).strip()
if len(output):
self.output += 1
else:
self.match += 1
return ret
- logger.info("command: {} {}".format(rtr, cmd))
logger.info("output: " + output)
- self.none += 1
+ else:
+ logger.info("No output")
+ self.none += 1
return None
def resetCounts(self):
cmd = "no {}".format(cmd)
config_data.append(cmd)
- if "helper-only" in gr_data and not gr_data["helper-only"]:
- cmd = "graceful-restart helper-only"
+ if "helper enable" in gr_data and not gr_data["helper enable"]:
+ cmd = "graceful-restart helper enable"
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
- elif "helper-only" in gr_data and type(gr_data["helper-only"]) is list:
- for rtrs in gr_data["helper-only"]:
- cmd = "graceful-restart helper-only {}".format(rtrs)
+ elif "helper enable" in gr_data and type(gr_data["helper enable"]) is list:
+ for rtrs in gr_data["helper enable"]:
+ cmd = "graceful-restart helper enable {}".format(rtrs)
if gr_data.setdefault("delete", False):
cmd = "no {}".format(cmd)
config_data.append(cmd)
if not isinstance(group_addresses, list):
group_addresses = [group_addresses]
- if not isinstance(iif, list) and iif is not "none":
+ if not isinstance(iif, list) and iif != "none":
iif = [iif]
- if not isinstance(oil, list) and oil is not "none":
+ if not isinstance(oil, list) and oil != "none":
oil = [oil]
for grp_addr in group_addresses:
return (False, result)
+def router_json_cmp_retry(router, cmd, data, exact=False, retry_timeout=10.0):
+ """
+ Runs `cmd` that returns JSON data (normally the command ends with 'json')
+ and compare with `data` contents. Retry by default for 10 seconds
+ """
+
+ def test_func():
+ return router_json_cmp(router, cmd, data, exact)
+
+ ok, _ = run_and_expect(test_func, None, int(retry_timeout), 1)
+ return ok
+
+
def int2dpid(dpid):
"Converting Integer to DPID"
clear_ip_mroute,
clear_ip_pim_interface_traffic,
verify_igmp_config,
- clear_ip_mroute_verify,
McastTesterHelper,
)
from lib.topolog import logger
result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "f1")
assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
- step("Clear the mroute on l1, wait for 5 sec")
- result = clear_ip_mroute_verify(tgen, "l1")
- assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
-
step(
- "After clear ip mroute (*,g) entries are re-populated again"
- " with same OIL and IIF, verify using 'show ip mroute' and "
- " 'show ip pim upstream' "
+ "Verify clear ip mroute (*,g) entries are populated by using "
+ "'show ip mroute' cli"
)
- source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
input_dict = [
{"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"}
]
+ for data in input_dict:
+ result = verify_ip_mroutes(
+ tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"]
+ )
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step("Clear mroutes on l1")
+ clear_ip_mroute(tgen, "l1")
+
+ step(
+ "After clear ip mroute (*,g) entries are re-populated again"
+ " with same OIL and IIF, verify using 'show ip mroute' and "
+ " 'show ip pim upstream' "
+ )
+
for data in input_dict:
result = verify_ip_mroutes(
tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"]
--- /dev/null
+:assword 1
+hostname rt1
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 1
+ ipv6 ospf network point-to-point
+!
+interface eth-rt2
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 1
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 1.1.1.1
+ redistribute connected
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
--- /dev/null
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"1",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"1",
+ "interface":"eth-rt2",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ }
+ ]
+ },
+ {
+ "areaId":"1",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "neighbors":[
+ {
+ "neighborId":"2.2.2.2",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt2",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
--- /dev/null
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ }
+}
--- /dev/null
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+password 1
+hostname rt1
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+interface stub1
+!
+interface eth-rt2
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt2
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt1
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 1
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt3
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 2.2.2.2
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
--- /dev/null
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ },
+ {
+ "areaId":"1",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt3",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ },
+ {
+ "areaId":"1",
+ "interface":"eth-rt1",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ }
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "neighbors":[
+ {
+ "neighborId":"3.3.3.3",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt3",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"1.1.1.1",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt1",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
--- /dev/null
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ }
+}
--- /dev/null
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+password 1
+hostname rt2
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+interface eth-rt1
+!
+interface eth-rt3
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt3
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt2
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt4
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt6
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 3.3.3.3
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
--- /dev/null
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt2",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"eth-rt4",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"eth-rt6",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "neighbors":[
+ {
+ "neighborId":"2.2.2.2",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt2",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"4.4.4.4",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt4",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"6.6.6.6",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt6",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
--- /dev/null
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ }
+}
--- /dev/null
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+password 1
+hostname rt3
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+interface eth-rt2
+!
+interface eth-rt4
+!
+interface eth-rt6
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt4
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt3
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt5
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 2
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 4.4.4.4
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
--- /dev/null
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ },
+ {
+ "areaId":"2",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"5.5.5.5"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"5.5.5.5",
+ "payload":"2001:db8:1000::5\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt3",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ },
+ {
+ "areaId":"2",
+ "interface":"eth-rt5",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"5.5.5.5"
+ }
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "neighbors":[
+ {
+ "neighborId":"3.3.3.3",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt3",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"5.5.5.5",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt5",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
--- /dev/null
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ }
+}
--- /dev/null
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+password 1
+hostname rt4
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address 2001:db8:1000::4/128
+!
+interface eth-rt3
+!
+interface eth-rt5
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt5
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 2
+ ipv6 ospf network point-to-point
+!
+interface eth-rt4
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 2
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 5.5.5.5
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
--- /dev/null
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"2",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"5.5.5.5"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"5.5.5.5",
+ "payload":"2001:db8:1000::5\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"2",
+ "interface":"eth-rt4",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"5.5.5.5"
+ }
+ ]
+ },
+ {
+ "areaId":"2",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "neighbors":[
+ {
+ "neighborId":"4.4.4.4",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt4",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
--- /dev/null
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ }
+}
--- /dev/null
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+password 1
+hostname rt5
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address 2001:db8:1000::5/128
+!
+interface eth-rt4
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt6
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt3
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt7
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 3
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 6.6.6.6
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
--- /dev/null
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ },
+ {
+ "areaId":"3",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"7.7.7.7"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"INP",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt3",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ },
+ {
+ "areaId":"3",
+ "interface":"eth-rt7",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"7.7.7.7"
+ }
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "neighbors":[
+ {
+ "neighborId":"3.3.3.3",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt3",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"7.7.7.7",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt7",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
--- /dev/null
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ }
+}
--- /dev/null
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+password 1
+hostname rt6
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 6.6.6.6/32
+ ipv6 address 2001:db8:1000::6/128
+!
+interface eth-rt3
+!
+interface eth-rt7
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt7
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 3
+ ipv6 ospf network point-to-point
+!
+interface eth-rt6
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 3
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 7.7.7.7
+ redistribute connected
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
--- /dev/null
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"3",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"7.7.7.7"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"INP",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"3",
+ "interface":"eth-rt6",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"7.7.7.7"
+ }
+ ]
+ },
+ {
+ "areaId":"3",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "neighbors":[
+ {
+ "neighborId":"6.6.6.6",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt6",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
--- /dev/null
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ }
+}
--- /dev/null
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+password 1
+hostname rt7
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 7.7.7.7/32
+ ipv6 address 2001:db8:1000::7/128
+!
+interface stub1
+!
+interface eth-rt6
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+#!/usr/bin/env python
+
+#
+# test_ospf6_gr_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_ospf6_gr_topo1.py:
+
+ +---------+
+ | RT1 |
+ | 1.1.1.1 |
+ +---------+
+ |eth-rt2
+ |
+ |eth-rt1
+ +---------+
+ | RT2 |
+ | 2.2.2.2 |
+ +---------+
+ |eth-rt3
+ |
+ |eth-rt2
+ +---------+
+ | RT3 |
+ | 3.3.3.3 |
+ +---------+
+ eth-rt4| |eth-rt6
+ | |
+ +---------+ +--------+
+ | |
+ |eth-rt3 |eth-rt3
+ +---------+ +---------+
+ | RT4 | | RT6 |
+ | 4.4.4.4 | | 6.6.6.6 |
+ +---------+ +---------+
+ |eth-rt5 |eth-rt7
+ | |
+ |eth-rt4 |eth-rt6
+ +---------+ +---------+
+ | RT5 | | RT7 |
+ | 5.5.5.5 | | 7.7.7.7 |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+from time import sleep
+from functools import partial
+
+# 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
+from lib.common_config import (
+ kill_router_daemons,
+ start_router_daemons,
+)
+
+pytestmark = [pytest.mark.ospf6d]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+def build_topo(tgen):
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt1"], nodeif="stub1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt7"], nodeif="stub1")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ 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_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def router_compare_json_output(rname, command, reference, tries):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=tries, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def check_routers(initial_convergence=False, exiting=None, restarting=None):
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ # Check the RIB first, which should be preserved across restarts in
+ # all routers of the routing domain.
+ if initial_convergence == True:
+ tries = 240
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname, "show ipv6 route ospf json", "show_ipv6_route.json", tries
+ )
+
+ # Check that all adjacencies are up and running (except when there's
+ # an OSPF instance that is shutting down).
+ if exiting == None:
+ tries = 240
+ router_compare_json_output(
+ rname,
+ "show ipv6 ospf neighbor json",
+ "show_ipv6_ospf_neighbor.json",
+ tries,
+ )
+
+ # Check the OSPF RIB and LSDB.
+ # In the restarting router, wait up to one minute for the LSDB to converge.
+ if exiting != rname:
+ if initial_convergence == True or restarting == rname:
+ tries = 240
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname,
+ "show ipv6 ospf database json",
+ "show_ipv6_ospf_database.json",
+ tries,
+ )
+ router_compare_json_output(
+ rname, "show ipv6 ospf route json", "show_ipv6_ospf_route.json", tries
+ )
+
+
+#
+# Test initial network convergence
+#
+def test_initial_convergence():
+ logger.info("Test: verify initial network convergence")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_routers(initial_convergence=True)
+
+
+#
+# Test rt1 performing a graceful restart
+#
+def test_gr_rt1():
+ logger.info("Test: verify rt1 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt1"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt1", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt1")
+
+ start_router_daemons(tgen, "rt1", ["ospf6d"])
+ check_routers(restarting="rt1")
+
+
+#
+# Test rt2 performing a graceful restart
+#
+def test_gr_rt2():
+ logger.info("Test: verify rt2 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt2"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt2", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt2")
+
+ start_router_daemons(tgen, "rt2", ["ospf6d"])
+ check_routers(restarting="rt2")
+
+
+#
+# Test rt3 performing a graceful restart
+#
+def test_gr_rt3():
+ logger.info("Test: verify rt3 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt3"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt3", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt3")
+
+ start_router_daemons(tgen, "rt3", ["ospf6d"])
+ check_routers(restarting="rt3")
+
+
+#
+# Test rt4 performing a graceful restart
+#
+def test_gr_rt4():
+ logger.info("Test: verify rt4 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt4"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt4", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt4")
+
+ start_router_daemons(tgen, "rt4", ["ospf6d"])
+ check_routers(restarting="rt4")
+
+
+#
+# Test rt5 performing a graceful restart
+#
+def test_gr_rt5():
+ logger.info("Test: verify rt5 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt5"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt5", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt5")
+
+ start_router_daemons(tgen, "rt5", ["ospf6d"])
+ check_routers(restarting="rt5")
+
+
+#
+# Test rt6 performing a graceful restart
+#
+def test_gr_rt6():
+ logger.info("Test: verify rt6 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt6"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt6", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt6")
+
+ start_router_daemons(tgen, "rt6", ["ospf6d"])
+ check_routers(restarting="rt6")
+
+
+#
+# Test rt7 performing a graceful restart
+#
+def test_gr_rt7():
+ logger.info("Test: verify rt7 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt7"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt7", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt7")
+
+ start_router_daemons(tgen, "rt7", ["ospf6d"])
+ check_routers(restarting="rt7")
+
+
+# Memory leak test template
+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))
{
"numberOfIntraAreaRoutes": 1,
"numberOfInterAreaRoutes": 2,
- "numberOfExternal1Routes": 4,
- "numberOfExternal2Routes": 0,
+ "numberOfExternal1Routes": 0,
+ "numberOfExternal2Routes": 3,
},
)
]
route = {
"2001:db8:100::/64": {
- "pathType": "E1",
+ "pathType": "E2",
"nextHops": [{"nextHop": "::", "interfaceName": "r4-eth0"}],
}
}
assert result is None, assertmsg
+def test_nssa_default_originate():
+ """
+ Test the following:
+ * A type-7 default route should be originated into the NSSA area
+ when the default-information-originate option is configured;
+ * Once the default-information-originate option is unconfigured, the
+ previously originated Type-7 default route should be removed.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Configure r2 to announce a Type-7 default route.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ no default-information originate
+ area 2 nssa default-information-originate
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting Type-7 default-route to be added")
+ routes = {"::/0": {}}
+ expect_ospfv3_routes("r4", routes, wait=30, type="external-2")
+
+ #
+ # Configure r2 to stop announcing a Type-7 default route.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ area 2 nssa
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting Type-7 default route to be removed")
+ test_func = partial(dont_expect_route, "r4", "::/0", type="external-2")
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = "r4's Type-7 default route still exists"
+ assert result is None, assertmsg
+
+
+def test_area_filters():
+ """
+ Test ABR import/export filters.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Configure import/export filters on r2 (ABR for area 1).
+ #
+ config = """
+ configure terminal
+ ipv6 access-list ACL_IMPORT seq 5 permit 2001:db8:2::/64
+ ipv6 access-list ACL_IMPORT seq 10 deny any
+ ipv6 access-list ACL_EXPORT seq 10 deny any
+ router ospf6
+ area 1 import-list ACL_IMPORT
+ area 1 export-list ACL_EXPORT
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting inter-area routes to be removed on r1")
+ for route in ["::/0", "2001:db8:3::/64"]:
+ test_func = partial(dont_expect_route, "r1", route, type="inter-area")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = "{}'s {} inter-area route still exists".format("r1", route)
+ assert result is None, assertmsg
+
+ logger.info("Expecting inter-area routes to be removed on r3")
+ for route in ["2001:db8:1::/64"]:
+ test_func = partial(dont_expect_route, "r3", route, type="inter-area")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = "{}'s {} inter-area route still exists".format("r3", route)
+ assert result is None, assertmsg
+
+ #
+ # Update the ACLs used by the import/export filters.
+ #
+ config = """
+ configure terminal
+ ipv6 access-list ACL_IMPORT seq 6 permit 2001:db8:3::/64
+ ipv6 access-list ACL_EXPORT seq 5 permit 2001:db8:1::/64
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting 2001:db8:3::/64 to be re-added on r1")
+ routes = {"2001:db8:3::/64": {}}
+ expect_ospfv3_routes("r1", routes, wait=30, type="inter-area")
+ logger.info("Expecting 2001:db8:1::/64 to be re-added on r3")
+ routes = {"2001:db8:1::/64": {}}
+ expect_ospfv3_routes("r3", routes, wait=30, type="inter-area")
+
+ #
+ # Unconfigure r2's ABR import/export filters.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ no area 1 import-list ACL_IMPORT
+ no area 1 export-list ACL_EXPORT
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting ::/0 to be re-added on r1")
+ routes = {"::/0": {}}
+ expect_ospfv3_routes("r1", routes, wait=30, type="inter-area")
+
+
def teardown_module(_mod):
"Teardown the pytest environment"
tgen = get_topogen()
step("Configure graceful restart in the DUT")
ospf_gr_r0 = {
- "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r0)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_gr_r1 = {
- "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_gr_r0 = {
"r0": {
"ospf": {
- "graceful-restart": {"helper-only": [], "opaque": True, "delete": True}
+ "graceful-restart": {"helper enable": [], "opaque": True, "delete": True}
}
}
}
step("Configure gr helper using the router id")
ospf_gr_r0 = {
"r0": {
- "ospf": {"graceful-restart": {"helper-only": ["1.1.1.1"], "opaque": True}}
+ "ospf": {"graceful-restart": {"helper enable": ["1.1.1.1"], "opaque": True}}
}
}
result = create_router_ospf(tgen, topo, ospf_gr_r0)
"r0": {
"ospf": {
"graceful-restart": {
- "helper-only": ["1.1.1.1"],
+ "helper enable": ["1.1.1.1"],
"opaque": True,
"delete": True,
}
ospf_covergence is True
), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
ospf_gr_r0 = {
- "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r0)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_gr_r1 = {
- "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_gr_r0 = {
- "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r0)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_gr_r1 = {
- "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_gr_r0 = {
- "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r0)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_gr_r1 = {
- "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_covergence is True
), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
ospf_gr_r0 = {
- "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r0)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_gr_r1 = {
- "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_covergence is True
), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
ospf_gr_r0 = {
- "r0": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r0)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
ospf_gr_r1 = {
- "r1": {"ospf": {"graceful-restart": {"helper-only": [], "opaque": True}}}
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
}
result = create_router_ospf(tgen, topo, ospf_gr_r1)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
capability opaque
redistribute connected
graceful-restart grace-period 120
- graceful-restart helper-only
+ graceful-restart helper enable
!
router-id 2.2.2.2
capability opaque
graceful-restart grace-period 120
- graceful-restart helper-only
+ graceful-restart helper enable
!
router-id 3.3.3.3
capability opaque
graceful-restart grace-period 120
- graceful-restart helper-only
+ graceful-restart helper enable
!
router-id 4.4.4.4
capability opaque
graceful-restart grace-period 120
- graceful-restart helper-only
+ graceful-restart helper enable
!
router-id 5.5.5.5
capability opaque
graceful-restart grace-period 120
- graceful-restart helper-only
+ graceful-restart helper enable
!
capability opaque
area 3 nssa
graceful-restart grace-period 120
- graceful-restart helper-only
+ graceful-restart helper enable
!
redistribute connected
area 3 nssa
graceful-restart grace-period 120
- graceful-restart helper-only
+ graceful-restart helper enable
!
--- /dev/null
+#!/usr/bin/env python
+
+#
+# scale_test_common.py
+#
+# Copyright (c) 2020 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+# 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.
+#
+
+"""
+scale_test_common.py: Common routines for testing route scale
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+from functools import partial
+
+# 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
+
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def scale_build_common(tgen):
+ "Build function"
+
+ # Populate routers
+ for routern in range(1, 2):
+ tgen.add_router("r{}".format(routern))
+
+ # Populate switches
+ for switchn in range(1, 33):
+ switch = tgen.add_switch("sw{}".format(switchn))
+ switch.add_link(tgen.gears["r1"])
+
+
+def scale_setup_module(module):
+ "Setup topology"
+ tgen = Topogen(scale_build_common, module.__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_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+ # tgen.mininet_cli()
+
+
+def scale_teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def scale_converge_protocols():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+
+def run_one_setup(r1, s):
+ "Run one ecmp config"
+
+ # Extract params
+ expected_installed = s["expect_in"]
+ expected_removed = s["expect_rem"]
+
+ retries = s["retries"]
+ wait = s["wait"]
+
+ for d in expected_installed["routes"]:
+ if d["type"] == "sharp":
+ count = d["rib"]
+ break
+
+ logger.info("Testing {} routes X {} ecmp".format(count, s["ecmp"]))
+
+ r1.vtysh_cmd(
+ "sharp install route 1.0.0.0 \
+ nexthop-group {} {}".format(
+ s["nhg"], count
+ ),
+ isjson=False,
+ )
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route summary json", expected_installed
+ )
+ success, result = topotest.run_and_expect(test_func, None, retries, wait)
+ assert success, "Route scale test install failed:\n{}".format(result)
+
+ output = r1.vtysh_cmd("sharp data route", isjson=False)
+ logger.info("{} routes X {} ecmp installed".format(count, s["ecmp"]))
+ logger.info(output)
+ r1.vtysh_cmd("sharp remove route 1.0.0.0 {}".format(count), isjson=False)
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route summary json", expected_removed
+ )
+ success, result = topotest.run_and_expect(test_func, None, retries, wait)
+ assert success, "Route scale test remove failed:\n{}".format(result)
+
+ output = r1.vtysh_cmd("sharp data route", isjson=False)
+ logger.info("{} routes x {} ecmp removed".format(count, s["ecmp"]))
+ logger.info(output)
+
+
+def route_install_helper(iter):
+ "Test route install for a variety of ecmp"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ # Avoid top ecmp case for runs with < 4G memory
+ output = tgen.net.cmd_raises("free")
+ m = re.search("Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", output)
+ total_mem = int(m.group(2))
+ if total_mem < 4000000 and iter == 5:
+ logger.info(
+ "Limited memory available: {}, skipping x32 testcase".format(total_mem)
+ )
+ return;
+
+ installed_file = "{}/r1/installed.routes.json".format(CWD)
+ expected_installed = json.loads(open(installed_file).read())
+
+ removed_file = "{}/r1/no.routes.json".format(CWD)
+ expected_removed = json.loads(open(removed_file).read())
+
+ # dict keys of params: ecmp number, corresponding nhg name, timeout,
+ # number of times to wait
+ scale_keys = ["ecmp", "nhg", "wait", "retries", "expect_in", "expect_rem"]
+
+ # Table of defaults, used for timeout values and 'expected' objects
+ scale_defaults = dict(
+ zip(scale_keys, [None, None, 7, 30, expected_installed, expected_removed])
+ )
+
+ # List of params for each step in the test; note extra time given
+ # for the highest ecmp steps. Executing 'show' at scale can be costly
+ # so we widen the interval there too.
+ scale_steps = [
+ [1, "one"],
+ [2, "two"],
+ [4, "four"],
+ [8, "eight"],
+ [16, "sixteen", 10, 40],
+ [32, "thirtytwo", 10, 40],
+ ]
+
+ # Build up a list of dicts with params for each step of the test;
+ # use defaults where the step doesn't supply a value
+ scale_setups = []
+ s = scale_steps[iter]
+
+ d = dict(zip(scale_keys, s))
+ for k in scale_keys:
+ if k not in d:
+ d[k] = scale_defaults[k]
+
+ run_one_setup(r1, d)
+
+
+# Mem leak testcase
+def scale_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()
+++ /dev/null
-#!/usr/bin/env python
-
-#
-# test_route_scale.py
-#
-# Copyright (c) 2020 by
-# Cumulus Networks, Inc.
-# Donald Sharp
-#
-# 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_route_scale.py: Testing route scale
-
-"""
-
-import os
-import re
-import sys
-import pytest
-import json
-from functools import partial
-
-# 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
-
-
-pytestmark = [pytest.mark.sharpd]
-
-
-#####################################################
-##
-## Network Topology Definition
-##
-#####################################################
-
-
-def build(tgen):
- "Build function"
-
- # Populate routers
- for routern in range(1, 2):
- tgen.add_router("r{}".format(routern))
-
- # Populate switches
- for switchn in range(1, 33):
- switch = tgen.add_switch("sw{}".format(switchn))
- switch.add_link(tgen.gears["r1"])
-
-
-#####################################################
-##
-## Tests starting
-##
-#####################################################
-
-
-def setup_module(module):
- "Setup topology"
- tgen = Topogen(build, module.__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_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
- )
-
- tgen.start_router()
- # tgen.mininet_cli()
-
-
-def teardown_module(_mod):
- "Teardown the pytest environment"
- tgen = get_topogen()
-
- # This function tears down the whole topology.
- tgen.stop_topology()
-
-
-def test_converge_protocols():
- "Wait for protocol convergence"
-
- tgen = get_topogen()
- # Don't run this test if we have any failure.
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
-
-
-def run_one_setup(r1, s):
- "Run one ecmp config"
-
- # Extract params
- expected_installed = s["expect_in"]
- expected_removed = s["expect_rem"]
-
- retries = s["retries"]
- wait = s["wait"]
-
- for d in expected_installed["routes"]:
- if d["type"] == "sharp":
- count = d["rib"]
- break
-
- logger.info("Testing {} routes X {} ecmp".format(count, s["ecmp"]))
-
- r1.vtysh_cmd(
- "sharp install route 1.0.0.0 \
- nexthop-group {} {}".format(
- s["nhg"], count
- ),
- isjson=False,
- )
-
- test_func = partial(
- topotest.router_json_cmp, r1, "show ip route summary json", expected_installed
- )
- success, result = topotest.run_and_expect(test_func, None, retries, wait)
- assert success, "Route scale test install failed:\n{}".format(result)
-
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("{} routes X {} ecmp installed".format(count, s["ecmp"]))
- logger.info(output)
- r1.vtysh_cmd("sharp remove route 1.0.0.0 {}".format(count), isjson=False)
- test_func = partial(
- topotest.router_json_cmp, r1, "show ip route summary json", expected_removed
- )
- success, result = topotest.run_and_expect(test_func, None, retries, wait)
- assert success, "Route scale test remove failed:\n{}".format(result)
-
- output = r1.vtysh_cmd("sharp data route", isjson=False)
- logger.info("{} routes x {} ecmp removed".format(count, s["ecmp"]))
- logger.info(output)
-
-
-def test_route_install():
- "Test route install for a variety of ecmp"
-
- tgen = get_topogen()
- # Don't run this test if we have any failure.
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
-
- r1 = tgen.gears["r1"]
-
- installed_file = "{}/r1/installed.routes.json".format(CWD)
- expected_installed = json.loads(open(installed_file).read())
-
- removed_file = "{}/r1/no.routes.json".format(CWD)
- expected_removed = json.loads(open(removed_file).read())
-
- # dict keys of params: ecmp number, corresponding nhg name, timeout,
- # number of times to wait
- scale_keys = ["ecmp", "nhg", "wait", "retries", "expect_in", "expect_rem"]
-
- # Table of defaults, used for timeout values and 'expected' objects
- scale_defaults = dict(
- zip(scale_keys, [None, None, 7, 30, expected_installed, expected_removed])
- )
-
- # List of params for each step in the test; note extra time given
- # for the highest ecmp steps. Executing 'show' at scale can be costly
- # so we widen the interval there too.
- scale_steps = [
- [1, "one"],
- [2, "two"],
- [4, "four"],
- [8, "eight"],
- [16, "sixteen", 10, 40],
- [32, "thirtytwo", 10, 40],
- ]
-
- # Build up a list of dicts with params for each step of the test;
- # use defaults where the step doesn't supply a value
- scale_setups = []
- for s in scale_steps:
- d = dict(zip(scale_keys, s))
- for k in scale_keys:
- if k not in d:
- d[k] = scale_defaults[k]
-
- scale_setups.append(d)
-
- # Avoid top ecmp case for runs with < 4G memory
- output = tgen.net.cmd_raises("free")
- m = re.search("Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", output)
- total_mem = int(m.group(2))
- if total_mem < 4000000:
- logger.info(
- "Limited memory available: {}, skipping x32 testcase".format(total_mem)
- )
- scale_setups = scale_setups[0:-1]
-
- # Run each step using the dicts we've built
- for s in scale_setups:
- run_one_setup(r1, s)
-
-
-# Mem leak testcase
-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
+#!/usr/bin/env python
+
+#
+# test_route_scale1.py
+#
+# Copyright (c) 2021 by
+# Nvidia, Inc.
+# Donald Sharp
+#
+# 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_route_scale1.py: Testing route scale
+
+"""
+import os
+import re
+import sys
+import pytest
+import json
+from functools import partial
+
+# 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
+
+from scale_test_common import scale_build_common, scale_setup_module, route_install_helper, scale_test_memory_leak, scale_converge_protocols, scale_teardown_module
+
+
+pytestmark = [pytest.mark.sharpd]
+
+def build(tgen):
+ scale_build_common(tgen)
+
+def setup_module(module):
+ scale_setup_module(module)
+
+def teardown_module(_mod):
+ scale_teardown_module(_mod)
+
+def test_converge_protocols():
+ scale_converge_protocols()
+
+def test_route_install_2nh():
+ route_install_helper(1)
+
+def test_route_install_4nh():
+ route_install_helper(2)
+
+def test_route_install_16nh():
+ route_install_helper(4)
+
+def test_memory_leak():
+ scale_test_memory_leak()
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
--- /dev/null
+#!/usr/bin/env python
+
+#
+# test_route_scale2.py
+#
+# Copyright (c) 2022 by
+# Nvidia, Inc.
+# Donald Sharp
+#
+# 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 NVIDIA DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NVIDIA 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_route_scale2.py: Testing route scale
+
+"""
+import os
+import re
+import sys
+import pytest
+import json
+from functools import partial
+
+# 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
+
+from scale_test_common import scale_build_common, scale_setup_module, route_install_helper, scale_test_memory_leak, scale_converge_protocols, scale_teardown_module
+
+
+pytestmark = [pytest.mark.sharpd]
+
+def build(tgen):
+ scale_build_common(tgen)
+
+def setup_module(module):
+ scale_setup_module(module)
+
+def teardown_module(_mod):
+ scale_teardown_module(_mod)
+
+def test_converge_protocols():
+ scale_converge_protocols()
+
+def test_route_install_1nh():
+ route_install_helper(0)
+
+def test_route_install_8nh():
+ route_install_helper(3)
+
+def test_route_install_32nh():
+ route_install_helper(5)
+
+def test_memory_leak():
+ scale_test_memory_leak()
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
[
- {
- "name": "loc3",
- "chunks": []
- }
]
[
- {
- "name": "loc3",
- "chunks": [
- "2001:db8:3:3::/64"
- ]
- }
]
]
},
{
- "name":"loc3",
- "statusUp":false,
- "chunks":[
+ "name": "loc3",
+ "prefix": "2001:db8:3:3::/64",
+ "statusUp": true,
+ "chunks": [
{
- "proto":"sharp"
+ "prefix": "2001:db8:3:3::/64",
+ "proto": "system"
}
]
}
{
"locators":[
- {
- "name": "loc1",
- "prefix": "2001:db8:1:1::/64",
- "statusUp": true,
- "chunks": [
- {
- "prefix": "2001:db8:1:1::/64",
- "proto": "system"
- }
- ]
- },
{
"name": "loc2",
"prefix": "2001:db8:2:2::/64",
"chunks":[
{
"prefix": "2001:db8:3:3::/64",
- "proto": "sharp"
+ "proto": "system"
}
]
}
--- /dev/null
+{
+ "locators":[
+ ]
+}
+
success, result = topotest.run_and_expect(func, None, count=5, wait=0.5)
assert result is None, "Failed"
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
logger.info("Test1 for Locator Configuration")
check_srv6_locator(router, "expected_locators1.json")
check_sharpd_chunk(router, "expected_chunks1.json")
check_srv6_locator(router, "expected_locators3.json")
check_sharpd_chunk(router, "expected_chunks3.json")
- logger.info("Test4 get chunk for non-exist locator by zclient")
- router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc3")
+ logger.info("Test4 additional locator loc3")
+ router.vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ locator loc3
+ prefix 2001:db8:3:3::/64
+ """
+ )
check_srv6_locator(router, "expected_locators4.json")
check_sharpd_chunk(router, "expected_chunks4.json")
- logger.info("Test5 Test for Zclient. after locator loc3 was configured")
+ logger.info("Test5 delete locator and chunk is released automatically")
router.vtysh_cmd(
"""
configure terminal
segment-routing
srv6
locators
- locator loc3
- prefix 2001:db8:3:3::/64
+ no locator loc1
"""
)
check_srv6_locator(router, "expected_locators5.json")
check_sharpd_chunk(router, "expected_chunks5.json")
+ logger.info("Test6 delete srv6 all configuration")
+ router.vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ no srv6
+ """
+ )
+ check_srv6_locator(router, "expected_locators6.json")
+ check_sharpd_chunk(router, "expected_chunks6.json")
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
+++ /dev/null
-{
- "2.1.3.7\/32":[
- {
- "prefix":"2.1.3.7\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.8\/32":[
- {
- "prefix":"2.1.3.8\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.9\/32":[
- {
- "prefix":"2.1.3.9\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.10\/32":[
- {
- "prefix":"2.1.3.10\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.11\/32":[
- {
- "prefix":"2.1.3.11\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.12\/32":[
- {
- "prefix":"2.1.3.12\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.13\/32":[
- {
- "prefix":"2.1.3.13\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.14\/32":[
- {
- "prefix":"2.1.3.14\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.15\/32":[
- {
- "prefix":"2.1.3.15\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.16\/32":[
- {
- "prefix":"2.1.3.16\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.17\/32":[
- {
- "prefix":"2.1.3.17\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.18\/32":[
- {
- "prefix":"2.1.3.18\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.19\/32":[
- {
- "prefix":"2.1.3.19\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.20\/32":[
- {
- "prefix":"2.1.3.20\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.21\/32":[
- {
- "prefix":"2.1.3.21\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.22\/32":[
- {
- "prefix":"2.1.3.22\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.23\/32":[
- {
- "prefix":"2.1.3.23\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.24\/32":[
- {
- "prefix":"2.1.3.24\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.25\/32":[
- {
- "prefix":"2.1.3.25\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.26\/32":[
- {
- "prefix":"2.1.3.26\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.27\/32":[
- {
- "prefix":"2.1.3.27\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.28\/32":[
- {
- "prefix":"2.1.3.28\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.29\/32":[
- {
- "prefix":"2.1.3.29\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.30\/32":[
- {
- "prefix":"2.1.3.30\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.31\/32":[
- {
- "prefix":"2.1.3.31\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.32\/32":[
- {
- "prefix":"2.1.3.32\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.33\/32":[
- {
- "prefix":"2.1.3.33\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.34\/32":[
- {
- "prefix":"2.1.3.34\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.35\/32":[
- {
- "prefix":"2.1.3.35\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.36\/32":[
- {
- "prefix":"2.1.3.36\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.37\/32":[
- {
- "prefix":"2.1.3.37\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.38\/32":[
- {
- "prefix":"2.1.3.38\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.39\/32":[
- {
- "prefix":"2.1.3.39\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.40\/32":[
- {
- "prefix":"2.1.3.40\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.41\/32":[
- {
- "prefix":"2.1.3.41\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.42\/32":[
- {
- "prefix":"2.1.3.42\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.43\/32":[
- {
- "prefix":"2.1.3.43\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.44\/32":[
- {
- "prefix":"2.1.3.44\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.45\/32":[
- {
- "prefix":"2.1.3.45\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.46\/32":[
- {
- "prefix":"2.1.3.46\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.47\/32":[
- {
- "prefix":"2.1.3.47\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.48\/32":[
- {
- "prefix":"2.1.3.48\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.49\/32":[
- {
- "prefix":"2.1.3.49\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.50\/32":[
- {
- "prefix":"2.1.3.50\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.51\/32":[
- {
- "prefix":"2.1.3.51\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.52\/32":[
- {
- "prefix":"2.1.3.52\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.53\/32":[
- {
- "prefix":"2.1.3.53\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.54\/32":[
- {
- "prefix":"2.1.3.54\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.55\/32":[
- {
- "prefix":"2.1.3.55\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.56\/32":[
- {
- "prefix":"2.1.3.56\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.57\/32":[
- {
- "prefix":"2.1.3.57\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.58\/32":[
- {
- "prefix":"2.1.3.58\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.59\/32":[
- {
- "prefix":"2.1.3.59\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.60\/32":[
- {
- "prefix":"2.1.3.60\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.61\/32":[
- {
- "prefix":"2.1.3.61\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.62\/32":[
- {
- "prefix":"2.1.3.62\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.63\/32":[
- {
- "prefix":"2.1.3.63\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.64\/32":[
- {
- "prefix":"2.1.3.64\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.65\/32":[
- {
- "prefix":"2.1.3.65\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.66\/32":[
- {
- "prefix":"2.1.3.66\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.67\/32":[
- {
- "prefix":"2.1.3.67\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.68\/32":[
- {
- "prefix":"2.1.3.68\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.69\/32":[
- {
- "prefix":"2.1.3.69\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.70\/32":[
- {
- "prefix":"2.1.3.70\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.71\/32":[
- {
- "prefix":"2.1.3.71\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.72\/32":[
- {
- "prefix":"2.1.3.72\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.73\/32":[
- {
- "prefix":"2.1.3.73\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.74\/32":[
- {
- "prefix":"2.1.3.74\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.75\/32":[
- {
- "prefix":"2.1.3.75\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.76\/32":[
- {
- "prefix":"2.1.3.76\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.77\/32":[
- {
- "prefix":"2.1.3.77\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.78\/32":[
- {
- "prefix":"2.1.3.78\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.79\/32":[
- {
- "prefix":"2.1.3.79\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.80\/32":[
- {
- "prefix":"2.1.3.80\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.81\/32":[
- {
- "prefix":"2.1.3.81\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.82\/32":[
- {
- "prefix":"2.1.3.82\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.83\/32":[
- {
- "prefix":"2.1.3.83\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.84\/32":[
- {
- "prefix":"2.1.3.84\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.85\/32":[
- {
- "prefix":"2.1.3.85\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.86\/32":[
- {
- "prefix":"2.1.3.86\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.87\/32":[
- {
- "prefix":"2.1.3.87\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.88\/32":[
- {
- "prefix":"2.1.3.88\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.89\/32":[
- {
- "prefix":"2.1.3.89\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.90\/32":[
- {
- "prefix":"2.1.3.90\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.91\/32":[
- {
- "prefix":"2.1.3.91\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.92\/32":[
- {
- "prefix":"2.1.3.92\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.93\/32":[
- {
- "prefix":"2.1.3.93\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.94\/32":[
- {
- "prefix":"2.1.3.94\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.95\/32":[
- {
- "prefix":"2.1.3.95\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.96\/32":[
- {
- "prefix":"2.1.3.96\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.97\/32":[
- {
- "prefix":"2.1.3.97\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.98\/32":[
- {
- "prefix":"2.1.3.98\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.99\/32":[
- {
- "prefix":"2.1.3.99\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.100\/32":[
- {
- "prefix":"2.1.3.100\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.101\/32":[
- {
- "prefix":"2.1.3.101\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.102\/32":[
- {
- "prefix":"2.1.3.102\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.103\/32":[
- {
- "prefix":"2.1.3.103\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.104\/32":[
- {
- "prefix":"2.1.3.104\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.105\/32":[
- {
- "prefix":"2.1.3.105\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ],
- "2.1.3.106\/32":[
- {
- "prefix":"2.1.3.106\/32",
- "protocol":"sharp",
- "selected":true,
- "destSelected":true,
- "distance":150,
- "metric":0,
- "installed":true,
- "table":254,
- "nexthops":[
- {
- "flags":3,
- "fib":true,
- "ip":"192.168.1.1",
- "afi":"ipv4",
- "interfaceName":"r1-eth0",
- "active":true,
- "weight":1
- }
- ]
- }
- ]
-}
test_zebra_netlink.py: Test some basic interactions with kernel using Netlink
"""
-
-import os
-import sys
-import pytest
+# pylint: disable=C0413
+import ipaddress
import json
+import sys
from functools import partial
-# 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
+import pytest
from lib import topotest
-from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topogen import Topogen, TopoRouter
from lib.topolog import logger
#####################################################
-def setup_module(mod):
+@pytest.fixture(scope="module")
+def tgen(request):
"Sets up the pytest environment"
topodef = {"s1": ("r1")}
- tgen = Topogen(topodef, mod.__name__)
+ tgen = Topogen(topodef, request.module.__name__)
tgen.start_topology()
+ # Initialize all routers.
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_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
- )
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_SHARP)
- # Initialize all routers.
tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
-def teardown_module(_mod):
- "Teardown the pytest environment"
- tgen = get_topogen()
-
- # This function tears down the whole topology.
- tgen.stop_topology()
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
-def test_zebra_netlink_batching():
+def test_zebra_netlink_batching(tgen):
"Test the situation where dataplane fills netlink send buffer entirely."
logger.info(
"Test the situation where dataplane fills netlink send buffer entirely."
)
- tgen = get_topogen()
- if tgen.routers_have_failure():
- pytest.skip("skipped because of previous test failure")
r1 = tgen.gears["r1"]
# Reduce the size of the buffer to hit the limit.
r1.vtysh_cmd("conf t\nzebra kernel netlink batch-tx-buf 256 256")
- r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100")
- json_file = "{}/r1/v4_route.json".format(CWD)
- expected = json.loads(open(json_file).read())
- test_func = partial(
- topotest.router_json_cmp,
- r1,
- "show ip route json",
- expected,
- )
- _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
- assertmsg = '"r1" JSON output mismatches'
- assert result is None, assertmsg
-
- r1.vtysh_cmd("sharp remove routes 2.1.3.7 100")
+ count = 100
+ r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 " + str(count))
+
+ # Generate expected results
+ entry = {
+ "protocol": "sharp",
+ "distance": 150,
+ "metric": 0,
+ "installed": True,
+ "table": 254,
+ "nexthops": [
+ {
+ "fib": True,
+ "ip": "192.168.1.1",
+ "afi": "ipv4",
+ "interfaceName": "r1-eth0",
+ "active": True,
+ "weight": 1,
+ }
+ ],
+ }
+
+ match = {}
+ base = int(ipaddress.ip_address(u"2.1.3.7"))
+ for i in range(base, base + count):
+ pfx = str(ipaddress.ip_network((i, 32)))
+ match[pfx] = [dict(entry, prefix=pfx)]
+
+ ok = topotest.router_json_cmp_retry(r1, "show ip route json", match)
+ assert ok, '"r1" JSON output mismatches'
+
+ r1.vtysh_cmd("sharp remove routes 2.1.3.7 " + str(count))
if __name__ == "__main__":
/* use external allocations */
-static void lp_plugin_init()
+static void lp_plugin_init(void)
{
/* register our own hooks */
hook_register(lm_client_connect, test_client_connect);
hook_register(lm_release_chunk, lm_release_chunk_pi);
}
-static void lp_plugin_cleanup()
+static void lp_plugin_cleanup(void)
{
/* register our own hooks */
hook_unregister(lm_client_connect, test_client_connect);
/* tests */
-static void test_lp_plugin()
+static void test_lp_plugin(void)
{
struct label_manager_chunk *lmc;
--- /dev/null
+@@
+identifier I;
+identifier func =~ "thread_add_";
+struct thread *thread;
+@@
+
+*thread = NULL;
+...
+func
--- /dev/null
+// zlog_* should not have \n or \r at the end usually.
+// spatch --sp-file tools/coccinelle/zlog_no_newline.cocci --macro-file tools/cocci.h ./ 2>/dev/null
+
+@r@
+expression fmt;
+identifier func =~ "zlog_";
+position p;
+@@
+(
+ func(fmt)@p
+|
+ func(fmt, ...)@p
+)
+
+@script:python@
+fmt << r.fmt;
+p << r.p;
+@@
+if "\\n" in str(fmt) or "\\r" in str(fmt):
+ print("Newline in logging function detected %s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt))
--- /dev/null
+#!/usr/bin/env python3
+'''
+Usage: frr_babeltrace.py trace_path
+
+FRR pushes data into lttng tracepoints in the least overhead way possible
+i.e. as binary-data/crf_arrays. These traces need to be converted into pretty
+strings for easy greping etc. This script is a babeltrace python plugin for
+that pretty printing.
+
+Copyright (C) 2021 NVIDIA Corporation
+Anuradha Karuppiah
+
+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
+'''
+
+import ipaddress
+import socket
+import sys
+
+import babeltrace
+
+########################### common parsers - start ############################
+def print_ip_addr(field_val):
+ '''
+ pretty print "struct ipaddr"
+ '''
+ if field_val[0] == socket.AF_INET:
+ addr = [str(fv) for fv in field_val[4:8]]
+ return str(ipaddress.IPv4Address('.'.join(addr)))
+
+ if field_val[0] == socket.AF_INET6:
+ tmp = ''.join('%02x' % fb for fb in field_val[4:])
+ addr = []
+ while tmp:
+ addr.append(tmp[:4])
+ tmp = tmp[4:]
+ addr = ':'.join(addr)
+ return str(ipaddress.IPv6Address(addr))
+
+ if not field_val[0]:
+ return ''
+
+ return field_val
+
+
+def print_mac(field_val):
+ '''
+ pretty print "u8 mac[6]"
+ '''
+ return ':'.join('%02x' % fb for fb in field_val)
+
+def print_net_ipv4_addr(field_val):
+ '''
+ pretty print ctf_integer_network ipv4
+ '''
+ return str(ipaddress.IPv4Address(field_val))
+
+def print_esi(field_val):
+ '''
+ pretty print ethernet segment id, esi_t
+ '''
+ return ':'.join('%02x' % fb for fb in field_val)
+
+def get_field_list(event):
+ '''
+ only fetch fields added via the TP, skip metadata etc.
+ '''
+ return event.field_list_with_scope(babeltrace.CTFScope.EVENT_FIELDS)
+
+def parse_event(event, field_parsers):
+ '''
+ Wild card event parser; doesn't make things any prettier
+ '''
+ field_list = get_field_list(event)
+ field_info = {}
+ for field in field_list:
+ if field in field_parsers:
+ field_parser = field_parsers.get(field)
+ field_info[field] = field_parser(event.get(field))
+ else:
+ field_info[field] = event.get(field)
+ print(event.name, field_info)
+############################ common parsers - end #############################
+
+############################ evpn parsers - start #############################
+def parse_frr_bgp_evpn_mac_ip_zsend(event):
+ '''
+ bgp evpn mac-ip parser; raw format -
+ ctf_array(unsigned char, mac, &pfx->prefix.macip_addr.mac,
+ sizeof(struct ethaddr))
+ ctf_array(unsigned char, ip, &pfx->prefix.macip_addr.ip,
+ sizeof(struct ipaddr))
+ ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr)
+ ctf_array(unsigned char, esi, esi, sizeof(esi_t))
+ '''
+ field_parsers = {'ip': print_ip_addr,
+ 'mac': print_mac,
+ 'esi': print_esi,
+ 'vtep': print_net_ipv4_addr}
+
+ parse_event(event, field_parsers)
+
+def parse_frr_bgp_evpn_bum_vtep_zsend(event):
+ '''
+ bgp evpn bum-vtep parser; raw format -
+ ctf_integer_network_hex(unsigned int, vtep,
+ pfx->prefix.imet_addr.ip.ipaddr_v4.s_addr)
+
+ '''
+ field_parsers = {'vtep': print_net_ipv4_addr}
+
+ parse_event(event, field_parsers)
+
+def parse_frr_bgp_evpn_mh_nh_rmac_send(event):
+ '''
+ bgp evpn nh-rmac parser; raw format -
+ ctf_array(unsigned char, rmac, &nh->rmac, sizeof(struct ethaddr))
+ '''
+ field_parsers = {'rmac': print_mac}
+
+ parse_event(event, field_parsers)
+
+############################ evpn parsers - end #############################
+
+def main():
+ '''
+ FRR lttng trace output parser; babel trace plugin
+ '''
+ event_parsers = {'frr_bgp:evpn_mac_ip_zsend':
+ parse_frr_bgp_evpn_mac_ip_zsend,
+ 'frr_bgp:evpn_bum_vtep_zsend':
+ parse_frr_bgp_evpn_bum_vtep_zsend,
+ 'frr_bgp:evpn_mh_nh_rmac_zsend':
+ parse_frr_bgp_evpn_mh_nh_rmac_send}
+
+ # get the trace path from the first command line argument
+ trace_path = sys.argv[1]
+
+ # grab events
+ trace_collection = babeltrace.TraceCollection()
+ trace_collection.add_traces_recursive(trace_path, 'ctf')
+
+ for event in trace_collection.events:
+ if event.name in event_parsers:
+ event_parser = event_parsers.get(event.name)
+ event_parser(event)
+ else:
+ parse_event(event, {})
+
+if __name__ == '__main__':
+ main()
struct cmd_token *stok = start->data;
struct graph_node *gnn;
struct listnode *ln;
+ bool is_neg = false;
// recursive dfs
listnode_add(position, start);
+
+ for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
+ struct cmd_token *tok = gnn->data;
+
+ if (tok->type == WORD_TKN && !strcmp(tok->text, "no")) {
+ is_neg = true;
+ break;
+ }
+ if (tok->type < SPECIAL_TKN)
+ break;
+ }
+
for (unsigned int i = 0; i < vector_active(start->to); i++) {
struct graph_node *gn = vector_slot(start->to, i);
struct cmd_token *tok = gn->data;
fprintf(stdout, "\n");
} else {
bool skip = false;
+
+ if (tok->type == NEG_ONLY_TKN && !is_neg)
+ continue;
if (stok->type == FORK_TKN && tok->type != FORK_TKN)
for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
if (gnn == gn) {
...
fun:sqlite3_step
}
+{
+ <libyang2 prefix_data stuff>
+ Memcheck:Leak
+ fun:calloc
+ fun:ly_store_prefix_data
+ ...
+ fun:yang_module_load
+}
+{
+ <libyang2 lys_compile_type_union>
+ Memcheck:Leak
+ fun:realloc
+ fun:lys_compile_type_union
+ ...
+ fun:yang_module_load
+}
+{
+ <libyang2 pcre2_compile>
+ Memcheck:Leak
+ fun:malloc
+ fun:pcre2_compile_8
+ ...
+ fun:yang_module_load
+}
+{
+ <libyang2 lys_compile_type_patterns malloc>
+ Memcheck:Leak
+ fun:malloc
+ fun:lys_compile_type_patterns
+ ...
+ fun:yang_module_load
+}
+{
+ <libyang2 lys_compile_type_patterns calloc>
+ Memcheck:Leak
+ fun:calloc
+ fun:lys_compile_type_patterns
+ ...
+ fun:yang_module_load
+}
+{
+ <libyang2 lys_compile_type>
+ Memcheck:Leak
+ fun:calloc
+ fun:lys_compile_type
+ ...
+ fun:yang_module_load
+}
$cppadd = $fabricd ? "-DFABRICD=1" : "";
- open (FH, "@CPP@ -P -std=gnu11 -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ @LIBYANG_CFLAGS@ $cppadd $file |");
+ $command_line = "@CPP@ -P -std=gnu11 -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ @LIBYANG_CFLAGS@ $cppadd $file |";
+ open (FH, $command_line)
+ || die "Open to the pipeline failed: $!\n\nCommand Issued:\n$command_line";
local $/; undef $/;
$line = <FH>;
if (!close (FH)) {
- printf "File: $file failed to compile, when extracting cli from it please inspect\n"
+ die "File: $file failed to compile:\n$!\nwhen extracting cli from it please inspect\n"
}
# ?: makes a group non-capturing
fprintf(stderr,
"%s is not running\n",
vtysh_client[i].name);
- continue;
+ cmd_stat = CMD_ERR_NO_DAEMON;
+ break;
}
}
cmd_stat = vtysh_client_execute(
break;
}
}
- if (cmd_stat != CMD_SUCCESS)
+ if (cmd_stat != CMD_SUCCESS && cmd_stat != CMD_ERR_NO_DAEMON)
break;
if (cmd->func)
} else if (config->index == RMAP_NODE
|| config->index == INTERFACE_NODE
|| config->index == VTY_NODE
- || config->index == VRF_NODE
|| config->index == NH_GROUP_NODE)
config_add_line_uniq(config->line, line);
else
struct thread_master *master;
static bool watch_only = false;
+const char *pathspace;
typedef enum {
PHASE_NONE = 0,
(long)delay.tv_sec, (restart->kills ? SIGKILL : SIGTERM));
kill(-restart->pid, (restart->kills ? SIGKILL : SIGTERM));
restart->kills++;
- restart->t_kill = NULL;
thread_add_timer(master, restart_kill, restart, gs.restart_timeout,
&restart->t_kill);
return 0;
char cmd[strlen(command) + strlen(restart->name) + 1];
snprintf(cmd, sizeof(cmd), command, restart->name);
if ((restart->pid = run_background(cmd)) > 0) {
- restart->t_kill = NULL;
thread_add_timer(master, restart_kill, restart,
gs.restart_timeout, &restart->t_kill);
restart->what = cmdtype;
zlog_debug("%s: connection in progress", dmn->name);
dmn->state = DAEMON_CONNECTING;
dmn->fd = sock;
- dmn->t_write = NULL;
thread_add_write(master, check_connect, dmn, dmn->fd,
&dmn->t_write);
- dmn->t_wakeup = NULL;
thread_add_timer(master, wakeup_connect_hanging, dmn,
gs.timeout, &dmn->t_wakeup);
SET_READ_HANDLER(dmn);
daemon_down(dmn, why);
} else {
gettimeofday(&dmn->echo_sent, NULL);
- dmn->t_wakeup = NULL;
thread_add_timer(master, wakeup_no_answer, dmn, gs.timeout,
&dmn->t_wakeup);
}
gs.numdaemons++;
gs.numdown++;
dmn->fd = -1;
- dmn->t_wakeup = NULL;
thread_add_timer_msec(master, wakeup_init, dmn, 0,
&dmn->t_wakeup);
dmn->restart.interval = gs.min_restart_interval;
else
unsetenv("FRR_PATHSPACE");
+ /*
+ * when watchfrr_di.pathspace is read, if it is not specified
+ * pathspace is NULL as expected
+ */
+ pathspace = watchfrr_di.pathspace;
+
if (netns_en && !netns)
netns = watchfrr_di.pathspace;
+
if (netns_en && netns && netns[0])
netns_setup(netns);
DECLARE_MGROUP(WATCHFRR);
+/*
+ * This is the name of the pathspace we are in `-N XXX`
+ * If the default then this is NULL
+ */
+extern const char *pathspace;
+
extern void watchfrr_vty_init(void);
extern pid_t integrated_write_pid;
/* don't allow the user to pass parameters, we're root here!
* should probably harden vtysh at some point too... */
- execl(VTYSH_BIN_PATH, "vtysh", "-w", NULL);
+ if (pathspace)
+ execl(VTYSH_BIN_PATH, "vtysh", "-N", pathspace, "-w", NULL);
+ else
+ execl(VTYSH_BIN_PATH, "vtysh", "-w", NULL);
/* unbuffered write; we just messed with stdout... */
char msg[512];
type union {
type inet:ip-address;
type string {
- pattern "";
+ length "0";
}
}
}
if (!ifc)
return;
- if (!if_is_loopback(ifp) && ifc->address->family == AF_INET &&
- !IS_ZEBRA_IF_VRF(ifp)) {
+ if (!if_is_loopback_or_vrf(ifp) && ifc->address->family == AF_INET) {
if (ifc->address->prefixlen == IPV4_MAX_BITLEN)
SET_FLAG(ifc->flags, ZEBRA_IFA_UNNUMBERED);
else
}
/* Add connected IPv4 route to the interface. */
-void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr,
- uint16_t prefixlen, struct in_addr *dest,
- const char *label, uint32_t metric)
+void connected_add_ipv4(struct interface *ifp, int flags,
+ const struct in_addr *addr, uint16_t prefixlen,
+ const struct in_addr *dest, const char *label,
+ uint32_t metric)
{
struct prefix_ipv4 *p;
struct connected *ifc;
/* Delete connected IPv4 route to the interface. */
void connected_delete_ipv4(struct interface *ifp, int flags,
- struct in_addr *addr, uint16_t prefixlen,
- struct in_addr *dest)
+ const struct in_addr *addr, uint16_t prefixlen,
+ const struct in_addr *dest)
{
struct prefix p, d;
struct connected *ifc;
}
/* Add connected IPv6 route to the interface. */
-void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
- struct in6_addr *dest, uint16_t prefixlen,
+void connected_add_ipv6(struct interface *ifp, int flags,
+ const struct in6_addr *addr,
+ const struct in6_addr *dest, uint16_t prefixlen,
const char *label, uint32_t metric)
{
struct prefix_ipv6 *p;
connected_update(ifp, ifc);
}
-void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address,
- struct in6_addr *dest, uint16_t prefixlen)
+void connected_delete_ipv6(struct interface *ifp,
+ const struct in6_addr *address,
+ const struct in6_addr *dest, uint16_t prefixlen)
{
struct prefix p, d;
struct connected *ifc;
union prefixconstptr d);
extern void connected_add_ipv4(struct interface *ifp, int flags,
- struct in_addr *addr, uint16_t prefixlen,
- struct in_addr *dest, const char *label,
+ const struct in_addr *addr, uint16_t prefixlen,
+ const struct in_addr *dest, const char *label,
uint32_t metric);
extern void connected_delete_ipv4(struct interface *ifp, int flags,
- struct in_addr *addr, uint16_t prefixlen,
- struct in_addr *dest);
+ const struct in_addr *addr,
+ uint16_t prefixlen,
+ const struct in_addr *dest);
extern void connected_delete_ipv4_unnumbered(struct connected *ifc);
extern void connected_down(struct interface *ifp, struct connected *ifc);
extern void connected_add_ipv6(struct interface *ifp, int flags,
- struct in6_addr *address, struct in6_addr *dest,
- uint16_t prefixlen, const char *label,
- uint32_t metric);
+ const struct in6_addr *address,
+ const struct in6_addr *dest, uint16_t prefixlen,
+ const char *label, uint32_t metric);
extern void connected_delete_ipv6(struct interface *ifp,
- struct in6_addr *address,
- struct in6_addr *dest, uint16_t prefixlen);
+ const struct in6_addr *address,
+ const struct in6_addr *dest,
+ uint16_t prefixlen);
extern int connected_is_unnumbered(struct interface *);
if (IS_ZEBRA_IF_BOND(ifp))
zebra_l2if_update_bond(ifp, true);
if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp))
- zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id);
+ zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id,
+ ZEBRA_BRIDGE_NO_ACTION);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp))
zebra_l2if_update_bond_slave(ifp, bond_ifindex, !!bypass);
if (ret < 0)
return ret;
ret = netlink_parse_info(netlink_interface, netlink_cmd, &dp_info, 0,
- 1);
+ true);
if (ret < 0)
return ret;
if (ret < 0)
return ret;
ret = netlink_parse_info(netlink_interface, netlink_cmd, &dp_info, 0,
- 0);
- if (ret < 0)
- return ret;
-
- /* Get interface information - for bridge interfaces. */
- ret = netlink_request_intf_addr(netlink_cmd, AF_BRIDGE, RTM_GETLINK,
- RTEXT_FILTER_BRVLAN);
- if (ret < 0)
- return ret;
- ret = netlink_parse_info(netlink_interface, netlink_cmd, &dp_info, 0,
- 0);
+ true);
if (ret < 0)
return ret;
if (ret < 0)
return ret;
ret = netlink_parse_info(netlink_interface_addr, netlink_cmd, &dp_info,
- 0, 1);
+ 0, true);
if (ret < 0)
return ret;
if (ret < 0)
return ret;
ret = netlink_parse_info(netlink_interface_addr, netlink_cmd, &dp_info,
- 0, 1);
+ 0, true);
if (ret < 0)
return ret;
nl_attr_put32(&req.n, sizeof(req), IFLA_LINK, slave->ifindex);
return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
- 0);
+ false);
}
/* Interface address modification. */
NULL, ifa->ifa_prefixlen);
}
-
/*
* Linux kernel does not send route delete on interface down/addr del
* so we have to re-process routes it owns (i.e. kernel routes)
return 0;
}
+/*
+ * Parse and validate an incoming interface address change message,
+ * generating a dplane context object.
+ * This runs in the dplane pthread; the context is enqueued to the
+ * main pthread for processing.
+ */
+int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup /*ignored*/)
+{
+ int len;
+ struct ifaddrmsg *ifa;
+ struct rtattr *tb[IFA_MAX + 1];
+ void *addr;
+ void *broad;
+ char *label = NULL;
+ uint32_t metric = METRIC_MAX;
+ uint32_t kernel_flags = 0;
+ struct zebra_dplane_ctx *ctx;
+ struct prefix p;
+
+ ifa = NLMSG_DATA(h);
+
+ /* Validate message types */
+ if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR)
+ return 0;
+
+ if (ifa->ifa_family != AF_INET && ifa->ifa_family != AF_INET6) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: Invalid address family: %u",
+ __func__, nl_msg_type_to_str(h->nlmsg_type),
+ ifa->ifa_family);
+ return 0;
+ }
+
+ len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ if (len < 0) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: netlink msg bad size: %d %zu",
+ __func__, nl_msg_type_to_str(h->nlmsg_type),
+ h->nlmsg_len,
+ (size_t)NLMSG_LENGTH(
+ sizeof(struct ifaddrmsg)));
+ return -1;
+ }
+
+ netlink_parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), len);
+
+ /* Flags passed through */
+ if (tb[IFA_FLAGS])
+ kernel_flags = *(int *)RTA_DATA(tb[IFA_FLAGS]);
+ else
+ kernel_flags = ifa->ifa_flags;
+
+ if (IS_ZEBRA_DEBUG_KERNEL) { /* remove this line to see initial ifcfg */
+ char buf[PREFIX_STRLEN];
+
+ zlog_debug("%s: %s nsid %u ifindex %u flags 0x%x:", __func__,
+ nl_msg_type_to_str(h->nlmsg_type), ns_id,
+ ifa->ifa_index, kernel_flags);
+ if (tb[IFA_LOCAL])
+ zlog_debug(" IFA_LOCAL %s/%d",
+ inet_ntop(ifa->ifa_family,
+ RTA_DATA(tb[IFA_LOCAL]), buf,
+ sizeof(buf)),
+ ifa->ifa_prefixlen);
+ if (tb[IFA_ADDRESS])
+ zlog_debug(" IFA_ADDRESS %s/%d",
+ inet_ntop(ifa->ifa_family,
+ RTA_DATA(tb[IFA_ADDRESS]), buf,
+ sizeof(buf)),
+ ifa->ifa_prefixlen);
+ if (tb[IFA_BROADCAST])
+ zlog_debug(" IFA_BROADCAST %s/%d",
+ inet_ntop(ifa->ifa_family,
+ RTA_DATA(tb[IFA_BROADCAST]), buf,
+ sizeof(buf)),
+ ifa->ifa_prefixlen);
+ if (tb[IFA_LABEL])
+ zlog_debug(" IFA_LABEL %s",
+ (const char *)RTA_DATA(tb[IFA_LABEL]));
+
+ if (tb[IFA_CACHEINFO]) {
+ struct ifa_cacheinfo *ci = RTA_DATA(tb[IFA_CACHEINFO]);
+
+ zlog_debug(" IFA_CACHEINFO pref %d, valid %d",
+ ci->ifa_prefered, ci->ifa_valid);
+ }
+ }
+
+ /* Validate prefix length */
+
+ if (ifa->ifa_family == AF_INET
+ && ifa->ifa_prefixlen > IPV4_MAX_BITLEN) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: Invalid prefix length: %u",
+ __func__, nl_msg_type_to_str(h->nlmsg_type),
+ ifa->ifa_prefixlen);
+ return -1;
+ }
+
+ if (ifa->ifa_family == AF_INET6) {
+ if (ifa->ifa_prefixlen > IPV6_MAX_BITLEN) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: Invalid prefix length: %u",
+ __func__,
+ nl_msg_type_to_str(h->nlmsg_type),
+ ifa->ifa_prefixlen);
+ return -1;
+ }
+
+ /* Only consider valid addresses; we'll not get a kernel
+ * notification till IPv6 DAD has completed, but at init
+ * time, FRR does query for and will receive all addresses.
+ */
+ if (h->nlmsg_type == RTM_NEWADDR
+ && (kernel_flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE))) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: Invalid/tentative addr",
+ __func__,
+ nl_msg_type_to_str(h->nlmsg_type));
+ return 0;
+ }
+ }
+
+ /* logic copied from iproute2/ip/ipaddress.c:print_addrinfo() */
+ if (tb[IFA_LOCAL] == NULL)
+ tb[IFA_LOCAL] = tb[IFA_ADDRESS];
+ if (tb[IFA_ADDRESS] == NULL)
+ tb[IFA_ADDRESS] = tb[IFA_LOCAL];
+
+ /* local interface address */
+ addr = (tb[IFA_LOCAL] ? RTA_DATA(tb[IFA_LOCAL]) : NULL);
+
+ /* addr is primary key, SOL if we don't have one */
+ if (addr == NULL) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: No local interface address",
+ __func__, nl_msg_type_to_str(h->nlmsg_type));
+ return -1;
+ }
+
+ /* Allocate a context object, now that validation is done. */
+ ctx = dplane_ctx_alloc();
+ if (h->nlmsg_type == RTM_NEWADDR)
+ dplane_ctx_set_op(ctx, DPLANE_OP_INTF_ADDR_ADD);
+ else
+ dplane_ctx_set_op(ctx, DPLANE_OP_INTF_ADDR_DEL);
+
+ dplane_ctx_set_ifindex(ctx, ifa->ifa_index);
+ dplane_ctx_set_ns_id(ctx, ns_id);
+
+ /* Convert addr to prefix */
+ memset(&p, 0, sizeof(p));
+ p.family = ifa->ifa_family;
+ p.prefixlen = ifa->ifa_prefixlen;
+ if (p.family == AF_INET)
+ p.u.prefix4 = *(struct in_addr *)addr;
+ else
+ p.u.prefix6 = *(struct in6_addr *)addr;
+
+ dplane_ctx_set_intf_addr(ctx, &p);
+
+ /* is there a peer address? */
+ if (tb[IFA_ADDRESS]
+ && memcmp(RTA_DATA(tb[IFA_ADDRESS]), RTA_DATA(tb[IFA_LOCAL]),
+ RTA_PAYLOAD(tb[IFA_ADDRESS]))) {
+ broad = RTA_DATA(tb[IFA_ADDRESS]);
+ dplane_ctx_intf_set_connected(ctx);
+ } else if (tb[IFA_BROADCAST]) {
+ /* seeking a broadcast address */
+ broad = RTA_DATA(tb[IFA_BROADCAST]);
+ dplane_ctx_intf_set_broadcast(ctx);
+ } else
+ broad = NULL;
+
+ if (broad) {
+ /* Convert addr to prefix */
+ memset(&p, 0, sizeof(p));
+ p.family = ifa->ifa_family;
+ p.prefixlen = ifa->ifa_prefixlen;
+ if (p.family == AF_INET)
+ p.u.prefix4 = *(struct in_addr *)broad;
+ else
+ p.u.prefix6 = *(struct in6_addr *)broad;
+
+ dplane_ctx_set_intf_dest(ctx, &p);
+ }
+
+ /* Flags. */
+ if (kernel_flags & IFA_F_SECONDARY)
+ dplane_ctx_intf_set_secondary(ctx);
+
+ /* Label */
+ if (tb[IFA_LABEL]) {
+ label = (char *)RTA_DATA(tb[IFA_LABEL]);
+ dplane_ctx_set_intf_label(ctx, label);
+ }
+
+ if (tb[IFA_RT_PRIORITY])
+ metric = *(uint32_t *)RTA_DATA(tb[IFA_RT_PRIORITY]);
+
+ dplane_ctx_set_intf_metric(ctx, metric);
+
+ /* Enqueue ctx for main pthread to process */
+ dplane_provider_enqueue_to_zebra(ctx);
+
+ return 0;
+}
+
int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
int len;
ifp, linkinfo[IFLA_INFO_DATA],
1, link_nsid);
if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp))
- zebra_l2if_update_bridge_slave(ifp,
- bridge_ifindex,
- ns_id);
+ zebra_l2if_update_bridge_slave(
+ ifp, bridge_ifindex, ns_id,
+ ZEBRA_BRIDGE_NO_ACTION);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp))
zebra_l2if_update_bond_slave(ifp, bond_ifindex,
!!bypass);
if_handle_vrf_change(ifp, vrf_id);
} else {
bool was_bridge_slave, was_bond_slave;
+ uint8_t chgflags = ZEBRA_BRIDGE_NO_ACTION;
/* Interface update. */
if (IS_ZEBRA_DEBUG_KERNEL)
if_down(ifp);
rib_update(RIB_UPDATE_KERNEL);
} else if (if_is_operative(ifp)) {
+ bool mac_updated = false;
+
/* Must notify client daemons of new
* interface status. */
if (IS_ZEBRA_DEBUG_KERNEL)
/* Update EVPN VNI when SVI MAC change
*/
- if (IS_ZEBRA_IF_VLAN(ifp) &&
- memcmp(old_hw_addr, ifp->hw_addr,
- INTERFACE_HWADDR_MAX)) {
+ if (memcmp(old_hw_addr, ifp->hw_addr,
+ INTERFACE_HWADDR_MAX))
+ mac_updated = true;
+ if (IS_ZEBRA_IF_VLAN(ifp)
+ && mac_updated) {
struct interface *link_if;
link_if =
if (link_if)
zebra_vxlan_svi_up(ifp,
link_if);
+ } else if (mac_updated
+ && IS_ZEBRA_IF_BRIDGE(ifp)) {
+ zlog_debug(
+ "Intf %s(%u) bridge changed MAC address",
+ name, ifp->ifindex);
+ chgflags =
+ ZEBRA_BRIDGE_MASTER_MAC_CHANGE;
}
}
} else {
"Intf %s(%u) has come UP",
name, ifp->ifindex);
if_up(ifp);
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ chgflags =
+ ZEBRA_BRIDGE_MASTER_UP;
} else {
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
netlink_interface_update_l2info(
ifp, linkinfo[IFLA_INFO_DATA],
0, link_nsid);
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ zebra_l2if_update_bridge(ifp, chgflags);
if (IS_ZEBRA_IF_BOND(ifp))
zebra_l2if_update_bond(ifp, true);
if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave)
- zebra_l2if_update_bridge_slave(ifp,
- bridge_ifindex,
- ns_id);
+ zebra_l2if_update_bridge_slave(
+ ifp, bridge_ifindex, ns_id, chgflags);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave)
zebra_l2if_update_bond_slave(ifp, bond_ifindex,
!!bypass);
nl_attr_put32(&req.n, sizeof(req), IFLA_LINK, ifp->ifindex);
return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
- 0);
+ false);
}
/* Interface information read by netlink. */
extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id,
int startup);
+
+/*
+ * Parse an incoming interface address change message, generate a dplane
+ * context object for processing.
+ */
+int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup);
+
extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int interface_lookup_netlink(struct zebra_ns *zns);
#endif
}
+/*
+ * Handle an interface addr event based on info in a dplane context object.
+ * This runs in the main pthread, using the info in the context object to
+ * modify an interface.
+ */
+void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx)
+{
+ struct interface *ifp;
+ uint8_t flags = 0;
+ const char *label = NULL;
+ ns_id_t ns_id;
+ struct zebra_ns *zns;
+ uint32_t metric = METRIC_MAX;
+ ifindex_t ifindex;
+ const struct prefix *addr, *dest = NULL;
+ enum dplane_op_e op;
+
+ op = dplane_ctx_get_op(ctx);
+ ns_id = dplane_ctx_get_ns_id(ctx);
+
+ zns = zebra_ns_lookup(ns_id);
+ if (zns == NULL) {
+ /* No ns - deleted maybe? */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: can't find zns id %u", __func__, ns_id);
+ goto done;
+ }
+
+ ifindex = dplane_ctx_get_ifindex(ctx);
+
+ ifp = if_lookup_by_index_per_ns(zns, ifindex);
+ if (ifp == NULL) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: can't find ifp at nsid %u index %d",
+ __func__, ns_id, ifindex);
+ goto done;
+ }
+
+ addr = dplane_ctx_get_intf_addr(ctx);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: ifindex %u, addr %pFX", __func__,
+ dplane_op2str(op), ifindex, addr);
+
+ /* Is there a peer or broadcast address? */
+ dest = dplane_ctx_get_intf_dest(ctx);
+ if (dest->prefixlen == 0)
+ dest = NULL;
+
+ if (dplane_ctx_intf_is_connected(ctx))
+ SET_FLAG(flags, ZEBRA_IFA_PEER);
+
+ /* Flags. */
+ if (dplane_ctx_intf_is_secondary(ctx))
+ SET_FLAG(flags, ZEBRA_IFA_SECONDARY);
+
+ /* Label? */
+ if (dplane_ctx_intf_has_label(ctx))
+ label = dplane_ctx_get_intf_label(ctx);
+
+ if (label && strcmp(ifp->name, label) == 0)
+ label = NULL;
+
+ metric = dplane_ctx_get_intf_metric(ctx);
+
+ /* Register interface address to the interface. */
+ if (addr->family == AF_INET) {
+ if (op == DPLANE_OP_INTF_ADDR_ADD)
+ connected_add_ipv4(
+ ifp, flags, &addr->u.prefix4, addr->prefixlen,
+ dest ? &dest->u.prefix4 : NULL, label, metric);
+ else if (CHECK_FLAG(flags, ZEBRA_IFA_PEER)) {
+ /* Delete with a peer address */
+ connected_delete_ipv4(ifp, flags, &addr->u.prefix4,
+ addr->prefixlen,
+ &dest->u.prefix4);
+ } else
+ connected_delete_ipv4(ifp, flags, &addr->u.prefix4,
+ addr->prefixlen, NULL);
+ }
+
+ if (addr->family == AF_INET6) {
+ if (op == DPLANE_OP_INTF_ADDR_ADD) {
+ connected_add_ipv6(ifp, flags, &addr->u.prefix6,
+ dest ? &dest->u.prefix6 : NULL,
+ addr->prefixlen, label, metric);
+ } else
+ connected_delete_ipv6(ifp, &addr->u.prefix6, NULL,
+ addr->prefixlen);
+ }
+
+ /*
+ * Linux kernel does not send route delete on interface down/addr del
+ * so we have to re-process routes it owns (i.e. kernel routes)
+ */
+ if (op != DPLANE_OP_INTF_ADDR_ADD)
+ rib_update(RIB_UPDATE_KERNEL);
+
+done:
+ /* We're responsible for the ctx object */
+ dplane_ctx_fini(&ctx);
+}
+
/* Dump if address information to vty. */
static void connected_dump_vty(struct vty *vty, json_object *json,
struct connected *connected)
extern void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif);
extern const char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc,
char *pd_buf, uint32_t pd_buf_len);
+void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx);
#ifdef HAVE_PROC_NET_DEV
extern void ifstat_update_proc(void);
return ret;
};
- t_irdp_raw = NULL;
thread_add_read(zrouter.master, irdp_read_raw, NULL, sock, &t_irdp_raw);
return sock;
int ret, ifindex = 0;
int irdp_sock = THREAD_FD(r);
- t_irdp_raw = NULL;
thread_add_read(zrouter.master, irdp_read_raw, NULL, irdp_sock,
&t_irdp_raw);
return ret;
}
+/*
+ * Dispatch an incoming netlink message; used by the zebra main pthread's
+ * netlink event reader.
+ */
static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
int startup)
{
return netlink_link_change(h, ns_id, startup);
case RTM_DELLINK:
return netlink_link_change(h, ns_id, startup);
- case RTM_NEWADDR:
- return netlink_interface_addr(h, ns_id, startup);
- case RTM_DELADDR:
- return netlink_interface_addr(h, ns_id, startup);
case RTM_NEWNEIGH:
case RTM_DELNEIGH:
case RTM_GETNEIGH:
return netlink_nexthop_change(h, ns_id, startup);
case RTM_DELNEXTHOP:
return netlink_nexthop_change(h, ns_id, startup);
+
+ /* Messages handled in the dplane thread */
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ return 0;
+
default:
/*
* If we have received this message then
return 0;
}
+/*
+ * Dispatch an incoming netlink message; used by the dataplane pthread's
+ * netlink event reader code.
+ */
+static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup)
+{
+ /*
+ * Dispatch the incoming messages that the dplane pthread handles
+ */
+ switch (h->nlmsg_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ return netlink_interface_addr_dplane(h, ns_id, startup);
+
+ /* TODO */
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int kernel_read(struct thread *thread)
{
struct zebra_ns *zns = (struct zebra_ns *)THREAD_ARG(thread);
zebra_dplane_info_from_zns(&dp_info, zns, false);
netlink_parse_info(netlink_information_fetch, &zns->netlink, &dp_info,
- 5, 0);
- zns->t_netlink = NULL;
+ 5, false);
+
thread_add_read(zrouter.master, kernel_read, zns, zns->netlink.sock,
&zns->t_netlink);
return 0;
}
+/*
+ * Called by the dplane pthread to read incoming OS messages and dispatch them.
+ */
+int kernel_dplane_read(struct zebra_dplane_info *info)
+{
+ netlink_parse_info(dplane_netlink_information_fetch, &info->nls, info,
+ 5, false);
+
+ return 0;
+}
+
/*
* Filter out messages from self that occur on listener socket,
* caused by our actions on the command socket(s)
* so that we only had to write one way to handle incoming
* address add/delete changes.
*/
-static void netlink_install_filter(int sock, __u32 pid, __u32 dplane_pid)
+static void netlink_install_filter(int sock, uint32_t pid, uint32_t dplane_pid)
{
/*
* BPF_JUMP instructions and where you jump to are based upon
safe_strerror(errno));
}
-void netlink_parse_rtattr_flags(struct rtattr **tb, int max,
- struct rtattr *rta, int len, unsigned short flags)
+void netlink_parse_rtattr_flags(struct rtattr **tb, int max, struct rtattr *rta,
+ int len, unsigned short flags)
{
unsigned short type;
* ignored, -1 otherwise.
*/
static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h,
- const struct zebra_dplane_info *zns,
- bool startup)
+ bool is_cmd, bool startup)
{
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
int errnum = err->error;
}
/* Deal with errors that occur because of races in link handling. */
- if (zns->is_cmd
+ if (is_cmd
&& ((msg_type == RTM_DELROUTE
&& (-errnum == ENODEV || -errnum == ESRCH))
|| (msg_type == RTM_NEWROUTE
* do not log these as an error.
*/
if (msg_type == RTM_DELNEIGH
- || (zns->is_cmd && msg_type == RTM_NEWROUTE
+ || (is_cmd && msg_type == RTM_NEWROUTE
&& (-errnum == ESRCH || -errnum == ENETUNREACH))) {
/*
* This is known to happen in some situations, don't log as
int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
const struct nlsock *nl,
const struct zebra_dplane_info *zns,
- int count, int startup)
+ int count, bool startup)
{
int status;
int ret = 0;
/* Error handling. */
if (h->nlmsg_type == NLMSG_ERROR) {
- int err = netlink_parse_error(nl, h, zns,
- startup);
+ int err = netlink_parse_error(
+ nl, h, zns->is_cmd, startup);
+
if (err == 1) {
if (!(h->nlmsg_flags & NLM_F_MULTI))
return 0;
/* OK we got netlink message. */
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- "netlink_parse_info: %s type %s(%u), len=%d, seq=%u, pid=%u",
- nl->name,
+ "%s: %s type %s(%u), len=%d, seq=%u, pid=%u",
+ __func__, nl->name,
nl_msg_type_to_str(h->nlmsg_type),
h->nlmsg_type, h->nlmsg_len,
h->nlmsg_seq, h->nlmsg_pid);
static int
netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
struct nlmsghdr *n, const struct zebra_dplane_info *dp_info,
- int startup)
+ bool startup)
{
const struct nlsock *nl;
*/
int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns,
- int startup)
+ bool startup)
{
struct zebra_dplane_info dp_info;
}
if (h->nlmsg_type == NLMSG_ERROR) {
- int err = netlink_parse_error(nl, h, bth->zns, 0);
+ int err = netlink_parse_error(nl, h, bth->zns->is_cmd,
+ false);
if (err == -1)
dplane_ctx_set_status(
case DPLANE_OP_GRE_SET:
return netlink_put_gre_set_msg(bth, ctx);
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
case DPLANE_OP_NONE:
return FRR_NETLINK_ERROR;
}
exit(-1);
}
- snprintf(zns->netlink_dplane.name, sizeof(zns->netlink_dplane.name),
- "netlink-dp (NS %u)", zns->ns_id);
- zns->netlink_dplane.sock = -1;
- if (netlink_socket(&zns->netlink_dplane, 0, zns->ns_id) < 0) {
+ /* Outbound socket for dplane programming of the host OS. */
+ snprintf(zns->netlink_dplane_out.name,
+ sizeof(zns->netlink_dplane_out.name), "netlink-dp (NS %u)",
+ zns->ns_id);
+ zns->netlink_dplane_out.sock = -1;
+ if (netlink_socket(&zns->netlink_dplane_out, 0, zns->ns_id) < 0) {
zlog_err("Failure to create %s socket",
- zns->netlink_dplane.name);
+ zns->netlink_dplane_out.name);
+ exit(-1);
+ }
+
+ /* Inbound socket for OS events coming to the dplane. */
+ snprintf(zns->netlink_dplane_in.name,
+ sizeof(zns->netlink_dplane_in.name), "netlink-dp-in (NS %u)",
+ zns->ns_id);
+ zns->netlink_dplane_in.sock = -1;
+ if (netlink_socket(&zns->netlink_dplane_in, groups, zns->ns_id) < 0) {
+ zlog_err("Failure to create %s socket",
+ zns->netlink_dplane_in.name);
exit(-1);
}
errno, safe_strerror(errno));
one = 1;
- ret = setsockopt(zns->netlink_dplane.sock, SOL_NETLINK, NETLINK_EXT_ACK,
- &one, sizeof(one));
+ ret = setsockopt(zns->netlink_dplane_out.sock, SOL_NETLINK,
+ NETLINK_EXT_ACK, &one, sizeof(one));
if (ret < 0)
zlog_notice("Registration for extended dp ACK failed : %d %s",
* setsockopt fails, ignore the error.
*/
one = 1;
- ret = setsockopt(zns->netlink_dplane.sock, SOL_NETLINK, NETLINK_CAP_ACK,
- &one, sizeof(one));
+ ret = setsockopt(zns->netlink_dplane_out.sock, SOL_NETLINK,
+ NETLINK_CAP_ACK, &one, sizeof(one));
if (ret < 0)
zlog_notice(
"Registration for reduced ACK packet size failed, probably running an early kernel");
zlog_err("Can't set %s socket error: %s(%d)",
zns->netlink_cmd.name, safe_strerror(errno), errno);
- if (fcntl(zns->netlink_dplane.sock, F_SETFL, O_NONBLOCK) < 0)
+ if (fcntl(zns->netlink_dplane_out.sock, F_SETFL, O_NONBLOCK) < 0)
zlog_err("Can't set %s socket error: %s(%d)",
- zns->netlink_dplane.name, safe_strerror(errno), errno);
+ zns->netlink_dplane_out.name, safe_strerror(errno),
+ errno);
+
+ if (fcntl(zns->netlink_dplane_in.sock, F_SETFL, O_NONBLOCK) < 0)
+ zlog_err("Can't set %s socket error: %s(%d)",
+ zns->netlink_dplane_in.name, safe_strerror(errno),
+ errno);
/* Set receive buffer size if it's set from command line */
if (nl_rcvbufsize) {
netlink_recvbuf(&zns->netlink, nl_rcvbufsize);
netlink_recvbuf(&zns->netlink_cmd, nl_rcvbufsize);
- netlink_recvbuf(&zns->netlink_dplane, nl_rcvbufsize);
+ netlink_recvbuf(&zns->netlink_dplane_out, nl_rcvbufsize);
+ netlink_recvbuf(&zns->netlink_dplane_in, nl_rcvbufsize);
}
- netlink_install_filter(zns->netlink.sock,
+ /* Set filter for inbound sockets, to exclude events we've generated
+ * ourselves.
+ */
+ netlink_install_filter(zns->netlink.sock, zns->netlink_cmd.snl.nl_pid,
+ zns->netlink_dplane_out.snl.nl_pid);
+
+ netlink_install_filter(zns->netlink_dplane_in.sock,
zns->netlink_cmd.snl.nl_pid,
- zns->netlink_dplane.snl.nl_pid);
+ zns->netlink_dplane_out.snl.nl_pid);
zns->t_netlink = NULL;
zns->netlink_cmd.sock = -1;
}
+ if (zns->netlink_dplane_in.sock >= 0) {
+ close(zns->netlink_dplane_in.sock);
+ zns->netlink_dplane_in.sock = -1;
+ }
+
/* During zebra shutdown, we need to leave the dataplane socket
* around until all work is done.
*/
if (complete) {
- if (zns->netlink_dplane.sock >= 0) {
- close(zns->netlink_dplane.sock);
- zns->netlink_dplane.sock = -1;
+ if (zns->netlink_dplane_out.sock >= 0) {
+ close(zns->netlink_dplane_out.sock);
+ zns->netlink_dplane_out.sock = -1;
}
}
}
extern int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
const struct nlsock *nl,
const struct zebra_dplane_info *dp_info,
- int count, int startup);
+ int count, bool startup);
extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup);
extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
struct nlmsghdr *n, struct nlsock *nl,
- struct zebra_ns *zns, int startup);
+ struct zebra_ns *zns, bool startup);
extern int netlink_request(struct nlsock *nl, void *req);
enum netlink_msg_status {
/* paranoia: sanity check structure */
if (ifm->ifm_msglen < sizeof(struct if_msghdr)) {
flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
- "ifm_read: ifm->ifm_msglen %d too short\n",
+ "ifm_read: ifm->ifm_msglen %d too short",
ifm->ifm_msglen);
return -1;
}
switch (sockunion_family(addr)) {
case AF_INET:
case AF_INET6: {
- char buf[4][INET6_ADDRSTRLEN];
int masklen =
(sockunion_family(addr) == AF_INET)
? ip_masklen(mask->sin.sin_addr)
: ip6_masklen(mask->sin6.sin6_addr);
zlog_debug(
- "%s: ifindex %d, ifname %s, ifam_addrs {%s}, ifam_flags 0x%x, addr %s/%d broad %s dst %s gateway %s",
+ "%s: ifindex %d, ifname %s, ifam_addrs {%s}, ifam_flags 0x%x, addr %pSU/%d broad %pSU dst %pSU gateway %pSU",
__func__, ifm->ifam_index,
(ifnlen ? ifname : "(nil)"),
rtatostr(ifm->ifam_addrs, fbuf, sizeof(fbuf)),
- ifm->ifam_flags,
- sockunion2str(addr, buf[0], sizeof(buf[0])),
- masklen,
- sockunion2str(brd, buf[1], sizeof(buf[1])),
- sockunion2str(&dst, buf[2], sizeof(buf[2])),
- sockunion2str(&gateway, buf[2],
- sizeof(buf[2])));
+ ifm->ifam_flags, addr, masklen, brd, &dst,
+ &gateway);
} break;
default:
zlog_debug("%s: ifindex %d, ifname %s, ifam_addrs {%s}",
/* rt_msghdr version check. */
if (rtm->rtm_version != RTM_VERSION)
flog_warn(EC_ZEBRA_RTM_VERSION_MISMATCH,
- "Routing message version different %d should be %d.This may cause problem\n",
+ "Routing message version different %d should be %d.This may cause problem",
rtm->rtm_version, RTM_VERSION);
/* Be sure structure is cleared */
return;
}
+/*
+ * Called by the dplane pthread to read incoming OS messages and dispatch them.
+ */
+int kernel_dplane_read(struct zebra_dplane_info *info)
+{
+ return 0;
+}
+
void kernel_update_multi(struct dplane_ctx_q *ctx_list)
{
struct zebra_dplane_ctx *ctx;
*/
extern void kernel_update_multi(struct dplane_ctx_q *ctx_list);
+/*
+ * Called by the dplane pthread to read incoming OS messages and dispatch them.
+ */
+int kernel_dplane_read(struct zebra_dplane_info *info);
+
#ifdef __cplusplus
}
#endif
if (ret < 0)
return ret;
ret = netlink_parse_info(netlink_route_change_read_unicast,
- &zns->netlink_cmd, &dp_info, 0, 1);
+ &zns->netlink_cmd, &dp_info, 0, true);
if (ret < 0)
return ret;
if (ret < 0)
return ret;
ret = netlink_parse_info(netlink_route_change_read_unicast,
- &zns->netlink_cmd, &dp_info, 0, 1);
+ &zns->netlink_cmd, &dp_info, 0, true);
if (ret < 0)
return ret;
nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, lla, llalen);
return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
- 0);
+ false);
}
static bool nexthop_set_src(const struct nexthop *nexthop, int family,
nl_attr_put32(&req.n, sizeof(req), RTA_TABLE, actual_table);
suc = netlink_talk(netlink_route_change_read_multicast, &req.n,
- &zns->netlink_cmd, zns, 0);
+ &zns->netlink_cmd, zns, false);
mroute = NULL;
return suc;
if (ret < 0)
return ret;
ret = netlink_parse_info(netlink_nexthop_change, &zns->netlink_cmd,
- &dp_info, 0, 1);
+ &dp_info, 0, true);
if (!ret)
/* If we succesfully read in nexthop objects,
/* We are reading entire table. */
filter_vlan = 0;
ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd,
- &dp_info, 0, 1);
+ &dp_info, 0, true);
return ret;
}
if (ret < 0)
return ret;
ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd,
- &dp_info, 0, 0);
+ &dp_info, 0, false);
/* Reset VLAN filter. */
filter_vlan = 0;
return ret;
ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd,
- &dp_info, 1, 0);
+ &dp_info, 1, false);
return ret;
}
if (ret < 0)
return ret;
ret = netlink_parse_info(netlink_neigh_table, &zns->netlink_cmd,
- &dp_info, 0, 1);
+ &dp_info, 0, true);
return ret;
}
if (ret < 0)
return ret;
ret = netlink_parse_info(netlink_neigh_table, &zns->netlink_cmd,
- &dp_info, 0, 0);
+ &dp_info, 0, false);
return ret;
}
return ret;
ret = netlink_parse_info(netlink_neigh_table, &zns->netlink_cmd,
- &dp_info, 1, 0);
+ &dp_info, 1, false);
return ret;
}
}
return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
- 0);
+ false);
}
static int netlink_fdb_nh_del(uint32_t nh_id)
}
return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
- 0);
+ false);
}
static int netlink_fdb_nhg_update(uint32_t nhg_id, uint32_t nh_cnt,
}
return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
- 0);
+ false);
}
static int netlink_fdb_nhg_del(uint32_t nhg_id)
RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id)
FOR_ALL_INTERFACES (vrf, ifp) {
- if (if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status,
- ZEBRA_INTERFACE_VRF_LOOPBACK)
+ if (if_is_loopback_or_vrf(ifp)
|| !if_is_operative(ifp))
continue;
VRF_LOGNAME(vrf), ifp->ifindex, len, addr_str);
}
- if (if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK))
+ if (if_is_loopback_or_vrf(ifp))
return;
/* Check interface configuration. */
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
- if (if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) {
+ if (if_is_loopback_or_vrf(ifp)) {
vty_out(vty,
"Cannot configure IPv6 Router Advertisements on this interface\n");
return CMD_WARNING_CONFIG_FAILED;
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
- if (if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) {
+ if (if_is_loopback_or_vrf(ifp)) {
vty_out(vty,
"Cannot configure IPv6 Router Advertisements on this interface\n");
return CMD_WARNING_CONFIG_FAILED;
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
- if (if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) {
+ if (if_is_loopback_or_vrf(ifp)) {
vty_out(vty,
"Cannot configure IPv6 Router Advertisements on this interface\n");
return CMD_WARNING_CONFIG_FAILED;
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
- if (if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) {
+ if (if_is_loopback_or_vrf(ifp)) {
vty_out(vty,
"Cannot configure IPv6 Router Advertisements on this interface\n");
return CMD_WARNING_CONFIG_FAILED;
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
- if (if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) {
+ if (if_is_loopback_or_vrf(ifp)) {
vty_out(vty,
"Cannot configure IPv6 Router Advertisements on loopback interface\n");
return CMD_WARNING_CONFIG_FAILED;
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
- if (if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) {
+ if (if_is_loopback_or_vrf(ifp)) {
vty_out(vty,
"Cannot remove IPv6 Router Advertisements on loopback interface\n");
return CMD_WARNING_CONFIG_FAILED;
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
- if (if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) {
+ if (if_is_loopback_or_vrf(ifp)) {
vty_out(vty,
"Cannot configure IPv6 Router Advertisements on this interface\n");
return CMD_WARNING_CONFIG_FAILED;
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
- if (if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) {
+ if (if_is_loopback_or_vrf(ifp)) {
vty_out(vty,
"Cannot configure IPv6 Router Advertisements on this interface\n");
return CMD_WARNING_CONFIG_FAILED;
zif = ifp->info;
- if (!(if_is_loopback(ifp)
- || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK))) {
+ if (!if_is_loopback_or_vrf(ifp)) {
if (zif->rtadv.AdvSendAdvertisements
&& CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED))
vty_out(vty, " no ipv6 nd suppress-ra\n");
return ret;
ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd,
- &dp_info, 0, 1);
+ &dp_info, 0, true);
if (ret < 0)
return ret;
return ret;
ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd,
- &dp_info, 0, 1);
+ &dp_info, 0, true);
return ret;
}
return zserv_send_message(client, s);
}
+/* SRv6 locator add notification from zebra daemon. */
+int zsend_zebra_srv6_locator_add(struct zserv *client, struct srv6_locator *loc)
+{
+ struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_SRV6_LOCATOR_ADD, VRF_DEFAULT);
+ zapi_srv6_locator_encode(s, loc);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zserv_send_message(client, s);
+}
+
+/* SRv6 locator delete notification from zebra daemon. */
+int zsend_zebra_srv6_locator_delete(struct zserv *client,
+ struct srv6_locator *loc)
+{
+ struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_SRV6_LOCATOR_DELETE, VRF_DEFAULT);
+ zapi_srv6_locator_encode(s, loc);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zserv_send_message(client, s);
+}
+
/* Inbound message handling ------------------------------------------------ */
const int cmd2type[] = {
zlog_debug("%s: nh blackhole %d",
__func__, api_nh->bh_type);
- nexthop = nexthop_from_blackhole(api_nh->bh_type);
+ nexthop =
+ nexthop_from_blackhole(api_nh->bh_type, api_nh->vrf_id);
break;
}
#include "zebra/zebra_pbr.h"
#include "printfrr.h"
-/* Memory type for context blocks */
+/* Memory types */
DEFINE_MTYPE_STATIC(ZEBRA, DP_CTX, "Zebra DPlane Ctx");
DEFINE_MTYPE_STATIC(ZEBRA, DP_INTF, "Zebra DPlane Intf");
DEFINE_MTYPE_STATIC(ZEBRA, DP_PROV, "Zebra DPlane Provider");
DEFINE_MTYPE_STATIC(ZEBRA, DP_NETFILTER, "Zebra Netfilter Internal Object");
+DEFINE_MTYPE_STATIC(ZEBRA, DP_NS, "DPlane NSes");
#ifndef AOK
# define AOK 0
TAILQ_ENTRY(zebra_dplane_provider) dp_prov_link;
};
+/* Declare types for list of zns info objects */
+PREDECL_DLIST(zns_info_list);
+
+struct dplane_zns_info {
+ struct zebra_dplane_info info;
+
+ /* Read event */
+ struct thread *t_read;
+
+ /* List linkage */
+ struct zns_info_list_item link;
+};
+
/*
* Globals
*/
/* Ordered list of providers */
TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider) dg_providers_q;
+ /* List of info about each zns */
+ struct zns_info_list_head dg_zns_list;
+
/* Counter used to assign internal ids to providers */
uint32_t dg_provider_id;
} zdplane_info;
+/* Instantiate zns list type */
+DECLARE_DLIST(zns_info_list, struct dplane_zns_info, link);
+
/*
* Lock and unlock for interactions with the zebra 'core' pthread
*/
case DPLANE_OP_ADDR_INSTALL:
case DPLANE_OP_ADDR_UNINSTALL:
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
/* Maybe free label string, if allocated */
if (ctx->u.intf.label != NULL &&
ctx->u.intf.label != ctx->u.intf.label_buf) {
case DPLANE_OP_GRE_SET:
ret = "GRE_SET";
break;
+
+ case DPLANE_OP_INTF_ADDR_ADD:
+ return "INTF_ADDR_ADD";
+
+ case DPLANE_OP_INTF_ADDR_DEL:
+ return "INTF_ADDR_DEL";
}
return ret;
return ctx->zd_vrf_id;
}
+/* In some paths we have only a namespace id */
+void dplane_ctx_set_ns_id(struct zebra_dplane_ctx *ctx, ns_id_t nsid)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->zd_ns_info.ns_id = nsid;
+}
+
+ns_id_t dplane_ctx_get_ns_id(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->zd_ns_info.ns_id;
+}
+
bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->zd_ifindex;
}
+void dplane_ctx_set_ifindex(struct zebra_dplane_ctx *ctx, ifindex_t ifindex)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->zd_ifindex = ifindex;
+}
+
void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.intf.metric;
}
+void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.intf.metric = metric;
+}
+
/* Is interface addr p2p? */
bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx)
{
return (ctx->u.intf.flags & DPLANE_INTF_BROADCAST);
}
+void dplane_ctx_intf_set_connected(struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.intf.flags |= DPLANE_INTF_CONNECTED;
+}
+
+void dplane_ctx_intf_set_secondary(struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.intf.flags |= DPLANE_INTF_SECONDARY;
+}
+
+void dplane_ctx_intf_set_broadcast(struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.intf.flags |= DPLANE_INTF_BROADCAST;
+}
+
const struct prefix *dplane_ctx_get_intf_addr(
const struct zebra_dplane_ctx *ctx)
{
return &(ctx->u.intf.prefix);
}
+void dplane_ctx_set_intf_addr(struct zebra_dplane_ctx *ctx,
+ const struct prefix *p)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ prefix_copy(&(ctx->u.intf.prefix), p);
+}
+
bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
{
DPLANE_CTX_VALID(ctx);
- if (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST)
- return &(ctx->u.intf.dest_prefix);
- else
- return NULL;
+ return &(ctx->u.intf.dest_prefix);
+}
+
+void dplane_ctx_set_intf_dest(struct zebra_dplane_ctx *ctx,
+ const struct prefix *p)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ prefix_copy(&(ctx->u.intf.dest_prefix), p);
}
bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx)
return ctx->u.intf.label;
}
+void dplane_ctx_set_intf_label(struct zebra_dplane_ctx *ctx, const char *label)
+{
+ size_t len;
+
+ DPLANE_CTX_VALID(ctx);
+
+ if (ctx->u.intf.label && ctx->u.intf.label != ctx->u.intf.label_buf)
+ free(ctx->u.intf.label);
+
+ ctx->u.intf.label = NULL;
+
+ if (label) {
+ ctx->u.intf.flags |= DPLANE_INTF_HAS_LABEL;
+
+ /* Use embedded buffer if it's adequate; else allocate. */
+ len = strlen(label);
+
+ if (len < sizeof(ctx->u.intf.label_buf)) {
+ strlcpy(ctx->u.intf.label_buf, label,
+ sizeof(ctx->u.intf.label_buf));
+ ctx->u.intf.label = ctx->u.intf.label_buf;
+ } else {
+ ctx->u.intf.label = strdup(label);
+ }
+ } else {
+ ctx->u.intf.flags &= ~DPLANE_INTF_HAS_LABEL;
+ }
+}
+
/* Accessors for MAC information */
vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx)
{
* two messages in some 'update' cases.
*/
if (is_update)
- zns->netlink_dplane.seq += 2;
+ zns->netlink_dplane_out.seq += 2;
else
- zns->netlink_dplane.seq++;
+ zns->netlink_dplane_out.seq++;
#endif /* HAVE_NETLINK */
return AOK;
#if defined(HAVE_NETLINK)
ns_info->is_cmd = true;
- ns_info->nls = zns->netlink_dplane;
+ ns_info->nls = zns->netlink_dplane_out;
#endif /* NETLINK */
}
+#ifdef HAVE_NETLINK
+/*
+ * Callback when an OS (netlink) incoming event read is ready. This runs
+ * in the dplane pthread.
+ */
+static int dplane_incoming_read(struct thread *event)
+{
+ struct dplane_zns_info *zi = THREAD_ARG(event);
+
+ kernel_dplane_read(&zi->info);
+
+ /* Re-start read task */
+ thread_add_read(zdplane_info.dg_master, dplane_incoming_read, zi,
+ zi->info.nls.sock, &zi->t_read);
+
+ return 0;
+}
+#endif /* HAVE_NETLINK */
+
+/*
+ * Notify dplane when namespaces are enabled and disabled. The dplane
+ * needs to start and stop reading incoming events from the zns. In the
+ * common case where vrfs are _not_ namespaces, there will only be one
+ * of these.
+ *
+ * This is called in the main pthread.
+ */
+void zebra_dplane_ns_enable(struct zebra_ns *zns, bool enabled)
+{
+ struct dplane_zns_info *zi;
+
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("%s: %s for nsid %u", __func__,
+ (enabled ? "ENABLED" : "DISABLED"), zns->ns_id);
+
+ /* Search for an existing zns info entry */
+ frr_each (zns_info_list, &zdplane_info.dg_zns_list, zi) {
+ if (zi->info.ns_id == zns->ns_id)
+ break;
+ }
+
+ if (enabled) {
+ /* Create a new entry if necessary; start reading. */
+ if (zi == NULL) {
+ zi = XCALLOC(MTYPE_DP_NS, sizeof(*zi));
+
+ zi->info.ns_id = zns->ns_id;
+
+ zns_info_list_add_tail(&zdplane_info.dg_zns_list, zi);
+
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("%s: nsid %u, new zi %p", __func__,
+ zns->ns_id, zi);
+ }
+
+ /* Make sure we're up-to-date with the zns object */
+#if defined(HAVE_NETLINK)
+ zi->info.is_cmd = false;
+ zi->info.nls = zns->netlink_dplane_in;
+
+ /* Start read task for the dplane pthread. */
+ if (zdplane_info.dg_master)
+ thread_add_read(zdplane_info.dg_master,
+ dplane_incoming_read, zi,
+ zi->info.nls.sock, &zi->t_read);
+#endif
+ } else if (zi) {
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("%s: nsid %u, deleting zi %p", __func__,
+ zns->ns_id, zi);
+
+ /* Stop reading, free memory */
+ zns_info_list_del(&zdplane_info.dg_zns_list, zi);
+
+ if (zdplane_info.dg_master)
+ thread_cancel_async(zdplane_info.dg_master, &zi->t_read,
+ NULL);
+
+ XFREE(MTYPE_DP_NS, zi);
+ }
+}
+
/*
* Provider api to signal that work/events are available
* for the dataplane pthread.
dplane_ctx_get_ifname(ctx),
ctx->u.gre.link_ifindex);
break;
+
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
+ zlog_debug("Dplane incoming op %s, intf %s, addr %pFX",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_ifname(ctx),
+ dplane_ctx_get_intf_addr(ctx));
+ break;
}
}
case DPLANE_OP_BR_PORT_UPDATE:
break;
+ /* TODO -- error counters for incoming events? */
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
+ break;
+
case DPLANE_OP_NONE:
if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
atomic_fetch_add_explicit(&zdplane_info.dg_other_errors,
*/
static int dplane_check_shutdown_status(struct thread *event)
{
+ struct dplane_zns_info *zi;
+
if (IS_ZEBRA_DEBUG_DPLANE)
zlog_debug("Zebra dataplane shutdown status check called");
+ /* Remove any zns info entries as we stop the dplane pthread. */
+ frr_each_safe (zns_info_list, &zdplane_info.dg_zns_list, zi) {
+ zns_info_list_del(&zdplane_info.dg_zns_list, zi);
+
+ if (zdplane_info.dg_master)
+ thread_cancel(&zi->t_read);
+
+ XFREE(MTYPE_DP_NS, zi);
+ }
+
if (dplane_work_pending()) {
/* Reschedule dplane check on a short timer */
thread_add_timer_msec(zdplane_info.dg_master,
TAILQ_INIT(&zdplane_info.dg_update_ctx_q);
TAILQ_INIT(&zdplane_info.dg_providers_q);
+ zns_info_list_init(&zdplane_info.dg_zns_list);
zdplane_info.dg_updates_per_cycle = DPLANE_DEFAULT_NEW_WORK;
*/
void zebra_dplane_start(void)
{
+ struct dplane_zns_info *zi;
struct zebra_dplane_provider *prov;
struct frr_pthread_attr pattr = {
.start = frr_pthread_attr_default.start,
thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0,
&zdplane_info.dg_t_update);
+ /* Enqueue reads if necessary */
+ frr_each (zns_info_list, &zdplane_info.dg_zns_list, zi) {
+#if defined(HAVE_NETLINK)
+ thread_add_read(zdplane_info.dg_master, dplane_incoming_read,
+ zi, zi->info.nls.sock, &zi->t_read);
+#endif
+ }
+
/* Call start callbacks for registered providers */
DPLANE_LOCK();
#endif /* NETLINK */
}
+/*
+ * Notify dplane when namespaces are enabled and disabled. The dplane
+ * needs to start and stop reading incoming events from the ns.
+ */
+void zebra_dplane_ns_enable(struct zebra_ns *zns, bool enabled);
+
/*
* Result codes used when returning status back to the main zebra context.
*/
*/
/*
- * Enqueue a route install or update for the dataplane.
+ * Operations that the dataplane can process.
*/
enum dplane_op_e {
DPLANE_OP_NONE = 0,
DPLANE_OP_NEIGH_TABLE_UPDATE,
DPLANE_OP_GRE_SET,
+
+ /* Incoming interface address events */
+ DPLANE_OP_INTF_ADDR_ADD,
+ DPLANE_OP_INTF_ADDR_DEL,
};
/*
const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_set_ifname(struct zebra_dplane_ctx *ctx, const char *ifname);
ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_ifindex(struct zebra_dplane_ctx *ctx, ifindex_t ifindex);
/* Retrieve last/current provider id */
uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf);
vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx);
+/* In some paths we have only a namespace id */
+void dplane_ctx_set_ns_id(struct zebra_dplane_ctx *ctx, ns_id_t nsid);
+ns_id_t dplane_ctx_get_ns_id(const struct zebra_dplane_ctx *ctx);
+
bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx,
uint32_t id);
/* Accessors for interface information */
uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric);
/* Is interface addr p2p? */
bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_intf_set_connected(struct zebra_dplane_ctx *ctx);
bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_intf_set_secondary(struct zebra_dplane_ctx *ctx);
bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_intf_set_broadcast(struct zebra_dplane_ctx *ctx);
const struct prefix *dplane_ctx_get_intf_addr(
const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_intf_addr(struct zebra_dplane_ctx *ctx,
+ const struct prefix *p);
bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx);
const struct prefix *dplane_ctx_get_intf_dest(
const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_intf_dest(struct zebra_dplane_ctx *ctx,
+ const struct prefix *p);
bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx);
const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_intf_label(struct zebra_dplane_ctx *ctx, const char *label);
/* Accessors for MAC information */
vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx);
"sync->remote neigh vni %u ip %pIA mac %pEA seq %d f0x%x",
n->zevpn->vni, &n->ip, &n->emac,
seq, n->flags);
- zebra_evpn_neigh_clear_sync_info(n);
if (IS_ZEBRA_NEIGH_ACTIVE(n))
zebra_evpn_neigh_send_del_to_client(
zevpn->vni, &n->ip, &n->emac,
n->flags, n->state,
false /*force*/);
+ zebra_evpn_neigh_clear_sync_info(n);
}
if (memcmp(&n->emac, &mac->macaddr,
sizeof(struct ethaddr))
static void zfpm_start_stats_timer(void);
static void zfpm_mac_info_del(struct fpm_mac_info_t *fpm_mac);
+static const char ipv4_ll_buf[16] = "169.254.0.1";
+union g_addr ipv4ll_gateway;
+
/*
* zfpm_thread_should_yield
*/
zfpm_stats_init(&zfpm_g->last_ivl_stats);
zfpm_stats_init(&zfpm_g->cumulative_stats);
+ memset(&ipv4ll_gateway, 0, sizeof(ipv4ll_gateway));
+ if (inet_pton(AF_INET, ipv4_ll_buf, &ipv4ll_gateway.ipv4) != 1)
+ zlog_warn("inet_pton failed for %s", ipv4_ll_buf);
+
install_node(&zebra_node);
install_element(ENABLE_NODE, &show_zebra_fpm_stats_cmd);
install_element(ENABLE_NODE, &clear_zebra_fpm_stats_cmd);
if (nexthop->type == NEXTHOP_TYPE_IPV6
|| nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
- nhi.gateway = &nexthop->gate;
+ /* Special handling for IPv4 route with IPv6 Link Local next hop
+ */
+ if (ri->af == AF_INET)
+ nhi.gateway = &ipv4ll_gateway;
+ else
+ nhi.gateway = &nexthop->gate;
}
if (nexthop->type == NEXTHOP_TYPE_IFINDEX) {
ri->af = rib_dest_af(dest);
if (zvrf && zvrf->zns)
- ri->nlmsg_pid = zvrf->zns->netlink_dplane.snl.nl_pid;
+ ri->nlmsg_pid = zvrf->zns->netlink_dplane_out.snl.nl_pid;
ri->nlmsg_type = cmd;
ri->rtm_table = table_info->table_id;
extern struct route_entry *zfpm_route_for_update(rib_dest_t *dest);
+extern union g_addr ipv4ll_gateway;
+
#ifdef __cplusplus
}
#endif
/* static function declarations */
/* Private functions */
-static void map_slaves_to_bridge(struct interface *br_if, int link)
+static void map_slaves_to_bridge(struct interface *br_if, int link,
+ bool update_slave, uint8_t chgflags)
{
struct vrf *vrf;
struct interface *ifp;
br_slave = &zif->brslave_info;
if (link) {
- if (br_slave->bridge_ifindex == br_if->ifindex &&
- br_slave->ns_id == zns->ns_id)
+ if (br_slave->bridge_ifindex == br_if->ifindex
+ && br_slave->ns_id == zns->ns_id) {
br_slave->br_if = br_if;
+ if (update_slave) {
+ zebra_l2if_update_bridge_slave(
+ ifp,
+ br_slave->bridge_ifindex,
+ br_slave->ns_id,
+ chgflags);
+ }
+ }
} else {
if (br_slave->br_if == br_if)
br_slave->br_if = NULL;
memcpy(&zif->l2info.br, bridge_info, sizeof(*bridge_info));
/* Link all slaves to this bridge */
- map_slaves_to_bridge(ifp, 1);
+ map_slaves_to_bridge(ifp, 1, false, ZEBRA_BRIDGE_NO_ACTION);
}
/*
void zebra_l2_bridge_del(struct interface *ifp)
{
/* Unlink all slaves to this bridge */
- map_slaves_to_bridge(ifp, 0);
+ map_slaves_to_bridge(ifp, 0, false, ZEBRA_BRIDGE_NO_ACTION);
+}
+
+void zebra_l2if_update_bridge(struct interface *ifp, uint8_t chgflags)
+{
+ if (!chgflags)
+ return;
+ map_slaves_to_bridge(ifp, 1, true, chgflags);
}
/*
* from a bridge before it can be mapped to another bridge.
*/
void zebra_l2if_update_bridge_slave(struct interface *ifp,
- ifindex_t bridge_ifindex,
- ns_id_t ns_id)
+ ifindex_t bridge_ifindex, ns_id_t ns_id,
+ uint8_t chgflags)
{
struct zebra_if *zif;
ifindex_t old_bridge_ifindex;
if (!zvrf)
return;
+ if (zif->zif_type == ZEBRA_IF_VXLAN
+ && chgflags != ZEBRA_BRIDGE_NO_ACTION) {
+ if (ZEBRA_BRIDGE_MASTER_MAC_CHANGE)
+ zebra_vxlan_if_update(ifp,
+ ZEBRA_VXLIF_MASTER_MAC_CHANGE);
+ if (ZEBRA_BRIDGE_MASTER_UP)
+ zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE);
+ }
old_bridge_ifindex = zif->brslave_info.bridge_ifindex;
old_ns_id = zif->brslave_info.ns_id;
if (old_bridge_ifindex == bridge_ifindex &&
extern "C" {
#endif
+#define ZEBRA_BRIDGE_NO_ACTION (0)
+#define ZEBRA_BRIDGE_MASTER_MAC_CHANGE (1 << 1)
+#define ZEBRA_BRIDGE_MASTER_UP (1 << 2)
+
/* zebra L2 interface information - bridge slave (linkage to bridge) */
struct zebra_l2info_brslave {
ifindex_t bridge_ifindex; /* Bridge Master */
extern void zebra_l2_vxlanif_del(struct interface *ifp);
extern void zebra_l2if_update_bridge_slave(struct interface *ifp,
ifindex_t bridge_ifindex,
- ns_id_t ns_id);
+ ns_id_t ns_id, uint8_t chgflags);
extern void zebra_l2if_update_bond_slave(struct interface *ifp,
ifindex_t bond_ifindex, bool bypass);
extern void zebra_vlan_mbr_re_eval(struct interface *ifp,
bitfield_t vlan_bitmap);
extern void zebra_l2if_update_bond(struct interface *ifp, bool add);
+extern void zebra_l2if_update_bridge(struct interface *ifp, uint8_t chgflags);
#ifdef __cplusplus
}
{
int fd_monitor;
- zebra_netns_notify_current = NULL;
fd_monitor = inotify_init();
if (fd_monitor < 0) {
flog_err_sys(
nexthop_add_labels(resolved_hop, label_type, num_labels,
labels);
+ if (nexthop->nh_srv6) {
+ nexthop_add_srv6_seg6local(resolved_hop,
+ nexthop->nh_srv6->seg6local_action,
+ &nexthop->nh_srv6->seg6local_ctx);
+ nexthop_add_srv6_seg6(resolved_hop,
+ &nexthop->nh_srv6->seg6_segs);
+ }
+
resolved_hop->rparent = nexthop;
_nexthop_add(&nexthop->resolved, resolved_hop);
case DPLANE_OP_IPSET_ENTRY_DELETE:
case DPLANE_OP_NEIGH_TABLE_UPDATE:
case DPLANE_OP_GRE_SET:
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
break;
}
zns->ns_id = ns_id;
kernel_init(zns);
+ zebra_dplane_ns_enable(zns, true);
interface_list(zns);
route_read(zns);
kernel_read_pbr_rules(zns);
{
route_table_finish(zns->if_table);
+ zebra_dplane_ns_enable(zns, false /*Disable*/);
+
kernel_terminate(zns, complete);
zns->ns_id = NS_DEFAULT;
#ifdef HAVE_NETLINK
struct nlsock netlink; /* kernel messages */
struct nlsock netlink_cmd; /* command channel */
- struct nlsock netlink_dplane; /* dataplane channel */
+
+ /* dplane system's channels: one for outgoing programming,
+ * for the FIB e.g., and one for incoming events from the OS.
+ */
+ struct nlsock netlink_dplane_out;
+ struct nlsock netlink_dplane_in;
struct thread *t_netlink;
#endif
if (!strcmp(ZEBRA_PTM_INVALID_VRF, vrf_str) && ifp) {
vrf_id = ifp->vrf_id;
} else {
- vrf_id = vrf_name_to_id(vrf_str);
+ struct vrf *pVrf;
+
+ pVrf = vrf_lookup_by_name(vrf_str);
+ if (pVrf)
+ vrf_id = pVrf->vrf_id;
+ else
+ vrf_id = VRF_DEFAULT;
}
if (!strcmp(bfdst_str, ZEBRA_PTM_BFDSTATUS_DOWN_STR)) {
zebra_pbr_dplane_result(ctx);
break;
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
+ zebra_if_addr_update_ctx(ctx);
+ break;
+
/* Some op codes not handled here */
case DPLANE_OP_ADDR_INSTALL:
case DPLANE_OP_ADDR_UNINSTALL:
{
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
struct srv6_locator *tmp;
+ struct listnode *node;
+ struct zserv *client;
tmp = zebra_srv6_locator_lookup(locator->name);
if (!tmp)
listnode_add(srv6->locators, locator);
+
+ /*
+ * Notify new locator info to zclients.
+ *
+ * The srv6 locators and their prefixes are managed by zserv(zebra).
+ * And an actual configuration the srv6 sid in the srv6 locator is done
+ * by zclient(bgpd, isisd, etc). The configuration of each locator
+ * allocation and specify it by zserv and zclient should be
+ * asynchronous. For that, zclient should be received the event via
+ * ZAPI when a srv6 locator is added on zebra.
+ * Basically, in SRv6, adding/removing SRv6 locators is performed less
+ * frequently than adding rib entries, so a broad to all zclients will
+ * not degrade the overall performance of FRRouting.
+ */
+ for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client))
+ zsend_zebra_srv6_locator_add(client, locator);
}
void zebra_srv6_locator_delete(struct srv6_locator *locator)
{
+ struct listnode *n;
+ struct srv6_locator_chunk *c;
struct zebra_srv6 *srv6 = zebra_srv6_get_default();
+ struct zserv *client;
+
+ /*
+ * Notify deleted locator info to zclients if needed.
+ *
+ * zclient(bgpd,isisd,etc) allocates a sid from srv6 locator chunk and
+ * uses it for its own purpose. For example, in the case of BGP L3VPN,
+ * the SID assigned to vpn unicast rib will be given.
+ * And when the locator is deleted by zserv(zebra), those SIDs need to
+ * be withdrawn. The zclient must initiate the withdrawal of the SIDs
+ * by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the
+ * owner of each chunk.
+ */
+ for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, n, c)) {
+ if (c->proto == ZEBRA_ROUTE_SYSTEM)
+ continue;
+ client = zserv_find_client(c->proto, c->instance);
+ if (!client) {
+ zlog_warn(
+ "%s: Not found zclient(proto=%u, instance=%u).",
+ __func__, c->proto, c->instance);
+ continue;
+ }
+ zsend_zebra_srv6_locator_delete(client, locator);
+ }
listnode_delete(srv6->locators, locator);
}
if (!loc) {
zlog_info("%s: locator %s was not found",
__func__, locator_name);
-
- loc = srv6_locator_alloc(locator_name);
- if (!loc) {
- zlog_info("%s: locator %s can't allocated",
- __func__, locator_name);
- return NULL;
- }
-
- loc->status_up = false;
- chunk = srv6_locator_chunk_alloc();
- chunk->proto = NO_PROTO;
- listnode_add(loc->chunks, chunk);
- zebra_srv6_locator_add(loc);
+ return NULL;
}
for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) {
return CMD_SUCCESS;
}
+DEFUN (no_srv6,
+ no_srv6_cmd,
+ "no srv6",
+ NO_STR
+ "Segment Routing SRv6\n")
+{
+ struct zebra_srv6 *srv6 = zebra_srv6_get_default();
+ struct srv6_locator *locator;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(srv6->locators, node, nnode, locator))
+ zebra_srv6_locator_delete(locator);
+ return CMD_SUCCESS;
+}
+
DEFUN_NOSH (srv6_locators,
srv6_locators_cmd,
"locators",
return CMD_SUCCESS;
}
+DEFUN (no_srv6_locator,
+ no_srv6_locator_cmd,
+ "no locator WORD",
+ NO_STR
+ "Segment Routing SRv6 locator\n"
+ "Specify locator-name\n")
+{
+ struct srv6_locator *locator = zebra_srv6_locator_lookup(argv[2]->arg);
+ if (!locator) {
+ vty_out(vty, "%% Can't find SRv6 locator\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ zebra_srv6_locator_delete(locator);
+ return CMD_SUCCESS;
+}
+
DEFPY (locator_prefix,
locator_prefix_cmd,
"prefix X:X::X:X/M$prefix [func-bits (16-64)$func_bit_len]",
/* Command for change node */
install_element(CONFIG_NODE, &segment_routing_cmd);
install_element(SEGMENT_ROUTING_NODE, &srv6_cmd);
+ install_element(SEGMENT_ROUTING_NODE, &no_srv6_cmd);
install_element(SRV6_NODE, &srv6_locators_cmd);
install_element(SRV6_LOCS_NODE, &srv6_locator_cmd);
+ install_element(SRV6_LOCS_NODE, &no_srv6_locator_cmd);
/* Command for configuration */
install_element(SRV6_LOC_NODE, &locator_prefix_cmd);
break;
}
break;
- default:
- break;
}
- if ((re->vrf_id != nexthop->vrf_id)
- && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE))
- vty_out(vty, "(vrf %s)", vrf_id_to_name(nexthop->vrf_id));
+ if (re->vrf_id != nexthop->vrf_id) {
+ struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
+
+ vty_out(vty, "(vrf %s)", VRF_LOGNAME(vrf));
+ }
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
vty_out(vty, " (duplicate nexthop removed)");
break;
}
break;
- default:
- break;
}
- if ((re == NULL || (nexthop->vrf_id != re->vrf_id))
- && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE))
+ if ((re == NULL || (nexthop->vrf_id != re->vrf_id)))
vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id));
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
break;
}
break;
- default:
- break;
}
- if ((nexthop->vrf_id != re->vrf_id)
- && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE))
+ if (nexthop->vrf_id != re->vrf_id)
json_object_string_add(json_nexthop, "vrf",
vrf_id_to_name(nexthop->vrf_id));
json_object_int_add(json_route, "internalNextHopActiveNum",
nexthop_group_active_nexthop_num(
&(re->nhe->nhg)));
+ json_object_int_add(json_route, "nexthopGroupId", re->nhe_id);
json_object_string_add(json_route, "uptime", up_str);
break;
}
break;
- default:
- break;
}
}
return zl3vni_lookup(zvrf->l3vni);
}
+static int zl3vni_from_svi_ns(struct ns *ns, void *_in_param, void **_p_zl3vni)
+{
+ int found = 0;
+ struct zebra_ns *zns = ns->info;
+ struct zebra_l3vni **p_zl3vni = (struct zebra_l3vni **)_p_zl3vni;
+ struct zebra_from_svi_param *in_param =
+ (struct zebra_from_svi_param *)_in_param;
+ struct route_node *rn = NULL;
+ struct interface *tmp_if = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ if (!in_param)
+ return NS_WALK_STOP;
+
+ /* loop through all vxlan-interface */
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ tmp_if = (struct interface *)rn->info;
+ if (!tmp_if)
+ continue;
+ zif = tmp_if->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+ if (!if_is_operative(tmp_if))
+ continue;
+ vxl = &zif->l2info.vxl;
+
+ if (zif->brslave_info.br_if != in_param->br_if)
+ continue;
+
+ if (!in_param->bridge_vlan_aware
+ || vxl->access_vlan == in_param->vid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return NS_WALK_CONTINUE;
+
+ if (p_zl3vni)
+ *p_zl3vni = zl3vni_lookup(vxl->vni);
+ return NS_WALK_STOP;
+}
+
/*
* Map SVI and associated bridge to a VNI. This is invoked upon getting
* neighbor notifications, to see if they are of interest.
static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp,
struct interface *br_if)
{
- int found = 0;
- vlanid_t vid = 0;
- uint8_t bridge_vlan_aware = 0;
struct zebra_l3vni *zl3vni = NULL;
- struct zebra_ns *zns = NULL;
- struct route_node *rn = NULL;
struct zebra_if *zif = NULL;
- struct interface *tmp_if = NULL;
struct zebra_l2info_bridge *br = NULL;
- struct zebra_l2info_vxlan *vxl = NULL;
+ struct zebra_from_svi_param in_param = {};
+ struct zebra_l3vni **p_zl3vni;
if (!br_if)
return NULL;
/* Make sure the linked interface is a bridge. */
if (!IS_ZEBRA_IF_BRIDGE(br_if))
return NULL;
+ in_param.br_if = br_if;
/* Determine if bridge is VLAN-aware or not */
zif = br_if->info;
assert(zif);
br = &zif->l2info.br;
- bridge_vlan_aware = br->vlan_aware;
- if (bridge_vlan_aware) {
+ in_param.bridge_vlan_aware = br->vlan_aware;
+ if (in_param.bridge_vlan_aware) {
struct zebra_l2info_vlan *vl;
if (!IS_ZEBRA_IF_VLAN(ifp))
zif = ifp->info;
assert(zif);
vl = &zif->l2info.vl;
- vid = vl->vid;
+ in_param.vid = vl->vid;
}
/* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
/* TODO: Optimize with a hash. */
- zns = zebra_ns_lookup(NS_DEFAULT);
- for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
- tmp_if = (struct interface *)rn->info;
- if (!tmp_if)
- continue;
- zif = tmp_if->info;
- if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
- continue;
- if (!if_is_operative(tmp_if))
- continue;
- vxl = &zif->l2info.vxl;
-
- if (zif->brslave_info.br_if != br_if)
- continue;
- if (!bridge_vlan_aware || vxl->access_vlan == vid) {
- found = 1;
- break;
- }
- }
+ p_zl3vni = &zl3vni;
- if (!found)
- return NULL;
-
- zl3vni = zl3vni_lookup(vxl->vni);
+ ns_walk_func(zl3vni_from_svi_ns, (void *)&in_param, (void **)p_zl3vni);
return zl3vni;
}
return 0;
}
+ if ((chgflags & ZEBRA_VXLIF_MASTER_MAC_CHANGE)
+ && if_is_operative(ifp) && is_l3vni_oper_up(zl3vni)) {
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ return 0;
+ }
+
/* access-vlan change - process oper down, associate with new
* svi_if and then process oper up again
*/
#define ZEBRA_VXLIF_MASTER_CHANGE (1 << 1)
#define ZEBRA_VXLIF_VLAN_CHANGE (1 << 2)
#define ZEBRA_VXLIF_MCAST_GRP_CHANGE (1 << 3)
+#define ZEBRA_VXLIF_MASTER_MAC_CHANGE (1 << 4)
#define VNI_STR_LEN 32