- SUBGRP_FOREACH_ADJ_SAFE
- AF_FOREACH
- FOREACH_AFI_SAFI
+ # ospfd
+ - LSDB_LOOP
| _Security_ | security@lists.frrouting.org |
| _Technical Steering Committee_ | tsc@lists.frrouting.org |
+The Development list is used to discuss and document general issues
+related to project development and governance. The public Slack
+instance, frrouting.slack.com, and weekly technical meetings provide a
+higher bandwidth channel for discussions. The results of such
+discussions must be reflected in updates, as appropriate, to code (i.e.,
+merges), [github](https://github.com/FRRouting/frr/issues) tracked
+issues, and for governance or process changes, updates to the
+Development list and either this file or information posted at
+[https://frrouting.org/](https://frrouting.org/).
+
### Changelog
-FRRouting is free software that manages various IPv4 and IPv6 routing
-protocols.
+FRRouting is free software that implements and manages various IPv4 and IPv6
+routing protocols.
-Currently FRRouting supports BGP4, BGP4+, OSPFv2, OSPFv3, RIPv1,
-RIPv2, RIPng, PIM-SM/MSDP and LDP as well as very early support for IS-IS,
-EIGRP and NHRP.
+Currently FRRouting supports BGP4, BGP4+, OSPFv2, OSPFv3, RIPv1, RIPv2, RIPng,
+IS-IS, PIM-SM/MSDP, LDP and Babel as well as very early support for EIGRP and
+NHRP.
See the file REPORTING-BUGS to report bugs.
+See COMMUNITY.md for information on contributing.
+
Free RRRouting is free software. See the file COPYING for copying conditions.
Public email discussion can be found at https://lists.frrouting.org/listinfo
int ifindex = if_nametoindex(ifp->name);
if(ifindex > 0) {
unsigned char eui[8];
- rc = if_eui64(ifp->name, ifindex, eui);
+ rc = if_eui64(ifindex, eui);
if(rc < 0)
continue;
memcpy(myid, eui, 8);
ifname = if_indextoname(i, buf);
if(ifname == NULL)
continue;
- rc = if_eui64(ifname, i, eui);
+ rc = if_eui64(i, eui);
if(rc < 0)
continue;
memcpy(myid, eui, 8);
api.type = ZEBRA_ROUTE_BABEL;
api.safi = SAFI_UNICAST;
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.prefix = quagga_prefix;
if(metric >= KERNEL_INFINITY) {
}
int
-if_eui64(char *ifname, int ifindex, unsigned char *eui)
+if_eui64(int ifindex, unsigned char *eui)
{
struct interface *ifp = if_lookup_by_index(ifindex, VRF_DEFAULT);
if (ifp == NULL) {
const unsigned char *gate, int ifindex, unsigned int metric,
const unsigned char *newgate, int newifindex,
unsigned int newmetric);
-int if_eui64(char *ifname, int ifindex, unsigned char *eui);
+int if_eui64(int ifindex, unsigned char *eui);
int gettime(struct timeval *tv);
int read_random_bytes(void *buf, size_t len);
bgpd.conf.vnc.sample
bgp_vty.o: bgp_vty_clippy.c
+bgp_route.o: bgp_route_clippy.c
EXTRA_DIST = BGP4-MIB.txt
BGP_ADV_FIFO_INIT(&sync->withdraw);
BGP_ADV_FIFO_INIT(&sync->withdraw_low);
peer->sync[afi][safi] = sync;
- peer->hash[afi][safi] = hash_create(baa_hash_key, baa_hash_cmp,
- "BGP Sync Hash");
}
}
if (peer->sync[afi][safi])
XFREE(MTYPE_BGP_SYNCHRONISE, peer->sync[afi][safi]);
peer->sync[afi][safi] = NULL;
-
- if (peer->hash[afi][safi])
- hash_free(peer->hash[afi][safi]);
- peer->hash[afi][safi] = NULL;
}
}
{BGP_ATTR_AS4_PATH, "AS4_PATH"},
{BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR"},
{BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT"},
+ {BGP_ATTR_PMSI_TUNNEL, "PMSI_TUNNEL_ATTRIBUTE"},
{BGP_ATTR_ENCAP, "ENCAP"},
#if ENABLE_BGP_VNC
{BGP_ATTR_VNC, "VNC"},
const struct attr *attr = (struct attr *)p;
uint32_t key = 0;
#define MIX(val) key = jhash_1word(val, key)
+#define MIX3(a, b, c) key = jhash_3words((a), (b), (c), key)
- MIX(attr->origin);
- MIX(attr->nexthop.s_addr);
- MIX(attr->med);
- MIX(attr->local_pref);
- MIX(attr->aggregator_as);
- MIX(attr->aggregator_addr.s_addr);
- MIX(attr->weight);
- MIX(attr->mp_nexthop_global_in.s_addr);
- MIX(attr->originator_id.s_addr);
- MIX(attr->tag);
- MIX(attr->label);
- MIX(attr->label_index);
+ MIX3(attr->origin, attr->nexthop.s_addr, attr->med);
+ MIX3(attr->local_pref, attr->aggregator_as, attr->aggregator_addr.s_addr);
+ MIX3(attr->weight, attr->mp_nexthop_global_in.s_addr,
+ attr->originator_id.s_addr);
+ MIX3(attr->tag, attr->label, attr->label_index);
if (attr->aspath)
MIX(aspath_key_make(attr->aspath));
&& attr1->tag == attr2->tag
&& attr1->label_index == attr2->label_index
&& attr1->mp_nexthop_len == attr2->mp_nexthop_len
- && IPV6_ADDR_SAME(&attr1->mp_nexthop_global,
- &attr2->mp_nexthop_global)
- && IPV6_ADDR_SAME(&attr1->mp_nexthop_local,
- &attr2->mp_nexthop_local)
- && IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in,
- &attr2->mp_nexthop_global_in)
&& attr1->ecommunity == attr2->ecommunity
&& attr1->lcommunity == attr2->lcommunity
&& attr1->cluster == attr2->cluster
#if ENABLE_BGP_VNC
&& encap_same(attr1->vnc_subtlvs, attr2->vnc_subtlvs)
#endif
+ && IPV6_ADDR_SAME(&attr1->mp_nexthop_global,
+ &attr2->mp_nexthop_global)
+ && IPV6_ADDR_SAME(&attr1->mp_nexthop_local,
+ &attr2->mp_nexthop_local)
+ && IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in,
+ &attr2->mp_nexthop_global_in)
&& IPV4_ADDR_SAME(&attr1->originator_id,
&attr2->originator_id)
&& overlay_index_same(attr1, attr2))
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_AS4_AGGREGATOR] =
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_PMSI_TUNNEL] =
+ BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_LARGE_COMMUNITIES] =
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_PREFIX_SID] =
attr->mm_seqnum = bgp_attr_mac_mobility_seqnum(attr, &sticky);
attr->sticky = sticky;
+ /* Extract the Rmac, if any */
+ bgp_attr_rmac(attr, &attr->rmac);
+
return BGP_ATTR_PARSE_PROCEED;
}
#endif
}
+ /* PMSI Tunnel */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)) {
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_PMSI_TUNNEL);
+ stream_putc(s, 9); // Length
+ stream_putc(s, 0); // Flags
+ stream_putc(s, 6); // Tunnel type: Ingress Replication (6)
+ stream_put(s, &(attr->label), BGP_LABEL_BYTES); // MPLS Label / VXLAN VNI
+ stream_put_ipv4(s, attr->nexthop.s_addr); // Unicast tunnel endpoint IP address
+ }
+
/* Unknown transit attribute. */
if (attr->transit)
stream_put(s, attr->transit->val, attr->transit->length);
/* EVPN MAC Mobility sequence number, if any. */
u_int32_t mm_seqnum;
+
+ /* EVPN local router-mac */
+ struct ethaddr rmac;
};
/* rmap_change_flags definition */
return prefix_mac2str((struct ethaddr *)en, NULL, 0);
}
+/* Fetch router-mac from extended community */
+void bgp_attr_rmac(struct attr *attr,
+ struct ethaddr *rmac)
+{
+ int i = 0;
+ struct ecommunity *ecom;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return;
+
+ /* If there is a router mac extended community, set RMAC in attr */
+ for (i = 0; i < ecom->size; i++) {
+ u_char *pnt = NULL;
+ u_char type = 0;
+ u_char sub_type = 0;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if (!(type == ECOMMUNITY_ENCODE_EVPN &&
+ sub_type == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC))
+ continue;
+
+ memcpy(rmac, pnt, ETH_ALEN);
+ }
+}
+
/*
* Fetch and return the sequence number from MAC Mobility extended
* community, if present, else 0.
struct ethaddr *routermac);
extern int bgp_build_evpn_prefix(int type, uint32_t eth_tag,
struct prefix *dst);
-
+extern void bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac);
extern u_int32_t bgp_attr_mac_mobility_seqnum(struct attr *attr,
u_char *sticky);
prefix2str(&dp, buf[0], sizeof(buf[0]));
if (ifp) {
zlog_debug(
- "Zebra: vrf %d interface %s bfd destination %s %s",
+ "Zebra: vrf %u interface %s bfd destination %s %s",
vrf_id, ifp->name, buf[0],
bfd_get_status_str(status));
} else {
prefix2str(&sp, buf[1], sizeof(buf[1]));
zlog_debug(
- "Zebra: vrf %d source %s bfd destination %s %s",
+ "Zebra: vrf %u source %s bfd destination %s %s",
vrf_id, buf[1], buf[0],
bfd_get_status_str(status));
}
} else if (type == ECOMMUNITY_ENCODE_EVPN) {
if (filter == ECOMMUNITY_ROUTE_TARGET)
continue;
- if (*pnt == ECOMMUNITY_SITE_ORIGIN) {
- char macaddr[6];
+ if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
+ struct ethaddr rmac;
pnt++;
- memcpy(&macaddr, pnt, 6);
+ memcpy(&rmac, pnt, ETH_ALEN);
len = sprintf(
str_buf + str_pnt,
- "EVPN:%02x:%02x:%02x:%02x:%02x:%02x",
- (uint8_t)macaddr[0],
- (uint8_t)macaddr[1],
- (uint8_t)macaddr[2],
- (uint8_t)macaddr[3],
- (uint8_t)macaddr[4],
- (uint8_t)macaddr[5]);
+ "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
+ (uint8_t)rmac.octet[0],
+ (uint8_t)rmac.octet[1],
+ (uint8_t)rmac.octet[2],
+ (uint8_t)rmac.octet[3],
+ (uint8_t)rmac.octet[4],
+ (uint8_t)rmac.octet[5]);
} else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
u_int32_t seqnum;
return (vpn1->vni == vpn2->vni);
}
+/*
+ * Make vrf import route target hash key.
+ */
+static unsigned int vrf_import_rt_hash_key_make(void *p)
+{
+ struct vrf_irt_node *irt = p;
+ char *pnt = irt->rt.val;
+ unsigned int key = 0;
+ int c = 0;
+
+ key += pnt[c];
+ key += pnt[c + 1];
+ key += pnt[c + 2];
+ key += pnt[c + 3];
+ key += pnt[c + 4];
+ key += pnt[c + 5];
+ key += pnt[c + 6];
+ key += pnt[c + 7];
+
+ return key;
+}
+
+/*
+ * Comparison function for vrf import rt hash
+ */
+static int vrf_import_rt_hash_cmp(const void *p1, const void *p2)
+{
+ const struct vrf_irt_node *irt1 = p1;
+ const struct vrf_irt_node *irt2 = p2;
+
+ if (irt1 == NULL && irt2 == NULL)
+ return 1;
+
+ if (irt1 == NULL || irt2 == NULL)
+ return 0;
+
+ return (memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0);
+}
+
+/*
+ * Create a new vrf import_rt in default instance
+ */
+static struct vrf_irt_node *vrf_import_rt_new(struct ecommunity_val *rt)
+{
+ struct bgp *bgp_def = NULL;
+ struct vrf_irt_node *irt;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def) {
+ zlog_err("vrf import rt new - def instance not created yet");
+ return NULL;
+ }
+
+ irt = XCALLOC(MTYPE_BGP_EVPN_VRF_IMPORT_RT,
+ sizeof(struct vrf_irt_node));
+ if (!irt)
+ return NULL;
+
+ irt->rt = *rt;
+ irt->vrfs = list_new();
+
+ /* Add to hash */
+ if (!hash_get(bgp_def->vrf_import_rt_hash, irt, hash_alloc_intern)) {
+ XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt);
+ return NULL;
+ }
+
+ return irt;
+}
+
+/*
+ * Free the vrf import rt node
+ */
+static void vrf_import_rt_free(struct vrf_irt_node *irt)
+{
+ struct bgp *bgp_def = NULL;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def) {
+ zlog_err("vrf import rt free - def instance not created yet");
+ return;
+ }
+
+ hash_release(bgp_def->vrf_import_rt_hash, irt);
+ XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt);
+}
+
+/*
+ * Function to lookup Import RT node - used to map a RT to set of
+ * VNIs importing routes with that RT.
+ */
+static struct vrf_irt_node *lookup_vrf_import_rt(struct ecommunity_val *rt)
+{
+ struct bgp *bgp_def = NULL;
+ struct vrf_irt_node *irt;
+ struct vrf_irt_node tmp;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def) {
+ zlog_err("vrf import rt lookup - def instance not created yet");
+ return NULL;
+ }
+
+ memset(&tmp, 0, sizeof(struct vrf_irt_node));
+ memcpy(&tmp.rt, rt, ECOMMUNITY_SIZE);
+ irt = hash_lookup(bgp_def->vrf_import_rt_hash, &tmp);
+ return irt;
+}
+
+/*
+ * Is specified VRF present on the RT's list of "importing" VRFs?
+ */
+static int is_vrf_present_in_irt_vrfs(struct list *vrfs,
+ struct bgp *bgp_vrf)
+{
+ struct listnode *node = NULL, *nnode = NULL;
+ struct bgp *tmp_bgp_vrf = NULL;
+
+ for (ALL_LIST_ELEMENTS(vrfs, node, nnode, tmp_bgp_vrf)) {
+ if (tmp_bgp_vrf == bgp_vrf)
+ return 1;
+ }
+ return 0;
+}
+
/*
* Make import route target hash key.
*/
}
}
+/*
+ * Map one RT to specified VRF.
+ * bgp_vrf = BGP vrf instance
+ */
+static void map_vrf_to_rt(struct bgp *bgp_vrf,
+ struct ecommunity_val *eval)
+{
+ struct vrf_irt_node *irt = NULL;
+ struct ecommunity_val eval_tmp;
+
+ /* If using "automatic" RT,
+ * we only care about the local-admin sub-field.
+ * This is to facilitate using L3VNI(VRF-VNI)
+ * as the RT for EBGP peering too.
+ */
+ memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_IMPORT_RT_CFGD))
+ mask_ecom_global_admin(&eval_tmp, eval);
+
+ irt = lookup_vrf_import_rt(&eval_tmp);
+ if (irt && irt->vrfs)
+ if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+ /* Already mapped. */
+ return;
+
+ if (!irt) {
+ irt = vrf_import_rt_new(&eval_tmp);
+ assert(irt);
+ }
+
+ /* Add VRF to the list for this RT. */
+ listnode_add(irt->vrfs, bgp_vrf);
+}
+
+/*
+ * Unmap specified VRF from specified RT. If there are no other
+ * VRFs for this RT, then the RT hash is deleted.
+ * bgp_vrf: BGP VRF specific instance
+ */
+static void unmap_vrf_from_rt(struct bgp *bgp_vrf,
+ struct vrf_irt_node *irt)
+{
+ /* Delete VRF from list for this RT. */
+ listnode_delete(irt->vrfs, bgp_vrf);
+ if (!listnode_head(irt->vrfs)) {
+ list_delete_and_null(&irt->vrfs);
+ vrf_import_rt_free(irt);
+ }
+}
+
/*
* Map one RT to specified VNI.
*/
* VNIs but the same across routers (in the same AS) for a particular
* VNI.
*/
-static void form_auto_rt(struct bgp *bgp, struct bgpevpn *vpn, struct list *rtl)
+static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl)
{
struct ecommunity_val eval;
struct ecommunity *ecomadd;
- encode_route_target_as((bgp->as & 0xFFFF), vpn->vni, &eval);
+ encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
ecomadd = ecommunity_new();
ecommunity_add_val(ecomadd, &eval);
return zclient_send_message(zclient);
}
+/*
+ * Build extended communities for EVPN prefix route.
+ */
+static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
+ struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity ecom_rmac;
+ struct ecommunity_val eval;
+ struct ecommunity_val eval_rmac;
+ bgp_encap_types tnl_type;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+ struct list *vrf_export_rtl = NULL;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset(&ecom_encap, 0, sizeof(ecom_encap));
+ encode_encap_extcomm(tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.val = (u_int8_t *)eval.val;
+
+ /* Add Encap */
+ attr->ecommunity = ecommunity_dup(&ecom_encap);
+
+ /* Add the export RTs for L3VNI/VRF */
+ vrf_export_rtl = bgp_vrf->vrf_export_rtl;
+ if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
+ for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom))
+ attr->ecommunity = ecommunity_merge(attr->ecommunity,
+ ecom);
+ }
+
+ /* add the router mac extended community */
+ if (!is_zero_mac(&attr->rmac)) {
+ memset(&ecom_rmac, 0, sizeof(ecom_rmac));
+ encode_rmac_extcomm(&eval_rmac, &attr->rmac);
+ ecom_rmac.size = 1;
+ ecom_rmac.val = (uint8_t *)eval_rmac.val;
+ attr->ecommunity = ecommunity_merge(attr->ecommunity,
+ &ecom_rmac);
+ }
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
+}
+
/*
* Build extended communities for EVPN route. RT and ENCAP are
* applicable to all routes.
+ * TODO: currently kernel doesnt support ipv6 routes with ipv4 nexthops.
+ * This means that we can't do symmetric routing for ipv6 hosts routes
+ * in the same way as ipv4 host routes.
+ * We wont attach l3-vni related RTs for ipv6 routes.
+ * For now, We will only adevrtise ipv4 host routes
+ * with L3-VNI related ext-comm.
*/
-static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr)
+static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
+ afi_t afi)
{
struct ecommunity ecom_encap;
struct ecommunity ecom_sticky;
+ struct ecommunity ecom_rmac;
struct ecommunity_val eval;
struct ecommunity_val eval_sticky;
+ struct ecommunity_val eval_rmac;
bgp_encap_types tnl_type;
struct listnode *node, *nnode;
struct ecommunity *ecom;
u_int32_t seqnum;
+ struct list *vrf_export_rtl = NULL;
/* Encap */
tnl_type = BGP_ENCAP_TYPE_VXLAN;
/* Add Encap */
attr->ecommunity = ecommunity_dup(&ecom_encap);
- /* Add the export RTs */
+ /* Add the export RTs for L2VNI */
for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom))
attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom);
+ /* Add the export RTs for L3VNI - currently only supported for IPV4 host
+ * routes
+ */
+ if (afi == AFI_IP) {
+ vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);
+ if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
+ for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode,
+ ecom))
+ attr->ecommunity =
+ ecommunity_merge(attr->ecommunity,
+ ecom);
+ }
+ }
+
if (attr->sticky) {
seqnum = 0;
memset(&ecom_sticky, 0, sizeof(ecom_sticky));
ecommunity_merge(attr->ecommunity, &ecom_sticky);
}
+ if (afi == AFI_IP && !is_zero_mac(&attr->rmac)) {
+ memset(&ecom_rmac, 0, sizeof(ecom_rmac));
+ encode_rmac_extcomm(&eval_rmac, &attr->rmac);
+ ecom_rmac.size = 1;
+ ecom_rmac.val = (uint8_t *)eval_rmac.val;
+ attr->ecommunity = ecommunity_merge(attr->ecommunity,
+ &ecom_rmac);
+ }
+
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
}
return local_ri->attr->sticky;
}
+static int update_evpn_type5_route_entry(struct bgp *bgp_def,
+ struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi, struct bgp_node *rn,
+ struct attr *attr, int *route_changed)
+{
+ struct attr *attr_new = NULL;
+ struct bgp_info *ri = NULL;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+ struct bgp_info *local_ri = NULL;
+ struct bgp_info *tmp_ri = NULL;
+
+ *route_changed = 0;
+ /* locate the local route entry if any */
+ for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) {
+ if (tmp_ri->peer == bgp_def->peer_self
+ && tmp_ri->type == ZEBRA_ROUTE_BGP
+ && tmp_ri->sub_type == BGP_ROUTE_STATIC)
+ local_ri = tmp_ri;
+ }
+
+ /* create a new route entry if one doesnt exist.
+ Otherwise see if route attr has changed
+ */
+ if (!local_ri) {
+
+ /* route has changed as this is the first entry */
+ *route_changed = 1;
+
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(attr);
+
+ /* create the route info from attribute */
+ ri = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0,
+ bgp_def->peer_self, attr_new, rn);
+ SET_FLAG(ri->flags, BGP_INFO_VALID);
+
+ /* L3-VNI goes in the label2 field */
+ bgp_info_extra_get(ri);
+ vni2label(bgp_vrf->l3vni, &label);
+ memcpy(&ri->extra->label2, &label, BGP_LABEL_BYTES);
+
+ /* add the route entry to route node*/
+ bgp_info_add(rn, ri);
+ } else {
+
+ tmp_ri = local_ri;
+ if (!attrhash_cmp(tmp_ri->attr, attr)) {
+
+ /* attribute changed */
+ *route_changed = 1;
+
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(attr);
+ bgp_info_set_flag(rn, tmp_ri, BGP_INFO_ATTR_CHANGED);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
+ bgp_info_restore(rn, tmp_ri);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&tmp_ri->attr);
+ tmp_ri->attr = attr_new;
+ tmp_ri->uptime = bgp_clock();
+ }
+ }
+ return 0;
+}
+
+/* update evpn type-5 route entry */
+static int update_evpn_type5_route(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct attr attr;
+ struct bgp_node *rn = NULL;
+ struct bgp *bgp_def = NULL;
+ int route_changed = 0;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return -1;
+
+ /* build path attribute for this route */
+ memset(&attr, 0, sizeof(struct attr));
+ bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
+ attr.nexthop = bgp_vrf->originator_ip;
+ attr.mp_nexthop_global_in = bgp_vrf->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ memcpy(&attr.rmac, &bgp_vrf->rmac, sizeof(struct ethaddr));
+
+ /* Setup RT and encap extended community */
+ build_evpn_type5_route_extcomm(bgp_vrf, &attr);
+
+ /* get the route node in global table */
+ rn = bgp_afi_node_get(bgp_def->rib[afi][safi], afi, safi,
+ (struct prefix *)evp,
+ &bgp_vrf->vrf_prd);
+ assert(rn);
+
+ /* create or update the route entry within the route node */
+ update_evpn_type5_route_entry(bgp_def, bgp_vrf,
+ afi, safi,
+ rn, &attr, &route_changed);
+
+ /* schedule for processing and unlock node */
+ if (route_changed) {
+ bgp_process(bgp_def, rn, afi, safi);
+ bgp_unlock_node(rn);
+ }
+
+ /* uninten temporary */
+ aspath_unintern(&attr.aspath);
+ return 0;
+}
+
/*
* Create or update EVPN route entry. This could be in the VNI route table
* or the global route table.
attr.mp_nexthop_global_in = vpn->originator_ip;
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
attr.sticky = CHECK_FLAG(flags, ZEBRA_MAC_TYPE_STICKY) ? 1 : 0;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL);
+ bgpevpn_get_rmac(vpn, &attr.rmac);
+ vni2label(vpn->vni, &(attr.label));
/* Set up RT and ENCAP extended community. */
- build_evpn_route_extcomm(vpn, &attr);
+ build_evpn_route_extcomm(vpn, &attr,
+ IS_EVPN_PREFIX_IPADDR_V4(p) ?
+ AFI_IP : AFI_IP6);
/* First, create (or fetch) route node within the VNI. */
/* NOTE: There is no RD here. */
return 0;
}
+/* Delete EVPN type5 route entry from global table */
+static void delete_evpn_type5_route_entry(struct bgp *bgp_def,
+ struct bgp *bgp_vrf,
+ afi_t afi, safi_t safi,
+ struct bgp_node *rn,
+ struct bgp_info **ri)
+{
+ struct bgp_info *tmp_ri = NULL;
+
+ *ri = NULL;
+
+ /* find the matching route entry */
+ for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next)
+ if (tmp_ri->peer == bgp_def->peer_self
+ && tmp_ri->type == ZEBRA_ROUTE_BGP
+ && tmp_ri->sub_type == BGP_ROUTE_STATIC)
+ break;
+
+ *ri = tmp_ri;
+
+ /* Mark route for delete. */
+ if (tmp_ri)
+ bgp_info_delete(rn, tmp_ri);
+}
+
+/* Delete EVPN type5 route */
+static int delete_evpn_type5_route(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_node *rn = NULL;
+ struct bgp_info *ri = NULL;
+ struct bgp *bgp_def = NULL; /* default bgp instance */
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return -1;
+
+ /* locate the global route entry for this type-5 prefix */
+ rn = bgp_afi_node_lookup(bgp_def->rib[afi][safi], afi, safi,
+ (struct prefix *)evp, &bgp_vrf->vrf_prd);
+ if (!rn)
+ return 0;
+
+ delete_evpn_type5_route_entry(bgp_def, bgp_vrf, afi, safi, rn, &ri);
+ if (ri)
+ bgp_process(bgp_def, rn, afi, safi);
+ bgp_unlock_node(rn);
+ return 0;
+}
+
/*
* Delete EVPN route entry. This could be in the VNI route table
* or the global route table.
struct bgp_info *ri;
struct attr attr;
struct attr attr_sticky;
+ struct attr attr_ip6;
+ struct attr attr_sticky_ip6;
struct attr *attr_new;
afi = AFI_L2VPN;
safi = SAFI_EVPN;
memset(&attr, 0, sizeof(struct attr));
memset(&attr_sticky, 0, sizeof(struct attr));
+ memset(&attr_ip6, 0, sizeof(struct attr));
+ memset(&attr_sticky_ip6, 0, sizeof(struct attr));
/* Build path-attribute - all type-2 routes for this VNI will share the
* same path attribute.
attr.nexthop = vpn->originator_ip;
attr.mp_nexthop_global_in = vpn->originator_ip;
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ bgpevpn_get_rmac(vpn, &attr.rmac);
attr_sticky.nexthop = vpn->originator_ip;
attr_sticky.mp_nexthop_global_in = vpn->originator_ip;
attr_sticky.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
attr_sticky.sticky = 1;
+ bgpevpn_get_rmac(vpn, &attr_sticky.rmac);
+ bgp_attr_default_set(&attr_ip6, BGP_ORIGIN_IGP);
+ bgp_attr_default_set(&attr_sticky_ip6, BGP_ORIGIN_IGP);
+ attr_ip6.nexthop = vpn->originator_ip;
+ attr_ip6.mp_nexthop_global_in = vpn->originator_ip;
+ attr_ip6.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ bgpevpn_get_rmac(vpn, &attr_ip6.rmac);
+ attr_sticky_ip6.nexthop = vpn->originator_ip;
+ attr_sticky_ip6.mp_nexthop_global_in = vpn->originator_ip;
+ attr_sticky_ip6.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr_sticky_ip6.sticky = 1;
+ bgpevpn_get_rmac(vpn, &attr_sticky_ip6.rmac);
/* Set up RT, ENCAP and sticky MAC extended community. */
- build_evpn_route_extcomm(vpn, &attr);
- build_evpn_route_extcomm(vpn, &attr_sticky);
+ build_evpn_route_extcomm(vpn, &attr, AFI_IP);
+ build_evpn_route_extcomm(vpn, &attr_sticky, AFI_IP);
+ build_evpn_route_extcomm(vpn, &attr_ip6, AFI_IP6);
+ build_evpn_route_extcomm(vpn, &attr_sticky_ip6, AFI_IP6);
/* Walk this VNI's route table and update local type-2 routes. For any
* routes updated, update corresponding entry in the global table too.
if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
continue;
- if (evpn_route_is_sticky(bgp, rn))
- update_evpn_route_entry(bgp, vpn, afi, safi, rn,
- &attr_sticky, 0, 1, &ri, 0);
- else
- update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr,
- 0, 1, &ri, 0);
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ if (evpn_route_is_sticky(bgp, rn))
+ update_evpn_route_entry(bgp, vpn, afi, safi, rn,
+ &attr_sticky, 0, 1,
+ &ri, 0);
+ else
+ update_evpn_route_entry(bgp, vpn, afi, safi, rn,
+ &attr, 0, 1, &ri, 0);
+ } else {
+ if (evpn_route_is_sticky(bgp, rn))
+ update_evpn_route_entry(bgp, vpn, afi, safi, rn,
+ &attr_sticky_ip6, 0, 1,
+ &ri, 0);
+ else
+ update_evpn_route_entry(bgp, vpn, afi, safi, rn,
+ &attr_ip6, 0, 1,
+ &ri, 0);
+ }
/* If a local route exists for this prefix, we need to update
* the global routing table too.
return update_routes_for_vni(bgp, vpn);
}
+/*
+ * Install route entry into the VRF routing table and invoke route selection.
+ */
+static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp,
+ struct bgp_info *parent_ri)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ struct attr *attr_new;
+ int ret = 0;
+ struct prefix p;
+ struct prefix *pp = &p;
+ afi_t afi = 0;
+ safi_t safi = 0;
+ char buf[PREFIX_STRLEN];
+ char buf1[PREFIX_STRLEN];
+
+ memset(pp, 0, sizeof(struct prefix));
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ ip_prefix_from_type2_prefix(evp, pp);
+ else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
+ ip_prefix_from_type5_prefix(evp, pp);
+
+ if (bgp_debug_zebra(NULL)) {
+ zlog_debug("installing evpn prefix %s as ip prefix %s in vrf %s",
+ prefix2str(evp, buf, sizeof(buf)),
+ prefix2str(pp, buf1, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+
+ /* Create (or fetch) route within the VRF. */
+ /* NOTE: There is no RD here. */
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp);
+ } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ afi = AFI_IP6;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_get(bgp_vrf->rib[afi][safi], pp);
+ } else
+ return 0;
+
+ /* Check if route entry is already present. */
+ for (ri = rn->info; ri; ri = ri->next)
+ if (ri->extra
+ && (struct bgp_info *)ri->extra->parent == parent_ri)
+ break;
+
+ if (!ri) {
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_ri->attr);
+
+ /* Create new route with its attribute. */
+ ri = info_make(parent_ri->type, parent_ri->sub_type, 0,
+ parent_ri->peer, attr_new, rn);
+ SET_FLAG(ri->flags, BGP_INFO_VALID);
+ bgp_info_extra_get(ri);
+ ri->extra->parent = parent_ri;
+ if (parent_ri->extra)
+ memcpy(&ri->extra->label, &parent_ri->extra->label,
+ BGP_LABEL_BYTES);
+ bgp_info_add(rn, ri);
+ } else {
+ if (attrhash_cmp(ri->attr, parent_ri->attr)
+ && !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) {
+ bgp_unlock_node(rn);
+ return 0;
+ }
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_ri->attr);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+ bgp_info_restore(rn, ri);
+
+ /* Mark if nexthop has changed. */
+ if (!IPV4_ADDR_SAME(&ri->attr->nexthop, &attr_new->nexthop))
+ SET_FLAG(ri->flags, BGP_INFO_IGP_CHANGED);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&ri->attr);
+ ri->attr = attr_new;
+ ri->uptime = bgp_clock();
+ }
+
+ /* Perform route selection and update zebra, if required. */
+ bgp_process(bgp_vrf, rn, afi, safi);
+
+ return ret;
+}
+
/*
* Install route entry into the VNI routing table and invoke route selection.
*/
return ret;
}
+/*
+ * Uninstall route entry from the VRF routing table and send message
+ * to zebra, if appropriate.
+ */
+static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
+ struct prefix_evpn *evp,
+ struct bgp_info *parent_ri)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ int ret = 0;
+ struct prefix p;
+ struct prefix *pp = &p;
+ afi_t afi = 0;
+ safi_t safi = 0;
+ char buf[PREFIX_STRLEN];
+ char buf1[PREFIX_STRLEN];
+
+ memset(pp, 0, sizeof(struct prefix));
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ ip_prefix_from_type2_prefix(evp, pp);
+ else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
+ ip_prefix_from_type5_prefix(evp, pp);
+
+ if (bgp_debug_zebra(NULL)) {
+ zlog_debug("uninstalling evpn prefix %s as ip prefix %s in vrf %s",
+ prefix2str(evp, buf, sizeof(buf)),
+ prefix2str(pp, buf1, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+
+ /* Locate route within the VRF. */
+ /* NOTE: There is no RD here. */
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp);
+ } else {
+ afi = AFI_IP6;
+ safi = SAFI_UNICAST;
+ rn = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp);
+ }
+
+ if (!rn)
+ return 0;
+
+ /* Find matching route entry. */
+ for (ri = rn->info; ri; ri = ri->next)
+ if (ri->extra
+ && (struct bgp_info *)ri->extra->parent == parent_ri)
+ break;
+
+ if (!ri)
+ return 0;
+
+ /* Mark entry for deletion */
+ bgp_info_delete(rn, ri);
+
+ /* Perform route selection and update zebra, if required. */
+ bgp_process(bgp_vrf, rn, afi, safi);
+
+ /* Unlock route node. */
+ bgp_unlock_node(rn);
+
+ return ret;
+}
+
/*
* Uninstall route entry from the VNI routing table and send message
* to zebra, if appropriate.
return ret;
}
+/*
+ * Given a route entry and a VRF, see if this route entry should be
+ * imported into the VRF i.e., RTs match.
+ */
+static int is_route_matching_for_vrf(struct bgp *bgp_vrf,
+ struct bgp_info *ri)
+{
+ struct attr *attr = ri->attr;
+ struct ecommunity *ecom;
+ int i;
+
+ assert(attr);
+ /* Route should have valid RT to be even considered. */
+ if (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
+ return 0;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return 0;
+
+ /* For each extended community RT, see if it matches this VNI. If any RT
+ * matches, we're done.
+ */
+ for (i = 0; i < ecom->size; i++) {
+ u_char *pnt;
+ u_char type, sub_type;
+ struct ecommunity_val *eval;
+ struct ecommunity_val eval_tmp;
+ struct vrf_irt_node *irt;
+
+ /* Only deal with RTs */
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ continue;
+
+ /* See if this RT matches specified VNIs import RTs */
+ irt = lookup_vrf_import_rt(eval);
+ if (irt && irt->vrfs)
+ if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+ return 1;
+
+ /* Also check for non-exact match. In this, we mask out the AS
+ * and
+ * only check on the local-admin sub-field. This is to
+ * facilitate using
+ * VNI as the RT for EBGP peering too.
+ */
+ irt = NULL;
+ if (type == ECOMMUNITY_ENCODE_AS
+ || type == ECOMMUNITY_ENCODE_AS4
+ || type == ECOMMUNITY_ENCODE_IP) {
+ memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
+ mask_ecom_global_admin(&eval_tmp, eval);
+ irt = lookup_vrf_import_rt(&eval_tmp);
+ }
+ if (irt && irt->vrfs)
+ if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* Given a route entry and a VNI, see if this route entry should be
* imported into the VNI i.e., RTs match.
return 0;
}
+/*
+ * Install or uninstall mac-ip routes are appropriate for this
+ * particular VRF.
+ */
+static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf,
+ int install)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_node *rd_rn, *rn;
+ struct bgp_table *table;
+ struct bgp_info *ri;
+ int ret;
+ char buf[PREFIX_STRLEN];
+ struct bgp *bgp_def = NULL;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return -1;
+
+ /* Walk entire global routing table and evaluate routes which could be
+ * imported into this VRF. Note that we need to loop through all global
+ * routes to determine which route matches the import rt on vrf
+ */
+ for (rd_rn = bgp_table_top(bgp_def->rib[afi][safi]); rd_rn;
+ rd_rn = bgp_route_next(rd_rn)) {
+ table = (struct bgp_table *)(rd_rn->info);
+ if (!table)
+ continue;
+
+ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ /* if not mac-ip route skip this route */
+ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
+ continue;
+
+ /* if not a mac+ip route skip this route */
+ if (!(IS_EVPN_PREFIX_IPADDR_V4(evp) ||
+ IS_EVPN_PREFIX_IPADDR_V6(evp)))
+ continue;
+
+ for (ri = rn->info; ri; ri = ri->next) {
+ /* Consider "valid" remote routes applicable for
+ * this VRF.
+ */
+ if (!(CHECK_FLAG(ri->flags, BGP_INFO_VALID)
+ && ri->type == ZEBRA_ROUTE_BGP
+ && ri->sub_type == BGP_ROUTE_NORMAL))
+ continue;
+
+ if (is_route_matching_for_vrf(bgp_vrf, ri)) {
+ if (install)
+ ret =
+ install_evpn_route_entry_in_vrf(
+ bgp_vrf, evp, ri);
+ else
+ ret =
+ uninstall_evpn_route_entry_in_vrf(
+ bgp_vrf, evp, ri);
+
+ if (ret) {
+ zlog_err(
+ "Failed to %s EVPN %s route in VRF %s",
+ install ? "install"
+ : "uninstall",
+ prefix2str(evp, buf,
+ sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ return ret;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
/*
* Install or uninstall routes of specified type that are appropriate for this
* particular VNI.
return 0;
}
+/* Install any existing remote routes applicable for this VRF into VRF RIB. This
+ * is invoked upon l3vni-add or l3vni import rt change
+ */
+static int install_routes_for_vrf(struct bgp *bgp_vrf)
+{
+ install_uninstall_routes_for_vrf(bgp_vrf, 1);
+ return 0;
+}
+
/*
* Install any existing remote routes applicable for this VNI into its
* routing table. This is invoked when a VNI becomes "live" or its Import
1);
}
+/* uninstall routes from l3vni vrf. */
+static int uninstall_routes_for_vrf(struct bgp *bgp_vrf)
+{
+ install_uninstall_routes_for_vrf(bgp_vrf, 0);
+ return 0;
+}
+
/*
* Uninstall any existing remote routes for this VNI. One scenario in which
* this is invoked is upon an import RT change.
0);
}
+/*
+ * Install or uninstall route in matching VRFs (list).
+ */
+static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi,
+ safi_t safi, struct prefix_evpn *evp,
+ struct bgp_info *ri,
+ struct list *vrfs, int install)
+{
+ char buf[PREFIX2STR_BUFFER];
+ struct bgp *bgp_vrf;
+ struct listnode *node, *nnode;
+
+ /* Only type-2/type-5 routes go into a VRF */
+ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
+ return 0;
+
+ /* if it is type-2 route and not a mac+ip route skip this route */
+ if ((evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) &&
+ !(IS_EVPN_PREFIX_IPADDR_V4(evp) || IS_EVPN_PREFIX_IPADDR_V6(evp)))
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) {
+ int ret;
+
+ if (install)
+ ret = install_evpn_route_entry_in_vrf(bgp_vrf,
+ evp, ri);
+ else
+ ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf,
+ evp, ri);
+
+ if (ret) {
+ zlog_err("%u: Failed to %s prefix %s in VRF %s",
+ bgp_def->vrf_id,
+ install ? "install" : "uninstall",
+ prefix2str(evp, buf, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
/*
* Install or uninstall route in matching VNIs (list).
*/
assert(attr);
- /* Only type-2 and type-3 routes go into a L2 VNI. */
+ /* Only type-2 and type-3 and type-5 are supported currently */
if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
- || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE))
+ || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
return 0;
/* If we don't have Route Target, nothing much to do. */
if (!ecom || !ecom->size)
return -1;
- /* For each extended community RT, see which VNIs match and import
- * the route into matching VNIs.
+ /* For each extended community RT, see which VNIs/VRFs match and import
+ * the route into matching VNIs/VRFs.
*/
for (i = 0; i < ecom->size; i++) {
u_char *pnt;
u_char type, sub_type;
struct ecommunity_val *eval;
struct ecommunity_val eval_tmp;
- struct irt_node *irt;
+ struct irt_node *irt; /* import rt for l2vni */
+ struct vrf_irt_node *vrf_irt; /* import rt for l3vni */
/* Only deal with RTs */
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
if (sub_type != ECOMMUNITY_ROUTE_TARGET)
continue;
- /* Are we interested in this RT? */
+ /* Import route into matching l2-vnis (type-2/type-3 routes go
+ * into l2vni table)
+ */
irt = lookup_import_rt(bgp, eval);
if (irt && irt->vnis)
install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri,
irt->vnis, import);
- /* Also check for non-exact match. In this, we mask out the AS
- * and
- * only check on the local-admin sub-field. This is to
- * facilitate using
+ /* Import route into matching l3-vnis (type-2/type-5 routes go
+ * into l3vni/vrf table)
+ */
+ vrf_irt = lookup_vrf_import_rt(eval);
+ if (vrf_irt && vrf_irt->vrfs)
+ install_uninstall_route_in_vrfs(bgp, afi, safi, evp, ri,
+ vrf_irt->vrfs, import);
+
+ /* Also check for non-exact match. In this,
+ * we mask out the AS and
+ * only check on the local-admin sub-field.
+ * This is to facilitate using
* VNI as the RT for EBGP peering too.
*/
irt = NULL;
+ vrf_irt = NULL;
if (type == ECOMMUNITY_ENCODE_AS
|| type == ECOMMUNITY_ENCODE_AS4
|| type == ECOMMUNITY_ENCODE_IP) {
memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
mask_ecom_global_admin(&eval_tmp, eval);
irt = lookup_import_rt(bgp, &eval_tmp);
+ vrf_irt = lookup_vrf_import_rt(&eval_tmp);
}
if (irt && irt->vnis)
install_uninstall_route_in_vnis(bgp, afi, safi, evp, ri,
irt->vnis, import);
+ if (vrf_irt && vrf_irt->vrfs)
+ install_uninstall_route_in_vrfs(bgp, afi, safi, evp,
+ ri, vrf_irt->vrfs,
+ import);
}
- return 0;
+ return 0;
+}
+
+/* delete and withdraw all ipv4 and ipv6 routes in the vrf table as type-5
+ * routes */
+static void delete_withdraw_vrf_routes(struct bgp *bgp_vrf)
+{
+ /* delete all ipv4 routes and withdraw from peers */
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST);
+
+ /* delete all ipv6 routes and withdraw from peers */
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST);
+}
+
+/* update and advertise all ipv4 and ipv6 routes in thr vrf table as type-5
+ * routes */
+static void update_advertise_vrf_routes(struct bgp *bgp_vrf)
+{
+ /* update all ipv4 routes */
+ bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST);
+
+ /* update all ipv6 routes */
+ bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST);
+}
+
+/*
+ * update and advertise local routes for a VRF as type-5 routes.
+ * This is invoked upon RD change for a VRF. Note taht the processing is only
+ * done in the global route table using the routes which already exist in the
+ * VRF routing table
+ */
+static void update_router_id_vrf(struct bgp *bgp_vrf)
+{
+ /* skip if the RD is configured */
+ if (is_vrf_rd_configured(bgp_vrf))
+ return;
+
+ /* derive the RD for the VRF based on new router-id */
+ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf);
+
+ /* update advertise ipv4|ipv6 routes as type-5 routes */
+ update_advertise_vrf_routes(bgp_vrf);
+}
+
+/*
+ * Delete and withdraw all type-5 routes for the RD corresponding to VRF.
+ * This is invoked upon VRF RD change. The processing is done only from global
+ * table.
+ */
+static void withdraw_router_id_vrf(struct bgp *bgp_vrf)
+{
+ /* skip if the RD is configured */
+ if (is_vrf_rd_configured(bgp_vrf))
+ return;
+
+ /* delete/withdraw ipv4|ipv6 routes as type-5 routes */
+ delete_withdraw_vrf_routes(bgp_vrf);
}
/*
/* Make EVPN prefix. */
memset(&p, 0, sizeof(struct prefix_evpn));
p.family = AF_EVPN;
+ p.prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
/* Additional information outside of prefix - ESI and GW IP */
pfx += 4;
memcpy(&evpn.gw_ip.ipv4, pfx, 4);
pfx += 4;
- p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV4;
} else {
SET_IPADDR_V6(&p.prefix.ip);
memcpy(&p.prefix.ip.ipaddr_v6, pfx, 16);
pfx += 16;
memcpy(&evpn.gw_ip.ipv6, pfx, 16);
pfx += 16;
- p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV6;
}
label_pnt = (mpls_label_t *)pfx;
return;
p_evpn_p = &(p->u.prefix_evpn);
+ /* len denites the total len of IP and GW-IP in the route
+ IP and GW-IP have to be both ipv4 or ipv6
+ */
if (IS_IPADDR_V4(&p_evpn_p->ip))
- len = 8; /* ipv4 */
+ len = 8; /* IP and GWIP are both ipv4 */
else
- len = 32; /* ipv6 */
+ len = 32; /* IP and GWIP are both ipv6 */
/* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */
stream_putc(s, 8 + 10 + 4 + 1 + len + 3);
stream_put(s, prd->val, 8);
bgp_evpn_free(bgp, vpn);
}
+/*
+ * Derive AUTO import RT for BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_def = NULL;
+
+ form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+
+ /* Map RT to VRF */
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return;
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+}
+
+/*
+ * Delete AUTO import RT from BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf)
+{
+ evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
+}
+
+/*
+ * Derive AUTO export RT for BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf)
+{
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
+ form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
+}
+
+/*
+ * Delete AUTO export RT from BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_export_delete_for_vrf(struct bgp *bgp_vrf)
+{
+ evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
+}
+
+static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_def = NULL;
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return;
+
+ /* update all type-5 routes */
+ update_advertise_vrf_routes(bgp_vrf);
+
+ /* update all type-2 routes */
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
+ update_routes_for_vni(bgp_def, vpn);
+}
/*
* Public functions.
*/
+/* withdraw type-5 route corresponding to ip prefix */
+void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn,
+ afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct prefix_evpn evp;
+ char buf[PREFIX_STRLEN];
+
+ build_type5_prefix_from_ip_prefix(&evp, &rn->p);
+ ret = delete_evpn_type5_route(bgp_vrf, &evp);
+ if (ret) {
+ zlog_err(
+ "%u failed to delete type-5 route for prefix %s in vrf %s",
+ bgp_vrf->vrf_id,
+ prefix2str(&rn->p, buf, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+}
+
+/* withdraw all type-5 routes for an address family */
+void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_table *table = NULL;
+ struct bgp_node *rn = NULL;
+
+ if (!advertise_type5_routes(bgp_vrf, afi))
+ return;
+
+ table = bgp_vrf->rib[afi][safi];
+ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn))
+ bgp_evpn_withdraw_type5_route(bgp_vrf, rn, afi, safi);
+
+}
+
+/* advertise ip prefix as type-5 route*/
+void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn,
+ afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct prefix_evpn evp;
+ char buf[PREFIX_STRLEN];
+
+ if (!advertise_type5_routes(bgp_vrf, afi))
+ return;
+
+ if (!rn->info)
+ return;
+
+ /* only advertise subnet routes as type-5 */
+ if (is_host_route(&rn->p))
+ return;
+
+ build_type5_prefix_from_ip_prefix(&evp, &rn->p);
+ ret = update_evpn_type5_route(bgp_vrf, &evp);
+ if (ret) {
+ zlog_err(
+ "%u failed to create type-5 route for prefix %s in vrf %s",
+ bgp_vrf->vrf_id,
+ prefix2str(&rn->p, buf, sizeof(buf)),
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+}
+
+/* advertise all type-5 routes for an address family */
+void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_table *table = NULL;
+ struct bgp_node *rn = NULL;
+
+ table = bgp_vrf->rib[afi][safi];
+ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn))
+ bgp_evpn_advertise_type5_route(bgp_vrf, rn, afi, safi);
+}
+
+void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni,
+ struct list *rtl)
+{
+ struct listnode *node, *nnode, *node_to_del;
+ struct ecommunity *ecom, *ecom_auto;
+ struct ecommunity_val eval;
+
+ encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
+
+ ecom_auto = ecommunity_new();
+ ecommunity_add_val(ecom_auto, &eval);
+ node_to_del = NULL;
+
+ for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecom_auto)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(rtl, node_to_del);
+
+ ecommunity_free(&ecom_auto);
+}
+
+void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd)
+{
+ /* uninstall routes from vrf */
+ uninstall_routes_for_vrf(bgp_vrf);
+
+ /* Cleanup the RT to VRF mapping */
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+
+ /* Remove auto generated RT */
+ evpn_auto_rt_import_delete_for_vrf(bgp_vrf);
+
+ /* Add the newly configured RT to RT list */
+ listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd);
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+
+ /* map VRF to its RTs */
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+
+ /* install routes matching the new VRF */
+ install_routes_for_vrf(bgp_vrf);
+}
+
+void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel)
+{
+ struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
+ struct ecommunity *ecom = NULL;
+
+ /* uninstall routes from vrf */
+ uninstall_routes_for_vrf(bgp_vrf);
+
+ /* Cleanup the RT to VRF mapping */
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+
+ /* remove the RT from the RT list */
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomdel)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del);
+
+ /* fallback to auto import rt, if this was the last RT */
+ if (list_isempty(bgp_vrf->vrf_import_rtl)) {
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+ evpn_auto_rt_import_add_for_vrf(bgp_vrf);
+ }
+
+ /* map VRFs to its RTs */
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+
+ /* install routes matching this new RT */
+ install_routes_for_vrf(bgp_vrf);
+}
+
+void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd)
+{
+ /* remove auto-generated RT */
+ evpn_auto_rt_export_delete_for_vrf(bgp_vrf);
+
+ /* Add the new RT to the RT list */
+ listnode_add_sort(bgp_vrf->vrf_export_rtl, ecomadd);
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
+
+ bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+
+}
+
+void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel)
+{
+ struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
+ struct ecommunity *ecom = NULL;
+
+ /* Remove the RT from the RT list */
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomdel)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(bgp_vrf->vrf_export_rtl, node_to_del);
+
+ /* fall back to auto-generated RT if this was the last RT */
+ if (bgp_vrf->vrf_export_rtl && list_isempty(bgp_vrf->vrf_export_rtl)) {
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
+ evpn_auto_rt_export_add_for_vrf(bgp_vrf);
+ }
+
+ bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+}
+
/*
* Handle change to BGP router id. This is invoked twice by the change
* handler, first before the router id has been changed and then after
* the router id has been changed. The first invocation will result in
- * local routes for all VNIs being deleted and withdrawn and the next
+ * local routes for all VNIs/VRF being deleted and withdrawn and the next
* will result in the routes being re-advertised.
*/
void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw)
{
- if (withdraw)
+ if (withdraw) {
+
+ /* delete and withdraw all the type-5 routes
+ stored in the global table for this vrf
+ */
+ withdraw_router_id_vrf(bgp);
+
+ /* delete all the VNI routes (type-2/type-3) routes for all the
+ * L2-VNIs
+ */
hash_iterate(bgp->vnihash,
(void (*)(struct hash_backet *,
void *))withdraw_router_id_vni,
bgp);
- else
+ } else {
+
+ /* advertise all routes in the vrf as type-5 routes with the new
+ * RD
+ */
+ update_router_id_vrf(bgp);
+
+ /* advertise all the VNI routes (type-2/type-3) routes with the
+ * new RD
+ */
hash_iterate(bgp->vnihash,
(void (*)(struct hash_backet *,
void *))update_router_id_vni,
bgp);
+ }
}
/*
return update_routes_for_vni(bgp, vpn);
}
+void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf,
+ int withdraw)
+{
+ if (withdraw)
+ delete_withdraw_vrf_routes(bgp_vrf);
+ else
+ update_advertise_vrf_routes(bgp_vrf);
+}
+
/*
* Handle change to RD. This is invoked twice by the change handler,
* first before the RD has been changed and then after the RD has
inet_ntop(family, &p->prefix.ip.ip.addr, buf2,
PREFIX2STR_BUFFER));
}
+ } else if (p->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
+ snprintf(buf, len, "[%d]:[0]:[%d]:[%s]",
+ p->prefix.route_type,
+ p->prefix.ip_prefix_length,
+ IS_EVPN_PREFIX_IPADDR_V4(p) ?
+ inet_ntoa(p->prefix.ip.ipaddr_v4) :
+ inet6_ntoa(p->prefix.ip.ipaddr_v6));
} else {
/* For EVPN route types not supported yet. */
snprintf(buf, len, "(unsupported route type %d)",
return 0;
}
+/*
+ * Map the RTs (configured or automatically derived) of a VRF to the VRF.
+ * The mapping will be used during route processing.
+ * bgp_def: default bgp instance
+ * bgp_vrf: specific bgp vrf instance on which RT is configured
+ */
+void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)
+{
+ int i = 0;
+ struct ecommunity_val *eval = NULL;
+ struct listnode *node = NULL, *nnode = NULL;
+ struct ecommunity *ecom = NULL;
+
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ for (i = 0; i < ecom->size; i++) {
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i
+ * ECOMMUNITY_SIZE));
+ map_vrf_to_rt(bgp_vrf, eval);
+ }
+ }
+}
+
+/*
+ * Unmap the RTs (configured or automatically derived) of a VRF from the VRF.
+ */
+void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf)
+{
+ int i;
+ struct ecommunity_val *eval;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ for (i = 0; i < ecom->size; i++) {
+ struct vrf_irt_node *irt;
+ struct ecommunity_val eval_tmp;
+
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i
+ * ECOMMUNITY_SIZE));
+ /* If using "automatic" RT, we only care about the
+ * local-admin sub-field.
+ * This is to facilitate using VNI as the RT for EBGP
+ * peering too.
+ */
+ memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_IMPORT_RT_CFGD))
+ mask_ecom_global_admin(&eval_tmp, eval);
+
+ irt = lookup_vrf_import_rt(&eval_tmp);
+ if (irt)
+ unmap_vrf_from_rt(bgp_vrf, irt);
+ }
+ }
+}
+
+
/*
* Map the RTs (configured or automatically derived) of a VNI to the VNI.
*/
void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
{
- form_auto_rt(bgp, vpn, vpn->import_rtl);
+ form_auto_rt(bgp, vpn->vni, vpn->import_rtl);
UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
/* Map RT to VNI */
*/
void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn)
{
- form_auto_rt(bgp, vpn, vpn->export_rtl);
+ form_auto_rt(bgp, vpn->vni, vpn->export_rtl);
UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
}
+/*
+ * Derive RD automatically for VNI using passed information - it
+ * is of the form RouterId:unique-id-for-vni.
+ */
+void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp)
+{
+ char buf[100];
+
+ bgp->vrf_prd.family = AF_UNSPEC;
+ bgp->vrf_prd.prefixlen = 64;
+ sprintf(buf, "%s:%hu", inet_ntoa(bgp->router_id), bgp->vrf_rd_id);
+ str2prefix_rd(buf, &bgp->vrf_prd);
+}
+
/*
* Derive RD automatically for VNI using passed information - it
* is of the form RouterId:unique-id-for-vni.
* Create a new vpn - invoked upon configuration or zebra notification.
*/
struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
- struct in_addr originator_ip)
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id)
{
struct bgpevpn *vpn;
/* Set values - RD and RT set to defaults. */
vpn->vni = vni;
vpn->originator_ip = originator_ip;
+ vpn->tenant_vrf_id = tenant_vrf_id;
/* Initialize route-target import and export lists */
vpn->import_rtl = list_new();
vpn->import_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp;
vpn->export_rtl = list_new();
vpn->export_rtl->cmp = (int (*)(void *, void *))evpn_route_target_cmp;
- bf_assign_index(bgp->rd_idspace, vpn->rd_id);
+ bf_assign_index(bm->rd_idspace, vpn->rd_id);
derive_rd_rt_for_vni(bgp, vpn);
/* Initialize EVPN route table. */
XFREE(MTYPE_BGP_EVPN, vpn);
return NULL;
}
+
+ /* add to l2vni list on corresponding vrf */
+ bgpevpn_link_to_l3vni(vpn);
+
QOBJ_REG(vpn, bgpevpn);
return vpn;
}
*/
void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
{
+ bgpevpn_unlink_from_l3vni(vpn);
bgp_table_unlock(vpn->route_table);
bgp_evpn_unmap_vni_from_its_rts(bgp, vpn);
list_delete_and_null(&vpn->import_rtl);
list_delete_and_null(&vpn->export_rtl);
- bf_release_index(bgp->rd_idspace, vpn->rd_id);
+ bf_release_index(bm->rd_idspace, vpn->rd_id);
hash_release(bgp->vnihash, vpn);
QOBJ_UNREG(vpn);
XFREE(MTYPE_BGP_EVPN, vpn);
return 0;
}
+static void link_l2vni_hash_to_l3vni(struct hash_backet *backet,
+ struct bgp *bgp_vrf)
+{
+ struct bgpevpn *vpn = NULL;
+ struct bgp *bgp_def = NULL;
+
+ bgp_def = bgp_get_default();
+ assert(bgp_def);
+
+ vpn = (struct bgpevpn *)backet->data;
+ if (vpn->tenant_vrf_id == bgp_vrf->vrf_id)
+ bgpevpn_link_to_l3vni(vpn);
+}
+
+int bgp_evpn_local_l3vni_add(vni_t l3vni,
+ vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct in_addr originator_ip)
+{
+ struct bgp *bgp_vrf = NULL; /* bgp VRF instance */
+ struct bgp *bgp_def = NULL; /* default bgp instance */
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+ as_t as = 0;
+
+ /* get the default instamce - required to get the AS number for VRF
+ * auto-creatio
+ */
+ bgp_def = bgp_get_default();
+ if (!bgp_def) {
+ zlog_err("Cannot process L3VNI %u ADD - default BGP instance not yet created",
+ l3vni);
+ return -1;
+ }
+ as = bgp_def->as;
+
+ /* if the BGP vrf instance doesnt exist - create one */
+ bgp_vrf = bgp_lookup_by_name(vrf_id_to_name(vrf_id));
+ if (!bgp_vrf) {
+
+ int ret = 0;
+
+ ret = bgp_get(&bgp_vrf, &as, vrf_id_to_name(vrf_id),
+ BGP_INSTANCE_TYPE_VRF);
+ switch (ret) {
+ case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET:
+ zlog_err("'bgp multiple-instance' not present\n");
+ return -1;
+ case BGP_ERR_AS_MISMATCH:
+ zlog_err("BGP is already running; AS is %u\n", as);
+ return -1;
+ case BGP_ERR_INSTANCE_MISMATCH:
+ zlog_err("BGP instance name and AS number mismatch\n");
+ return -1;
+ }
+
+ /* mark as auto created */
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO);
+ }
+
+ /* associate with l3vni */
+ bgp_vrf->l3vni = l3vni;
+
+ /* set the router mac - to be used in mac-ip routes for this vrf */
+ memcpy(&bgp_vrf->rmac, rmac, sizeof(struct ethaddr));
+
+ /* set the originator ip */
+ bgp_vrf->originator_ip = originator_ip;
+
+ /* auto derive RD/RT */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+ evpn_auto_rt_import_add_for_vrf(bgp_vrf);
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD))
+ evpn_auto_rt_export_add_for_vrf(bgp_vrf);
+ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf);
+
+ /* link all corresponding l2vnis */
+ hash_iterate(bgp_def->vnihash,
+ (void (*)(struct hash_backet *, void *))
+ link_l2vni_hash_to_l3vni,
+ bgp_vrf);
+
+ /* updates all corresponding local mac-ip routes */
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
+ update_routes_for_vni(bgp_def, vpn);
+
+ /* advertise type-5 routes if needed */
+ update_advertise_vrf_routes(bgp_vrf);
+
+ /* install all remote routes belonging to this l3vni into correspondng
+ * vrf */
+ install_routes_for_vrf(bgp_vrf);
+
+ return 0;
+}
+
+int bgp_evpn_local_l3vni_del(vni_t l3vni,
+ vrf_id_t vrf_id)
+{
+ struct bgp *bgp_vrf = NULL; /* bgp vrf instance */
+ struct bgp *bgp_def = NULL; /* default bgp instance */
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp_vrf) {
+ zlog_err("Cannot process L3VNI %u Del - Could not find BGP instance",
+ l3vni);
+ return -1;
+ }
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def) {
+ zlog_err("Cannot process L3VNI %u Del - Could not find default BGP instance",
+ l3vni);
+ return -1;
+ }
+
+ /* unimport remote routes from VRF, if it is AUTO vrf bgp_delete will
+ * take care of uninstalling the routes from zebra
+ */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))
+ uninstall_routes_for_vrf(bgp_vrf);
+
+ /* delete/withdraw all type-5 routes */
+ delete_withdraw_vrf_routes(bgp_vrf);
+
+ /* remove the l3vni from vrf instance */
+ bgp_vrf->l3vni = 0;
+
+ /* remove the Rmac from the BGP vrf */
+ memset(&bgp_vrf->rmac, 0, sizeof(struct ethaddr));
+
+ /* delete RD/RT */
+ if (bgp_vrf->vrf_import_rtl && !list_isempty(bgp_vrf->vrf_import_rtl)) {
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+ list_delete_all_node(bgp_vrf->vrf_import_rtl);
+ }
+ if (bgp_vrf->vrf_export_rtl && !list_isempty(bgp_vrf->vrf_export_rtl)) {
+ list_delete_all_node(bgp_vrf->vrf_export_rtl);
+ }
+
+ /* update all corresponding local mac-ip routes */
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
+ update_routes_for_vni(bgp_def, vpn);
+
+
+ /* Delete the instance if it was autocreated */
+ if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))
+ bgp_delete(bgp_vrf);
+
+ return 0;
+}
+
/*
* Handle del of a local VNI.
*/
* about is change to local-tunnel-ip.
*/
int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
- struct in_addr originator_ip)
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id)
{
struct bgpevpn *vpn;
struct prefix_evpn p;
/* Lookup VNI. If present and no change, exit. */
vpn = bgp_evpn_lookup_vni(bgp, vni);
if (vpn) {
+
+ /* update tenant_vrf_id if required */
+ if (vpn->tenant_vrf_id != tenant_vrf_id) {
+ bgpevpn_unlink_from_l3vni(vpn);
+ vpn->tenant_vrf_id = tenant_vrf_id;
+ bgpevpn_link_to_l3vni(vpn);
+
+ /* update all routes with new export RT for VRFs */
+ update_routes_for_vni(bgp, vpn);
+ }
+
if (is_vni_live(vpn)
&& IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip))
/* Probably some other param has changed that we don't
/* Create or update as appropriate. */
if (!vpn) {
- vpn = bgp_evpn_new(bgp, vni, originator_ip);
+ vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id);
if (!vpn) {
zlog_err(
"%u: Failed to allocate VNI entry for VNI %u - at Add",
if (bgp->import_rt_hash)
hash_free(bgp->import_rt_hash);
bgp->import_rt_hash = NULL;
+ if (bgp->vrf_import_rt_hash)
+ hash_free(bgp->vrf_import_rt_hash);
+ bgp->vrf_import_rt_hash = NULL;
if (bgp->vnihash)
hash_free(bgp->vnihash);
bgp->vnihash = NULL;
- bf_free(bgp->rd_idspace);
+ if (bgp->vrf_import_rtl)
+ list_delete_and_null(&bgp->vrf_import_rtl);
+ if (bgp->vrf_export_rtl)
+ list_delete_and_null(&bgp->vrf_export_rtl);
+ if (bgp->l2vnis)
+ list_delete_and_null(&bgp->l2vnis);
+ bf_release_index(bm->rd_idspace, bgp->vrf_rd_id);
}
/*
* Create
* VNI hash table
* hash for RT to VNI
- * unique rd id space for auto derivation of RD for VNIs
+ * assign a unique rd id for auto derivation of vrf_prd
*/
void bgp_evpn_init(struct bgp *bgp)
{
bgp->import_rt_hash =
hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
"BGP Import RT Hash");
- bf_init(bgp->rd_idspace, UINT16_MAX);
- /*assign 0th index in the bitfield, so that we start with id 1*/
- bf_assign_zero_index(bgp->rd_idspace);
+ bgp->vrf_import_rt_hash =
+ hash_create(vrf_import_rt_hash_key_make, vrf_import_rt_hash_cmp,
+ "BGP VRF Import RT Hash");
+ bgp->vrf_import_rtl = list_new();
+ bgp->vrf_import_rtl->cmp =
+ (int (*)(void *, void *))evpn_route_target_cmp;
+
+ bgp->vrf_export_rtl = list_new();
+ bgp->vrf_export_rtl->cmp =
+ (int (*)(void *, void *))evpn_route_target_cmp;
+ bgp->l2vnis = list_new();
+ bgp->l2vnis->cmp =
+ (int (*)(void *, void *))vni_hash_cmp;
+ bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id);
+
+}
+
+void bgp_evpn_vrf_delete(struct bgp *bgp_vrf)
+{
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
}
#define _QUAGGA_BGP_EVPN_H
#include "vxlan.h"
+#include "bgpd.h"
#define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */
+static inline int is_evpn_enabled(void)
+{
+ struct bgp *bgp = NULL;
+
+ bgp = bgp_get_default();
+ return bgp ? bgp->advertise_all_vni : 0;
+}
+
+extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf,
+ struct bgp_node *rn,
+ afi_t afi, safi_t safi);
+extern void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf,
+ struct bgp_node *rn,
+ afi_t afi, safi_t safi);
+extern void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi);
+extern void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi);
+extern void bgp_evpn_vrf_delete(struct bgp *bgp_vrf);
extern void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw);
extern char *bgp_evpn_label2str(mpls_label_t *label, char *buf, int len);
extern char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len);
extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
struct ethaddr *mac, struct ipaddr *ip,
u_char flags);
+extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct in_addr originator_ip);
+extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id);
extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni);
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
- struct in_addr originator_ip);
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id);
extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp);
extern void bgp_evpn_cleanup(struct bgp *bgp);
extern void bgp_evpn_init(struct bgp *bgp);
#define RT_ADDRSTRLEN 28
-/* EVPN prefix lengths. */
+/* EVPN prefix lengths. This reprsent the sizeof struct prefix_evpn */
#define EVPN_TYPE_2_ROUTE_PREFIXLEN 224
#define EVPN_TYPE_3_ROUTE_PREFIXLEN 224
+#define EVPN_TYPE_5_ROUTE_PREFIXLEN 224
/* EVPN route types. */
typedef enum {
*/
struct bgpevpn {
vni_t vni;
+ vrf_id_t tenant_vrf_id;
u_int32_t flags;
#define VNI_FLAG_CFGD 0x1 /* VNI is user configured */
#define VNI_FLAG_LIVE 0x2 /* VNI is "live" */
struct list *vnis;
};
+/* Mapping of Import RT to VRFs.
+ * The Import RTs of all VRFss are maintained in a hash table with each
+ * RT linking to all VRFs that will import routes matching this RT.
+ */
+struct vrf_irt_node {
+ /* RT */
+ struct ecommunity_val rt;
+
+ /* List of VNIs importing routes matching this RT. */
+ struct list *vrfs;
+};
+
+
#define RT_TYPE_IMPORT 1
#define RT_TYPE_EXPORT 2
#define RT_TYPE_BOTH 3
+static inline int is_vrf_rd_configured(struct bgp *bgp_vrf)
+{
+ return (CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_RD_CFGD));
+}
+
+static inline int bgp_evpn_vrf_rd_matches_existing(struct bgp *bgp_vrf,
+ struct prefix_rd *prd)
+{
+ return (memcmp(&bgp_vrf->vrf_prd.val, prd->val, ECOMMUNITY_SIZE) == 0);
+}
+
+static inline vni_t bgpevpn_get_l3vni(struct bgpevpn *vpn)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return 0;
+
+ return bgp_vrf->l3vni;
+}
+
+static inline void bgpevpn_get_rmac(struct bgpevpn *vpn, struct ethaddr *rmac)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ memset(rmac, 0, sizeof(struct ethaddr));
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return;
+ memcpy(rmac, &bgp_vrf->rmac, sizeof(struct ethaddr));
+}
+
+static inline struct list *bgpevpn_get_vrf_export_rtl(struct bgpevpn *vpn)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return NULL;
+
+ return bgp_vrf->vrf_export_rtl;
+}
+
+static inline struct list *bgpevpn_get_vrf_import_rtl(struct bgpevpn *vpn)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return NULL;
+
+ return bgp_vrf->vrf_import_rtl;
+}
+
+static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf || !bgp_vrf->l2vnis)
+ return;
+ listnode_delete(bgp_vrf->l2vnis, vpn);
+}
+
+static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf || !bgp_vrf->l2vnis)
+ return;
+ listnode_add_sort(bgp_vrf->l2vnis, vpn);
+}
+
static inline int is_vni_configured(struct bgpevpn *vpn)
{
return (CHECK_FLAG(vpn->flags, VNI_FLAG_CFGD));
return vni;
}
+static inline void encode_rmac_extcomm(struct ecommunity_val *eval,
+ struct ethaddr *rmac)
+{
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC;
+ memcpy(&eval->val[2], rmac, ETH_ALEN);
+}
+
static inline void encode_mac_mobility_extcomm(int static_mac, u_int32_t seq,
struct ecommunity_val *eval)
{
eval->val[7] = seq & 0xff;
}
+static inline void ip_prefix_from_type5_prefix(struct prefix_evpn *evp,
+ struct prefix *ip)
+{
+ memset(ip, 0, sizeof(struct prefix));
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ ip->family = AF_INET;
+ ip->prefixlen = evp->prefix.ip_prefix_length;
+ memcpy(&(ip->u.prefix4),
+ &(evp->prefix.ip.ip),
+ IPV4_MAX_BYTELEN);
+ } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ ip->family = AF_INET6;
+ ip->prefixlen = evp->prefix.ip_prefix_length;
+ memcpy(&(ip->u.prefix6),
+ &(evp->prefix.ip.ip),
+ IPV6_MAX_BYTELEN);
+ }
+}
+
+static inline void ip_prefix_from_type2_prefix(struct prefix_evpn *evp,
+ struct prefix *ip)
+{
+ memset(ip, 0, sizeof(struct prefix));
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp)) {
+ ip->family = AF_INET;
+ ip->prefixlen = IPV4_MAX_BITLEN;
+ memcpy(&(ip->u.prefix4),
+ &(evp->prefix.ip.ip),
+ IPV4_MAX_BYTELEN);
+ } else if (IS_EVPN_PREFIX_IPADDR_V6(evp)) {
+ ip->family = AF_INET6;
+ ip->prefixlen = IPV6_MAX_BITLEN;
+ memcpy(&(ip->u.prefix6),
+ &(evp->prefix.ip.ip),
+ IPV6_MAX_BYTELEN);
+ }
+}
+
static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
struct ethaddr *mac,
struct ipaddr *ip)
memcpy(&p->prefix.ip, ip, sizeof(*ip));
}
+static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp,
+ struct prefix *ip_prefix)
+{
+ struct ipaddr ip;
+
+ memset(&ip, 0, sizeof(struct ipaddr));
+ if (ip_prefix->family == AF_INET) {
+ ip.ipa_type = IPADDR_V4;
+ memcpy(&ip.ipaddr_v4, &ip_prefix->u.prefix4,
+ sizeof(struct in_addr));
+ } else {
+ ip.ipa_type = IPADDR_V6;
+ memcpy(&ip.ipaddr_v6, &ip_prefix->u.prefix6,
+ sizeof(struct in6_addr));
+ }
+
+ memset(evp, 0, sizeof(struct prefix_evpn));
+ evp->family = AF_EVPN;
+ evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN;
+ evp->prefix.ip_prefix_length = ip_prefix->prefixlen;
+ evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
+ evp->prefix.ip.ipa_type = ip.ipa_type;
+ memcpy(&evp->prefix.ip, &ip, sizeof(struct ipaddr));
+}
+
static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
struct in_addr originator_ip)
{
p->prefix.ip.ipaddr_v4 = originator_ip;
}
+static inline int advertise_type5_routes(struct bgp *bgp_vrf,
+ afi_t afi)
+{
+ if (!bgp_vrf->l3vni)
+ return 0;
+
+ if (afi == AFI_IP &&
+ CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV4_IN_EVPN))
+ return 1;
+
+ if (afi == AFI_IP6 &&
+ CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN))
+ return 1;
+
+ return 0;
+}
+extern void evpn_rt_delete_auto(struct bgp*, vni_t, struct list*);
+extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd);
+extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel);
+extern void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd);
+extern void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel);
extern int bgp_evpn_handle_export_rt_change(struct bgp *bgp,
struct bgpevpn *vpn);
+extern void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf, int withdraw);
extern void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn,
int withdraw);
extern int bgp_evpn_install_routes(struct bgp *bgp, struct bgpevpn *vpn);
extern int bgp_evpn_uninstall_routes(struct bgp *bgp, struct bgpevpn *vpn);
+extern void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf);
+extern void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf);
extern void bgp_evpn_map_vni_to_its_rts(struct bgp *bgp, struct bgpevpn *vpn);
extern void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp,
struct bgpevpn *vpn);
extern void bgp_evpn_derive_auto_rt_export(struct bgp *bgp,
struct bgpevpn *vpn);
extern void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn);
+extern void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp);
extern struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni);
extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
- struct in_addr originator_ip);
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id);
extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn);
#endif /* _BGP_EVPN_PRIVATE_H */
};
#if defined(HAVE_CUMULUS)
+static void display_vrf_import_rt(struct vty *vty,
+ struct vrf_irt_node *irt,
+ json_object *json)
+{
+ u_char *pnt;
+ u_char type, sub_type;
+ struct ecommunity_as eas;
+ struct ecommunity_ip eip;
+ struct listnode *node, *nnode;
+ struct bgp *tmp_bgp_vrf = NULL;
+ json_object *json_rt = NULL;
+ json_object *json_vrfs = NULL;
+ char rt_buf[RT_ADDRSTRLEN];
+
+ if (json) {
+ json_rt = json_object_new_object();
+ json_vrfs = json_object_new_array();
+ }
+
+ pnt = (u_char *)&irt->rt.val;
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ return;
+
+ memset(&eas, 0, sizeof(eas));
+ switch (type) {
+ case ECOMMUNITY_ENCODE_AS:
+ eas.as = (*pnt++ << 8);
+ eas.as |= (*pnt++);
+ pnt = ptr_get_be32(pnt, &eas.val);
+
+ snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ case ECOMMUNITY_ENCODE_IP:
+ memcpy(&eip.ip, pnt, 4);
+ pnt += 4;
+ eip.val = (*pnt++ << 8);
+ eip.val |= (*pnt++);
+
+ snprintf(rt_buf, RT_ADDRSTRLEN, "%s:%u", inet_ntoa(eip.ip),
+ eip.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ case ECOMMUNITY_ENCODE_AS4:
+ pnt = ptr_get_be32(pnt, &eas.val);
+ eas.val = (*pnt++ << 8);
+ eas.val |= (*pnt++);
+
+ snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ default:
+ return;
+ }
+
+ if (!json) {
+ vty_out(vty,
+ "\nList of VRFs importing routes with this route-target:\n");
+ }
+
+ for (ALL_LIST_ELEMENTS(irt->vrfs, node, nnode, tmp_bgp_vrf)) {
+ if (json)
+ json_object_array_add(
+ json_vrfs,
+ json_object_new_string(
+ vrf_id_to_name(
+ tmp_bgp_vrf->vrf_id)));
+ else
+ vty_out(vty, " %s\n",
+ vrf_id_to_name(tmp_bgp_vrf->vrf_id));
+ }
+
+ if (json) {
+ json_object_object_add(json_rt, "vrfs", json_vrfs);
+ json_object_object_add(json, rt_buf, json_rt);
+ }
+}
+
+static void show_vrf_import_rt_entry(struct hash_backet *backet,
+ void *args[])
+{
+ json_object *json = NULL;
+ struct vty *vty = NULL;
+ struct vrf_irt_node *irt = (struct vrf_irt_node *)backet->data;
+
+ vty = (struct vty *)args[0];
+ json = (struct json_object *)args[1];
+
+ display_vrf_import_rt(vty, irt, json);
+}
+
static void display_import_rt(struct vty *vty, struct irt_node *irt,
json_object *json)
{
static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
{
- char buf1[INET6_ADDRSTRLEN];
+ char buf1[RD_ADDRSTRLEN];
char *ecom_str;
struct listnode *node, *nnode;
struct ecommunity *ecom;
is_vni_live(vpn) ? "Yes" : "No");
json_object_string_add(
json, "rd",
- prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN));
+ prefix_rd2str(&vpn->prd, buf1, sizeof(buf1)));
json_object_string_add(json, "originatorIp",
inet_ntoa(vpn->originator_ip));
json_object_string_add(json, "advertiseGatewayMacip",
vty_out(vty, " (known to the kernel)");
vty_out(vty, "\n");
+ vty_out(vty, " Tenant-Vrf: %s\n",
+ vrf_id_to_name(vpn->tenant_vrf_id));
vty_out(vty, " RD: %s\n",
- prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN));
+ prefix_rd2str(&vpn->prd, buf1, sizeof(buf1)));
vty_out(vty, " Originator IP: %s\n",
inet_ntoa(vpn->originator_ip));
vty_out(vty, " Advertise-gw-macip : %s\n",
json_object_object_add(json, "exportRts", json_export_rtl);
}
+static void evpn_show_vrf_routes(struct vty *vty,
+ struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_def = NULL;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ int header = 1;
+ u_int32_t prefix_cnt, path_cnt;
+ struct bgp_table *table;
+
+ prefix_cnt = path_cnt = 0;
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return;
+
+ table = (struct bgp_table *)bgp_vrf->rib[AFI_L2VPN][SAFI_EVPN];
+ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
+ char prefix_str[BUFSIZ];
+
+ bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str,
+ sizeof(prefix_str));
+
+ if (rn->info) {
+ /* Overall header/legend displayed once. */
+ if (header) {
+ bgp_evpn_show_route_header(vty, bgp_def, NULL);
+ header = 0;
+ }
+ prefix_cnt++;
+ }
+
+ /* For EVPN, the prefix is displayed for each path (to fit in
+ * with code that already exists).
+ */
+ for (ri = rn->info; ri; ri = ri->next) {
+ route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, NULL);
+ path_cnt++;
+ }
+ }
+
+ if (prefix_cnt == 0)
+ vty_out(vty, "No EVPN prefixes exist for this VRF");
+ else
+ vty_out(vty, "\nDisplayed %u prefixes (%u paths)",
+ prefix_cnt, path_cnt);
+}
+
static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
struct vty *vty, struct in_addr vtep_ip,
json_object *json)
{
struct vty *vty;
json_object *json;
- json_object *json_vni;
- json_object *json_import_rtl;
- json_object *json_export_rtl;
+ json_object *json_vni = NULL;
+ json_object *json_import_rtl = NULL;
+ json_object *json_export_rtl = NULL;
struct bgpevpn *vpn = (struct bgpevpn *)backet->data;
char buf1[10];
- char buf2[INET6_ADDRSTRLEN];
+ char buf2[RD_ADDRSTRLEN];
char rt_buf[25];
char *ecom_str;
struct listnode *node, *nnode;
inet_ntoa(vpn->originator_ip));
json_object_string_add(
json_vni, "rd",
- prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN));
+ prefix_rd2str(&vpn->prd, buf2, sizeof(buf2)));
} else {
vty_out(vty, "%-1s %-10u %-15s %-21s", buf1, vpn->vni,
inet_ntoa(vpn->originator_ip),
break;
}
+ if (!json)
+ vty_out(vty, "%-37s", vrf_id_to_name(vpn->tenant_vrf_id));
+
if (json) {
char vni_str[VNI_STR_LEN];
}
#if defined(HAVE_CUMULUS)
-static void evpn_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn,
- struct list *rtl)
-{
- struct listnode *node, *nnode, *node_to_del;
- struct ecommunity *ecom, *ecom_auto;
- struct ecommunity_val eval;
-
- encode_route_target_as((bgp->as & 0xFFFF), vpn->vni, &eval);
-
- ecom_auto = ecommunity_new();
- ecommunity_add_val(ecom_auto, &eval);
- node_to_del = NULL;
-
- for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) {
- if (ecommunity_match(ecom, ecom_auto)) {
- ecommunity_free(&ecom);
- node_to_del = node;
- }
- }
-
- if (node_to_del)
- list_delete_node(rtl, node_to_del);
-
- ecommunity_free(&ecom_auto);
-}
static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
- evpn_rt_delete_auto(bgp, vpn, vpn->import_rtl);
+ evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl);
}
static void evpn_export_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
{
- evpn_rt_delete_auto(bgp, vpn, vpn->export_rtl);
+ evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl);
}
/*
bgp_evpn_handle_export_rt_change(bgp, vpn);
}
+/*
+ * Configure RD for VRF
+ */
+static void evpn_configure_vrf_rd(struct bgp *bgp_vrf,
+ struct prefix_rd *rd)
+{
+ /* If we have already advertise type-5 routes with a diffrent RD, we
+ * have to delete and withdraw them firs
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1);
+
+ /* update RD */
+ memcpy(&bgp_vrf->vrf_prd, rd, sizeof(struct prefix_rd));
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD);
+
+ /* We have a new RD for VRF.
+ * Advertise all type-5 routes again with the new RD
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0);
+}
+
+/*
+ * Unconfigure RD for VRF
+ */
+static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf)
+{
+ /* If we have already advertise type-5 routes with a diffrent RD, we
+ * have to delete and withdraw them firs
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1);
+
+ /* fall back to default RD */
+ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf);
+
+ /* We have a new RD for VRF.
+ * Advertise all type-5 routes again with the new RD
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0);
+}
+
/*
* Configure RD for a VNI (vty handler)
*/
vpn = bgp_evpn_lookup_vni(bgp, vni);
if (!vpn) {
- vpn = bgp_evpn_new(bgp, vni, bgp->router_id);
+ /* tenant vrf will be updated when we get local_vni_add from
+ * zebra
+ */
+ vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0);
if (!vpn) {
zlog_err(
"%u: Failed to allocate VNI entry for VNI %u - at Config",
return 0;
}
+/*
+ * Display import RT mapping to VRFs (vty handler)
+ * bgp_def: default bgp instance
+ */
+static void evpn_show_vrf_import_rts(struct vty *vty,
+ struct bgp *bgp_def,
+ json_object *json)
+{
+ void *args[2];
+
+ args[0] = vty;
+ args[1] = json;
+
+ hash_iterate(bgp_def->vrf_import_rt_hash,
+ (void (*)(struct hash_backet *, void *))
+ show_vrf_import_rt_entry,
+ args);
+}
+
/*
* Display import RT mapping to VNIs (vty handler)
*/
/* RD header and legend - once overall. */
if (rd_header && !json) {
vty_out(vty,
- "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:"
- "[MAC]\n");
+ "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]\n");
+ vty_out(vty,
+ "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
vty_out(vty,
- "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:"
- "[OrigIP]\n\n");
+ "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
rd_header = 0;
}
} else {
vty_out(vty, "Number of VNIs: %u\n", num_vnis);
vty_out(vty, "Flags: * - Kernel\n");
- vty_out(vty, " %-10s %-15s %-21s %-25s %-25s\n", "VNI",
- "Orig IP", "RD", "Import RT", "Export RT");
+ vty_out(vty, " %-10s %-15s %-21s %-25s %-25s %-37s\n", "VNI",
+ "Orig IP", "RD", "Import RT",
+ "Export RT", "Tenant-Vrf");
}
args[0] = vty;
static void write_vni_config(struct vty *vty, struct bgpevpn *vpn)
{
- char buf1[INET6_ADDRSTRLEN];
+ char buf1[RD_ADDRSTRLEN];
char *ecom_str;
struct listnode *node, *nnode;
struct ecommunity *ecom;
vty_out(vty, " vni %d\n", vpn->vni);
if (is_rd_configured(vpn))
vty_out(vty, " rd %s\n",
- prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN));
+ prefix_rd2str(&vpn->prd, buf1, sizeof(buf1)));
if (is_import_rt_configured(vpn)) {
for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode,
return CMD_SUCCESS;
}
+DEFUN (bgp_evpn_advertise_type5,
+ bgp_evpn_advertise_type5_cmd,
+ "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR,
+ "Advertise prefix routes\n"
+ BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR)
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
+ int idx_afi = 0;
+ int idx_safi = 0;
+ afi_t afi = 0;
+ safi_t safi = 0;
+
+ argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
+ argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+
+ if (!(afi == AFI_IP) || (afi == AFI_IP6)) {
+ vty_out(vty,
+ "%%only ipv4 or ipv6 address families are supported");
+ return CMD_WARNING;
+ }
+
+ if (safi != SAFI_UNICAST) {
+ vty_out(vty,
+ "%%only ipv4 unicast or ipv6 unicast are supported");
+ return CMD_WARNING;
+ }
+
+ if (afi == AFI_IP) {
+
+ /* if we are already advertising ipv4 prefix as type-5
+ * nothing to do
+ */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) {
+ SET_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV4_IN_EVPN);
+ bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
+ }
+ } else {
+
+ /* if we are already advertising ipv6 prefix as type-5
+ * nothing to do
+ */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) {
+ SET_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
+ bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_type5,
+ no_bgp_evpn_advertise_type5_cmd,
+ "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR,
+ NO_STR
+ "Advertise prefix routes\n"
+ BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR)
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
+ int idx_afi = 0;
+ int idx_safi = 0;
+ afi_t afi = 0;
+ safi_t safi = 0;
+
+ argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
+ argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+
+ if (!(afi == AFI_IP) || (afi == AFI_IP6)) {
+ vty_out(vty,
+ "%%only ipv4 or ipv6 address families are supported");
+ return CMD_WARNING;
+ }
+
+ if (safi != SAFI_UNICAST) {
+ vty_out(vty,
+ "%%only ipv4 unicast or ipv6 unicast are supported");
+ return CMD_WARNING;
+ }
+
+ if (afi == AFI_IP) {
+
+ /* if we are already advertising ipv4 prefix as type-5
+ * nothing to do
+ */
+ if (CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) {
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
+ UNSET_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV4_IN_EVPN);
+ }
+ } else {
+
+ /* if we are already advertising ipv6 prefix as type-5
+ * nothing to do
+ */
+ if (CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) {
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
+ UNSET_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
+ }
+ }
+ return CMD_SUCCESS;
+}
+
/*
* Display VNI information - for all or a specific VNI
*/
? "Enabled"
: "Disabled");
json_object_string_add(json, "advertiseAllVnis",
- bgp->advertise_all_vni
+ is_evpn_enabled()
? "Enabled"
: "Disabled");
} else {
/* Display all VNIs */
vty_out(vty, "Advertise All VNI flag: %s\n",
- bgp->advertise_all_vni ? "Enabled"
- : "Disabled");
+ is_evpn_enabled() ? "Enabled" : "Disabled");
}
evpn_show_all_vnis(vty, bgp, json);
return CMD_SUCCESS;
}
+/*
+ * Display per-VRF EVPN routing table.
+ */
+DEFUN(show_bgp_l2vpn_evpn_route_vrf, show_bgp_l2vpn_evpn_route_vrf_cmd,
+ "show bgp l2vpn evpn route vrf VRFNAME",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
+ "VRF\n"
+ "VRF Name\n")
+{
+ int vrf_idx = 6;
+ char *vrf_name = NULL;
+ struct bgp *bgp_vrf = NULL;
+
+ vrf_name = argv[vrf_idx]->arg;
+ bgp_vrf = bgp_lookup_by_name(vrf_name);
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ evpn_show_vrf_routes(vty, bgp_vrf);
+
+ return CMD_SUCCESS;
+}
+
/*
* Display per-VNI EVPN routing table.
*/
return CMD_SUCCESS;
}
+/*
+ * Display EVPN import route-target hash table
+ */
+DEFUN(show_bgp_l2vpn_evpn_vrf_import_rt,
+ show_bgp_l2vpn_evpn_vrf_import_rt_cmd,
+ "show bgp l2vpn evpn vrf-import-rt [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show vrf import route target\n"
+ JSON_STR)
+{
+ u_char uj = 0;
+ struct bgp *bgp_def = NULL;
+ json_object *json = NULL;
+
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
+ return CMD_WARNING;
+
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ evpn_show_vrf_import_rts(vty, bgp_def, json);
+
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+
+ return CMD_SUCCESS;
+}
+
/*
* Display EVPN import route-target hash table
*/
return CMD_SUCCESS;
}
+DEFUN (bgp_evpn_vrf_rd,
+ bgp_evpn_vrf_rd_cmd,
+ "rd ASN:NN_OR_IP-ADDRESS:NN",
+ "Route Distinguisher\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ int ret;
+ struct prefix_rd prd;
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ ret = str2prefix_rd(argv[1]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ /* If same as existing value, there is nothing more to do. */
+ if (bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd))
+ return CMD_SUCCESS;
+
+ /* Configure or update the RD. */
+ evpn_configure_vrf_rd(bgp_vrf, &prd);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vrf_rd,
+ no_bgp_evpn_vrf_rd_cmd,
+ "no rd ASN:NN_OR_IP-ADDRESS:NN",
+ NO_STR
+ "Route Distinguisher\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ int ret;
+ struct prefix_rd prd;
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ ret = str2prefix_rd(argv[2]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ /* Check if we should disallow. */
+ if (!is_vrf_rd_configured(bgp_vrf)) {
+ vty_out(vty, "%% RD is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+
+ if (!bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd)) {
+ vty_out(vty,
+ "%% RD specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+
+ evpn_unconfigure_vrf_rd(bgp_vrf);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vrf_rd_without_val,
+ no_bgp_evpn_vrf_rd_without_val_cmd,
+ "no rd",
+ NO_STR
+ "Route Distinguisher\n")
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ /* Check if we should disallow. */
+ if (!is_vrf_rd_configured(bgp_vrf)) {
+ vty_out(vty, "%% RD is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+
+ evpn_unconfigure_vrf_rd(bgp_vrf);
+ return CMD_SUCCESS;
+}
+
DEFUN (bgp_evpn_vni_rd,
bgp_evpn_vni_rd_cmd,
"rd ASN:NN_OR_IP-ADDRESS:NN",
return 0;
}
+/* display L3VNI related info for a VRF instance */
+DEFUN (show_bgp_vrf_l3vni_info,
+ show_bgp_vrf_l3vni_info_cmd,
+ "show bgp vrf VRFNAME l3vni info [json]",
+ SHOW_STR
+ BGP_STR
+ "show bgp vrf\n"
+ "VRF Name\n"
+ "L3-VNI\n"
+ "L3-VNI info\n"
+ JSON_STR)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ int idx_vrf = 3;
+ const char *name = NULL;
+ struct bgp *bgp = NULL;
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+ struct ecommunity *ecom = NULL;
+ json_object *json = NULL;
+ json_object *json_vnis = NULL;
+ json_object *json_export_rts = NULL;
+ json_object *json_import_rts = NULL;
+ u_char uj = use_json(argc, argv);
+
+ if (uj) {
+ json = json_object_new_object();
+ json_vnis = json_object_new_array();
+ json_export_rts = json_object_new_array();
+ json_import_rts = json_object_new_array();
+ }
+
+ name = argv[idx_vrf]->arg;
+ bgp = bgp_lookup_by_name(name);
+ if (!bgp) {
+ if (!uj)
+ vty_out(vty, "BGP instance for VRF %s not found",
+ name);
+ else {
+ json_object_string_add(json, "warning",
+ "BGP instance not found");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json));
+ json_object_free(json);
+ }
+ return CMD_WARNING;
+ }
+
+ if (!json) {
+ vty_out(vty, "BGP VRF: %s\n", name);
+ vty_out(vty, " Local-Ip: %s\n",
+ inet_ntoa(bgp->originator_ip));
+ vty_out(vty, " L3-VNI: %u\n", bgp->l3vni);
+ vty_out(vty, " Rmac: %s\n",
+ prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
+ vty_out(vty, " L2-VNI List:\n");
+ vty_out(vty, " ");
+ for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
+ vty_out(vty, "%u ", vpn->vni);
+ vty_out(vty, "\n");
+ vty_out(vty, " Export-RTs:\n");
+ vty_out(vty, " ");
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom))
+ vty_out(vty, "%s ", ecommunity_str(ecom));
+ vty_out(vty, "\n");
+ vty_out(vty, " Import-RTs:\n");
+ vty_out(vty, " ");
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom))
+ vty_out(vty, "%s ", ecommunity_str(ecom));
+ vty_out(vty, "\n");
+ vty_out(vty, " RD: %s\n",
+ prefix_rd2str(&bgp->vrf_prd, buf1, RD_ADDRSTRLEN));
+ } else {
+ json_object_string_add(json, "vrf", name);
+ json_object_string_add(json, "local-ip",
+ inet_ntoa(bgp->originator_ip));
+ json_object_int_add(json, "l3vni", bgp->l3vni);
+ json_object_string_add(json, "rmac",
+ prefix_mac2str(&bgp->rmac, buf,
+ sizeof(buf)));
+ /* list of l2vnis */
+ for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
+ json_object_array_add(json_vnis,
+ json_object_new_int(vpn->vni));
+ json_object_object_add(json, "l2vnis", json_vnis);
+
+ /* export rts */
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom))
+ json_object_array_add(json_export_rts,
+ json_object_new_string(
+ ecommunity_str(ecom)));
+ json_object_object_add(json, "export-rts", json_export_rts);
+
+ /* import rts */
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom))
+ json_object_array_add(json_import_rts,
+ json_object_new_string(
+ ecommunity_str(ecom)));
+ json_object_object_add(json, "import-rts", json_import_rts);
+ json_object_string_add(
+ json, "rd",
+ prefix_rd2str(&bgp->vrf_prd, buf1, RD_ADDRSTRLEN));
+
+ }
+
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return CMD_SUCCESS;
+}
+
+/* import/export rt for l3vni-vrf */
+DEFUN (bgp_evpn_vrf_rt,
+ bgp_evpn_vrf_rt_cmd,
+ "route-target <both|import|export> RT",
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ int rt_type;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ struct ecommunity *ecomadd = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!strcmp(argv[1]->arg, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (!strcmp(argv[1]->arg, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (!strcmp(argv[1]->arg, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ /* Add/update the import route-target */
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) {
+ ecomadd = ecommunity_str2com(argv[2]->arg,
+ ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomadd) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomadd);
+
+ /* Do nothing if we already have this import route-target */
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
+ ecomadd))
+ bgp_evpn_configure_import_rt_for_vrf(bgp, ecomadd);
+ }
+
+ /* Add/update the export route-target */
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) {
+ ecomadd = ecommunity_str2com(argv[2]->arg,
+ ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomadd) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomadd);
+
+ /* Do nothing if we already have this export route-target */
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
+ ecomadd))
+ bgp_evpn_configure_export_rt_for_vrf(bgp, ecomadd);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vrf_rt,
+ no_bgp_evpn_vrf_rt_cmd,
+ "no route-target <both|import|export> RT",
+ NO_STR
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ int rt_type, found_ecomdel;
+ struct ecommunity *ecomdel = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!strcmp(argv[2]->arg, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (!strcmp(argv[2]->arg, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (!strcmp(argv[2]->arg, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ if (rt_type == RT_TYPE_IMPORT) {
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
+ vty_out(vty,
+ "%% Import RT is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+ } else if (rt_type == RT_TYPE_EXPORT) {
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ vty_out(vty,
+ "%% Export RT is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+ } else if (rt_type == RT_TYPE_BOTH) {
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)
+ && !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ vty_out(vty,
+ "%% Import/Export RT is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+ }
+
+ ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomdel) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomdel);
+
+ if (rt_type == RT_TYPE_IMPORT) {
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
+ ecomdel)) {
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+ bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel);
+ } else if (rt_type == RT_TYPE_EXPORT) {
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
+ ecomdel)) {
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+ bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel);
+ } else if (rt_type == RT_TYPE_BOTH) {
+ found_ecomdel = 0;
+
+ if (bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
+ ecomdel)) {
+ bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel);
+ found_ecomdel = 1;
+ }
+
+ if (bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
+ ecomdel)) {
+ bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel);
+ found_ecomdel = 1;
+ }
+
+ if (!found_ecomdel) {
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
DEFUN (bgp_evpn_vni_rt,
bgp_evpn_vni_rt_cmd,
if (bgp->advertise_gw_macip)
vty_out(vty, " advertise-default-gw\n");
+
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV4_IN_EVPN))
+ vty_out(vty, " advertise ipv4 unicast\n");
+
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN))
+ vty_out(vty, " advertise ipv6 unicast\n");
}
void bgp_ethernetvpn_init(void)
install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_default_gw_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_default_gw_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_type5_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_type5_cmd);
/* "show bgp l2vpn evpn" commands. */
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vrf_cmd);
install_element(VIEW_NODE,
&show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd);
/* "show bgp evpn" commands. */
install_element(VIEW_NODE, &show_bgp_evpn_vni_cmd);
install_element(VIEW_NODE, &show_bgp_evpn_route_vni_macip_cmd);
install_element(VIEW_NODE, &show_bgp_evpn_route_vni_all_cmd);
install_element(VIEW_NODE, &show_bgp_evpn_import_rt_cmd);
+ install_element(VIEW_NODE, &show_bgp_vrf_l3vni_info_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_vni_cmd);
install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rt_cmd);
install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_cmd);
install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_without_val_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rd_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rd_cmd);
+ install_element(BGP_NODE, &no_bgp_evpn_vrf_rd_without_val_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_cmd);
install_element(BGP_EVPN_VNI_NODE,
&bgp_evpn_advertise_default_gw_vni_cmd);
install_element(BGP_EVPN_VNI_NODE,
#include "thread.h"
#include "log.h"
#include "stream.h"
+#include "ringbuf.h"
#include "memory.h"
#include "plist.h"
#include "workqueue.h"
stream_fifo_clean(peer->ibuf);
stream_fifo_clean(peer->obuf);
- stream_reset(peer->ibuf_work);
/*
* this should never happen, since bgp_process_packet() is the
stream_fifo_push(peer->ibuf,
stream_fifo_pop(from_peer->ibuf));
- stream_copy(peer->ibuf_work, from_peer->ibuf_work);
+ ringbuf_wipe(peer->ibuf_work);
+ ringbuf_copy(peer->ibuf_work, from_peer->ibuf_work,
+ ringbuf_remain(from_peer->ibuf_work));
}
pthread_mutex_unlock(&from_peer->io_mtx);
pthread_mutex_unlock(&peer->io_mtx);
}
}
+
+ // Note: peer_xfer_stats() must be called with I/O turned OFF
+ if (from_peer)
+ peer_xfer_stats(peer, from_peer);
+
bgp_reads_on(peer);
bgp_writes_on(peer);
thread_add_timer_msec(bm->master, bgp_process_packet, peer, 0,
&peer->t_process_packet);
- if (from_peer)
- peer_xfer_stats(peer, from_peer);
-
return (peer);
}
stream_fifo_clean(peer->obuf);
if (peer->ibuf_work)
- stream_reset(peer->ibuf_work);
+ ringbuf_wipe(peer->ibuf_work);
if (peer->obuf_work)
stream_reset(peer->obuf_work);
#include "memory.h" // for MTYPE_TMP, XCALLOC, XFREE
#include "network.h" // for ERRNO_IO_RETRY
#include "stream.h" // for stream_get_endp, stream_getw_from, str...
+#include "ringbuf.h" // for ringbuf_remain, ringbuf_peek, ringbuf_...
#include "thread.h" // for THREAD_OFF, THREAD_ARG, thread, thread...
#include "zassert.h" // for assert
#define BGP_IO_TRANS_ERR (1 << 0) // EAGAIN or similar occurred
#define BGP_IO_FATAL_ERR (1 << 1) // some kind of fatal TCP error
-/* Start and stop routines for I/O pthread + control variables
- * ------------------------------------------------------------------------ */
-_Atomic bool bgp_io_thread_run;
-_Atomic bool bgp_io_thread_started;
-
-void bgp_io_init()
-{
- bgp_io_thread_run = false;
- bgp_io_thread_started = false;
-}
-
-/* Unused callback for thread_add_read() */
-static int bgp_io_dummy(struct thread *thread) { return 0; }
-
-void *bgp_io_start(void *arg)
-{
- struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
- fpt->master->owner = pthread_self();
-
- // fd so we can sleep in poll()
- int sleeper[2];
- pipe(sleeper);
- thread_add_read(fpt->master, &bgp_io_dummy, NULL, sleeper[0], NULL);
-
- // we definitely don't want to handle signals
- fpt->master->handle_signals = false;
-
- struct thread task;
-
- atomic_store_explicit(&bgp_io_thread_run, true, memory_order_seq_cst);
- atomic_store_explicit(&bgp_io_thread_started, true,
- memory_order_seq_cst);
-
- while (bgp_io_thread_run) {
- if (thread_fetch(fpt->master, &task)) {
- thread_call(&task);
- }
- }
-
- close(sleeper[1]);
- close(sleeper[0]);
-
- return NULL;
-}
-
-static int bgp_io_finish(struct thread *thread)
-{
- atomic_store_explicit(&bgp_io_thread_run, false, memory_order_seq_cst);
- return 0;
-}
-
-int bgp_io_stop(void **result, struct frr_pthread *fpt)
-{
- thread_add_event(fpt->master, &bgp_io_finish, NULL, 0, NULL);
- pthread_join(fpt->thread, result);
- return 0;
-}
-
-/* Extern API -------------------------------------------------------------- */
+/* Thread external API ----------------------------------------------------- */
void bgp_writes_on(struct peer *peer)
{
- while (
- !atomic_load_explicit(&bgp_io_thread_started, memory_order_seq_cst))
- ;
+ struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
+ assert(fpt->running);
assert(peer->status != Deleted);
assert(peer->obuf);
assert(!peer->t_connect_check_w);
assert(peer->fd);
- struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
-
thread_add_write(fpt->master, bgp_process_writes, peer, peer->fd,
&peer->t_write);
SET_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON);
void bgp_writes_off(struct peer *peer)
{
- while (
- !atomic_load_explicit(&bgp_io_thread_started, memory_order_seq_cst))
- ;
-
struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
+ assert(fpt->running);
thread_cancel_async(fpt->master, &peer->t_write, NULL);
THREAD_OFF(peer->t_generate_updgrp_packets);
void bgp_reads_on(struct peer *peer)
{
- while (
- !atomic_load_explicit(&bgp_io_thread_started, memory_order_seq_cst))
- ;
+ struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
+ assert(fpt->running);
assert(peer->status != Deleted);
assert(peer->ibuf);
assert(!peer->t_connect_check_w);
assert(peer->fd);
- struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
-
thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd,
&peer->t_read);
void bgp_reads_off(struct peer *peer)
{
- while (
- !atomic_load_explicit(&bgp_io_thread_started, memory_order_seq_cst))
- ;
-
struct frr_pthread *fpt = frr_pthread_get(PTHREAD_IO);
+ assert(fpt->running);
thread_cancel_async(fpt->master, &peer->t_read, NULL);
THREAD_OFF(peer->t_process_packet);
UNSET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON);
}
-/* Internal functions ------------------------------------------------------- */
+/* Thread internal functions ----------------------------------------------- */
-/**
+/*
* Called from I/O pthread when a file descriptor has become ready for writing.
*/
static int bgp_process_writes(struct thread *thread)
}
pthread_mutex_unlock(&peer->io_mtx);
- if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) { /* no problem */
+ /* no problem */
+ if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) {
}
+ /* problem */
if (CHECK_FLAG(status, BGP_IO_FATAL_ERR)) {
- reschedule = false; /* problem */
+ reschedule = false;
fatal = true;
}
return 0;
}
-/**
+/*
* Called from I/O pthread when a file descriptor has become ready for reading,
* or has hung up.
*
/* static buffer for transferring packets */
static unsigned char pktbuf[BGP_MAX_PACKET_SIZE];
/* shorter alias to peer's input buffer */
- struct stream *ibw = peer->ibuf_work;
- /* offset of start of current packet */
- size_t offset = stream_get_getp(ibw);
+ struct ringbuf *ibw = peer->ibuf_work;
/* packet size as given by header */
- u_int16_t pktsize = 0;
+ uint16_t pktsize = 0;
/* check that we have enough data for a header */
- if (STREAM_READABLE(ibw) < BGP_HEADER_SIZE)
+ if (ringbuf_remain(ibw) < BGP_HEADER_SIZE)
break;
/* validate header */
}
/* header is valid; retrieve packet size */
- pktsize = stream_getw_from(ibw, offset + BGP_MARKER_SIZE);
+ ringbuf_peek(ibw, BGP_MARKER_SIZE, &pktsize, sizeof(pktsize));
+
+ pktsize = ntohs(pktsize);
/* if this fails we are seriously screwed */
assert(pktsize <= BGP_MAX_PACKET_SIZE);
- /* If we have that much data, chuck it into its own
- * stream and append to input queue for processing. */
- if (STREAM_READABLE(ibw) >= pktsize) {
+ /*
+ * If we have that much data, chuck it into its own
+ * stream and append to input queue for processing.
+ */
+ if (ringbuf_remain(ibw) >= pktsize) {
struct stream *pkt = stream_new(pktsize);
- stream_get(pktbuf, ibw, pktsize);
+ assert(ringbuf_get(ibw, pktbuf, pktsize) == pktsize);
stream_put(pkt, pktbuf, pktsize);
pthread_mutex_lock(&peer->io_mtx);
break;
}
- /*
- * After reading:
- * 1. Move unread data to stream start to make room for more.
- * 2. Reschedule and return when we have additional data.
- *
- * XXX: Heavy abuse of stream API. This needs a ring buffer.
- */
- if (more && STREAM_WRITEABLE(peer->ibuf_work) < BGP_MAX_PACKET_SIZE) {
- void *from = stream_pnt(peer->ibuf_work);
- void *to = peer->ibuf_work->data;
- size_t siz = STREAM_READABLE(peer->ibuf_work);
- memmove(to, from, siz);
- stream_set_getp(peer->ibuf_work, 0);
- stream_set_endp(peer->ibuf_work, siz);
- }
-
- assert(STREAM_WRITEABLE(peer->ibuf_work) >= BGP_MAX_PACKET_SIZE);
+ assert(ringbuf_space(peer->ibuf_work) >= BGP_MAX_PACKET_SIZE);
/* handle invalid header */
if (fatal) {
/* wipe buffer just in case someone screwed up */
- stream_reset(peer->ibuf_work);
+ ringbuf_wipe(peer->ibuf_work);
} else {
thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd,
&peer->t_read);
return 0;
}
-/**
+/*
* Flush peer output buffer.
*
* This function pops packets off of peer->obuf and writes them to peer->fd.
int num;
int update_last_write = 0;
unsigned int count = 0;
- uint32_t oc;
- uint32_t uo;
+ uint32_t uo = 0;
uint16_t status = 0;
uint32_t wpkt_quanta_old;
- // save current # updates sent
- oc = atomic_load_explicit(&peer->update_out, memory_order_relaxed);
-
- // cache current write quanta
wpkt_quanta_old =
atomic_load_explicit(&peer->bgp->wpkt_quanta, memory_order_relaxed);
}
goto done;
- } else if (num != writenum) // incomplete write
+ } else if (num != writenum)
stream_forward_getp(s, num);
} while (num != writenum);
case BGP_MSG_UPDATE:
atomic_fetch_add_explicit(&peer->update_out, 1,
memory_order_relaxed);
+ uo++;
break;
case BGP_MSG_NOTIFY:
atomic_fetch_add_explicit(&peer->notify_out, 1,
if (peer->v_start >= (60 * 2))
peer->v_start = (60 * 2);
- /* Handle Graceful Restart case where the state changes
- * to Connect instead of Idle */
+ /*
+ * Handle Graceful Restart case where the state changes
+ * to Connect instead of Idle.
+ */
BGP_EVENT_ADD(peer, BGP_Stop);
goto done;
}
done : {
- /* Update last_update if UPDATEs were written. */
- uo = atomic_load_explicit(&peer->update_out, memory_order_relaxed);
- if (uo > oc)
+ /*
+ * Update last_update if UPDATEs were written.
+ * Note: that these are only updated at end,
+ * not per message (i.e., per loop)
+ */
+ if (uo)
atomic_store_explicit(&peer->last_update, bgp_clock(),
memory_order_relaxed);
return status;
}
-/**
+/*
* Reads a chunk of data from peer->fd into peer->ibuf_work.
*
* @return status flag (see top-of-file)
size_t readsize; // how many bytes we want to read
ssize_t nbytes; // how many bytes we actually read
uint16_t status = 0;
+ static uint8_t ibw[BGP_MAX_PACKET_SIZE * BGP_READ_PACKET_MAX];
- readsize = STREAM_WRITEABLE(peer->ibuf_work);
+ readsize = MIN(ringbuf_space(peer->ibuf_work), sizeof(ibw));
+ nbytes = read(peer->fd, ibw, readsize);
- nbytes = stream_read_try(peer->ibuf_work, peer->fd, readsize);
-
- switch (nbytes) {
+ /* EAGAIN or EWOULDBLOCK; come back later */
+ if (nbytes < 0 && ERRNO_IO_RETRY(errno)) {
+ SET_FLAG(status, BGP_IO_TRANS_ERR);
/* Fatal error; tear down session */
- case -1:
+ } else if (nbytes < 0) {
zlog_err("%s [Error] bgp_read_packet error: %s", peer->host,
safe_strerror(errno));
BGP_EVENT_ADD(peer, TCP_fatal_error);
SET_FLAG(status, BGP_IO_FATAL_ERR);
- break;
-
/* Received EOF / TCP session closed */
- case 0:
+ } else if (nbytes == 0) {
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s [Event] BGP connection closed fd %d",
peer->host, peer->fd);
BGP_EVENT_ADD(peer, TCP_connection_closed);
SET_FLAG(status, BGP_IO_FATAL_ERR);
- break;
-
- /* EAGAIN or EWOULDBLOCK; come back later */
- case -2:
- SET_FLAG(status, BGP_IO_TRANS_ERR);
- break;
- default:
- break;
+ } else {
+ assert(ringbuf_put(peer->ibuf_work, ibw, nbytes)
+ == (size_t)nbytes);
}
return status;
/*
* Called after we have read a BGP packet header. Validates marker, message
* type and packet length. If any of these aren't correct, sends a notify.
+ *
+ * Assumes that there are at least BGP_HEADER_SIZE readable bytes in the input
+ * buffer.
*/
static bool validate_header(struct peer *peer)
{
uint16_t size;
uint8_t type;
- struct stream *pkt = peer->ibuf_work;
- size_t getp = stream_get_getp(pkt);
+ struct ringbuf *pkt = peer->ibuf_work;
- static uint8_t marker[BGP_MARKER_SIZE] = {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ static uint8_t m_correct[BGP_MARKER_SIZE] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ uint8_t m_rx[BGP_MARKER_SIZE] = {0x00};
- if (memcmp(marker, stream_pnt(pkt), BGP_MARKER_SIZE) != 0) {
+ if (ringbuf_peek(pkt, 0, m_rx, BGP_MARKER_SIZE) != BGP_MARKER_SIZE)
+ return false;
+
+ if (memcmp(m_correct, m_rx, BGP_MARKER_SIZE) != 0) {
bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR,
BGP_NOTIFY_HEADER_NOT_SYNC);
return false;
}
- /* Get size and type in host byte order. */
- size = stream_getw_from(pkt, getp + BGP_MARKER_SIZE);
- type = stream_getc_from(pkt, getp + BGP_MARKER_SIZE + 2);
+ /* Get size and type in network byte order. */
+ ringbuf_peek(pkt, BGP_MARKER_SIZE, &size, sizeof(size));
+ ringbuf_peek(pkt, BGP_MARKER_SIZE + 2, &type, sizeof(type));
+
+ size = ntohs(size);
/* BGP type check. */
if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE
#include "bgpd/bgpd.h"
#include "frr_pthread.h"
-/**
- * Initializes data structures and flags for the write thread.
- *
- * This function should be called from the main thread before
- * bgp_writes_start() is invoked.
- */
-extern void bgp_io_init(void);
-
/**
* Start function for write thread.
*
#include "bgpd/bgp_keepalives.h"
/* clang-format on */
-/**
+/*
* Peer KeepAlive Timer.
* Associates a peer with the time of its last keepalive.
*/
struct pkat {
- // the peer to send keepalives to
+ /* the peer to send keepalives to */
struct peer *peer;
- // absolute time of last keepalive sent
+ /* absolute time of last keepalive sent */
struct timeval last;
};
static pthread_cond_t *peerhash_cond;
static struct hash *peerhash;
-/* Thread control flag. */
-bool bgp_keepalives_thread_run = false;
-
static struct pkat *pkat_new(struct peer *peer)
{
struct pkat *pkat = XMALLOC(MTYPE_TMP, sizeof(struct pkat));
static struct timeval tolerance = {0, 100000};
- // calculate elapsed time since last keepalive
+ /* calculate elapsed time since last keepalive */
monotime_since(&pkat->last, &elapsed);
- // calculate difference between elapsed time and configured time
+ /* calculate difference between elapsed time and configured time */
ka.tv_sec = pkat->peer->v_keepalive;
timersub(&ka, &elapsed, &diff);
bgp_keepalive_send(pkat->peer);
monotime(&pkat->last);
memset(&elapsed, 0x00, sizeof(struct timeval));
- diff = ka; // time until next keepalive == peer keepalive time
+ diff = ka;
}
- // if calculated next update for this peer < current delay, use it
+ /* if calculated next update for this peer < current delay, use it */
if (next_update->tv_sec <= 0 || timercmp(&diff, next_update, <))
*next_update = diff;
}
return (uintptr_t)pkat->peer;
}
-void bgp_keepalives_init()
-{
- peerhash_mtx = XCALLOC(MTYPE_TMP, sizeof(pthread_mutex_t));
- peerhash_cond = XCALLOC(MTYPE_TMP, sizeof(pthread_cond_t));
-
- // initialize mutex
- pthread_mutex_init(peerhash_mtx, NULL);
-
- // use monotonic clock with condition variable
- pthread_condattr_t attrs;
- pthread_condattr_init(&attrs);
- pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
- pthread_cond_init(peerhash_cond, &attrs);
- pthread_condattr_destroy(&attrs);
-
- // initialize peer hashtable
- peerhash = hash_create_size(2048, peer_hash_key, peer_hash_cmp, NULL);
-}
-
+/* Cleanup handler / deinitializer. */
static void bgp_keepalives_finish(void *arg)
{
- bgp_keepalives_thread_run = false;
-
if (peerhash) {
hash_clean(peerhash, pkat_del);
hash_free(peerhash);
XFREE(MTYPE_TMP, peerhash_cond);
}
-/**
+/*
* Entry function for peer keepalive generation pthread.
- *
- * bgp_keepalives_init() must be called prior to this.
*/
void *bgp_keepalives_start(void *arg)
{
+ struct frr_pthread *fpt = arg;
+ fpt->master->owner = pthread_self();
+
struct timeval currtime = {0, 0};
struct timeval aftertime = {0, 0};
struct timeval next_update = {0, 0};
struct timespec next_update_ts = {0, 0};
+ peerhash_mtx = XCALLOC(MTYPE_TMP, sizeof(pthread_mutex_t));
+ peerhash_cond = XCALLOC(MTYPE_TMP, sizeof(pthread_cond_t));
+
+ /* initialize mutex */
+ pthread_mutex_init(peerhash_mtx, NULL);
+
+ /* use monotonic clock with condition variable */
+ pthread_condattr_t attrs;
+ pthread_condattr_init(&attrs);
+ pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
+ pthread_cond_init(peerhash_cond, &attrs);
+ pthread_condattr_destroy(&attrs);
+
+ /* initialize peer hashtable */
+ peerhash = hash_create_size(2048, peer_hash_key, peer_hash_cmp, NULL);
pthread_mutex_lock(peerhash_mtx);
- // register cleanup handler
+ /* register cleanup handler */
pthread_cleanup_push(&bgp_keepalives_finish, NULL);
- bgp_keepalives_thread_run = true;
+ /* notify anybody waiting on us that we are done starting up */
+ frr_pthread_notify_running(fpt);
- while (bgp_keepalives_thread_run) {
+ while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
if (peerhash->count > 0)
pthread_cond_timedwait(peerhash_cond, peerhash_mtx,
&next_update_ts);
else
while (peerhash->count == 0
- && bgp_keepalives_thread_run)
+ && atomic_load_explicit(&fpt->running,
+ memory_order_relaxed))
pthread_cond_wait(peerhash_cond, peerhash_mtx);
monotime(&currtime);
TIMEVAL_TO_TIMESPEC(&next_update, &next_update_ts);
}
- // clean up
+ /* clean up */
pthread_cleanup_pop(1);
return NULL;
void bgp_keepalives_on(struct peer *peer)
{
+ if (CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON))
+ return;
+
+ struct frr_pthread *fpt = frr_pthread_get(PTHREAD_KEEPALIVES);
+ assert(fpt->running);
+
/* placeholder bucket data to use for fast key lookups */
static struct pkat holder = {0};
void bgp_keepalives_off(struct peer *peer)
{
+ if (!CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON))
+ return;
+
+ struct frr_pthread *fpt = frr_pthread_get(PTHREAD_KEEPALIVES);
+ assert(fpt->running);
+
/* placeholder bucket data to use for fast key lookups */
static struct pkat holder = {0};
pthread_mutex_unlock(peerhash_mtx);
}
-int bgp_keepalives_stop(void **result, struct frr_pthread *fpt)
+int bgp_keepalives_stop(struct frr_pthread *fpt, void **result)
{
- bgp_keepalives_thread_run = false;
+ assert(fpt->running);
+
+ atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
bgp_keepalives_wake();
+
pthread_join(fpt->thread, result);
return 0;
}
/**
* Stops the thread and blocks until it terminates.
*/
-int bgp_keepalives_stop(void **result, struct frr_pthread *fpt);
+int bgp_keepalives_stop(struct frr_pthread *fpt, void **result);
#endif /* _FRR_BGP_KEEPALIVES_H */
#endif
bgp_zebra_destroy();
+ bf_free(bm->rd_idspace);
list_delete_and_null(&bm->bgp);
memset(bm, 0, sizeof(*bm));
static int bgp_vrf_new(struct vrf *vrf)
{
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("VRF Created: %s(%d)", vrf->name, vrf->vrf_id);
+ zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id);
return 0;
}
static int bgp_vrf_delete(struct vrf *vrf)
{
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("VRF Deletion: %s(%d)", vrf->name, vrf->vrf_id);
+ zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id);
return 0;
}
vrf_id_t old_vrf_id;
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("VRF enable add %s id %d", vrf->name, vrf->vrf_id);
+ zlog_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id);
bgp = bgp_lookup_by_name(vrf->name);
if (bgp) {
DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information")
DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT")
+DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT")
DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP")
DECLARE_MTYPE(BGP_EVPN)
DECLARE_MTYPE(BGP_EVPN_IMPORT_RT)
+DECLARE_MTYPE(BGP_EVPN_VRF_IMPORT_RT)
DECLARE_MTYPE(BGP_EVPN_MACIP)
#endif /* _QUAGGA_BGP_MEMORY_H */
bgp = bgp_lookup_by_vrf_id(vrf_id);
if (!bgp) {
zlog_err(
- "parse nexthop update: instance not found for vrf_id %d",
+ "parse nexthop update: instance not found for vrf_id %u",
vrf_id);
return;
}
char buf[PREFIX2STR_BUFFER];
prefix2str(&p, buf, sizeof(buf));
zlog_debug(
- "%d: Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x",
+ "%u: Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x",
vrf_id, buf, metric, bnc->metric, nexthop_num,
bnc->nexthop_num, bnc->flags);
}
*/
static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command)
{
- struct stream *s;
struct prefix *p;
+ bool exact_match = false;
int ret;
- /* Check socket. */
- if (!zclient || zclient->sock < 0)
+ if (!zclient)
return;
/* Don't try to register if Zebra doesn't know of this instance. */
return;
p = &(bnc->node->p);
- s = zclient->obuf;
- stream_reset(s);
- zclient_create_header(s, command, bnc->bgp->vrf_id);
if ((command == ZEBRA_NEXTHOP_REGISTER ||
command == ZEBRA_IMPORT_ROUTE_REGISTER) &&
(CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)
|| CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)))
- stream_putc(s, 1);
- else
- stream_putc(s, 0);
-
- stream_putw(s, PREFIX_FAMILY(p));
- stream_putc(s, p->prefixlen);
- switch (PREFIX_FAMILY(p)) {
- case AF_INET:
- stream_put_in_addr(s, &p->u.prefix4);
- break;
- case AF_INET6:
- stream_put(s, &(p->u.prefix6), 16);
- break;
- default:
- break;
- }
- stream_putw_at(s, 0, stream_get_endp(s));
+ exact_match = true;
- ret = zclient_send_message(zclient);
+ ret = zclient_send_rnh(zclient, command, p,
+ exact_match, bnc->bgp->vrf_id);
/* TBD: handle the failure */
if (ret < 0)
zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
bgp_writes_on(peer);
}
-/* This is only for sending NOTIFICATION message to neighbor. */
+/*
+ * Writes NOTIFICATION message directly to a peer socket without waiting for
+ * the I/O thread.
+ *
+ * There must be exactly one stream on the peer->obuf FIFO, and the data within
+ * this stream must match the format of a BGP NOTIFICATION message.
+ * Transmission is best-effort.
+ *
+ * @requires peer->io_mtx
+ * @param peer
+ * @return 0
+ */
static int bgp_write_notify(struct peer *peer)
{
int ret, val;
u_char type;
struct stream *s;
- pthread_mutex_lock(&peer->io_mtx);
- {
- /* There should be at least one packet. */
- s = stream_fifo_pop(peer->obuf);
- }
- pthread_mutex_unlock(&peer->io_mtx);
+ /* There should be at least one packet. */
+ s = stream_fifo_pop(peer->obuf);
if (!s)
return 0;
assert(type == BGP_MSG_NOTIFY);
/* Type should be notify. */
+ atomic_fetch_add_explicit(&peer->notify_out, 1, memory_order_relaxed);
peer->notify_out++;
/* Double start timer. */
* This function attempts to write the packet from the thread it is called
* from, to ensure the packet gets out ASAP.
*
+ * This function may be called from multiple threads. Since the function
+ * modifies I/O buffer(s) in the peer, these are locked for the duration of the
+ * call to prevent tampering from other threads.
+ *
+ * Delivery of the NOTIFICATION is attempted once and is best-effort. After
+ * return, the peer structure *must* be reset; no assumptions about session
+ * state are valid.
+ *
* @param peer
* @param code BGP error code
* @param sub_code BGP error subcode
struct stream *s;
int length;
+ /* Lock I/O mutex to prevent other threads from pushing packets */
+ pthread_mutex_lock(&peer->io_mtx);
+ /* ============================================== */
+
/* Allocate new stream. */
s = stream_new(BGP_MAX_PACKET_SIZE);
/* Set BGP packet length. */
length = bgp_packet_set_size(s);
- /*
- * Turn off keepalive generation for peer. This is necessary because
- * otherwise between the time we wipe the output buffer and the time we
- * push the NOTIFY onto it, the KA generation thread could have pushed
- * a KEEPALIVE in the middle.
- */
- bgp_keepalives_off(peer);
-
/* wipe output buffer */
- pthread_mutex_lock(&peer->io_mtx);
- {
- stream_fifo_clean(peer->obuf);
- }
- pthread_mutex_unlock(&peer->io_mtx);
+ stream_fifo_clean(peer->obuf);
/*
* If possible, store last packet for debugging purposes. This check is
peer->last_reset = PEER_DOWN_NOTIFY_SEND;
/* Add packet to peer's output queue */
- bgp_packet_add(peer, s);
+ stream_fifo_push(peer->obuf, s);
bgp_write_notify(peer);
+
+ /* ============================================== */
+ pthread_mutex_unlock(&peer->io_mtx);
}
/*
}
/* peer count update */
- peer->notify_in++;
+ atomic_fetch_add_explicit(&peer->notify_in, 1, memory_order_relaxed);
peer->last_reset = PEER_DOWN_NOTIFY_RECEIVED;
*/
switch (type) {
case BGP_MSG_OPEN:
- peer->open_in++;
+ atomic_fetch_add_explicit(&peer->open_in, 1,
+ memory_order_relaxed);
mprc = bgp_open_receive(peer, size);
if (mprc == BGP_Stop)
zlog_err(
__FUNCTION__, peer->host);
break;
case BGP_MSG_UPDATE:
- peer->update_in++;
+ atomic_fetch_add_explicit(&peer->update_in, 1,
+ memory_order_relaxed);
peer->readtime = monotime(NULL);
mprc = bgp_update_receive(peer, size);
if (mprc == BGP_Stop)
__FUNCTION__, peer->host);
break;
case BGP_MSG_NOTIFY:
- peer->notify_in++;
+ atomic_fetch_add_explicit(&peer->notify_in, 1,
+ memory_order_relaxed);
mprc = bgp_notify_receive(peer, size);
if (mprc == BGP_Stop)
zlog_err(
break;
case BGP_MSG_KEEPALIVE:
peer->readtime = monotime(NULL);
- peer->keepalive_in++;
+ atomic_fetch_add_explicit(&peer->keepalive_in, 1,
+ memory_order_relaxed);
mprc = bgp_keepalive_receive(peer, size);
if (mprc == BGP_Stop)
zlog_err(
break;
case BGP_MSG_ROUTE_REFRESH_NEW:
case BGP_MSG_ROUTE_REFRESH_OLD:
- peer->refresh_in++;
+ atomic_fetch_add_explicit(&peer->refresh_in, 1,
+ memory_order_relaxed);
mprc = bgp_route_refresh_receive(peer, size);
if (mprc == BGP_Stop)
zlog_err(
__FUNCTION__, peer->host);
break;
case BGP_MSG_CAPABILITY:
- peer->dynamic_cap_in++;
+ atomic_fetch_add_explicit(&peer->dynamic_cap_in, 1,
+ memory_order_relaxed);
mprc = bgp_capability_receive(peer, size);
if (mprc == BGP_Stop)
zlog_err(
struct rd_as rd_as;
struct rd_ip rd_ip;
- if (size < RD_ADDRSTRLEN)
- return NULL;
+ assert(size >= RD_ADDRSTRLEN);
pnt = prd->val;
return buf;
}
#endif
- return NULL;
+
+ snprintf(buf, size, "Unknown Type: %d", type);
+ return buf;
}
#include "bgpd/bgp_evpn.h"
#include "bgpd/bgp_evpn_vty.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_route_clippy.c"
+#endif
/* Extern from bgp_dump.c */
extern const char *bgp_origin_str[];
}
}
+ /* advertise/withdraw type-5 routes */
+ if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
+ if (new_select)
+ bgp_evpn_advertise_type5_route(bgp, rn, afi, safi);
+ else if (old_select)
+ bgp_evpn_withdraw_type5_route(bgp, rn, afi, safi);
+ }
+
/* Clear any route change flags. */
bgp_zebra_clear_route_change_flags(rn);
/* AS path local-as loop check. */
if (peer->change_local_as) {
- if (!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND))
+ if (peer->allowas_in[afi][safi])
+ aspath_loop_count = peer->allowas_in[afi][safi];
+ else if (!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND))
aspath_loop_count = 1;
if (aspath_loop_check(attr->aspath, peer->change_local_as)
/* Configure static BGP network. When user don't run zebra, static
route should be installed as valid. */
-static int bgp_static_set(struct vty *vty, const char *ip_str, afi_t afi,
- safi_t safi, const char *rmap, int backdoor,
- u_int32_t label_index)
+static int bgp_static_set(struct vty *vty, const char *negate,
+ const char *ip_str, afi_t afi, safi_t safi,
+ const char *rmap, int backdoor, u_int32_t label_index)
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
int ret;
apply_mask(&p);
- /* Set BGP static route configuration. */
- rn = bgp_node_get(bgp->route[afi][safi], &p);
+ if (negate) {
- if (rn->info) {
- /* Configuration change. */
- bgp_static = rn->info;
+ /* Set BGP static route configuration. */
+ rn = bgp_node_lookup(bgp->route[afi][safi], &p);
- /* Label index cannot be changed. */
- if (bgp_static->label_index != label_index) {
- vty_out(vty, "%% Label index cannot be changed\n");
+ if (!rn) {
+ vty_out(vty,
+ "%% Can't find static route specified\n");
return CMD_WARNING_CONFIG_FAILED;
}
- /* Check previous routes are installed into BGP. */
- if (bgp_static->valid && bgp_static->backdoor != backdoor)
- need_update = 1;
-
- bgp_static->backdoor = backdoor;
+ bgp_static = rn->info;
- if (rmap) {
- if (bgp_static->rmap.name)
- XFREE(MTYPE_ROUTE_MAP_NAME,
- bgp_static->rmap.name);
- bgp_static->rmap.name =
- XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
- bgp_static->rmap.map = route_map_lookup_by_name(rmap);
- } else {
- if (bgp_static->rmap.name)
- XFREE(MTYPE_ROUTE_MAP_NAME,
- bgp_static->rmap.name);
- bgp_static->rmap.name = NULL;
- bgp_static->rmap.map = NULL;
- bgp_static->valid = 0;
+ if ((label_index != BGP_INVALID_LABEL_INDEX)
+ && (label_index != bgp_static->label_index)) {
+ vty_out(vty,
+ "%% label-index doesn't match static route\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
- bgp_unlock_node(rn);
- } else {
- /* New configuration. */
- bgp_static = bgp_static_new();
- bgp_static->backdoor = backdoor;
- bgp_static->valid = 0;
- bgp_static->igpmetric = 0;
- bgp_static->igpnexthop.s_addr = 0;
- bgp_static->label_index = label_index;
- if (rmap) {
- if (bgp_static->rmap.name)
- XFREE(MTYPE_ROUTE_MAP_NAME,
- bgp_static->rmap.name);
- bgp_static->rmap.name =
- XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
- bgp_static->rmap.map = route_map_lookup_by_name(rmap);
+ if ((rmap && bgp_static->rmap.name)
+ && strcmp(rmap, bgp_static->rmap.name)) {
+ vty_out(vty,
+ "%% route-map name doesn't match static route\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
- rn->info = bgp_static;
- }
- bgp_static->valid = 1;
- if (need_update)
- bgp_static_withdraw(bgp, &p, afi, safi);
+ /* Update BGP RIB. */
+ if (!bgp_static->backdoor)
+ bgp_static_withdraw(bgp, &p, afi, safi);
- if (!bgp_static->backdoor)
- bgp_static_update(bgp, &p, bgp_static, afi, safi);
+ /* Clear configuration. */
+ bgp_static_free(bgp_static);
+ rn->info = NULL;
+ bgp_unlock_node(rn);
+ bgp_unlock_node(rn);
+ } else {
- return CMD_SUCCESS;
-}
+ /* Set BGP static route configuration. */
+ rn = bgp_node_get(bgp->route[afi][safi], &p);
-/* Configure static BGP network. */
-static int bgp_static_unset(struct vty *vty, const char *ip_str, afi_t afi,
- safi_t safi)
-{
- VTY_DECLVAR_CONTEXT(bgp, bgp);
- int ret;
- struct prefix p;
- struct bgp_static *bgp_static;
- struct bgp_node *rn;
+ if (rn->info) {
+ /* Configuration change. */
+ bgp_static = rn->info;
- /* Convert IP prefix string to struct prefix. */
- ret = str2prefix(ip_str, &p);
- if (!ret) {
- vty_out(vty, "%% Malformed prefix\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
- if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) {
- vty_out(vty, "%% Malformed prefix (link-local address)\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
+ /* Label index cannot be changed. */
+ if (bgp_static->label_index != label_index) {
+ vty_out(vty, "%% cannot change label-index\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
- apply_mask(&p);
+ /* Check previous routes are installed into BGP. */
+ if (bgp_static->valid &&
+ bgp_static->backdoor != backdoor)
+ need_update = 1;
- rn = bgp_node_lookup(bgp->route[afi][safi], &p);
- if (!rn) {
- vty_out(vty,
- "%% Can't find specified static route configuration.\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
+ bgp_static->backdoor = backdoor;
+
+ if (rmap) {
+ if (bgp_static->rmap.name)
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_static->rmap.name);
+ bgp_static->rmap.name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
+ bgp_static->rmap.map =
+ route_map_lookup_by_name(rmap);
+ } else {
+ if (bgp_static->rmap.name)
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_static->rmap.name);
+ bgp_static->rmap.name = NULL;
+ bgp_static->rmap.map = NULL;
+ bgp_static->valid = 0;
+ }
+ bgp_unlock_node(rn);
+ } else {
+ /* New configuration. */
+ bgp_static = bgp_static_new();
+ bgp_static->backdoor = backdoor;
+ bgp_static->valid = 0;
+ bgp_static->igpmetric = 0;
+ bgp_static->igpnexthop.s_addr = 0;
+ bgp_static->label_index = label_index;
- bgp_static = rn->info;
+ if (rmap) {
+ if (bgp_static->rmap.name)
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_static->rmap.name);
+ bgp_static->rmap.name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
+ bgp_static->rmap.map =
+ route_map_lookup_by_name(rmap);
+ }
+ rn->info = bgp_static;
+ }
- /* Update BGP RIB. */
- if (!bgp_static->backdoor)
- bgp_static_withdraw(bgp, &p, afi, safi);
+ bgp_static->valid = 1;
+ if (need_update)
+ bgp_static_withdraw(bgp, &p, afi, safi);
- /* Clear configuration. */
- bgp_static_free(bgp_static);
- rn->info = NULL;
- bgp_unlock_node(rn);
- bgp_unlock_node(rn);
+ if (!bgp_static->backdoor)
+ bgp_static_update(bgp, &p, bgp_static, afi, safi);
+ }
return CMD_SUCCESS;
}
argv[idx_word]->arg);
}
-DEFUN (bgp_network,
- bgp_network_cmd,
- "network A.B.C.D/M",
- "Specify a network to announce via BGP\n"
- "IPv4 prefix\n")
-{
- int idx_ipv4_prefixlen = 1;
- return bgp_static_set(vty, argv[idx_ipv4_prefixlen]->arg, AFI_IP,
- bgp_node_safi(vty), NULL, 0,
- BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (bgp_network_route_map,
- bgp_network_route_map_cmd,
- "network A.B.C.D/M route-map WORD",
- "Specify a network to announce via BGP\n"
- "IPv4 prefix\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-{
- int idx_ipv4_prefixlen = 1;
- int idx_word = 3;
- return bgp_static_set(vty, argv[idx_ipv4_prefixlen]->arg, AFI_IP,
- bgp_node_safi(vty), argv[idx_word]->arg, 0,
- BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (bgp_network_backdoor,
- bgp_network_backdoor_cmd,
- "network A.B.C.D/M backdoor",
- "Specify a network to announce via BGP\n"
- "IPv4 prefix\n"
- "Specify a BGP backdoor route\n")
-{
- int idx_ipv4_prefixlen = 1;
- return bgp_static_set(vty, argv[idx_ipv4_prefixlen]->arg, AFI_IP,
- SAFI_UNICAST, NULL, 1, BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (bgp_network_mask,
- bgp_network_mask_cmd,
- "network A.B.C.D mask A.B.C.D",
- "Specify a network to announce via BGP\n"
- "Network number\n"
- "Network mask\n"
- "Network mask\n")
-{
- int idx_ipv4 = 1;
- int idx_ipv4_2 = 3;
- int ret;
- char prefix_str[BUFSIZ];
-
- ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg,
- prefix_str);
- if (!ret) {
- vty_out(vty, "%% Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return bgp_static_set(vty, prefix_str, AFI_IP, bgp_node_safi(vty), NULL,
- 0, BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (bgp_network_mask_route_map,
- bgp_network_mask_route_map_cmd,
- "network A.B.C.D mask A.B.C.D route-map WORD",
- "Specify a network to announce via BGP\n"
- "Network number\n"
- "Network mask\n"
- "Network mask\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-{
- int idx_ipv4 = 1;
- int idx_ipv4_2 = 3;
- int idx_word = 5;
- int ret;
- char prefix_str[BUFSIZ];
-
- ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg,
- prefix_str);
- if (!ret) {
- vty_out(vty, "%% Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return bgp_static_set(vty, prefix_str, AFI_IP, bgp_node_safi(vty),
- argv[idx_word]->arg, 0, BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (bgp_network_mask_backdoor,
- bgp_network_mask_backdoor_cmd,
- "network A.B.C.D mask A.B.C.D backdoor",
- "Specify a network to announce via BGP\n"
- "Network number\n"
- "Network mask\n"
- "Network mask\n"
- "Specify a BGP backdoor route\n")
-{
- int idx_ipv4 = 1;
- int idx_ipv4_2 = 3;
- int ret;
- char prefix_str[BUFSIZ];
-
- ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg,
- prefix_str);
- if (!ret) {
- vty_out(vty, "%% Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return bgp_static_set(vty, prefix_str, AFI_IP, SAFI_UNICAST, NULL, 1,
- BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (bgp_network_mask_natural,
- bgp_network_mask_natural_cmd,
- "network A.B.C.D",
- "Specify a network to announce via BGP\n"
- "Network number\n")
-{
- int idx_ipv4 = 1;
- int ret;
- char prefix_str[BUFSIZ];
-
- ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, NULL, prefix_str);
- if (!ret) {
- vty_out(vty, "%% Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return bgp_static_set(vty, prefix_str, AFI_IP, bgp_node_safi(vty), NULL,
- 0, BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (bgp_network_mask_natural_route_map,
- bgp_network_mask_natural_route_map_cmd,
- "network A.B.C.D route-map WORD",
- "Specify a network to announce via BGP\n"
- "Network number\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-{
- int idx_ipv4 = 1;
- int idx_word = 3;
- int ret;
- char prefix_str[BUFSIZ];
-
- ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, NULL, prefix_str);
- if (!ret) {
- vty_out(vty, "%% Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return bgp_static_set(vty, prefix_str, AFI_IP, bgp_node_safi(vty),
- argv[idx_word]->arg, 0, BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (bgp_network_mask_natural_backdoor,
- bgp_network_mask_natural_backdoor_cmd,
- "network A.B.C.D backdoor",
- "Specify a network to announce via BGP\n"
- "Network number\n"
- "Specify a BGP backdoor route\n")
-{
- int idx_ipv4 = 1;
- int ret;
- char prefix_str[BUFSIZ];
-
- ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, NULL, prefix_str);
- if (!ret) {
- vty_out(vty, "%% Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return bgp_static_set(vty, prefix_str, AFI_IP, SAFI_UNICAST, NULL, 1,
- BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (bgp_network_label_index,
- bgp_network_label_index_cmd,
- "network A.B.C.D/M label-index (0-1048560)",
- "Specify a network to announce via BGP\n"
- "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
- "Label index to associate with the prefix\n"
- "Label index value\n")
-{
- u_int32_t label_index;
-
- label_index = strtoul(argv[3]->arg, NULL, 10);
- return bgp_static_set(vty, argv[1]->arg, AFI_IP, bgp_node_safi(vty),
- NULL, 0, label_index);
-}
-
-DEFUN (bgp_network_label_index_route_map,
- bgp_network_label_index_route_map_cmd,
- "network A.B.C.D/M label-index (0-1048560) route-map WORD",
- "Specify a network to announce via BGP\n"
- "IP prefix\n"
- "Label index to associate with the prefix\n"
- "Label index value\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-{
- u_int32_t label_index;
-
- label_index = strtoul(argv[3]->arg, NULL, 10);
- return bgp_static_set(vty, argv[1]->arg, AFI_IP, bgp_node_safi(vty),
- argv[5]->arg, 0, label_index);
-}
-
-DEFUN (no_bgp_network,
- no_bgp_network_cmd,
- "no network A.B.C.D/M [<backdoor|route-map WORD>]",
- NO_STR
- "Specify a network to announce via BGP\n"
- "IPv4 prefix\n"
- "Specify a BGP backdoor route\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-{
- int idx_ipv4_prefixlen = 2;
- return bgp_static_unset(vty, argv[idx_ipv4_prefixlen]->arg, AFI_IP,
- bgp_node_safi(vty));
-}
-
-DEFUN (no_bgp_network_mask,
- no_bgp_network_mask_cmd,
- "no network A.B.C.D mask A.B.C.D [<backdoor|route-map WORD>]",
- NO_STR
- "Specify a network to announce via BGP\n"
- "Network number\n"
- "Network mask\n"
- "Network mask\n"
- "Specify a BGP backdoor route\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-{
- int idx_ipv4 = 2;
- int idx_ipv4_2 = 4;
- int ret;
- char prefix_str[BUFSIZ];
-
- ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg,
- prefix_str);
- if (!ret) {
- vty_out(vty, "%% Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return bgp_static_unset(vty, prefix_str, AFI_IP, bgp_node_safi(vty));
-}
-
-DEFUN (no_bgp_network_mask_natural,
- no_bgp_network_mask_natural_cmd,
- "no network A.B.C.D [<backdoor|route-map WORD>]",
- NO_STR
- "Specify a network to announce via BGP\n"
- "Network number\n"
- "Specify a BGP backdoor route\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-{
- int idx_ipv4 = 2;
- int ret;
- char prefix_str[BUFSIZ];
+DEFPY(bgp_network,
+ bgp_network_cmd,
+ "[no] network \
+ <A.B.C.D/M$prefix|A.B.C.D$address [mask A.B.C.D$netmask]> \
+ [{route-map WORD$map_name|label-index (0-1048560)$label_index| \
+ backdoor$backdoor}]",
+ NO_STR
+ "Specify a network to announce via BGP\n"
+ "IPv4 prefix\n"
+ "Network number\n"
+ "Network mask\n"
+ "Network mask\n"
+ "Route-map to modify the attributes\n"
+ "Name of the route map\n"
+ "Label index to associate with the prefix\n"
+ "Label index value\n"
+ "Specify a BGP backdoor route\n")
+{
+ char addr_prefix_str[BUFSIZ];
+
+ if (address_str) {
+ int ret;
- ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, NULL, prefix_str);
- if (!ret) {
- vty_out(vty, "%% Inconsistent address and mask\n");
- return CMD_WARNING_CONFIG_FAILED;
+ ret = netmask_str2prefix_str(address_str, netmask_str,
+ addr_prefix_str);
+ if (!ret) {
+ vty_out(vty, "%% Inconsistent address and mask\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
}
- return bgp_static_unset(vty, prefix_str, AFI_IP, bgp_node_safi(vty));
-}
-
-ALIAS(no_bgp_network, no_bgp_network_label_index_cmd,
- "no network A.B.C.D/M label-index (0-1048560)", NO_STR
- "Specify a network to announce via BGP\n"
- "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
- "Label index to associate with the prefix\n"
- "Label index value\n")
-
-ALIAS(no_bgp_network, no_bgp_network_label_index_route_map_cmd,
- "no network A.B.C.D/M label-index (0-1048560) route-map WORD", NO_STR
- "Specify a network to announce via BGP\n"
- "IP prefix\n"
- "Label index to associate with the prefix\n"
- "Label index value\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-
-DEFUN (ipv6_bgp_network,
- ipv6_bgp_network_cmd,
- "network X:X::X:X/M",
- "Specify a network to announce via BGP\n"
- "IPv6 prefix\n")
-{
- int idx_ipv6_prefixlen = 1;
- return bgp_static_set(vty, argv[idx_ipv6_prefixlen]->arg, AFI_IP6,
- bgp_node_safi(vty), NULL, 0,
- BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (ipv6_bgp_network_route_map,
- ipv6_bgp_network_route_map_cmd,
- "network X:X::X:X/M route-map WORD",
- "Specify a network to announce via BGP\n"
- "IPv6 prefix\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-{
- int idx_ipv6_prefixlen = 1;
- int idx_word = 3;
- return bgp_static_set(vty, argv[idx_ipv6_prefixlen]->arg, AFI_IP6,
- bgp_node_safi(vty), argv[idx_word]->arg, 0,
- BGP_INVALID_LABEL_INDEX);
-}
-
-DEFUN (ipv6_bgp_network_label_index,
- ipv6_bgp_network_label_index_cmd,
- "network X:X::X:X/M label-index (0-1048560)",
- "Specify a network to announce via BGP\n"
- "IPv6 prefix <network>/<length>\n"
- "Label index to associate with the prefix\n"
- "Label index value\n")
-{
- u_int32_t label_index;
-
- label_index = strtoul(argv[3]->arg, NULL, 10);
- return bgp_static_set(vty, argv[1]->arg, AFI_IP6, bgp_node_safi(vty),
- NULL, 0, label_index);
-}
-
-DEFUN (ipv6_bgp_network_label_index_route_map,
- ipv6_bgp_network_label_index_route_map_cmd,
- "network X:X::X:X/M label-index (0-1048560) route-map WORD",
- "Specify a network to announce via BGP\n"
- "IPv6 prefix\n"
- "Label index to associate with the prefix\n"
- "Label index value\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-{
- u_int32_t label_index;
-
- label_index = strtoul(argv[3]->arg, NULL, 10);
- return bgp_static_set(vty, argv[1]->arg, AFI_IP6, bgp_node_safi(vty),
- argv[5]->arg, 0, label_index);
+ return bgp_static_set(vty, no, address_str ? addr_prefix_str:prefix_str,
+ AFI_IP, bgp_node_safi(vty),
+ map_name, backdoor?1:0,
+ label_index ?
+ (uint32_t)label_index : BGP_INVALID_LABEL_INDEX);
}
-DEFUN (no_ipv6_bgp_network,
- no_ipv6_bgp_network_cmd,
- "no network X:X::X:X/M [route-map WORD]",
- NO_STR
- "Specify a network to announce via BGP\n"
- "IPv6 prefix\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
+DEFPY(ipv6_bgp_network,
+ ipv6_bgp_network_cmd,
+ "[no] network X:X::X:X/M$prefix \
+ [{route-map WORD$map_name|label-index (0-1048560)$label_index}]",
+ NO_STR
+ "Specify a network to announce via BGP\n"
+ "IPv6 prefix\n"
+ "Route-map to modify the attributes\n"
+ "Name of the route map\n"
+ "Label index to associate with the prefix\n"
+ "Label index value\n")
{
- int idx_ipv6_prefixlen = 2;
- return bgp_static_unset(vty, argv[idx_ipv6_prefixlen]->arg, AFI_IP6,
- bgp_node_safi(vty));
+ return bgp_static_set(vty, no, prefix_str, AFI_IP6,
+ bgp_node_safi(vty), map_name, 0,
+ label_index ?
+ (uint32_t)label_index : BGP_INVALID_LABEL_INDEX);
}
-ALIAS(no_ipv6_bgp_network, no_ipv6_bgp_network_label_index_cmd,
- "no network X:X::X:X/M label-index (0-1048560)", NO_STR
- "Specify a network to announce via BGP\n"
- "IPv6 prefix <network>/<length>\n"
- "Label index to associate with the prefix\n"
- "Label index value\n")
-
-ALIAS(no_ipv6_bgp_network, no_ipv6_bgp_network_label_index_route_map_cmd,
- "no network X:X::X:X/M label-index (0-1048560) route-map WORD", NO_STR
- "Specify a network to announce via BGP\n"
- "IPv6 prefix\n"
- "Label index to associate with the prefix\n"
- "Label index value\n"
- "Route-map to modify the attributes\n"
- "Name of the route map\n")
-
/* Aggreagete address:
advertise-map Set condition to advertise attribute
vty_out(vty, " Imported from %s:%s\n",
prefix_rd2str(
(struct prefix_rd *)&prn->p,
- buf1, RD_ADDRSTRLEN),
+ buf1, sizeof(buf1)),
buf2);
}
}
json_peer, "routerId",
inet_ntop(AF_INET,
&binfo->peer->remote_id, buf1,
- BUFSIZ));
+ sizeof(buf1)));
if (binfo->peer->hostname)
json_object_string_add(
inet_ntop(
AF_INET,
&binfo->peer->remote_id,
- buf1, BUFSIZ));
+ buf1, sizeof(buf1)));
}
}
vty_out(vty,
"{\n \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64
",\n \"routerId\": \"%s\",\n \"routes\": { ",
- bgp->vrf_id == VRF_UNKNOWN ? -1 : bgp->vrf_id,
+ bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id,
bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT ? "Default"
: bgp->name,
table->version, inet_ntoa(bgp->router_id));
vty_out(vty, ",\"%s\": ", buf2);
vty_out(vty, "%s",
- json_object_to_json_string_ext(json_paths, JSON_C_TO_STRING_PRETTY));
+ json_object_to_json_string(json_paths));
json_object_free(json_paths);
+ json_paths = NULL;
first = 0;
}
}
*total_cum = total_count;
}
if (use_json) {
- json_object_free(json_paths);
+ if (json_paths)
+ json_object_free(json_paths);
if (is_last)
vty_out(vty, " } }\n");
else
struct bgp_node *rn, *next;
unsigned long output_cum = 0;
unsigned long total_cum = 0;
+ bool show_msg;
+
+ show_msg = (!use_json && type == bgp_show_type_normal);
for (rn = bgp_table_top(table); rn; rn = next) {
next = bgp_route_next(rn);
continue;
if (rn->info != NULL) {
struct prefix_rd prd;
- char rd[BUFSIZ];
+ char rd[RD_ADDRSTRLEN];
memcpy(&prd, &(rn->p), sizeof(struct prefix_rd));
- if (prefix_rd2str(&prd, rd, BUFSIZ) == NULL)
- sprintf(rd,
- "Unknown Type: %u",
- decode_rd_type(prd.val));
+ prefix_rd2str(&prd, rd, sizeof(rd));
bgp_show_table(vty, bgp, safi, rn->info, type,
output_arg, use_json,
rd, next == NULL,
&output_cum, &total_cum);
+ if (next == NULL)
+ show_msg = false;
}
}
+ if (show_msg) {
+ if (output_cum == 0)
+ vty_out(vty, "No BGP prefixes displayed, %ld exist\n",
+ total_cum);
+ else
+ vty_out(vty,
+ "\nDisplayed %ld routes and %ld total paths\n",
+ output_cum, total_cum);
+ }
if (use_json)
vty_out(vty, " } }");
return CMD_SUCCESS;
struct prefix *p;
struct peer *peer;
struct listnode *node, *nnode;
- char buf1[INET6_ADDRSTRLEN];
+ char buf1[RD_ADDRSTRLEN];
char buf2[INET6_ADDRSTRLEN];
#if defined(HAVE_CUMULUS)
char buf3[EVPN_ROUTE_STRLEN];
#if defined(HAVE_CUMULUS)
if (safi == SAFI_EVPN)
vty_out(vty, "BGP routing table entry for %s%s%s\n",
- prd ? prefix_rd2str(prd, buf1, RD_ADDRSTRLEN)
+ prd ? prefix_rd2str(prd, buf1, sizeof(buf1))
: "",
prd ? ":" : "",
bgp_evpn_route2str((struct prefix_evpn *)p,
vty_out(vty, "BGP routing table entry for %s%s%s/%d\n",
((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP)
? prefix_rd2str(prd, buf1,
- RD_ADDRSTRLEN)
+ sizeof(buf1))
: ""),
safi == SAFI_MPLS_VPN ? ":" : "",
inet_ntop(p->family, &p->u.prefix, buf2,
vty_out(vty, "BGP routing table entry for %s%s%s/%d\n",
((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
|| safi == SAFI_EVPN)
- ? prefix_rd2str(prd, buf1, RD_ADDRSTRLEN)
- : ""),
+ ? prefix_rd2str(prd, buf1, sizeof(buf1))
+ : ""),
((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) ? ":"
: "",
buf2, p->prefixlen);
afi_t afi = AFI_MAX;
int idx = 0;
- (void)argv_find_and_parse_afi(argv, argc, &idx, &afi);
+ if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ vty_out(vty, "%% Malformed Address Family\n");
+ return CMD_WARNING;
+ }
+
ret = str2prefix_rd(argv[5]->arg, &prd);
if (!ret) {
vty_out(vty, "%% Malformed Route Distinguisher\n");
return CMD_WARNING;
}
+
return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, &prd,
0, BGP_PATH_ALL, use_json(argc, argv));
}
prd = (struct prefix_rd *)&prn->p;
/* "network" configuration display. */
- prefix_rd2str(prd, rdbuf, RD_ADDRSTRLEN);
+ prefix_rd2str(prd, rdbuf, sizeof(rdbuf));
label = decode_label(&bgp_static->label);
vty_out(vty, " network %s/%d rd %s",
if (bgp_static->rmap.name)
vty_out(vty, " route-map %s",
bgp_static->rmap.name);
- else {
- if (bgp_static->backdoor)
- vty_out(vty, " backdoor");
- }
+
+ if (bgp_static->backdoor)
+ vty_out(vty, " backdoor");
+
vty_out(vty, "\n");
}
}
prd = (struct prefix_rd *)&prn->p;
/* "network" configuration display. */
- prefix_rd2str(prd, rdbuf, RD_ADDRSTRLEN);
+ prefix_rd2str(prd, rdbuf, sizeof(rdbuf));
if (p->u.prefix_evpn.route_type == 5) {
char local_buf[PREFIX_STRLEN];
uint8_t family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)
&bgp_static->gatewayIp.u.prefix, buf2,
sizeof(buf2));
vty_out(vty,
- " network %s rd %s ethtag %u tag %u esi %s gwip %s routermac %s\n",
+ " network %s rd %s ethtag %u label %u esi %s gwip %s routermac %s\n",
buf, rdbuf, p->u.prefix_evpn.eth_tag,
decode_label(&bgp_static->label), esi, buf2,
macrouter);
if (bgp_static->rmap.name)
vty_out(vty, " route-map %s", bgp_static->rmap.name);
- else {
- if (bgp_static->backdoor)
- vty_out(vty, " backdoor");
- }
+
+ if (bgp_static->backdoor)
+ vty_out(vty, " backdoor");
vty_out(vty, "\n");
}
/* IPv4 BGP commands. */
install_element(BGP_NODE, &bgp_table_map_cmd);
install_element(BGP_NODE, &bgp_network_cmd);
- install_element(BGP_NODE, &bgp_network_mask_cmd);
- install_element(BGP_NODE, &bgp_network_mask_natural_cmd);
- install_element(BGP_NODE, &bgp_network_route_map_cmd);
- install_element(BGP_NODE, &bgp_network_mask_route_map_cmd);
- install_element(BGP_NODE, &bgp_network_mask_natural_route_map_cmd);
- install_element(BGP_NODE, &bgp_network_backdoor_cmd);
- install_element(BGP_NODE, &bgp_network_mask_backdoor_cmd);
- install_element(BGP_NODE, &bgp_network_mask_natural_backdoor_cmd);
install_element(BGP_NODE, &no_bgp_table_map_cmd);
- install_element(BGP_NODE, &no_bgp_network_cmd);
- install_element(BGP_NODE, &no_bgp_network_mask_cmd);
- install_element(BGP_NODE, &no_bgp_network_mask_natural_cmd);
install_element(BGP_NODE, &aggregate_address_cmd);
install_element(BGP_NODE, &aggregate_address_mask_cmd);
/* IPv4 unicast configuration. */
install_element(BGP_IPV4_NODE, &bgp_table_map_cmd);
install_element(BGP_IPV4_NODE, &bgp_network_cmd);
- install_element(BGP_IPV4_NODE, &bgp_network_mask_cmd);
- install_element(BGP_IPV4_NODE, &bgp_network_mask_natural_cmd);
- install_element(BGP_IPV4_NODE, &bgp_network_route_map_cmd);
- install_element(BGP_IPV4_NODE, &bgp_network_mask_route_map_cmd);
- install_element(BGP_IPV4_NODE, &bgp_network_mask_natural_route_map_cmd);
- install_element(BGP_IPV4_NODE, &bgp_network_label_index_cmd);
- install_element(BGP_IPV4_NODE, &bgp_network_label_index_route_map_cmd);
- install_element(BGP_IPV4_NODE, &no_bgp_network_label_index_cmd);
- install_element(BGP_IPV4_NODE,
- &no_bgp_network_label_index_route_map_cmd);
install_element(BGP_IPV4_NODE, &no_bgp_table_map_cmd);
- install_element(BGP_IPV4_NODE, &no_bgp_network_cmd);
- install_element(BGP_IPV4_NODE, &no_bgp_network_mask_cmd);
- install_element(BGP_IPV4_NODE, &no_bgp_network_mask_natural_cmd);
install_element(BGP_IPV4_NODE, &aggregate_address_cmd);
install_element(BGP_IPV4_NODE, &aggregate_address_mask_cmd);
/* IPv4 multicast configuration. */
install_element(BGP_IPV4M_NODE, &bgp_table_map_cmd);
install_element(BGP_IPV4M_NODE, &bgp_network_cmd);
- install_element(BGP_IPV4M_NODE, &bgp_network_mask_cmd);
- install_element(BGP_IPV4M_NODE, &bgp_network_mask_natural_cmd);
- install_element(BGP_IPV4M_NODE, &bgp_network_route_map_cmd);
- install_element(BGP_IPV4M_NODE, &bgp_network_mask_route_map_cmd);
- install_element(BGP_IPV4M_NODE,
- &bgp_network_mask_natural_route_map_cmd);
install_element(BGP_IPV4M_NODE, &no_bgp_table_map_cmd);
- install_element(BGP_IPV4M_NODE, &no_bgp_network_cmd);
- install_element(BGP_IPV4M_NODE, &no_bgp_network_mask_cmd);
- install_element(BGP_IPV4M_NODE, &no_bgp_network_mask_natural_cmd);
install_element(BGP_IPV4M_NODE, &aggregate_address_cmd);
install_element(BGP_IPV4M_NODE, &aggregate_address_mask_cmd);
install_element(BGP_IPV4M_NODE, &no_aggregate_address_cmd);
/* New config IPv6 BGP commands. */
install_element(BGP_IPV6_NODE, &bgp_table_map_cmd);
install_element(BGP_IPV6_NODE, &ipv6_bgp_network_cmd);
- install_element(BGP_IPV6_NODE, &ipv6_bgp_network_route_map_cmd);
install_element(BGP_IPV6_NODE, &no_bgp_table_map_cmd);
- install_element(BGP_IPV6_NODE, &no_ipv6_bgp_network_cmd);
- install_element(BGP_IPV6_NODE, &ipv6_bgp_network_label_index_cmd);
- install_element(BGP_IPV6_NODE, &no_ipv6_bgp_network_label_index_cmd);
- install_element(BGP_IPV6_NODE,
- &ipv6_bgp_network_label_index_route_map_cmd);
- install_element(BGP_IPV6_NODE,
- &no_ipv6_bgp_network_label_index_route_map_cmd);
install_element(BGP_IPV6_NODE, &ipv6_aggregate_address_cmd);
install_element(BGP_IPV6_NODE, &no_ipv6_aggregate_address_cmd);
install_element(BGP_IPV6M_NODE, &ipv6_bgp_network_cmd);
- install_element(BGP_IPV6M_NODE, &no_ipv6_bgp_network_cmd);
install_element(BGP_NODE, &bgp_distance_cmd);
install_element(BGP_NODE, &no_bgp_distance_cmd);
/* Nexthop reachability check. */
u_int32_t igpmetric;
- /* MPLS label. */
+ /* MPLS label - L2VNI */
mpls_label_t label;
+ /* MPLS label - L3-VNI */
+ mpls_label_t label2;
+
#if ENABLE_BGP_VNC
union {
aggregator =
XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct aggregator));
- sscanf(arg, "%s %s", as, address);
+ if (sscanf(arg, "%s %s", as, address) != 2) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, aggregator);
+ return NULL;
+ }
aggregator->as = strtoul(as, NULL, 10);
ret = inet_aton(address, &aggregator->address);
{
static struct in_addr addr;
struct peer *peer;
+ uint32_t ui, uo;
if (smux_header_table(v, name, length, exact, var_len, write_method)
== MATCH_FAILED)
return SNMP_INTEGER(peer->as);
break;
case BGPPEERINUPDATES:
- return SNMP_INTEGER(peer->update_in);
+ ui = atomic_load_explicit(&peer->update_in,
+ memory_order_relaxed);
+ return SNMP_INTEGER(ui);
break;
case BGPPEEROUTUPDATES:
- return SNMP_INTEGER(peer->update_out);
+ uo = atomic_load_explicit(&peer->update_out,
+ memory_order_relaxed);
+ return SNMP_INTEGER(uo);
break;
case BGPPEERINTOTALMESSAGES:
- return SNMP_INTEGER(peer->open_in + peer->update_in
- + peer->keepalive_in + peer->notify_in
- + peer->refresh_in + peer->dynamic_cap_in);
+ return SNMP_INTEGER(PEER_TOTAL_RX(peer));
break;
case BGPPEEROUTTOTALMESSAGES:
- return SNMP_INTEGER(peer->open_out + peer->update_out
- + peer->keepalive_out + peer->notify_out
- + peer->refresh_out
- + peer->dynamic_cap_out);
+ return SNMP_INTEGER(PEER_TOTAL_TX(peer));
break;
case BGPPEERLASTERROR: {
static u_char lasterror[2];
#include "bgpd/bgp_updgrp.h"
#include "bgpd/bgp_bfd.h"
#include "bgpd/bgp_io.h"
+#include "bgpd/bgp_evpn.h"
static struct peer_group *listen_range_exists(struct bgp *bgp,
struct prefix *range, int exact);
*/
}
+ /* unset the auto created flag as the user config is now present */
+ UNSET_FLAG(bgp->vrf_flags, BGP_VRF_AUTO);
VTY_PUSH_CONTEXT(BGP_NODE, bgp);
return CMD_SUCCESS;
"%% Multiple BGP processes are configured\n");
return CMD_WARNING_CONFIG_FAILED;
}
+
+ if (bgp->l3vni) {
+ vty_out(vty, "%% Please unconfigure l3vni %u",
+ bgp->l3vni);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
} else {
as = strtoul(argv[idx_asn]->arg, NULL, 10);
vty_out(vty, "%% Can't find BGP instance\n");
return CMD_WARNING_CONFIG_FAILED;
}
+
+ if (bgp->l3vni) {
+ vty_out(vty, "%% Please unconfigure l3vni %u",
+ bgp->l3vni);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
}
bgp_delete(bgp);
void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp)
{
- if (bgp->coalesce_time != BGP_DEFAULT_SUBGROUP_COALESCE_TIME)
+ if (!bgp->heuristic_coalesce)
vty_out(vty, " coalesce-time %u\n", bgp->coalesce_time);
}
int idx = 0;
argv_find(argv, argc, "(0-4294967295)", &idx);
+ bgp->heuristic_coalesce = false;
bgp->coalesce_time = strtoul(argv[idx]->arg, NULL, 10);
return CMD_SUCCESS;
}
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp->heuristic_coalesce = true;
bgp->coalesce_time = BGP_DEFAULT_SUBGROUP_COALESCE_TIME;
return CMD_SUCCESS;
}
return bgp_vty_return(vty, ret);
}
+DEFUN (bgp_default_shutdown,
+ bgp_default_shutdown_cmd,
+ "[no] bgp default shutdown",
+ NO_STR
+ BGP_STR
+ "Configure BGP defaults\n"
+ "Do not automatically activate peers upon configuration\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp->autoshutdown = !strmatch(argv[0]->text, "no");
+ return CMD_SUCCESS;
+}
+
DEFUN (neighbor_remote_as,
neighbor_remote_as_cmd,
"neighbor <A.B.C.D|X:X::X:X|WORD> remote-as <(1-4294967295)|internal|external>",
return bgp_vty_return(vty, ret);
}
-
DEFUN (neighbor_activate,
neighbor_activate_cmd,
"neighbor <A.B.C.D|X:X::X:X|WORD> activate",
"Show BGP VRFs\n"
JSON_STR)
{
+ char buf[ETHER_ADDR_STRLEN];
struct list *inst = bm->bgp;
struct listnode *node;
struct bgp *bgp;
json_object *json = NULL;
json_object *json_vrfs = NULL;
int count = 0;
- static char header[] =
- "Type Id RouterId #PeersCfg #PeersEstb Name";
if (!bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) {
vty_out(vty, "BGP Multiple Instance is not enabled\n");
struct listnode *node, *nnode;
int peers_cfg, peers_estb;
json_object *json_vrf = NULL;
- int vrf_id_ui;
/* Skip Views. */
if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
count++;
if (!uj && count == 1)
- vty_out(vty, "%s\n", header);
+ vty_out(vty,
+ "%4s %-5s %-16s %9s %10s %-37s %-10s %-15s\n",
+ "Type", "Id", "routerId", "#PeersVfg",
+ "#PeersEstb", "Name", "L3-VNI", "Rmac");
peers_cfg = peers_estb = 0;
if (uj)
type = "VRF";
}
- vrf_id_ui = (bgp->vrf_id == VRF_UNKNOWN) ? -1 : bgp->vrf_id;
+
if (uj) {
+ int64_t vrf_id_ui = (bgp->vrf_id == VRF_UNKNOWN) ? -1 :
+ (int64_t)bgp->vrf_id;
json_object_string_add(json_vrf, "type", type);
json_object_int_add(json_vrf, "vrfId", vrf_id_ui);
json_object_string_add(json_vrf, "routerId",
json_object_int_add(json_vrf, "numEstablishedPeers",
peers_estb);
+ json_object_int_add(json_vrf, "l3vni", bgp->l3vni);
+ json_object_string_add(json_vrf, "rmac",
+ prefix_mac2str(&bgp->rmac, buf,
+ sizeof(buf)));
json_object_object_add(json_vrfs, name, json_vrf);
} else
- vty_out(vty, "%4s %-5d %-16s %9u %10u %s\n", type,
- vrf_id_ui, inet_ntoa(bgp->router_id), peers_cfg,
- peers_estb, name);
+ vty_out(vty,
+ "%4s %-5d %-16s %9u %10u %-37s %-10u %-15s\n",
+ type, bgp->vrf_id == VRF_UNKNOWN ?
+ -1 : (int)bgp->vrf_id,
+ inet_ntoa(bgp->router_id),
+ peers_cfg, peers_estb, name, bgp->l3vni,
+ prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
}
if (uj) {
if (!count) {
unsigned long ents;
char memstrbuf[MTYPE_MEMSTR_LEN];
- int vrf_id_ui;
+ int64_t vrf_id_ui;
vrf_id_ui =
- (bgp->vrf_id == VRF_UNKNOWN) ? -1 : bgp->vrf_id;
+ (bgp->vrf_id == VRF_UNKNOWN) ? -1 :
+ (int64_t)bgp->vrf_id;
/* Usage summary and header */
if (use_json) {
vty_out(vty,
"BGP router identifier %s, local AS number %u vrf-id %d",
inet_ntoa(bgp->router_id), bgp->as,
- vrf_id_ui);
+ bgp->vrf_id == VRF_UNKNOWN ? -1 :
+ (int)bgp->vrf_id);
vty_out(vty, "\n");
}
json_object_int_add(json_peer, "remoteAs", peer->as);
json_object_int_add(json_peer, "version", 4);
json_object_int_add(json_peer, "msgRcvd",
- peer->open_in + peer->update_in
- + peer->keepalive_in
- + peer->notify_in
- + peer->refresh_in
- + peer->dynamic_cap_in);
+ PEER_TOTAL_RX(peer));
json_object_int_add(json_peer, "msgSent",
- peer->open_out + peer->update_out
- + peer->keepalive_out
- + peer->notify_out
- + peer->refresh_out
- + peer->dynamic_cap_out);
+ PEER_TOTAL_TX(peer));
json_object_int_add(json_peer, "tableVersion",
peer->version[afi][safi]);
" ");
vty_out(vty, "4 %10u %7u %7u %8" PRIu64 " %4d %4zd %8s",
- peer->as,
- atomic_load_explicit(&peer->open_in,
- memory_order_relaxed)
- + atomic_load_explicit(
- &peer->update_in,
- memory_order_relaxed)
- + atomic_load_explicit(
- &peer->keepalive_in,
- memory_order_relaxed)
- + atomic_load_explicit(
- &peer->notify_in,
- memory_order_relaxed)
- + atomic_load_explicit(
- &peer->refresh_in,
- memory_order_relaxed)
- + atomic_load_explicit(
- &peer->dynamic_cap_in,
- memory_order_relaxed),
- atomic_load_explicit(&peer->open_out,
- memory_order_relaxed)
- + atomic_load_explicit(
- &peer->update_out,
- memory_order_relaxed)
- + atomic_load_explicit(
- &peer->keepalive_out,
- memory_order_relaxed)
- + atomic_load_explicit(
- &peer->notify_out,
- memory_order_relaxed)
- + atomic_load_explicit(
- &peer->refresh_out,
- memory_order_relaxed)
- + atomic_load_explicit(
- &peer->dynamic_cap_out,
- memory_order_relaxed),
- peer->version[afi][safi], 0, peer->obuf->count,
+ peer->as, PEER_TOTAL_RX(peer),
+ PEER_TOTAL_TX(peer), peer->version[afi][safi],
+ 0, peer->obuf->count,
peer_uptime(peer->uptime, timebuf,
BGP_UPTIME_LEN, 0, NULL));
if (peer->status == Established)
- vty_out(vty, " %12ld",
- peer->pcount[afi][pfx_rcd_safi]);
+ if (peer->afc_recv[afi][pfx_rcd_safi])
+ vty_out(vty, " %12ld",
+ peer->pcount[afi][pfx_rcd_safi]);
+ else
+ vty_out(vty, " NoNeg");
else {
if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN))
vty_out(vty, " Idle (Admin)");
}
if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
- if (p->bgp->advertise_all_vni)
+ if (is_evpn_enabled())
json_object_boolean_true_add(
json_addr, "advertiseAllVnis");
}
/* advertise-vni-all */
if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
- if (p->bgp->advertise_all_vni)
+ if (is_evpn_enabled())
vty_out(vty, " advertise-all-vni\n");
}
if (p->status == Established) {
time_t uptime;
- struct tm *tm;
uptime = bgp_clock();
uptime -= p->uptime;
- tm = gmtime(&uptime);
epoch_tbuf = time(NULL) - uptime;
+#if CONFDATE > 20200101
+ CPP_NOTICE("bgpTimerUp should be deprecated and can be removed now");
+#endif
+ /*
+ * bgpTimerUp was miliseconds that was accurate
+ * up to 1 day, then the value returned
+ * became garbage. So in order to provide
+ * some level of backwards compatability,
+ * we still provde the data, but now
+ * we are returning the correct value
+ * and also adding a new bgpTimerUpMsec
+ * which will allow us to deprecate
+ * this eventually
+ */
json_object_int_add(json_neigh, "bgpTimerUp",
- (tm->tm_sec * 1000)
- + (tm->tm_min * 60000)
- + (tm->tm_hour * 3600000));
+ uptime * 1000);
+ json_object_int_add(json_neigh, "bgpTimerUpMsec",
+ uptime * 1000);
json_object_string_add(json_neigh, "bgpTimerUpString",
peer_uptime(p->uptime, timebuf,
BGP_UPTIME_LEN, 0,
json_object_int_add(json_stat, "depthInq", 0);
json_object_int_add(json_stat, "depthOutq",
(unsigned long)p->obuf->count);
- json_object_int_add(json_stat, "opensSent", p->open_out);
- json_object_int_add(json_stat, "opensRecv", p->open_in);
+ json_object_int_add(json_stat, "opensSent",
+ atomic_load_explicit(&p->open_out,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "opensRecv",
+ atomic_load_explicit(&p->open_in,
+ memory_order_relaxed));
json_object_int_add(json_stat, "notificationsSent",
- p->notify_out);
+ atomic_load_explicit(&p->notify_out,
+ memory_order_relaxed));
json_object_int_add(json_stat, "notificationsRecv",
- p->notify_in);
- json_object_int_add(json_stat, "updatesSent", p->update_out);
- json_object_int_add(json_stat, "updatesRecv", p->update_in);
+ atomic_load_explicit(&p->notify_in,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "updatesSent",
+ atomic_load_explicit(&p->update_out,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "updatesRecv",
+ atomic_load_explicit(&p->update_in,
+ memory_order_relaxed));
json_object_int_add(json_stat, "keepalivesSent",
- p->keepalive_out);
+ atomic_load_explicit(&p->keepalive_out,
+ memory_order_relaxed));
json_object_int_add(json_stat, "keepalivesRecv",
- p->keepalive_in);
+ atomic_load_explicit(&p->keepalive_in,
+ memory_order_relaxed));
json_object_int_add(json_stat, "routeRefreshSent",
- p->refresh_out);
+ atomic_load_explicit(&p->refresh_out,
+ memory_order_relaxed));
json_object_int_add(json_stat, "routeRefreshRecv",
- p->refresh_in);
+ atomic_load_explicit(&p->refresh_in,
+ memory_order_relaxed));
json_object_int_add(json_stat, "capabilitySent",
- p->dynamic_cap_out);
+ atomic_load_explicit(&p->dynamic_cap_out,
+ memory_order_relaxed));
json_object_int_add(json_stat, "capabilityRecv",
- p->dynamic_cap_in);
- json_object_int_add(json_stat, "totalSent",
- p->open_out + p->notify_out + p->update_out
- + p->keepalive_out + p->refresh_out
- + p->dynamic_cap_out);
- json_object_int_add(json_stat, "totalRecv",
- p->open_in + p->notify_in + p->update_in
- + p->keepalive_in + p->refresh_in
- + p->dynamic_cap_in);
+ atomic_load_explicit(&p->dynamic_cap_in,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "totalSent", PEER_TOTAL_TX(p));
+ json_object_int_add(json_stat, "totalRecv", PEER_TOTAL_RX(p));
json_object_object_add(json_neigh, "messageStats", json_stat);
} else {
/* Packet counts. */
vty_out(vty, " Outq depth is %lu\n",
(unsigned long)p->obuf->count);
vty_out(vty, " Sent Rcvd\n");
- vty_out(vty, " Opens: %10d %10d\n", p->open_out,
- p->open_in);
- vty_out(vty, " Notifications: %10d %10d\n", p->notify_out,
- p->notify_in);
- vty_out(vty, " Updates: %10d %10d\n", p->update_out,
- p->update_in);
- vty_out(vty, " Keepalives: %10d %10d\n", p->keepalive_out,
- p->keepalive_in);
- vty_out(vty, " Route Refresh: %10d %10d\n", p->refresh_out,
- p->refresh_in);
+ vty_out(vty, " Opens: %10d %10d\n",
+ atomic_load_explicit(&p->open_out,
+ memory_order_relaxed),
+ atomic_load_explicit(&p->open_in,
+ memory_order_relaxed));
+ vty_out(vty, " Notifications: %10d %10d\n",
+ atomic_load_explicit(&p->notify_out,
+ memory_order_relaxed),
+ atomic_load_explicit(&p->notify_in,
+ memory_order_relaxed));
+ vty_out(vty, " Updates: %10d %10d\n",
+ atomic_load_explicit(&p->update_out,
+ memory_order_relaxed),
+ atomic_load_explicit(&p->update_in,
+ memory_order_relaxed));
+ vty_out(vty, " Keepalives: %10d %10d\n",
+ atomic_load_explicit(&p->keepalive_out,
+ memory_order_relaxed),
+ atomic_load_explicit(&p->keepalive_in,
+ memory_order_relaxed));
+ vty_out(vty, " Route Refresh: %10d %10d\n",
+ atomic_load_explicit(&p->refresh_out,
+ memory_order_relaxed),
+ atomic_load_explicit(&p->refresh_in,
+ memory_order_relaxed));
vty_out(vty, " Capability: %10d %10d\n",
- p->dynamic_cap_out, p->dynamic_cap_in);
- vty_out(vty, " Total: %10d %10d\n",
- p->open_out + p->notify_out + p->update_out
- + p->keepalive_out + p->refresh_out
- + p->dynamic_cap_out,
- p->open_in + p->notify_in + p->update_in
- + p->keepalive_in + p->refresh_in
- + p->dynamic_cap_in);
+ atomic_load_explicit(&p->dynamic_cap_out,
+ memory_order_relaxed),
+ atomic_load_explicit(&p->dynamic_cap_in,
+ memory_order_relaxed));
+ vty_out(vty, " Total: %10d %10d\n", PEER_TOTAL_TX(p),
+ PEER_TOTAL_RX(p));
}
if (use_json) {
}
if (use_json) {
- bgp_show_bestpath_json(bgp, json);
vty_out(vty, "%s\n", json_object_to_json_string_ext(
json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
}
static void bgp_show_all_instances_neighbors_vty(struct vty *vty,
+ enum show_type type,
+ const char *ip_str,
u_char use_json)
{
struct listnode *node, *nnode;
struct bgp *bgp;
+ union sockunion su;
json_object *json = NULL;
- int is_first = 1;
+ int ret, is_first = 1;
if (use_json)
vty_out(vty, "{\n");
json_object_int_add(json, "vrfId",
(bgp->vrf_id == VRF_UNKNOWN)
- ? -1
- : bgp->vrf_id);
+ ? -1 : (int64_t) bgp->vrf_id);
json_object_string_add(
json, "vrfName",
(bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
? "Default"
: bgp->name);
}
- bgp_show_neighbor(vty, bgp, show_all, NULL, NULL, use_json,
- json);
+
+ if (type == show_peer) {
+ ret = str2sockunion(ip_str, &su);
+ if (ret < 0)
+ bgp_show_neighbor(vty, bgp, type, NULL, ip_str,
+ use_json, json);
+ else
+ bgp_show_neighbor(vty, bgp, type, &su, NULL,
+ use_json, json);
+ } else {
+ bgp_show_neighbor(vty, bgp, show_all, NULL, NULL,
+ use_json, json);
+ }
}
if (use_json)
if (name) {
if (strmatch(name, "all")) {
- bgp_show_all_instances_neighbors_vty(vty, use_json);
+ bgp_show_all_instances_neighbors_vty(vty, type, ip_str,
+ use_json);
return CMD_SUCCESS;
} else {
bgp = bgp_lookup_by_name(name);
install_element(BGP_NODE, &bgp_listen_range_cmd);
install_element(BGP_NODE, &no_bgp_listen_range_cmd);
+ /* "neighbors auto-shutdown" command */
+ install_element(BGP_NODE, &bgp_default_shutdown_cmd);
+
/* "neighbor remote-as" commands. */
install_element(BGP_NODE, &neighbor_remote_as_cmd);
install_element(BGP_NODE, &neighbor_interface_config_cmd);
/* Make Zebra API structure. */
memset(&api, 0, sizeof(api));
+ memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr));
api.vrf_id = bgp->vrf_id;
+ api.nh_vrf_id = bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP;
api.safi = safi;
api.prefix = *p;
if (info->sub_type == BGP_ROUTE_AGGREGATE)
zapi_route_set_blackhole(&api, BLACKHOLE_NULL);
+ /* If it is an EVPN route mark as such.
+ * Currently presence of rmac in attr denotes
+ * this is an EVPN type-2 route
+ */
+ if (!is_zero_mac(&(info->attr->rmac)))
+ SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
+
if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED
|| info->sub_type == BGP_ROUTE_AGGREGATE) {
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
api_nh = &api.nexthops[valid_nh_count];
api_nh->gate.ipv4 = *nexthop;
- api_nh->type = NEXTHOP_TYPE_IPV4;
+
+ /* EVPN type-2 routes are
+ programmed as onlink on l3-vni SVI
+ */
+ if (CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ else
+ api_nh->type = NEXTHOP_TYPE_IPV4;
} else {
ifindex_t ifindex;
struct in6_addr *nexthop;
api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
}
- if (mpinfo->extra
- && bgp_is_valid_label(&mpinfo->extra->label)) {
+ if (mpinfo->extra && bgp_is_valid_label(&mpinfo->extra->label)
+ && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) {
has_valid_label = 1;
label = label_pton(&mpinfo->extra->label);
valid_nh_count++;
}
- if (has_valid_label)
+ /* if this is a evpn route we don't have to include the label */
+ if (has_valid_label &&
+ !(CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)))
SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
if (info->sub_type != BGP_ROUTE_AGGREGATE)
sizeof(nh_buf));
label_buf[0] = '\0';
- if (has_valid_label)
+ if (has_valid_label &&
+ !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))
sprintf(label_buf, "label %u",
api_nh->labels[0]);
zlog_debug(" nhop [%d]: %s %s", i + 1, nh_buf,
return;
memset(&api, 0, sizeof(api));
+ memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr));
api.vrf_id = peer->bgp->vrf_id;
+ api.nh_vrf_id = peer->bgp->vrf_id;
api.type = ZEBRA_ROUTE_BGP;
api.safi = safi;
api.prefix = *p;
+ /* If it is an EVPN route mark as such.
+ * Currently presence of rmac in attr denotes
+ * this is an EVPN type-2 route
+ */
+ if (!is_zero_mac(&(info->attr->rmac)))
+ SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
+
if (peer->sort == BGP_PEER_IBGP) {
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL);
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
- if (vrf_bitmap_check(zclient->redist[afi][i],
- old_vrf_id)) {
+ if ((old_vrf_id == VRF_UNKNOWN)
+ || vrf_bitmap_check(zclient->redist[afi][i],
+ old_vrf_id)) {
vrf_bitmap_unset(zclient->redist[afi][i],
old_vrf_id);
vrf_bitmap_set(zclient->redist[afi][i],
/* For default instance, register to learn about VNIs, if appropriate.
*/
if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
- && bgp->advertise_all_vni)
+ && is_evpn_enabled())
bgp_zebra_advertise_all_vni(bgp, 1);
}
/* For default instance, unregister learning about VNIs, if appropriate.
*/
if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
- && bgp->advertise_all_vni)
+ && is_evpn_enabled())
bgp_zebra_advertise_all_vni(bgp, 0);
/* Deregister for router-id, interfaces, redistributed routes. */
*/
}
+static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ vni_t l3vni = 0;
+ struct ethaddr rmac;
+ struct in_addr originator_ip;
+ struct stream *s;
+
+ memset(&rmac, 0, sizeof(struct ethaddr));
+ memset(&originator_ip, 0, sizeof(struct in_addr));
+ s = zclient->ibuf;
+ l3vni = stream_getl(s);
+ if (cmd == ZEBRA_L3VNI_ADD) {
+ stream_get(&rmac, s, sizeof(struct ethaddr));
+ originator_ip.s_addr = stream_get_ipv4(s);
+ }
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s",
+ (cmd == ZEBRA_L3VNI_ADD) ? "add" : "del",
+ vrf_id_to_name(vrf_id),
+ l3vni,
+ prefix_mac2str(&rmac, buf, sizeof(buf)));
+
+ if (cmd == ZEBRA_L3VNI_ADD)
+ bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip);
+ else
+ bgp_evpn_local_l3vni_del(l3vni, vrf_id);
+
+ return 0;
+}
+
static int bgp_zebra_process_local_vni(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
vni_t vni;
struct bgp *bgp;
struct in_addr vtep_ip;
+ vrf_id_t tenant_vrf_id = VRF_DEFAULT;
s = zclient->ibuf;
vni = stream_getl(s);
- if (command == ZEBRA_VNI_ADD)
+ if (command == ZEBRA_VNI_ADD) {
vtep_ip.s_addr = stream_get_ipv4(s);
+ stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t));
+ }
+
bgp = bgp_lookup_by_vrf_id(vrf_id);
if (!bgp)
return 0;
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("Rx VNI %s VRF %u VNI %u",
- (command == ZEBRA_VNI_ADD) ? "add" : "del", vrf_id,
- vni);
+ zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s",
+ (command == ZEBRA_VNI_ADD) ? "add" : "del",
+ vrf_id_to_name(vrf_id),
+ vni, vrf_id_to_name(tenant_vrf_id));
if (command == ZEBRA_VNI_ADD)
return bgp_evpn_local_vni_add(
- bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id);
+ bgp, vni, vtep_ip.s_addr ? vtep_ip : bgp->router_id,
+ tenant_vrf_id);
else
return bgp_evpn_local_vni_del(bgp, vni);
}
zclient->local_vni_del = bgp_zebra_process_local_vni;
zclient->local_macip_add = bgp_zebra_process_local_macip;
zclient->local_macip_del = bgp_zebra_process_local_macip;
+ zclient->local_l3vni_add = bgp_zebra_process_local_l3vni;
+ zclient->local_l3vni_del = bgp_zebra_process_local_l3vni;
}
void bgp_zebra_destroy(void)
#include "thread.h"
#include "buffer.h"
#include "stream.h"
+#include "ringbuf.h"
#include "command.h"
#include "sockunion.h"
#include "sockopt.h"
#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_keepalives.h"
#include "bgpd/bgp_io.h"
-
+#include "bgpd/bgp_ecommunity.h"
DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
DEFINE_QOBJ_TYPE(bgp_master)
return 0;
/* EVPN uses router id in RD, withdraw them */
- if (bgp->advertise_all_vni)
+ if (is_evpn_enabled())
bgp_evpn_handle_router_id_update(bgp, TRUE);
IPV4_ADDR_COPY(&bgp->router_id, id);
}
/* EVPN uses router id in RD, update them */
- if (bgp->advertise_all_vni)
+ if (is_evpn_enabled())
bgp_evpn_handle_router_id_update(bgp, FALSE);
return 0;
/* peer global config reset */
static void peer_global_config_reset(struct peer *peer)
{
-
- int v6only;
+ int saved_flags = 0;
peer->change_local_as = 0;
peer->ttl = (peer_sort(peer) == BGP_PEER_IBGP ? MAXTTL : 1);
else
peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
- /* This is a per-peer specific flag and so we must preserve it */
- v6only = CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY);
-
+ /* These are per-peer specific flags and so we must preserve them */
+ saved_flags |= CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY);
+ saved_flags |= CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN);
peer->flags = 0;
-
- if (v6only)
- SET_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY);
+ SET_FLAG(peer->flags, saved_flags);
peer->config = 0;
peer->holdtime = 0;
}
/* Check peer's AS number and determines if this peer is IBGP or EBGP */
-static bgp_peer_sort_t peer_calc_sort(struct peer *peer)
+static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer)
{
struct bgp *bgp;
* - We RX a BGP_UPDATE where the attributes alone are just
* under BGP_MAX_PACKET_SIZE
* - The user configures an outbound route-map that does many as-path
- * prepends or adds many communities. At most they can have
- * CMD_ARGC_MAX
- * args in a route-map so there is a finite limit on how large they
- * can
- * make the attributes.
+ * prepends or adds many communities. At most they can have
+ * CMD_ARGC_MAX args in a route-map so there is a finite limit on how
+ * large they can make the attributes.
*
* Having a buffer with BGP_MAX_PACKET_SIZE_OVERFLOW allows us to avoid
- * bounds
- * checking for every single attribute as we construct an UPDATE.
+ * bounds checking for every single attribute as we construct an
+ * UPDATE.
*/
peer->obuf_work =
stream_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW);
- peer->ibuf_work = stream_new(BGP_MAX_PACKET_SIZE * BGP_READ_PACKET_MAX);
+ peer->ibuf_work =
+ ringbuf_new(BGP_MAX_PACKET_SIZE * BGP_READ_PACKET_MAX);
+
peer->scratch = stream_new(BGP_MAX_PACKET_SIZE);
bgp_sync_init(peer);
peer_dst->local_as = peer_src->local_as;
peer_dst->ifindex = peer_src->ifindex;
peer_dst->port = peer_src->port;
- peer_sort(peer_dst);
+ (void)peer_sort(peer_dst);
peer_dst->rmap_type = peer_src->rmap_type;
/* Timers */
hash_get(bgp->peerhash, peer, hash_alloc_intern);
/* Adjust update-group coalesce timer heuristics for # peers. */
- long ct = BGP_DEFAULT_SUBGROUP_COALESCE_TIME
- + (bgp->peer->count * BGP_PEER_ADJUST_SUBGROUP_COALESCE_TIME);
- bgp->coalesce_time = MIN(BGP_MAX_SUBGROUP_COALESCE_TIME, ct);
+ if (bgp->heuristic_coalesce) {
+ long ct = BGP_DEFAULT_SUBGROUP_COALESCE_TIME
+ + (bgp->peer->count
+ * BGP_PEER_ADJUST_SUBGROUP_COALESCE_TIME);
+ bgp->coalesce_time = MIN(BGP_MAX_SUBGROUP_COALESCE_TIME, ct);
+ }
active = peer_active(peer);
peer_af_create(peer, afi, safi);
}
+ /* auto shutdown if configured */
+ if (bgp->autoshutdown)
+ peer_flag_set(peer, PEER_FLAG_SHUTDOWN);
/* Set up peer's events and timers. */
- if (!active && peer_active(peer))
+ else if (!active && peer_active(peer))
bgp_timer_set(peer);
return peer;
}
if (peer->ibuf_work) {
- stream_free(peer->ibuf_work);
+ ringbuf_del(peer->ibuf_work);
peer->ibuf_work = NULL;
}
struct peer *peer)
{
struct peer *conf;
- int v6only;
+ int saved_flags = 0;
conf = group->conf;
/* GTSM hops */
peer->gtsm_hops = conf->gtsm_hops;
- /* this flag is per-neighbor and so has to be preserved */
- v6only = CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY);
-
- /* peer flags apply */
+ /* These are per-peer specific flags and so we must preserve them */
+ saved_flags |= CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY);
+ saved_flags |= CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN);
peer->flags = conf->flags;
-
- if (v6only)
- SET_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY);
+ SET_FLAG(peer->flags, saved_flags);
/* peer config apply */
peer->config = conf->config;
bgp->name);
}
+ /* unmap from RT list */
+ bgp_evpn_vrf_delete(bgp);
+
/* Stop timers. */
if (bgp->t_rmap_def_originate_eval) {
BGP_TIMER_OFF(bgp->t_rmap_def_originate_eval);
/* BGP configuration. */
for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+
+ /* skip all auto created vrf as they dont have user config */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO))
+ continue;
+
/* Router bgp ASN */
vty_out(vty, "router bgp %u", bgp->as);
vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n",
bgp->default_subgroup_pkt_queue_max);
+ /* BGP default autoshutdown neighbors */
+ if (bgp->autoshutdown)
+ vty_out(vty, " bgp default auto-shutdown\n");
+
/* BGP client-to-client reflection. */
if (bgp_flag_check(bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT))
vty_out(vty, " no bgp client-to-client reflection\n");
if (bgp_option_check(BGP_OPT_CONFIG_CISCO))
vty_out(vty, " no auto-summary\n");
+ /* import route-target */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode,
+ ecom)) {
+ ecom_str = ecommunity_ecom2str(
+ ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " route-target import %s\n",
+ ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+
+ /* export route-target */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode,
+ ecom)) {
+ ecom_str = ecommunity_ecom2str(
+ ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " route-target export %s\n",
+ ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+
/* IPv4 unicast configuration. */
bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);
bgp_process_queue_init();
+ /* init the rd id space.
+ assign 0th index in the bitfield,
+ so that we start with id 1
+ */
+ bf_init(bm->rd_idspace, UINT16_MAX);
+ bf_assign_zero_index(bm->rd_idspace);
+
/* Enable multiple instances by default. */
bgp_option_set(BGP_OPT_MULTIPLE_INSTANCE);
{
frr_pthread_init();
- frr_pthread_new("BGP i/o thread", PTHREAD_IO, bgp_io_start,
- bgp_io_stop);
- frr_pthread_new("BGP keepalives thread", PTHREAD_KEEPALIVES,
- bgp_keepalives_start, bgp_keepalives_stop);
-
- /* pre-run initialization */
- bgp_keepalives_init();
- bgp_io_init();
+ struct frr_pthread_attr io = {
+ .id = PTHREAD_IO,
+ .start = frr_pthread_attr_default.start,
+ .stop = frr_pthread_attr_default.stop,
+ .name = "BGP I/O thread",
+ };
+ struct frr_pthread_attr ka = {
+ .id = PTHREAD_KEEPALIVES,
+ .start = bgp_keepalives_start,
+ .stop = bgp_keepalives_stop,
+ .name = "BGP Keepalives thread",
+ };
+ frr_pthread_new(&io);
+ frr_pthread_new(&ka);
}
void bgp_pthreads_run()
{
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
+ struct frr_pthread *io = frr_pthread_get(PTHREAD_IO);
+ struct frr_pthread *ka = frr_pthread_get(PTHREAD_KEEPALIVES);
+
+ frr_pthread_run(io, NULL);
+ frr_pthread_run(ka, NULL);
- frr_pthread_run(PTHREAD_IO, &attr, NULL);
- frr_pthread_run(PTHREAD_KEEPALIVES, &attr, NULL);
+ /* Wait until threads are ready. */
+ frr_pthread_wait_running(io);
+ frr_pthread_wait_running(ka);
}
void bgp_pthreads_finish()
*/
/* reverse bgp_master_init */
bgp_close();
+
if (bm->listen_sockets)
list_delete_and_null(&bm->listen_sockets);
#include "defaults.h"
#include "bgp_memory.h"
#include "bitfield.h"
+#include "vxlan.h"
#define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */
#define BGP_PEER_MAX_HASH_SIZE 16384
/* clang-format off */
#define RMAP_DEFAULT_UPDATE_TIMER 5 /* disabled by default */
+ /* Id space for automatic RD derivation for an EVI/VRF */
+ bitfield_t rd_idspace;
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(bgp_master)
_Atomic uint32_t wpkt_quanta; // max # packets to write per i/o cycle
_Atomic uint32_t rpkt_quanta; // max # packets to read per i/o cycle
- u_int32_t coalesce_time;
+ /* Automatic coalesce adjust on/off */
+ bool heuristic_coalesce;
+ /* Actual coalesce time */
+ uint32_t coalesce_time;
+
+ /* Auto-shutdown new peers */
+ bool autoshutdown;
u_int32_t addpath_tx_id;
int addpath_tx_used[AFI_MAX][SAFI_MAX];
/* Hash table of Import RTs to EVIs */
struct hash *import_rt_hash;
- /* Id space for automatic RD derivation for an EVI */
- bitfield_t rd_idspace;
+ /* Hash table of VRF import RTs to VRFs */
+ struct hash *vrf_import_rt_hash;
+
+ /* L3-VNI corresponding to this vrf */
+ vni_t l3vni;
+
+ /* router-mac to be used in mac-ip routes for this vrf */
+ struct ethaddr rmac;
+
+ /* originator ip - to be used as NH for type-5 routes */
+ struct in_addr originator_ip;
+
+ /* vrf flags */
+ uint32_t vrf_flags;
+#define BGP_VRF_AUTO (1 << 0)
+#define BGP_VRF_ADVERTISE_IPV4_IN_EVPN (1 << 1)
+#define BGP_VRF_ADVERTISE_IPV6_IN_EVPN (1 << 2)
+#define BGP_VRF_IMPORT_RT_CFGD (1 << 3)
+#define BGP_VRF_EXPORT_RT_CFGD (1 << 4)
+#define BGP_VRF_RD_CFGD (1 << 5)
+
+ /* unique ID for auto derivation of RD for this vrf */
+ uint16_t vrf_rd_id;
+
+ /* RD for this VRF */
+ struct prefix_rd vrf_prd;
+
+ /* import rt list for the vrf instance */
+ struct list *vrf_import_rtl;
+
+ /* export rt list for the vrf instance */
+ struct list *vrf_export_rtl;
+
+ /* list of corresponding l2vnis (struct bgpevpn) */
+ struct list *l2vnis;
QOBJ_FIELDS
};
struct stream_fifo *ibuf; // packets waiting to be processed
struct stream_fifo *obuf; // packets waiting to be written
- struct stream *ibuf_work; // WiP buffer used by bgp_read() only
- struct stream *obuf_work; // WiP buffer used to construct packets
+ struct ringbuf *ibuf_work; // WiP buffer used by bgp_read() only
+ struct stream *obuf_work; // WiP buffer used to construct packets
struct stream *curr; // the current packet being parsed
/* workqueues */
struct work_queue *clear_node_queue;
+#define PEER_TOTAL_RX(peer) \
+ atomic_load_explicit(&peer->open_in, memory_order_relaxed) + \
+ atomic_load_explicit(&peer->update_in, memory_order_relaxed) + \
+ atomic_load_explicit(&peer->notify_in, memory_order_relaxed) + \
+ atomic_load_explicit(&peer->refresh_in, memory_order_relaxed) + \
+ atomic_load_explicit(&peer->keepalive_in, memory_order_relaxed) + \
+ atomic_load_explicit(&peer->dynamic_cap_in, memory_order_relaxed)
+
+#define PEER_TOTAL_TX(peer) \
+ atomic_load_explicit(&peer->open_out, memory_order_relaxed) + \
+ atomic_load_explicit(&peer->update_out, memory_order_relaxed) + \
+ atomic_load_explicit(&peer->notify_out, memory_order_relaxed) + \
+ atomic_load_explicit(&peer->refresh_out, memory_order_relaxed) + \
+ atomic_load_explicit(&peer->keepalive_out, memory_order_relaxed) + \
+ atomic_load_explicit(&peer->dynamic_cap_out, memory_order_relaxed)
+
/* Statistics field */
_Atomic uint32_t open_in; /* Open message input count */
_Atomic uint32_t open_out; /* Open message output count */
/* Send prefix count. */
unsigned long scount[AFI_MAX][SAFI_MAX];
- /* Announcement attribute hash. */
- struct hash *hash[AFI_MAX][SAFI_MAX];
-
/* Notify data. */
struct bgp_notify notify;
#define BGP_ATTR_AS4_PATH 17
#define BGP_ATTR_AS4_AGGREGATOR 18
#define BGP_ATTR_AS_PATHLIMIT 21
+#define BGP_ATTR_PMSI_TUNNEL 22
#define BGP_ATTR_ENCAP 23
#define BGP_ATTR_LARGE_COMMUNITIES 32
#define BGP_ATTR_PREFIX_SID 40
#if BGP_VNC_DEBUG_MATCH_GROUP
{
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- prefix2str(vn, buf, BUFSIZ);
+ prefix2str(vn, buf, sizeof(buf));
vnc_zlog_debug_verbose("%s: vn prefix: %s", __func__, buf);
- prefix2str(un, buf, BUFSIZ);
+ prefix2str(un, buf, sizeof(buf));
vnc_zlog_debug_verbose("%s: un prefix: %s", __func__, buf);
vnc_zlog_debug_verbose(
return CMD_WARNING_CONFIG_FAILED;
}
- argv_find_and_parse_afi(argv, argc, &idx, &afi);
+ if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ vty_out(vty, "%% Malformed Address Family\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
if (argv[idx-1]->text[0] == 'z')
is_bgp = 0;
idx += 2; /* skip afi and keyword */
return CMD_WARNING_CONFIG_FAILED;
}
- argv_find_and_parse_afi(argv, argc, &idx, &afi);
+ if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ vty_out(vty, "%% Malformed Address Family\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
if (argv[idx-1]->text[0] == 'z')
is_bgp = 0;
idx = argc - 1;
}
if (rfg->rd.prefixlen) {
- char buf[BUFSIZ];
- buf[0] = buf[BUFSIZ - 1] = 0;
+ char buf[RD_ADDRSTRLEN];
if (AF_UNIX == rfg->rd.family) {
vty_out(vty, " rd auto:nh:%d\n",
value);
- } else {
-
- if (!prefix_rd2str(&rfg->rd, buf,
- BUFSIZ)
- || !buf[0] || buf[BUFSIZ - 1]) {
-
- vty_out(vty,
- "!Error: Can't convert rd\n");
- } else {
- vty_out(vty, " rd %s\n", buf);
- }
- }
+ } else
+ vty_out(vty, " rd %s\n",
+ prefix_rd2str(&rfg->rd, buf,
+ sizeof(buf)));
}
if (rfg->rt_import_list && rfg->rt_export_list
}
}
- if (hc->default_rd.prefixlen || hc->default_response_lifetime
+ if (hc->default_rd.prefixlen
+ || hc->default_response_lifetime != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT
|| hc->default_rt_import_list || hc->default_rt_export_list
|| hc->nve_groups_sequential->count) {
vty_out(vty, " vnc defaults\n");
if (hc->default_rd.prefixlen) {
- char buf[BUFSIZ];
- buf[0] = buf[BUFSIZ - 1] = 0;
+ char buf[RD_ADDRSTRLEN];
if (AF_UNIX == hc->default_rd.family) {
uint16_t value = 0;
vty_out(vty, " rd auto:vn:%d\n",
value);
- } else {
-
- if (!prefix_rd2str(&hc->default_rd, buf,
- BUFSIZ)
- || !buf[0] || buf[BUFSIZ - 1]) {
-
- vty_out(vty,
- "!Error: Can't convert rd\n");
- } else {
- vty_out(vty, " rd %s\n", buf);
- }
- }
+ } else
+ vty_out(vty, " rd %s\n",
+ prefix_rd2str(&hc->default_rd,
+ buf,
+ sizeof(buf)));
}
- if (hc->default_response_lifetime) {
+ if (hc->default_response_lifetime
+ != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT) {
vty_out(vty, " response-lifetime ");
if (hc->default_response_lifetime != UINT32_MAX)
vty_out(vty, "%d",
vty_out(vty, " vnc nve-group %s\n", rfg->name);
if (rfg->vn_prefix.family && rfg->vn_node) {
- char buf[BUFSIZ];
- buf[0] = buf[BUFSIZ - 1] = 0;
+ char buf[PREFIX_STRLEN];
prefix2str(&rfg->vn_prefix, buf,
- BUFSIZ);
- if (!buf[0] || buf[BUFSIZ - 1]) {
- vty_out(vty,
- "!Error: Can't convert prefix\n");
- } else {
- vty_out(vty, " prefix %s %s\n",
- "vn", buf);
- }
+ sizeof(buf));
+ vty_out(vty, " prefix %s %s\n",
+ "vn", buf);
}
if (rfg->un_prefix.family && rfg->un_node) {
- char buf[BUFSIZ];
- buf[0] = buf[BUFSIZ - 1] = 0;
+ char buf[PREFIX_STRLEN];
+
prefix2str(&rfg->un_prefix, buf,
- BUFSIZ);
- if (!buf[0] || buf[BUFSIZ - 1]) {
- vty_out(vty,
- "!Error: Can't convert prefix\n");
- } else {
- vty_out(vty, " prefix %s %s\n",
- "un", buf);
- }
+ sizeof(buf));
+ vty_out(vty, " prefix %s %s\n",
+ "un", buf);
}
if (rfg->rd.prefixlen) {
- char buf[BUFSIZ];
- buf[0] = buf[BUFSIZ - 1] = 0;
+ char buf[RD_ADDRSTRLEN];
if (AF_UNIX == rfg->rd.family) {
" rd auto:vn:%d\n",
value);
- } else {
-
- if (!prefix_rd2str(&rfg->rd,
- buf, BUFSIZ)
- || !buf[0]
- || buf[BUFSIZ - 1]) {
-
- vty_out(vty,
- "!Error: Can't convert rd\n");
- } else {
- vty_out(vty,
- " rd %s\n",
- buf);
- }
- }
+ } else
+ vty_out(vty,
+ " rd %s\n",
+ prefix_rd2str(&rfg->rd,
+ buf,
+ sizeof(buf)));
}
if (rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME) {
vty_out(vty, " response-lifetime ");
#include "lib/linklist.h"
#include "lib/command.h"
#include "lib/stream.h"
+#include "lib/ringbuf.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_ecommunity.h"
afi_t afi; /* of the VN address */
struct bgp_node *bn;
struct bgp_info *bi;
- char buf[BUFSIZ];
- char buf2[BUFSIZ];
+ char buf[PREFIX_STRLEN];
+ char buf2[RD_ADDRSTRLEN];
struct prefix_rd prd0;
- prefix2str(p, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */
-
- prefix_rd2str(prd, buf2, BUFSIZ);
- buf2[BUFSIZ - 1] = 0;
+ prefix2str(p, buf, sizeof(buf));
afi = family2afi(p->family);
assert(afi == AFI_IP || afi == AFI_IP6);
vnc_zlog_debug_verbose(
"%s: peer=%p, prefix=%s, prd=%s afi=%d, safi=%d bn=%p, bn->info=%p",
- __func__, peer, buf, buf2, afi, safi, bn,
+ __func__, peer, buf,
+ prefix_rd2str(prd, buf2, sizeof(buf2)), afi, safi, bn,
(bn ? bn->info : NULL));
for (bi = (bn ? bn->info : NULL); bi; bi = bi->next) {
rfapiProcessWithdraw(peer, rfd, p, prd, NULL, afi, safi, type, kill);
if (bi) {
- char buf[BUFSIZ];
-
- prefix2str(p, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */
+ char buf[PREFIX_STRLEN];
+ prefix2str(p, buf, sizeof(buf));
vnc_zlog_debug_verbose(
"%s: Found route (safi=%d) to delete at prefix %s",
__func__, safi, buf);
uint32_t label_val;
struct bgp_attr_encap_subtlv *encaptlv;
- char buf[BUFSIZ];
- char buf2[BUFSIZ];
+ char buf[PREFIX_STRLEN];
+ char buf2[RD_ADDRSTRLEN];
#if 0 /* unused? */
struct prefix pfx_buf;
#endif
else
label_val = MPLS_LABEL_IMPLICIT_NULL;
- prefix_rd2str(prd, buf2, BUFSIZ);
- buf2[BUFSIZ - 1] = 0;
-
+ prefix_rd2str(prd, buf2, sizeof(buf2));
afi = family2afi(p->family);
assert(afi == AFI_IP || afi == AFI_IP6);
}
- prefix2str(p, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */
+ prefix2str(p, buf, sizeof(buf));
/*
* At this point:
* aspath: points to interned hash from aspath hash table
*/
- red = bgp_redist_lookup(bgp, afi, type, VRF_DEFAULT);
+ red = bgp_redist_lookup(bgp, afi, type, 0);
if (red && red->redist_metric_flag) {
attr.med = red->redist_metric;
stream_fifo_free(rfd->peer->obuf);
if (rfd->peer->ibuf_work)
- stream_free(rfd->peer->ibuf_work);
+ ringbuf_del(rfd->peer->ibuf_work);
if (rfd->peer->obuf_work)
stream_free(rfd->peer->obuf_work);
}
{
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
char *s;
- prefix2str(&p, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */
+ prefix2str(&p, buf, sizeof(buf));
vnc_zlog_debug_verbose("%s(rfd=%p, target=%s, ppNextHop=%p)",
__func__, rfd, buf, ppNextHopEntry);
{
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- prefix2str(&p, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */
+ prefix2str(&p, buf, sizeof(buf));
vnc_zlog_debug_verbose(
"%s(rfd=%p, pfx=%s, lifetime=%d, opts_un=%p, opts_vn=%p, action=%s)",
__func__, rfd, buf, lifetime, options_un, options_vn,
4); /* low order 4 bytes */
}
{
- char buf[BUFSIZ];
- buf[0] = 0;
- prefix_rd2str(rd, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0;
+ char buf[RD_ADDRSTRLEN];
+
vnc_zlog_debug_verbose("%s: auto-RD is set to %s", __func__,
- buf);
+ prefix_rd2str(rd, buf, sizeof(buf)));
}
return 0;
}
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_mplsvpn.h" /* prefix_rd2str() */
#include "bgpd/bgp_vnc_types.h"
+#include "bgpd/bgp_rd.h"
#include "bgpd/rfapi/rfapi.h"
#include "bgpd/rfapi/bgp_rfapi_cfg.h"
#if DEBUG_MONITOR_MOVE_SHORTER
{
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- prefix2str(&original_vpn_node->p, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0;
+ prefix2str(&original_vpn_node->p, buf, sizeof(buf));
vnc_zlog_debug_verbose("%s: called with node pfx=%s", __func__,
buf);
}
#if DEBUG_MONITOR_MOVE_SHORTER
{
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- prefix2str(&par->p, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0;
+ prefix2str(&par->p, buf, sizeof(buf));
vnc_zlog_debug_verbose("%s: moved to node pfx=%s", __func__,
buf);
}
__func__, __LINE__, have_vnc_tunnel_un);
#endif
- if (!have_vnc_tunnel_un && bi && bi->extra) {
+ if (!have_vnc_tunnel_un && bi->extra) {
/*
* use cached UN address from ENCAP route
*/
}
if (!skiplist_search(seen_nexthops, &pfx_vn, NULL)) {
#if DEBUG_RETURNED_NHL
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- prefix2str(&pfx_vn, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */
+ prefix2str(&pfx_vn, buf, sizeof(buf));
vnc_zlog_debug_verbose(
"%s: already put VN/nexthop %s, skip", __func__,
buf);
#if DEBUG_RETURNED_NHL
{
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- prefix2str(&rn->p, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0;
+ prefix2str(&rn->p, buf, sizeof(buf));
vnc_zlog_debug_verbose("%s: called with node pfx=%s", __func__,
buf);
}
assert(bi->extra);
{
- char buf[BUFSIZ];
- prefix_rd2str(&bi->extra->vnc.import.rd, buf, BUFSIZ);
+ char buf[RD_ADDRSTRLEN];
+
vnc_zlog_debug_verbose("%s: bi %p, peer %p, rd %s", __func__,
- bi, bi->peer, buf);
+ bi, bi->peer,
+ prefix_rd2str(&bi->extra->vnc.import.rd,
+ buf, sizeof(buf)));
}
sl = RFAPI_RDINDEX_W_ALLOC(rn);
for (rc = skiplist_next(sl, (void **)&k, (void **)&v, &cursor); !rc;
rc = skiplist_next(sl, (void **)&k, (void **)&v, &cursor)) {
- char buf[BUFSIZ];
- char buf_aux_pfx[BUFSIZ];
+ char buf[RD_ADDRSTRLEN];
+ char buf_aux_pfx[PREFIX_STRLEN];
- prefix_rd2str(&k->extra->vnc.import.rd, buf, BUFSIZ);
- buf_aux_pfx[0] = 0;
+ prefix_rd2str(&k->extra->vnc.import.rd, buf, sizeof(buf));
if (k->extra->vnc.import.aux_prefix.family) {
prefix2str(&k->extra->vnc.import.aux_prefix,
- buf_aux_pfx, BUFSIZ);
- } else {
- strncpy(buf_aux_pfx, "(none)", BUFSIZ);
- buf_aux_pfx[BUFSIZ - 1] = 0;
- }
+ buf_aux_pfx, sizeof(buf_aux_pfx));
+ } else
+ strncpy(buf_aux_pfx, "(none)", PREFIX_STRLEN);
vnc_zlog_debug_verbose("bi %p, peer %p, rd %s, aux_prefix %s",
k, k->peer, buf, buf_aux_pfx);
#if DEBUG_BI_SEARCH
{
- char buf[BUFSIZ];
- char buf_aux_pfx[BUFSIZ];
+ char buf[RD_ADDRSTRLEN];
+ char buf_aux_pfx[PREFIX_STRLEN];
- prefix_rd2str(prd, buf, BUFSIZ);
if (aux_prefix) {
- prefix2str(aux_prefix, buf_aux_pfx, BUFSIZ);
- } else {
- strncpy(buf_aux_pfx, "(nil)", BUFSIZ - 1);
- buf_aux_pfx[BUFSIZ - 1] = 0;
- }
+ prefix2str(aux_prefix, buf_aux_pfx,
+ sizeof(buf_aux_pfx));
+ } else
+ strncpy(buf_aux_pfx, "(nil)", sizeof(buf_aux_pfx));
vnc_zlog_debug_verbose("%s want prd=%s, peer=%p, aux_prefix=%s",
- __func__, buf, peer, buf_aux_pfx);
+ __func__,
+ prefix_rd2str(prd, buf, sizeof(buf)),
+ peer, buf_aux_pfx);
rfapiItBiIndexDump(rn);
}
#endif
bi_result = bi_result->next) {
#if DEBUG_BI_SEARCH
{
- char buf[BUFSIZ];
- prefix_rd2str(&bi_result->extra->vnc.import.rd,
- buf, BUFSIZ);
+ char buf[RD_ADDRSTRLEN];
+
vnc_zlog_debug_verbose(
"%s: bi has prd=%s, peer=%p", __func__,
- buf, bi_result->peer);
+ prefix_rd2str(&bi_result->extra->vnc.import.rd,
+ buf,
+ sizeof(buf)),
+ bi_result->peer);
}
#endif
if (peer == bi_result->peer
int rc;
{
- char buf[BUFSIZ];
- prefix_rd2str(&bi->extra->vnc.import.rd, buf, BUFSIZ);
+ char buf[RD_ADDRSTRLEN];
+
vnc_zlog_debug_verbose("%s: bi %p, peer %p, rd %s", __func__,
- bi, bi->peer, buf);
+ bi, bi->peer,
+ prefix_rd2str(&bi->extra->vnc.import.rd,
+ buf, sizeof(buf)));
}
sl = RFAPI_RDINDEX(rn);
rfapiCopyUnEncap2VPN(ern->info, info_new);
route_unlock_node(ern); /* undo lock in route_note_match */
} else {
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
+
prefix2str(&vn_prefix, buf, sizeof(buf));
- buf[BUFSIZ - 1] = 0;
/* Not a big deal, just means VPN route got here first */
vnc_zlog_debug_verbose("%s: no encap route for vn addr %s",
__func__, buf);
vnc_zlog_debug_verbose(
"%s: rfapiEcommunityGetLNI returned %d, lni=%d, attr=%p",
__func__, rc, lni, attr);
- if (attr && !rc) {
+ if (!rc) {
it = rfapiMacImportTableGet(bgp, lni);
rfapiBgpInfoFilteredImportVPN(
#if DEBUG_L2_EXTRA
{
- char buf_pfx[BUFSIZ];
+ char buf_pfx[PREFIX_STRLEN];
if (p) {
- prefix2str(p, buf_pfx, BUFSIZ);
+ prefix2str(p, buf_pfx, sizeof(buf_pfx));
} else {
buf_pfx[0] = '*';
buf_pfx[1] = 0;
struct bgp_info *next;
if (VNC_DEBUG(IMPORT_DEL_REMOTE)) {
- char p1line[BUFSIZ];
- char p2line[BUFSIZ];
+ char p1line[PREFIX_STRLEN];
+ char p2line[PREFIX_STRLEN];
- prefix2str(p, p1line, BUFSIZ);
- prefix2str(&rn->p, p2line, BUFSIZ);
+ prefix2str(p, p1line, sizeof(p1line));
+ prefix2str(&rn->p, p2line, sizeof(p2line));
vnc_zlog_debug_any("%s: want %s, have %s",
__func__, p1line, p2line);
}
continue;
{
- char buf_pfx[BUFSIZ];
- prefix2str(&rn->p, buf_pfx, BUFSIZ);
+ char buf_pfx[PREFIX_STRLEN];
+
+ prefix2str(&rn->p, buf_pfx, sizeof(buf_pfx));
vnc_zlog_debug_verbose("%s: rn pfx=%s",
__func__, buf_pfx);
}
struct bgp *bgp = bgp_get_default();
afi_t afi = family2afi(rn->p.family);
#if DEBUG_L2_EXTRA
- char buf_prefix[BUFSIZ];
+ char buf_prefix[PREFIX_STRLEN];
#endif
assert(bgp);
nves_seen = skiplist_new(0, NULL, NULL);
#if DEBUG_L2_EXTRA
- prefix2str(&it_node->p, buf_prefix, BUFSIZ);
+ prefix2str(&it_node->p, buf_prefix, sizeof(buf_prefix));
vnc_zlog_debug_verbose("%s: it=%p, it_node=%p, it_node->prefix=%s",
__func__, import_table, it_node, buf_prefix);
#endif
assert(!skiplist_insert(nves_seen,
m->rfd, NULL));
- {
- char buf_attach_pfx[BUFSIZ];
- char buf_target_pfx[BUFSIZ];
-
- prefix2str(&m->node->p,
- buf_attach_pfx,
- BUFSIZ);
- prefix2str(&m->p,
- buf_target_pfx,
- BUFSIZ);
- vnc_zlog_debug_verbose(
- "%s: update rfd %p attached to pfx %s (targ=%s)",
- __func__, m->rfd,
- buf_attach_pfx,
- buf_target_pfx);
- }
+ char buf_attach_pfx[PREFIX_STRLEN];
+ char buf_target_pfx[PREFIX_STRLEN];
+
+ prefix2str(&m->node->p,
+ buf_attach_pfx,
+ sizeof(buf_attach_pfx));
+ prefix2str(&m->p,
+ buf_target_pfx,
+ sizeof(buf_target_pfx));
+ vnc_zlog_debug_verbose(
+ "%s: update rfd %p attached to pfx %s (targ=%s)",
+ __func__, m->rfd,
+ buf_attach_pfx,
+ buf_target_pfx);
/*
* update its RIB
assert(rn);
#if DEBUG_L2_EXTRA
- char buf_prefix[BUFSIZ];
- prefix2str(&rn->p, buf_prefix, BUFSIZ);
+ char buf_prefix[PREFIX_STRLEN];
+
+ prefix2str(&rn->p, buf_prefix, sizeof(buf_prefix));
#endif
/*
}
}
- if (checkstats && bgp && bgp->rfapi) {
+ if (checkstats && bgp->rfapi) {
if (t_pfx_active != bgp->rfapi->rib_prefix_count_total) {
vnc_zlog_debug_verbose(
"%s: actual total pfx count %u != running %u",
{
struct thread *t = ri->timer;
struct rfapi_rib_tcb *tcb = NULL;
- char buf_prefix[BUFSIZ];
+ char buf_prefix[PREFIX_STRLEN];
if (t) {
tcb = t->arg;
UNSET_FLAG(tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED);
}
- prefix2str(&rn->p, buf_prefix, BUFSIZ);
+ prefix2str(&rn->p, buf_prefix, sizeof(buf_prefix));
vnc_zlog_debug_verbose("%s: rfd %p pfx %s life %u", __func__, rfd,
buf_prefix, ri->lifetime);
ri->timer = NULL;
memcpy(&vo->v.l2addr.macaddr, bi->extra->vnc.import.rd.val + 2,
ETH_ALEN);
- if (bi->attr) {
- (void)rfapiEcommunityGetLNI(
- bi->attr->ecommunity,
- &vo->v.l2addr.logical_net_id);
- (void)rfapiEcommunityGetEthernetTag(
- bi->attr->ecommunity, &vo->v.l2addr.tag_id);
- }
+ (void)rfapiEcommunityGetLNI(bi->attr->ecommunity,
+ &vo->v.l2addr.logical_net_id);
+ (void)rfapiEcommunityGetEthernetTag(bi->attr->ecommunity,
+ &vo->v.l2addr.tag_id);
/* local_nve_id comes from RD */
vo->v.l2addr.local_nve_id = bi->extra->vnc.import.rd.val[1];
/*
* If there is an auxiliary IP address (L2 can have it), copy it
*/
- if (bi && bi->extra && bi->extra->vnc.import.aux_prefix.family) {
+ if (bi->extra && bi->extra->vnc.import.aux_prefix.family) {
ri->rk.aux_prefix = bi->extra->vnc.import.aux_prefix;
}
}
struct list *lPendCost = NULL;
struct list *delete_list = NULL;
int printedprefix = 0;
- char buf_prefix[BUFSIZ];
+ char buf_prefix[PREFIX_STRLEN];
int rib_node_started_nonempty = 0;
int sendingsomeroutes = 0;
#endif
assert(pn);
- prefix2str(&pn->p, buf_prefix, BUFSIZ);
+ prefix2str(&pn->p, buf_prefix, sizeof(buf_prefix));
vnc_zlog_debug_verbose("%s: afi=%d, %s pn->info=%p", __func__, afi,
buf_prefix, pn->info);
while (0
== skiplist_first(slRibPt, NULL, (void **)&ri)) {
- char buf[BUFSIZ];
- char buf2[BUFSIZ];
+ char buf[PREFIX_STRLEN];
+ char buf2[PREFIX_STRLEN];
listnode_add(delete_list, ri);
vnc_zlog_debug_verbose(
ri->timer = NULL;
}
- prefix2str(&ri->rk.vn, buf, BUFSIZ);
- prefix2str(&ri->un, buf2, BUFSIZ);
+ prefix2str(&ri->rk.vn, buf, sizeof(buf));
+ prefix2str(&ri->un, buf2, sizeof(buf2));
vnc_zlog_debug_verbose(
"%s: put dl pfx=%s vn=%s un=%s cost=%d life=%d vn_options=%p",
__func__, buf_prefix, buf, buf2,
} else {
- char buf_rd[BUFSIZ];
+ char buf_rd[RD_ADDRSTRLEN];
/* not found: add new route to RIB */
ori = rfapi_info_new();
ri->last_sent_time = rfapi_time(NULL);
#if DEBUG_RIB_SL_RD
{
- char buf_rd[BUFSIZ];
- prefix_rd2str(&ri->rk.rd,
- buf_rd,
- sizeof(buf_rd));
+ char buf_rd[RD_ADDRSTRLEN];
+
vnc_zlog_debug_verbose(
"%s: move route to recently deleted list, rd=%s",
- __func__, buf_rd);
+ __func__,
+ prefix_rd2str(&ri->rk.rd,
+ buf_rd,
+ sizeof(buf_rd)));
}
#endif
afi_t afi;
uint32_t queued_flag;
int count = 0;
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
vnc_zlog_debug_verbose("%s: entry", __func__);
prefix = &it_node->p;
afi = family2afi(prefix->family);
- prefix2str(prefix, buf, BUFSIZ);
+ prefix2str(prefix, buf, sizeof(buf));
vnc_zlog_debug_verbose("%s: prefix=%s", __func__, buf);
pn = route_node_get(rfd->rib_pending[afi], prefix);
#if DEBUG_FTD_FILTER_RECENT
{
- char buf_pfx[BUFSIZ];
+ char buf_pfx[PREFIX_STRLEN];
- prefix2str(&it_rn->p, buf_pfx, BUFSIZ);
+ prefix2str(&it_rn->p, buf_pfx, sizeof(buf_pfx));
vnc_zlog_debug_verbose("%s: prefix %s", __func__, buf_pfx);
}
#endif
#if DEBUG_NHL
{
- char str_vn[BUFSIZ];
- char str_aux_prefix[BUFSIZ];
+ char str_vn[PREFIX_STRLEN];
+ char str_aux_prefix[PREFIX_STRLEN];
str_vn[0] = 0;
str_aux_prefix[0] = 0;
- prefix2str(&rk.vn, str_vn, BUFSIZ);
- prefix2str(&rk.aux_prefix, str_aux_prefix, BUFSIZ);
+ prefix2str(&rk.vn, str_vn, sizeof(str_vn));
+ prefix2str(&rk.aux_prefix, str_aux_prefix,
+ sizeof(str_aux_prefix));
if (!rk.aux_prefix.family) {
}
route_unlock_node(trn);
{
- char str_pfx[BUFSIZ];
- char str_pfx_vn[BUFSIZ];
+ char str_pfx[PREFIX_STRLEN];
+ char str_pfx_vn[PREFIX_STRLEN];
- prefix2str(&pfx, str_pfx, BUFSIZ);
- prefix2str(&rk.vn, str_pfx_vn, BUFSIZ);
+ prefix2str(&pfx, str_pfx, sizeof(str_pfx));
+ prefix2str(&rk.vn, str_pfx_vn, sizeof(str_pfx_vn));
vnc_zlog_debug_verbose(
"%s: added pfx=%s nh[vn]=%s, cost=%u, lifetime=%u, allowed=%d",
__func__, str_pfx, str_pfx_vn, nhp->prefix.cost,
{
struct rfapi_descriptor *rfd;
struct listnode *node;
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- prefix2str(&it_node->p, buf, BUFSIZ);
+ prefix2str(&it_node->p, buf, sizeof(buf));
vnc_zlog_debug_verbose("%s: entry, it=%p, afi=%d, it_node=%p, pfx=%s",
__func__, it, afi, it_node, buf);
for (rc = skiplist_next(sl, NULL, (void **)&ri, &cursor); !rc;
rc = skiplist_next(sl, NULL, (void **)&ri, &cursor)) {
- char str_vn[BUFSIZ];
- char str_un[BUFSIZ];
+ char str_vn[PREFIX_STRLEN];
+ char str_un[PREFIX_STRLEN];
char str_lifetime[BUFSIZ];
char str_age[BUFSIZ];
char *p;
- char str_rd[BUFSIZ];
+ char str_rd[RD_ADDRSTRLEN];
++routes_displayed;
- prefix2str(&ri->rk.vn, str_vn, BUFSIZ);
+ prefix2str(&ri->rk.vn, str_vn, sizeof(str_vn));
p = index(str_vn, '/');
if (p)
*p = 0;
- prefix2str(&ri->un, str_un, BUFSIZ);
+ prefix2str(&ri->un, str_un, sizeof(str_un));
p = index(str_un, '/');
if (p)
*p = 0;
str_rd[0] = 0; /* start empty */
#if DEBUG_RIB_SL_RD
- str_rd[0] = ' ';
- prefix_rd2str(&ri->rk.rd, str_rd + 1, BUFSIZ - 1);
+ prefix_rd2str(&ri->rk.rd, str_rd, sizeof(str_rd));
#endif
- fp(out, " %c %-20s %-15s %-15s %-4u %-8s %-8s%s\n",
+ fp(out, " %c %-20s %-15s %-15s %-4u %-8s %-8s %s\n",
deleted ? 'r' : ' ', *printedprefix ? "" : str_pfx, str_vn,
str_un, ri->cost, str_lifetime, str_age, str_rd);
const char *vty_newline;
int nhs_displayed = 0;
- char str_pfx[BUFSIZ];
+ char str_pfx[PREFIX_STRLEN];
int printedprefix = 0;
if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
return;
- prefix2str(pfx, str_pfx, BUFSIZ);
+ prefix2str(pfx, str_pfx, sizeof(str_pfx));
nhs_displayed +=
print_rib_sl(fp, vty, out, sl, 0, str_pfx, &printedprefix);
rn = route_next(rn)) {
struct skiplist *sl;
- char str_pfx[BUFSIZ];
+ char str_pfx[PREFIX_STRLEN];
int printedprefix = 0;
if (!show_removed)
str_un,
BUFSIZ));
}
- prefix2str(&rn->p, str_pfx, BUFSIZ);
+ prefix2str(&rn->p, str_pfx, sizeof(str_pfx));
// fp(out, " %s\n", buf); /* prefix */
routes_displayed++;
char buf_age[BUFSIZ];
- if (bi && bi->extra && bi->extra->vnc.import.create_time) {
+ if (bi->extra && bi->extra->vnc.import.create_time) {
rfapiFormatAge(bi->extra->vnc.import.create_time,
buf_age, BUFSIZ);
} else {
* print that on the next line
*/
- if (bi && bi->extra
+ if (bi->extra
&& bi->extra->vnc.import.aux_prefix.family) {
const char *sp;
void rfapiPrintRd(struct vty *vty, struct prefix_rd *prd)
{
- char buf[BUFSIZ];
+ char buf[RD_ADDRSTRLEN];
- buf[0] = 0;
- prefix_rd2str(prd, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0;
+ prefix_rd2str(prd, buf, sizeof(buf));
vty_out(vty, "%s", buf);
}
int rc;
afi_t afi;
struct rfapi_adb *adb;
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
vty_out(vty, "%-10p ", rfd);
rfapiPrintRfapiIpAddr(vty, &rfd->un_addr);
if (family != adb->u.s.prefix_ip.family)
continue;
- prefix2str(&adb->u.s.prefix_ip, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */
+ prefix2str(&adb->u.s.prefix_ip, buf, sizeof(buf));
vty_out(vty, " Adv Pfx: %s%s", buf, HVTYNL);
rfapiPrintAdvertisedInfo(vty, rfd, SAFI_MPLS_VPN,
rc == 0; rc = skiplist_next(rfd->advertised.ip0_by_ether, NULL,
(void **)&adb, &cursor)) {
- prefix2str(&adb->u.s.prefix_eth, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */
+ prefix2str(&adb->u.s.prefix_eth, buf, sizeof(buf));
vty_out(vty, " Adv Pfx: %s%s", buf, HVTYNL);
continue;
{
- char prefixstr[BUFSIZ];
-
- prefixstr[0] = 0;
- inet_ntop(rn->p.family, &rn->p.u.prefix, prefixstr,
- BUFSIZ);
- vnc_zlog_debug_verbose("%s: checking prefix %s/%d",
- __func__, prefixstr,
- rn->p.prefixlen);
+ char prefixstr[PREFIX_STRLEN];
+
+ prefix2str(&rn->p, prefixstr, sizeof(prefixstr));
+ vnc_zlog_debug_verbose("%s: checking prefix %s",
+ __func__, prefixstr);
}
for (ri = rn->info; ri; ri = ri->next) {
continue;
{
- char prefixstr[BUFSIZ];
+ char prefixstr[PREFIX_STRLEN];
- prefixstr[0] = 0;
- inet_ntop(rn->p.family, &rn->p.u.prefix,
- prefixstr, BUFSIZ);
+ prefix2str(&rn->p, prefixstr,
+ sizeof(prefixstr));
vnc_zlog_debug_verbose(
- "%s: checking prefix %s/%d", __func__,
- prefixstr, rn->p.prefixlen);
+ "%s: checking prefix %s", __func__,
+ prefixstr);
}
/*
/* XXX uses secret knowledge of skiplist structure */
for (p = sl->header->forward[0]; p; p = p->forward[0]) {
- char kbuf[BUFSIZ];
- char hbuf[BUFSIZ];
- char ubuf[BUFSIZ];
+ char kbuf[PREFIX_STRLEN];
+ char hbuf[PREFIX_STRLEN];
+ char ubuf[PREFIX_STRLEN];
pb = p->value;
- prefix2str(p->key, kbuf, BUFSIZ);
- prefix2str(&pb->hpfx, hbuf, BUFSIZ);
- prefix2str(&pb->upfx, ubuf, BUFSIZ);
+ prefix2str(p->key, kbuf, sizeof(kbuf));
+ prefix2str(&pb->hpfx, hbuf, sizeof(hbuf));
+ prefix2str(&pb->upfx, ubuf, sizeof(ubuf));
vnc_zlog_debug_verbose(
"RHN Entry %d (q=%p): kpfx=%s, upfx=%s, hpfx=%s, ubi=%p",
* pfx */
assert(!vnc_prefix_cmp(&pb->hpfx, pkey));
if (vnc_prefix_cmp(&pb->hpfx, &pfx_orig_nexthop)) {
- char str_onh[BUFSIZ];
- char str_nve_pfx[BUFSIZ];
+ char str_onh[PREFIX_STRLEN];
+ char str_nve_pfx[PREFIX_STRLEN];
- prefix2str(&pfx_orig_nexthop, str_onh, BUFSIZ);
- str_onh[BUFSIZ - 1] = 0;
-
- prefix2str(&pb->hpfx, str_nve_pfx, BUFSIZ);
- str_nve_pfx[BUFSIZ - 1] = 0;
+ prefix2str(&pfx_orig_nexthop, str_onh, sizeof(str_onh));
+ prefix2str(&pb->hpfx, str_nve_pfx, sizeof(str_nve_pfx));
vnc_zlog_debug_verbose(
"%s: %s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s",
return;
{
- char str_nh[BUFSIZ];
+ char str_nh[PREFIX_STRLEN];
- prefix2str(ubi_nexthop, str_nh, BUFSIZ);
- str_nh[BUFSIZ - 1] = 0;
+ prefix2str(ubi_nexthop, str_nh, sizeof(str_nh));
vnc_zlog_debug_verbose("%s: ubi_nexthop=%s", __func__, str_nh);
}
/*debugging */
if (VNC_DEBUG(VERBOSE)) {
- char str_pfx[BUFSIZ];
- char str_nh[BUFSIZ];
+ char str_pfx[PREFIX_STRLEN];
+ char str_nh[PREFIX_STRLEN];
struct prefix nh;
- prefix2str(prefix, str_pfx, BUFSIZ);
- str_pfx[BUFSIZ - 1] = 0;
+ prefix2str(prefix, str_pfx, sizeof(str_pfx));
nh.prefixlen = 0;
rfapiUnicastNexthop2Prefix(afi, info->attr, &nh);
if (nh.prefixlen) {
- prefix2str(&nh, str_nh, BUFSIZ);
- str_nh[BUFSIZ - 1] = 0;
+ prefix2str(&nh, str_nh, sizeof(str_nh));
} else {
str_nh[0] = '?';
str_nh[1] = 0;
uint32_t *med = NULL;
{
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- buf[0] = 0;
- prefix2str(prefix, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0;
+ prefix2str(prefix, buf, sizeof(buf));
vnc_zlog_debug_verbose("%s(prefix=%s) entry", __func__, buf);
}
}
if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) {
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- buf[0] = 0;
- prefix2str(vn_pfx, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0;
+ prefix2str(vn_pfx, buf, sizeof(buf));
vnc_zlog_debug_any("%s vn_pfx=%s", __func__, buf);
}
}
if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) {
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- buf[0] = 0;
- rfapiRfapiIpAddr2Str(&vnaddr, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0;
+ rfapiRfapiIpAddr2Str(&vnaddr, buf, sizeof(buf));
vnc_zlog_debug_any("%s: setting vnaddr to %s", __func__, buf);
}
uint32_t local_pref;
{
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- buf[0] = 0;
- prefix2str(prefix, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0;
+ prefix2str(prefix, buf, sizeof(buf));
vnc_zlog_debug_verbose("%s(prefix=%s) entry", __func__, buf);
}
}
if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) {
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
- buf[0] = 0;
- prefix2str(vn_pfx, buf, BUFSIZ);
- buf[BUFSIZ - 1] = 0;
+ prefix2str(vn_pfx, buf, sizeof(buf));
vnc_zlog_debug_any("%s vn_pfx=%s", __func__, buf);
}
assert(afi);
- assert((rfg = bgp->rfapi_cfg->rfg_redist));
+ rfg = bgp->rfapi_cfg->rfg_redist;
+ assert(rfg);
/*
* Compute VN address
return;
{
- char str_nh[BUFSIZ];
-
- prefix2str(ubi_nexthop, str_nh, BUFSIZ);
- str_nh[BUFSIZ - 1] = 0;
+ char str_nh[PREFIX_STRLEN];
+ prefix2str(ubi_nexthop, str_nh, sizeof(str_nh));
vnc_zlog_debug_verbose("%s: ubi_nexthop=%s", __func__, str_nh);
}
return;
}
- if (bgp && bgp->rfapi)
+ if (bgp->rfapi)
sl = bgp->rfapi->resolve_nve_nexthop;
if (!sl) {
sizeof(struct prefix)); /* keep valgrind happy */
if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) {
- char hbuf[BUFSIZ];
- char ubuf[BUFSIZ];
+ char hbuf[PREFIX_STRLEN];
+ char ubuf[PREFIX_STRLEN];
- prefix2str(&pb->hpfx, hbuf, BUFSIZ);
- prefix2str(&pb->upfx, ubuf, BUFSIZ);
+ prefix2str(&pb->hpfx, hbuf, sizeof(hbuf));
+ prefix2str(&pb->upfx, ubuf, sizeof(ubuf));
vnc_zlog_debug_any(
"%s: examining RHN Entry (q=%p): upfx=%s, hpfx=%s, ubi=%p",
* Sanity check
*/
if (vnc_prefix_cmp(&pfx_unicast_nexthop, prefix)) {
- char str_unh[BUFSIZ];
- char str_nve_pfx[BUFSIZ];
+ char str_unh[PREFIX_STRLEN];
+ char str_nve_pfx[PREFIX_STRLEN];
- prefix2str(&pfx_unicast_nexthop, str_unh, BUFSIZ);
- str_unh[BUFSIZ - 1] = 0;
-
- prefix2str(prefix, str_nve_pfx, BUFSIZ);
- str_nve_pfx[BUFSIZ - 1] = 0;
+ prefix2str(&pfx_unicast_nexthop, str_unh,
+ sizeof(str_unh));
+ prefix2str(prefix, str_nve_pfx, sizeof(str_nve_pfx));
vnc_zlog_debug_verbose(
"%s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s",
#if DEBUG_RHN_LIST
/* debug */
{
- char pbuf[BUFSIZ];
+ char pbuf[PREFIX_STRLEN];
- prefix2str(prefix, pbuf, BUFSIZ);
+ prefix2str(prefix, pbuf, sizeof(pbuf));
vnc_zlog_debug_verbose(
"%s: advancing past RHN Entry (q=%p): with prefix %s",
int rc;
{
- char str_pfx[BUFSIZ];
+ char str_pfx[PREFIX_STRLEN];
- prefix2str(prefix, str_pfx, BUFSIZ);
- str_pfx[BUFSIZ - 1] = 0;
+ prefix2str(prefix, str_pfx, sizeof(str_pfx));
vnc_zlog_debug_verbose("%s(bgp=%p, nve prefix=%s)", __func__,
bgp, str_pfx);
return;
}
- if (bgp && bgp->rfapi)
+ if (bgp->rfapi)
sl = bgp->rfapi->resolve_nve_nexthop;
if (!sl) {
* Sanity check
*/
if (vnc_prefix_cmp(&pfx_unicast_nexthop, prefix)) {
- char str_unh[BUFSIZ];
- char str_nve_pfx[BUFSIZ];
-
- prefix2str(&pfx_unicast_nexthop, str_unh, BUFSIZ);
- str_unh[BUFSIZ - 1] = 0;
+ char str_unh[PREFIX_STRLEN];
+ char str_nve_pfx[PREFIX_STRLEN];
- prefix2str(prefix, str_nve_pfx, BUFSIZ);
- str_nve_pfx[BUFSIZ - 1] = 0;
+ prefix2str(&pfx_unicast_nexthop, str_unh,
+ sizeof(str_unh));
+ prefix2str(prefix, str_nve_pfx, sizeof(str_nve_pfx));
vnc_zlog_debug_verbose(
"%s: FATAL: resolve_nve_nexthop list item bi nexthop %s != nve pfx %s",
/*debugging */
{
- char str_pfx[BUFSIZ];
-
- prefix2str(&rn_interior->p, str_pfx, BUFSIZ);
- str_pfx[BUFSIZ - 1] = 0;
+ char str_pfx[PREFIX_STRLEN];
+ prefix2str(&rn_interior->p, str_pfx, sizeof(str_pfx));
vnc_zlog_debug_verbose("%s: interior prefix=%s, bi type=%d",
__func__, str_pfx, bi_interior->type);
}
(void **)&pfx_exterior, &cursor)) {
struct prefix pfx_nexthop;
- char buf[BUFSIZ];
+ char buf[PREFIX_STRLEN];
afi_t afi_exterior = family2afi(pfx_exterior->family);
prefix2str(pfx_exterior, buf, sizeof(buf));
- buf[sizeof(buf) - 1] = 0;
vnc_zlog_debug_verbose(
"%s: checking exterior orphan at prefix %s", __func__,
buf);
/*debugging */
{
- char str_pfx[BUFSIZ];
+ char str_pfx[PREFIX_STRLEN];
- prefix2str(&rn_interior->p, str_pfx, BUFSIZ);
- str_pfx[BUFSIZ - 1] = 0;
+ prefix2str(&rn_interior->p, str_pfx, sizeof(str_pfx));
vnc_zlog_debug_verbose("%s: interior prefix=%s, bi type=%d",
__func__, str_pfx, bi_interior->type);
if (VNC_DEBUG(VERBOSE)) {
struct prefix pfx_nexthop;
- char buf[BUFSIZ];
- char buf_nh[BUFSIZ];
+ char buf[PREFIX_STRLEN];
+ char buf_nh[PREFIX_STRLEN];
- prefix2str(prefix, buf, BUFSIZ);
+ prefix2str(prefix, buf, sizeof(buf));
rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_nexthop);
- prefix2str(&pfx_nexthop, buf_nh, BUFSIZ);
+ prefix2str(&pfx_nexthop, buf_nh, sizeof(buf_nh));
vnc_zlog_debug_verbose("%s: pfx %s, nh %s", __func__, buf,
buf_nh);
{
struct prefix pfx_nexthop;
- char buf[BUFSIZ];
- char buf_nh[BUFSIZ];
+ char buf[PREFIX_STRLEN];
+ char buf_nh[PREFIX_STRLEN];
- prefix2str(prefix, buf, BUFSIZ);
+ prefix2str(prefix, buf, sizeof(buf));
rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_nexthop);
- prefix2str(&pfx_nexthop, buf_nh, BUFSIZ);
+ prefix2str(&pfx_nexthop, buf_nh, sizeof(buf_nh));
vnc_zlog_debug_verbose("%s: pfx %s, nh %s", __func__, buf,
buf_nh);
#include "lib/command.h"
#include "lib/zclient.h"
#include "lib/stream.h"
+#include "lib/ringbuf.h"
#include "lib/memory.h"
#include "bgpd/bgpd.h"
stream_fifo_free(vncHD1VR.peer->obuf);
if (vncHD1VR.peer->ibuf_work)
- stream_free(vncHD1VR.peer->ibuf_work);
+ ringbuf_del(vncHD1VR.peer->ibuf_work);
if (vncHD1VR.peer->obuf_work)
stream_free(vncHD1VR.peer->obuf_work);
struct zapi_route api;
struct zapi_nexthop *api_nh;
int i;
+ struct in_addr **nhp_ary4 = nhp_ary;
+ struct in6_addr **nhp_ary6 = nhp_ary;
if (!nhp_count) {
vnc_zlog_debug_verbose("%s: empty nexthop list, skipping",
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_VNC;
api.safi = SAFI_UNICAST;
api.prefix = *p;
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api.nexthop_num = MIN(nhp_count, multipath_num);
for (i = 0; i < api.nexthop_num; i++) {
- struct in_addr *nhp_ary4;
- struct in6_addr *nhp_ary6;
api_nh = &api.nexthops[i];
switch (p->family) {
case AF_INET:
- nhp_ary4 = nhp_ary;
- memcpy(&api_nh->gate.ipv4, &nhp_ary4[i],
+ memcpy(&api_nh->gate.ipv4, nhp_ary4[i],
sizeof(api_nh->gate.ipv4));
api_nh->type = NEXTHOP_TYPE_IPV4;
break;
case AF_INET6:
- nhp_ary6 = nhp_ary;
- memcpy(&api_nh->gate.ipv6, &nhp_ary6[i],
+ memcpy(&api_nh->gate.ipv6, nhp_ary6[i],
sizeof(api_nh->gate.ipv6));
api_nh->type = NEXTHOP_TYPE_IPV6;
break;
( CPPFLAGS="$HOST_CPPFLAGS"; \
CFLAGS="$HOST_CFLAGS"; \
LDFLAGS="$HOST_LDFLAGS"; \
- cd hosttools; "${abssrc}/configure" "--host=$build" "--build=$build"; )
+ cd hosttools; "${abssrc}/configure" "--host=$build" "--build=$build" "--enable-clippy-only" "--disable-nhrpd" "--disable-vtysh"; )
AC_MSG_NOTICE([...])
AC_MSG_NOTICE([... cross-compilation: finished self-configuring for build platform tools])
AS_HELP_STRING([--enable-logfile-mask=ARG], [set mask for log files]))
AC_ARG_ENABLE(shell_access,
AS_HELP_STRING([--enable-shell-access], [Allow users to access shell/telnet/ssh]))
+AC_ARG_ENABLE(realms,
+ AS_HELP_STRING([--enable-realms], [enable REALMS support under Linux]))
AC_ARG_ENABLE(rtadv,
AS_HELP_STRING([--disable-rtadv], [disable IPV6 router advertisement feature]))
AC_ARG_ENABLE(irdp,
AS_HELP_STRING([--enable-oldvpn-commands], [Keep old vpn commands]))
AC_ARG_ENABLE(rpki,
AS_HELP_STRING([--enable-rpki], [enable RPKI prefix validation support]))
+AC_ARG_ENABLE([clippy-only],
+ AS_HELP_STRING([--enable-clippy-only], [Only build clippy]))
+AS_IF([test "${enable_clippy_only}" != "yes"], [
AC_CHECK_HEADERS(json-c/json.h)
AC_CHECK_LIB(json-c, json_object_get, LIBS="$LIBS -ljson-c", [], [-lm])
if test $ac_cv_lib_json_c_json_object_get = no; then
AC_MSG_ERROR([lib json is needed to compile])
fi
fi
+])
AC_ARG_ENABLE([dev_build],
AS_HELP_STRING([--enable-dev-build], [build for development]))
AC_DEFINE(OPEN_BSD,,OpenBSD)
AC_DEFINE(KAME,1,KAME IPv6)
+ AC_DEFINE(BSD_V6_SYSCTL,1,BSD v6 sysctl to turn on and off forwarding)
if test "x${enable_pimd}" != "xno"; then
case "$host_os" in
AC_DEFINE(HAVE_NET_RT_IFLIST,,NET_RT_IFLIST)
AC_DEFINE(KAME,1,KAME IPv6)
+ AC_DEFINE(BSD_V6_SYSCTL,1,BSD v6 sysctl to turn on and off forwarding)
;;
esac
AM_CONDITIONAL(SOLARIS, test "${SOLARIS}" = "solaris")
AC_SYS_LARGEFILE
+dnl ------------------------
+dnl Integrated REALMS option
+dnl ------------------------
+if test "${enable_realms}" = "yes"; then
+ case "$host_os" in
+ linux*)
+ AC_DEFINE(SUPPORT_REALMS,, Realms support)
+ ;;
+ *)
+ echo "Sorry, only Linux has REALMS support"
+ exit 1
+ ;;
+ esac
+fi
+AM_CONDITIONAL([SUPPORT_REALMS], [test "${enable_realms}" = "yes"])
+
dnl ---------------------
dnl Integrated VTY option
dnl ---------------------
backports/ubuntu17.10/versionext \
frr-doc.docs frr-doc.info frr-doc.install \
frr-doc.lintian-overrides frr.conf \
+ frr-dbg.lintian-overrides \
frr.dirs frr.docs frr.install \
frr.lintian-overrides frr.logrotate \
frr.manpages frr.pam frr.postinst frr.postrm \
(see `rules` file for available options)
export WANT_BGP_VNC=1
- export WANT_WANT_CUMULUS_MODE=1
+ export WANT_CUMULUS_MODE=1
debuild -b -uc -us
DONE.
frr.dvi: $(frr_TEXINFOS) defines.texi
frr.html: $(frr_TEXINFOS) defines.texi
-frr_TEXINFOS = appendix.texi basic.texi bgpd.texi isisd.texi filter.texi \
+frr_TEXINFOS = \
+ appendix.texi \
+ basic.texi \
+ bgpd.texi \
+ isisd.texi \
+ filter.texi \
vnc.texi \
babeld.texi \
- install.texi ipv6.texi kernel.texi main.texi \
+ install.texi \
+ ipv6.texi \
+ kernel.texi \
+ main.texi \
nhrpd.texi \
eigrpd.texi \
- ospf6d.texi ospfd.texi \
- overview.texi protocol.texi ripd.texi ripngd.texi routemap.texi \
- snmp.texi vtysh.texi routeserver.texi $(figures_png) \
- snmptrap.texi ospf_fundamentals.texi isisd.texi $(figures_txt) \
- rpki.texi
+ ospf6d.texi \
+ ospfd.texi \
+ overview.texi \
+ protocol.texi \
+ ripd.texi \
+ ripngd.texi \
+ routemap.texi \
+ snmp.texi \
+ vtysh.texi \
+ routeserver.texi \
+ $(figures_png) \
+ snmptrap.texi \
+ ospf_fundamentals.texi \
+ isisd.texi $(figures_txt) \
+ rpki.texi \
+ pimd.texi \
+ #END
.png.eps:
$(PNGTOEPS) $< "$@"
--- /dev/null
+@c -*-texinfo-*-
+@c This is part of the Frr Manual.
+@c @value{COPYRIGHT_STR}
+@c Portions:
+@c Copyright @copyright{} 2015 Hewlett Packard Enterprise Development LP
+@c See file frr.texi for copying conditions.
+@node BGP
+@chapter BGP
+
+@acronym{BGP} stands for a Border Gateway Protocol. The lastest BGP version
+is 4. It is referred as BGP-4. BGP-4 is one of the Exterior Gateway
+Protocols and de-fact standard of Inter Domain routing protocol.
+BGP-4 is described in @cite{RFC1771, A Border Gateway Protocol
+4 (BGP-4)}.
+
+Many extensions have been added to @cite{RFC1771}. @cite{RFC2858,
+Multiprotocol Extensions for BGP-4} provides multiprotocol support to
+BGP-4.
+
+@menu
+* Starting BGP::
+* BGP router::
+* BGP MED::
+* BGP network::
+* BGP Peer::
+* BGP Peer Group::
+* BGP Address Family::
+* Autonomous System::
+* BGP Communities Attribute::
+* BGP Extended Communities Attribute::
+* BGP Large Communities Attribute::
+* Displaying BGP information::
+* Capability Negotiation::
+* Route Reflector::
+* Route Server::
+* BGP Regular Expressions::
+* How to set up a 6-Bone connection::
+* Dump BGP packets and table::
+* BGP Configuration Examples::
+* Prefix Origin Validation Using RPKI::
+@end menu
+
+@node Starting BGP
+@section Starting BGP
+
+Default configuration file of @command{bgpd} is @file{bgpd.conf}.
+@command{bgpd} searches the current directory first then
+@value{INSTALL_PREFIX_ETC}/bgpd.conf. All of bgpd's command must be
+configured in @file{bgpd.conf}.
+
+@command{bgpd} specific invocation options are described below. Common
+options may also be specified (@pxref{Common Invocation Options}).
+
+@table @samp
+@item -p @var{PORT}
+@itemx --bgp_port=@var{PORT}
+Set the bgp protocol's port number.
+
+@item -r
+@itemx --retain
+When program terminates, retain BGP routes added by zebra.
+
+@item -l
+@itemx --listenon
+Specify a specific IP address for bgpd to listen on, rather than its
+default of INADDR_ANY / IN6ADDR_ANY. This can be useful to constrain bgpd
+to an internal address, or to run multiple bgpd processes on one host.
+
+@end table
+
+@node BGP router
+@section BGP router
+
+ First of all you must configure BGP router with @command{router bgp}
+command. To configure BGP router, you need AS number. AS number is an
+identification of autonomous system. BGP protocol uses the AS number
+for detecting whether the BGP connection is internal one or external one.
+
+@deffn Command {router bgp @var{asn}} {}
+Enable a BGP protocol process with the specified @var{asn}. After
+this statement you can input any @code{BGP Commands}. You can not
+create different BGP process under different @var{asn} without
+specifying @code{multiple-instance} (@pxref{Multiple instance}).
+@end deffn
+
+@deffn Command {no router bgp @var{asn}} {}
+Destroy a BGP protocol process with the specified @var{asn}.
+@end deffn
+
+@deffn {BGP} {bgp router-id @var{A.B.C.D}} {}
+This command specifies the router-ID. If @command{bgpd} connects to @command{zebra} it gets
+interface and address information. In that case default router ID value
+is selected as the largest IP Address of the interfaces. When
+@code{router zebra} is not enabled @command{bgpd} can't get interface information
+so @code{router-id} is set to 0.0.0.0. So please set router-id by hand.
+@end deffn
+
+@menu
+* BGP distance::
+* BGP decision process::
+* BGP route flap dampening::
+@end menu
+
+@node BGP distance
+@subsection BGP distance
+
+@deffn {BGP} {distance bgp <1-255> <1-255> <1-255>} {}
+This command change distance value of BGP. Each argument is distance
+value for external routes, internal routes and local routes.
+@end deffn
+
+@deffn {BGP} {distance <1-255> @var{A.B.C.D/M}} {}
+@deffnx {BGP} {distance <1-255> @var{A.B.C.D/M} @var{word}} {}
+This command set distance value to
+@end deffn
+
+@node BGP decision process
+@subsection BGP decision process
+
+The decision process Frr BGP uses to select routes is as follows:
+
+@table @asis
+@item 1. Weight check
+prefer higher local weight routes to lower routes.
+
+@item 2. Local preference check
+prefer higher local preference routes to lower.
+
+@item 3. Local route check
+Prefer local routes (statics, aggregates, redistributed) to received routes.
+
+@item 4. AS path length check
+Prefer shortest hop-count AS_PATHs.
+
+@item 5. Origin check
+Prefer the lowest origin type route. That is, prefer IGP origin routes to
+EGP, to Incomplete routes.
+
+@item 6. MED check
+Where routes with a MED were received from the same AS,
+prefer the route with the lowest MED. @xref{BGP MED}.
+
+@item 7. External check
+Prefer the route received from an external, eBGP peer
+over routes received from other types of peers.
+
+@item 8. IGP cost check
+Prefer the route with the lower IGP cost.
+
+@item 9. Multi-path check
+If multi-pathing is enabled, then check whether
+the routes not yet distinguished in preference may be considered equal. If
+@ref{bgp bestpath as-path multipath-relax} is set, all such routes are
+considered equal, otherwise routes received via iBGP with identical AS_PATHs
+or routes received from eBGP neighbours in the same AS are considered equal.
+
+@item 10 Already-selected external check
+
+Where both routes were received from eBGP peers, then prefer the route which
+is already selected. Note that this check is not applied if @ref{bgp
+bestpath compare-routerid} is configured. This check can prevent some cases
+of oscillation.
+
+@item 11. Router-ID check
+Prefer the route with the lowest @w{router-ID}. If the
+route has an @w{ORIGINATOR_ID} attribute, through iBGP reflection, then that
+router ID is used, otherwise the @w{router-ID} of the peer the route was
+received from is used.
+
+@item 12. Cluster-List length check
+The route with the shortest cluster-list
+length is used. The cluster-list reflects the iBGP reflection path the
+route has taken.
+
+@item 13. Peer address
+Prefer the route received from the peer with the higher
+transport layer address, as a last-resort tie-breaker.
+
+@end table
+
+@deffn {BGP} {bgp bestpath as-path confed} {}
+This command specifies that the length of confederation path sets and
+sequences should should be taken into account during the BGP best path
+decision process.
+@end deffn
+
+@deffn {BGP} {bgp bestpath as-path multipath-relax} {}
+@anchor{bgp bestpath as-path multipath-relax}
+This command specifies that BGP decision process should consider paths
+of equal AS_PATH length candidates for multipath computation. Without
+the knob, the entire AS_PATH must match for multipath computation.
+@end deffn
+
+@deffn {BGP} {bgp bestpath compare-routerid} {}
+@anchor{bgp bestpath compare-routerid}
+
+Ensure that when comparing routes where both are equal on most metrics,
+including local-pref, AS_PATH length, IGP cost, MED, that the tie is broken
+based on router-ID.
+
+If this option is enabled, then the already-selected check, where
+already selected eBGP routes are preferred, is skipped.
+
+If a route has an @w{ORIGINATOR_ID} attribute because it has been reflected,
+that @w{ORIGINATOR_ID} will be used. Otherwise, the router-ID of the peer the
+route was received from will be used.
+
+The advantage of this is that the route-selection (at this point) will be
+more deterministic. The disadvantage is that a few or even one lowest-ID
+router may attract all trafic to otherwise-equal paths because of this
+check. It may increase the possibility of MED or IGP oscillation, unless
+other measures were taken to avoid these. The exact behaviour will be
+sensitive to the iBGP and reflection topology.
+
+@end deffn
+
+
+@node BGP route flap dampening
+@subsection BGP route flap dampening
+
+@deffn {BGP} {bgp dampening @var{<1-45>} @var{<1-20000>} @var{<1-20000>} @var{<1-255>}} {}
+This command enables BGP route-flap dampening and specifies dampening parameters.
+
+@table @asis
+@item @asis{half-life}
+Half-life time for the penalty
+@item @asis{reuse-threshold}
+Value to start reusing a route
+@item @asis{suppress-threshold}
+Value to start suppressing a route
+@item @asis{max-suppress}
+Maximum duration to suppress a stable route
+@end table
+
+The route-flap damping algorithm is compatible with @cite{RFC2439}. The use of this command
+is not recommended nowadays, see @uref{http://www.ripe.net/ripe/docs/ripe-378,,RIPE-378}.
+@end deffn
+
+@node BGP MED
+@section BGP MED
+
+The BGP MED (Multi_Exit_Discriminator) attribute has properties which can
+cause subtle convergence problems in BGP. These properties and problems
+have proven to be hard to understand, at least historically, and may still
+not be widely understood. The following attempts to collect together and
+present what is known about MED, to help operators and Frr users in
+designing and configuring their networks.
+
+The BGP @acronym{MED, Multi_Exit_Discriminator} attribute is intended to
+allow one AS to indicate its preferences for its ingress points to another
+AS. The MED attribute will not be propagated on to another AS by the
+receiving AS - it is `non-transitive' in the BGP sense.
+
+E.g., if AS X and AS Y have 2 different BGP peering points, then AS X
+might set a MED of 100 on routes advertised at one and a MED of 200 at the
+other. When AS Y selects between otherwise equal routes to or via
+AS X, AS Y should prefer to take the path via the lower MED peering of 100 with
+AS X. Setting the MED allows an AS to influence the routing taken to it
+within another, neighbouring AS.
+
+In this use of MED it is not really meaningful to compare the MED value on
+routes where the next AS on the paths differs. E.g., if AS Y also had a
+route for some destination via AS Z in addition to the routes from AS X, and
+AS Z had also set a MED, it wouldn't make sense for AS Y to compare AS Z's
+MED values to those of AS X. The MED values have been set by different
+administrators, with different frames of reference.
+
+The default behaviour of BGP therefore is to not compare MED values across
+routes received from different neighbouring ASes. In Frr this is done by
+comparing the neighbouring, left-most AS in the received AS_PATHs of the
+routes and only comparing MED if those are the same.
+
+@c TeXInfo uses the old, non-UTF-8 capable, pdftex, and so
+@c doesn't render TeX the unicode precedes character correctly in PDF, etc.
+@c Using a TeX code on the other hand doesn't work for non-TeX outputs
+@c (plaintext, e.g.). So, use an output-conditional macro.
+
+@iftex
+@macro mprec{}
+@math{\\prec}
+@end macro
+@end iftex
+
+@ifnottex
+@macro mprec{}
+@math{≺}
+@end macro
+@end ifnottex
+
+Unfortunately, this behaviour of MED, of sometimes being compared across
+routes and sometimes not, depending on the properties of those other routes,
+means MED can cause the order of preference over all the routes to be
+undefined. That is, given routes A, B, and C, if A is preferred to B, and B
+is preferred to C, then a well-defined order should mean the preference is
+transitive (in the sense of orders @footnote{For some set of objects to have
+an order, there @emph{must} be some binary ordering relation that is defined
+for @emph{every} combination of those objects, and that relation @emph{must}
+be transitive. I.e.@:, if the relation operator is @mprec{}, and if
+a @mprec{} b and b @mprec{} c then that relation must carry over
+and it @emph{must} be that a @mprec{} c for the objects to have an
+order. The ordering relation may allow for equality, i.e.
+a @mprec{} b and b @mprec{} a may both be true amd imply that
+a and b are equal in the order and not distinguished by it, in
+which case the set has a partial order. Otherwise, if there is an order,
+all the objects have a distinct place in the order and the set has a total
+order.}) and that A would be preferred to C.
+
+However, when MED is involved this need not be the case. With MED it is
+possible that C is actually preferred over A. So A is preferred to B, B is
+preferred to C, but C is preferred to A. This can be true even where BGP
+defines a deterministic ``most preferred'' route out of the full set of
+A,B,C. With MED, for any given set of routes there may be a
+deterministically preferred route, but there need not be any way to arrange
+them into any order of preference. With unmodified MED, the order of
+preference of routes literally becomes undefined.
+
+That MED can induce non-transitive preferences over routes can cause issues.
+Firstly, it may be perceived to cause routing table churn locally at
+speakers; secondly, and more seriously, it may cause routing instability in
+iBGP topologies, where sets of speakers continually oscillate between
+different paths.
+
+The first issue arises from how speakers often implement routing decisions.
+Though BGP defines a selection process that will deterministically select
+the same route as best at any given speaker, even with MED, that process
+requires evaluating all routes together. For performance and ease of
+implementation reasons, many implementations evaluate route preferences in a
+pair-wise fashion instead. Given there is no well-defined order when MED is
+involved, the best route that will be chosen becomes subject to
+implementation details, such as the order the routes are stored in. That
+may be (locally) non-deterministic, e.g.@: it may be the order the routes
+were received in.
+
+This indeterminism may be considered undesirable, though it need not cause
+problems. It may mean additional routing churn is perceived, as sometimes
+more updates may be produced than at other times in reaction to some event .
+
+This first issue can be fixed with a more deterministic route selection that
+ensures routes are ordered by the neighbouring AS during selection.
+@xref{bgp deterministic-med}. This may reduce the number of updates as
+routes are received, and may in some cases reduce routing churn. Though, it
+could equally deterministically produce the largest possible set of updates
+in response to the most common sequence of received updates.
+
+A deterministic order of evaluation tends to imply an additional overhead of
+sorting over any set of n routes to a destination. The implementation of
+deterministic MED in Frr scales significantly worse than most sorting
+algorithms at present, with the number of paths to a given destination.
+That number is often low enough to not cause any issues, but where there are
+many paths, the deterministic comparison may quickly become increasingly
+expensive in terms of CPU.
+
+Deterministic local evaluation can @emph{not} fix the second, more major,
+issue of MED however. Which is that the non-transitive preference of routes
+MED can cause may lead to routing instability or oscillation across multiple
+speakers in iBGP topologies. This can occur with full-mesh iBGP, but is
+particularly problematic in non-full-mesh iBGP topologies that further
+reduce the routing information known to each speaker. This has primarily
+been documented with iBGP route-reflection topologies. However, any
+route-hiding technologies potentially could also exacerbate oscillation with
+MED.
+
+This second issue occurs where speakers each have only a subset of routes,
+and there are cycles in the preferences between different combinations of
+routes - as the undefined order of preference of MED allows - and the routes
+are distributed in a way that causes the BGP speakers to 'chase' those
+cycles. This can occur even if all speakers use a deterministic order of
+evaluation in route selection.
+
+E.g., speaker 4 in AS A might receive a route from speaker 2 in AS X, and
+from speaker 3 in AS Y; while speaker 5 in AS A might receive that route
+from speaker 1 in AS Y. AS Y might set a MED of 200 at speaker 1, and 100
+at speaker 3. I.e, using ASN:ID:MED to label the speakers:
+
+@example
+
+ /---------------\
+ X:2------|--A:4-------A:5--|-Y:1:200
+ Y:3:100--|-/ |
+ \---------------/
+
+@end example
+
+Assuming all other metrics are equal (AS_PATH, ORIGIN, 0 IGP costs), then
+based on the RFC4271 decision process speaker 4 will choose X:2 over
+Y:3:100, based on the lower ID of 2. Speaker 4 advertises X:2 to speaker 5.
+Speaker 5 will continue to prefer Y:1:200 based on the ID, and advertise
+this to speaker 4. Speaker 4 will now have the full set of routes, and the
+Y:1:200 it receives from 5 will beat X:2, but when speaker 4 compares
+Y:1:200 to Y:3:100 the MED check now becomes active as the ASes match, and
+now Y:3:100 is preferred. Speaker 4 therefore now advertises Y:3:100 to 5,
+which will also agrees that Y:3:100 is preferred to Y:1:200, and so
+withdraws the latter route from 4. Speaker 4 now has only X:2 and Y:3:100,
+and X:2 beats Y:3:100, and so speaker 4 implicitly updates its route to
+speaker 5 to X:2. Speaker 5 sees that Y:1:200 beats X:2 based on the ID,
+and advertises Y:1:200 to speaker 4, and the cycle continues.
+
+The root cause is the lack of a clear order of preference caused by how MED
+sometimes is and sometimes is not compared, leading to this cycle in the
+preferences between the routes:
+
+@example
+
+ /---> X:2 ---beats---> Y:3:100 --\
+ | |
+ | |
+ \---beats--- Y:1:200 <---beats---/
+
+@end example
+
+This particular type of oscillation in full-mesh iBGP topologies can be
+avoided by speakers preferring already selected, external routes rather than
+choosing to update to new a route based on a post-MED metric (e.g.
+router-ID), at the cost of a non-deterministic selection process. Frr
+implements this, as do many other implementations, so long as it is not
+overridden by setting @ref{bgp bestpath compare-routerid}, and see also
+@ref{BGP decision process}, .
+
+However, more complex and insidious cycles of oscillation are possible with
+iBGP route-reflection, which are not so easily avoided. These have been
+documented in various places. See, e.g., @cite{McPherson, D. and Gill, V.
+and Walton, D., "Border Gateway Protocol (BGP) Persistent Route Oscillation
+Condition", IETF RFC3345}, and @cite{Flavel, A. and M. Roughan, "Stable
+and flexible iBGP", ACM SIGCOMM 2009}, and @cite{Griffin, T. and G. Wilfong,
+"On the correctness of IBGP configuration", ACM SIGCOMM 2002} for concrete
+examples and further references.
+
+There is as of this writing @emph{no} known way to use MED for its original
+purpose; @emph{and} reduce routing information in iBGP topologies;
+@emph{and} be sure to avoid the instability problems of MED due the
+non-transitive routing preferences it can induce; in general on arbitrary
+networks.
+
+There may be iBGP topology specific ways to reduce the instability risks,
+even while using MED, e.g.@: by constraining the reflection topology and by
+tuning IGP costs between route-reflector clusters, see RFC3345 for details.
+In the near future, the Add-Path extension to BGP may also solve MED
+oscillation while still allowing MED to be used as intended, by distributing
+"best-paths per neighbour AS". This would be at the cost of distributing at
+least as many routes to all speakers as a full-mesh iBGP would, if not more,
+while also imposing similar CPU overheads as the "Deterministic MED" feature
+at each Add-Path reflector.
+
+More generally, the instability problems that MED can introduce on more
+complex, non-full-mesh, iBGP topologies may be avoided either by:
+
+@itemize
+
+@item
+Setting @ref{bgp always-compare-med}, however this allows MED to be compared
+across values set by different neighbour ASes, which may not produce
+coherent desirable results, of itself.
+
+@item
+Effectively ignoring MED by setting MED to the same value (e.g.@: 0) using
+@ref{routemap set metric} on all received routes, in combination with
+setting @ref{bgp always-compare-med} on all speakers. This is the simplest
+and most performant way to avoid MED oscillation issues, where an AS is happy
+not to allow neighbours to inject this problematic metric.
+
+@end itemize
+
+As MED is evaluated after the AS_PATH length check, another possible use for
+MED is for intra-AS steering of routes with equal AS_PATH length, as an
+extension of the last case above. As MED is evaluated before IGP metric,
+this can allow cold-potato routing to be implemented to send traffic to
+preferred hand-offs with neighbours, rather than the closest hand-off
+according to the IGP metric.
+
+Note that even if action is taken to address the MED non-transitivity
+issues, other oscillations may still be possible. E.g., on IGP cost if
+iBGP and IGP topologies are at cross-purposes with each other - see the
+Flavel and Roughan paper above for an example. Hence the guideline that the
+iBGP topology should follow the IGP topology.
+
+@deffn {BGP} {bgp deterministic-med} {}
+@anchor{bgp deterministic-med}
+
+Carry out route-selection in way that produces deterministic answers
+locally, even in the face of MED and the lack of a well-defined order of
+preference it can induce on routes. Without this option the preferred route
+with MED may be determined largely by the order that routes were received
+in.
+
+Setting this option will have a performance cost that may be noticeable when
+there are many routes for each destination. Currently in Frr it is
+implemented in a way that scales poorly as the number of routes per
+destination increases.
+
+The default is that this option is not set.
+@end deffn
+
+Note that there are other sources of indeterminism in the route selection
+process, specifically, the preference for older and already selected routes
+from eBGP peers, @xref{BGP decision process}.
+
+@deffn {BGP} {bgp always-compare-med} {}
+@anchor{bgp always-compare-med}
+
+Always compare the MED on routes, even when they were received from
+different neighbouring ASes. Setting this option makes the order of
+preference of routes more defined, and should eliminate MED induced
+oscillations.
+
+If using this option, it may also be desirable to use @ref{routemap set
+metric} to set MED to 0 on routes received from external neighbours.
+
+This option can be used, together with @ref{routemap set metric} to use MED
+as an intra-AS metric to steer equal-length AS_PATH routes to, e.g., desired
+exit points.
+@end deffn
+
+
+
+@node BGP network
+@section BGP network
+
+@menu
+* BGP route::
+* Route Aggregation::
+* Redistribute to BGP::
+@end menu
+
+@node BGP route
+@subsection BGP route
+
+@deffn {BGP} {network @var{A.B.C.D/M}} {}
+This command adds the announcement network.
+@example
+@group
+router bgp 1
+ address-family ipv4 unicast
+ network 10.0.0.0/8
+ exit-address-family
+@end group
+@end example
+This configuration example says that network 10.0.0.0/8 will be
+announced to all neighbors. Some vendors' routers don't advertise
+routes if they aren't present in their IGP routing tables; @code{bgpd}
+doesn't care about IGP routes when announcing its routes.
+@end deffn
+
+@deffn {BGP} {no network @var{A.B.C.D/M}} {}
+@end deffn
+
+@node Route Aggregation
+@subsection Route Aggregation
+
+@deffn {BGP} {aggregate-address @var{A.B.C.D/M}} {}
+This command specifies an aggregate address.
+@end deffn
+
+@deffn {BGP} {aggregate-address @var{A.B.C.D/M} as-set} {}
+This command specifies an aggregate address. Resulting routes include
+AS set.
+@end deffn
+
+@deffn {BGP} {aggregate-address @var{A.B.C.D/M} summary-only} {}
+This command specifies an aggregate address. Aggreated routes will
+not be announce.
+@end deffn
+
+@deffn {BGP} {no aggregate-address @var{A.B.C.D/M}} {}
+@end deffn
+
+@node Redistribute to BGP
+@subsection Redistribute to BGP
+
+@deffn {BGP} {redistribute kernel} {}
+Redistribute kernel route to BGP process.
+@end deffn
+
+@deffn {BGP} {redistribute static} {}
+Redistribute static route to BGP process.
+@end deffn
+
+@deffn {BGP} {redistribute connected} {}
+Redistribute connected route to BGP process.
+@end deffn
+
+@deffn {BGP} {redistribute rip} {}
+Redistribute RIP route to BGP process.
+@end deffn
+
+@deffn {BGP} {redistribute ospf} {}
+Redistribute OSPF route to BGP process.
+@end deffn
+
+@deffn {BGP} {redistribute vpn} {}
+Redistribute VNC routes to BGP process.
+@end deffn
+
+@deffn {BGP} {update-delay @var{max-delay}} {}
+@deffnx {BGP} {update-delay @var{max-delay} @var{establish-wait}} {}
+This feature is used to enable read-only mode on BGP process restart or when
+BGP process is cleared using 'clear ip bgp *'. When applicable, read-only mode
+would begin as soon as the first peer reaches Established status and a timer
+for max-delay seconds is started.
+
+During this mode BGP doesn't run any best-path or generate any updates to its
+peers. This mode continues until:
+1. All the configured peers, except the shutdown peers, have sent explicit EOR
+(End-Of-RIB) or an implicit-EOR. The first keep-alive after BGP has reached
+Established is considered an implicit-EOR.
+ If the establish-wait optional value is given, then BGP will wait for
+ peers to reach established from the begining of the update-delay till the
+ establish-wait period is over, i.e. the minimum set of established peers for
+ which EOR is expected would be peers established during the establish-wait
+ window, not necessarily all the configured neighbors.
+2. max-delay period is over.
+On hitting any of the above two conditions, BGP resumes the decision process
+and generates updates to its peers.
+
+Default max-delay is 0, i.e. the feature is off by default.
+@end deffn
+
+@deffn {BGP} {table-map @var{route-map-name}} {}
+This feature is used to apply a route-map on route updates from BGP to Zebra.
+All the applicable match operations are allowed, such as match on prefix,
+next-hop, communities, etc. Set operations for this attach-point are limited
+to metric and next-hop only. Any operation of this feature does not affect
+BGPs internal RIB.
+
+Supported for ipv4 and ipv6 address families. It works on multi-paths as well,
+however, metric setting is based on the best-path only.
+@end deffn
+
+@node BGP Peer
+@section BGP Peer
+
+@menu
+* Defining Peer::
+* BGP Peer commands::
+* Peer filtering::
+@end menu
+
+@node Defining Peer
+@subsection Defining Peer
+
+@deffn {BGP} {neighbor @var{peer} remote-as @var{asn}} {}
+Creates a new neighbor whose remote-as is @var{asn}. @var{peer}
+can be an IPv4 address or an IPv6 address.
+@example
+@group
+router bgp 1
+ neighbor 10.0.0.1 remote-as 2
+@end group
+@end example
+In this case my router, in AS-1, is trying to peer with AS-2 at
+10.0.0.1.
+
+This command must be the first command used when configuring a neighbor.
+If the remote-as is not specified, @command{bgpd} will complain like this:
+@example
+can't find neighbor 10.0.0.1
+@end example
+@end deffn
+
+@node BGP Peer commands
+@subsection BGP Peer commands
+
+In a @code{router bgp} clause there are neighbor specific configurations
+required.
+
+@deffn {BGP} {neighbor @var{peer} shutdown} {}
+@deffnx {BGP} {no neighbor @var{peer} shutdown} {}
+Shutdown the peer. We can delete the neighbor's configuration by
+@code{no neighbor @var{peer} remote-as @var{as-number}} but all
+configuration of the neighbor will be deleted. When you want to
+preserve the configuration, but want to drop the BGP peer, use this
+syntax.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} ebgp-multihop} {}
+@deffnx {BGP} {no neighbor @var{peer} ebgp-multihop} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} description ...} {}
+@deffnx {BGP} {no neighbor @var{peer} description ...} {}
+Set description of the peer.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} version @var{version}} {}
+Set up the neighbor's BGP version. @var{version} can be @var{4},
+@var{4+} or @var{4-}. BGP version @var{4} is the default value used for
+BGP peering. BGP version @var{4+} means that the neighbor supports
+Multiprotocol Extensions for BGP-4. BGP version @var{4-} is similar but
+the neighbor speaks the old Internet-Draft revision 00's Multiprotocol
+Extensions for BGP-4. Some routing software is still using this
+version.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} interface @var{ifname}} {}
+@deffnx {BGP} {no neighbor @var{peer} interface @var{ifname}} {}
+When you connect to a BGP peer over an IPv6 link-local address, you
+have to specify the @var{ifname} of the interface used for the
+connection. To specify IPv4 session addresses, see the
+@code{neighbor @var{peer} update-source} command below.
+
+This command is deprecated and may be removed in a future release. Its
+use should be avoided.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} next-hop-self [all]} {}
+@deffnx {BGP} {no neighbor @var{peer} next-hop-self [all]} {}
+This command specifies an announced route's nexthop as being equivalent
+to the address of the bgp router if it is learned via eBGP.
+If the optional keyword @code{all} is specified the modifiation is done
+also for routes learned via iBGP.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} update-source @var{<ifname|address>}} {}
+@deffnx {BGP} {no neighbor @var{peer} update-source} {}
+Specify the IPv4 source address to use for the @acronym{BGP} session to this
+neighbour, may be specified as either an IPv4 address directly or
+as an interface name (in which case the @command{zebra} daemon MUST be running
+in order for @command{bgpd} to be able to retrieve interface state).
+@example
+@group
+router bgp 64555
+ neighbor foo update-source 192.168.0.1
+ neighbor bar update-source lo0
+@end group
+@end example
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} default-originate} {}
+@deffnx {BGP} {no neighbor @var{peer} default-originate} {}
+@command{bgpd}'s default is to not announce the default route (0.0.0.0/0) even it
+is in routing table. When you want to announce default routes to the
+peer, use this command.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} port @var{port}} {}
+@deffnx {BGP} {neighbor @var{peer} port @var{port}} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} send-community} {}
+@deffnx {BGP} {neighbor @var{peer} send-community} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} weight @var{weight}} {}
+@deffnx {BGP} {no neighbor @var{peer} weight @var{weight}} {}
+This command specifies a default @var{weight} value for the neighbor's
+routes.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} maximum-prefix @var{number}} {}
+@deffnx {BGP} {no neighbor @var{peer} maximum-prefix @var{number}} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} local-as @var{as-number}} {}
+@deffnx {BGP} {neighbor @var{peer} local-as @var{as-number} no-prepend} {}
+@deffnx {BGP} {neighbor @var{peer} local-as @var{as-number} no-prepend replace-as} {}
+@deffnx {BGP} {no neighbor @var{peer} local-as} {}
+Specify an alternate AS for this BGP process when interacting with the
+specified peer. With no modifiers, the specified local-as is prepended to
+the received AS_PATH when receiving routing updates from the peer, and
+prepended to the outgoing AS_PATH (after the process local AS) when
+transmitting local routes to the peer.
+
+If the no-prepend attribute is specified, then the supplied local-as is not
+prepended to the received AS_PATH.
+
+If the replace-as attribute is specified, then only the supplied local-as is
+prepended to the AS_PATH when transmitting local-route updates to this peer.
+
+Note that replace-as can only be specified if no-prepend is.
+
+This command is only allowed for eBGP peers.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} ttl-security hops @var{number}} {}
+@deffnx {BGP} {no neighbor @var{peer} ttl-security hops @var{number}} {}
+This command enforces Generalized TTL Security Mechanism (GTSM), as
+specified in RFC 5082. With this command, only neighbors that are the
+specified number of hops away will be allowed to become neighbors. This
+command is mututally exclusive with @command{ebgp-multihop}.
+@end deffn
+
+@node Peer filtering
+@subsection Peer filtering
+
+@deffn {BGP} {neighbor @var{peer} distribute-list @var{name} [in|out]} {}
+This command specifies a distribute-list for the peer. @var{direct} is
+@samp{in} or @samp{out}.
+@end deffn
+
+@deffn {BGP command} {neighbor @var{peer} prefix-list @var{name} [in|out]} {}
+@end deffn
+
+@deffn {BGP command} {neighbor @var{peer} filter-list @var{name} [in|out]} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} route-map @var{name} [in|out]} {}
+Apply a route-map on the neighbor. @var{direct} must be @code{in} or
+@code{out}.
+@end deffn
+
+@deffn {BGP} {bgp route-reflector allow-outbound-policy} {}
+By default, attribute modification via route-map policy out is not reflected
+on reflected routes. This option allows the modifications to be reflected as
+well. Once enabled, it affects all reflected routes.
+@end deffn
+
+@c -----------------------------------------------------------------------
+@node BGP Peer Group
+@section BGP Peer Group
+
+@deffn {BGP} {neighbor @var{word} peer-group} {}
+This command defines a new peer group.
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} peer-group @var{word}} {}
+This command bind specific peer to peer group @var{word}.
+@end deffn
+
+@node BGP Address Family
+@section BGP Address Family
+
+Multiprotocol BGP enables BGP to carry routing information for multiple
+Network Layer protocols. BGP supports multiple Address Family
+Identifier (AFI), namely IPv4 and IPv6. Support is also provided for
+multiple sets of per-AFI information via Subsequent Address Family
+Identifiers (SAFI). In addition to unicast information, VPN information
+@cite{RFC4364} and @cite{RFC4659}, and Encapsulation attribute
+@cite{RFC5512} is supported.
+
+@deffn {Command} {show ip bgp ipv4 vpn} {}
+@deffnx {Command} {show ipv6 bgp ipv6 vpn} {}
+Print active IPV4 or IPV6 routes advertised via the VPN SAFI.
+@end deffn
+
+@deffn {Command} {show bgp ipv4 vpn summary} {}
+@deffnx {Command} {show bgp ipv6 vpn summary} {}
+Print a summary of neighbor connections for the specified AFI/SAFI combination.
+@end deffn
+
+@c -----------------------------------------------------------------------
+@node Autonomous System
+@section Autonomous System
+
+The @acronym{AS,Autonomous System} number is one of the essential
+element of BGP. BGP is a distance vector routing protocol, and the
+AS-Path framework provides distance vector metric and loop detection to
+BGP. @cite{RFC1930, Guidelines for creation, selection, and
+registration of an Autonomous System (AS)} provides some background on
+the concepts of an AS.
+
+The AS number is a two octet value, ranging in value from 1 to 65535.
+The AS numbers 64512 through 65535 are defined as private AS numbers.
+Private AS numbers must not to be advertised in the global Internet.
+
+@menu
+* Display BGP Routes by AS Path::
+* AS Path Access List::
+* Using AS Path in Route Map::
+* Private AS Numbers::
+@end menu
+
+@node Display BGP Routes by AS Path
+@subsection Display BGP Routes by AS Path
+
+To show BGP routes which has specific AS path information @code{show
+ip bgp} command can be used.
+
+@deffn Command {show bgp @{ipv4|ipv6@} regexp @var{line}} {}
+This commands displays BGP routes that matches a regular
+expression @var{line} (@pxref{BGP Regular Expressions}).
+@end deffn
+
+@node AS Path Access List
+@subsection AS Path Access List
+
+AS path access list is user defined AS path.
+
+@deffn {Command} {ip as-path access-list @var{word} @{permit|deny@} @var{line}} {}
+This command defines a new AS path access list.
+@end deffn
+
+@deffn {Command} {no ip as-path access-list @var{word}} {}
+@deffnx {Command} {no ip as-path access-list @var{word} @{permit|deny@} @var{line}} {}
+@end deffn
+
+@node Using AS Path in Route Map
+@subsection Using AS Path in Route Map
+
+@deffn {Route Map} {match as-path @var{word}} {}
+@end deffn
+
+@deffn {Route Map} {set as-path prepend @var{as-path}} {}
+Prepend the given string of AS numbers to the AS_PATH.
+@end deffn
+
+@deffn {Route Map} {set as-path prepend last-as @var{num}} {}
+Prepend the existing last AS number (the leftmost ASN) to the AS_PATH.
+@end deffn
+
+@node Private AS Numbers
+@subsection Private AS Numbers
+
+@c -----------------------------------------------------------------------
+@node BGP Communities Attribute
+@section BGP Communities Attribute
+
+BGP communities attribute is widely used for implementing policy
+routing. Network operators can manipulate BGP communities attribute
+based on their network policy. BGP communities attribute is defined
+in @cite{RFC1997, BGP Communities Attribute} and
+@cite{RFC1998, An Application of the BGP Community Attribute
+in Multi-home Routing}. It is an optional transitive attribute,
+therefore local policy can travel through different autonomous system.
+
+Communities attribute is a set of communities values. Each
+communities value is 4 octet long. The following format is used to
+define communities value.
+
+@table @code
+@item AS:VAL
+This format represents 4 octet communities value. @code{AS} is high
+order 2 octet in digit format. @code{VAL} is low order 2 octet in
+digit format. This format is useful to define AS oriented policy
+value. For example, @code{7675:80} can be used when AS 7675 wants to
+pass local policy value 80 to neighboring peer.
+@item internet
+@code{internet} represents well-known communities value 0.
+@item no-export
+@code{no-export} represents well-known communities value @code{NO_EXPORT}@*
+@r{(0xFFFFFF01)}. All routes carry this value must not be advertised
+to outside a BGP confederation boundary. If neighboring BGP peer is
+part of BGP confederation, the peer is considered as inside a BGP
+confederation boundary, so the route will be announced to the peer.
+@item no-advertise
+@code{no-advertise} represents well-known communities value
+@code{NO_ADVERTISE}@*@r{(0xFFFFFF02)}. All routes carry this value
+must not be advertise to other BGP peers.
+@item local-AS
+@code{local-AS} represents well-known communities value
+@code{NO_EXPORT_SUBCONFED} @r{(0xFFFFFF03)}. All routes carry this
+value must not be advertised to external BGP peers. Even if the
+neighboring router is part of confederation, it is considered as
+external BGP peer, so the route will not be announced to the peer.
+@end table
+
+ When BGP communities attribute is received, duplicated communities
+value in the communities attribute is ignored and each communities
+values are sorted in numerical order.
+
+@menu
+* BGP Community Lists::
+* Numbered BGP Community Lists::
+* BGP Community in Route Map::
+* Display BGP Routes by Community::
+* Using BGP Communities Attribute::
+@end menu
+
+@node BGP Community Lists
+@subsection BGP Community Lists
+
+ BGP community list is a user defined BGP communites attribute list.
+BGP community list can be used for matching or manipulating BGP
+communities attribute in updates.
+
+There are two types of community list. One is standard community
+list and another is expanded community list. Standard community list
+defines communities attribute. Expanded community list defines
+communities attribute string with regular expression. Standard
+community list is compiled into binary format when user define it.
+Standard community list will be directly compared to BGP communities
+attribute in BGP updates. Therefore the comparison is faster than
+expanded community list.
+
+@deffn Command {ip community-list standard @var{name} @{permit|deny@} @var{community}} {}
+This command defines a new standard community list. @var{community}
+is communities value. The @var{community} is compiled into community
+structure. We can define multiple community list under same name. In
+that case match will happen user defined order. Once the
+community list matches to communities attribute in BGP updates it
+return permit or deny by the community list definition. When there is
+no matched entry, deny will be returned. When @var{community} is
+empty it matches to any routes.
+@end deffn
+
+@deffn Command {ip community-list expanded @var{name} @{permit|deny@} @var{line}} {}
+This command defines a new expanded community list. @var{line} is a
+string expression of communities attribute. @var{line} can be a
+regular expression (@pxref{BGP Regular Expressions}) to match
+the communities attribute in BGP updates.
+@end deffn
+
+@deffn Command {no ip community-list @var{name}} {}
+@deffnx Command {no ip community-list standard @var{name}} {}
+@deffnx Command {no ip community-list expanded @var{name}} {}
+These commands delete community lists specified by @var{name}. All of
+community lists shares a single name space. So community lists can be
+removed simpley specifying community lists name.
+@end deffn
+
+@deffn {Command} {show ip community-list} {}
+@deffnx {Command} {show ip community-list @var{name}} {}
+This command displays current community list information. When
+@var{name} is specified the specified community list's information is
+shown.
+
+@example
+# show ip community-list
+Named Community standard list CLIST
+ permit 7675:80 7675:100 no-export
+ deny internet
+Named Community expanded list EXPAND
+ permit :
+
+# show ip community-list CLIST
+Named Community standard list CLIST
+ permit 7675:80 7675:100 no-export
+ deny internet
+@end example
+@end deffn
+
+@node Numbered BGP Community Lists
+@subsection Numbered BGP Community Lists
+
+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
+as numbered community lists. On the other hand normal community lists
+is called as named community lists.
+
+@deffn Command {ip community-list <1-99> @{permit|deny@} @var{community}} {}
+This command defines a new community list. <1-99> is standard
+community list number. Community list name within this range defines
+standard community list. When @var{community} is empty it matches to
+any routes.
+@end deffn
+
+@deffn Command {ip community-list <100-199> @{permit|deny@} @var{community}} {}
+This command defines a new community list. <100-199> is expanded
+community list number. Community list name within this range defines
+expanded community list.
+@end deffn
+
+@deffn Command {ip community-list @var{name} @{permit|deny@} @var{community}} {}
+When community list type is not specifed, the community list type is
+automatically detected. If @var{community} can be compiled into
+communities attribute, the community list is defined as a standard
+community list. Otherwise it is defined as an expanded community
+list. This feature is left for backward compability. Use of this
+feature is not recommended.
+@end deffn
+
+@node BGP Community in Route Map
+@subsection BGP Community in Route Map
+
+In Route Map (@pxref{Route Map}), we can match or set BGP
+communities attribute. Using this feature network operator can
+implement their network policy based on BGP communities attribute.
+
+Following commands can be used in Route Map.
+
+@deffn {Route Map} {match community @var{word}} {}
+@deffnx {Route Map} {match community @var{word} exact-match} {}
+This command perform match to BGP updates using community list
+@var{word}. When the one of BGP communities value match to the one of
+communities value in community list, it is match. When
+@code{exact-match} keyword is spcified, match happen only when BGP
+updates have completely same communities value specified in the
+community list.
+@end deffn
+
+@deffn {Route Map} {set community none} {}
+@deffnx {Route Map} {set community @var{community}} {}
+@deffnx {Route Map} {set community @var{community} additive} {}
+This command manipulate communities value in BGP updates. When
+@code{none} is specified as communities value, it removes entire
+communities attribute from BGP updates. When @var{community} is not
+@code{none}, specified communities value is set to BGP updates. If
+BGP updates already has BGP communities value, the existing BGP
+communities value is replaced with specified @var{community} value.
+When @code{additive} keyword is specified, @var{community} is appended
+to the existing communities value.
+@end deffn
+
+@deffn {Route Map} {set comm-list @var{word} delete} {}
+This command remove communities value from BGP communities attribute.
+The @var{word} is community list name. When BGP route's communities
+value matches to the community list @var{word}, the communities value
+is removed. When all of communities value is removed eventually, the
+BGP update's communities attribute is completely removed.
+@end deffn
+
+@node Display BGP Routes by Community
+@subsection Display BGP Routes by Community
+
+To show BGP routes which has specific BGP communities attribute,
+@code{show bgp @{ipv4|ipv6@}} command can be used. The
+@var{community} and @var{community-list} subcommand can be used.
+
+@deffn Command {show bgp @{ipv4|ipv6@} community} {}
+@deffnx Command {show bgp @{ipv4|ipv6@} community @var{community}} {}
+@deffnx Command {show bgp @{ipv4|ipv6@} community @var{community} exact-match} {}
+@code{show bgp @{ipv4|ipv6@} community} displays BGP routes which has communities
+attribute. Where the address family can be IPv4 or IPv6 among others. When
+@var{community} is specified, BGP routes that matches @var{community} value is
+displayed. For this command, @code{internet} keyword can't be used for
+@var{community} value. When @code{exact-match} is specified, it display only
+routes that have an exact match.
+@end deffn
+
+@deffn Command {show bgp @{ipv4|ipv6@} community-list @var{word}} {}
+@deffnx Command {show bgp @{ipv4|ipv6@} community-list @var{word} exact-match} {}
+This commands display BGP routes for the address family specified that matches
+community list @var{word}. When @code{exact-match} is specified, display only
+routes that have an exact match.
+@end deffn
+
+@node Using BGP Communities Attribute
+@subsection Using BGP Communities Attribute
+
+Following configuration is the most typical usage of BGP communities
+attribute. AS 7675 provides upstream Internet connection to AS 100.
+When following configuration exists in AS 7675, AS 100 networks
+operator can set local preference in AS 7675 network by setting BGP
+communities attribute to the updates.
+
+@example
+router bgp 7675
+ neighbor 192.168.0.1 remote-as 100
+ address-family ipv4 unicast
+ neighbor 192.168.0.1 route-map RMAP in
+ exit-address-family
+!
+ip community-list 70 permit 7675:70
+ip community-list 70 deny
+ip community-list 80 permit 7675:80
+ip community-list 80 deny
+ip community-list 90 permit 7675:90
+ip community-list 90 deny
+!
+route-map RMAP permit 10
+ match community 70
+ set local-preference 70
+!
+route-map RMAP permit 20
+ match community 80
+ set local-preference 80
+!
+route-map RMAP permit 30
+ match community 90
+ set local-preference 90
+@end example
+
+Following configuration announce 10.0.0.0/8 from AS 100 to AS 7675.
+The route has communities value 7675:80 so when above configuration
+exists in AS 7675, announced route's local preference will be set to
+value 80.
+
+@example
+router bgp 100
+ network 10.0.0.0/8
+ neighbor 192.168.0.2 remote-as 7675
+ address-family ipv4 unicast
+ neighbor 192.168.0.2 route-map RMAP out
+ exit-address-family
+!
+ip prefix-list PLIST permit 10.0.0.0/8
+!
+route-map RMAP permit 10
+ match ip address prefix-list PLIST
+ set community 7675:80
+@end example
+
+Following configuration is an example of BGP route filtering using
+communities attribute. This configuration only permit BGP routes
+which has BGP communities value 0:80 or 0:90. Network operator can
+put special internal communities value at BGP border router, then
+limit the BGP routes announcement into the internal network.
+
+@example
+router bgp 7675
+ neighbor 192.168.0.1 remote-as 100
+ address-family ipv4 unicast
+ neighbor 192.168.0.1 route-map RMAP in
+ exit-address-family
+!
+ip community-list 1 permit 0:80 0:90
+!
+route-map RMAP permit in
+ match community 1
+@end example
+
+Following exmaple filter BGP routes which has communities value 1:1.
+When there is no match community-list returns deny. To avoid
+filtering all of routes, we need to define permit any at last.
+
+@example
+router bgp 7675
+ neighbor 192.168.0.1 remote-as 100
+ address-family ipv4 unicast
+ neighbor 192.168.0.1 route-map RMAP in
+ exit-address-family
+!
+ip community-list standard FILTER deny 1:1
+ip community-list standard FILTER permit
+!
+route-map RMAP permit 10
+ match community FILTER
+@end example
+
+Communities value keyword @code{internet} has special meanings in
+standard community lists. In below example @code{internet} act as
+match any. It matches all of BGP routes even if the route does not
+have communities attribute at all. So community list @code{INTERNET}
+is same as above example's @code{FILTER}.
+
+@example
+ip community-list standard INTERNET deny 1:1
+ip community-list standard INTERNET permit internet
+@end example
+
+Following configuration is an example of communities value deletion.
+With this configuration communities value 100:1 and 100:2 is removed
+from BGP updates. For communities value deletion, only @code{permit}
+community-list is used. @code{deny} community-list is ignored.
+
+@example
+router bgp 7675
+ neighbor 192.168.0.1 remote-as 100
+ address-family ipv4 unicast
+ neighbor 192.168.0.1 route-map RMAP in
+ exit-address-family
+!
+ip community-list standard DEL permit 100:1 100:2
+!
+route-map RMAP permit 10
+ set comm-list DEL delete
+@end example
+
+@c -----------------------------------------------------------------------
+@node BGP Extended Communities Attribute
+@section BGP Extended Communities Attribute
+
+BGP extended communities attribute is introduced with MPLS VPN/BGP
+technology. MPLS VPN/BGP expands capability of network infrastructure
+to provide VPN functionality. At the same time it requires a new
+framework for policy routing. With BGP Extended Communities Attribute
+we can use Route Target or Site of Origin for implementing network
+policy for MPLS VPN/BGP.
+
+BGP Extended Communities Attribute is similar to BGP Communities
+Attribute. It is an optional transitive attribute. BGP Extended
+Communities Attribute can carry multiple Extended Community value.
+Each Extended Community value is eight octet length.
+
+BGP Extended Communities Attribute provides an extended range
+compared with BGP Communities Attribute. Adding to that there is a
+type field in each value to provides community space structure.
+
+There are two format to define Extended Community value. One is AS
+based format the other is IP address based format.
+
+@table @code
+@item AS:VAL
+This is a format to define AS based Extended Community value.
+@code{AS} part is 2 octets Global Administrator subfield in Extended
+Community value. @code{VAL} part is 4 octets Local Administrator
+subfield. @code{7675:100} represents AS 7675 policy value 100.
+@item IP-Address:VAL
+This is a format to define IP address based Extended Community value.
+@code{IP-Address} part is 4 octets Global Administrator subfield.
+@code{VAL} part is 2 octets Local Administrator subfield.
+@code{10.0.0.1:100} represents
+@end table
+
+@menu
+* BGP Extended Community Lists::
+* BGP Extended Communities in Route Map::
+@end menu
+
+@node BGP Extended Community Lists
+@subsection BGP Extended Community Lists
+
+Expanded Community Lists is a user defined BGP Expanded Community
+Lists.
+
+@deffn Command {ip extcommunity-list standard @var{name} @{permit|deny@} @var{extcommunity}} {}
+This command defines a new standard extcommunity-list.
+@var{extcommunity} is extended communities value. The
+@var{extcommunity} is compiled into extended community structure. We
+can define multiple extcommunity-list under same name. In that case
+match will happen user defined order. Once the extcommunity-list
+matches to extended communities attribute in BGP updates it return
+permit or deny based upon the extcommunity-list definition. When
+there is no matched entry, deny will be returned. When
+@var{extcommunity} is empty it matches to any routes.
+@end deffn
+
+@deffn Command {ip extcommunity-list expanded @var{name} @{permit|deny@} @var{line}} {}
+This command defines a new expanded extcommunity-list. @var{line} is
+a string expression of extended communities attribute. @var{line} can
+be a regular expression (@pxref{BGP Regular Expressions}) to match an
+extended communities attribute in BGP updates.
+@end deffn
+
+@deffn Command {no ip extcommunity-list @var{name}} {}
+@deffnx Command {no ip extcommunity-list standard @var{name}} {}
+@deffnx Command {no ip extcommunity-list expanded @var{name}} {}
+These commands delete extended community lists specified by
+@var{name}. All of extended community lists shares a single name
+space. So extended community lists can be removed simpley specifying
+the name.
+@end deffn
+
+@deffn {Command} {show ip extcommunity-list} {}
+@deffnx {Command} {show ip extcommunity-list @var{name}} {}
+This command displays current extcommunity-list information. When
+@var{name} is specified the community list's information is shown.
+
+@example
+# show ip extcommunity-list
+@end example
+@end deffn
+
+@node BGP Extended Communities in Route Map
+@subsection BGP Extended Communities in Route Map
+
+@deffn {Route Map} {match extcommunity @var{word}} {}
+@end deffn
+
+@deffn {Route Map} {set extcommunity rt @var{extcommunity}} {}
+This command set Route Target value.
+@end deffn
+
+@deffn {Route Map} {set extcommunity soo @var{extcommunity}} {}
+This command set Site of Origin value.
+@end deffn
+
+@c -----------------------------------------------------------------------
+@node BGP Large Communities Attribute
+@section BGP Large Communities Attribute
+
+The BGP Large Communities attribute was introduced in Feb 2017 with
+@cite{RFC8092, BGP Large Communities Attribute}.
+
+The BGP Large Communities Attribute is similar to the BGP Communities
+Attribute except that it has 3 components instead of two and each of
+which are 4 octets in length. Large Communities bring additional
+functionality and convenience over traditional communities, specifically
+the fact that the @code{GLOBAL} part below is now 4 octets wide allowing
+AS4 operators seamless use.
+
+@table @code
+@item GLOBAL:LOCAL1:LOCAL2
+This is the format to define Large Community values. Referencing
+@cite{RFC8195, Use of BGP Large Communities} the values are commonly
+referred to as follows.
+The @code{GLOBAL} part is a 4 octet Global Administrator field, common
+use of this field is the operators AS number.
+The @code{LOCAL1} part is a 4 octet Local Data Part 1 subfield referred
+to as a function.
+The @code{LOCAL2} part is a 4 octet Local Data Part 2 field and referred
+to as the parameter subfield. @code{65551:1:10} represents AS 65551
+function 1 and parameter 10.
+The referenced RFC above gives some guidelines on recommended usage.
+@end table
+
+@menu
+* BGP Large Community Lists::
+* BGP Large Communities in Route Map::
+@end menu
+
+@node BGP Large Community Lists
+@subsection BGP Large Community Lists
+
+Two types of large community lists are supported, namely @code{standard} and
+@code{expanded}.
+
+@deffn Command {ip large-community-list standard @var{name} @{permit|deny@} @var{large-community}} {}
+This command defines a new standard large-community-list.
+@var{large-community} is the Large Community value. We
+can add multiple large communities under same name. In that case
+the match will happen in the user defined order. Once the large-community-list
+matches the Large Communities attribute in BGP updates it will return
+permit or deny based upon the large-community-list definition. When
+there is no matched entry, a deny will be returned. When @var{large-community}
+is empty it matches any routes.
+@end deffn
+
+@deffn Command {ip large-community-list expanded @var{name} @{permit|deny@} @var{line}} {}
+This command defines a new expanded large-community-list. Where @var{line} is
+a string matching expression, it will be compared to the entire Large Communities
+attribute as a string, with each large-community in order from lowest to highest.
+@var{line} can also be a regular expression which matches this Large
+Community attribute.
+@end deffn
+
+@deffn Command {no ip large-community-list @var{name}} {}
+@deffnx Command {no ip large-community-list standard @var{name}} {}
+@deffnx Command {no ip large-community-list expanded @var{name}} {}
+These commands delete Large Community lists specified by
+@var{name}. All Large Community lists share a single namespace.
+This means Large Community lists can be removed by simply specifying the name.
+@end deffn
+
+@deffn {Command} {show ip large-community-list} {}
+@deffnx {Command} {show ip large-community-list @var{name}} {}
+This command display current large-community-list information. When
+@var{name} is specified the community list information is shown.
+@end deffn
+
+@deffn {Command} {show ip bgp large-community-info} {}
+This command displays the current large communities in use.
+@end deffn
+
+@node BGP Large Communities in Route Map
+@subsection BGP Large Communities in Route Map
+
+@deffn {Route Map} {match large-community @var{line}} {}
+Where @var{line} can be a simple string to match, or a regular expression.
+It is very important to note that this match occurs on the entire
+large-community string as a whole, where each large-community is ordered
+from lowest to highest.
+@end deffn
+
+@deffn {Route Map} {set large-community @var{large-community}} {}
+@deffnx {Route Map} {set large-community @var{large-community} @var{large-community}} {}
+@deffnx {Route Map} {set large-community @var{large-community} additive} {}
+These commands are used for setting large-community values. The first
+command will overwrite any large-communities currently present.
+The second specifies two large-communities, which overwrites the current
+large-community list. The third will add a large-community value without
+overwriting other values. Multiple large-community values can be specified.
+@end deffn
+
+@c -----------------------------------------------------------------------
+
+@node Displaying BGP information
+@section Displaying BGP information
+
+@menu
+* Showing BGP information::
+* Other BGP commands::
+@end menu
+
+@node Showing BGP information
+@subsection Showing BGP information
+
+@deffn {Command} {show ip bgp} {}
+@deffnx {Command} {show ip bgp @var{A.B.C.D}} {}
+@deffnx {Command} {show ip bgp @var{X:X::X:X}} {}
+This command displays BGP routes. When no route is specified it
+display all of IPv4 BGP routes.
+@end deffn
+
+@example
+BGP table version is 0, local router ID is 10.1.1.1
+Status codes: s suppressed, d damped, h history, * valid, > best, i - internal
+Origin codes: i - IGP, e - EGP, ? - incomplete
+
+ Network Next Hop Metric LocPrf Weight Path
+*> 1.1.1.1/32 0.0.0.0 0 32768 i
+
+Total number of prefixes 1
+@end example
+
+@deffn {Command} {show ip bgp regexp @var{line}} {}
+This command displays BGP routes using AS path regular expression
+(@pxref{BGP Regular Expressions}).
+@end deffn
+
+@deffn Command {show ip bgp community @var{community}} {}
+@deffnx Command {show ip bgp community @var{community} exact-match} {}
+This command displays BGP routes using @var{community} (@pxref{Display
+BGP Routes by Community}).
+@end deffn
+
+@deffn Command {show ip bgp community-list @var{word}} {}
+@deffnx Command {show ip bgp community-list @var{word} exact-match} {}
+This command displays BGP routes using community list (@pxref{Display
+BGP Routes by Community}).
+@end deffn
+
+@deffn {Command} {show bgp @{ipv4|ipv6@} summary} {}
+Show a bgp peer summary for the specified address family.
+@end deffn
+
+@deffn {Command} {show bgp @{ipv4|ipv6@} neighbor [@var{peer}]} {}
+This command shows information on a specific BGP @var{peer}.
+@end deffn
+
+@deffn {Command} {show bgp @{ipv4|ipv6@} dampening dampened-paths} {}
+Display paths suppressed due to dampening.
+@end deffn
+
+@deffn {Command} {show bgp @{ipv4|ipv6@} dampening flap-statistics} {}
+Display flap statistics of routes.
+@end deffn
+
+@node Other BGP commands
+@subsection Other BGP commands
+
+@deffn {Command} {clear bgp @{ipv4|ipv6@} *} {}
+Clear all address family peers.
+@end deffn
+
+@deffn {Command} {clear bgp @{ipv4|ipv6@} @var{peer}} {}
+Clear peers which have addresses of X.X.X.X
+@end deffn
+
+@deffn {Command} {clear bgp @{ipv4|ipv6@} @var{peer} soft in} {}
+Clear peer using soft reconfiguration.
+@end deffn
+
+@deffn {Command} {show debug} {}
+@end deffn
+
+@deffn {Command} {debug event} {}
+@end deffn
+
+@deffn {Command} {debug update} {}
+@end deffn
+
+@deffn {Command} {debug keepalive} {}
+@end deffn
+
+@deffn {Command} {no debug event} {}
+@end deffn
+
+@deffn {Command} {no debug update} {}
+@end deffn
+
+@deffn {Command} {no debug keepalive} {}
+@end deffn
+
+@node Capability Negotiation
+@section Capability Negotiation
+
+When adding IPv6 routing information exchange feature to BGP. There
+were some proposals. @acronym{IETF,Internet Engineering Task Force}
+@acronym{IDR, Inter Domain Routing} @acronym{WG, Working group} adopted
+a proposal called Multiprotocol Extension for BGP. The specification
+is described in @cite{RFC2283}. The protocol does not define new protocols.
+It defines new attributes to existing BGP. When it is used exchanging
+IPv6 routing information it is called BGP-4+. When it is used for
+exchanging multicast routing information it is called MBGP.
+
+@command{bgpd} supports Multiprotocol Extension for BGP. So if remote
+peer supports the protocol, @command{bgpd} can exchange IPv6 and/or
+multicast routing information.
+
+Traditional BGP did not have the feature to detect remote peer's
+capabilities, e.g. whether it can handle prefix types other than IPv4
+unicast routes. This was a big problem using Multiprotocol Extension
+for BGP to operational network. @cite{RFC2842, Capabilities
+Advertisement with BGP-4} adopted a feature called Capability
+Negotiation. @command{bgpd} use this Capability Negotiation to detect
+the remote peer's capabilities. If the peer is only configured as IPv4
+unicast neighbor, @command{bgpd} does not send these Capability
+Negotiation packets (at least not unless other optional BGP features
+require capability negotation).
+
+By default, Frr will bring up peering with minimal common capability
+for the both sides. For example, local router has unicast and
+multicast capabilitie and remote router has unicast capability. In
+this case, the local router will establish the connection with unicast
+only capability. When there are no common capabilities, Frr sends
+Unsupported Capability error and then resets the connection.
+
+If you want to completely match capabilities with remote peer. Please
+use @command{strict-capability-match} command.
+
+@deffn {BGP} {neighbor @var{peer} strict-capability-match} {}
+@deffnx {BGP} {no neighbor @var{peer} strict-capability-match} {}
+Strictly compares remote capabilities and local capabilities. If capabilities
+are different, send Unsupported Capability error then reset connection.
+@end deffn
+
+You may want to disable sending Capability Negotiation OPEN message
+optional parameter to the peer when remote peer does not implement
+Capability Negotiation. Please use @command{dont-capability-negotiate}
+command to disable the feature.
+
+@deffn {BGP} {neighbor @var{peer} dont-capability-negotiate} {}
+@deffnx {BGP} {no neighbor @var{peer} dont-capability-negotiate} {}
+Suppress sending Capability Negotiation as OPEN message optional
+parameter to the peer. This command only affects the peer is configured
+other than IPv4 unicast configuration.
+@end deffn
+
+When remote peer does not have capability negotiation feature, remote
+peer will not send any capabilities at all. In that case, bgp
+configures the peer with configured capabilities.
+
+You may prefer locally configured capabilities more than the negotiated
+capabilities even though remote peer sends capabilities. If the peer
+is configured by @command{override-capability}, @command{bgpd} ignores
+received capabilities then override negotiated capabilities with
+configured values.
+
+@deffn {BGP} {neighbor @var{peer} override-capability} {}
+@deffnx {BGP} {no neighbor @var{peer} override-capability} {}
+Override the result of Capability Negotiation with local configuration.
+Ignore remote peer's capability value.
+@end deffn
+
+@node Route Reflector
+@section Route Reflector
+
+@deffn {BGP} {bgp cluster-id @var{a.b.c.d}} {}
+@end deffn
+
+@deffn {BGP} {neighbor @var{peer} route-reflector-client} {}
+@deffnx {BGP} {no neighbor @var{peer} route-reflector-client} {}
+@end deffn
+
+@node Route Server
+@section Route Server
+
+At an Internet Exchange point, many ISPs are connected to each other by
+external BGP peering. Normally these external BGP connection are done by
+@samp{full mesh} method. As with internal BGP full mesh formation,
+this method has a scaling problem.
+
+This scaling problem is well known. Route Server is a method to resolve
+the problem. Each ISP's BGP router only peers to Route Server. Route
+Server serves as BGP information exchange to other BGP routers. By
+applying this method, numbers of BGP connections is reduced from
+O(n*(n-1)/2) to O(n).
+
+Unlike normal BGP router, Route Server must have several routing tables
+for managing different routing policies for each BGP speaker. We call the
+routing tables as different @code{view}s. @command{bgpd} can work as
+normal BGP router or Route Server or both at the same time.
+
+@menu
+* Multiple instance::
+* BGP instance and view::
+* Routing policy::
+* Viewing the view::
+@end menu
+
+@node Multiple instance
+@subsection Multiple instance
+
+To enable multiple view function of @code{bgpd}, you must turn on
+multiple instance feature beforehand.
+
+@deffn {Command} {bgp multiple-instance} {}
+Enable BGP multiple instance feature. After this feature is enabled,
+you can make multiple BGP instances or multiple BGP views.
+@end deffn
+
+@deffn {Command} {no bgp multiple-instance} {}
+Disable BGP multiple instance feature. You can not disable this feature
+when BGP multiple instances or views exist.
+@end deffn
+
+When you want to make configuration more Cisco like one,
+
+@deffn {Command} {bgp config-type cisco} {}
+Cisco compatible BGP configuration output.
+@end deffn
+
+When bgp config-type cisco is specified,
+
+``no synchronization'' is displayed.
+``no auto-summary'' is displayed.
+
+``network'' and ``aggregate-address'' argument is displayed as
+``A.B.C.D M.M.M.M''
+
+Frr: network 10.0.0.0/8
+Cisco: network 10.0.0.0
+
+Frr: aggregate-address 192.168.0.0/24
+Cisco: aggregate-address 192.168.0.0 255.255.255.0
+
+Community attribute handling is also different. If there is no
+configuration is specified community attribute and extended community
+attribute are sent to neighbor. When user manually disable the
+feature community attribute is not sent to the neighbor. In case of
+@command{bgp config-type cisco} is specified, community attribute is not
+sent to the neighbor by default. To send community attribute user has
+to specify @command{neighbor A.B.C.D send-community} command.
+
+@example
+!
+router bgp 1
+ neighbor 10.0.0.1 remote-as 1
+ address-family ipv4 unicast
+ no neighbor 10.0.0.1 send-community
+ exit-address-family
+!
+router bgp 1
+ neighbor 10.0.0.1 remote-as 1
+ address-family ipv4 unicast
+ neighbor 10.0.0.1 send-community
+ exit-address-family
+!
+@end example
+
+@deffn {Command} {bgp config-type zebra} {}
+Frr style BGP configuration. This is default.
+@end deffn
+
+@node BGP instance and view
+@subsection BGP instance and view
+
+BGP instance is a normal BGP process. The result of route selection
+goes to the kernel routing table. You can setup different AS at the
+same time when BGP multiple instance feature is enabled.
+
+@deffn {Command} {router bgp @var{as-number}} {}
+Make a new BGP instance. You can use arbitrary word for the @var{name}.
+@end deffn
+
+@example
+@group
+bgp multiple-instance
+!
+router bgp 1
+ neighbor 10.0.0.1 remote-as 2
+ neighbor 10.0.0.2 remote-as 3
+!
+router bgp 2
+ neighbor 10.0.0.3 remote-as 4
+ neighbor 10.0.0.4 remote-as 5
+@end group
+@end example
+
+BGP view is almost same as normal BGP process. The result of
+route selection does not go to the kernel routing table. BGP view is
+only for exchanging BGP routing information.
+
+@deffn {Command} {router bgp @var{as-number} view @var{name}} {}
+Make a new BGP view. You can use arbitrary word for the @var{name}. This
+view's route selection result does not go to the kernel routing table.
+@end deffn
+
+With this command, you can setup Route Server like below.
+
+@example
+@group
+bgp multiple-instance
+!
+router bgp 1 view 1
+ neighbor 10.0.0.1 remote-as 2
+ neighbor 10.0.0.2 remote-as 3
+!
+router bgp 2 view 2
+ neighbor 10.0.0.3 remote-as 4
+ neighbor 10.0.0.4 remote-as 5
+@end group
+@end example
+
+@node Routing policy
+@subsection Routing policy
+
+You can set different routing policy for a peer. For example, you can
+set different filter for a peer.
+
+@example
+@group
+bgp multiple-instance
+!
+router bgp 1 view 1
+ neighbor 10.0.0.1 remote-as 2
+ address-family ipv4 unicast
+ neighbor 10.0.0.1 distribute-list 1 in
+ exit-address-family
+!
+router bgp 1 view 2
+ neighbor 10.0.0.1 remote-as 2
+ address-family ipv4 unicast
+ neighbor 10.0.0.1 distribute-list 2 in
+ exit-address-family
+@end group
+@end example
+
+This means BGP update from a peer 10.0.0.1 goes to both BGP view 1 and view
+2. When the update is inserted into view 1, distribute-list 1 is
+applied. On the other hand, when the update is inserted into view 2,
+distribute-list 2 is applied.
+
+@node Viewing the view
+@subsection Viewing the view
+
+To display routing table of BGP view, you must specify view name.
+
+@deffn {Command} {show ip bgp view @var{name}} {}
+Display routing table of BGP view @var{name}.
+@end deffn
+
+@node BGP Regular Expressions
+@section BGP Regular Expressions
+
+BGP regular expressions are based on @code{POSIX 1003.2} regular
+expressions. The following description is just a quick subset of the
+@code{POSIX} regular expressions. Adding to that, the special character
+'_' is added.
+
+@table @code
+@item .
+Matches any single character.
+@item *
+Matches 0 or more occurrences of pattern.
+@item +
+Matches 1 or more occurrences of pattern.
+@item ?
+Match 0 or 1 occurrences of pattern.
+@item ^
+Matches the beginning of the line.
+@item $
+Matches the end of the line.
+@item _
+Character @code{_} has special meanings in BGP regular expressions.
+It matches to space and comma , and AS set delimiter @{ and @} and AS
+confederation delimiter @code{(} and @code{)}. And it also matches to
+the beginning of the line and the end of the line. So @code{_} can be
+used for AS value boundaries match. This character technically evaluates
+to @code{(^|[,@{@}() ]|$)}.
+@end table
+
+@node How to set up a 6-Bone connection
+@section How to set up a 6-Bone connection
+
+
+@example
+@group
+zebra configuration
+===================
+!
+! Actually there is no need to configure zebra
+!
+
+bgpd configuration
+==================
+!
+! This means that routes go through zebra and into the kernel.
+!
+router zebra
+!
+! MP-BGP configuration
+!
+router bgp 7675
+ bgp router-id 10.0.0.1
+ neighbor 3ffe:1cfa:0:2:2a0:c9ff:fe9e:f56 remote-as @var{as-number}
+!
+ address-family ipv6
+ network 3ffe:506::/32
+ neighbor 3ffe:1cfa:0:2:2a0:c9ff:fe9e:f56 activate
+ neighbor 3ffe:1cfa:0:2:2a0:c9ff:fe9e:f56 route-map set-nexthop out
+ neighbor 3ffe:1cfa:0:2:2c0:4fff:fe68:a231 remote-as @var{as-number}
+ neighbor 3ffe:1cfa:0:2:2c0:4fff:fe68:a231 route-map set-nexthop out
+ exit-address-family
+!
+ipv6 access-list all permit any
+!
+! Set output nexthop address.
+!
+route-map set-nexthop permit 10
+ match ipv6 address all
+ set ipv6 nexthop global 3ffe:1cfa:0:2:2c0:4fff:fe68:a225
+ set ipv6 nexthop local fe80::2c0:4fff:fe68:a225
+!
+! logfile FILENAME is obsolete. Please use log file FILENAME
+
+log file bgpd.log
+!
+@end group
+@end example
+
+@node Dump BGP packets and table
+@section Dump BGP packets and table
+
+@deffn Command {dump bgp all @var{path} [@var{interval}]} {}
+@deffnx Command {dump bgp all-et @var{path} [@var{interval}]} {}
+@deffnx Command {no dump bgp all [@var{path}] [@var{interval}]} {}
+Dump all BGP packet and events to @var{path} file.
+If @var{interval} is set, a new file will be created for echo @var{interval} of seconds.
+The path @var{path} can be set with date and time formatting (strftime).
+The type ‘all-et’ enables support for Extended Timestamp Header (@pxref{Packet Binary Dump Format}).
+(@pxref{Packet Binary Dump Format})
+@end deffn
+
+@deffn Command {dump bgp updates @var{path} [@var{interval}]} {}
+@deffnx Command {dump bgp updates-et @var{path} [@var{interval}]} {}
+@deffnx Command {no dump bgp updates [@var{path}] [@var{interval}]} {}
+Dump only BGP updates messages to @var{path} file.
+If @var{interval} is set, a new file will be created for echo @var{interval} of seconds.
+The path @var{path} can be set with date and time formatting (strftime).
+The type ‘updates-et’ enables support for Extended Timestamp Header (@pxref{Packet Binary Dump Format}).
+@end deffn
+
+@deffn Command {dump bgp routes-mrt @var{path}} {}
+@deffnx Command {dump bgp routes-mrt @var{path} @var{interval}} {}
+@deffnx Command {no dump bgp route-mrt [@var{path}] [@var{interval}]} {}
+Dump whole BGP routing table to @var{path}. This is heavy process.
+The path @var{path} can be set with date and time formatting (strftime).
+If @var{interval} is set, a new file will be created for echo @var{interval} of seconds.
+@end deffn
+
+Note: the interval variable can also be set using hours and minutes: 04h20m00.
+
+
+@node BGP Configuration Examples
+@section BGP Configuration Examples
+
+Example of a session to an upstream, advertising only one prefix to it.
+
+@example
+router bgp 64512
+ bgp router-id 10.236.87.1
+ neighbor upstream peer-group
+ neighbor upstream remote-as 64515
+ neighbor upstream capability dynamic
+ neighbor 10.1.1.1 peer-group upstream
+ neighbor 10.1.1.1 description ACME ISP
+
+ address-family ipv4 unicast
+ network 10.236.87.0/24
+ neighbor upstream prefix-list pl-allowed-adv out
+ exit-address-family
+!
+ip prefix-list pl-allowed-adv seq 5 permit 82.195.133.0/25
+ip prefix-list pl-allowed-adv seq 10 deny any
+
+@end example
+
+A more complex example. With upstream, peer and customer sessions.
+Advertising global prefixes and NO_EXPORT prefixes and providing
+actions for customer routes based on community values. Extensive use of
+route-maps and the 'call' feature to support selective advertising of
+prefixes. This example is intended as guidance only, it has NOT been
+tested and almost certainly containts silly mistakes, if not serious
+flaws.
+
+@example
+router bgp 64512
+ bgp router-id 10.236.87.1
+ neighbor upstream capability dynamic
+ neighbor cust capability dynamic
+ neighbor peer capability dynamic
+ neighbor 10.1.1.1 remote-as 64515
+ neighbor 10.1.1.1 peer-group upstream
+ neighbor 10.2.1.1 remote-as 64516
+ neighbor 10.2.1.1 peer-group upstream
+ neighbor 10.3.1.1 remote-as 64517
+ neighbor 10.3.1.1 peer-group cust-default
+ neighbor 10.3.1.1 description customer1
+ neighbor 10.4.1.1 remote-as 64518
+ neighbor 10.4.1.1 peer-group cust
+ neighbor 10.4.1.1 description customer2
+ neighbor 10.5.1.1 remote-as 64519
+ neighbor 10.5.1.1 peer-group peer
+ neighbor 10.5.1.1 description peer AS 1
+ neighbor 10.6.1.1 remote-as 64520
+ neighbor 10.6.1.1 peer-group peer
+ neighbor 10.6.1.1 description peer AS 2
+
+ address-family ipv4 unicast
+ network 10.123.456.0/24
+ network 10.123.456.128/25 route-map rm-no-export
+ neighbor upstream route-map rm-upstream-out out
+ neighbor cust route-map rm-cust-in in
+ neighbor cust route-map rm-cust-out out
+ neighbor cust send-community both
+ neighbor peer route-map rm-peer-in in
+ neighbor peer route-map rm-peer-out out
+ neighbor peer send-community both
+ neighbor 10.3.1.1 prefix-list pl-cust1-network in
+ neighbor 10.4.1.1 prefix-list pl-cust2-network in
+ neighbor 10.5.1.1 prefix-list pl-peer1-network in
+ neighbor 10.6.1.1 prefix-list pl-peer2-network in
+ exit-address-family
+!
+ip prefix-list pl-default permit 0.0.0.0/0
+!
+ip prefix-list pl-upstream-peers permit 10.1.1.1/32
+ip prefix-list pl-upstream-peers permit 10.2.1.1/32
+!
+ip prefix-list pl-cust1-network permit 10.3.1.0/24
+ip prefix-list pl-cust1-network permit 10.3.2.0/24
+!
+ip prefix-list pl-cust2-network permit 10.4.1.0/24
+!
+ip prefix-list pl-peer1-network permit 10.5.1.0/24
+ip prefix-list pl-peer1-network permit 10.5.2.0/24
+ip prefix-list pl-peer1-network permit 192.168.0.0/24
+!
+ip prefix-list pl-peer2-network permit 10.6.1.0/24
+ip prefix-list pl-peer2-network permit 10.6.2.0/24
+ip prefix-list pl-peer2-network permit 192.168.1.0/24
+ip prefix-list pl-peer2-network permit 192.168.2.0/24
+ip prefix-list pl-peer2-network permit 172.16.1/24
+!
+ip as-path access-list asp-own-as permit ^$
+ip as-path access-list asp-own-as permit _64512_
+!
+! #################################################################
+! Match communities we provide actions for, on routes receives from
+! customers. Communities values of <our-ASN>:X, with X, have actions:
+!
+! 100 - blackhole the prefix
+! 200 - set no_export
+! 300 - advertise only to other customers
+! 400 - advertise only to upstreams
+! 500 - set no_export when advertising to upstreams
+! 2X00 - set local_preference to X00
+!
+! blackhole the prefix of the route
+ip community-list standard cm-blackhole permit 64512:100
+!
+! set no-export community before advertising
+ip community-list standard cm-set-no-export permit 64512:200
+!
+! advertise only to other customers
+ip community-list standard cm-cust-only permit 64512:300
+!
+! advertise only to upstreams
+ip community-list standard cm-upstream-only permit 64512:400
+!
+! advertise to upstreams with no-export
+ip community-list standard cm-upstream-noexport permit 64512:500
+!
+! set local-pref to least significant 3 digits of the community
+ip community-list standard cm-prefmod-100 permit 64512:2100
+ip community-list standard cm-prefmod-200 permit 64512:2200
+ip community-list standard cm-prefmod-300 permit 64512:2300
+ip community-list standard cm-prefmod-400 permit 64512:2400
+ip community-list expanded cme-prefmod-range permit 64512:2...
+!
+! Informational communities
+!
+! 3000 - learned from upstream
+! 3100 - learned from customer
+! 3200 - learned from peer
+!
+ip community-list standard cm-learnt-upstream permit 64512:3000
+ip community-list standard cm-learnt-cust permit 64512:3100
+ip community-list standard cm-learnt-peer permit 64512:3200
+!
+! ###################################################################
+! Utility route-maps
+!
+! These utility route-maps generally should not used to permit/deny
+! routes, i.e. they do not have meaning as filters, and hence probably
+! should be used with 'on-match next'. These all finish with an empty
+! permit entry so as not interfere with processing in the caller.
+!
+route-map rm-no-export permit 10
+ set community additive no-export
+route-map rm-no-export permit 20
+!
+route-map rm-blackhole permit 10
+ description blackhole, up-pref and ensure it cant escape this AS
+ set ip next-hop 127.0.0.1
+ set local-preference 10
+ set community additive no-export
+route-map rm-blackhole permit 20
+!
+! Set local-pref as requested
+route-map rm-prefmod permit 10
+ match community cm-prefmod-100
+ set local-preference 100
+route-map rm-prefmod permit 20
+ match community cm-prefmod-200
+ set local-preference 200
+route-map rm-prefmod permit 30
+ match community cm-prefmod-300
+ set local-preference 300
+route-map rm-prefmod permit 40
+ match community cm-prefmod-400
+ set local-preference 400
+route-map rm-prefmod permit 50
+!
+! Community actions to take on receipt of route.
+route-map rm-community-in permit 10
+ description check for blackholing, no point continuing if it matches.
+ match community cm-blackhole
+ call rm-blackhole
+route-map rm-community-in permit 20
+ match community cm-set-no-export
+ call rm-no-export
+ on-match next
+route-map rm-community-in permit 30
+ match community cme-prefmod-range
+ call rm-prefmod
+route-map rm-community-in permit 40
+!
+! #####################################################################
+! Community actions to take when advertising a route.
+! These are filtering route-maps,
+!
+! Deny customer routes to upstream with cust-only set.
+route-map rm-community-filt-to-upstream deny 10
+ match community cm-learnt-cust
+ match community cm-cust-only
+route-map rm-community-filt-to-upstream permit 20
+!
+! Deny customer routes to other customers with upstream-only set.
+route-map rm-community-filt-to-cust deny 10
+ match community cm-learnt-cust
+ match community cm-upstream-only
+route-map rm-community-filt-to-cust permit 20
+!
+! ###################################################################
+! The top-level route-maps applied to sessions. Further entries could
+! be added obviously..
+!
+! Customers
+route-map rm-cust-in permit 10
+ call rm-community-in
+ on-match next
+route-map rm-cust-in permit 20
+ set community additive 64512:3100
+route-map rm-cust-in permit 30
+!
+route-map rm-cust-out permit 10
+ call rm-community-filt-to-cust
+ on-match next
+route-map rm-cust-out permit 20
+!
+! Upstream transit ASes
+route-map rm-upstream-out permit 10
+ description filter customer prefixes which are marked cust-only
+ call rm-community-filt-to-upstream
+ on-match next
+route-map rm-upstream-out permit 20
+ description only customer routes are provided to upstreams/peers
+ match community cm-learnt-cust
+!
+! Peer ASes
+! outbound policy is same as for upstream
+route-map rm-peer-out permit 10
+ call rm-upstream-out
+!
+route-map rm-peer-in permit 10
+ set community additive 64512:3200
+@end example
+
+@include rpki.texi
--- /dev/null
+@node Installation
+@chapter Installation
+
+@cindex How to install Frr
+@cindex Installation
+@cindex Installing Frr
+@cindex Building the system
+@cindex Making Frr
+
+There are three steps for installing the software: configuration,
+compilation, and installation.
+
+@menu
+* Configure the Software::
+* Build the Software::
+* Install the Software::
+@end menu
+
+The easiest way to get Frr running is to issue the following
+commands:
+
+@example
+% configure
+% make
+% make install
+@end example
+
+@node Configure the Software
+@section Configure the Software
+
+@menu
+* The Configure script and its options::
+* Least-Privilege support::
+* Linux notes::
+@end menu
+
+@node The Configure script and its options
+@subsection The Configure script and its options
+
+@cindex Configuration options
+@cindex Options for configuring
+@cindex Build options
+@cindex Distribution configuration
+@cindex Options to @code{./configure}
+
+Frr has an excellent configure script which automatically detects most
+host configurations. There are several additional configure options to
+customize the build to include or exclude specific features and dependencies.
+
+@table @option
+@item --disable-zebra
+Do not build zebra daemon.
+@item --disable-ripd
+Do not build ripd.
+@item --disable-ripngd
+Do not build ripngd.
+@item --disable-ospfd
+Do not build ospfd.
+@item --disable-ospf6d
+Do not build ospf6d.
+@item --disable-bgpd
+Do not build bgpd.
+@item --disable-bgp-announce
+Make @command{bgpd} which does not make bgp announcements at all. This
+feature is good for using @command{bgpd} as a BGP announcement listener.
+@item --enable-datacenter
+Enable system defaults to work as if in a Data Center. See defaults.h
+for what is changed by this configure option.
+@item --enable-snmp
+Enable SNMP support. By default, SNMP support is disabled.
+@item --disable-ospfapi
+Disable support for OSPF-API, an API to interface directly with ospfd.
+OSPF-API is enabled if --enable-opaque-lsa is set.
+@item --disable-ospfclient
+Disable building of the example OSPF-API client.
+@item --disable-ospf-ri
+Disable support for OSPF Router Information (RFC4970 & RFC5088) this
+requires support for Opaque LSAs and Traffic Engineering.
+@item --disable-isisd
+Do not build isisd.
+@item --enable-isis-topology
+Enable IS-IS topology generator.
+@item --enable-isis-te
+Enable Traffic Engineering Extension for ISIS (RFC5305)
+@item --enable-multipath=@var{ARG}
+Enable support for Equal Cost Multipath. @var{ARG} is the maximum number
+of ECMP paths to allow, set to 0 to allow unlimited number of paths.
+@item --enable-realms
+Enable the support of linux Realms. Convert tag values from 1-255
+into a realm value when inserting into the linux kernel. Then
+routing policy can be assigned to the realm. See the tc man page.
+@item --disable-rtadv
+Disable support IPV6 router advertisement in zebra.
+@item --enable-gcc-rdynamic
+Pass the @command{-rdynamic} option to the linker driver. This is in most
+cases neccessary for getting usable backtraces. This option defaults to on
+if the compiler is detected as gcc, but giving an explicit enable/disable is
+suggested.
+@item --disable-backtrace
+Controls backtrace support for the crash handlers. This is autodetected by
+default. Using the switch will enforce the requested behaviour, failing with
+an error if support is requested but not available. On BSD systems, this
+needs libexecinfo, while on glibc support for this is part of libc itself.
+@item --enable-dev-build
+Turn on some options for compiling FRR within a development environment in
+mind. Specifically turn on -g3 -O0 for compiling options and add inclusion
+of grammar sandbox.
+@item --enable-fuzzing
+Turn on some compile options to allow you to run fuzzing tools
+against the system. This tools is intended as a developer
+only tool and should not be used for normal operations
+@end table
+
+You may specify any combination of the above options to the configure
+script. By default, the executables are placed in @file{/usr/local/sbin}
+and the configuration files in @file{/usr/local/etc}. The @file{/usr/local/}
+installation prefix and other directories may be changed using the following
+options to the configuration script.
+
+@table @option
+@item --prefix=@var{prefix}
+Install architecture-independent files in @var{prefix} [/usr/local].
+@item --sysconfdir=@var{dir}
+Look for configuration files in @var{dir} [@var{prefix}/etc]. Note
+that sample configuration files will be installed here.
+@item --localstatedir=@var{dir}
+Configure zebra to use @var{dir} for local state files, such
+as pid files and unix sockets.
+@end table
+
+@example
+% ./configure --disable-snmp
+@end example
+
+This command will configure zebra and the routing daemons.
+
+@node Least-Privilege support
+@subsection Least-Privilege support
+
+@cindex Frr Least-Privileges
+@cindex Frr Privileges
+
+Additionally, you may configure zebra to drop its elevated privileges
+shortly after startup and switch to another user. The configure script will
+automatically try to configure this support. There are three configure
+options to control the behaviour of Frr daemons.
+
+@table @option
+@item --enable-user=@var{user}
+Switch to user @var{ARG} shortly after startup, and run as user @var{ARG}
+in normal operation.
+@item --enable-group=@var{group}
+Switch real and effective group to @var{group} shortly after
+startup.
+@item --enable-vty-group=@var{group}
+Create Unix Vty sockets (for use with vtysh) with group owndership set to
+@var{group}. This allows one to create a seperate group which is
+restricted to accessing only the Vty sockets, hence allowing one to
+delegate this group to individual users, or to run vtysh setgid to
+this group.
+@end table
+
+The default user and group which will be configured is 'frr' if no user
+or group is specified. Note that this user or group requires write access to
+the local state directory (see --localstatedir) and requires at least read
+access, and write access if you wish to allow daemons to write out their
+configuration, to the configuration directory (see --sysconfdir).
+
+On systems which have the 'libcap' capabilities manipulation library
+(currently only linux), the frr system will retain only minimal
+capabilities required, further it will only raise these capabilities for
+brief periods. On systems without libcap, frr will run as the user
+specified and only raise its uid back to uid 0 for brief periods.
+
+@node Linux notes
+@subsection Linux Notes
+
+@cindex Configuring Frr
+@cindex Building on Linux boxes
+@cindex Linux configurations
+
+There are several options available only to @sc{gnu}/Linux systems:
+@footnote{@sc{gnu}/Linux has very flexible kernel configuration features}. If
+you use @sc{gnu}/Linux, make sure that the current kernel configuration is
+what you want. Frr will run with any kernel configuration but some
+recommendations do exist.
+
+@table @var
+
+@item CONFIG_NETLINK
+Kernel/User netlink socket. This is a brand new feature which enables an
+advanced interface between the Linux kernel and zebra (@pxref{Kernel Interface}).
+
+@item CONFIG_RTNETLINK
+Routing messages.
+This makes it possible to receive netlink routing messages. If you
+specify this option, @command{zebra} can detect routing information
+updates directly from the kernel (@pxref{Kernel Interface}).
+
+@item CONFIG_IP_MULTICAST
+IP: multicasting.
+This option should be specified when you use @command{ripd} (@pxref{RIP}) or
+@command{ospfd} (@pxref{OSPFv2}) because these protocols use multicast.
+
+@end table
+
+IPv6 support has been added in @sc{gnu}/Linux kernel version 2.2. If you
+try to use the Frr IPv6 feature on a @sc{gnu}/Linux kernel, please
+make sure the following libraries have been installed. Please note that
+these libraries will not be needed when you uses @sc{gnu} C library 2.1
+or upper.
+
+@table @code
+
+@item inet6-apps
+The @code{inet6-apps} package includes basic IPv6 related libraries such
+as @code{inet_ntop} and @code{inet_pton}. Some basic IPv6 programs such
+as @command{ping}, @command{ftp}, and @command{inetd} are also
+included. The @code{inet-apps} can be found at
+@uref{ftp://ftp.inner.net/pub/ipv6/}.
+
+@item net-tools
+The @code{net-tools} package provides an IPv6 enabled interface and
+routing utility. It contains @command{ifconfig}, @command{route},
+@command{netstat}, and other tools. @code{net-tools} may be found at
+@uref{http://www.tazenda.demon.co.uk/phil/net-tools/}.
+
+@end table
+@c A - end of footnote
+
+@node Build the Software
+@section Build the Software
+
+After configuring the software, you will need to compile it for your
+system. Simply issue the command @command{make} in the root of the source
+directory and the software will be compiled. Cliff Note versions of
+different compilation examples can be found in the doc/Building_FRR_on_XXX.md
+files. If you have *any* problems at this stage, be certain to send a
+bug report @xref{Bug Reports}.
+
+@example
+% ./bootstrap.sh
+% ./configure <appropriate to your system>
+% make
+@end example
+@c A - End of node, Building the Software
+
+
+@node Install the Software
+@comment node-name, next, previous, up
+@section Install the Software
+
+Installing the software to your system consists of copying the compiled
+programs and supporting files to a standard location. After the
+installation process has completed, these files have been copied
+from your work directory to @file{/usr/local/bin}, and @file{/usr/local/etc}.
+
+To install the Frr suite, issue the following command at your shell
+prompt: @command{make install}.
+
+@example
+%
+% make install
+%
+@end example
+
+Frr daemons have their own terminal interface or VTY. After
+installation, you have to setup each beast's port number to connect to
+them. Please add the following entries to @file{/etc/services}.
+
+@example
+zebrasrv 2600/tcp # zebra service
+zebra 2601/tcp # zebra vty
+ripd 2602/tcp # RIPd vty
+ripngd 2603/tcp # RIPngd vty
+ospfd 2604/tcp # OSPFd vty
+bgpd 2605/tcp # BGPd vty
+ospf6d 2606/tcp # OSPF6d vty
+ospfapi 2607/tcp # ospfapi
+isisd 2608/tcp # ISISd vty
+nhrpd 2610/tcp # nhrpd vty
+pimd 2611/tcp # PIMd vty
+@end example
+
+If you use a FreeBSD newer than 2.2.8, the above entries are already
+added to @file{/etc/services} so there is no need to add it. If you
+specify a port number when starting the daemon, these entries may not be
+needed.
+
+You may need to make changes to the config files in
+@file{@value{INSTALL_PREFIX_ETC}/*.conf}. @xref{Config Commands}.
--- /dev/null
+@cindex ISIS
+@node ISIS
+@chapter ISIS
+
+@acronym{ISIS,Intermediate System to Intermediate System} is a routing protocol
+which is described in @cite{ISO10589, RFC1195, RFC5308}. ISIS is an
+@acronym{IGP,Interior Gateway Protocol}. Compared with @acronym{RIP},
+@acronym{ISIS} can provide scalable network support and faster
+convergence times like @acronym{OSPF}. ISIS is widely used in large networks such as
+@acronym{ISP,Internet Service Provider} and carrier backbone networks.
+
+@menu
+* Configuring isisd::
+* ISIS router::
+* ISIS Timer::
+* ISIS region::
+* ISIS interface::
+* Showing ISIS information::
+* ISIS Traffic Engineering::
+* Debugging ISIS::
+* ISIS Configuration Examples::
+@end menu
+
+@node Configuring isisd
+@section Configuring isisd
+
+There are no @command{isisd} specific options. Common options can be
+specified (@pxref{Common Invocation Options}) to @command{isisd}.
+@command{isisd} needs to acquire interface information from
+@command{zebra} in order to function. Therefore @command{zebra} must be
+running before invoking @command{isisd}. Also, if @command{zebra} is
+restarted then @command{isisd} must be too.
+
+Like other daemons, @command{isisd} configuration is done in @acronym{ISIS}
+specific configuration file @file{isisd.conf}.
+
+@node ISIS router
+@section ISIS router
+
+To start ISIS process you have to specify the ISIS router. As of this
+writing, @command{isisd} does not support multiple ISIS processes.
+
+@deffn Command {router isis WORD} {}
+@deffnx Command {no router isis WORD} {}
+@anchor{router isis WORD}Enable or disable the ISIS process by specifying the ISIS domain with 'WORD'.
+@command{isisd} does not yet support multiple ISIS processes but you must specify
+the name of ISIS process. The ISIS process name 'WORD' is then used for interface
+(see command @ref{ip router isis WORD}).
+@end deffn
+
+@deffn {ISIS Command} {net XX.XXXX. ... .XXX.XX} {}
+@deffnx {ISIS Command} {no net XX.XXXX. ... .XXX.XX} {}
+Set/Unset network entity title (NET) provided in ISO format.
+@end deffn
+
+@deffn {ISIS Command} {hostname dynamic} {}
+@deffnx {ISIS Command} {no hostname dynamic} {}
+Enable support for dynamic hostname.
+@end deffn
+
+@deffn {ISIS Command} {area-password [clear | md5] <password>} {}
+@deffnx {ISIS Command} {domain-password [clear | md5] <password>} {}
+@deffnx {ISIS Command} {no area-password} {}
+@deffnx {ISIS Command} {no domain-password} {}
+Configure the authentication password for an area, respectively a domain,
+as clear text or md5 one.
+@end deffn
+
+@deffn {ISIS Command} {log-adjacency-changes} {}
+@deffnx {ISIS Command} {no log-adjacency-changes} {}
+Log changes in adjacency state.
+@end deffn
+
+@deffn {ISIS Command} {metric-style [narrow | transition | wide]} {}
+@deffnx {ISIS Command} {no metric-style} {}
+@anchor{metric-style}Set old-style (ISO 10589) or new-style packet formats:
+ - narrow Use old style of TLVs with narrow metric
+ - transition Send and accept both styles of TLVs during transition
+ - wide Use new style of TLVs to carry wider metric
+@end deffn
+
+@deffn {ISIS Command} {set-overload-bit} {}
+@deffnx {ISIS Command} {no set-overload-bit} {}
+Set overload bit to avoid any transit traffic.
+@end deffn
+
+@node ISIS Timer
+@section ISIS Timer
+
+@deffn {ISIS Command} {lsp-gen-interval <1-120>} {}
+@deffnx {ISIS Command} {lsp-gen-interval [level-1 | level-2] <1-120>} {}
+@deffnx {ISIS Command} {no lsp-gen-interval} {}
+@deffnx {ISIS Command} {no lsp-gen-interval [level-1 | level-2]} {}
+Set minimum interval in seconds between regenerating same LSP,
+globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {ISIS Command} {lsp-refresh-interval <1-65235>} {}
+@deffnx {ISIS Command} {lsp-refresh-interval [level-1 | level-2] <1-65235>} {}
+@deffnx {ISIS Command} {no lsp-refresh-interval} {}
+@deffnx {ISIS Command} {no lsp-refresh-interval [level-1 | level-2]} {}
+Set LSP refresh interval in seconds, globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {ISIS Command} {lsp-refresh-interval <1-65235>} {}
+@deffnx {ISIS Command} {lsp-refresh-interval [level-1 | level-2] <1-65235>} {}
+@deffnx {ISIS Command} {no lsp-refresh-interval} {}
+@deffnx {ISIS Command} {no lsp-refresh-interval [level-1 | level-2]} {}
+Set LSP refresh interval in seconds, globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {ISIS Command} {max-lsp-lifetime <360-65535>} {}
+@deffnx {ISIS Command} {max-lsp-lifetime [level-1 | level-2] <360-65535>} {}
+@deffnx {ISIS Command} {no max-lsp-lifetime} {}
+@deffnx {ISIS Command} {no max-lsp-lifetime [level-1 | level-2]} {}
+Set LSP maximum LSP lifetime in seconds, globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {ISIS Command} {spf-interval <1-120>} {}
+@deffnx {ISIS Command} {spf-interval [level-1 | level-2] <1-120>} {}
+@deffnx {ISIS Command} {no spf-interval} {}
+@deffnx {ISIS Command} {no spf-interval [level-1 | level-2]} {}
+Set minimum interval between consecutive SPF calculations in seconds.
+@end deffn
+
+@node ISIS region
+@section ISIS region
+
+@deffn {ISIS Command} {is-type [level-1 | level-1-2 | level-2-only]} {}
+@deffnx {ISIS Command} {no is-type} {}
+Define the ISIS router behavior:
+ - level-1 Act as a station router only
+ - level-1-2 Act as both a station router and an area router
+ - level-2-only Act as an area router only
+@end deffn
+
+@node ISIS interface
+@section ISIS interface
+
+@deffn {Interface Command} {ip router isis WORD} {}
+@deffnx {Interface Command} {no ip router isis WORD} {}
+@anchor{ip router isis WORD}Activate ISIS adjacency on this interface. Note that the name
+of ISIS instance must be the same as the one used to configure the ISIS process
+(see command @ref{router isis WORD}).
+@end deffn
+
+@deffn {Interface Command} {isis circuit-type [level-1 | level-1-2 | level-2]} {}
+@deffnx {Interface Command} {no isis circuit-type} {}
+Configure circuit type for interface:
+ - level-1 Level-1 only adjacencies are formed
+ - level-1-2 Level-1-2 adjacencies are formed
+ - level-2-only Level-2 only adjacencies are formed
+@end deffn
+
+@deffn {Interface Command} {isis csnp-interval <1-600>} {}
+@deffnx {Interface Command} {isis csnp-interval <1-600> [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis csnp-interval} {}
+@deffnx {Interface Command} {no isis csnp-interval [level-1 | level-2]} {}
+Set CSNP interval in seconds globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {Interface Command} {isis hello padding} {}
+Add padding to IS-IS hello packets.
+@end deffn
+
+@deffn {Interface Command} {isis hello-interval <1-600>} {}
+@deffnx {Interface Command} {isis hello-interval <1-600> [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis hello-interval} {}
+@deffnx {Interface Command} {no isis hello-interval [level-1 | level-2]} {}
+Set Hello interval in seconds globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {Interface Command} {isis hello-multiplier <2-100>} {}
+@deffnx {Interface Command} {isis hello-multiplier <2-100> [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis hello-multiplier} {}
+@deffnx {Interface Command} {no isis hello-multiplier [level-1 | level-2]} {}
+Set multiplier for Hello holding time globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@deffn {Interface Command} {isis metric [<0-255> | <0-16777215>]} {}
+@deffnx {Interface Command} {isis metric [<0-255> | <0-16777215>] [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis metric} {}
+@deffnx {Interface Command} {no isis metric [level-1 | level-2]} {}
+Set default metric value globally, for an area (level-1) or a domain (level-2).
+Max value depend if metric support narrow or wide value (see command @ref{metric-style}).
+@end deffn
+
+@deffn {Interface Command} {isis network point-to-point} {}
+@deffnx {Interface Command} {no isis network point-to-point} {}
+Set network type to 'Point-to-Point' (broadcast by default).
+@end deffn
+
+@deffn {Interface Command} {isis passive} {}
+@deffnx {Interface Command} {no isis passive} {}
+Configure the passive mode for this interface.
+@end deffn
+
+@deffn {Interface Command} {isis password [clear | md5] <password>} {}
+@deffnx {Interface Command} {no isis password} {}
+Configure the authentication password (clear or encoded text) for the interface.
+@end deffn
+
+@deffn {Interface Command} {isis priority <0-127>} {}
+@deffnx {Interface Command} {isis priority <0-127> [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis priority} {}
+@deffnx {Interface Command} {no isis priority [level-1 | level-2]} {}
+Set priority for Designated Router election, globally, for the area (level-1)
+or the domain (level-2).
+@end deffn
+
+@deffn {Interface Command} {isis psnp-interval <1-120>} {}
+@deffnx {Interface Command} {isis psnp-interval <1-120> [level-1 | level-2]} {}
+@deffnx {Interface Command} {no isis psnp-interval} {}
+@deffnx {Interface Command} {no isis psnp-interval [level-1 | level-2]} {}
+Set PSNP interval in seconds globally, for an area (level-1) or a domain (level-2).
+@end deffn
+
+@node Showing ISIS information
+@section Showing ISIS information
+
+@deffn {Command} {show isis summary} {}
+Show summary information about ISIS.
+@end deffn
+
+@deffn {Command} {show isis hostname} {}
+Show information about ISIS node.
+@end deffn
+
+@deffn {Command} {show isis interface} {}
+@deffnx {Command} {show isis interface detail} {}
+@deffnx {Command} {show isis interface <interface name>} {}
+Show state and configuration of ISIS specified interface, or all
+interfaces if no interface is given with or without details.
+@end deffn
+
+@deffn {Command} {show isis neighbor} {}
+@deffnx {Command} {show isis neighbor <System Id>} {}
+@deffnx {Command} {show isis neighbor detail} {}
+Show state and information of ISIS specified neighbor, or all
+neighbors if no system id is given with or without details.
+@end deffn
+
+@deffn {Command} {show isis database} {}
+@deffnx {Command} {show isis database [detail]} {}
+@deffnx {Command} {show isis database <LSP id> [detail]} {}
+@deffnx {Command} {show isis database detail <LSP id>} {}
+Show the ISIS database globally, for a specific LSP id without or with details.
+@end deffn
+
+@deffn {Command} {show isis topology} {}
+@deffnx {Command} {show isis topology [level-1|level-2]} {}
+Show topology IS-IS paths to Intermediate Systems, globally,
+in area (level-1) or domain (level-2).
+@end deffn
+
+@deffn {Command} {show ip route isis} {}
+Show the ISIS routing table, as determined by the most recent SPF calculation.
+@end deffn
+
+@node ISIS Traffic Engineering
+@section Traffic Engineering
+
+@deffn {ISIS Command} {mpls-te on} {}
+@deffnx {ISIS Command} {no mpls-te} {}
+Enable Traffic Engineering LSP flooding.
+@end deffn
+
+@deffn {ISIS Command} {mpls-te router-address <A.B.C.D>} {}
+@deffnx {ISIS Command} {no mpls-te router-address} {}
+Configure stable IP address for MPLS-TE.
+@end deffn
+
+@deffn {Command} {show isis mpls-te interface} {}
+@deffnx {Command} {show isis mpls-te interface @var{interface}} {}
+Show MPLS Traffic Engineering parameters for all or specified interface.
+@end deffn
+
+@deffn {Command} {show isis mpls-te router} {}
+Show Traffic Engineering router parameters.
+@end deffn
+
+@node Debugging ISIS
+@section Debugging ISIS
+
+@deffn {Command} {debug isis adj-packets} {}
+@deffnx {Command} {no debug isis adj-packets} {}
+IS-IS Adjacency related packets.
+@end deffn
+
+@deffn {Command} {debug isis checksum-errors} {}
+@deffnx {Command} {no debug isis checksum-errors} {}
+IS-IS LSP checksum errors.
+@end deffn
+
+@deffn {Command} {debug isis events} {}
+@deffnx {Command} {no debug isis events} {}
+IS-IS Events.
+@end deffn
+
+@deffn {Command} {debug isis local-updates} {}
+@deffnx {Command} {no debug isis local-updates} {}
+IS-IS local update packets.
+@end deffn
+
+@deffn {Command} {debug isis packet-dump} {}
+@deffnx {Command} {no debug isis packet-dump} {}
+IS-IS packet dump.
+@end deffn
+
+@deffn {Command} {debug isis protocol-errors} {}
+@deffnx {Command} {no debug isis protocol-errors} {}
+IS-IS LSP protocol errors.
+@end deffn
+
+@deffn {Command} {debug isis route-events} {}
+@deffnx {Command} {no debug isis route-events} {}
+IS-IS Route related events.
+@end deffn
+
+@deffn {Command} {debug isis snp-packets} {}
+@deffnx {Command} {no debug isis snp-packets} {}
+IS-IS CSNP/PSNP packets.
+@end deffn
+
+@deffn {Command} {debug isis spf-events} {}
+@deffnx {Command} {debug isis spf-statistics} {}
+@deffnx {Command} {debug isis spf-triggers} {}
+@deffnx {Command} {no debug isis spf-events} {}
+@deffnx {Command} {no debug isis spf-statistics} {}
+@deffnx {Command} {no debug isis spf-triggers} {}
+IS-IS Shortest Path First Events, Timing and Statistic Data
+and triggering events.
+@end deffn
+
+@deffn {Command} {debug isis update-packets} {}
+@deffnx {Command} {no debug isis update-packets} {}
+Update related packets.
+@end deffn
+
+@deffn {Command} {show debugging isis} {}
+Print which ISIS debug level is activate.
+@end deffn
+
+@node ISIS Configuration Examples
+@section ISIS Configuration Examples
+A simple example, with MD5 authentication enabled:
+
+@example
+@group
+!
+interface eth0
+ ip router isis FOO
+ isis network point-to-point
+ isis circuit-type level-2-only
+!
+router isis FOO
+net 47.0023.0000.0000.0000.0000.0000.0000.1900.0004.00
+ metric-style wide
+ is-type level-2-only
+@end group
+@end example
+
+
+A Traffic Engineering configuration, with Inter-ASv2 support.
+
+ - First, the 'zebra.conf' part:
+
+@example
+@group
+hostname HOSTNAME
+password PASSWORD
+log file /var/log/zebra.log
+!
+interface eth0
+ ip address 10.2.2.2/24
+ link-params
+ enable
+ metric 100
+ max-bw 1.25e+07
+ max-rsv-bw 1.25e+06
+ unrsv-bw 0 1.25e+06
+ unrsv-bw 1 1.25e+06
+ unrsv-bw 2 1.25e+06
+ unrsv-bw 3 1.25e+06
+ unrsv-bw 4 1.25e+06
+ unrsv-bw 5 1.25e+06
+ unrsv-bw 6 1.25e+06
+ unrsv-bw 7 1.25e+06
+ admin-grp 0xab
+!
+interface eth1
+ ip address 10.1.1.1/24
+ link-params
+ enable
+ metric 100
+ max-bw 1.25e+07
+ max-rsv-bw 1.25e+06
+ unrsv-bw 0 1.25e+06
+ unrsv-bw 1 1.25e+06
+ unrsv-bw 2 1.25e+06
+ unrsv-bw 3 1.25e+06
+ unrsv-bw 4 1.25e+06
+ unrsv-bw 5 1.25e+06
+ unrsv-bw 6 1.25e+06
+ unrsv-bw 7 1.25e+06
+ neighbor 10.1.1.2 as 65000
+@end group
+@end example
+
+ - Then the 'isisd.conf' itself:
+
+@example
+@group
+hostname HOSTNAME
+password PASSWORD
+log file /var/log/isisd.log
+!
+!
+interface eth0
+ ip router isis FOO
+!
+interface eth1
+ ip router isis FOO
+!
+!
+router isis FOO
+ isis net 47.0023.0000.0000.0000.0000.0000.0000.1900.0004.00
+ mpls-te on
+ mpls-te router-address 10.1.1.1
+!
+line vty
+@end group
+@end example
--- /dev/null
+
+@cindex OSPFv2
+@node OSPFv2
+@chapter OSPFv2
+
+@acronym{OSPF,Open Shortest Path First} version 2 is a routing protocol
+which is described in @cite{RFC2328, OSPF Version 2}. OSPF is an
+@acronym{IGP,Interior Gateway Protocol}. Compared with @acronym{RIP},
+@acronym{OSPF} can provide scalable network support and faster
+convergence times. OSPF is widely used in large networks such as
+@acronym{ISP,Internet Service Provider} backbone and enterprise
+networks.
+
+@menu
+* OSPF Fundamentals::
+* Configuring ospfd::
+* OSPF router::
+* OSPF area::
+* OSPF interface::
+* Redistribute routes to OSPF::
+* Showing OSPF information::
+* Opaque LSA::
+* OSPF Traffic Engineering::
+* Router Information::
+* Debugging OSPF::
+* OSPF Configuration Examples::
+@end menu
+
+@include ospf_fundamentals.texi
+
+@node Configuring ospfd
+@section Configuring ospfd
+
+There are no @command{ospfd} specific options. Common options can be
+specified (@pxref{Common Invocation Options}) to @command{ospfd}.
+@command{ospfd} needs to acquire interface information from
+@command{zebra} in order to function. Therefore @command{zebra} must be
+running before invoking @command{ospfd}. Also, if @command{zebra} is
+restarted then @command{ospfd} must be too.
+
+Like other daemons, @command{ospfd} configuration is done in @acronym{OSPF}
+specific configuration file @file{ospfd.conf}.
+
+@node OSPF router
+@section OSPF router
+
+To start OSPF process you have to specify the OSPF router. As of this
+writing, @command{ospfd} does not support multiple OSPF processes.
+
+@deffn Command {router ospf} {}
+@deffnx Command {no router ospf} {}
+Enable or disable the OSPF process. @command{ospfd} does not yet
+support multiple OSPF processes. So you can not specify an OSPF process
+number.
+@end deffn
+
+@deffn {OSPF Command} {ospf router-id @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {no ospf router-id} {}
+@anchor{ospf router-id}This sets the router-ID of the OSPF process. The
+router-ID may be an IP address of the router, but need not be - it can
+be any arbitrary 32bit number. However it MUST be unique within the
+entire OSPF domain to the OSPF speaker - bad things will happen if
+multiple OSPF speakers are configured with the same router-ID! If one
+is not specified then @command{ospfd} will obtain a router-ID
+automatically from @command{zebra}.
+@end deffn
+
+@deffn {OSPF Command} {ospf abr-type @var{type}} {}
+@deffnx {OSPF Command} {no ospf abr-type @var{type}} {}
+@var{type} can be cisco|ibm|shortcut|standard. The "Cisco" and "IBM" types
+are equivalent.
+
+The OSPF standard for ABR behaviour does not allow an ABR to consider
+routes through non-backbone areas when its links to the backbone are
+down, even when there are other ABRs in attached non-backbone areas
+which still can reach the backbone - this restriction exists primarily
+to ensure routing-loops are avoided.
+
+With the "Cisco" or "IBM" ABR type, the default in this release of
+Frr, this restriction is lifted, allowing an ABR to consider
+summaries learnt from other ABRs through non-backbone areas, and hence
+route via non-backbone areas as a last resort when, and only when,
+backbone links are down.
+
+Note that areas with fully-adjacent virtual-links are considered to be
+"transit capable" and can always be used to route backbone traffic, and
+hence are unaffected by this setting (@pxref{OSPF virtual-link}).
+
+More information regarding the behaviour controlled by this command can
+be found in @cite{RFC 3509, Alternative Implementations of OSPF Area
+Border Routers}, and @cite{draft-ietf-ospf-shortcut-abr-02.txt}.
+
+Quote: "Though the definition of the @acronym{ABR,Area Border Router}
+in the OSPF specification does not require a router with multiple
+attached areas to have a backbone connection, it is actually
+necessary to provide successful routing to the inter-area and
+external destinations. If this requirement is not met, all traffic
+destined for the areas not connected to such an ABR or out of the
+OSPF domain, is dropped. This document describes alternative ABR
+behaviors implemented in Cisco and IBM routers."
+@end deffn
+
+@deffn {OSPF Command} {ospf rfc1583compatibility} {}
+@deffnx {OSPF Command} {no ospf rfc1583compatibility} {}
+@cite{RFC2328}, the sucessor to @cite{RFC1583}, suggests according
+to section G.2 (changes) in section 16.4 a change to the path
+preference algorithm that prevents possible routing loops that were
+possible in the old version of OSPFv2. More specifically it demands
+that inter-area paths and intra-area backbone path are now of equal preference
+but still both preferred to external paths.
+
+This command should NOT be set normally.
+@end deffn
+
+@deffn {OSPF Command} {log-adjacency-changes [detail]} {}
+@deffnx {OSPF Command} {no log-adjacency-changes [detail]} {}
+Configures ospfd to log changes in adjacency. With the optional
+detail argument, all changes in adjacency status are shown. Without detail,
+only changes to full or regressions are shown.
+@end deffn
+
+@deffn {OSPF Command} {passive-interface @var{interface}} {}
+@deffnx {OSPF Command} {no passive-interface @var{interface}} {}
+@anchor{OSPF passive-interface} Do not speak OSPF interface on the
+given interface, but do advertise the interface as a stub link in the
+router-@acronym{LSA,Link State Advertisement} for this router. This
+allows one to advertise addresses on such connected interfaces without
+having to originate AS-External/Type-5 LSAs (which have global flooding
+scope) - as would occur if connected addresses were redistributed into
+OSPF (@pxref{Redistribute routes to OSPF})@. This is the only way to
+advertise non-OSPF links into stub areas.
+@end deffn
+
+@deffn {OSPF Command} {timers throttle spf @var{delay} @var{initial-holdtime} @var{max-holdtime}} {}
+@deffnx {OSPF Command} {no timers throttle spf} {}
+This command sets the initial @var{delay}, the @var{initial-holdtime}
+and the @var{maximum-holdtime} between when SPF is calculated and the
+event which triggered the calculation. The times are specified in
+milliseconds and must be in the range of 0 to 600000 milliseconds.
+
+The @var{delay} specifies the minimum amount of time to delay SPF
+calculation (hence it affects how long SPF calculation is delayed after
+an event which occurs outside of the holdtime of any previous SPF
+calculation, and also serves as a minimum holdtime).
+
+Consecutive SPF calculations will always be seperated by at least
+'hold-time' milliseconds. The hold-time is adaptive and initially is
+set to the @var{initial-holdtime} configured with the above command.
+Events which occur within the holdtime of the previous SPF calculation
+will cause the holdtime to be increased by @var{initial-holdtime}, bounded
+by the @var{maximum-holdtime} configured with this command. If the adaptive
+hold-time elapses without any SPF-triggering event occuring then
+the current holdtime is reset to the @var{initial-holdtime}. The current
+holdtime can be viewed with @ref{show ip ospf}, where it is expressed as
+a multiplier of the @var{initial-holdtime}.
+
+@example
+@group
+router ospf
+ timers throttle spf 200 400 10000
+@end group
+@end example
+
+In this example, the @var{delay} is set to 200ms, the @var{initial
+holdtime} is set to 400ms and the @var{maximum holdtime} to 10s. Hence
+there will always be at least 200ms between an event which requires SPF
+calculation and the actual SPF calculation. Further consecutive SPF
+calculations will always be seperated by between 400ms to 10s, the
+hold-time increasing by 400ms each time an SPF-triggering event occurs
+within the hold-time of the previous SPF calculation.
+
+This command supercedes the @command{timers spf} command in previous Frr
+releases.
+@end deffn
+
+@deffn {OSPF Command} {max-metric router-lsa [on-startup|on-shutdown] <5-86400>} {}
+@deffnx {OSPF Command} {max-metric router-lsa administrative} {}
+@deffnx {OSPF Command} {no max-metric router-lsa [on-startup|on-shutdown|administrative]} {}
+This enables @cite{RFC3137, OSPF Stub Router Advertisement} support,
+where the OSPF process describes its transit links in its router-LSA as
+having infinite distance so that other routers will avoid calculating
+transit paths through the router while still being able to reach
+networks through the router.
+
+This support may be enabled administratively (and indefinitely) or
+conditionally. Conditional enabling of max-metric router-lsas can be
+for a period of seconds after startup and/or for a period of seconds
+prior to shutdown.
+
+Enabling this for a period after startup allows OSPF to converge fully
+first without affecting any existing routes used by other routers,
+while still allowing any connected stub links and/or redistributed
+routes to be reachable. Enabling this for a period of time in advance
+of shutdown allows the router to gracefully excuse itself from the OSPF
+domain.
+
+Enabling this feature administratively allows for administrative
+intervention for whatever reason, for an indefinite period of time.
+Note that if the configuration is written to file, this administrative
+form of the stub-router command will also be written to file. If
+@command{ospfd} is restarted later, the command will then take effect
+until manually deconfigured.
+
+Configured state of this feature as well as current status, such as the
+number of second remaining till on-startup or on-shutdown ends, can be
+viewed with the @ref{show ip ospf} command.
+@end deffn
+
+@deffn {OSPF Command} {auto-cost reference-bandwidth <1-4294967>} {}
+@deffnx {OSPF Command} {no auto-cost reference-bandwidth} {}
+@anchor{OSPF auto-cost reference-bandwidth}This sets the reference
+bandwidth for cost calculations, where this bandwidth is considered
+equivalent to an OSPF cost of 1, specified in Mbits/s. The default is
+100Mbit/s (i.e. a link of bandwidth 100Mbit/s or higher will have a
+cost of 1. Cost of lower bandwidth links will be scaled with reference
+to this cost).
+
+This configuration setting MUST be consistent across all routers within the
+OSPF domain.
+@end deffn
+
+@deffn {OSPF Command} {network @var{a.b.c.d/m} area @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {network @var{a.b.c.d/m} area @var{<0-4294967295>}} {}
+@deffnx {OSPF Command} {no network @var{a.b.c.d/m} area @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {no network @var{a.b.c.d/m} area @var{<0-4294967295>}} {}
+@anchor{OSPF network command}
+This command specifies the OSPF enabled interface(s). If the interface has
+an address from range 192.168.1.0/24 then the command below enables ospf
+on this interface so router can provide network information to the other
+ospf routers via this interface.
+
+@example
+@group
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+@end group
+@end example
+
+Prefix length in interface must be equal or bigger (ie. smaller network) than
+prefix length in network statement. For example statement above doesn't enable
+ospf on interface with address 192.168.1.1/23, but it does on interface with
+address 192.168.1.129/25.
+
+Note that the behavior when there is a peer address
+defined on an interface changed after release 0.99.7.
+Currently, if a peer prefix has been configured,
+then we test whether the prefix in the network command contains
+the destination prefix. Otherwise, we test whether the network command prefix
+contains the local address prefix of the interface.
+
+In some cases it may be more convenient to enable OSPF on a per
+interface/subnet basis (@pxref{OSPF ip ospf area command}).
+
+@end deffn
+
+@node OSPF area
+@section OSPF area
+
+@deffn {OSPF Command} {area @var{a.b.c.d} range @var{a.b.c.d/m}} {}
+@deffnx {OSPF Command} {area <0-4294967295> range @var{a.b.c.d/m}} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} range @var{a.b.c.d/m}} {}
+@deffnx {OSPF Command} {no area <0-4294967295> range @var{a.b.c.d/m}} {}
+Summarize intra area paths from specified area into one Type-3 summary-LSA
+announced to other areas. This command can be used only in ABR and ONLY
+router-LSAs (Type-1) and network-LSAs (Type-2) (ie. LSAs with scope area) can
+be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS.
+Summarizing Type-7 AS-external-LSAs isn't supported yet by Frr.
+
+@example
+@group
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 range 10.0.0.0/8
+@end group
+@end example
+
+With configuration above one Type-3 Summary-LSA with routing info 10.0.0.0/8 is
+announced into backbone area if area 0.0.0.10 contains at least one intra-area
+network (ie. described with router or network LSA) from this range.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} range IPV4_PREFIX not-advertise} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} range IPV4_PREFIX not-advertise} {}
+Instead of summarizing intra area paths filter them - ie. intra area paths from this
+range are not advertised into other areas.
+This command makes sense in ABR only.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} range IPV4_PREFIX substitute IPV4_PREFIX} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} range IPV4_PREFIX substitute IPV4_PREFIX} {}
+Substitute summarized prefix with another prefix.
+
+@example
+@group
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 range 10.0.0.0/8 substitute 11.0.0.0/8
+@end group
+@end example
+
+One Type-3 summary-LSA with routing info 11.0.0.0/8 is announced into backbone area if
+area 0.0.0.10 contains at least one intra-area network (ie. described with router-LSA or
+network-LSA) from range 10.0.0.0/8.
+This command makes sense in ABR only.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} virtual-link @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {area <0-4294967295> virtual-link @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} virtual-link @var{a.b.c.d}} {}
+@deffnx {OSPF Command} {no area <0-4294967295> virtual-link @var{a.b.c.d}} {}
+@anchor{OSPF virtual-link}
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} shortcut} {}
+@deffnx {OSPF Command} {area <0-4294967295> shortcut} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} shortcut} {}
+@deffnx {OSPF Command} {no area <0-4294967295> shortcut} {}
+Configure the area as Shortcut capable. See @cite{RFC3509}. This requires
+that the 'abr-type' be set to 'shortcut'.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} stub} {}
+@deffnx {OSPF Command} {area <0-4294967295> stub} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} stub} {}
+@deffnx {OSPF Command} {no area <0-4294967295> stub} {}
+Configure the area to be a stub area. That is, an area where no router
+originates routes external to OSPF and hence an area where all external
+routes are via the ABR(s). Hence, ABRs for such an area do not need
+to pass AS-External LSAs (type-5s) or ASBR-Summary LSAs (type-4) into the
+area. They need only pass Network-Summary (type-3) LSAs into such an area,
+along with a default-route summary.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} stub no-summary} {}
+@deffnx {OSPF Command} {area <0-4294967295> stub no-summary} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} stub no-summary} {}
+@deffnx {OSPF Command} {no area <0-4294967295> stub no-summary} {}
+Prevents an @command{ospfd} ABR from injecting inter-area
+summaries into the specified stub area.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} default-cost <0-16777215>} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} default-cost <0-16777215>} {}
+Set the cost of default-summary LSAs announced to stubby areas.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} export-list NAME} {}
+@deffnx {OSPF Command} {area <0-4294967295> export-list NAME} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} export-list NAME} {}
+@deffnx {OSPF Command} {no area <0-4294967295> export-list NAME} {}
+Filter Type-3 summary-LSAs announced to other areas originated from intra-
+area paths from specified area.
+
+@example
+@group
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 10.0.0.0/8 area 0.0.0.10
+ area 0.0.0.10 export-list foo
+!
+access-list foo permit 10.10.0.0/16
+access-list foo deny any
+@end group
+@end example
+
+With example above any intra-area paths from area 0.0.0.10 and from range
+10.10.0.0/16 (for example 10.10.1.0/24 and 10.10.2.128/30) are announced into
+other areas as Type-3 summary-LSA's, but any others (for example 10.11.0.0/16
+or 10.128.30.16/30) aren't.
+
+This command is only relevant if the router is an ABR for the specified
+area.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} import-list NAME} {}
+@deffnx {OSPF Command} {area <0-4294967295> import-list NAME} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} import-list NAME} {}
+@deffnx {OSPF Command} {no area <0-4294967295> import-list NAME} {}
+Same as export-list, but it applies to paths announced into specified area as
+Type-3 summary-LSAs.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} filter-list prefix NAME in} {}
+@deffnx {OSPF Command} {area @var{a.b.c.d} filter-list prefix NAME out} {}
+@deffnx {OSPF Command} {area <0-4294967295> filter-list prefix NAME in} {}
+@deffnx {OSPF Command} {area <0-4294967295> filter-list prefix NAME out} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} filter-list prefix NAME in} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} filter-list prefix NAME out} {}
+@deffnx {OSPF Command} {no area <0-4294967295> filter-list prefix NAME in} {}
+@deffnx {OSPF Command} {no 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.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} authentication} {}
+@deffnx {OSPF Command} {area <0-4294967295> authentication} {}
+@deffnx {OSPF Command} {no area @var{a.b.c.d} authentication} {}
+@deffnx {OSPF Command} {no area <0-4294967295> authentication} {}
+Specify that simple password authentication should be used for the given
+area.
+@end deffn
+
+@deffn {OSPF Command} {area @var{a.b.c.d} authentication message-digest} {}
+@deffnx {OSPF Command} {area <0-4294967295> authentication message-digest} {}
+
+@anchor{area authentication message-digest}Specify that OSPF packets
+must be authenticated with MD5 HMACs within the given area. Keying
+material must also be configured on a per-interface basis (@pxref{ip
+ospf message-digest-key}).
+
+MD5 authentication may also be configured on a per-interface basis
+(@pxref{ip ospf authentication message-digest}). Such per-interface
+settings will override any per-area authentication setting.
+@end deffn
+
+@node OSPF interface
+@section OSPF interface
+
+@deffn {Interface Command} {ip ospf area @var{AREA} [@var{ADDR}]} {}
+@deffnx {Interface Command} {no ip ospf area [@var{ADDR}]} {}
+@anchor{OSPF ip ospf area command}
+
+Enable OSPF on the interface, optionally restricted to just the IP address
+given by @var{ADDR}, putting it in the @var{AREA} area. Per interface area
+settings take precedence to network commands (@pxref{OSPF network command}).
+
+If you have a lot of interfaces, and/or a lot of subnets, then enabling OSPF
+via this command may result in a slight performance improvement.
+
+@end deffn
+
+@deffn {Interface Command} {ip ospf authentication-key @var{AUTH_KEY}} {}
+@deffnx {Interface Command} {no ip ospf authentication-key} {}
+Set OSPF authentication key to a simple password. After setting @var{AUTH_KEY},
+all OSPF packets are authenticated. @var{AUTH_KEY} has length up to 8 chars.
+
+Simple text password authentication is insecure and deprecated in favour of
+MD5 HMAC authentication (@pxref{ip ospf authentication message-digest}).
+@end deffn
+
+@deffn {Interface Command} {ip ospf authentication message-digest} {}
+@anchor{ip ospf authentication message-digest}Specify that MD5 HMAC
+authentication must be used on this interface. MD5 keying material must
+also be configured (@pxref{ip ospf message-digest-key}). Overrides any
+authentication enabled on a per-area basis (@pxref{area
+authentication message-digest}).
+
+Note that OSPF MD5 authentication requires that time never go backwards
+(correct time is NOT important, only that it never goes backwards), even
+across resets, if ospfd is to be able to promptly reestabish adjacencies
+with its neighbours after restarts/reboots. The host should have system
+time be set at boot from an external or non-volatile source (eg battery backed clock, NTP,
+etc.) or else the system clock should be periodically saved to non-volative
+storage and restored at boot if MD5 authentication is to be expected to work
+reliably.
+@end deffn
+
+@deffn {Interface Command} {ip ospf message-digest-key KEYID md5 KEY} {}
+@deffnx {Interface Command} {no ip ospf message-digest-key} {}
+@anchor{ip ospf message-digest-key}Set OSPF authentication key to a
+cryptographic password. The cryptographic algorithm is MD5.
+
+KEYID identifies secret key used to create the message digest. This ID
+is part of the protocol and must be consistent across routers on a
+link.
+
+KEY is the actual message digest key, of up to 16 chars (larger strings
+will be truncated), and is associated with the given KEYID.
+@end deffn
+
+@deffn {Interface Command} {ip ospf cost <1-65535>} {}
+@deffnx {Interface Command} {no ip ospf cost} {}
+Set link cost for the specified interface. The cost value is set to router-LSA's
+metric field and used for SPF calculation.
+@end deffn
+
+@deffn {Interface Command} {ip ospf dead-interval <1-65535>} {}
+@deffnx {Interface Command} {ip ospf dead-interval minimal hello-multiplier <2-20>} {}
+@deffnx {Interface Command} {no ip ospf dead-interval} {}
+@anchor{ip ospf dead-interval minimal} Set number of seconds for
+RouterDeadInterval timer value used for Wait Timer and Inactivity
+Timer. This value must be the same for all routers attached to a
+common network. The default value is 40 seconds.
+
+If 'minimal' is specified instead, then the dead-interval is set to 1
+second and one must specify a hello-multiplier. The hello-multiplier
+specifies how many Hellos to send per second, from 2 (every 500ms) to
+20 (every 50ms). Thus one can have 1s convergence time for OSPF. If this form
+is specified, then the hello-interval advertised in Hello packets is set to
+0 and the hello-interval on received Hello packets is not checked, thus
+the hello-multiplier need NOT be the same across multiple routers on a common
+link.
+@end deffn
+
+@deffn {Interface Command} {ip ospf hello-interval <1-65535>} {}
+@deffnx {Interface Command} {no ip ospf hello-interval} {}
+Set number of seconds for HelloInterval timer value. Setting this value,
+Hello packet will be sent every timer value seconds on the specified interface.
+This value must be the same for all routers attached to a common network.
+The default value is 10 seconds.
+
+This command has no effect if @ref{ip ospf dead-interval minimal} is also
+specified for the interface.
+@end deffn
+
+@deffn {Interface Command} {ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)} {}
+@deffnx {Interface Command} {no ip ospf network} {}
+Set explicitly network type for specifed interface.
+@end deffn
+
+@deffn {Interface Command} {ip ospf priority <0-255>} {}
+@deffnx {Interface Command} {no ip ospf priority} {}
+Set RouterPriority integer value. The router with the highest priority
+will be more eligible to become Designated Router. Setting the value
+to 0, makes the router ineligible to become Designated Router. The
+default value is 1.
+@end deffn
+
+@deffn {Interface Command} {ip ospf retransmit-interval <1-65535>} {}
+@deffnx {Interface Command} {no ip ospf retransmit interval} {}
+Set number of seconds for RxmtInterval timer value. This value is used
+when retransmitting Database Description and Link State Request packets.
+The default value is 5 seconds.
+@end deffn
+
+@deffn {Interface Command} {ip ospf transmit-delay} {}
+@deffnx {Interface Command} {no ip ospf transmit-delay} {}
+Set number of seconds for InfTransDelay value. LSAs' age should be
+incremented by this value when transmitting.
+The default value is 1 seconds.
+@end deffn
+
+@deffn {Interface Command} {ip ospf area (A.B.C.D|<0-4294967295>)} {}
+@deffnx {Interface Command} {no ip ospf area} {}
+Enable ospf on an interface and set associated area.
+@end deffn
+
+@node Redistribute routes to OSPF
+@section Redistribute routes to OSPF
+
+@deffn {OSPF Command} {redistribute (kernel|connected|static|rip|bgp)} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) @var{route-map}} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2)} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2) route-map @var{word}} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric <0-16777214>} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric <0-16777214> route-map @var{word}} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2) metric <0-16777214>} {}
+@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2) metric <0-16777214> route-map @var{word}} {}
+@deffnx {OSPF Command} {no redistribute (kernel|connected|static|rip|bgp)} {}
+@anchor{OSPF redistribute}Redistribute routes of the specified protocol
+or kind into OSPF, with the metric type and metric set if specified,
+filtering the routes using the given route-map if specified.
+Redistributed routes may also be filtered with distribute-lists, see
+@ref{ospf distribute-list}.
+
+Redistributed routes are distributed as into OSPF as Type-5 External
+LSAs into links to areas that accept external routes, Type-7 External LSAs
+for NSSA areas and are not redistributed at all into Stub areas, where
+external routes are not permitted.
+
+Note that for connected routes, one may instead use
+@dfn{passive-interface}, see @ref{OSPF passive-interface}.
+@end deffn
+
+@deffn {OSPF Command} {default-information originate} {}
+@deffnx {OSPF Command} {default-information originate metric <0-16777214>} {}
+@deffnx {OSPF Command} {default-information originate metric <0-16777214> metric-type (1|2)} {}
+@deffnx {OSPF Command} {default-information originate metric <0-16777214> metric-type (1|2) route-map @var{word}} {}
+@deffnx {OSPF Command} {default-information originate always} {}
+@deffnx {OSPF Command} {default-information originate always metric <0-16777214>} {}
+@deffnx {OSPF Command} {default-information originate always metric <0-16777214> metric-type (1|2)} {}
+@deffnx {OSPF Command} {default-information originate always metric <0-16777214> metric-type (1|2) route-map @var{word}} {}
+@deffnx {OSPF Command} {no default-information originate} {}
+Originate an AS-External (type-5) LSA describing a default route into
+all external-routing capable areas, of the specified metric and metric
+type. If the 'always' keyword is given then the default is always
+advertised, even when there is no default present in the routing table.
+@end deffn
+
+@deffn {OSPF Command} {distribute-list NAME out (kernel|connected|static|rip|ospf} {}
+@deffnx {OSPF Command} {no distribute-list NAME out (kernel|connected|static|rip|ospf} {}
+@anchor{ospf distribute-list}Apply the access-list filter, NAME, to
+redistributed routes of the given type before allowing the routes to
+redistributed into OSPF (@pxref{OSPF redistribute}).
+@end deffn
+
+@deffn {OSPF Command} {default-metric <0-16777214>} {}
+@deffnx {OSPF Command} {no default-metric} {}
+@end deffn
+
+@deffn {OSPF Command} {distance <1-255>} {}
+@deffnx {OSPF Command} {no distance <1-255>} {}
+@end deffn
+
+@deffn {OSPF Command} {distance ospf (intra-area|inter-area|external) <1-255>} {}
+@deffnx {OSPF Command} {no distance ospf} {}
+@end deffn
+
+@deffn {Command} {router zebra} {}
+@deffnx {Command} {no router zebra} {}
+@end deffn
+
+@node Showing OSPF information
+@section Showing OSPF information
+
+@deffn {Command} {show ip ospf} {}
+@anchor{show ip ospf}Show information on a variety of general OSPF and
+area state and configuration information.
+@end deffn
+
+@deffn {Command} {show ip ospf interface [INTERFACE]} {}
+Show state and configuration of OSPF the specified interface, or all
+interfaces if no interface is given.
+@end deffn
+
+@deffn {Command} {show ip ospf neighbor} {}
+@deffnx {Command} {show ip ospf neighbor INTERFACE} {}
+@deffnx {Command} {show ip ospf neighbor detail} {}
+@deffnx {Command} {show ip ospf neighbor INTERFACE detail} {}
+@end deffn
+
+@deffn {Command} {show ip ospf database} {}
+@end deffn
+
+@deffn {Command} {show ip ospf database (asbr-summary|external|network|router|summary)} {}
+@deffnx {Command} {show ip ospf database (asbr-summary|external|network|router|summary) @var{link-state-id}} {}
+@deffnx {Command} {show ip ospf database (asbr-summary|external|network|router|summary) @var{link-state-id} adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database (asbr-summary|external|network|router|summary) adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database (asbr-summary|external|network|router|summary) @var{link-state-id} self-originate} {}
+@deffnx {Command} {show ip ospf database (asbr-summary|external|network|router|summary) self-originate} {}
+@end deffn
+
+@deffn {Command} {show ip ospf database max-age} {}
+@end deffn
+
+@deffn {Command} {show ip ospf database self-originate} {}
+@end deffn
+
+@deffn {Command} {show ip ospf route} {}
+Show the OSPF routing table, as determined by the most recent SPF calculation.
+@end deffn
+
+@node Opaque LSA
+@section Opaque LSA
+
+@deffn {OSPF Command} {ospf opaque-lsa} {}
+@deffnx {OSPF Command} {capability opaque} {}
+@deffnx {OSPF Command} {no ospf opaque-lsa} {}
+@deffnx {OSPF Command} {no capability opaque} {}
+@command{ospfd} support Opaque LSA (RFC2370) as fondment for MPLS Traffic Engineering LSA. Prior to used MPLS TE, opaque-lsa must be enable in the configuration file. Alternate command could be "mpls-te on" (@ref{OSPF Traffic Engineering}).
+@end deffn
+
+@deffn {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external)} {}
+@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) @var{link-state-id}} {}
+@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) @var{link-state-id} adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) @var{link-state-id} self-originate} {}
+@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) self-originate} {}
+Show Opaque LSA from the database.
+@end deffn
+
+@node OSPF Traffic Engineering
+@section Traffic Engineering
+
+@deffn {OSPF Command} {mpls-te on} {}
+@deffnx {OSPF Command} {no mpls-te} {}
+Enable Traffic Engineering LSA flooding.
+@end deffn
+
+@deffn {OSPF Command} {mpls-te router-address <A.B.C.D>} {}
+@deffnx {OSPF Command} {no mpls-te} {}
+Configure stable IP address for MPLS-TE. This IP address is then advertise in Opaque LSA Type-10 TLV=1 (TE)
+option 1 (Router-Address).
+@end deffn
+
+@deffn {OSPF Command} {mpls-te inter-as area <area-id>|as} {}
+@deffnx {OSPF Command} {no mpls-te inter-as} {}
+Enable RFC5392 suuport - Inter-AS TE v2 - to flood Traffic Engineering parameters of Inter-AS link.
+2 modes are supported: AREA and AS; LSA are flood in AREA <area-id> with Opaque Type-10,
+respectively in AS with Opaque Type-11. In all case, Opaque-LSA TLV=6.
+@end deffn
+
+@deffn {Command} {show ip ospf mpls-te interface} {}
+@deffnx {Command} {show ip ospf mpls-te interface @var{interface}} {}
+Show MPLS Traffic Engineering parameters for all or specified interface.
+@end deffn
+
+@deffn {Command} {show ip ospf mpls-te router} {}
+Show Traffic Engineering router parameters.
+@end deffn
+
+@node Router Information
+@section Router Information
+
+@deffn {OSPF Command} {router-info [as | area <A.B.C.D>]} {}
+@deffnx {OSPF Command} {no router-info} {}
+Enable Router Information (RFC4970) LSA advertisement with AS scope (default) or Area scope flooding
+when area is specified.
+@end deffn
+
+@deffn {OSPF Command} {pce address <A.B.C.D>} {}
+@deffnx {OSPF Command} {no pce address} {}
+@deffnx {OSPF Command} {pce domain as <0-65535>} {}
+@deffnx {OSPF Command} {no pce domain as <0-65535>} {}
+@deffnx {OSPF Command} {pce neighbor as <0-65535>} {}
+@deffnx {OSPF Command} {no pce neighbor as <0-65535>} {}
+@deffnx {OSPF Command} {pce flag BITPATTERN} {}
+@deffnx {OSPF Command} {no pce flag} {}
+@deffnx {OSPF Command} {pce scope BITPATTERN} {}
+@deffnx {OSPF Command} {no pce scope} {}
+The commands are conform to RFC 5088 and allow OSPF router announce Path Compuatation Elemenent (PCE) capabilities
+through the Router Information (RI) LSA. Router Information must be enable prior to this. The command set/unset
+respectively the PCE IP adress, Autonomous System (AS) numbers of controlled domains, neighbor ASs, flag and scope.
+For flag and scope, please refer to RFC5088 for the BITPATTERN recognition. Multiple 'pce neighbor' command could
+be specified in order to specify all PCE neighbours.
+@end deffn
+
+@deffn {Command} {show ip ospf router-info} {}
+Show Router Capabilities flag.
+@end deffn
+@deffn {Command} {show ip ospf router-info pce} {}
+Show Router Capabilities PCE parameters.
+@end deffn
+
+@node Debugging OSPF
+@section Debugging OSPF
+
+@deffn {Command} {debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail]} {}
+@deffnx {Command} {no debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail]} {}
+Dump Packet for debugging
+@end deffn
+
+@deffn {Command} {debug ospf ism} {}
+@deffnx {Command} {debug ospf ism (status|events|timers)} {}
+@deffnx {Command} {no debug ospf ism} {}
+@deffnx {Command} {no debug ospf ism (status|events|timers)} {}
+Show debug information of Interface State Machine
+@end deffn
+
+@deffn {Command} {debug ospf nsm} {}
+@deffnx {Command} {debug ospf nsm (status|events|timers)} {}
+@deffnx {Command} {no debug ospf nsm} {}
+@deffnx {Command} {no debug ospf nsm (status|events|timers)} {}
+Show debug information of Network State Machine
+@end deffn
+
+@deffn {Command} {debug ospf event} {}
+@deffnx {Command} {no debug ospf event} {}
+Show debug information of OSPF event
+@end deffn
+
+@deffn {Command} {debug ospf nssa} {}
+@deffnx {Command} {no debug ospf nssa} {}
+Show debug information about Not So Stub Area
+@end deffn
+
+@deffn {Command} {debug ospf lsa} {}
+@deffnx {Command} {debug ospf lsa (generate|flooding|refresh)} {}
+@deffnx {Command} {no debug ospf lsa} {}
+@deffnx {Command} {no debug ospf lsa (generate|flooding|refresh)} {}
+Show debug detail of Link State messages
+@end deffn
+
+@deffn {Command} {debug ospf te} {}
+@deffnx {Command} {no debug ospf te} {}
+Show debug information about Traffic Engineering LSA
+@end deffn
+
+@deffn {Command} {debug ospf zebra} {}
+@deffnx {Command} {debug ospf zebra (interface|redistribute)} {}
+@deffnx {Command} {no debug ospf zebra} {}
+@deffnx {Command} {no debug ospf zebra (interface|redistribute)} {}
+Show debug information of ZEBRA API
+@end deffn
+
+@deffn {Command} {show debugging ospf} {}
+@end deffn
+
+@node OSPF Configuration Examples
+@section OSPF Configuration Examples
+A simple example, with MD5 authentication enabled:
+
+@example
+@group
+!
+interface bge0
+ ip ospf authentication message-digest
+ ip ospf message-digest-key 1 md5 ABCDEFGHIJK
+!
+router ospf
+ network 192.168.0.0/16 area 0.0.0.1
+ area 0.0.0.1 authentication message-digest
+@end group
+@end example
+
+An @acronym{ABR} router, with MD5 authentication and performing summarisation
+of networks between the areas:
+
+@example
+@group
+!
+password ABCDEF
+log file /var/log/frr/ospfd.log
+service advanced-vty
+!
+interface eth0
+ ip ospf authentication message-digest
+ ip ospf message-digest-key 1 md5 ABCDEFGHIJK
+!
+interface ppp0
+!
+interface br0
+ ip ospf authentication message-digest
+ ip ospf message-digest-key 2 md5 XYZ12345
+!
+router ospf
+ ospf router-id 192.168.0.1
+ redistribute connected
+ passive interface ppp0
+ network 192.168.0.0/24 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ network 192.168.1.0/24 area 0.0.0.1
+ area 0.0.0.0 authentication message-digest
+ area 0.0.0.0 range 10.0.0.0/16
+ area 0.0.0.0 range 192.168.0.0/24
+ area 0.0.0.1 authentication message-digest
+ area 0.0.0.1 range 10.2.0.0/16
+!
+@end group
+@end example
+
+A Traffic Engineering configuration, with Inter-ASv2 support.
+
+ - First, the 'zebra.conf' part:
+
+@example
+@group
+hostname HOSTNAME
+password PASSWORD
+log file /var/log/zebra.log
+!
+interface eth0
+ ip address 198.168.1.1/24
+ link-params
+ enable
+ admin-grp 0xa1
+ metric 100
+ max-bw 1.25e+07
+ max-rsv-bw 1.25e+06
+ unrsv-bw 0 1.25e+06
+ unrsv-bw 1 1.25e+06
+ unrsv-bw 2 1.25e+06
+ unrsv-bw 3 1.25e+06
+ unrsv-bw 4 1.25e+06
+ unrsv-bw 5 1.25e+06
+ unrsv-bw 6 1.25e+06
+ unrsv-bw 7 1.25e+06
+!
+interface eth1
+ ip address 192.168.2.1/24
+ link-params
+ enable
+ metric 10
+ max-bw 1.25e+07
+ max-rsv-bw 1.25e+06
+ unrsv-bw 0 1.25e+06
+ unrsv-bw 1 1.25e+06
+ unrsv-bw 2 1.25e+06
+ unrsv-bw 3 1.25e+06
+ unrsv-bw 4 1.25e+06
+ unrsv-bw 5 1.25e+06
+ unrsv-bw 6 1.25e+06
+ unrsv-bw 7 1.25e+06
+ neighbor 192.168.2.2 as 65000
+@end group
+@end example
+
+ - Then the 'ospfd.conf' itself:
+
+@example
+@group
+hostname HOSTNAME
+password PASSWORD
+log file /var/log/ospfd.log
+!
+!
+interface eth0
+ ip ospf hello-interval 60
+ ip ospf dead-interval 240
+!
+interface eth1
+ ip ospf hello-interval 60
+ ip ospf dead-interval 240
+!
+!
+router ospf
+ ospf router-id 192.168.1.1
+ network 192.168.0.0/16 area 1
+ ospf opaque-lsa
+ mpls-te
+ mpls-te router-address 192.168.1.1
+ mpls-te inter-as area 1
+!
+line vty
+@end group
+@end example
+
+A router information example with PCE advsertisement:
+
+@example
+@group
+!
+router ospf
+ ospf router-id 192.168.1.1
+ network 192.168.0.0/16 area 1
+ capability opaque
+ mpls-te
+ mpls-te router-address 192.168.1.1
+ router-info area 0.0.0.1
+ pce address 192.168.1.1
+ pce flag 0x80
+ pce domain as 65400
+ pce neighbor as 65500
+ pce neighbor as 65200
+ pce scope 0x80
+!
+@end group
+@end example
--- /dev/null
+@c -*-texinfo-*-
+@c This is part of the Frr Manual.
+@c @value{COPYRIGHT_STR}
+@c See file frr.texi for copying conditions.
+@node PIM
+@chapter PIM
+
+PIM -- Protocol Independent Multicast
+
+@command{pimd} supports pim-sm as well as igmp v2 and v3. pim is
+vrf aware and can work within the context of vrf's in order to
+do S,G mrouting.
+
+@menu
+* Starting and Stopping pimd::
+* PIM Configuration::
+* PIM Interface Configuration::
+* PIM Multicast RIB insertion::
+* Show PIM Information::
+* PIM Debug Commands::
+@end menu
+
+@node Starting and Stopping pimd
+@section Starting and Stopping pimd
+
+The default configuration file name of @command{pimd}'s is
+@file{pimd.conf}. When invocation @command{pimd} searches directory
+@value{INSTALL_PREFIX_ETC}. If @file{pimd.conf} is not there
+then next search current directory.
+
+@command{pimd} requires zebra for proper operation. Additionally
+@command{pimd} depends on routing properly setup and working
+in the network that it is working on.
+
+@example
+@group
+# zebra -d
+# pimd -d
+@end group
+@end example
+
+Please note that @command{zebra} must be invoked before @command{pimd}.
+
+To stop @command{pimd}. Please use @command{kill `cat
+/var/run/pimd.pid`}. Certain signals have special meanings to @command{pimd}.
+
+@table @samp
+@item SIGUSR1
+Rotate @command{pimd} logfile.
+@item SIGINT
+@itemx SIGTERM
+@command{pimd} sweeps all installed PIM mroutes then terminates properly.
+@end table
+
+@command{pimd} invocation options. Common options that can be specified
+(@pxref{Common Invocation Options}).
+
+@node PIM Configuration
+
+@deffn Command {ip pim rp A.B.C.D A.B.C.D/M} {}
+In order to use pim, it is necessary to configure a RP for join
+messages to be sent to. Currently the only methodology to
+do this is via static rp commands. All routers in the
+pim network must agree on these values. The first ip address
+is the RP's address and the second value is the matching
+prefix of group ranges covered. This command is vrf aware,
+to configure for a vrf, enter the vrf submode.
+@end deffn
+
+@deffn Command {ip pim spt-switchover infinity-and-beyond} {}
+On the last hop router if it is desired to not switch over
+to the SPT tree. Configure this command. This command is
+vrf aware, to configure for a vrf, enter the vrf submode.
+#end deffn
+
+@deffn Comand {ip pim ecmp} {}
+If pim has the a choice of ECMP nexthops for a particular
+RPF, pim will cause S,G flows to be spread out amongst
+the nexthops. If this command is not specified then
+the first nexthop found will be used. This command
+is vrf aware, to configure for a vrf, enter the vrf submode.
+@end deffn
+
+@deffn Command {ip pim ecmp rebalance} {}
+If pim is using ECMP and an interface goes down, cause
+pim to rebalance all S,G flows aross the remaining
+nexthops. If this command is not configured pim only
+modifies those S,G flows that were using the interface
+that went down. This command is vrf aware, to configure
+for a vrf, enter the vrf submode.
+@end deffn
+
+@deffn Command {ip pim join-prune-interval (60-600)} {}
+Modify the join/prune interval that pim uses to the
+new value. Time is specified in seconds. This command
+is vrf aware, to configure for a vrf, enter the vrf submode.
+@end deffn
+
+@deffn Command {ip pim keep-alive-timer (31-60000)} {}
+Modify the time out value for a S,G flow from 31-60000
+seconds. 31 seconds is choosen for a lower bound
+because some hardware platforms cannot see data flowing
+in better than 30 second chunks. This comand is vrf
+aware, to configure for a vrf, enter the vrf submode.
+@end deffn
+
+@deffn Command {ip pim packets (1-100)} {}
+When processing packets from a neighbor process the
+number of packets incoming at one time before moving
+on to the next task. The default value is 3 packets.
+This command is only useful at scale when you can
+possibly have a large number of pim control packets
+flowing. This command is vrf aware, to configure for
+a vrf, enter the vrf submode.
+@end deffn
+
+@deffn Command {ip pim register-suppress-time (5-60000)} {}
+Modify the time that pim will register suppress a FHR
+will send register notifications to the kernel. This command
+is vrf aware, to configure for a vrf, enter the vrf submode.
+@end deffn
+
+@deffn Command {ip pim send-v6-secondary} {}
+When sending pim hello packets tell pim to send
+any v6 secondary addresses on the interface. This
+information is used to allow pim to use v6 nexthops
+in it's decision for RPF lookup. This command
+is vrf aware, to configure for a vrf, enter the vrf submode.
+@end deffn
+
+@deffn Command {ip pim ssm prefix-list WORD} {}
+Specify a range of group addresses via a prefix-list
+that forces pim to never do SM over. This command
+is vrf aware, to configure for a vrf, enter the vrf submode.
+@end deffn
+
+@deffn Command {ip multicast rpf-lookup-mode WORD} {}
+Modify how PIM does RPF lookups in the zebra routing table.
+You can use these choices:
+@table @lookup_modes
+@item longer-prefix
+Lookup the RPF in both tables using the longer prefix as a match
+@item lower-distance
+Lookup the RPF in both tables using the lower distance as a match
+@item mrib-only
+Lookup in the Multicast RIB only
+@item mrib-then-urib
+Lookup in the Multicast RIB then the Unicast Rib, returning first found.
+This is the default value for lookup if this command is not entered
+@item urib-only
+Lookup in the Unicast Rib only.
+@end table
+@end deffn
+
+@node PIM Interface Configuration
+@section PIM Interface Configuration
+
+PIM interface commands allow you to configure an
+interface as either a Receiver or a interface
+that you would like to form pim neighbors on. If the
+interface is in a vrf, enter the interface command with
+the vrf keyword at the end.
+
+@deffn {PIM Interface Command] {ip pim bfd} {}
+Turns on BFD support for PIM for this interface.
+@end deffn
+
+@deffn {PIM Interface Command} {ip pim drpriority (1-4294967295)} {}
+Set the DR Priority for the interface. This command is useful
+to allow the user to influence what node becomes the DR for a
+lan segment.
+@end deffn
+
+@deffn {PIM Interface Command} {ip pim hello (1-180) (1-180)} {}
+Set the pim hello and hold interval for a interface.
+@end deffn
+
+@deffn {PIM Interface Command} {ip pim sm} {}
+Tell pim that we would like to use this interface to form
+pim neighbors over. Please note we will *not* accept
+igmp reports over this interface with this command.
+@end deffn
+
+@deffn {PIM Interface Command} {ip igmp} {}
+Tell pim to receive IGMP reports and Query on this
+interface. The default version is v3. This command
+is useful on the LHR.
+@end deffn
+
+@deffn {PIM Interface Command} {ip igmp query-interval (1-1800)} {}
+Set the IGMP query interval that PIM will use.
+@end deffn
+
+@deffn {PIM Interface Command} {ip igmp query-max-response-time (10-250)} {}
+Set the IGMP query response timeout value. If an report is not returned
+in the specified time we will assume the S,G or *,G has timed out.
+@end deffn
+
+@deffn {PIM Interface Command} {ip igmp version (2-3)} {}
+Set the IGMP version used on this interface. The default value
+is 3.
+@end deffn
+
+@deffn {PIM Interface Command} {ip multicat boundary oil WORD} {}
+Set a pim multicast boundary, based upon the WORD prefix-list. If
+a pim join or IGMP report is received on this interface and the Group
+is denyed by the prefix-list, PIM will ignore the join or report.
+@end deffn
+
+@node PIM Multicast RIB insertion::
+@section PIM Multicast RIB insertion::
+
+In order to influence Multicast RPF lookup, it is possible to insert
+into zebra routes for the Multicast RIB. These routes are only
+used for RPF lookup and will not be used by zebra for insertion
+into the kernel *or* for normal rib processing. As such it is
+possible to create weird states with these commands. Use with
+caution. Most of the time this will not be necessary.
+
+@deffn {PIM Multicast RIB insertion} {ip mroute A.B.C.D/M A.B.C.D (1-255)} {}
+Insert into the Multicast Rib Route A.B.C.D/M with specified nexthop. The distance can be specified as well if desired.
+@end deffn
+
+@deffn {PIM Multicast RIB insertion} {ip mroute A.B.C.D/M INTERFACE (1-255)} {}
+Insert into the Multicast Rib Route A.B.C.D/M using the specified INTERFACE.
+The distance can be specified as well if desired.
+@end deffn
+
+@node Show PIM Information::
+@section Show PIM Information
+
+All PIM show commands are vrf aware and typically allow you to insert
+a specified vrf command if information is desired about a specific vrf.
+If no vrf is specified then the default vrf is assumed. Finally
+the special keyword 'all' allows you to look at all vrfs for the command.
+Naming a vrf 'all' will cause great confusion.
+
+@deffn {Show PIM Information} {show ip multicast}
+Display various information about the interfaces used in this pim
+instance.
+@end deffn
+
+@deffn {Show PIM Information} {show ip mroute}
+Display information about installed into the kernel S,G mroutes.
+@end deffn
+
+@deffn {Show PIM Information} {show ip mroute count}
+Display information about installed into the kernel S,G mroutes
+and in addition display data about packet flow for the mroutes.
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim assert}
+Display information about asserts in the PIM system for S,G mroutes.
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim assert-internal}
+Display internal assert state for S,G mroutes
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim assert-metric}
+Display metric information about assert state for S,G mroutes
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim assert-winner-metric}
+Display winner metric for assert state for S,G mroutes
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim group-type}
+Display SSM group ranges
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim interface}
+Display information about interfaces PIM is using.
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim join}
+Display information about PIM joins received.
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim local-membership} {}
+Display information about PIM interface local-membership
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim neighbor} {}
+Display information about PIM neighbors
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim nexthop} {}
+Display information about pim nexthops that are being
+used
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim nexthop-lookup} {}
+Display information about a S,G pair and how the RPF would
+be choosen. This is especially useful if there are ECMP's
+available from the RPF lookup.
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim rp-info} {}
+Display information about RP's that are configured on
+this router
+@end deffn
+
+@deffn {Show PIM Information} {show ip pim rpf} {}
+Display information about currently being used S,G's
+and their RPF lookup information. Additionally display
+some statistics about what has been happening on the
+router
+@end deffn
+
+@deffn {show PIM Information} {show ip pim secondary} {}
+Display information about an interface and all the
+secondary addresses associated with it
+@end deffn
+
+@deffn {show PIM Information} {show ip pim state} {}
+Display information about known S,G's and incoming
+interface as well as the OIL and how they were choosen
+@end deffn
+
+@deffn {show PIM Information} {show ip pim upstream} {}
+Display upstream information about a S,G mroute
+@end deffn
+
+@deffn {show PIM Information} {show ip pim upstream-join-desired} {}
+Display upstream information for S,G's and if we desire to
+join the mcast tree
+@end deffn
+
+@deffn {show PIM Information} {show ip pim upstream-rpf} {}
+Display upstream information for S,G's and the RPF data
+associated with them
+@end deffn
+
+@deffn {show PIM Information} {show ip rpf} {}
+Display the multicast RIB created in zebra
+@end deffn
+
+@node PIM Debug Commands
+@section PIM Debug Commands
+
+The debugging subsystem for PIM behaves in accordance with how FRR handles debugging. You can specify debugging at the enable cli mode as well as the configure cli mode. If you specify debug commands in the configuration cli mode, the debug commands can be persistent across restarts of the FRR pimd if the config was written out.
+
+@deffn {PIM Debug Commands} {debug pim events}
+This turns on debugging for PIM system events. Especially timers.
+@end deffn
+
+@deffn {PIM Debug Commands} {debug pim nht}
+This turns on debugging for PIM nexthop tracking. It will display information about RPF lookups and information about when a nexthop changes.
+@end deffn
+
+@deffn {PIM Debug Commands} {debug pim packet-dump}
+This turns on an extraordinary amount of data. Each pim packet sent and received is dumped for debugging purposes. This should be considered a developer only command
+@end deffn
+
+@deffn {PIM Debug Commands} {debug pim packets}
+This turns on information about packet generation for sending and about packet handling from a received packet
+@end deffn
+
+@deffn {PIM Debug Commands} {debug pim trace}
+This traces pim code and how it is running.
+@end deffn
+
+@deffn {PIM Debug Commands} {debug pim zebra}
+This gathers data about events from zebra that come up through the zapi
+@end deffn
--- /dev/null
+@node Route Map
+@chapter Route Map
+
+Route maps provide a means to both filter and/or apply actions to
+route, hence allowing policy to be applied to routes.
+
+@menu
+* Route Map Command::
+* Route Map Match Command::
+* Route Map Set Command::
+* Route Map Call Command::
+* Route Map Exit Action Command::
+* Route Map Examples::
+@end menu
+
+Route-maps are an ordered list of route-map entries. Each entry may
+specify up to four distincts sets of clauses:
+
+@table @samp
+@item Matching Policy
+
+This specifies the policy implied if the @samp{Matching Conditions} are
+met or not met, and which actions of the route-map are to be taken, if
+any. The two possibilities are:
+
+@itemize @minus
+@item
+@samp{permit}: If the entry matches, then carry out the @samp{Set
+Actions}. Then finish processing the route-map, permitting the route,
+unless an @samp{Exit Action} indicates otherwise.
+
+@item
+@samp{deny}: If the entry matches, then finish processing the route-map and
+deny the route (return @samp{deny}).
+@end itemize
+
+The @samp{Matching Policy} is specified as part of the command which
+defines the ordered entry in the route-map. See below.
+
+@item Matching Conditions
+
+A route-map entry may, optionally, specify one or more conditions which
+must be matched if the entry is to be considered further, as governed
+by the Match Policy. If a route-map entry does not explicitely specify
+any matching conditions, then it always matches.
+
+@item Set Actions
+
+A route-map entry may, optionally, specify one or more @samp{Set
+Actions} to set or modify attributes of the route.
+
+@item Call Action
+
+Call to another route-map, after any @samp{Set Actions} have been
+carried out. If the route-map called returns @samp{deny} then
+processing of the route-map finishes and the route is denied,
+regardless of the @samp{Matching Policy} or the @samp{Exit Policy}. If
+the called route-map returns @samp{permit}, then @samp{Matching Policy}
+and @samp{Exit Policy} govern further behaviour, as normal.
+
+@item Exit Policy
+
+An entry may, optionally, specify an alternative @samp{Exit Policy} to
+take if the entry matched, rather than the normal policy of exiting the
+route-map and permitting the route. The two possibilities are:
+
+@itemize @minus
+@item
+@samp{next}: Continue on with processing of the route-map entries.
+
+@item
+@samp{goto N}: Jump ahead to the first route-map entry whose order in
+the route-map is >= N. Jumping to a previous entry is not permitted.
+@end itemize
+@end table
+
+The default action of a route-map, if no entries match, is to deny.
+I.e. a route-map essentially has as its last entry an empty @samp{deny}
+entry, which matches all routes. To change this behaviour, one must
+specify an empty @samp{permit} entry as the last entry in the route-map.
+
+To summarise the above:
+
+@multitable {permit} {action} {No Match}
+@headitem @tab Match @tab No Match
+@item @emph{Permit} @tab action @tab cont
+@item @emph{Deny} @tab deny @tab cont
+@end multitable
+
+@table @samp
+
+@item action
+@itemize @minus
+@item
+Apply @emph{set} statements
+
+@item
+If @emph{call} is present, call given route-map. If that returns a @samp{deny}, finish
+processing and return @samp{deny}.
+
+@item
+If @samp{Exit Policy} is @emph{next}, goto next route-map entry
+
+@item
+If @samp{Exit Policy} is @emph{goto}, goto first entry whose order in the list
+is >= the given order.
+
+@item
+Finish processing the route-map and permit the route.
+@end itemize
+
+@item deny
+@itemize @minus
+@item
+The route is denied by the route-map (return @samp{deny}).
+@end itemize
+
+@item cont
+@itemize @minus
+@item
+goto next route-map entry
+@end itemize
+@end table
+
+@node Route Map Command
+@section Route Map Command
+
+@deffn {Command} {route-map @var{route-map-name} (permit|deny) @var{order}} {}
+
+Configure the @var{order}'th entry in @var{route-map-name} with
+@samp{Match Policy} of either @emph{permit} or @emph{deny}.
+
+@end deffn
+
+@node Route Map Match Command
+@section Route Map Match Command
+
+@deffn {Route-map Command} {match ip address @var{access_list}} {}
+Matches the specified @var{access_list}
+@end deffn
+
+@deffn {Route-map Command} {match ip address @var{prefix-list}} {}
+Matches the specified @var{prefix-list}
+@end deffn
+
+@deffn {Route-map Command} {match ip address prefix-len @var{0-32}} {}
+Matches the specified @var{prefix-len}. This is a Zebra specific command.
+@end deffn
+
+@deffn {Route-map Command} {match ipv6 address @var{access_list}} {}
+Matches the specified @var{access_list}
+@end deffn
+
+@deffn {Route-map Command} {match ipv6 address @var{prefix-list}} {}
+Matches the specified @var{prefix-list}
+@end deffn
+
+@deffn {Route-map Command} {match ipv6 address prefix-len @var{0-128}} {}
+Matches the specified @var{prefix-len}. This is a Zebra specific command.
+@end deffn
+
+@deffn {Route-map Command} {match ip next-hop @var{ipv4_addr}} {}
+Matches the specified @var{ipv4_addr}.
+@end deffn
+
+@deffn {Route-map Command} {match aspath @var{as_path}} {}
+Matches the specified @var{as_path}.
+@end deffn
+
+@deffn {Route-map Command} {match metric @var{metric}} {}
+Matches the specified @var{metric}.
+@end deffn
+
+@deffn {Route-map Command} {match tag @var{tag}} {}
+Matches the specified tag value associated with the route.
+This tag value can be in the range of (1-4294967295).
+@end deffn
+
+@deffn {Route-map Command} {match local-preference @var{metric}} {}
+Matches the specified @var{local-preference}.
+@end deffn
+
+@deffn {Route-map Command} {match community @var{community_list}} {}
+Matches the specified @var{community_list}
+@end deffn
+
+@deffn {Route-map Command} {match peer @var{ipv4_addr}} {}
+This is a BGP specific match command. Matches the peer ip address
+if the neighbor was specified in this manner.
+@end deffn
+
+@deffn {Route-map Command} {match peer @var{ipv6_addr}} {}
+This is a BGP specific match command. Matches the peer ipv6
+address if the neighbor was specified in this manner.
+@end deffn
+
+@deffn {Route-map Command} {match peer @var{interface_name}} {}
+This is a BGP specific match command. Matches the peer
+interface name specified if the neighbor was specified
+in this manner.
+@end deffn
+
+@node Route Map Set Command
+@section Route Map Set Command
+
+@deffn {Route-map Command} {set tag @var{tag}} {}
+Set a tag on the matched route. This tag value can be from
+(1-4294967295). Additionally if you have compiled with
+the --enable-realms configure option. Tag values from (1-255)
+are sent to the linux kernel as a realm value. Then route
+policy can be applied. See the tc man page.
+@end deffn
+
+@deffn {Route-map Command} {set ip next-hop @var{ipv4_address}} {}
+Set the BGP nexthop address.
+@end deffn
+
+@deffn {Route-map Command} {set local-preference @var{local_pref}} {}
+Set the BGP local preference to @var{local_pref}.
+@end deffn
+
+@deffn {Route-map Command} {set weight @var{weight}} {}
+Set the route's weight.
+@end deffn
+
+@deffn {Route-map Command} {set metric @var{metric}} {}
+@anchor{routemap set metric}
+Set the BGP attribute MED.
+@end deffn
+
+@deffn {Route-map Command} {set as-path prepend @var{as_path}} {}
+Set the BGP AS path to prepend.
+@end deffn
+
+@deffn {Route-map Command} {set community @var{community}} {}
+Set the BGP community attribute.
+@end deffn
+
+@deffn {Route-map Command} {set ipv6 next-hop global @var{ipv6_address}} {}
+Set the BGP-4+ global IPv6 nexthop address.
+@end deffn
+
+@deffn {Route-map Command} {set ipv6 next-hop local @var{ipv6_address}} {}
+Set the BGP-4+ link local IPv6 nexthop address.
+@end deffn
+
+@node Route Map Call Command
+@section Route Map Call Command
+
+@deffn {Route-map Command} {call @var{name}} {}
+Call route-map @var{name}. If it returns deny, deny the route and
+finish processing the route-map.
+@end deffn
+
+@node Route Map Exit Action Command
+@section Route Map Exit Action Command
+
+@deffn {Route-map Command} {on-match next} {}
+@deffnx {Route-map Command} {continue} {}
+Proceed on to the next entry in the route-map.
+@end deffn
+
+@deffn {Route-map Command} {on-match goto @var{N}} {}
+@deffnx {Route-map Command} {continue @var{N}} {}
+Proceed processing the route-map at the first entry whose order is >= N
+@end deffn
+
+@node Route Map Examples
+@section Route Map Examples
+
+A simple example of a route-map:
+
+@example
+@group
+route-map test permit 10
+ match ip address 10
+ set local-preference 200
+@end group
+@end example
+
+This means that if a route matches ip access-list number 10 it's
+local-preference value is set to 200.
+
+See @ref{BGP Configuration Examples} for examples of more sophisticated
+useage of route-maps, including of the @samp{call} action.
IPv4 and IPv6. Support is also provided for multiple sets of per-AFI
information via Subsequent Address Family Identifiers (SAFI). In addition to
unicast information, VPN information :rfc:`4364` and :rfc:`4659`, and
-Encapsulation information :rfc:`5512` is supported.
+Encapsulation attribute :rfc:`5512` is supported.
-.. index:: show ip bgp vpnv4 all
-.. clicmd:: show ip bgp vpnv4 all
+.. index:: show ip bgp ipv4 vpn
+.. clicmd:: show ip bgp ipv4 vpn
-.. index:: show ipv6 bgp vpn all
-.. clicmd:: show ipv6 bgp vpn all
+.. index:: show ipv6 bgp ipv6 vpn
+.. clicmd:: show ipv6 bgp ipv6 vpn
Print active IPV4 or IPV6 routes advertised via the VPN SAFI.
-.. index:: show ip bgp encap all
-.. clicmd:: show ip bgp encap all
-
-.. index:: show ipv6 bgp encap all
-.. clicmd:: show ipv6 bgp encap all
-
- Print active IPV4 or IPV6 routes advertised via the Encapsulation SAFI.
-
-.. index:: show bgp ipv4 encap summary
-.. clicmd:: show bgp ipv4 encap summary
-
.. index:: show bgp ipv4 vpn summary
.. clicmd:: show bgp ipv4 vpn summary
-.. index:: show bgp ipv6 encap summary
-.. clicmd:: show bgp ipv6 encap summary
-
.. index:: show bgp ipv6 vpn summary
.. clicmd:: show bgp ipv6 vpn summary
BGP, with IP VPNs and Tunnel Encapsulation, is used to distribute VN
information between NVAs. BGP based IP VPN support is defined in :rfc:`4364`,
-and :rfc:`4659`. Both the Encapsulation Subsequent Address Family Identifier
-(SAFI) and the Tunnel Encapsulation Attribute, :rfc:`5512` are supported.
+and :rfc:`4659`. Encapsulation information is provided via the Tunnel
+Encapsulation Attribute, :rfc:`5512`.
The protocol that is used to communicate routing and Ethernet / Layer 2 (L2)
forwarding information between NVAs and NVEs is referred to as the Remote
- :dfn:`General VNC` configuration applies to general VNC operation and is
primarily used to control the method used to advertise tunnel information.
- - :dfn:`Remote Forwarder Protocol (RFP)` configuration relates to the protocol
- used between NVAs and NVEs.
- - :dfn:`VNC Defaults` provides default parameters for registered NVEs.
- - :dfn:`VNC NVE Group` provides for configuration of a specific set of
- registered NVEs and overrides default parameters.
- - :dfn:`Redistribution` and :dfn:`Export` control VNC-GW operation, i.e., the
- import/export of routing information between VNC and customer edge routers
- (:abbr:`CE`s) operating within a VN.
+- :dfn:`Remote Forwarder Protocol (RFP)` configuration relates to the protocol
+ used between NVAs and NVEs.
- .. _General_VNC_Configuration:
+- :dfn:`VNC Defaults` provides default parameters for registered NVEs.
- General VNC Configuration
- -------------------------
+- :dfn:`VNC NVE Group` provides for configuration of a specific set of
+ registered NVEs and overrides default parameters.
- .. clicmd:: vnc advertise-un-method encap-safi|encap-attr
+- :dfn:`Redistribution` and :dfn:`Export` control VNC-GW operation, i.e., the
+ import/export of routing information between VNC and customer edge routers
+ (:abbr:`CE` s) operating within a VN.
- Advertise NVE underlay-network IP addresses using the encapsulation SAFI
- (`encap-safi`) or the UN address sub-TLV of the Tunnel Encapsulation
- attribute (`encap-attr`). When `encap-safi` is used, neighbors under
- `address-family encap` and/or `address-family encapv6` must be configured.
- The default is `encap-attr`.
- .. _RFP_Related_Configuration:
+.. _General_VNC_Configuration:
+
+.. General VNC Configuration
+.. -------------------------
+
+.. _RFP_Related_Configuration:
RFP Related Configuration
-------------------------
::
-vnc defaults
-... various VNC defaults
-exit-vnc
+ vnc defaults
+ ... various VNC defaults
+ exit-vnc
These are the statements that can appear between ``vnc defaults`` and
in one of the following forms:
- ``IPv4-address:two-byte-integer``
- - ``four-byte-autonomous-system-number:two-byte-integer``
- - ``two-byte-autonomous-system-number:four-byte-integer``
+ - ``four-byte-autonomous-system-number:two-byte-integer``
+ - ``two-byte-autonomous-system-number:four-byte-integer``
- If no default import RT list is specified, then the default import RT list
- is empty. If no default export RT list is specified, then the default export
- RT list is empty.
+ If no default import RT list is specified, then the default import RT list
+ is empty. If no default export RT list is specified, then the default export
+ RT list is empty.
- A complete definition of these parameters is given below
- (:ref:`VNC_NVE_Group_Configuration`).
+ A complete definition of these parameters is given below
+ (:ref:`VNC_NVE_Group_Configuration`).
- .. index:: rd route-distinguisher
- .. clicmd:: rd ROUTE-DISTINGUISHER
+.. index:: rd route-distinguisher
+.. clicmd:: rd ROUTE-DISTINGUISHER
Specify the default route distinguisher (RD) for routes advertised via BGP
VPNs. The route distinguisher must be in one of four forms:
- ``IPv4-address:two-byte-integer``
- - ``four-byte-autonomous-system-number:two-byte-integer``
- - ``two-byte-autonomous-system-number:four-byte-integer``
- - ``auto:vn:two-byte-integer``
+ - ``four-byte-autonomous-system-number:two-byte-integer``
+ - ``two-byte-autonomous-system-number:four-byte-integer``
+ - ``auto:vn:two-byte-integer``
- If RD is specified in the defaults section, the default RD value is
- `two-byte-autonomous-system-number=0`:`four-byte-integer=0`.
+ If RD is specified in the defaults section, the default RD value is
+ `two-byte-autonomous-system-number=0:four-byte-integer=0`.
- A complete definition of this parameter is given below
- (:ref:`VNC_NVE_Group_Configuration`).
+ A complete definition of this parameter is given below
+ (:ref:`VNC_NVE_Group_Configuration`).
- .. index:: l2rd NVE-ID-VALUE
-
- .. clicmd:: l2rd NVE-ID-VALUE
+.. index:: l2rd NVE-ID-VALUE
+.. clicmd:: l2rd NVE-ID-VALUE
Set the value used to distinguish NVEs connected to the same logical
Ethernet segment (i.e., L2VPN). A complete definition of this parameter is
given below (:ref:`VNC_NVE_Group_Configuration`).
- .. index:: response-lifetime LIFETIME|infinite
- .. clicmd:: response-lifetime LIFETIME|infinite
+.. index:: response-lifetime LIFETIME|infinite
+.. clicmd:: response-lifetime LIFETIME|infinite
Specify the default lifetime to be included in RFP response messages sent to
NVEs.
(:ref:`VNC_NVE_Group_Configuration`).
.. index:: export bgp|zebra route-map MAP-NAME
-
.. clicmd:: export bgp|zebra route-map MAP-NAME
-Specify that the named route-map should be applied to routes being exported
-to bgp or zebra.
+ Specify that the named route-map should be applied to routes being exported
+ to bgp or zebra.
.. index:: export bgp|zebra no route-map
-
.. clicmd:: export bgp|zebra no route-map
-Specify that no route-map should be applied to routes being exported to bgp
-or zebra.
+ Specify that no route-map should be applied to routes being exported to bgp
+ or zebra.
.. index:: exit-vnc
.. clicmd:: exit-vnc
Exit VNC configuration mode.
- .. _VNC_NVE_Group_Configuration:
+.. _VNC_NVE_Group_Configuration:
VNC NVE Group Configuration
---------------------------
::
- vnc nve-group group1
- ... configuration commands
- exit-vnc
+ vnc nve-group group1
+ ... configuration commands
+ exit-vnc
.. index:: no vnc nve-group NAME
The following statements are valid in an NVE group definition:
- .. index:: l2rd NVE-ID-VALUE
- .. clicmd:: l2rd NVE-ID-VALUE
+.. index:: l2rd NVE-ID-VALUE
+.. clicmd:: l2rd NVE-ID-VALUE
-Set the value used to distinguish NVEs connected to the same physical
-Ethernet segment (i.e., at the same location) [#]_.
+ Set the value used to distinguish NVEs connected to the same physical
+ Ethernet segment (i.e., at the same location) [#]_.
-The nve-id subfield may be specified as either a literal value in the range
-1-255, or it may be specified as `auto:vn`, which means to use the
-least-significant octet of the originating NVE's VN address.
+ The nve-id subfield may be specified as either a literal value in the range
+ 1-255, or it may be specified as `auto:vn`, which means to use the
+ least-significant octet of the originating NVE's VN address.
.. index:: prefix vn|un A.B.C.D/M|X:X::X:X/M
.. clicmd:: prefix vn|un A.B.C.D/M|X:X::X:X/M
- Specify the matching prefix for this NVE group by either virtual-network
- address (`vn`) or underlay-network address (`un`). Either or both
- virtual-network and underlay-network prefixes may be specified. Subsequent
- virtual-network or underlay-network values within a `vnc nve-group`
- `exit-vnc` block override their respective previous values.
+ Specify the matching prefix for this NVE group by either virtual-network
+ address (`vn`) or underlay-network address (`un`). Either or both
+ virtual-network and underlay-network prefixes may be specified. Subsequent
+ virtual-network or underlay-network values within a `vnc nve-group`
+ `exit-vnc` block override their respective previous values.
- These prefixes are used only for determining assignments of NVEs to NVE
- Groups.
+ These prefixes are used only for determining assignments of NVEs to NVE
+ Groups.
- .. index:: rd ROUTE-DISTINGUISHER
- .. clicmd:: rd ROUTE-DISTINGUISHER
+.. index:: rd ROUTE-DISTINGUISHER
+.. clicmd:: rd ROUTE-DISTINGUISHER
Specify the route distinguisher for routes advertised via BGP
VPNs. The route distinguisher must be in one of these forms:
- ``IPv4-address:two-byte-integer``
- - ``four-byte-autonomous-system-number:two-byte-integer``
- - ``two-byte-autonomous-system-number:four-byte-integer``
- - ``auto:vn:`two-byte-integer`
-
- Routes originated by NVEs in the NVE group will use the group's specified
- `route-distinguisher` when they are advertised via BGP. If the `auto` form
- is specified, it means that a matching NVE has its RD set to
- ``rd_type=IP=1:IPv4-address=VN-address:two-byte-integer``, for IPv4 VN
- addresses and
- ``rd_type=IP=1`:`IPv4-address=Last-four-bytes-of-VN-address:two-byte-integer``,
- for IPv6 VN addresses.
+ - ``four-byte-autonomous-system-number:two-byte-integer``
+ - ``two-byte-autonomous-system-number:four-byte-integer``
+ - ``auto:vn:`two-byte-integer`
+
+ Routes originated by NVEs in the NVE group will use the group's specified
+ `route-distinguisher` when they are advertised via BGP. If the `auto` form
+ is specified, it means that a matching NVE has its RD set to
+ ``rd_type=IP=1:IPv4-address=VN-address:two-byte-integer``, for IPv4 VN
+ addresses and
+ ``rd_type=IP=1:IPv4-address=Last-four-bytes-of-VN-address:two-byte-integer``,
+ for IPv6 VN addresses.
+
+ If the NVE group definition does not specify a `route-distinguisher`, then
+ the default `route-distinguisher` is used. If neither a group nor a default
+ `route-distinguisher` is configured, then the advertised RD is set to
+ ``two-byte-autonomous-system-number=0:four-byte-integer=0``.
+
+.. index:: response-lifetime LIFETIME|infinite
+.. clicmd:: response-lifetime LIFETIME|infinite
+
+ Specify the response lifetime, in seconds, to be included in RFP response
+ messages sent to NVEs. If the value 'infinite' is given, an infinite
+ lifetime will be used.
+
+ Note that this parameter is not the same as the lifetime supplied by NVEs in
+ RFP registration messages. This parameter does not affect the lifetime value
+ attached to routes sent by this server via BGP.
+
+ If the NVE group definition does not specify a `response-lifetime`, the
+ default `response-lifetime` will be used. If neither a group nor a default
+ `response-lifetime` is configured, the value 3600 will be used. The maximum
+ response lifetime is 2147483647.
- If the NVE group definition does not specify a `route-distinguisher`, then
- the default `route-distinguisher` is used. If neither a group nor a default
- `route-distinguisher` is configured, then the advertised RD is set to
- ``two-byte-autonomous-system-number=0:four-byte-integer=0``.
-
- .. index:: response-lifetime LIFETIME|infinite
- .. clicmd:: response-lifetime LIFETIME|infinite
-
- Specify the response lifetime, in seconds, to be included in RFP response
- messages sent to NVEs. If the value 'infinite' is given, an infinite
- lifetime will be used.
-
- Note that this parameter is not the same as the lifetime supplied by NVEs in
- RFP registration messages. This parameter does not affect the lifetime value
- attached to routes sent by this server via BGP.
-
- If the NVE group definition does not specify a `response-lifetime`, the
- default `response-lifetime` will be used. If neither a group nor a default
- `response-lifetime` is configured, the value 3600 will be used. The maximum
- response lifetime is 2147483647.
-
- .. index:: rt export RT-LIST
- .. clicmd:: rt export RT-LIST
+.. index:: rt export RT-LIST
+.. clicmd:: rt export RT-LIST
- .. index:: rt import RT-LIST
- .. clicmd:: rt import RT-LIST
+.. index:: rt import RT-LIST
+.. clicmd:: rt import RT-LIST
.. index:: rt both RT-LIST
.. clicmd:: rt both RT-LIST
space-separated list of route targets, each element of which is
in one of the following forms:
- ``IPv4-address:two-byte-integer``
- ``four-byte-autonomous-system-number:two-byte-integer``
- ``two-byte-autonomous-system-number:four-byte-integer``
+ - ``IPv4-address:two-byte-integer``
+ - ``four-byte-autonomous-system-number:two-byte-integer``
+ - ``two-byte-autonomous-system-number:four-byte-integer``
The first form, `rt export`, specifies an `export rt-list`. The `export
rt-list` will be attached to routes originated by NVEs in the NVE group
The second form, `rt import` specifies an `import rt-list`, which is a
filter for incoming routes. In order to be made available to NVEs in the
- group, incoming BGP VPN and `ENCAP` `SAFI` (when `vnc advertise-un-method
- encap-safi` is set) routes must have RT lists that have at least one
+ group, incoming BGP VPN routes must have RT lists that have at least one
route target in common with the group's `import rt-list`.
If the NVE group definition does not specify an import filter, then the
.. index:: no vnc redistribute ipv4|ipv6 bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static
.. clicmd:: no vnc redistribute ipv4|ipv6 bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static
-
Import (or do not import) prefixes from another routing protocols. Specify
both the address family to import (`ipv4` or `ipv6`) and the protocol
(`bgp`, `bgp-direct`, `bgp-direct-to-nve-groups`, `connected`, `kernel`,
.. index:: vnc redistribute mode plain|nve-group|resolve-nve
.. clicmd:: vnc redistribute mode plain|nve-group|resolve-nve
-
Redistribute routes from other protocols into VNC using the specified mode.
Not all combinations of modes and protocols are supported.
.. index:: no vnc redistribute nve-group GROUP-NAME
.. clicmd:: no vnc redistribute nve-group GROUP-NAME
-
When using `nve-group` mode, assign (or do not assign) the NVE group
`group-name` to routes redistributed from another routing protocol.
`group-name` must be configured using `vnc nve-group`.
.. index:: vnc redistribute lifetime LIFETIME|infinite
.. clicmd:: vnc redistribute lifetime LIFETIME|infinite
-
Assign a registration lifetime, either `lifetime` seconds or `infinite`, to
prefixes redistributed from other routing protocols as if they had been
received via RFP registration messages from an NVE. `lifetime` can be any
.. index:: vnc redistribute resolve-nve roo-ec-local-admin 0-65536
.. clicmd:: vnc redistribute resolve-nve roo-ec-local-admin 0-65536
-
Assign a value to the local-administrator subfield used in the
Route Origin extended community that is assigned to routes exported
under the `resolve-nve` mode. The default value is `5226`.
.. index:: export bgp|zebra mode none|group-nve|registering-nve|ce
.. clicmd:: export bgp|zebra mode none|group-nve|registering-nve|ce
-
Specify how routes should be exported to bgp or zebra. If the mode is
`none`, routes are not exported. If the mode is `group-nve`, routes are
exported according to nve-group or vrf-policy group configuration
The next hop of the exported route is set to the encoded NVE connected CE
Router.
- The default for both bgp and zebra is mode `none`.
+ The default for both bgp and zebra is mode `none`.
.. index:: vnc export bgp|zebra group-nve group GROUP-NAME
.. clicmd:: vnc export bgp|zebra group-nve group GROUP-NAME
.. index:: export bgp|zebra no ipv4|ipv6 prefix-list
.. clicmd:: export bgp|zebra no ipv4|ipv6 prefix-list
- When export mode is `ce` or `registering-nve`,
- specifies that no prefix-list should be applied to routes
- being exported to bgp or zebra.
+ When export mode is `ce` or `registering-nve`,
+ specifies that no prefix-list should be applied to routes
+ being exported to bgp or zebra.
.. index:: export bgp|zebra route-map MAP-NAME
.. clicmd:: export bgp|zebra route-map MAP-NAME
-
When export mode is `ce` or `registering-nve`, specifies that the named
route-map should be applied to routes being exported to bgp or zebra.
.. index:: show memory vnc
.. clicmd:: show memory vnc
-
Print the number of memory items allocated by the NVA.
.. _Example_VNC_and_VNC-GW_Configurations:
Example VNC and VNC-GW Configurations
=====================================
+.. _vnc-mesh-nva-config:
+
+Mesh NVA Configuration
+----------------------
+
+This example includes three NVAs, nine NVEs, and two NVE groups. Note that
+while not shown, a single physical device may support multiple logical NVEs.
+:figure:`fig-vnc-mesh` shows ``code NVA-1`` (192.168.1.100), ``NVA 2``
+(192.168.1.101), and ``NVA 3`` (192.168.1.102), which are connected in a full
+mesh. Each is a member of the autonomous system 64512. Each NVA provides VNC
+services to three NVE clients in the 172.16.0.0/16 virtual-network address
+range. The 172.16.0.0/16 address range is partitioned into two NVE groups,
+``group1`` (172.16.0.0/17) and ``group2`` (172.16.128.0/17).
+
+Each NVE belongs to either NVE group ``group1`` or NVE group
+``group2``. The NVEs ``NVE 1``, ``NVE 2``, @code{NVE
+4}, ``NVE 7``, and ``NVE 8`` are members of the NVE group
+``group1``. The NVEs ``NVE 3``, ``NVE 5``, @code{NVE
+6}, and ``NVE 9`` are members of the NVE group ``group2``.
+
+Each NVA advertises NVE underlay-network IP addresses using the
+Tunnel Encapsulation Attribute.
+
+.. _vnc-fig-vnc-mesh:
+.. figure:: ../figure/fig-vnc-mesh.png
+ :align: center
+ :alt: Three-way Mesh
+
+ A three-way full mesh with three NVEs per NVA.
+
+:file:`bgpd.conf` for ``NVA 1`` (192.168.1.100):::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.100
+
+ neighbor 192.168.1.101 remote-as 64512
+ neighbor 192.168.1.102 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.101 activate
+ neighbor 192.168.1.102 activate
+ exit-address-family
+
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rt both 1000:1
+ exit-vnc
+
+ vnc nve-group group2
+ prefix vn 172.16.128.0/17
+ rt both 1000:2
+ exit-vnc
+
+ exit
+
+:file:`bgpd.conf` for ``NVA 2`` (192.168.1.101):::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.101
+
+ neighbor 192.168.1.100 remote-as 64512
+ neighbor 192.168.1.102 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ neighbor 192.168.1.102 activate
+ exit-address-family
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+ exit
+
+:file:`bgpd.conf` for ``NVA 3`` (192.168.1.102):::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.102
+
+ neighbor 192.168.1.101 remote-as 64512
+ neighbor 192.168.1.102 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ neighbor 192.168.1.101 activate
+ exit-address-family
+
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.128.0/17
+ exit-vnc
+ exit
+
+
+Mesh NVA and VNC-GW Configuration
+---------------------------------
+
+This example includes two NVAs, each with two associated NVEs, and two VNC-GWs,
+each supporting two CE routers physically attached to the four NVEs. Note that
+this example is showing a more complex configuration where VNC-GW is separated
+from normal NVA functions; it is equally possible to simplify the configuration
+and combine NVA and VNC-GW functions in a single FRR instance.
+
+.. _vnc-fig-vnc-gw:
+.. figure:: ../figures/fig-vnc-gw.png
+ :align: center
+ :alt: FRR VNC Gateway
+
+ Meshed NVEs and VNC-GWs
+
+As shown in :figure:`fig-vnc-gw`, NVAs and VNC-GWs are connected in a full iBGP
+mesh. The VNC-GWs each have two CEs configured as route-reflector clients.
+Each client provides BGP updates with unicast routes that the VNC-GW reflects
+to the other client. The VNC-GW also imports these unicast routes into VPN
+routes to be shared with the other VNC-GW and the two NVAs. This route
+importation is controlled with the ``vnc redistribute`` statements shown in the
+configuration. Similarly, registrations sent by NVEs via RFP to the NVAs are
+exported by the VNC-GWs to the route-reflector clients as unicast routes. RFP
+registrations exported this way have a next-hop address of the CE behind the
+connected (registering) NVE. Exporting VNC routes as IPv4 unicast is enabled
+with the ``vnc export`` command below.
+
+The configuration for ``VNC-GW 1`` is shown below.::
+
+ router bgp 64512
+ bgp router-id 192.168.1.101
+ bgp cluster-id 1.2.3.4
+ neighbor 192.168.1.102 remote-as 64512
+ neighbor 192.168.1.103 remote-as 64512
+ neighbor 192.168.1.104 remote-as 64512
+ neighbor 172.16.1.2 remote-as 64512
+ neighbor 172.16.2.2 remote-as 64512
+ !
+ address-family ipv4 unicast
+ redistribute vnc-direct
+ no neighbor 192.168.1.102 activate
+ no neighbor 192.168.1.103 activate
+ no neighbor 192.168.1.104 activate
+ neighbor 172.16.1.2 route-reflector-client
+ neighbor 172.16.2.2 route-reflector-client
+ exit-address-family
+ !
+ address-family ipv4 vpn
+ neighbor 192.168.1.102 activate
+ neighbor 192.168.1.103 activate
+ neighbor 192.168.1.104 activate
+ exit-address-family
+ vnc export bgp mode ce
+ vnc redistribute mode resolve-nve
+ vnc redistribute ipv4 bgp-direct
+ exit
+
+Note that in the VNC-GW configuration, the neighboring VNC-GW and NVAs each
+have a statement disabling the IPv4 unicast address family. IPv4 unicast is on
+by default and this prevents the other VNC-GW and NVAs from learning unicast
+routes advertised by the route-reflector clients.
+
+Configuration for ``NVA 2``:::
+
+ router bgp 64512
+ bgp router-id 192.168.1.104
+ neighbor 192.168.1.101 remote-as 64512
+ neighbor 192.168.1.102 remote-as 64512
+ neighbor 192.168.1.103 remote-as 64512
+ !
+ address-family ipv4 unicast
+ no neighbor 192.168.1.101 activate
+ no neighbor 192.168.1.102 activate
+ no neighbor 192.168.1.103 activate
+ exit-address-family
+ !
+ address-family ipv4 vpn
+ neighbor 192.168.1.101 activate
+ neighbor 192.168.1.102 activate
+ neighbor 192.168.1.103 activate
+ exit-address-family
+ !
+ vnc defaults
+ response-lifetime 3600
+ exit-vnc
+ vnc nve-group nve1
+ prefix vn 172.16.1.1/32
+ response-lifetime 3600
+ rt both 1000:1 1000:2
+ exit-vnc
+ vnc nve-group nve2
+ prefix vn 172.16.2.1/32
+ response-lifetime 3600
+ rt both 1000:1 1000:2
+ exit-vnc
+ exit
+
+.. TBD make this its own example:
+..
+.. @float Figure,fig:fig-vnc-gw-rr
+.. @center @image{fig-vnc-gw-rr,400pt,,Frr VNC Gateway with RR}
+.. @end float
+.. An NVA can also import unicast routes from BGP without advertising the
+.. imported routes as VPN routes. Such imported routes, while not
+.. distributed to other NVAs or VNC-GWs, are are available to NVEs via
+.. RFP query messages sent to the NVA. @ref{fig:fig-vnc-gw-rr}
+.. shows an example topology where unicast routes are imported into NVAs
+.. from a Route Reflector. (@pxref{Route Reflector} for route reflector
+.. configuration details.) The following three lines can be added to the
+.. ``NVA 1`` and ``NVA 2`` configurations to import routes into VNC
+.. for local VNC use:
+..
+.. @verbatim
+.. neighbor 192.168.1.105 remote-as 64512
+.. vnc redistribute mode plain
+.. vnc redistribute ipv4 bgp-direct-to-nve-groups
+.. @end verbatim
+
+.. _vnc-with-frr-route-reflector-config:
+
+VNC with FRR Route Reflector Configuration
+------------------------------------------
+
+A route reflector eliminates the need for a fully meshed NVA network by acting
+as the hub between NVAs. :figure:`vnc-fig-vnc-frr-route-reflector` shows BGP
+route reflector ``BGP Route Reflector 1`` (192.168.1.100) as a route reflector
+for NVAs ``NVA 2``(192.168.1.101) and ``NVA 3`` (192.168.1.102).
+
+@float Figure,fig:fig-vnc-frr-route-reflector @center
+@image{fig-vnc-frr-route-reflector,400pt,,Frr Route Reflector} @caption{Two
+NVAs and a BGP Route Reflector} @end float
+
+.. _vnc-fig-vnc-frr-route-reflector:
+.. figure:: ../figures/fig-vnc-frr-route-reflector.png
+ :align: center
+ :alt: FRR Route Reflector
+
+ Two NVAs and a BGP Route Reflector
+
+``NVA 2`` and ``NVA 3`` advertise NVE underlay-network IP addresses using the
+Tunnel Encapsulation Attribute. ``BGP Route Reflector 1`` ``reflects''
+advertisements from ``NVA 2`` to ``NVA 3`` and vice versa.
+
+As in the example of :ref:`vnc-mesh-nva-config`, there are two NVE groups. The
+172.16.0.0/16 address range is partitioned into two NVE groups, ``group1``
+(172.16.0.0/17) and ``group2`` (172.16.128.0/17). The NVE ``NVE 4``, ``NVE
+7``, and ``NVE 8`` are members of the NVE group ``group1``. The NVEs ``NVE
+5``, ``NVE 6``, and ``NVE 9`` are members of the NVE group ``group2``.
+
+:file:`bgpd.conf` for ``BGP Route Reflector 1`` on 192.168.1.100:::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.100
+
+ neighbor 192.168.1.101 remote-as 64512
+ neighbor 192.168.1.101 port 7179
+ neighbor 192.168.1.101 description iBGP-client-192-168-1-101
+
+ neighbor 192.168.1.102 remote-as 64512
+ neighbor 192.168.1.102 port 7179
+ neighbor 192.168.1.102 description iBGP-client-192-168-1-102
+
+ address-family ipv4 unicast
+ neighbor 192.168.1.101 route-reflector-client
+ neighbor 192.168.1.102 route-reflector-client
+ exit-address-family
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.101 activate
+ neighbor 192.168.1.102 activate
+
+ neighbor 192.168.1.101 route-reflector-client
+ neighbor 192.168.1.102 route-reflector-client
+ exit-address-family
+
+ exit
+
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.101
+
+ neighbor 192.168.1.100 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ exit-address-family
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+ exit
+
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.102:::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.102
+
+ neighbor 192.168.1.100 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ exit-address-family
+
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.128.0/17
+ exit-vnc
+ exit
+
+While not shown, an NVA can also be configured as a route reflector.
+
+.. _vnc-with-commercial-route-reflector-config:
+
+VNC with Commercial Route Reflector Configuration
+-------------------------------------------------
+
+This example is identical to :ref:`vnc-with-frr-route-reflector-configuration`
+with the exception that the route reflector is a commercial router. Only the
+VNC-relevant configuration is provided.
+
+.. figure:: ../figures/fig-vnc-commercial-route-reflector
+ :align: center
+ :alt: Commercial Route Reflector
+
+ Two NVAs with a commercial route reflector
+
+:file:`bgpd.conf` for BGP route reflector ``Commercial Router`` on 192.168.1.104:::
+
+ version 8.5R1.13;
+ routing-options {
+ rib inet.0 {
+ static {
+ route 172.16.0.0/16 next-hop 192.168.1.104;
+ }
+ }
+ autonomous-system 64512;
+ resolution {
+ rib inet.3 {
+ resolution-ribs inet.0;
+ }
+ rib bgp.l3vpn.0 {
+ resolution-ribs inet.0;
+ }
+ }
+ }
+ protocols {
+ bgp {
+ advertise-inactive;
+ family inet {
+ labeled-unicast;
+ }
+ group 1 {
+ type internal;
+ advertise-inactive;
+ advertise-peer-as;
+ import h;
+ family inet {
+ unicast;
+ }
+ family inet-vpn {
+ unicast;
+ }
+ cluster 192.168.1.104;
+ neighbor 192.168.1.101;
+ neighbor 192.168.1.102;
+ }
+ }
+ }
+ policy-options {
+ policy-statement h {
+ from protocol bgp;
+ then {
+ as-path-prepend 64512;
+ accept;
+ }
+ }
+ }
+
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.101
+
+ neighbor 192.168.1.100 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ exit-address-family
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+ exit
+
+:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102:::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.102
+
+ neighbor 192.168.1.100 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ exit-address-family
+
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.128.0/17
+ exit-vnc
+ exit
+
+VNC with Redundant Route Reflectors Configuration
+-------------------------------------------------
+
+This example combines the previous two
+(:ref:`vnc-with-frr-route-reflector-config` and
+:ref:`vnc-with-commercial-route-reflector-config`) into a redundant route
+reflector configuration. BGP route reflectors ``BGP Route Reflector 1`` and
+``Commercial Router`` are the route reflectors for NVAs ``NVA 2`` and ``NVA
+3``. The two NVAs have connections to both route reflectors.
+
+.. figure:: ../fig-vnc-redundant-route-reflectors.png
+ :align: center
+ :alt: Redundant Route Reflectors
+
+ FRR-based NVA with redundant route reflectors
+
+:file:`bgpd.conf` for ``Bgpd Route Reflector 1`` on 192.168.1.100:::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.100
+ bgp cluster-id 192.168.1.100
+
+ neighbor 192.168.1.104 remote-as 64512
+
+ neighbor 192.168.1.101 remote-as 64512
+ neighbor 192.168.1.101 description iBGP-client-192-168-1-101
+ neighbor 192.168.1.101 route-reflector-client
+
+ neighbor 192.168.1.102 remote-as 64512
+ neighbor 192.168.1.102 description iBGP-client-192-168-1-102
+ neighbor 192.168.1.102 route-reflector-client
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.101 activate
+ neighbor 192.168.1.102 activate
+ neighbor 192.168.1.104 activate
+
+ neighbor 192.168.1.101 route-reflector-client
+ neighbor 192.168.1.102 route-reflector-client
+ exit-address-family
+ exit
+
+:file:`bgpd.conf` for ``NVA 2`` on 192.168.1.101:::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.101
+
+ neighbor 192.168.1.100 remote-as 64512
+ neighbor 192.168.1.104 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ neighbor 192.168.1.104 activate
+ exit-address-family
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+ exit
+
+:file:`bgpd.conf` for ``NVA 3`` on 192.168.1.102:::
+
+ router bgp 64512
+
+ bgp router-id 192.168.1.102
+
+ neighbor 192.168.1.100 remote-as 64512
+ neighbor 192.168.1.104 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ neighbor 192.168.1.104 activate
+ exit-address-family
+
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.128.0/17
+ exit-vnc
+ exit
+
+:file:`bgpd.conf` for the Commercial Router route reflector on 192.168.1.104:::
+
+ routing-options {
+ rib inet.0 {
+ static {
+ route 172.16.0.0/16 next-hop 192.168.1.104;
+ }
+ }
+ autonomous-system 64512;
+ resolution {
+ rib inet.3 {
+ resolution-ribs inet.0;
+ }
+ rib bgp.l3vpn.0 {
+ resolution-ribs inet.0;
+ }
+ }
+ }
+ protocols {
+ bgp {
+ advertise-inactive;
+ family inet {
+ labeled-unicast;
+ }
+ group 1 {
+ type internal;
+ advertise-inactive;
+ advertise-peer-as;
+ import h;
+ family inet {
+ unicast;
+ }
+ family inet-vpn {
+ unicast;
+ }
+ cluster 192.168.1.104;
+ neighbor 192.168.1.101;
+ neighbor 192.168.1.102;
+ }
+
+ group 2 {
+ type internal;
+ advertise-inactive;
+ advertise-peer-as;
+ import h;
+ family inet {
+ unicast;
+ }
+ family inet-vpn {
+ unicast;
+ }
+ neighbor 192.168.1.100;
+ }
+
+ }
+ }
+ policy-options {
+ policy-statement h {
+ from protocol bgp;
+ then {
+ as-path-prepend 64512;
+ accept;
+ }
+ }
+ }
+
.. [#] The nve-id is carriedin the route distinguisher. It is the second octet
of the eight-octet route distinguisher generated for Ethernet / L2
advertisements. The first octet is a constant 0xFF, and the third
--- /dev/null
+@c -*-texinfo-*-
+@c This is part of the Frr Manual.
+@c @value{COPYRIGHT_STR}
+@c See file frr.texi for copying conditions.
+
+@node VNC and VNC-GW
+@chapter VNC and VNC-GW
+This chapter describes how to use
+Virtual Network Control (@acronym{VNC}) services,
+including Network Virtualization Authority (@acronym{NVA}) and
+VNC Gateway (@acronym{VNC-GW}) functions.
+Background information on NVAs,
+Network Virtualization Edges (@acronym{NVE}s), underlay networks (@acronym{UN}s),
+and virtual networks (@acronym{VN}s) is available from the
+@url{https://datatracker.ietf.org/wg/nvo3,IETF Network Virtualization Overlays (@acronym{NVO3}) Working Group}.
+VNC Gateways (@acronym{VNC-GW}s) support the import/export of routing
+information between VNC and customer edge routers (@acronym{CE}s)
+operating within a VN. Both IP/Layer 3 (L3) VNs, and IP with
+Ethernet/Layer 2 (L2) VNs are supported.
+
+BGP, with IP VPNs and Tunnel Encapsulation, is used to distribute VN
+information between NVAs. BGP based IP VPN support is defined in
+@cite{RFC4364, BGP/MPLS IP Virtual Private Networks (VPNs)}, and
+@cite{RFC4659, BGP-MPLS IP Virtual Private Network (VPN) Extension for
+IPv6 VPN }. Encapsulation information is provided via
+the Tunnel Encapsulation Attribute, @cite{RFC5512, The BGP
+Encapsulation Subsequent Address Family Identifier (SAFI) and the BGP
+Tunnel Encapsulation Attribute}, are supported.
+
+The protocol that is used to communicate routing and Ethernet / Layer 2
+(L2) forwarding information between NVAs and NVEs is referred to as the
+Remote Forwarder Protocol (RFP). @code{OpenFlow} is an example
+RFP. Specific RFP implementations may choose to implement either a
+@code{hard-state} or @code{soft-state} prefix and address registration
+model. To support a @code{soft-state} refresh model, a @var{lifetime}
+in seconds is associated with all registrations and responses.
+
+The chapter also provides sample configurations for basic example scenarios.
+
+@menu
+* Configuring VNC Services::
+* Manual Address Control::
+* Other VNC-Related Commands::
+* Example VNC and VNC-GW Configurations::
+* Release Notes::
+@end menu
+
+@node Configuring VNC Services
+@section Configuring VNC
+
+Virtual Network Control (@acronym{VNC}) service configuration commands
+appear in the @code{router bgp} section of the BGPD configuration file
+(@pxref{BGP Configuration Examples}). The commands are broken down into
+the following areas:
+
+@menu
+* General VNC Configuration::
+* RFP Related Configuration::
+* VNC Defaults Configuration::
+* VNC NVE Group Configuration::
+* VNC L2 Group Configuration::
+* Configuring Redistribution of Routes from Other Routing Protocols::
+* Configuring Export of Routes to Other Routing Protocols::
+@end menu
+
+@code{General VNC} configuration applies to general VNC operation and is
+primarily used to control the method used to advertise tunnel
+information.
+
+@code{Remote Forwarder Protocol (RFP)} configuration relates to the
+protocol used between NVAs and NVEs.
+
+@code{VNC Defaults} provides default parameters for registered NVEs.
+
+@code{VNC NVE Group} provides for configuration of a specific set of
+registered NVEs and overrides default parameters.
+
+@code{Redistribution} and @code{Export} control VNC-GW operation, i.e.,
+the import/export of routing
+information between VNC and customer edge routers (@acronym{CE}s)
+operating within a VN.
+
+@node General VNC Configuration
+@subsection General VNC Configuration
+
+@deffn {VNC} {vnc advertise-un-method encap-attr} {}
+Advertise NVE underlay-network IP addresses using
+the UN address sub-TLV of the Tunnel Encapsulation attribute
+(@code{encap-attr}). The default is @code{encap-attr}.
+@end deffn
+
+@node RFP Related Configuration
+@subsection RFP Related Configuration
+
+The protocol that is used to communicate routing and Ethernet / L2
+forwarding information between NVAs and NVEs is referred to as the
+Remote Forwarder Protocol (RFP). Currently, only a simple example RFP
+is included in Frr. Developers may use this example as a starting
+point to integrate Frr with an RFP of their choosing, e.g.,
+@code{OpenFlow}. The example code includes the following sample
+configuration:
+
+@deffn {RFP} {rfp example-config-value @var{VALUE}}
+This is a simple example configuration parameter included as part of the
+RFP example code. @code{VALUE} must be in the range of 0 to 4294967295.
+@end deffn
+
+@node VNC Defaults Configuration
+@subsection VNC Defaults Configuration
+
+The VNC Defaults section allows the user to specify default values for
+configuration parameters for all registered NVEs.
+Default values are overridden by @ref{VNC NVE Group Configuration}.
+
+@deffn {VNC} {vnc defaults} {}
+Enter VNC configuration mode for specifying VNC default behaviors. Use
+@code{exit-vnc} to leave VNC configuration mode. @code{vnc
+defaults} is optional.
+
+@example
+vnc defaults
+ ... various VNC defaults
+exit-vnc
+@end example
+@end deffn
+
+These are the statements that can appear between @code{vnc defaults}
+and @code{exit-vnc}.
+
+@deffn {VNC} {rt import @var{rt-list}} {}
+@deffnx {VNC} {rt export @var{rt-list}} {}
+@deffnx {VNC} {rt both @var{rt-list}} {}
+
+Specify default route target import and export lists. @var{rt-list} is a
+space-separated list of route targets, each element of which is
+in one of the following forms:
+@itemize
+@item @var{IPv4-address}:@var{two-byte-integer}
+@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer}
+@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer}
+@end itemize
+
+If no default import RT list is specified, then the default import RT
+list is empty.
+If no default export RT list is specified, then the default export RT
+list is empty.
+
+A complete definition of these parameters is
+given below (@pxref{VNC NVE Group Configuration}).
+
+@end deffn
+
+@deffn {VNC} {rd @var{route-distinguisher}}
+
+Specify the default route distinguisher (RD) for routes advertised via BGP
+VPNs. The route distinguisher must be in one of four forms:
+@itemize
+@item @var{IPv4-address}:@var{two-byte-integer}
+@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer}
+@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer}
+@item auto:vn:@var{two-byte-integer}
+@end itemize
+
+If RD is specified in the defaults section, the default RD
+value is @var{two-byte-autonomous-system-number=0}:@var{four-byte-integer=0}.
+
+A complete definition of this parameter is
+given below (@pxref{VNC NVE Group Configuration}).
+@end deffn
+
+@deffn {VNC} {l2rd @var{nve-id-value}}
+Set the value used to distinguish NVEs connected to the same logical
+Ethernet segment (i.e., L2VPN).
+
+A complete definition of this parameter is
+given below (@pxref{VNC NVE Group Configuration}).
+@end deffn
+
+@deffn {VNC} {response-lifetime @var{lifetime}|infinite} {}
+Specify the default lifetime to be included in RFP
+response messages sent to NVEs.
+
+A complete definition of this parameter is
+given below (@pxref{VNC NVE Group Configuration}).
+
+@end deffn
+
+@deffn {VNC} {export bgp|zebra route-map MAP-NAME}
+Specify that the named route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no route-map}
+Specify that no route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME}
+Specify that the named prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+Prefix-lists for ipv4 and ipv6 are independent of each other.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no ipv4|ipv6 prefix-list}
+Specify that no prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} {exit-vnc} {}
+Exit VNC configuration mode.
+@end deffn
+
+@c The following example @code{vnc defaults} defines a route target import-export
+@c list for the route targets 1000:1 and 1000:2; a default route
+@c distinguisher, 4444:10; and a default response lifetime of 500
+@c seconds.
+@c
+@c @example
+@c vnc defaults
+@c rt both 1000:1 1000:2
+@c rd 4444:10
+@c response-lifetime 500
+@c exit-vnc
+@c @end example
+
+@node VNC NVE Group Configuration
+@subsection VNC NVE Group Configuration
+
+A NVE Group corresponds to a specific set of NVEs. A Client NVE is
+assigned to an NVE Group based on whether there is a match for either
+its virtual or underlay network address against the VN and/or UN address
+prefixes specified in the NVE Group definition. When an NVE Group
+definition specifies both VN and UN address prefixes, then an NVE must
+match both prefixes in order to be assigned to the NVE Group. In the
+event that multiple NVE Groups match based on VN and/or UN addresses,
+the NVE is assigned to the first NVE Group listed in the configuration.
+If an NVE is not assigned to an NVE Group, its messages will be ignored.
+
+Configuration values specified for an NVE group apply to all
+member NVEs and override configuration values specified in the VNC
+Defaults section.
+
+@strong{At least one @code{nve-group} is mandatory for useful VNC
+operation.}
+
+@deffn {VNC} {vnc nve-group @var{name}} {}
+Enter VNC configuration mode for defining the NVE group @var{name}.
+Use @code{exit} or @code{exit-vnc} to exit group configuration mode.
+
+@example
+vnc nve-group group1
+ ... configuration commands
+exit-vnc
+@end example
+@end deffn
+
+@deffn {VNC} {no vnc nve-group @var{name}} {}
+Delete the NVE group named @var{name}.
+@end deffn
+
+The following statements are valid in an NVE group definition:
+
+@deffn {VNC} {l2rd @var{nve-id-value}}
+Set the value used to distinguish NVEs connected to the same physical
+Ethernet segment (i.e., at the same location)@footnote{The nve-id is
+carried in the route
+distinguisher. It is the second octet of the eight-octet route
+distinguisher generated for Ethernet / L2 advertisements.
+The first octet is a constant 0xFF, and the third through eighth
+octets are set to the L2 ethernet address being advertised.}
+
+The nve-id subfield may be specified as either a literal value
+in the range 1-255, or it may be specified as @code{auto:vn}, which
+means to use the least-significant octet of the originating
+NVE's VN address.
+@end deffn
+
+@deffn {VNC} {prefix vn|un A.B.C.D/M|X:X::X:X/M} {}
+@anchor{prefix}
+Specify the matching prefix for this NVE group by either virtual-network address
+(@code{vn}) or underlay-network address (@code{un}). Either or both virtual-network
+and underlay-network prefixes may be specified. Subsequent virtual-network or
+underlay-network values within a @code{vnc nve-group} @code{exit-vnc}
+block override their respective previous values.
+
+These prefixes are used only for determining assignments of NVEs
+to NVE Groups.
+@end deffn
+
+@deffn {VNC} {rd @var{route-distinguisher}}
+Specify the route distinguisher for routes advertised via BGP
+VPNs. The route distinguisher must be in one of these forms:
+@itemize
+@item @var{IPv4-address}:@var{two-byte-integer}
+@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer}
+@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer}
+@item auto:vn:@var{two-byte-integer}
+@end itemize
+
+Routes originated by NVEs in the NVE group will use
+the group's specified @var{route-distinguisher} when they are
+advertised via BGP.
+If the @code{auto} form is specified, it means that a matching NVE has
+its RD set to
+@var{rd_type=IP=1}:@var{IPv4-address=VN-address}:@var{two-byte-integer},
+for IPv4 VN addresses and
+@var{rd_type=IP=1}:@var{IPv4-address=Last-four-bytes-of-VN-address}:@var{two-byte-integer},
+for IPv6 VN addresses.
+
+If the NVE group definition does not specify a @var{route-distinguisher},
+then the default @var{route-distinguisher} is used.
+If neither a group nor a default @var{route-distinguisher} is
+configured, then the advertised RD is set to
+@var{two-byte-autonomous-system-number=0}:@var{four-byte-integer=0}.
+@end deffn
+
+@deffn {VNC} {response-lifetime @var{lifetime}|infinite} {}
+Specify the response lifetime, in seconds, to be included in RFP
+response messages sent to NVEs. If the value
+``infinite'' is given, an infinite lifetime will be used.
+
+Note that this parameter is not the same as the lifetime supplied by
+NVEs in RFP registration messages. This parameter does not affect
+the lifetime value attached to routes sent by this server via BGP.
+
+If the NVE group definition does not specify a @var{response-lifetime},
+the default @var{response-lifetime} will be used.
+If neither a group nor a default @var{response-lifetime} is configured,
+the value 3600 will be used. The maximum response lifetime is 2147483647.
+@end deffn
+
+@deffn {VNC} {rt export @var{rt-list}} {}
+@deffnx {VNC} {rt import @var{rt-list}} {}
+@deffnx {VNC} {rt both @var{rt-list}} {}
+Specify route target import and export lists. @var{rt-list} is a
+space-separated list of route targets, each element of which is
+in one of the following forms:
+@itemize
+@item @var{IPv4-address}:@var{two-byte-integer}
+@item @var{four-byte-autonomous-system-number}:@var{two-byte-integer}
+@item @var{two-byte-autonomous-system-number}:@var{four-byte-integer}
+@end itemize
+
+The first form, @code{rt export}, specifies an @var{export rt-list}.
+The @var{export rt-list} will be attached to routes originated by
+NVEs in the NVE group when they are advertised via BGP.
+If the NVE group definition does not specify an @var{export rt-list},
+then the default @var{export rt-list} is used.
+If neither a group nor a default @var{export rt-list} is configured,
+then no RT list will be sent; in turn, these routes will probably
+not be processed
+by receiving NVAs.
+
+The second form, @code{rt import} specifies an @var{import rt-list},
+which is a filter for incoming routes.
+In order to be made available to NVEs in the group,
+incoming BGP VPN @w{SAFI} routes must have
+RT lists that have at least one route target in common with the
+group's @var{import rt-list}.
+
+If the NVE group definition does not specify an import filter,
+then the default @var{import rt-list} is used.
+If neither a group nor a default @var{import rt-list} is configured,
+there can be no RT intersections when receiving BGP routes and
+therefore no incoming BGP routes will be processed for the group.
+
+The third, @code{rt both}, is a shorthand way of specifying both
+lists simultaneously, and is equivalent to @code{rt export @var{rt-list}}
+followed by @code{rt import @var{rt-list}}.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra route-map MAP-NAME}
+Specify that the named route-map should be applied to routes
+being exported to bgp or zebra.
+This paramter is used in conjunction with
+@ref{Configuring Export of Routes to Other Routing Protocols}.
+This item is optional.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no route-map}
+Specify that no route-map should be applied to routes
+being exported to bgp or zebra.
+This paramter is used in conjunction with
+@ref{Configuring Export of Routes to Other Routing Protocols}.
+This item is optional.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME}
+Specify that the named prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+Prefix-lists for ipv4 and ipv6 are independent of each other.
+This paramter is used in conjunction with
+@ref{Configuring Export of Routes to Other Routing Protocols}.
+This item is optional.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no ipv4|ipv6 prefix-list}
+Specify that no prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+This paramter is used in conjunction with
+@ref{Configuring Export of Routes to Other Routing Protocols}.
+This item is optional.
+@end deffn
+
+@c The following example shows two @code{vnc nve-group} definitions. The first one,
+@c ``group1'', applies to the IPV4 virtual-network route prefix 172.16/16. It
+@c sets the response lifetime to 200 seconds. It defines a route target
+@c import-export filter for the route targets 1000:1 and 1000:2
+@c
+@c The second @code{vnc nve-group} definition, ``group2'', applies to the IPV6
+@c underlay-network route prefix 10.0.2/24. It defines the same response
+@c lifetime and import-export filter as ``group1''.
+@c
+@c @example
+@c vnc nve-group group1
+@c prefix vn 172.16/16
+@c response-lifetime 200
+@c rt both 1000:1 1000:2
+@c exit-vnc
+@c
+@c vnc nve-group group2
+@c prefix un 10.0.2/24
+@c response-lifetime 200
+@c rt both 1000:1 1000:2
+@c exit-vnc
+@c @end example
+
+@node VNC L2 Group Configuration
+@subsection VNC L2 Group Configuration
+
+The route targets advertised with prefixes and addresses registered by
+an NVE are determined based on the NVE's associated VNC NVE Group
+Configuration, @pxref{VNC NVE Group Configuration}. Layer 2 (L2) Groups
+are used to override the route targets for an NVE's Ethernet
+registrations based on the Logical Network Identifier and label value.
+A Logical Network Identifier is used to uniquely identify a logical
+Ethernet segment and is conceptually similar to the Ethernet Segment
+Identifier defined in @cite{RFC7432, BGP MPLS-Based Ethernet VPN}. Both
+the Logical Network Identifier and Label are passed to VNC via RFP
+prefix and address registration.
+
+Note that a corresponding NVE group configuration must be present, and
+that other NVE associated configuration information, notably RD, is
+not impacted by L2 Group Configuration.
+
+@deffn {VNC} {vnc l2-group @var{name}} {}
+Enter VNC configuration mode for defining the L2 group @var{name}.
+Use @code{exit} or @code{exit-vnc} to exit group configuration mode.
+
+@example
+vnc l2-group group1
+ ... configuration commands
+exit-vnc
+@end example
+@end deffn
+
+@deffn {VNC} {no vnc l2-group @var{name}} {}
+Delete the L2 group named @var{name}.
+@end deffn
+
+The following statements are valid in a L2 group definition:
+
+@deffn {VNC} {logical-network-id @var{VALUE}}
+Define the Logical Network Identifier with a value in the range of
+0-4294967295 that identifies the logical Ethernet segment.
+@end deffn
+
+@deffn {VNC} {labels @var{label-list}}
+@deffnx {VNC} {no labels @var{label-list}}
+Add or remove labels associated with the group. @var{label-list} is a
+space separated list of label values in the range of 0-1048575.
+@end deffn
+
+@deffn {VNC} {rt import @var{rt-target}} {}
+@deffnx {VNC} {rt export @var{rt-target}} {}
+@deffnx {VNC} {rt both @var{rt-target}} {}
+Specify the route target import and export value associated with the
+group. A complete definition of these parameters is given above,
+@pxref{VNC NVE Group Configuration}.
+@end deffn
+
+
+@node Configuring Redistribution of Routes from Other Routing Protocols
+@subsection Configuring Redistribution of Routes from Other Routing Protocols
+
+Routes from other protocols (including BGP) can be provided to VNC (both
+for RFP and for redistribution via BGP)
+from three sources: the zebra kernel routing process;
+directly from the main (default) unicast BGP RIB; or directly
+from a designated BGP unicast exterior routing RIB instance.
+
+The protocol named in the @code{vnc redistribute} command indicates
+the route source:
+@code{bgp-direct} routes come directly from the main (default)
+unicast BGP RIB and are available for RFP and are redistributed via BGP;
+@code{bgp-direct-to-nve-groups} routes come directly from a designated
+BGP unicast routing RIB and are made available only to RFP;
+and routes from other protocols come from the zebra kernel
+routing process.
+Note that the zebra process does not need to be active if
+only @code{bgp-direct} or @code{bgp-direct-to-nve-groups} routes are used.
+
+@subsubsection @code{zebra} routes
+
+Routes originating from protocols other than BGP must be obtained
+via the zebra routing process.
+Redistribution of these routes into VNC does not support policy mechanisms
+such as prefix-lists or route-maps.
+
+@subsubsection @code{bgp-direct} routes
+
+@code{bgp-direct} redistribution supports policy via
+prefix lists and route-maps. This policy is applied to incoming
+original unicast routes before the redistribution translations
+(described below) are performed.
+
+Redistribution of @code{bgp-direct} routes is performed in one of three
+possible modes: @code{plain}, @code{nve-group}, or @code{resolve-nve}.
+The default mode is @code{plain}.
+These modes indicate the kind of translations applied to routes before
+they are added to the VNC RIB.
+
+In @code{plain} mode, the route's next hop is unchanged and the RD is set
+based on the next hop.
+For @code{bgp-direct} redistribution, the following translations are performed:
+@itemize @bullet
+@item
+The VN address is set to the original unicast route's next hop address.
+@item
+The UN address is NOT set. (VN->UN mapping will occur via
+ENCAP route or attribute, based on @code{vnc advertise-un-method}
+setting, generated by the RFP registration of the actual NVE)
+@item
+The RD is set to as if auto:vn:0 were specified (i.e.,
+@var{rd_type=IP=1}:@var{IPv4-address=VN-address}:@var{two-byte-integer=0})
+@item
+The RT list is included in the extended community list copied from the
+original unicast route (i.e., it must be set in the original unicast route).
+@end itemize
+
+
+
+In @code{nve-group} mode, routes are registered with VNC as
+if they came from an NVE in the nve-group designated in the
+@code{vnc redistribute nve-group} command. The following
+translations are performed:
+
+@itemize @bullet
+@item
+The next hop/VN address is set to the VN prefix configured for the
+redistribute nve-group.
+@item
+The UN address is set to the UN prefix configured for the
+redistribute nve-group.
+@item
+The RD is set to the RD configured for the redistribute nve-group.
+@item
+The RT list is set to the RT list configured for the redistribute nve-group.
+If @code{bgp-direct} routes are being redistributed,
+any extended communities present in the original unicast route
+will also be included.
+@end itemize
+
+
+In @code{resolve-nve} mode, the next hop of the original BGP route is
+typically the address of an NVE connected router (CE) connected by one or
+more NVEs.
+Each of the connected NVEs will register, via RFP, a VNC host route
+to the CE.
+This mode may be though of as a mechanism to proxy RFP registrations
+of BGP unicast routes on behalf of registering NVEs.
+
+Multiple copies of the BGP route, one per matching NVE host route, will be
+added to VNC.
+In other words, for a given BGP unicast route, each instance of a
+RFP-registered host route to the unicast route's next hop will result
+in an instance of an imported VNC route.
+Each such imported VNC route will have a prefix equal to the original
+BGP unicast route's prefix, and a next hop equal to the next hop of the
+matching RFP-registered host route.
+If there is no RFP-registered host route to the next hop of the BGP unicast
+route, no corresponding VNC route will be imported.
+
+The following translations are applied:
+
+@itemize @bullet
+@item
+The Next Hop is set to the next hop of the NVE route (i.e., the
+VN address of the NVE).
+
+@item
+The extended community list in the new route is set to the
+union of:
+@itemize @minus
+@item
+Any extended communities in the original BGP route
+@item
+Any extended communities in the NVE route
+@item
+An added route-origin extended community with the next hop of the
+original BGP route
+is added to the new route.
+The value of the local administrator field defaults 5226 but may
+be configured by the user via the @code{roo-ec-local-admin} parameter.
+@end itemize
+
+@item
+The Tunnel Encapsulation attribute is set to the value of the Tunnel
+Encapsulation attribute of the NVE route, if any.
+
+@end itemize
+
+@subsubsection @code{bgp-direct-to-nve-groups} routes
+
+Unicast routes from the main or a designated instance of BGP
+may be redistributed to VNC as bgp-direct-to-nve-groups routes. These
+routes are NOT announced via BGP,
+but they are made available for local RFP lookup in response to
+queries from NVEs.
+
+A non-main/default BGP instance is configured using the
+@code{bgp multiple-instance} and @code{router bgp AS view NAME}
+commands as described elsewhere in this document.
+
+In order for a route in the unicast BGP RIB to be made
+available to a querying NVE, there must already be, available to
+that NVE, an (interior) VNC route matching the next hop address
+of the unicast route.
+When the unicast route is provided to the NVE, its next hop
+is replaced by the next hop of the corresponding
+NVE. If there are multiple longest-prefix-match VNC routes,
+the unicast route will be replicated for each.
+
+There is currently no policy (prefix-list or route-map) support
+for @code{bgp-direct-to-nve-groups} routes.
+
+@subsubsection Redistribution Command Syntax
+
+@deffn {VNC} {vnc redistribute ipv4|ipv6 bgp|bgp-direct|ipv6 bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static} {}
+@deffnx {VNC} {vnc redistribute ipv4|ipv6 bgp-direct-to-nve-groups view @var{VIEWNAME}} {}
+@deffnx {VNC} {no vnc redistribute ipv4|ipv6 bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static} {}
+Import (or do not import) prefixes from another routing
+protocols. Specify both the address family to import (@code{ipv4} or
+@code{ipv6}) and the protocol (@code{bgp}, @code{bgp-direct},
+@code{bgp-direct-to-nve-groups}, @code{connected},
+@code{kernel}, @code{ospf}, @code{rip}, or @code{static}). Repeat
+this statement as needed for each combination of address family and
+routing protocol.
+Prefixes from protocol @code{bgp-direct} are imported from unicast BGP
+in the same bgpd process.
+Prefixes from all other protocols (including @code{bgp}) are imported
+via the @code{zebra} kernel routing process.
+@end deffn
+
+@deffn {VNC} {vnc redistribute mode plain|nve-group|resolve-nve}
+Redistribute routes from other protocols into VNC using the
+specified mode.
+Not all combinations of modes and protocols are supported.
+@end deffn
+
+@deffn {VNC} {vnc redistribute nve-group @var{group-name}} {}
+@deffnx {VNC} {no vnc redistribute nve-group @var{group-name}} {}
+When using @code{nve-group} mode,
+assign (or do not assign) the NVE group @var{group-name} to routes
+redistributed from another routing protocol. @var{group-name}
+must be configured using @code{vnc nve-group}.
+
+The VN and UN prefixes of the nve-group must both be configured,
+and each prefix must be specified as a full-length (/32 for IPv4,
+/128 for IPv6) prefix.
+@end deffn
+
+@deffn {VNC} {vnc redistribute lifetime @var{lifetime}|infinite} {}
+Assign a registration lifetime, either @var{lifetime} seconds or
+@code{infinite}, to prefixes redistributed from other routing
+protocols as if they had been received via RFP registration messages
+from an NVE. @var{lifetime} can be any integer between 1 and
+4294967295, inclusive.
+@end deffn
+
+@deffn {VNC} {vnc redistribute resolve-nve roo-ec-local-admin @var{0-65536}}
+Assign a value to the local-administrator subfield used in the
+Route Origin extended community that is assigned to routes exported
+under the @code{resolve-nve} mode. The default value is @var{5226}.
+@end deffn
+
+The following four @code{prefix-list} and @code{route-map} commands
+may be specified in the context of an nve-group or not.
+If they are specified in the context of an nve-group, they
+apply only if the redistribution mode is @code{nve-group},
+and then only for routes being redistributed from
+@code{bgp-direct}.
+If they are specified outside the context of an nve-group, then
+they apply only for redistribution modes @code{plain} and @code{resolve-nve},
+and then only for routes being redistributed from @code{bgp-direct}.
+
+@deffn {VNC} {vnc redistribute bgp-direct (ipv4|ipv6) prefix-list @var{LIST-NAME}}
+When redistributing @code{bgp-direct} routes,
+specifies that the named prefix-list should be applied.
+@end deffn
+
+@deffn {VNC} {vnc redistribute bgp-direct no (ipv4|ipv6) prefix-list}
+When redistributing @code{bgp-direct} routes,
+specifies that no prefix-list should be applied.
+@end deffn
+
+@deffn {VNC} {vnc redistribute bgp-direct route-map @var{MAP-NAME}}
+When redistributing @code{bgp-direct} routes,
+specifies that the named route-map should be applied.
+@end deffn
+
+@deffn {VNC} {vnc redistribute bgp-direct no route-map}
+When redistributing @code{bgp-direct} routes,
+specifies that no route-map should be applied.
+@end deffn
+
+@node Configuring Export of Routes to Other Routing Protocols
+@subsection Configuring Export of Routes to Other Routing Protocols
+
+Routes from VNC (both for RFP and for redistribution via BGP) can be
+provided to other protocols, either via zebra or directly to BGP.
+
+It is important to note that when exporting routes to other protocols,
+the downstream protocol must also be configured to import the routes.
+For example, when VNC routes are exported to unicast BGP, the BGP
+configuration must include a corresponding @code{redistribute vnc-direct}
+statement.
+
+@deffn {VNC} {export bgp|zebra mode none|group-nve|registering-nve|ce}
+Specify how routes should be exported to bgp or zebra.
+If the mode is @code{none}, routes are not exported.
+If the mode is @code{group-nve}, routes are exported according
+to nve-group or vrf-policy group configuration (@pxref{VNC NVE Group Configuration}): if a group is configured to
+allow export, then each prefix visible to the group is exported
+with next hops set to the currently-registered NVEs.
+If the mode is @code{registering-nve}, then all VNC routes are
+exported with their original next hops.
+If the mode is @code{ce}, only VNC routes that have an NVE connected CE Router
+encoded in a Route Origin Extended Community are exported.
+This extended community must have an administrative value that
+matches the configured @code{roo-ec-local-admin} value.
+The next hop of the exported route is set to the encoded
+NVE connected CE Router.
+
+The default for both bgp and zebra is mode @code{none}.
+@end deffn
+
+@deffn {VNC} {vnc export bgp|zebra group-nve group @var{group-name}}
+@deffnx {VNC} {vnc export bgp|zebra group-nve no group @var{group-name}}
+When export mode is @code{group-nve},
+export (or do not export) prefixes from the specified nve-group or
+vrf-policy group
+to unicast BGP or to zebra.
+Repeat this statement as needed for each nve-group to be exported.
+Each VNC prefix that is exported will result in N exported routes to the
+prefix, each with a next hop corresponding to one of the N NVEs currently
+associated with the nve-group.
+@end deffn
+
+@deffn {VNC} export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME
+When export mode is @code{ce} or @code{registering-nve},
+specifies that the named prefix-list should be applied to routes
+being exported to bgp or zebra.
+Prefix-lists for ipv4 and ipv6 are independent of each other.
+@end deffn
+
+@deffn {VNC} export bgp|zebra no ipv4|ipv6 prefix-list
+When export mode is @code{ce} or @code{registering-nve},
+specifies that no prefix-list should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} export bgp|zebra route-map MAP-NAME
+When export mode is @code{ce} or @code{registering-nve},
+specifies that the named route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} export bgp|zebra no route-map
+When export mode is @code{ce} or @code{registering-nve},
+specifies that no route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+When the export mode is @code{group-nve}, policy for exported
+routes is specified per-NVE-group or vrf-policy group inside a @code{nve-group} @var{RFG-NAME} block
+via the following commands(@pxref{VNC NVE Group Configuration}):
+
+@deffn {VNC} {export bgp|zebra route-map MAP-NAME}
+This command is valid inside a @code{nve-group} @var{RFG-NAME} block.
+It specifies that the named route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no route-map}
+This command is valid inside a @code{nve-group} @var{RFG-NAME} block.
+It specifies that no route-map should be applied to routes
+being exported to bgp or zebra.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra ipv4|ipv6 prefix-list LIST-NAME}
+This command is valid inside a @code{nve-group} @var{RFG-NAME} block.
+It specifies that the named prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+Prefix-lists for ipv4 and ipv6 are independent of each other.
+@end deffn
+
+@deffn {VNC} {export bgp|zebra no ipv4|ipv6 prefix-list}
+This command is valid inside a @code{nve-group} @var{RFG-NAME} block.
+It specifies that no prefix-list filter should be applied to
+routes being exported to bgp or zebra.
+@end deffn
+
+@node Manual Address Control
+@section Manual Address Control
+
+The commands in this section can be used to augment normal dynamic VNC.
+The @code{add vnc} commands can be used to manually add IP prefix or
+Ethernet MAC address forwarding information. The @code{clear vnc}
+commands can be used to remove manually and dynamically added
+information.
+
+@deffn {Command} {add vnc prefix (A.B.C.D/M|X:X::X:X/M) vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) [cost <0-255>] [lifetime (infinite|<1-4294967295>)] [local-next-hop (A.B.C.D|X:X::X:X) [local-cost <0-255>]]} {}
+Register an IP prefix on behalf of the NVE identified by the VN and UN
+addresses. The @code{cost} parameter provides the administrative
+preference of the forwarding information for remote advertisement. If
+omitted, it defaults to 255 (lowest preference). The @code{lifetime}
+parameter identifies the period, in seconds, that the information
+remains valid. If omitted, it defaults to @var{infinite}. The optional
+@code{local-next-hop} parameter is used to configure a nexthop to be
+used by an NVE to reach the prefix via a locally connected CE router.
+This information remains local to the NVA, i.e., not passed to other
+NVAs, and is only passed to registered NVEs. When specified, it is also
+possible to provide a @code{local-cost} parameter to provide a
+forwarding preference. If omitted, it defaults to 255 (lowest
+preference).
+@end deffn
+
+
+@deffn {Command} {add vnc mac xx:xx:xx:xx:xx:xx virtual-network-identifier <1-4294967295> vn (A.B.C.D|X:X::X:X) un (A.B.C.D|X:X::X:X) [prefix (A.B.C.D/M|X:X::X:X/M)] [cost <0-255>] [lifetime (infinite|<1-4294967295>)]} {}
+Register a MAC address for a logical Ethernet (L2VPN) on behalf of the
+NVE identified by the VN and UN addresses.
+The optional @code{prefix} parameter is to support enable IP address
+mediation for the given prefix. The @code{cost} parameter provides the administrative
+preference of the forwarding information. If omitted, it defaults to
+255. The @code{lifetime} parameter identifies the period, in seconds,
+that the information remains valid. If omitted, it defaults to
+@var{infinite}.
+@end deffn
+
+@deffn {Command} {clear vnc prefix (*|A.B.C.D/M|X:X::X:X/M) (*|[(vn|un) (A.B.C.D|X:X::X:X|*) [(un|vn) (A.B.C.D|X:X::X:X|*)] [mac xx:xx:xx:xx:xx:xx] [local-next-hop (A.B.C.D|X:X::X:X)])} {}
+Delete the information identified by prefix, VN address, and UN address.
+Any or all of these parameters may be wilcarded to (potentially) match
+more than one registration.
+The optional @code{mac} parameter specifies a layer-2 MAC address
+that must match the registration(s) to be deleted.
+The optional @code{local-next-hop} parameter is used to
+delete specific local nexthop information.
+@end deffn
+
+@deffn {Command} {clear vnc mac (*|xx:xx:xx:xx:xx:xx) virtual-network-identifier (*|<1-4294967295>) (*|[(vn|un) (A.B.C.D|X:X::X:X|*) [(un|vn) (A.B.C.D|X:X::X:X|*)] [prefix (*|A.B.C.D/M|X:X::X:X/M)])} {}
+Delete mac forwarding information.
+Any or all of these parameters may be wilcarded to (potentially) match
+more than one registration.
+The default value for the @code{prefix} parameter is the wildcard value @var{*}.
+@end deffn
+
+@deffn {Command} {clear vnc nve (*|((vn|un) (A.B.C.D|X:X::X:X) [(un|vn) (A.B.C.D|X:X::X:X)])) } {}
+Delete prefixes associated with the NVE specified by the given VN and UN
+addresses.
+It is permissible to specify only one of VN or UN, in which case
+any matching registration will be deleted.
+It is also permissible to specify @code{*} in lieu of any VN or UN
+address, in which case all registrations will match.
+@end deffn
+
+@node Other VNC-Related Commands
+@section Other VNC-Related Commands
+
+Note: VNC-Related configuration can be obtained via the @code{show
+running-configuration} command when in @code{enable} mode.
+
+The following commands are used to clear and display
+Virtual Network Control related information:
+
+@deffn {COMMAND} {clear vnc counters} {}
+Reset the counter values stored by the NVA. Counter
+values can be seen using the @code{show vnc} commands listed above. This
+command is only available in @code{enable} mode.
+@end deffn
+
+@deffn {Command} {show vnc summary} {}
+Print counter values and other general information
+about the NVA. Counter values can be reset
+using the @code{clear vnc counters} command listed below.
+@end deffn
+
+@deffn {Command} {show vnc nves} {}
+@deffnx {Command} {show vnc nves vn|un @var{address}} {}
+Display the NVA's current clients. Specifying @var{address}
+limits the output to the NVEs whose addresses match @var{address}.
+The time since the NVA last communicated with the NVE, per-NVE
+summary counters and each NVE's addresses will be displayed.
+@end deffn
+
+@deffn {Command} {show vnc queries} {}
+@deffnx {Command} {show vnc queries @var{prefix}} {}
+Display active Query information. Queries remain valid for the default
+Response Lifetime (@pxref{VNC Defaults Configuration}) or NVE-group
+Response Lifetime (@pxref{VNC NVE Group Configuration}). Specifying
+@var{prefix} limits the output to Query Targets that fall within
+@var{prefix}.
+
+Query information is provided for each querying NVE, and includes the
+Query Target and the time remaining before the information is removed.
+@end deffn
+
+@deffn {Command} {show vnc registrations [all|local|remote|holddown|imported]} {}
+@deffnx {Command} {show vnc registrations [all|local|remote|holddown|imported] @var{prefix}} {}
+Display local, remote, holddown, and/or imported registration information.
+Local registrations are routes received via RFP, which are present in the
+NVA Registrations Cache.
+Remote registrations are routes received via BGP (VPN SAFIs), which
+are present in the NVE-group import tables.
+Holddown registrations are local and remote routes that have been
+withdrawn but whose holddown timeouts have not yet elapsed.
+Imported information represents routes that are imported into NVA and
+are made available to querying NVEs. Depending on configuration,
+imported routes may also be advertised via BGP.
+Specifying @var{prefix} limits the output to the registered prefixes that
+fall within @var{prefix}.
+
+Registration information includes the registered prefix, the registering
+NVE addresses, the registered administrative cost, the registration
+lifetime and the time since the information was registered or, in the
+case of Holddown registrations, the amount of time remaining before the
+information is removed.
+@end deffn
+
+@deffn {Command} {show vnc responses [active|removed]} {}
+@deffnx {Command} {show vnc responses [active|removed] @var{prefix}} {}
+Display all, active and/or removed response information which are
+present in the NVA Responses Cache. Responses remain valid for the
+default Response Lifetime (@pxref{VNC Defaults Configuration}) or
+NVE-group Response Lifetime (@pxref{VNC NVE Group Configuration}.)
+When Removal Responses are enabled (@pxref{General VNC Configuration}),
+such responses are listed for the Response Lifetime. Specifying
+@var{prefix} limits the output to the addresses that fall within
+@var{prefix}.
+
+Response information is provided for each querying NVE, and includes
+the response prefix, the prefix-associated registering NVE addresses,
+the administrative cost, the provided response lifetime and the time
+remaining before the information is to be removed or will become inactive.
+@end deffn
+
+@deffn {Command} {show memory vnc} {}
+Print the number of memory items allocated by the NVA.
+@end deffn
+
+@node Example VNC and VNC-GW Configurations
+@section Example VNC and VNC-GW Configurations
+
+@menu
+* Mesh NVA Configuration::
+* Mesh NVA and VNC-GW Configuration::
+* VNC with Frr Route Reflector Configuration::
+* VNC with Commercial Route Reflector Configuration::
+* VNC with Redundant Route Reflectors Configuration::
+@c * Interfacing VNC to an IGP::
+@end menu
+
+@node Mesh NVA Configuration
+@subsection Mesh NVA Configuration
+
+This example includes three NVAs, nine NVEs, and two NVE groups. Note
+that while not shown, a single physical device may support multiple
+logical NVEs. @ref{fig:fig-vnc-mesh} shows @code{NVA 1}
+(192.168.1.100), @code{NVA 2} (192.168.1.101), and @code{NVA 3}
+(192.168.1.102), which are connected in a full mesh. Each is a
+member of the autonomous system 64512. Each NVA provides VNC
+services to three NVE clients in the 172.16.0.0/16 virtual-network
+address range. The 172.16.0.0/16 address range is partitioned into
+two NVE groups, @code{group1} (172.16.0.0/17) and @code{group2}
+(172.16.128.0/17).
+
+Each NVE belongs to either NVE group @code{group1} or NVE group
+@code{group2}. The NVEs @code{NVE 1}, @code{NVE 2}, @code{NVE
+4}, @code{NVE 7}, and @code{NVE 8} are members of the NVE group
+@code{group1}. The NVEs @code{NVE 3}, @code{NVE 5}, @code{NVE
+6}, and @code{NVE 9} are members of the NVE group @code{group2}.
+
+Each NVA advertises NVE underlay-network IP addresses using the
+Tunnel Encapsulation Attribute.
+
+@float Figure,fig:fig-vnc-mesh
+@center @image{fig-vnc-mesh,400pt,,Three-way Mesh}
+@caption{A three-way full mesh with three NVEs per NVA}
+@end float
+
+@file{bgpd.conf} for @code{NVA 1} (192.168.1.100)
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.100
+
+ neighbor 192.168.1.101 remote-as 64512
+ neighbor 192.168.1.102 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.101 activate
+ neighbor 192.168.1.102 activate
+ exit-address-family
+
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rt both 1000:1
+ exit-vnc
+
+ vnc nve-group group2
+ prefix vn 172.16.128.0/17
+ rt both 1000:2
+ exit-vnc
+
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 2} (192.168.1.101):
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.101
+
+ neighbor 192.168.1.100 remote-as 64512
+ neighbor 192.168.1.102 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ neighbor 192.168.1.102 activate
+ exit-address-family
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 3} (192.168.1.102):
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.102
+
+ neighbor 192.168.1.101 remote-as 64512
+ neighbor 192.168.1.102 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ neighbor 192.168.1.101 activate
+ exit-address-family
+
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.128.0/17
+ exit-vnc
+exit
+@end verbatim
+
+@node Mesh NVA and VNC-GW Configuration
+@subsection Mesh NVA and VNC-GW Configuration
+
+This example includes two NVAs, each with two associated NVEs, and two
+VNC-GWs, each supporting two CE routers physically attached to the four
+NVEs. Note that this example is showing a more complex configuration
+where VNC-GW is separated from normal NVA functions; it is equally
+possible to simplify the configuration and combine NVA and VNC-GW
+functions in a single frr instance.
+
+@float Figure,fig:fig-vnc-gw
+@center @image{fig-vnc-gw,400pt,,Frr VNC Gateway}
+@caption{Meshed NVEs and VNC-GWs}
+@end float
+
+As shown in @ref{fig:fig-vnc-gw}, NVAs and VNC-GWs are connected in a
+full iBGP mesh. The VNC-GWs each have two CEs configured as
+route-reflector clients. Each client provides BGP updates with unicast
+routes that the VNC-GW reflects to the other client. The VNC-GW also
+imports these unicast routes into VPN routes to be shared with the other
+VNC-GW and the two NVAs. This route importation is controlled with the
+@code{vnc redistribute} statements shown in the configuration.
+Similarly, registrations sent by NVEs via RFP to the NVAs are exported
+by the VNC-GWs to the route-reflector clients as unicast routes. RFP
+registrations exported this way have a next-hop address of the CE behind
+the connected (registering) NVE. Exporting VNC routes as IPv4 unicast
+is enabled with the @code{vnc export} command below.
+
+The configuration for @code{VNC-GW 1} is shown below.
+@verbatim
+router bgp 64512
+ bgp router-id 192.168.1.101
+ bgp cluster-id 1.2.3.4
+ neighbor 192.168.1.102 remote-as 64512
+ neighbor 192.168.1.103 remote-as 64512
+ neighbor 192.168.1.104 remote-as 64512
+ neighbor 172.16.1.2 remote-as 64512
+ neighbor 172.16.2.2 remote-as 64512
+ !
+ address-family ipv4 unicast
+ redistribute vnc-direct
+ no neighbor 192.168.1.102 activate
+ no neighbor 192.168.1.103 activate
+ no neighbor 192.168.1.104 activate
+ neighbor 172.16.1.2 route-reflector-client
+ neighbor 172.16.2.2 route-reflector-client
+ exit-address-family
+ !
+ address-family ipv4 vpn
+ neighbor 192.168.1.102 activate
+ neighbor 192.168.1.103 activate
+ neighbor 192.168.1.104 activate
+ exit-address-family
+ vnc export bgp mode ce
+ vnc redistribute mode resolve-nve
+ vnc redistribute ipv4 bgp-direct
+ exit
+@end verbatim
+
+Note that in the VNC-GW configuration, the neighboring VNC-GW and
+NVAs each have a statement disabling the IPv4 unicast address family.
+IPv4 unicast is on by default and this prevents the other VNC-GW and
+NVAs from learning unicast routes advertised by the route-reflector clients.
+
+Configuration for @code{NVA 2}:
+@verbatim
+router bgp 64512
+ bgp router-id 192.168.1.104
+ neighbor 192.168.1.101 remote-as 64512
+ neighbor 192.168.1.102 remote-as 64512
+ neighbor 192.168.1.103 remote-as 64512
+ !
+ address-family ipv4 unicast
+ no neighbor 192.168.1.101 activate
+ no neighbor 192.168.1.102 activate
+ no neighbor 192.168.1.103 activate
+ exit-address-family
+ !
+ address-family ipv4 vpn
+ neighbor 192.168.1.101 activate
+ neighbor 192.168.1.102 activate
+ neighbor 192.168.1.103 activate
+ exit-address-family
+ !
+ vnc defaults
+ response-lifetime 3600
+ exit-vnc
+ vnc nve-group nve1
+ prefix vn 172.16.1.1/32
+ response-lifetime 3600
+ rt both 1000:1 1000:2
+ exit-vnc
+ vnc nve-group nve2
+ prefix vn 172.16.2.1/32
+ response-lifetime 3600
+ rt both 1000:1 1000:2
+ exit-vnc
+ exit
+@end verbatim
+
+@c TBD make this its own example:
+@c
+@c @float Figure,fig:fig-vnc-gw-rr
+@c @center @image{fig-vnc-gw-rr,400pt,,Frr VNC Gateway with RR}
+@c @end float
+@c An NVA can also import unicast routes from BGP without advertising the
+@c imported routes as VPN routes. Such imported routes, while not
+@c distributed to other NVAs or VNC-GWs, are are available to NVEs via
+@c RFP query messages sent to the NVA. @ref{fig:fig-vnc-gw-rr}
+@c shows an example topology where unicast routes are imported into NVAs
+@c from a Route Reflector. (@pxref{Route Reflector} for route reflector
+@c configuration details.) The following three lines can be added to the
+@c @code{NVA 1} and @code{NVA 2} configurations to import routes into VNC
+@c for local VNC use:
+@c
+@c @verbatim
+@c neighbor 192.168.1.105 remote-as 64512
+@c vnc redistribute mode plain
+@c vnc redistribute ipv4 bgp-direct-to-nve-groups
+@c @end verbatim
+
+@node VNC with Frr Route Reflector Configuration
+@subsection VNC with Frr Route Reflector Configuration
+A route reflector eliminates the need for a fully meshed NVA
+network by acting as the hub between NVAs.
+@ref{fig:fig-vnc-frr-route-reflector} shows BGP route reflector
+@code{BGP Route Reflector 1} (192.168.1.100) as a route reflector for
+NVAs @code{NVA 2}(192.168.1.101) and @code{NVA 3}
+(192.168.1.102).
+
+@float Figure,fig:fig-vnc-frr-route-reflector
+@center @image{fig-vnc-frr-route-reflector,400pt,,Frr Route Reflector}
+@caption{Two NVAs and a BGP Route Reflector}
+@end float
+
+@code{NVA 2} and @code{NVA 3}
+advertise NVE underlay-network IP addresses using the Tunnel Encapsulation Attribute.
+@code{BGP Route Reflector 1} ``reflects'' advertisements from
+@code{NVA 2} to @code{NVA 3} and vice versa.
+
+As in the example of @ref{Mesh NVA Configuration}, there are two NVE groups.
+The 172.16.0.0/16 address range is partitioned into two NVE groups,
+@code{group1} (172.16.0.0/17) and @code{group2} (172.16.128.0/17).
+The NVE @code{NVE 4}, @code{NVE 7}, and @code{NVE 8} are
+members of the NVE group @code{group1}. The NVEs @code{NVE 5},
+@code{NVE 6}, and @code{NVE 9} are members of the NVE group
+@code{group2}.
+
+@file{bgpd.conf} for @code{BGP Route Reflector 1} on 192.168.1.100:
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.100
+
+ neighbor 192.168.1.101 remote-as 64512
+ neighbor 192.168.1.101 port 7179
+ neighbor 192.168.1.101 description iBGP-client-192-168-1-101
+
+ neighbor 192.168.1.102 remote-as 64512
+ neighbor 192.168.1.102 port 7179
+ neighbor 192.168.1.102 description iBGP-client-192-168-1-102
+
+ address-family ipv4 unicast
+ neighbor 192.168.1.101 route-reflector-client
+ neighbor 192.168.1.102 route-reflector-client
+ exit-address-family
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.101 activate
+ neighbor 192.168.1.102 activate
+
+ neighbor 192.168.1.101 route-reflector-client
+ neighbor 192.168.1.102 route-reflector-client
+ exit-address-family
+
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 2} on 192.168.1.101:
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.101
+
+ neighbor 192.168.1.100 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ exit-address-family
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 2} on 192.168.1.102:
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.102
+
+ neighbor 192.168.1.100 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ exit-address-family
+
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.128.0/17
+ exit-vnc
+exit
+@end verbatim
+
+While not shown, an NVA can also be configured as a route reflector.
+
+@node VNC with Commercial Route Reflector Configuration
+@subsection VNC with Commercial Route Reflector Configuration
+This example is identical to @ref{VNC with Frr Route Reflector
+Configuration} with the exception that the route reflector is a
+commercial router. Only the
+VNC-relevant configuration is provided.
+
+@float Figure,fig:fig-vnc-commercial-route-reflector
+@center @image{fig-vnc-commercial-route-reflector,400pt,,Commercial Route Reflector}
+@caption{Two NVAs with a commercial route reflector}
+@end float
+
+@file{bgpd.conf} for BGP route reflector @code{Commercial Router} on 192.168.1.104:
+@verbatim
+version 8.5R1.13;
+routing-options {
+ rib inet.0 {
+ static {
+ route 172.16.0.0/16 next-hop 192.168.1.104;
+ }
+ }
+ autonomous-system 64512;
+ resolution {
+ rib inet.3 {
+ resolution-ribs inet.0;
+ }
+ rib bgp.l3vpn.0 {
+ resolution-ribs inet.0;
+ }
+ }
+}
+protocols {
+ bgp {
+ advertise-inactive;
+ family inet {
+ labeled-unicast;
+ }
+ group 1 {
+ type internal;
+ advertise-inactive;
+ advertise-peer-as;
+ import h;
+ family inet {
+ unicast;
+ }
+ family inet-vpn {
+ unicast;
+ }
+ cluster 192.168.1.104;
+ neighbor 192.168.1.101;
+ neighbor 192.168.1.102;
+ }
+ }
+}
+policy-options {
+ policy-statement h {
+ from protocol bgp;
+ then {
+ as-path-prepend 64512;
+ accept;
+ }
+ }
+}
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 2} on 192.168.1.101:
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.101
+
+ neighbor 192.168.1.100 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ exit-address-family
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 3} on 192.168.1.102:
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.102
+
+ neighbor 192.168.1.100 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ exit-address-family
+
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.128.0/17
+ exit-vnc
+exit
+@end verbatim
+
+@node VNC with Redundant Route Reflectors Configuration
+@subsection VNC with Redundant Route Reflectors Configuration
+This example combines the previous two (@ref{VNC with Frr Route
+Reflector Configuration} and @ref{VNC with Commercial Route Reflector
+Configuration}) into a redundant route reflector configuration. BGP
+route reflectors @code{BGP Route Reflector 1} and @code{Commercial Router}
+are the route reflectors for NVAs @code{NVA 2} and
+@code{NVA 3}. The two NVAs have connections to both
+route reflectors.
+
+@float Figure,fig:fig-vnc-redundant-route-reflectors
+@center @image{fig-vnc-redundant-route-reflectors,400pt,,Redundant Route Reflectors}
+@caption{Frr-based NVA with redundant route reflectors}
+@end float
+
+@file{bgpd.conf} for @code{Bgpd Route Reflector 1} on 192.168.1.100:
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.100
+ bgp cluster-id 192.168.1.100
+
+ neighbor 192.168.1.104 remote-as 64512
+
+ neighbor 192.168.1.101 remote-as 64512
+ neighbor 192.168.1.101 description iBGP-client-192-168-1-101
+ neighbor 192.168.1.101 route-reflector-client
+
+ neighbor 192.168.1.102 remote-as 64512
+ neighbor 192.168.1.102 description iBGP-client-192-168-1-102
+ neighbor 192.168.1.102 route-reflector-client
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.101 activate
+ neighbor 192.168.1.102 activate
+ neighbor 192.168.1.104 activate
+
+ neighbor 192.168.1.101 route-reflector-client
+ neighbor 192.168.1.102 route-reflector-client
+ exit-address-family
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 2} on 192.168.1.101:
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.101
+
+ neighbor 192.168.1.100 remote-as 64512
+ neighbor 192.168.1.104 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ neighbor 192.168.1.104 activate
+ exit-address-family
+
+ vnc nve-group group1
+ prefix vn 172.16.0.0/17
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+exit
+@end verbatim
+
+@file{bgpd.conf} for @code{NVA 3} on 192.168.1.102:
+@verbatim
+router bgp 64512
+
+ bgp router-id 192.168.1.102
+
+ neighbor 192.168.1.100 remote-as 64512
+ neighbor 192.168.1.104 remote-as 64512
+
+ address-family ipv4 vpn
+ neighbor 192.168.1.100 activate
+ neighbor 192.168.1.104 activate
+ exit-address-family
+
+ vnc defaults
+ rd 64512:1
+ response-lifetime 200
+ rt both 1000:1 1000:2
+ exit-vnc
+
+ vnc nve-group group1
+ prefix vn 172.16.128.0/17
+ exit-vnc
+exit
+@end verbatim
+
+@file{bgpd.conf} for the Commercial Router route reflector on
+192.168.1.104:
+@verbatim
+routing-options {
+ rib inet.0 {
+ static {
+ route 172.16.0.0/16 next-hop 192.168.1.104;
+ }
+ }
+ autonomous-system 64512;
+ resolution {
+ rib inet.3 {
+ resolution-ribs inet.0;
+ }
+ rib bgp.l3vpn.0 {
+ resolution-ribs inet.0;
+ }
+ }
+}
+protocols {
+ bgp {
+ advertise-inactive;
+ family inet {
+ labeled-unicast;
+ }
+ group 1 {
+ type internal;
+ advertise-inactive;
+ advertise-peer-as;
+ import h;
+ family inet {
+ unicast;
+ }
+ family inet-vpn {
+ unicast;
+ }
+ cluster 192.168.1.104;
+ neighbor 192.168.1.101;
+ neighbor 192.168.1.102;
+ }
+
+ group 2 {
+ type internal;
+ advertise-inactive;
+ advertise-peer-as;
+ import h;
+ family inet {
+ unicast;
+ }
+ family inet-vpn {
+ unicast;
+ }
+ neighbor 192.168.1.100;
+ }
+
+ }
+}
+policy-options {
+ policy-statement h {
+ from protocol bgp;
+ then {
+ as-path-prepend 64512;
+ accept;
+ }
+ }
+}
+@end verbatim
+
+@node Release Notes
+@section Release Notes
+
+@c A paragraph that introduces our release notes.
+
+@c outer list, one item per VNC release, items preceded by bullet
+@itemize @bullet
+@item
+
+@c @item
+@end itemize
+
+@evenheading @thispage@|@|@thistitle
+@oddheading @thischapter@|@|@thispage
+@everyfooting
+
*/
if (!dest) {
char buf[PREFIX_STRLEN];
+
zlog_err("%s: Received prefix %s which we do not know about",
__PRETTY_FUNCTION__,
- prefix2str(&dest_addr, buf, strlen(buf)));
+ prefix2str(&dest_addr, buf, sizeof(buf)));
+ eigrp_IPv4_InternalTLV_free(tlv);
continue;
}
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_EIGRP;
api.safi = SAFI_UNICAST;
memcpy(&api.prefix, p, sizeof(*p));
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_EIGRP;
api.safi = SAFI_UNICAST;
memcpy(&api.prefix, p, sizeof(*p));
* into proprietary software; there is no requirement for such software to
* contain a copyright notice related to this source.
*
- * $Id: dict.h,v 1.3 2005/09/25 12:04:25 hasso Exp $
- * $Name: $
*/
#ifndef DICT_H
#include "privs.h"
struct bpf_insn llcfilter[] = {
- /* check first byte */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETH_ALEN),
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS,
+ ETHER_HDR_LEN), /* check first byte */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 5),
- /* check second byte */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETH_ALEN + 1),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 3),
- /* check third byte */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETH_ALEN + 2),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1),
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 1),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0,
+ 3), /* check second byte */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 2),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1), /* check third byte */
BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
BPF_STMT(BPF_RET + BPF_K, 0)};
u_int readblen = 0;
assert(bpf_hdr->bh_caplen == bpf_hdr->bh_datalen);
- offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETH_ALEN;
+ offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETHER_HDR_LEN;
/* then we lose the BPF, LLC and ethernet headers */
stream_write(circuit->rcv_stream, readbuff + offset,
- bpf_hdr->bh_caplen - LLC_LEN - ETH_ALEN);
+ bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN);
stream_set_getp(circuit->rcv_stream, 0);
- memcpy(ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETH_ALEN, ETH_ALEN);
+ memcpy(ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETH_ALEN,
+ ETH_ALEN);
if (ioctl(circuit->fd, BIOCFLUSH, &one) < 0)
zlog_warn("Flushing failed: %s", safe_strerror(errno));
ssize_t written;
size_t buflen;
- buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN + ETH_ALEN;
+ buflen = stream_get_endp(circuit->snd_stream) + LLC_LEN + ETHER_HDR_LEN;
if (buflen > sizeof(sock_buff)) {
zlog_warn(
"isis_send_pdu_bcast: sock_buff size %zu is less than "
/*
* Then the LLC
*/
- sock_buff[ETH_ALEN] = ISO_SAP;
- sock_buff[ETH_ALEN + 1] = ISO_SAP;
- sock_buff[ETH_ALEN + 2] = 0x03;
+ sock_buff[ETHER_HDR_LEN] = ISO_SAP;
+ sock_buff[ETHER_HDR_LEN + 1] = ISO_SAP;
+ sock_buff[ETHER_HDR_LEN + 2] = 0x03;
/* then we copy the data */
- memcpy(sock_buff + (LLC_LEN + ETH_ALEN), circuit->snd_stream->data,
+ memcpy(sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data,
stream_get_endp(circuit->snd_stream));
/* now we can send this */
circuit->lsp_queue = list_new();
circuit->lsp_hash = isis_lsp_hash_new();
- circuit->lsp_queue_last_push = monotime(NULL);
+ circuit->lsp_queue_last_push[0] = circuit->lsp_queue_last_push[1] =
+ monotime(NULL);
return ISIS_OK;
}
struct thread *t_send_lsp;
struct list *lsp_queue; /* LSPs to be txed (both levels) */
struct isis_lsp_hash *lsp_hash; /* Hashtable synchronized with lsp_queue */
- time_t lsp_queue_last_push; /* timestamp used to enforce transmit
+ time_t lsp_queue_last_push[2]; /* timestamp used to enforce transmit
* interval;
* for scalability, use one timestamp per
* circuit, instead of one per lsp per
if (!circuit->lsp_queue)
continue;
- if (now - circuit->lsp_queue_last_push
+ if (now - circuit->lsp_queue_last_push[level]
< MIN_LSP_RETRANS_INTERVAL) {
continue;
}
- circuit->lsp_queue_last_push = now;
+ circuit->lsp_queue_last_push[level] = now;
for (ALL_LIST_ELEMENTS_RO(
lsp_list, lspnode, lsp)) {
}
static int isis_run_spf(struct isis_area *area, int level, int family,
- u_char *sysid)
+ u_char *sysid, struct timeval *nowtv)
{
int retval = ISIS_OK;
struct isis_vertex *vertex;
uint16_t mtid;
/* Get time that can't roll backwards. */
- monotime(&time_now);
- start_time = time_now.tv_sec;
- start_time = (start_time * 1000000) + time_now.tv_usec;
+ start_time = nowtv->tv_sec;
+ start_time = (start_time * 1000000) + nowtv->tv_usec;
if (family == AF_INET)
spftree = area->spftree[level - 1];
area->area_tag, level);
if (area->ip_circuits)
- retval = isis_run_spf(area, level, AF_INET, isis->sysid);
+ retval = isis_run_spf(area, level, AF_INET, isis->sysid,
+ &thread->real);
if (area->ipv6_circuits)
- retval = isis_run_spf(area, level, AF_INET6, isis->sysid);
+ retval = isis_run_spf(area, level, AF_INET6, isis->sysid,
+ &thread->real);
return retval;
}
timer, &area->spf_timer[level - 1]);
if (isis->debugs & DEBUG_SPF_EVENTS)
- zlog_debug("ISIS-Spf (%s) L%d SPF scheduled %d sec from now",
- area->area_tag, level,
- area->min_spf_interval[level - 1] - diff);
+ zlog_debug("ISIS-Spf (%s) L%d SPF scheduled %ld sec from now",
+ area->area_tag, level, timer);
return ISIS_OK;
}
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_ISIS;
api.safi = SAFI_UNICAST;
api.prefix = *prefix;
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_ISIS;
api.safi = SAFI_UNICAST;
api.prefix = *prefix;
if (dir_str[0] == 'r') {
if (negate)
- DEBUG_OFF(hello, HELLO_RECV);
+ DEBUG_OFF(hello, LDP_DEBUG_HELLO_RECV);
else
- DEBUG_ON(hello, HELLO_RECV);
+ DEBUG_ON(hello, LDP_DEBUG_HELLO_RECV);
} else {
if (negate)
- DEBUG_OFF(hello, HELLO_SEND);
+ DEBUG_OFF(hello, LDP_DEBUG_HELLO_SEND);
else
- DEBUG_ON(hello, HELLO_SEND);
+ DEBUG_ON(hello, LDP_DEBUG_HELLO_SEND);
}
} else if (strcmp(type_str, "errors") == 0) {
if (negate)
- DEBUG_OFF(errors, ERRORS);
+ DEBUG_OFF(errors, LDP_DEBUG_ERRORS);
else
- DEBUG_ON(errors, ERRORS);
+ DEBUG_ON(errors, LDP_DEBUG_ERRORS);
} else if (strcmp(type_str, "event") == 0) {
if (negate)
- DEBUG_OFF(event, EVENT);
+ DEBUG_OFF(event, LDP_DEBUG_EVENT);
else
- DEBUG_ON(event, EVENT);
+ DEBUG_ON(event, LDP_DEBUG_EVENT);
} else if (strcmp(type_str, "labels") == 0) {
if (negate)
- DEBUG_OFF(labels, LABELS);
+ DEBUG_OFF(labels, LDP_DEBUG_LABELS);
else
- DEBUG_ON(labels, LABELS);
+ DEBUG_ON(labels, LDP_DEBUG_LABELS);
} else if (strcmp(type_str, "messages") == 0) {
if (dir_str == NULL)
return (CMD_WARNING_CONFIG_FAILED);
if (dir_str[0] == 'r') {
if (negate) {
- DEBUG_OFF(msg, MSG_RECV);
- DEBUG_OFF(msg, MSG_RECV_ALL);
+ DEBUG_OFF(msg, LDP_DEBUG_MSG_RECV);
+ DEBUG_OFF(msg, LDP_DEBUG_MSG_RECV_ALL);
} else {
- DEBUG_ON(msg, MSG_RECV);
+ DEBUG_ON(msg, LDP_DEBUG_MSG_RECV);
if (all)
- DEBUG_ON(msg, MSG_RECV_ALL);
+ DEBUG_ON(msg, LDP_DEBUG_MSG_RECV_ALL);
}
} else {
if (negate) {
- DEBUG_OFF(msg, MSG_SEND);
- DEBUG_OFF(msg, MSG_SEND_ALL);
+ DEBUG_OFF(msg, LDP_DEBUG_MSG_SEND);
+ DEBUG_OFF(msg, LDP_DEBUG_MSG_SEND_ALL);
} else {
- DEBUG_ON(msg, MSG_SEND);
+ DEBUG_ON(msg, LDP_DEBUG_MSG_SEND);
if (all)
- DEBUG_ON(msg, MSG_SEND_ALL);
+ DEBUG_ON(msg, LDP_DEBUG_MSG_SEND_ALL);
}
}
} else if (strcmp(type_str, "zebra") == 0) {
if (negate)
- DEBUG_OFF(zebra, ZEBRA);
+ DEBUG_OFF(zebra, LDP_DEBUG_ZEBRA);
else
- DEBUG_ON(zebra, ZEBRA);
+ DEBUG_ON(zebra, LDP_DEBUG_ZEBRA);
}
main_imsg_compose_both(IMSG_DEBUG_UPDATE, &ldp_debug,
{
vty_out (vty, "LDP debugging status:\n");
- if (LDP_DEBUG(hello, HELLO_RECV))
+ if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_RECV))
vty_out (vty," LDP discovery debugging is on (inbound)\n");
- if (LDP_DEBUG(hello, HELLO_SEND))
+ if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_SEND))
vty_out (vty," LDP discovery debugging is on (outbound)\n");
- if (LDP_DEBUG(errors, ERRORS))
+ if (LDP_DEBUG(errors, LDP_DEBUG_ERRORS))
vty_out (vty, " LDP errors debugging is on\n");
- if (LDP_DEBUG(event, EVENT))
+ if (LDP_DEBUG(event, LDP_DEBUG_EVENT))
vty_out (vty, " LDP events debugging is on\n");
- if (LDP_DEBUG(labels, LABELS))
+ if (LDP_DEBUG(labels, LDP_DEBUG_LABELS))
vty_out (vty, " LDP labels debugging is on\n");
- if (LDP_DEBUG(msg, MSG_RECV_ALL))
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV_ALL))
vty_out (vty,
" LDP detailed messages debugging is on (inbound)\n");
- else if (LDP_DEBUG(msg, MSG_RECV))
+ else if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV))
vty_out (vty," LDP messages debugging is on (inbound)\n");
- if (LDP_DEBUG(msg, MSG_SEND_ALL))
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND_ALL))
vty_out (vty,
" LDP detailed messages debugging is on (outbound)\n");
- else if (LDP_DEBUG(msg, MSG_SEND))
+ else if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND))
vty_out (vty," LDP messages debugging is on (outbound)\n");
- if (LDP_DEBUG(zebra, ZEBRA))
+ if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA))
vty_out (vty, " LDP zebra debugging is on\n");
vty_out (vty, "\n");
{
int write = 0;
- if (CONF_LDP_DEBUG(hello, HELLO_RECV)) {
+ if (CONF_LDP_DEBUG(hello, LDP_DEBUG_HELLO_RECV)) {
vty_out (vty,"debug mpls ldp discovery hello recv\n");
write = 1;
}
- if (CONF_LDP_DEBUG(hello, HELLO_SEND)) {
+ if (CONF_LDP_DEBUG(hello, LDP_DEBUG_HELLO_SEND)) {
vty_out (vty,"debug mpls ldp discovery hello sent\n");
write = 1;
}
- if (CONF_LDP_DEBUG(errors, ERRORS)) {
+ if (CONF_LDP_DEBUG(errors, LDP_DEBUG_ERRORS)) {
vty_out (vty, "debug mpls ldp errors\n");
write = 1;
}
- if (CONF_LDP_DEBUG(event, EVENT)) {
+ if (CONF_LDP_DEBUG(event, LDP_DEBUG_EVENT)) {
vty_out (vty, "debug mpls ldp event\n");
write = 1;
}
- if (CONF_LDP_DEBUG(labels, LABELS)) {
+ if (CONF_LDP_DEBUG(labels, LDP_DEBUG_LABELS)) {
vty_out (vty, "debug mpls ldp labels\n");
write = 1;
}
- if (CONF_LDP_DEBUG(msg, MSG_RECV_ALL)) {
+ if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV_ALL)) {
vty_out (vty, "debug mpls ldp messages recv all\n");
write = 1;
- } else if (CONF_LDP_DEBUG(msg, MSG_RECV)) {
+ } else if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV)) {
vty_out (vty, "debug mpls ldp messages recv\n");
write = 1;
}
- if (CONF_LDP_DEBUG(msg, MSG_SEND_ALL)) {
+ if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND_ALL)) {
vty_out (vty, "debug mpls ldp messages sent all\n");
write = 1;
- } else if (CONF_LDP_DEBUG(msg, MSG_SEND)) {
+ } else if (CONF_LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND)) {
vty_out (vty, "debug mpls ldp messages sent\n");
write = 1;
}
- if (CONF_LDP_DEBUG(zebra, ZEBRA)) {
+ if (CONF_LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) {
vty_out (vty, "debug mpls ldp zebra\n");
write = 1;
}
extern struct ldp_debug conf_ldp_debug;
extern struct ldp_debug ldp_debug;
-#define CONF_DEBUG_ON(a, b) (conf_ldp_debug.a |= (LDP_DEBUG_ ## b))
-#define CONF_DEBUG_OFF(a, b) (conf_ldp_debug.a &= ~(LDP_DEBUG_ ## b))
+#define CONF_DEBUG_ON(a, b) (conf_ldp_debug.a |= (b))
+#define CONF_DEBUG_OFF(a, b) (conf_ldp_debug.a &= ~(b))
-#define TERM_DEBUG_ON(a, b) (ldp_debug.a |= (LDP_DEBUG_ ## b))
-#define TERM_DEBUG_OFF(a, b) (ldp_debug.a &= ~(LDP_DEBUG_ ## b))
+#define TERM_DEBUG_ON(a, b) (ldp_debug.a |= (b))
+#define TERM_DEBUG_OFF(a, b) (ldp_debug.a &= ~(b))
#define DEBUG_ON(a, b) \
do { \
TERM_DEBUG_OFF(a, b); \
} while (0)
-#define LDP_DEBUG(a, b) (ldp_debug.a & LDP_DEBUG_ ## b)
-#define CONF_LDP_DEBUG(a, b) (conf_ldp_debug.a & LDP_DEBUG_ ## b)
+#define LDP_DEBUG(a, b) (ldp_debug.a & b)
+#define CONF_LDP_DEBUG(a, b) (conf_ldp_debug.a & b)
#define debug_hello_recv(emsg, ...) \
do { \
- if (LDP_DEBUG(hello, HELLO_RECV)) \
+ if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_RECV)) \
log_debug("discovery[recv]: " emsg, __VA_ARGS__); \
} while (0)
#define debug_hello_send(emsg, ...) \
do { \
- if (LDP_DEBUG(hello, HELLO_SEND)) \
+ if (LDP_DEBUG(hello, LDP_DEBUG_HELLO_SEND)) \
log_debug("discovery[send]: " emsg, __VA_ARGS__); \
} while (0)
#define debug_err(emsg, ...) \
do { \
- if (LDP_DEBUG(errors, ERRORS)) \
+ if (LDP_DEBUG(errors, LDP_DEBUG_ERRORS)) \
log_debug("error: " emsg, __VA_ARGS__); \
} while (0)
#define debug_evt(emsg, ...) \
do { \
- if (LDP_DEBUG(event, EVENT)) \
+ if (LDP_DEBUG(event, LDP_DEBUG_EVENT)) \
log_debug("event: " emsg, __VA_ARGS__); \
} while (0)
#define debug_labels(emsg, ...) \
do { \
- if (LDP_DEBUG(labels, LABELS)) \
+ if (LDP_DEBUG(labels, LDP_DEBUG_LABELS)) \
log_debug("labels: " emsg, __VA_ARGS__); \
} while (0)
#define debug_msg_recv(emsg, ...) \
do { \
- if (LDP_DEBUG(msg, MSG_RECV)) \
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV)) \
log_debug("msg[in]: " emsg, __VA_ARGS__); \
} while (0)
#define debug_msg_send(emsg, ...) \
do { \
- if (LDP_DEBUG(msg, MSG_SEND)) \
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND)) \
log_debug("msg[out]: " emsg, __VA_ARGS__); \
} while (0)
#define debug_kalive_recv(emsg, ...) \
do { \
- if (LDP_DEBUG(msg, MSG_RECV_ALL)) \
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV_ALL)) \
log_debug("kalive[in]: " emsg, __VA_ARGS__); \
} while (0)
#define debug_kalive_send(emsg, ...) \
do { \
- if (LDP_DEBUG(msg, MSG_SEND_ALL)) \
+ if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND_ALL)) \
log_debug("kalive[out]: " emsg, __VA_ARGS__); \
} while (0)
#define debug_zebra_in(emsg, ...) \
do { \
- if (LDP_DEBUG(zebra, ZEBRA)) \
+ if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) \
log_debug("zebra[in]: " emsg, __VA_ARGS__); \
} while (0)
#define debug_zebra_out(emsg, ...) \
do { \
- if (LDP_DEBUG(zebra, ZEBRA)) \
+ if (LDP_DEBUG(zebra, LDP_DEBUG_ZEBRA)) \
log_debug("zebra[out]: " emsg, __VA_ARGS__); \
} while (0)
#define CMD_NOT_MY_INSTANCE 14
/* Argc max counts. */
-#define CMD_ARGC_MAX 25
+#define CMD_ARGC_MAX 256
/* Turn off these macros when uisng cpp with extract.pl */
#ifndef VTYSH_EXTRACT_PL
DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack")
-#define MAXDEPTH 256
-
#ifdef TRACE_MATCHER
#define TM 1
#else
enum matcher_rv command_match(struct graph *cmdgraph, vector vline,
struct list **argv, const struct cmd_element **el)
{
- struct graph_node *stack[MAXDEPTH];
+ struct graph_node *stack[CMD_ARGC_MAX];
enum matcher_rv status;
*argv = NULL;
/* check history/stack of tokens
* this disallows matching the same one more than once if there is a
* circle in the graph (used for keyword arguments) */
- if (n == MAXDEPTH)
+ if (n == CMD_ARGC_MAX)
return MATCHER_NO_MATCH;
if (!token->allowrepeat)
for (size_t s = 0; s < n; s++)
/*
- * Utilities and interfaces for managing POSIX threads
+ * Utilities and interfaces for managing POSIX threads within FRR.
* Copyright (C) 2017 Cumulus Networks
*
* This program is free software; you can redistribute it and/or modify
#include <zebra.h>
#include <pthread.h>
+#include <sched.h>
#include "frr_pthread.h"
#include "memory.h"
#include "hash.h"
-DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread");
+DEFINE_MTYPE(LIB, FRR_PTHREAD, "FRR POSIX Thread");
+DEFINE_MTYPE(LIB, PTHREAD_PRIM, "POSIX synchronization primitives");
+/* id for next created pthread */
static unsigned int next_id = 0;
-/* Hash table of all frr_pthreads along with synchronization primitive(s) and
- * hash table callbacks.
- * ------------------------------------------------------------------------ */
-static struct hash *pthread_table;
-static pthread_mutex_t pthread_table_mtx = PTHREAD_MUTEX_INITIALIZER;
+/* default frr_pthread start/stop routine prototypes */
+static void *fpt_run(void *arg);
+static int fpt_halt(struct frr_pthread *fpt, void **res);
-/* pthread_table->hash_cmp */
-static int pthread_table_hash_cmp(const void *value1, const void *value2)
+/* default frr_pthread attributes */
+struct frr_pthread_attr frr_pthread_attr_default = {
+ .id = 0,
+ .start = fpt_run,
+ .stop = fpt_halt,
+ .name = "Anonymous",
+};
+
+/* hash table to keep track of all frr_pthreads */
+static struct hash *frr_pthread_hash;
+static pthread_mutex_t frr_pthread_hash_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+/* frr_pthread_hash->hash_cmp */
+static int frr_pthread_hash_cmp(const void *value1, const void *value2)
{
const struct frr_pthread *tq1 = value1;
const struct frr_pthread *tq2 = value2;
- return (tq1->id == tq2->id);
+ return (tq1->attr.id == tq2->attr.id);
}
-/* pthread_table->hash_key */
-static unsigned int pthread_table_hash_key(void *value)
+/* frr_pthread_hash->hash_key */
+static unsigned int frr_pthread_hash_key(void *value)
{
- return ((struct frr_pthread *)value)->id;
+ return ((struct frr_pthread *)value)->attr.id;
}
+
/* ------------------------------------------------------------------------ */
void frr_pthread_init()
{
- pthread_mutex_lock(&pthread_table_mtx);
+ pthread_mutex_lock(&frr_pthread_hash_mtx);
{
- pthread_table = hash_create(pthread_table_hash_key,
- pthread_table_hash_cmp, NULL);
+ frr_pthread_hash = hash_create(frr_pthread_hash_key,
+ frr_pthread_hash_cmp, NULL);
}
- pthread_mutex_unlock(&pthread_table_mtx);
+ pthread_mutex_unlock(&frr_pthread_hash_mtx);
}
void frr_pthread_finish()
{
- pthread_mutex_lock(&pthread_table_mtx);
+ pthread_mutex_lock(&frr_pthread_hash_mtx);
{
- hash_clean(pthread_table,
+ hash_clean(frr_pthread_hash,
(void (*)(void *))frr_pthread_destroy);
- hash_free(pthread_table);
+ hash_free(frr_pthread_hash);
}
- pthread_mutex_unlock(&pthread_table_mtx);
+ pthread_mutex_unlock(&frr_pthread_hash_mtx);
}
-struct frr_pthread *frr_pthread_new(const char *name, unsigned int id,
- void *(*start_routine)(void *),
- int (*stop_routine)(void **,
- struct frr_pthread *))
+struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr)
{
static struct frr_pthread holder = {0};
struct frr_pthread *fpt = NULL;
- pthread_mutex_lock(&pthread_table_mtx);
+ attr = attr ? attr : &frr_pthread_attr_default;
+
+ pthread_mutex_lock(&frr_pthread_hash_mtx);
{
- holder.id = id;
-
- if (!hash_lookup(pthread_table, &holder)) {
- struct frr_pthread *fpt = XCALLOC(
- MTYPE_FRR_PTHREAD, sizeof(struct frr_pthread));
- fpt->id = id;
- fpt->master = thread_master_create(name);
- fpt->start_routine = start_routine;
- fpt->stop_routine = stop_routine;
- fpt->name = XSTRDUP(MTYPE_FRR_PTHREAD, name);
-
- hash_get(pthread_table, fpt, hash_alloc_intern);
+ holder.attr.id = attr->id;
+
+ if (!hash_lookup(frr_pthread_hash, &holder)) {
+ fpt = XCALLOC(MTYPE_FRR_PTHREAD,
+ sizeof(struct frr_pthread));
+ /* create new thread master */
+ fpt->master = thread_master_create(attr->name);
+ /* set attributes */
+ fpt->attr = *attr;
+ if (attr == &frr_pthread_attr_default)
+ fpt->attr.id = frr_pthread_get_id();
+ /* initialize startup synchronization primitives */
+ fpt->running_cond_mtx = XCALLOC(
+ MTYPE_PTHREAD_PRIM, sizeof(pthread_mutex_t));
+ fpt->running_cond = XCALLOC(MTYPE_PTHREAD_PRIM,
+ sizeof(pthread_cond_t));
+ pthread_mutex_init(fpt->running_cond_mtx, NULL);
+ pthread_cond_init(fpt->running_cond, NULL);
+
+ /* insert into global thread hash */
+ hash_get(frr_pthread_hash, fpt, hash_alloc_intern);
}
}
- pthread_mutex_unlock(&pthread_table_mtx);
+ pthread_mutex_unlock(&frr_pthread_hash_mtx);
return fpt;
}
void frr_pthread_destroy(struct frr_pthread *fpt)
{
thread_master_free(fpt->master);
- XFREE(MTYPE_FRR_PTHREAD, fpt->name);
+
+ pthread_mutex_destroy(fpt->running_cond_mtx);
+ pthread_cond_destroy(fpt->running_cond);
+ XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond_mtx);
+ XFREE(MTYPE_PTHREAD_PRIM, fpt->running_cond);
XFREE(MTYPE_FRR_PTHREAD, fpt);
}
static struct frr_pthread holder = {0};
struct frr_pthread *fpt;
- pthread_mutex_lock(&pthread_table_mtx);
+ pthread_mutex_lock(&frr_pthread_hash_mtx);
{
- holder.id = id;
- fpt = hash_lookup(pthread_table, &holder);
+ holder.attr.id = id;
+ fpt = hash_lookup(frr_pthread_hash, &holder);
}
- pthread_mutex_unlock(&pthread_table_mtx);
+ pthread_mutex_unlock(&frr_pthread_hash_mtx);
return fpt;
}
-int frr_pthread_run(unsigned int id, const pthread_attr_t *attr, void *arg)
+int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
{
- struct frr_pthread *fpt = frr_pthread_get(id);
int ret;
- if (!fpt)
- return -1;
+ ret = pthread_create(&fpt->thread, attr, fpt->attr.start, fpt);
- ret = pthread_create(&fpt->thread, attr, fpt->start_routine, arg);
-
- /* Per pthread_create(3), the contents of fpt->thread are undefined if
- * pthread_create() did not succeed. Reset this value to zero. */
+ /*
+ * Per pthread_create(3), the contents of fpt->thread are undefined if
+ * pthread_create() did not succeed. Reset this value to zero.
+ */
if (ret < 0)
memset(&fpt->thread, 0x00, sizeof(fpt->thread));
return ret;
}
-/**
- * Calls the stop routine for the frr_pthread and resets any relevant fields.
- *
- * @param fpt - the frr_pthread to stop
- * @param result - pointer to result pointer
- * @return the return code from the stop routine
- */
-static int frr_pthread_stop_actual(struct frr_pthread *fpt, void **result)
+void frr_pthread_wait_running(struct frr_pthread *fpt)
{
- int ret = (*fpt->stop_routine)(result, fpt);
- memset(&fpt->thread, 0x00, sizeof(fpt->thread));
- return ret;
+ pthread_mutex_lock(fpt->running_cond_mtx);
+ {
+ while (!fpt->running)
+ pthread_cond_wait(fpt->running_cond,
+ fpt->running_cond_mtx);
+ }
+ pthread_mutex_unlock(fpt->running_cond_mtx);
+}
+
+void frr_pthread_notify_running(struct frr_pthread *fpt)
+{
+ pthread_mutex_lock(fpt->running_cond_mtx);
+ {
+ fpt->running = true;
+ pthread_cond_signal(fpt->running_cond);
+ }
+ pthread_mutex_unlock(fpt->running_cond_mtx);
}
-int frr_pthread_stop(unsigned int id, void **result)
+int frr_pthread_stop(struct frr_pthread *fpt, void **result)
{
- struct frr_pthread *fpt = frr_pthread_get(id);
- return frr_pthread_stop_actual(fpt, result);
+ int ret = (*fpt->attr.stop)(fpt, result);
+ memset(&fpt->thread, 0x00, sizeof(fpt->thread));
+ return ret;
}
-/**
+/*
* Callback for hash_iterate to stop all frr_pthread's.
*/
static void frr_pthread_stop_all_iter(struct hash_backet *hb, void *arg)
{
struct frr_pthread *fpt = hb->data;
- frr_pthread_stop_actual(fpt, NULL);
+ frr_pthread_stop(fpt, NULL);
}
void frr_pthread_stop_all()
{
- pthread_mutex_lock(&pthread_table_mtx);
+ pthread_mutex_lock(&frr_pthread_hash_mtx);
{
- hash_iterate(pthread_table, frr_pthread_stop_all_iter, NULL);
+ hash_iterate(frr_pthread_hash, frr_pthread_stop_all_iter, NULL);
}
- pthread_mutex_unlock(&pthread_table_mtx);
+ pthread_mutex_unlock(&frr_pthread_hash_mtx);
}
unsigned int frr_pthread_get_id()
{
+ /* just a sanity check, this should never happen */
+ assert(next_id <= INT_MAX - 1);
return next_id++;
}
+
+void frr_pthread_yield(void)
+{
+ (void)sched_yield();
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Default Event Loop
+ * ----------------------------------------------------------------------------
+ */
+
+/* dummy task for sleeper pipe */
+static int fpt_dummy(struct thread *thread)
+{
+ return 0;
+}
+
+/* poison pill task to end event loop */
+static int fpt_finish(struct thread *thread)
+{
+ struct frr_pthread *fpt = THREAD_ARG(thread);
+ atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
+ return 0;
+}
+
+/* stop function, called from other threads to halt this one */
+static int fpt_halt(struct frr_pthread *fpt, void **res)
+{
+ thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL);
+ pthread_join(fpt->thread, res);
+ fpt = NULL;
+
+ return 0;
+}
+
+/* entry pthread function & main event loop */
+static void *fpt_run(void *arg)
+{
+ struct frr_pthread *fpt = arg;
+ fpt->master->owner = pthread_self();
+
+ int sleeper[2];
+ pipe(sleeper);
+ thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL);
+
+ fpt->master->handle_signals = false;
+
+ frr_pthread_notify_running(fpt);
+
+ struct thread task;
+ while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
+ if (thread_fetch(fpt->master, &task)) {
+ thread_call(&task);
+ }
+ }
+
+ close(sleeper[1]);
+ close(sleeper[0]);
+
+ return NULL;
+}
/*
- * Utilities and interfaces for managing POSIX threads
+ * Utilities and interfaces for managing POSIX threads within FRR.
* Copyright (C) 2017 Cumulus Networks
*
* This program is free software; you can redistribute it and/or modify
#define _FRR_PTHREAD_H
#include <pthread.h>
+#include "frratomic.h"
+#include "memory.h"
#include "thread.h"
+DECLARE_MTYPE(FRR_PTHREAD);
+DECLARE_MTYPE(PTHREAD_PRIM);
+
+struct frr_pthread;
+struct frr_pthread_attr;
+
+struct frr_pthread_attr {
+ int id;
+ void *(*start)(void *);
+ int (*stop)(struct frr_pthread *, void **);
+ const char *name;
+};
+
struct frr_pthread {
/* pthread id */
pthread_t thread;
- /* frr thread identifier */
- unsigned int id;
-
/* thread master for this pthread's thread.c event loop */
struct thread_master *master;
- /* start routine */
- void *(*start_routine)(void *);
-
- /* stop routine */
- int (*stop_routine)(void **, struct frr_pthread *);
-
- /* the (hopefully descriptive) name of this thread */
- char *name;
+ /* caller-specified data; start & stop funcs, name, id */
+ struct frr_pthread_attr attr;
+
+ /*
+ * Notification mechanism for allowing pthreads to notify their parents
+ * when they are ready to do work. This mechanism has two associated
+ * functions:
+ *
+ * - frr_pthread_wait_running()
+ * This function should be called by the spawning thread after
+ * frr_pthread_run(). It safely waits until the spawned thread
+ * indicates that is ready to do work by posting to the condition
+ * variable.
+ *
+ * - frr_pthread_notify_running()
+ * This function should be called by the spawned thread when it is
+ * ready to do work. It will wake up any threads waiting on the
+ * previously described condition.
+ */
+ pthread_cond_t *running_cond;
+ pthread_mutex_t *running_cond_mtx;
+ _Atomic bool running;
+
+ /*
+ * Fake thread-specific storage. No constraints on usage. Helpful when
+ * creating reentrant pthread implementations. Can be used to pass
+ * argument to pthread entry function.
+ */
+ void *data;
};
-/* Initializes this module.
+extern struct frr_pthread_attr frr_pthread_attr_default;
+
+/*
+ * Initializes this module.
*
* Must be called before using any of the other functions.
*/
void frr_pthread_init(void);
-/* Uninitializes this module.
+/*
+ * Uninitializes this module.
*
* Destroys all registered frr_pthread's and internal data structures.
*
*/
void frr_pthread_finish(void);
-/* Creates a new frr_pthread.
- *
- * If the provided ID is already assigned to an existing frr_pthread, the
- * return value will be NULL.
- *
- * @param name - the name of the thread. Doesn't have to be unique, but it
- * probably should be. This value is copied and may be safely free'd upon
- * return.
- *
- * @param id - the integral ID of the thread. MUST be unique. The caller may
- * use this id to retrieve the thread.
- *
- * @param start_routine - start routine for the pthread, will be passed to
- * pthread_create (see those docs for details)
+/*
+ * Creates a new frr_pthread with the given attributes.
*
- * @param stop_routine - stop routine for the pthread, called to terminate the
- * thread. This function should gracefully stop the pthread and clean up any
- * thread-specific resources. The passed pointer is used to return a data
- * result.
+ * The 'attr' argument should be filled out with the desired attributes,
+ * including ID, start and stop functions and the desired name. Alternatively,
+ * if attr is NULL, the default attributes will be used. The pthread will be
+ * set up to run a basic threadmaster loop and the name will be "Anonymous".
+ * Scheduling tasks onto the threadmaster in the 'master' field of the returned
+ * frr_pthread will cause them to run on that pthread.
*
+ * @param attr - the thread attributes
* @return the created frr_pthread upon success, or NULL upon failure
*/
-struct frr_pthread *frr_pthread_new(const char *name, unsigned int id,
- void *(*start_routine)(void *),
- int (*stop_routine)(void **,
- struct frr_pthread *));
+struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr);
-/* Destroys an frr_pthread.
+/*
+ * Destroys an frr_pthread.
*
* Assumes that the associated pthread, if any, has already terminated.
*
*/
void frr_pthread_destroy(struct frr_pthread *fpt);
-/* Gets an existing frr_pthread by its id.
+/*
+ * Gets an existing frr_pthread by its id.
*
* @return frr_thread associated with the provided id, or NULL on error
*/
struct frr_pthread *frr_pthread_get(unsigned int id);
-/* Creates a new pthread and binds it to a frr_pthread.
+/*
+ * Creates a new pthread and binds it to a frr_pthread.
*
* This function is a wrapper for pthread_create. The first parameter is the
* frr_pthread to bind the created pthread to. All subsequent arguments are
- * passed unmodified to pthread_create().
+ * passed unmodified to pthread_create(). The frr_pthread * provided will be
+ * used as the argument to the pthread entry function. If it is necessary to
+ * pass additional data, the 'data' field in the frr_pthread may be used.
*
* This function returns the same code as pthread_create(). If the value is
* zero, the provided frr_pthread is bound to a running POSIX thread. If the
* value is less than zero, the provided frr_pthread is guaranteed to be a
* clean instance that may be susbsequently passed to frr_pthread_run().
*
- * @param id - frr_pthread to bind the created pthread to
+ * @param fpt - frr_pthread * to run
* @param attr - see pthread_create(3)
- * @param arg - see pthread_create(3)
*
* @return see pthread_create(3)
*/
-int frr_pthread_run(unsigned int id, const pthread_attr_t *attr, void *arg);
+int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr);
+
+/*
+ * Waits until the specified pthread has finished setting up and is ready to
+ * begin work.
+ *
+ * If the pthread's code makes use of the startup synchronization mechanism,
+ * this function should be called before attempting to use the functionality
+ * exposed by the pthread. It waits until the 'running' condition is satisfied
+ * (see struct definition of frr_pthread).
+ *
+ * @param fpt - the frr_pthread * to wait on
+ */
+void frr_pthread_wait_running(struct frr_pthread *fpt);
-/* Stops an frr_pthread with a result.
+/*
+ * Notifies other pthreads that the calling thread has finished setting up and
+ * is ready to begin work.
*
- * @param id - frr_pthread to stop
+ * This will allow any other pthreads waiting in 'frr_pthread_wait_running' to
+ * proceed.
+ *
+ * @param fpt - the frr_pthread * that has finished setting up
+ */
+void frr_pthread_notify_running(struct frr_pthread *fpt);
+
+/*
+ * Stops a frr_pthread with a result.
+ *
+ * @param fpt - frr_pthread * to stop
* @param result - where to store the thread's result, if any. May be NULL if a
* result is not needed.
*/
-int frr_pthread_stop(unsigned int id, void **result);
+int frr_pthread_stop(struct frr_pthread *fpt, void **result);
/* Stops all frr_pthread's. */
void frr_pthread_stop_all(void);
-/* Returns a unique identifier for use with frr_pthread_new().
+/* Yields the current thread of execution */
+void frr_pthread_yield(void);
+
+/*
+ * Returns a unique identifier for use with frr_pthread_new().
*
* Internally, this is an integer that increments after each call to this
* function. Because the number of pthreads created should never exceed INT_MAX
}
}
-/* read callback integration */
-struct frrzmq_cb {
- struct thread *thread;
- void *zmqsock;
- void *arg;
- int fd;
-
- bool cancelled;
-
- void (*cb_msg)(void *arg, void *zmqsock);
- void (*cb_part)(void *arg, void *zmqsock,
- zmq_msg_t *msg, unsigned partnum);
-};
-
-
static int frrzmq_read_msg(struct thread *t)
{
- struct frrzmq_cb *cb = THREAD_ARG(t);
+ struct frrzmq_cb **cbp = THREAD_ARG(t);
+ struct frrzmq_cb *cb;
zmq_msg_t msg;
unsigned partno;
+ unsigned char read = 0;
int ret, more;
size_t moresz;
+ if (!cbp)
+ return 1;
+ cb = (*cbp);
+ if (!cb || !cb->zmqsock)
+ return 1;
+
while (1) {
- zmq_pollitem_t polli = {
- .socket = cb->zmqsock,
- .events = ZMQ_POLLIN
- };
+ zmq_pollitem_t polli = {.socket = cb->zmqsock,
+ .events = ZMQ_POLLIN};
ret = zmq_poll(&polli, 1, 0);
if (ret < 0)
goto out_err;
+
if (!(polli.revents & ZMQ_POLLIN))
break;
- if (cb->cb_msg) {
- cb->cb_msg(cb->arg, cb->zmqsock);
+ if (cb->read.cb_msg) {
+ cb->read.cb_msg(cb->read.arg, cb->zmqsock);
+ read = 1;
- if (cb->cancelled) {
- XFREE(MTYPE_ZEROMQ_CB, cb);
+ if (cb->read.cancelled) {
+ frrzmq_check_events(cbp, &cb->write,
+ ZMQ_POLLOUT);
+ cb->read.thread = NULL;
+ if (cb->write.cancelled && !cb->write.thread)
+ XFREE(MTYPE_ZEROMQ_CB, cb);
return 0;
}
continue;
zmq_msg_close(&msg);
goto out_err;
}
+ read = 1;
- cb->cb_part(cb->arg, cb->zmqsock, &msg, partno);
- if (cb->cancelled) {
+ cb->read.cb_part(cb->read.arg, cb->zmqsock, &msg,
+ partno);
+ if (cb->read.cancelled) {
zmq_msg_close(&msg);
- XFREE(MTYPE_ZEROMQ_CB, cb);
+ frrzmq_check_events(cbp, &cb->write,
+ ZMQ_POLLOUT);
+ cb->read.thread = NULL;
+ if (cb->write.cancelled && !cb->write.thread)
+ XFREE(MTYPE_ZEROMQ_CB, cb);
return 0;
}
* message; don't use zmq_msg_more here */
moresz = sizeof(more);
more = 0;
- ret = zmq_getsockopt(cb->zmqsock, ZMQ_RCVMORE,
- &more, &moresz);
+ ret = zmq_getsockopt(cb->zmqsock, ZMQ_RCVMORE, &more,
+ &moresz);
if (ret < 0) {
zmq_msg_close(&msg);
goto out_err;
zmq_msg_close(&msg);
}
- funcname_thread_add_read_write(THREAD_READ, t->master, frrzmq_read_msg,
- cb, cb->fd, &cb->thread, t->funcname, t->schedfrom,
- t->schedfrom_line);
+ if (read)
+ frrzmq_check_events(cbp, &cb->write, ZMQ_POLLOUT);
+
+ funcname_thread_add_read_write(
+ THREAD_READ, t->master, frrzmq_read_msg, cbp, cb->fd,
+ &cb->read.thread, t->funcname, t->schedfrom, t->schedfrom_line);
return 0;
out_err:
- zlog_err("ZeroMQ error: %s(%d)", strerror (errno), errno);
- return 0;
+ zlog_err("ZeroMQ read error: %s(%d)", strerror(errno), errno);
+ if (cb->read.cb_error)
+ cb->read.cb_error(cb->read.arg, cb->zmqsock);
+ return 1;
}
-struct frrzmq_cb *funcname_frrzmq_thread_add_read(
- struct thread_master *master,
- void (*msgfunc)(void *arg, void *zmqsock),
- void (*partfunc)(void *arg, void *zmqsock,
- zmq_msg_t *msg, unsigned partnum),
- void *arg, void *zmqsock, debugargdef)
+int funcname_frrzmq_thread_add_read(struct thread_master *master,
+ void (*msgfunc)(void *arg, void *zmqsock),
+ void (*partfunc)(void *arg, void *zmqsock,
+ zmq_msg_t *msg,
+ unsigned partnum),
+ void (*errfunc)(void *arg, void *zmqsock),
+ void *arg, void *zmqsock,
+ struct frrzmq_cb **cbp, debugargdef)
{
int fd, events;
size_t len;
struct frrzmq_cb *cb;
+ if (!cbp)
+ return -1;
if (!(msgfunc || partfunc) || (msgfunc && partfunc))
- return NULL;
+ return -1;
+ len = sizeof(fd);
+ if (zmq_getsockopt(zmqsock, ZMQ_FD, &fd, &len))
+ return -1;
+ len = sizeof(events);
+ if (zmq_getsockopt(zmqsock, ZMQ_EVENTS, &events, &len))
+ return -1;
+
+ if (*cbp)
+ cb = *cbp;
+ else {
+ cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb));
+ cb->write.cancelled = 1;
+ if (!cb)
+ return -1;
+ *cbp = cb;
+ }
+
+ cb->zmqsock = zmqsock;
+ cb->fd = fd;
+ cb->read.arg = arg;
+ cb->read.cb_msg = msgfunc;
+ cb->read.cb_part = partfunc;
+ cb->read.cb_error = errfunc;
+ cb->read.cancelled = 0;
+
+ if (events & ZMQ_POLLIN) {
+ if (cb->read.thread) {
+ thread_cancel(cb->read.thread);
+ cb->read.thread = NULL;
+ }
+ funcname_thread_add_event(master, frrzmq_read_msg, cbp, fd,
+ &cb->read.thread, funcname, schedfrom,
+ fromln);
+ } else
+ funcname_thread_add_read_write(
+ THREAD_READ, master, frrzmq_read_msg, cbp, fd,
+ &cb->read.thread, funcname, schedfrom, fromln);
+ return 0;
+}
+
+static int frrzmq_write_msg(struct thread *t)
+{
+ struct frrzmq_cb **cbp = THREAD_ARG(t);
+ struct frrzmq_cb *cb;
+ unsigned char written = 0;
+ int ret;
+
+ if (!cbp)
+ return 1;
+ cb = (*cbp);
+ if (!cb || !cb->zmqsock)
+ return 1;
+
+ while (1) {
+ zmq_pollitem_t polli = {.socket = cb->zmqsock,
+ .events = ZMQ_POLLOUT};
+ ret = zmq_poll(&polli, 1, 0);
+
+ if (ret < 0)
+ goto out_err;
+
+ if (!(polli.revents & ZMQ_POLLOUT))
+ break;
+
+ if (cb->write.cb_msg) {
+ cb->write.cb_msg(cb->write.arg, cb->zmqsock);
+ 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);
+ return 0;
+ }
+ continue;
+ }
+ }
+
+ if (written)
+ frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN);
+
+ funcname_thread_add_read_write(THREAD_WRITE, t->master,
+ frrzmq_write_msg, cbp, cb->fd,
+ &cb->write.thread, t->funcname,
+ t->schedfrom, t->schedfrom_line);
+ return 0;
+
+out_err:
+ zlog_err("ZeroMQ write error: %s(%d)", strerror(errno), errno);
+ if (cb->write.cb_error)
+ cb->write.cb_error(cb->write.arg, cb->zmqsock);
+ return 1;
+}
+int funcname_frrzmq_thread_add_write(struct thread_master *master,
+ void (*msgfunc)(void *arg, void *zmqsock),
+ void (*errfunc)(void *arg, void *zmqsock),
+ void *arg, void *zmqsock,
+ struct frrzmq_cb **cbp, debugargdef)
+{
+ int fd, events;
+ size_t len;
+ struct frrzmq_cb *cb;
+
+ if (!cbp)
+ return -1;
+ if (!msgfunc)
+ return -1;
len = sizeof(fd);
if (zmq_getsockopt(zmqsock, ZMQ_FD, &fd, &len))
- return NULL;
+ return -1;
len = sizeof(events);
if (zmq_getsockopt(zmqsock, ZMQ_EVENTS, &events, &len))
- return NULL;
+ return -1;
- cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb));
- if (!cb)
- return NULL;
+ if (*cbp)
+ cb = *cbp;
+ else {
+ cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb));
+ cb->read.cancelled = 1;
+ if (!cb)
+ return -1;
+ *cbp = cb;
+ }
- cb->arg = arg;
cb->zmqsock = zmqsock;
- cb->cb_msg = msgfunc;
- cb->cb_part = partfunc;
cb->fd = fd;
+ cb->write.arg = arg;
+ cb->write.cb_msg = msgfunc;
+ cb->write.cb_part = NULL;
+ cb->write.cb_error = errfunc;
+ cb->write.cancelled = 0;
+
+ if (events & ZMQ_POLLOUT) {
+ if (cb->write.thread) {
+ thread_cancel(cb->write.thread);
+ cb->write.thread = NULL;
+ }
+ funcname_thread_add_event(master, frrzmq_write_msg, cbp, fd,
+ &cb->write.thread, funcname,
+ schedfrom, fromln);
+ } else
+ funcname_thread_add_read_write(
+ THREAD_WRITE, master, frrzmq_write_msg, cbp, fd,
+ &cb->write.thread, funcname, schedfrom, fromln);
+ return 0;
+}
- if (events & ZMQ_POLLIN)
- funcname_thread_add_event(master,
- frrzmq_read_msg, cb, fd, &cb->thread,
- funcname, schedfrom, fromln);
- else
- funcname_thread_add_read_write(THREAD_READ, master,
- frrzmq_read_msg, cb, fd, &cb->thread,
- funcname, schedfrom, fromln);
- return cb;
+void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core)
+{
+ if (!cb || !*cb)
+ return;
+ core->cancelled = 1;
+ if (core->thread) {
+ thread_cancel(core->thread);
+ core->thread = NULL;
+ }
+ if ((*cb)->read.cancelled && !(*cb)->read.thread
+ && (*cb)->write.cancelled && (*cb)->write.thread)
+ XFREE(MTYPE_ZEROMQ_CB, *cb);
}
-void frrzmq_thread_cancel(struct frrzmq_cb *cb)
+void frrzmq_check_events(struct frrzmq_cb **cbp, struct cb_core *core,
+ int event)
{
- if (!cb->thread) {
- /* canceling from within callback */
- cb->cancelled = 1;
+ struct frrzmq_cb *cb;
+ int events;
+ size_t len;
+
+ if (!cbp)
+ return;
+ cb = (*cbp);
+ if (!cb || !cb->zmqsock)
+ return;
+
+ if (zmq_getsockopt(cb->zmqsock, ZMQ_EVENTS, &events, &len))
return;
+ if (events & event && core->thread && !core->cancelled) {
+ struct thread_master *tm = core->thread->master;
+ thread_cancel(core->thread);
+ core->thread = NULL;
+ thread_add_event(tm, (event == ZMQ_POLLIN ? frrzmq_read_msg
+ : frrzmq_write_msg),
+ cbp, cb->fd, &core->thread);
}
- thread_cancel(cb->thread);
- XFREE(MTYPE_ZEROMQ_CB, cb);
}
* foo_LDFLAGS = libfrrzmq.la libfrr.la $(ZEROMQ_LIBS)
*/
+/* callback integration */
+struct cb_core {
+ struct thread *thread;
+ void *arg;
+
+ bool cancelled;
+
+ void (*cb_msg)(void *arg, void *zmqsock);
+ void (*cb_part)(void *arg, void *zmqsock, zmq_msg_t *msg,
+ unsigned partnum);
+ void (*cb_error)(void *arg, void *zmqsock);
+};
+struct frrzmq_cb {
+ void *zmqsock;
+ int fd;
+
+ struct cb_core read;
+ struct cb_core write;
+};
+
/* libzmq's context
*
* this is mostly here as a convenience, it has IPv6 enabled but nothing
*/
extern void *frrzmq_context;
-extern void frrzmq_init (void);
-extern void frrzmq_finish (void);
+extern void frrzmq_init(void);
+extern void frrzmq_finish(void);
#define debugargdef const char *funcname, const char *schedfrom, int fromln
/* core event registration, one of these 2 macros should be used */
-#define frrzmq_thread_add_read_msg(m,f,a,z) funcname_frrzmq_thread_add_read( \
- m,f,NULL,a,z,#f,__FILE__,__LINE__)
-#define frrzmq_thread_add_read_part(m,f,a,z) funcname_frrzmq_thread_add_read( \
- m,NULL,f,a,z,#f,__FILE__,__LINE__)
+#define frrzmq_thread_add_read_msg(m, f, e, a, z, d) \
+ funcname_frrzmq_thread_add_read(m, f, NULL, e, a, z, d, #f, __FILE__, \
+ __LINE__)
+#define frrzmq_thread_add_read_part(m, f, e, a, z, d) \
+ funcname_frrzmq_thread_add_read(m, NULL, f, e, a, z, d, #f, __FILE__, \
+ __LINE__)
+#define frrzmq_thread_add_write_msg(m, f, e, a, z, d) \
+ funcname_frrzmq_thread_add_write(m, f, e, a, z, d, #f, __FILE__, \
+ __LINE__)
+struct cb_core;
struct frrzmq_cb;
-/* Set up a POLLIN notification to be called from the libfrr main loop.
- * This has the following properties:
+/* Set up a POLLIN or POLLOUT notification to be called from the libfrr main
+ * loop. This has the following properties:
*
* - since ZeroMQ works with edge triggered notifications, it will loop and
* dispatch as many events as ZeroMQ has pending at the time libfrr calls
* - if partfunc is specified, the message is read and partfunc is called
* for each ZeroMQ multi-part subpart. Note that you can't send replies
* before all parts have been read because that violates the ZeroMQ FSM.
+ * - write version doesn't allow for partial callback, you must handle the
+ * whole message (all parts) in msgfunc callback
* - you can safely cancel the callback from within itself
* - installing a callback will check for pending events (ZMQ_EVENTS) and
* may schedule the event to run as soon as libfrr is back in its main
* loop.
+ */
+extern int funcname_frrzmq_thread_add_read(
+ struct thread_master *master, void (*msgfunc)(void *arg, void *zmqsock),
+ void (*partfunc)(void *arg, void *zmqsock, zmq_msg_t *msg,
+ unsigned partnum),
+ void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock,
+ struct frrzmq_cb **cb, debugargdef);
+extern int funcname_frrzmq_thread_add_write(
+ struct thread_master *master, void (*msgfunc)(void *arg, void *zmqsock),
+ void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock,
+ struct frrzmq_cb **cb, debugargdef);
+
+extern void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core);
+
+/*
+ * http://api.zeromq.org/4-2:zmq-getsockopt#toc10
*
- * TODO #1: add ZMQ_POLLERR / error callback
- * TODO #2: add frrzmq_check_events() function to check for edge triggered
- * things that may have happened after a zmq_send() call or so
+ * As the descriptor is edge triggered, applications must update the state of
+ * ZMQ_EVENTS after each invocation of zmq_send or zmq_recv.To be more explicit:
+ * after calling zmq_send the socket may become readable (and vice versa)
+ * without triggering a read event on the file descriptor.
*/
-extern struct frrzmq_cb *funcname_frrzmq_thread_add_read(
- struct thread_master *master,
- void (*msgfunc)(void *arg, void *zmqsock),
- void (*partfunc)(void *arg, void *zmqsock,
- zmq_msg_t *msg, unsigned partnum),
- void *arg, void *zmqsock, debugargdef);
-
-extern void frrzmq_thread_cancel(struct frrzmq_cb *cb);
+extern void frrzmq_check_events(struct frrzmq_cb **cbp, struct cb_core *core,
+ int event);
#endif /* _FRRZMQ_H */
DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
-#define MAXDEPTH 64
-
/** headers **/
void grammar_sandbox_init(void);
void pretty_print_graph(struct vty *vty, struct graph_node *, int, int,
{
check_nodegraph();
- struct graph_node *stack[MAXDEPTH];
+ struct graph_node *stack[CMD_ARGC_MAX];
pretty_print_graph(vty, vector_slot(nodegraph->nodes, 0), 0, argc >= 3,
stack, 0);
return CMD_SUCCESS;
{
check_nodegraph();
- struct graph_node *stack[MAXDEPTH];
- struct graph_node *visited[MAXDEPTH * MAXDEPTH];
+ struct graph_node *stack[CMD_ARGC_MAX];
+ struct graph_node *visited[CMD_ARGC_MAX * CMD_ARGC_MAX];
size_t vpos = 0;
FILE *ofd = fopen(argv[2]->arg, "w");
return;
}
- if (++stackpos == MAXDEPTH)
+ if (++stackpos == CMD_ARGC_MAX)
return;
for (i = 0; i < vector_active(gn->to); i++) {
static struct list *cmd_graph_permutations(struct graph *graph)
{
char accumulate[2048] = "";
- struct graph_node *stack[MAXDEPTH];
+ struct graph_node *stack[CMD_ARGC_MAX];
struct list *rv = list_new();
rv->cmp = cmd_permute_cmp;
vty_out(vty, " ?'%s'", tok->desc);
vty_out(vty, " ");
- if (stackpos == MAXDEPTH) {
+ if (stackpos == CMD_ARGC_MAX) {
vty_out(vty, " -aborting! (depth limit)\n");
return;
}
if (visited[i] == start)
return;
visited[(*visitpos)++] = start;
- if ((*visitpos) == MAXDEPTH * MAXDEPTH)
+ if ((*visitpos) == CMD_ARGC_MAX * CMD_ARGC_MAX)
return;
snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
}
fprintf(ofd, ">, style = filled, fillcolor = \"%s\" ];\n", color);
- if (stackpos == MAXDEPTH)
+ if (stackpos == CMD_ARGC_MAX)
return;
stack[stackpos++] = start;
if_link_params_free(ifp);
+ if (ifp->desc)
+ XFREE(MTYPE_TMP, ifp->desc);
+
XFREE(MTYPE_IF, ifp);
}
struct vrf *vrf;
struct interface if_tmp;
+ if (vrf_id == VRF_UNKNOWN) {
+ struct interface *ifp;
+
+ RB_FOREACH(vrf, vrf_id_head, &vrfs_by_id) {
+ ifp = if_lookup_by_index(ifindex, vrf->vrf_id);
+ if (ifp)
+ return ifp;
+ }
+
+ return NULL;
+ }
+
vrf = vrf_lookup_by_id(vrf_id);
if (!vrf)
return NULL;
"Interface's name\n"
VRF_CMD_HELP_STR)
{
+ int idx_vrf = 4;
const char *ifname = argv[2]->arg;
- const char *vrfname = (argc > 3) ? argv[3]->arg : NULL;
+ const char *vrfname = (argc > 3) ? argv[idx_vrf]->arg : NULL;
// deleting interface
struct interface *ifp;
/* Prototypes. */
extern int if_cmp_name_func(char *, char *);
+/*
+ * Passing in VRF_UNKNOWN is a valid thing to do, unless we
+ * are creating a new interface.
+ *
+ * This is useful for vrf route-leaking. So more than anything
+ * else think before you use VRF_UNKNOWN
+ */
extern void if_update_to_new_vrf(struct interface *, vrf_id_t vrf_id);
extern struct interface *if_create(const char *name, vrf_id_t vrf_id);
extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id);
DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW),
DESC_ENTRY(ZEBRA_VNI_ADD),
DESC_ENTRY(ZEBRA_VNI_DEL),
+ DESC_ENTRY(ZEBRA_L3VNI_ADD),
+ DESC_ENTRY(ZEBRA_L3VNI_DEL),
DESC_ENTRY(ZEBRA_REMOTE_VTEP_ADD),
DESC_ENTRY(ZEBRA_REMOTE_VTEP_DEL),
DESC_ENTRY(ZEBRA_MACIP_ADD),
-/* $USAGI: md5.c,v 1.2 2000/11/02 11:59:24 yoshfuji Exp $ */
-/* $KAME: md5.c,v 1.2 2000/05/27 07:07:48 jinmei Exp $ */
-/* $Id: md5.c,v 1.6 2006/01/17 23:39:04 vincent Exp $ */
-
/*
* Copyright (C) 2004 6WIND
* <Vincent.Jardin@6WIND.com>
-/* $USAGI: md5.h,v 1.2 2000/11/02 11:59:25 yoshfuji Exp $ */
-/* $KAME: md5.h,v 1.4 2000/03/27 04:36:22 sumikawa Exp $ */
-/* $Id: md5.h,v 1.3 2006/01/17 17:40:45 paul Exp $ */
-
/*
* Copyright (C) 2004 6WIND
* <Vincent.Jardin@6WIND.com>
/* MPLS label value as a 32-bit (mostly we only care about the label value). */
typedef unsigned int mpls_label_t;
+struct mpls_label_stack {
+ uint8_t num_labels;
+ uint8_t reserved[3];
+ mpls_label_t label[0]; /* 1 or more labels */
+};
+
/* The MPLS explicit-null label is 0 which means when you memset a mpls_label_t
* to zero you have set that variable to explicit-null which was probably not
* your intent. The work-around is to use one bit to indicate if the
*/
int nexthop_labels_match(struct nexthop *nh1, struct nexthop *nh2)
{
- struct nexthop_label *nhl1, *nhl2;
+ struct mpls_label_stack *nhl1, *nhl2;
nhl1 = nh1->nh_label;
nhl2 = nh2->nh_label;
void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type,
u_int8_t num_labels, mpls_label_t *label)
{
- struct nexthop_label *nh_label;
+ struct mpls_label_stack *nh_label;
int i;
nexthop->nh_label_type = type;
nh_label = XCALLOC(MTYPE_NH_LABEL,
- sizeof(struct nexthop_label)
+ sizeof(struct mpls_label_stack)
+ num_labels * sizeof(mpls_label_t));
nh_label->num_labels = num_labels;
for (i = 0; i < num_labels; i++)
((type) == NEXTHOP_TYPE_IFINDEX || (type) == NEXTHOP_TYPE_BLACKHOLE) \
? (type) : ((type) | 1)
-/* Nexthop label structure. */
-struct nexthop_label {
- u_int8_t num_labels;
- u_int8_t reserved[3];
- mpls_label_t label[0]; /* 1 or more labels. */
-};
-
/* Nexthop structure. */
struct nexthop {
struct nexthop *next;
#define NEXTHOP_FLAG_MATCHED (1 << 4) /* Already matched vs a nexthop */
#define NEXTHOP_FLAG_FILTERED (1 << 5) /* rmap filtered, used by static only */
#define NEXTHOP_FLAG_DUPLICATE (1 << 6) /* nexthop duplicates another active one */
+#define NEXTHOP_FLAG_EVPN_RVTEP (1 << 7) /* EVPN remote vtep nexthop */
#define NEXTHOP_IS_ACTIVE(flags) \
(CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \
&& !CHECK_FLAG(flags, NEXTHOP_FLAG_DUPLICATE))
enum lsp_types_t nh_label_type;
/* Label(s) associated with this nexthop. */
- struct nexthop_label *nh_label;
+ struct mpls_label_stack *nh_label;
};
/* The following for loop allows to iterate over the nexthop
#include "openbsd-tree.h"
#include "linklist.h"
-typedef u_int16_t ns_id_t;
+typedef u_int32_t ns_id_t;
-/* The default NS ID */
+/* the default NS ID */
#define NS_DEFAULT 0
+#define NS_UNKNOWN UINT32_MAX
/* Default netns directory (Linux) */
#define NS_RUN_DIR "/var/run/netns"
#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff)
-static int is_zero_mac(const struct ethaddr *mac)
+int is_zero_mac(struct ethaddr *mac)
{
int i = 0;
family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)
? AF_INET
: AF_INET6;
- snprintf(str, size, "[%d]:[%u][%s]/%d",
+ snprintf(str, size, "[%d]:[%u][%s/%d]/%d",
p->u.prefix_evpn.route_type, p->u.prefix_evpn.eth_tag,
inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, buf,
PREFIX2STR_BUFFER),
+ p->u.prefix_evpn.ip_prefix_length,
p->prefixlen);
} else {
sprintf(str, "Unsupported EVPN route type %d",
extern const char *inet6_ntoa(struct in6_addr);
+extern int is_zero_mac(struct ethaddr *mac);
extern int prefix_str2mac(const char *str, struct ethaddr *mac);
extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size);
return 0;
}
+static inline int is_host_route(struct prefix *p)
+{
+ if (p->family == AF_INET)
+ return (p->prefixlen == IPV4_MAX_BITLEN);
+ else if (p->family == AF_INET6)
+ return (p->prefixlen == IPV6_MAX_BITLEN);
+ return 0;
+}
+
#endif /* _ZEBRA_PREFIX_H */
return 0;
}
+int ptm_lib_cleanup_msg(ptm_lib_handle_t *hdl, void *ctxt)
+{
+ ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
+ csv_t *csv;
+
+ if (!p_ctxt) {
+ ERRLOG("%s: no context \n", __FUNCTION__);
+ return -1;
+ }
+
+ csv = p_ctxt->csv;
+
+ csv_clean(csv);
+ csv_free(csv);
+ free(p_ctxt);
+
+ return 0;
+}
+
int ptm_lib_complete_msg(ptm_lib_handle_t *hdl, void *ctxt, char *buf, int *len)
{
ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
int ptm_lib_init_msg(ptm_lib_handle_t *, int, int, void *, void **);
int ptm_lib_append_msg(ptm_lib_handle_t *, void *, const char *, const char *);
int ptm_lib_complete_msg(ptm_lib_handle_t *, void *, char *, int *);
+int ptm_lib_cleanup_msg(ptm_lib_handle_t *, void *);
#endif
--- /dev/null
+/*
+ * Circular buffer implementation.
+ * Copyright (C) 2017 Cumulus Networks
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include "ringbuf.h"
+#include "memory.h"
+
+DEFINE_MTYPE_STATIC(LIB, RINGBUFFER, "Ring buffer")
+
+struct ringbuf *ringbuf_new(size_t size)
+{
+ struct ringbuf *buf = XCALLOC(MTYPE_RINGBUFFER, sizeof(struct ringbuf));
+ buf->data = XCALLOC(MTYPE_RINGBUFFER, size);
+ buf->size = size;
+ buf->empty = true;
+ return buf;
+}
+
+void ringbuf_del(struct ringbuf *buf)
+{
+ XFREE(MTYPE_RINGBUFFER, buf->data);
+ XFREE(MTYPE_RINGBUFFER, buf);
+}
+
+size_t ringbuf_remain(struct ringbuf *buf)
+{
+ ssize_t diff = buf->end - buf->start;
+ diff += ((diff == 0) && !buf->empty) ? buf->size : 0;
+ diff += (diff < 0) ? buf->size : 0;
+ return (size_t)diff;
+}
+
+size_t ringbuf_space(struct ringbuf *buf)
+{
+ return buf->size - ringbuf_remain(buf);
+}
+
+size_t ringbuf_put(struct ringbuf *buf, const void *data, size_t size)
+{
+ const uint8_t *dp = data;
+ size_t space = ringbuf_space(buf);
+ size_t copysize = MIN(size, space);
+ size_t tocopy = copysize;
+ if (tocopy >= buf->size - buf->end) {
+ size_t ts = buf->size - buf->end;
+ memcpy(buf->data + buf->end, dp, ts);
+ buf->end = 0;
+ tocopy -= ts;
+ dp += ts;
+ }
+ memcpy(buf->data + buf->end, dp, tocopy);
+ buf->end += tocopy;
+ buf->empty = (buf->start == buf->end) && (buf->empty && !copysize);
+ return copysize;
+}
+
+size_t ringbuf_get(struct ringbuf *buf, void *data, size_t size)
+{
+ uint8_t *dp = data;
+ size_t remain = ringbuf_remain(buf);
+ size_t copysize = MIN(remain, size);
+ size_t tocopy = copysize;
+ if (tocopy >= buf->size - buf->start) {
+ size_t ts = buf->size - buf->start;
+ memcpy(dp, buf->data + buf->start, ts);
+ buf->start = 0;
+ tocopy -= ts;
+ dp += ts;
+ }
+ memcpy(dp, buf->data + buf->start, tocopy);
+ buf->start = buf->start + tocopy;
+ buf->empty = (buf->start == buf->end) && (buf->empty || copysize);
+ return copysize;
+}
+
+size_t ringbuf_peek(struct ringbuf *buf, size_t offset, void *data, size_t size)
+{
+ uint8_t *dp = data;
+ size_t remain = ringbuf_remain(buf);
+ if (offset >= remain)
+ return 0;
+ size_t copysize = MAX(MIN(remain - offset, size), (size_t) 0);
+ size_t tocopy = copysize;
+ size_t cstart = (buf->start + offset) % buf->size;
+ if (tocopy >= buf->size - cstart) {
+ size_t ts = buf->size - cstart;
+ memcpy(dp, buf->data + cstart, ts);
+ cstart = 0;
+ tocopy -= ts;
+ dp += ts;
+ }
+ memcpy(dp, buf->data + cstart, tocopy);
+ return copysize;
+}
+
+size_t ringbuf_copy(struct ringbuf *to, struct ringbuf *from, size_t size)
+{
+ size_t tocopy = MIN(ringbuf_space(to), size);
+ uint8_t *cbuf = XCALLOC(MTYPE_TMP, tocopy);
+ tocopy = ringbuf_peek(from, 0, cbuf, tocopy);
+ size_t put = ringbuf_put(to, cbuf, tocopy);
+ XFREE(MTYPE_TMP, cbuf);
+ return put;
+}
+
+void ringbuf_reset(struct ringbuf *buf)
+{
+ buf->start = buf->end = 0;
+ buf->empty = true;
+}
+
+void ringbuf_wipe(struct ringbuf *buf)
+{
+ memset(buf->data, 0x00, buf->size);
+ ringbuf_reset(buf);
+}
--- /dev/null
+/*
+ * Circular buffer implementation.
+ * Copyright (C) 2017 Cumulus Networks
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef _FRR_RINGBUF_H_
+#define _FRR_RINGBUF_H_
+
+#include <zebra.h>
+#include <stdint.h>
+
+#include "memory.h"
+
+struct ringbuf {
+ size_t size;
+ ssize_t start;
+ ssize_t end;
+ bool empty;
+ uint8_t *data;
+};
+
+/*
+ * Creates a new ring buffer.
+ *
+ * @param size buffer size, in bytes
+ * @return the newly created buffer
+ */
+struct ringbuf *ringbuf_new(size_t size);
+
+/*
+ * Deletes a ring buffer and frees all associated resources.
+ *
+ * @param buf the ring buffer to destroy
+ */
+void ringbuf_del(struct ringbuf *buf);
+
+/*
+ * Get amount of data left to read from the buffer.
+ *
+ * @return number of readable bytes
+ */
+size_t ringbuf_remain(struct ringbuf *buf);
+
+/*
+ * Get amount of space left to write to the buffer
+ *
+ * @return number of writeable bytes
+ */
+size_t ringbuf_space(struct ringbuf *buf);
+
+
+/*
+ * Put data into the ring buffer.
+ *
+ * @param data the data to put in the buffer
+ * @param size how much of data to put in
+ * @return number of bytes written; will be less than size if there was not
+ * enough space
+ */
+size_t ringbuf_put(struct ringbuf *buf, const void *data, size_t size);
+
+/*
+ * Get data from the ring buffer.
+ *
+ * @param data where to put the data
+ * @param size how much of data to get
+ * @return number of bytes read into data; will be less than size if there was
+ * not enough data to read
+ */
+size_t ringbuf_get(struct ringbuf *buf, void *data, size_t size);
+
+/*
+ * Peek data from the ring buffer.
+ *
+ * @param offset where to get the data from, in bytes offset from the
+ * start of the data
+ * @param data where to put the data
+ * @param size how much data to get
+ * @return number of bytes read into data; will be less than size
+ * if there was not enough data to read; will be -1 if the
+ * offset exceeds the amount of data left in the ring
+ * buffer
+ */
+size_t ringbuf_peek(struct ringbuf *buf, size_t offset, void *data,
+ size_t size);
+
+/*
+ * Copy data from one ringbuf to another.
+ *
+ * @param to destination ringbuf
+ * @param from source ringbuf
+ * @param size how much data to copy
+ * @return amount of data copied
+ */
+size_t ringbuf_copy(struct ringbuf *to, struct ringbuf *from, size_t size);
+
+/*
+ * Reset buffer. Does not wipe.
+ *
+ * @param buf
+ */
+void ringbuf_reset(struct ringbuf *buf);
+
+/*
+ * Reset buffer. Wipes.
+ *
+ * @param buf
+ */
+void ringbuf_wipe(struct ringbuf *buf);
+
+#endif /* _FRR_RINGBUF_H_ */
lib/privs.c \
lib/ptm_lib.c \
lib/qobj.c \
+ lib/ringbuf.c \
lib/routemap.c \
lib/sbuf.c \
lib/sha256.c \
lib/pw.h \
lib/qobj.h \
lib/queue.h \
+ lib/ringbuf.h \
lib/routemap.h \
lib/sbuf.h \
lib/sha256.h \
*/
static void thread_cancel_rw(struct thread_master *master, int fd, short state)
{
+ bool found = false;
+
/* Cancel POLLHUP too just in case some bozo set it */
state |= POLLHUP;
nfds_t i;
for (i = 0; i < master->handler.pfdcount; i++)
- if (master->handler.pfds[i].fd == fd)
+ if (master->handler.pfds[i].fd == fd) {
+ found = true;
break;
+ }
+
+ if (!found) {
+ zlog_debug(
+ "[!] Received cancellation request for nonexistent rw job");
+ zlog_debug("[!] threadmaster: %s | fd: %d",
+ master->name ? master->name : "", fd);
+ return;
+ }
/* NOT out event. */
master->handler.pfds[i].events &= ~(state);
int new = 0;
if (debug_vrf)
- zlog_debug("VRF_GET: %s(%d)", name, vrf_id);
+ zlog_debug("VRF_GET: %s(%u)", name, vrf_id);
/* Nothing to see, move along here */
if (!name && vrf_id == VRF_UNKNOWN)
(*vrf_master.vrf_disable_hook)(vrf);
}
+const char *vrf_id_to_name(vrf_id_t vrf_id)
+{
+ struct vrf *vrf;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (vrf)
+ return vrf->name;
+
+ return "n/a";
+}
+
vrf_id_t vrf_name_to_id(const char *name)
{
struct vrf *vrf;
* VRF bit-map
*/
-#define VRF_BITMAP_NUM_OF_GROUPS 8
-#define VRF_BITMAP_NUM_OF_BITS_IN_GROUP (UINT16_MAX / VRF_BITMAP_NUM_OF_GROUPS)
+#define VRF_BITMAP_NUM_OF_GROUPS 1024
+#define VRF_BITMAP_NUM_OF_BITS_IN_GROUP (UINT32_MAX / VRF_BITMAP_NUM_OF_GROUPS)
#define VRF_BITMAP_NUM_OF_BYTES_IN_GROUP \
(VRF_BITMAP_NUM_OF_BITS_IN_GROUP / CHAR_BIT + 1) /* +1 for ensure */
struct vrf *vrf = NULL;
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
- if (vrf->vrf_id != 0)
+ if (vrf->vrf_id != VRF_DEFAULT)
vector_set(comps, XSTRDUP(MTYPE_COMPLETION, vrf->name));
}
}
/* The default VRF ID */
#define VRF_DEFAULT 0
-#define VRF_UNKNOWN UINT16_MAX
-#define VRF_ALL UINT16_MAX - 1
+#define VRF_UNKNOWN UINT32_MAX
/* Pending: May need to refine this. */
#ifndef IFLA_VRF_MAX
extern struct vrf *vrf_lookup_by_id(vrf_id_t);
extern struct vrf *vrf_lookup_by_name(const char *);
extern struct vrf *vrf_get(vrf_id_t, const char *);
+extern const char *vrf_id_to_name(vrf_id_t vrf_id);
extern vrf_id_t vrf_name_to_id(const char *);
#define VRF_GET_ID(V, NAME) \
/*
- * $Id: zassert.h,v 1.2 2004/12/03 18:01:04 ajs Exp $
- *
* This file is part of Quagga.
*
* Quagga is free software; you can redistribute it and/or modify it
stream_putw(s, ZEBRA_HEADER_SIZE);
stream_putc(s, ZEBRA_HEADER_MARKER);
stream_putc(s, ZSERV_VERSION);
- stream_putw(s, vrf_id);
+ stream_putl(s, vrf_id);
stream_putw(s, command);
}
*size -= ZEBRA_HEADER_SIZE;
STREAM_GETC(s, *marker);
STREAM_GETC(s, *version);
- STREAM_GETW(s, *vrf_id);
+ STREAM_GETL(s, *vrf_id);
STREAM_GETW(s, *cmd);
if (*version != ZSERV_VERSION || *marker != ZEBRA_HEADER_MARKER) {
vrf_id);
/* Flush all redistribute request. */
- if (vrf_id == VRF_DEFAULT)
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
- if (zclient->mi_redist[afi][i].enabled) {
- struct listnode *node;
- u_short *id;
-
- for (ALL_LIST_ELEMENTS_RO(
- zclient->mi_redist[afi][i]
- .instances,
- node, id))
- if (!(i == zclient->redist_default
- && *id == zclient->instance))
- zebra_redistribute_send(
- ZEBRA_REDISTRIBUTE_ADD,
- zclient, afi, i,
- *id,
- VRF_DEFAULT);
- }
+ if (vrf_id == VRF_DEFAULT) {
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (!zclient->mi_redist[afi][i].enabled)
+ continue;
+
+ struct listnode *node;
+ u_short *id;
+
+ for (ALL_LIST_ELEMENTS_RO(
+ zclient->mi_redist[afi][i]
+ .instances, node, id))
+ if (!(i == zclient->redist_default
+ && *id == zclient->instance))
+ zebra_redistribute_send(
+ ZEBRA_REDISTRIBUTE_ADD,
+ zclient, afi, i,
+ *id,
+ VRF_DEFAULT);
+ }
+ }
+ }
/* Flush all redistribute request. */
for (afi = AFI_IP; afi < AFI_MAX; afi++)
/* Set unwanted redistribute route. */
for (afi = AFI_IP; afi < AFI_MAX; afi++)
- vrf_bitmap_set(zclient->redist[afi][zclient->redist_default],
- vrf_id);
+ vrf_bitmap_unset(zclient->redist[afi][zclient->redist_default],
+ vrf_id);
/* Flush all redistribute request. */
- if (vrf_id == VRF_DEFAULT)
- for (afi = AFI_IP; afi < AFI_MAX; afi++)
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
- if (zclient->mi_redist[afi][i].enabled) {
- struct listnode *node;
- u_short *id;
-
- for (ALL_LIST_ELEMENTS_RO(
- zclient->mi_redist[afi][i]
- .instances,
- node, id))
- if (!(i == zclient->redist_default
- && *id == zclient->instance))
- zebra_redistribute_send(
- ZEBRA_REDISTRIBUTE_DELETE,
- zclient, afi, i,
- *id,
- VRF_DEFAULT);
- }
+ if (vrf_id == VRF_DEFAULT) {
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (!zclient->mi_redist[afi][i].enabled)
+ continue;
+
+ struct listnode *node;
+ u_short *id;
+
+ for (ALL_LIST_ELEMENTS_RO(
+ zclient->mi_redist[afi][i]
+ .instances, node, id))
+ if (!(i == zclient->redist_default
+ && *id == zclient->instance))
+ zebra_redistribute_send(
+ ZEBRA_REDISTRIBUTE_DELETE,
+ zclient, afi, i,
+ *id,
+ VRF_DEFAULT);
+ }
+ }
+ }
/* Flush all redistribute request. */
for (afi = AFI_IP; afi < AFI_MAX; afi++)
return zclient_start(zclient);
}
+int zclient_send_rnh(struct zclient *zclient, int command, struct prefix *p,
+ bool exact_match, vrf_id_t vrf_id)
+{
+ struct stream *s;
+
+ s = zclient->obuf;
+ stream_reset(s);
+ zclient_create_header(s, command, vrf_id);
+ stream_putc(s, (exact_match) ? 1 : 0);
+
+ stream_putw(s, PREFIX_FAMILY(p));
+ stream_putc(s, p->prefixlen);
+ switch (PREFIX_FAMILY(p)) {
+ case AF_INET:
+ stream_put_in_addr(s, &p->u.prefix4);
+ break;
+ case AF_INET6:
+ stream_put(s, &(p->u.prefix6), 16);
+ break;
+ default:
+ break;
+ }
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
/*
* "xdr_encode"-like interface that allows daemon (client) to send
* a message to zebra server for a route that needs to be
stream_putl(s, api->flags);
stream_putc(s, api->message);
stream_putc(s, api->safi);
+ if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
+ stream_put(s, &(api->rmac), sizeof(struct ethaddr));
/* Put prefix information. */
stream_putc(s, api->prefix.family);
}
stream_putw(s, api->nexthop_num);
+ if (api->nexthop_num)
+ stream_putw(s, api->nh_vrf_id);
for (i = 0; i < api->nexthop_num; i++) {
api_nh = &api->nexthops[i];
STREAM_GETL(s, api->flags);
STREAM_GETC(s, api->message);
STREAM_GETC(s, api->safi);
+ if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
+ stream_get(&(api->rmac), s, sizeof(struct ethaddr));
/* Prefix. */
STREAM_GETC(s, api->prefix.family);
return -1;
}
+ if (api->nexthop_num)
+ STREAM_GETW(s, api->nh_vrf_id);
+
for (i = 0; i < api->nexthop_num; i++) {
api_nh = &api->nexthops[i];
{
unsigned int ifindex;
struct interface *ifp;
- vrf_id_t new_id = VRF_DEFAULT;
+ vrf_id_t new_id;
/* Get interface index. */
ifindex = stream_getl(s);
length = stream_getw(zclient->ibuf);
marker = stream_getc(zclient->ibuf);
version = stream_getc(zclient->ibuf);
- vrf_id = stream_getw(zclient->ibuf);
+ vrf_id = stream_getl(zclient->ibuf);
command = stream_getw(zclient->ibuf);
if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION) {
(*zclient->local_vni_del)(command, zclient, length,
vrf_id);
break;
+ case ZEBRA_L3VNI_ADD:
+ if (zclient->local_l3vni_add)
+ (*zclient->local_l3vni_add)(command, zclient, length,
+ vrf_id);
+ break;
+ case ZEBRA_L3VNI_DEL:
+ if (zclient->local_l3vni_del)
+ (*zclient->local_l3vni_del)(command, zclient, length,
+ vrf_id);
+ break;
case ZEBRA_MACIP_ADD:
if (zclient->local_macip_add)
(*zclient->local_macip_add)(command, zclient, length,
zclient_create_header(s, ZEBRA_INTERFACE_SET_MASTER, master->vrf_id);
- stream_putw(s, master->vrf_id);
+ stream_putl(s, master->vrf_id);
stream_putl(s, master->ifindex);
- stream_putw(s, slave->vrf_id);
+ stream_putl(s, slave->vrf_id);
stream_putl(s, slave->ifindex);
stream_putw_at(s, 0, stream_get_endp(s));
#define ZEBRA_MAX_PACKET_SIZ 4096
/* Zebra header size. */
-#define ZEBRA_HEADER_SIZE 8
+#define ZEBRA_HEADER_SIZE 10
/* special socket path name to use TCP
* @ is used as first character because that's abstract socket names on Linux
ZEBRA_ADVERTISE_ALL_VNI,
ZEBRA_VNI_ADD,
ZEBRA_VNI_DEL,
+ ZEBRA_L3VNI_ADD,
+ ZEBRA_L3VNI_DEL,
ZEBRA_REMOTE_VTEP_ADD,
ZEBRA_REMOTE_VTEP_DEL,
ZEBRA_MACIP_ADD,
int (*fec_update)(int, struct zclient *, uint16_t);
int (*local_vni_add)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t);
+ int (*local_l3vni_add)(int, struct zclient *, uint16_t, vrf_id_t);
+ int (*local_l3vni_del)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t);
int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t);
* always set to 255 in new zserv.
*/
uint8_t version;
-#define ZSERV_VERSION 4
+#define ZSERV_VERSION 5
vrf_id_t vrf_id;
uint16_t command;
};
u_int32_t mtu;
vrf_id_t vrf_id;
+ vrf_id_t nh_vrf_id;
+
+ struct ethaddr rmac;
};
/* Zebra IPv4 route message API. */
vrf_id_t *new_vrf_id);
extern void zebra_interface_if_set_value(struct stream *, struct interface *);
extern void zebra_router_id_update_read(struct stream *s, struct prefix *rid);
+
+#if CONFDATE > 20180823
+CPP_NOTICE("zapi_ipv4_route, zapi_ipv6_route, zapi_ipv4_route_ipv6_nexthop as well as the zapi_ipv4 and zapi_ipv6 data structures should be removed now");
+#endif
+
extern int zapi_ipv4_route(u_char, struct zclient *, struct prefix_ipv4 *,
struct zapi_ipv4 *) __attribute__((deprecated));
struct zapi_ipv6 *)
__attribute__((deprecated));
extern int zclient_route_send(u_char, struct zclient *, struct zapi_route *);
+extern int zclient_send_rnh(struct zclient *zclient, int command,
+ struct prefix *p, bool exact_match,
+ vrf_id_t vrf_id);
extern int zapi_route_encode(u_char, struct stream *, struct zapi_route *);
extern int zapi_route_decode(struct stream *, struct zapi_route *);
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
#define ZEBRA_FLAG_STATIC 0x40
#define ZEBRA_FLAG_SCOPE_LINK 0x100
#define ZEBRA_FLAG_FIB_OVERRIDE 0x200
+#define ZEBRA_FLAG_EVPN_ROUTE 0x400
/* ZEBRA_FLAG_BLACKHOLE was 0x04 */
/* ZEBRA_FLAG_REJECT was 0x80 */
typedef u_int16_t zebra_command_t;
/* VRF ID type. */
-typedef u_int16_t vrf_id_t;
+typedef uint32_t vrf_id_t;
typedef uint32_t route_tag_t;
#define ROUTE_TAG_MAX UINT32_MAX
memset(&api, 0, sizeof(api));
api.type = ZEBRA_ROUTE_NHRP;
api.safi = SAFI_UNICAST;
+ api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.prefix = *p;
switch (type) {
ospf6_route_copy_nexthops(route, abr_entry);
+
/* (7) If the routes are identical, copy the next hops over to existing
route. ospf6's route table implementation will otherwise string both
routes, but keep the older one as the best route since the routes
if (old && (ospf6_route_cmp(route, old) == 0)) {
ospf6_route_merge_nexthops(old, route);
+
+ if (is_debug)
+ zlog_debug("%s: Update route: %s nh count %u",
+ __PRETTY_FUNCTION__,
+ buf, listcount(route->nh_list));
+
/* Update RIB/FIB */
if (table->hook_add)
(*table->hook_add)(old);
ospf6_route_delete(route);
} else {
if (is_debug)
- zlog_debug("Install route: %s", buf);
+ zlog_debug("Install route: %s nh count %u",
+ buf, listcount(route->nh_list));
/* ospf6_ia_add_nw_route (table, &prefix, route); */
ospf6_route_add(route, table);
}
static void ospf6_area_route_hook_add(struct ospf6_route *route)
{
- struct ospf6_route *copy = ospf6_route_copy(route);
+ struct ospf6_route *copy;
+
+ copy = ospf6_route_copy(route);
ospf6_route_add(copy, ospf6->route_table);
}
oa->lsdb->hook_add = ospf6_area_lsdb_hook_add;
oa->lsdb->hook_remove = ospf6_area_lsdb_hook_remove;
oa->lsdb_self = ospf6_lsdb_create(oa);
+ oa->temp_router_lsa_lsdb = ospf6_lsdb_create(oa);
oa->spf_table = OSPF6_ROUTE_TABLE_CREATE(AREA, SPF_RESULTS);
oa->spf_table->scope = oa;
ospf6_lsdb_delete(oa->lsdb);
ospf6_lsdb_delete(oa->lsdb_self);
+ ospf6_lsdb_delete(oa->temp_router_lsa_lsdb);
ospf6_spf_table_finish(oa->spf_table);
ospf6_route_table_delete(oa->spf_table);
struct ospf6_lsdb *lsdb;
struct ospf6_lsdb *lsdb_self;
+ struct ospf6_lsdb *temp_router_lsa_lsdb;
struct ospf6_route_table *spf_table;
struct ospf6_route_table *route_table;
return ntohl(network_order);
}
+void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
+ struct ospf6_route *route)
+{
+ struct ospf6_route *old_route;
+ struct ospf6_path *ecmp_path, *o_path = NULL;
+ struct listnode *anode;
+ struct listnode *nnode, *rnode, *rnext;
+ struct ospf6_nexthop *nh, *rnh;
+ char buf[PREFIX2STR_BUFFER];
+ bool route_found = false;
+
+ 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) &&
+ (old_route->path.cost == route->path.cost) &&
+ (old_route->path.u.cost_e2 == route->path.u.cost_e2)) {
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ prefix2str(&old_route->prefix, buf,
+ sizeof(buf));
+ zlog_debug("%s: old route %s path cost %u [%u]",
+ __PRETTY_FUNCTION__, buf,
+ old_route->path.cost,
+ ospf6_route_is_same(old_route,
+ route));
+ }
+ route_found = true;
+ /* check if this path exists already in
+ * route->paths list, if so, replace nh_list
+ * from asbr_entry.
+ */
+ for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
+ o_path)) {
+ if ((o_path->origin.id == route->path.origin.id)
+ && (o_path->origin.adv_router ==
+ route->path.origin.adv_router))
+ break;
+ }
+ /* If path is not found in old_route paths's list,
+ * add a new path to route paths list and merge
+ * nexthops in route->path->nh_list.
+ * Otherwise replace existing path's nh_list.
+ */
+ if (o_path == NULL) {
+ ecmp_path = ospf6_path_dup(&route->path);
+
+ /* Add a nh_list to new ecmp path */
+ ospf6_copy_nexthops(ecmp_path->nh_list,
+ route->nh_list);
+ /* Merge nexthop to existing route's nh_list */
+ ospf6_route_merge_nexthops(old_route, route);
+
+ /* Update RIB/FIB */
+ if (ospf6->route_table->hook_add)
+ (*ospf6->route_table->hook_add)
+ (old_route);
+
+ /* Add the new path to route's path list */
+ listnode_add_sort(old_route->paths, ecmp_path);
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ prefix2str(&route->prefix, buf,
+ sizeof(buf));
+ zlog_debug("%s: route %s another path added with nh %u, Paths %u",
+ __PRETTY_FUNCTION__, buf,
+ listcount(ecmp_path->nh_list),
+ old_route->paths ?
+ listcount(old_route->paths)
+ : 0);
+ }
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(o_path->nh_list,
+ nnode, nh)) {
+ for (ALL_LIST_ELEMENTS(
+ old_route->nh_list,
+ rnode, rnext, rnh)) {
+ if (!ospf6_nexthop_is_same(rnh,
+ nh))
+ continue;
+
+ listnode_delete(
+ old_route->nh_list,
+ rnh);
+ ospf6_nexthop_delete(rnh);
+ }
+ }
+ list_delete_all_node(o_path->nh_list);
+ ospf6_copy_nexthops(o_path->nh_list,
+ route->nh_list);
+
+ /* Merge nexthop to existing route's nh_list */
+ ospf6_route_merge_nexthops(old_route,
+ route);
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ prefix2str(&route->prefix,
+ buf, sizeof(buf));
+ zlog_debug("%s: existing route %s with effective nh count %u",
+ __PRETTY_FUNCTION__, buf,
+ old_route->nh_list ?
+ listcount(old_route->nh_list)
+ : 0);
+ }
+
+ /* Update RIB/FIB */
+ if (ospf6->route_table->hook_add)
+ (*ospf6->route_table->hook_add)
+ (old_route);
+
+ }
+ /* Delete the new route its info added to existing
+ * route.
+ */
+ ospf6_route_delete(route);
+ break;
+ }
+ }
+
+ if (!route_found) {
+ /* Add new route to existing node in ospf6 route table. */
+ ospf6_route_add(route, ospf6->route_table);
+ }
+}
+
void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
{
struct ospf6_as_external_lsa *external;
struct prefix asbr_id;
- struct ospf6_route *asbr_entry, *route;
+ struct ospf6_route *asbr_entry, *route, *old;
+ struct ospf6_path *path;
char buf[PREFIX2STR_BUFFER];
external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
ospf6_route_copy_nexthops(route, asbr_entry);
+ path = ospf6_path_dup(&route->path);
+ ospf6_copy_nexthops(path->nh_list, asbr_entry->nh_list);
+ listnode_add_sort(route->paths, path);
+
+
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&route->prefix, buf, sizeof(buf));
- zlog_debug("AS-External route add: %s", buf);
+ zlog_debug("%s: AS-External %u route add %s cost %u(%u) nh %u",
+ __PRETTY_FUNCTION__,
+ (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1)
+ ? 1 : 2, buf, route->path.cost,
+ route->path.u.cost_e2,
+ listcount(route->nh_list));
+ }
+
+ old = ospf6_route_lookup(&route->prefix, ospf6->route_table);
+ if (!old) {
+ /* Add the new route to ospf6 instance route table. */
+ ospf6_route_add(route, ospf6->route_table);
+ } else {
+ /* RFC 2328 16.4 (6)
+ * ECMP: Keep new equal preference path in current
+ * route's path list, update zebra with new effective
+ * list along with addition of ECMP path.
+ */
+ ospf6_asbr_update_route_ecmp_path(old, route);
}
- ospf6_route_add(route, ospf6->route_table);
}
void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
nroute = ospf6_route_next(route);
if (route->type != OSPF6_DEST_TYPE_NETWORK)
continue;
- if (route->path.origin.type != lsa->header->type)
- continue;
- if (route->path.origin.id != lsa->header->id)
- continue;
- if (route->path.origin.adv_router != lsa->header->adv_router)
+
+ /* Route has multiple ECMP paths remove,
+ * matching path and update effective route's nh list.
+ */
+ if (listcount(route->paths) > 1) {
+ struct listnode *anode, *anext;
+ struct listnode *nnode, *rnode, *rnext;
+ struct ospf6_nexthop *nh, *rnh;
+ struct ospf6_path *o_path;
+ bool nh_updated = false;
+
+ /* Iterate all paths of route to find maching with LSA
+ * remove from route path list. If route->path is same,
+ * replace from paths list.
+ */
+ for (ALL_LIST_ELEMENTS(route->paths, anode, anext,
+ o_path)) {
+ if (o_path->origin.type != lsa->header->type)
+ continue;
+ if (o_path->origin.id != lsa->header->id)
+ continue;
+ if (o_path->origin.adv_router !=
+ lsa->header->adv_router)
+ continue;
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ prefix2str(&prefix, buf, sizeof(buf));
+ zlog_debug(
+ "%s: route %s path found with nh %u",
+ __PRETTY_FUNCTION__, buf,
+ listcount(o_path->nh_list));
+ }
+
+ /* Remove found path's nh_list from
+ * the route's nh_list.
+ */
+ for (ALL_LIST_ELEMENTS_RO(o_path->nh_list,
+ nnode, nh)) {
+ for (ALL_LIST_ELEMENTS(route->nh_list,
+ rnode, rnext, rnh)) {
+ if (!ospf6_nexthop_is_same(rnh,
+ nh))
+ continue;
+ listnode_delete(route->nh_list,
+ rnh);
+ ospf6_nexthop_delete(rnh);
+ }
+ }
+ /* Delete the path from route's path list */
+ listnode_delete(route->paths, o_path);
+ ospf6_path_free(o_path);
+ nh_updated = true;
+ }
+
+ if (nh_updated) {
+ /* Iterate all paths and merge nexthop,
+ * unlesss any of the nexthop similar to
+ * ones deleted as part of path deletion.
+ */
+
+ for (ALL_LIST_ELEMENTS(route->paths, anode,
+ anext, o_path)) {
+ ospf6_merge_nexthops(route->nh_list,
+ o_path->nh_list);
+ }
+
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ prefix2str(&route->prefix, buf,
+ sizeof(buf));
+ zlog_debug("%s: AS-External %u route %s update paths %u nh %u"
+ , __PRETTY_FUNCTION__,
+ (route->path.type ==
+ OSPF6_PATH_TYPE_EXTERNAL1)
+ ? 1 : 2, buf,
+ listcount(route->paths),
+ listcount(route->nh_list));
+ }
+
+ /* Update RIB/FIB w/ effective nh_list */
+ if (ospf6->route_table->hook_add)
+ (*ospf6->route_table->hook_add)(route);
+
+ /* route's path is similar to lsa header,
+ * replace route's path with route's
+ * paths list head.
+ */
+ if (route->path.origin.id == lsa->header->id &&
+ route->path.origin.adv_router ==
+ lsa->header->adv_router) {
+ struct ospf6_path *h_path;
+
+ h_path = (struct ospf6_path *)
+ listgetdata(listhead(route->paths));
+ route->path.origin.type =
+ h_path->origin.type;
+ route->path.origin.id =
+ h_path->origin.id;
+ route->path.origin.adv_router =
+ h_path->origin.adv_router;
+ }
+ }
continue;
+ } else {
+ if (route->path.origin.type != lsa->header->type)
+ continue;
+ if (route->path.origin.id != lsa->header->id)
+ continue;
+ if (route->path.origin.adv_router !=
+ lsa->header->adv_router)
+ continue;
+ }
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&route->prefix, buf, sizeof(buf));
- zlog_debug("AS-External route remove: %s", buf);
+ zlog_debug("%s: AS-External %u route remove %s cost %u(%u) nh %u",
+ __PRETTY_FUNCTION__,
+ route->path.type == OSPF6_PATH_TYPE_EXTERNAL1
+ ? 1 : 2, buf, route->path.cost,
+ route->path.u.cost_e2,
+ listcount(route->nh_list));
}
ospf6_route_remove(route, ospf6->route_table);
}
return;
for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
- if (ospf6->rmap[type].name)
+ if (ospf6->rmap[type].name) {
ospf6->rmap[type].map = route_map_lookup_by_name(
ospf6->rmap[type].name);
- else
+
+ if (mapname && ospf6->rmap[type].map &&
+ (strcmp(ospf6->rmap[type].name, mapname) == 0)) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("%s: route-map %s update, reset redist %s",
+ __PRETTY_FUNCTION__, mapname,
+ ZROUTE_NAME(type));
+
+ ospf6_zebra_no_redistribute(type);
+ ospf6_zebra_redistribute(type);
+ }
+ } else
ospf6->rmap[type].map = NULL;
}
}
extern int config_write_ospf6_debug_asbr(struct vty *vty);
extern void install_element_ospf6_debug_asbr(void);
+extern void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
+ struct ospf6_route *route);
#endif /* OSPF6_ASBR_H */
return CMD_SUCCESS;
}
+static int ospf6_interface_show_traffic(struct vty *vty,
+ uint32_t vrf_id,
+ struct interface *intf_ifp,
+ int display_once)
+{
+ struct interface *ifp;
+ struct vrf *vrf = NULL;
+ struct ospf6_interface *oi = NULL;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+
+ if (!display_once) {
+ vty_out(vty, "\n");
+ vty_out(vty, "%-12s%-17s%-17s%-17s%-17s%-17s\n",
+ "Interface", " HELLO", " DB-Desc", " LS-Req",
+ " LS-Update", " LS-Ack");
+ vty_out(vty, "%-10s%-18s%-18s%-17s%-17s%-17s\n", "",
+ " Rx/Tx", " Rx/Tx", " Rx/Tx", " Rx/Tx", " Rx/Tx");
+ vty_out(vty,
+ "--------------------------------------------------------------------------------------------\n");
+ }
+
+ if (intf_ifp == NULL) {
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ if (ifp->info)
+ oi = (struct ospf6_interface *)ifp->info;
+ else
+ continue;
+
+ vty_out(vty,
+ "%-10s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u\n",
+ oi->interface->name, oi->hello_in,
+ oi->hello_out,
+ oi->db_desc_in, oi->db_desc_out,
+ oi->ls_req_in, oi->ls_req_out,
+ oi->ls_upd_in, oi->ls_upd_out,
+ oi->ls_ack_in, oi->ls_ack_out);
+ }
+ } else {
+ oi = intf_ifp->info;
+ if (oi == NULL)
+ return CMD_WARNING;
+
+ vty_out(vty,
+ "%-10s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u\n",
+ oi->interface->name, oi->hello_in,
+ oi->hello_out,
+ oi->db_desc_in, oi->db_desc_out,
+ oi->ls_req_in, oi->ls_req_out,
+ oi->ls_upd_in, oi->ls_upd_out,
+ oi->ls_ack_in, oi->ls_ack_out);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* show interface */
+DEFUN (show_ipv6_ospf6_interface_traffic,
+ show_ipv6_ospf6_interface_traffic_cmd,
+ "show ipv6 ospf6 interface traffic [IFNAME]",
+ SHOW_STR
+ IP6_STR
+ OSPF6_STR
+ INTERFACE_STR
+ "Protocol Packet counters\n"
+ IFNAME_STR)
+{
+ int idx_ifname = 0;
+ int display_once = 0;
+ char *intf_name = NULL;
+ struct interface *ifp = NULL;
+
+ if (argv_find(argv, argc, "IFNAME", &idx_ifname)) {
+ intf_name = argv[idx_ifname]->arg;
+ ifp = if_lookup_by_name(intf_name, VRF_DEFAULT);
+ if (ifp == NULL) {
+ vty_out(vty,
+ "No such Interface: %s\n",
+ intf_name);
+ return CMD_WARNING;
+ }
+ if (ifp->info == NULL) {
+ vty_out(vty,
+ " OSPF not enabled on this interface %s\n",
+ intf_name);
+ return 0;
+ }
+ }
+
+ ospf6_interface_show_traffic(vty, VRF_DEFAULT, ifp,
+ display_once);
+
+
+ return CMD_SUCCESS;
+}
+
+
DEFUN (show_ipv6_ospf6_interface_ifname_prefix,
show_ipv6_ospf6_interface_ifname_prefix_cmd,
"show ipv6 ospf6 interface IFNAME prefix [<X:X::X:X|X:X::X:X/M>] [<match|detail>]",
install_element(VIEW_NODE, &show_ipv6_ospf6_interface_ifname_cmd);
install_element(VIEW_NODE,
&show_ipv6_ospf6_interface_ifname_prefix_cmd);
+ install_element(VIEW_NODE,
+ &show_ipv6_ospf6_interface_traffic_cmd);
install_element(INTERFACE_NODE, &ipv6_ospf6_cost_cmd);
install_element(INTERFACE_NODE, &no_ipv6_ospf6_cost_cmd);
/* BFD information */
void *bfd_info;
+ /* Statistics Fields */
+ u_int32_t hello_in;
+ u_int32_t hello_out;
+ u_int32_t db_desc_in;
+ u_int32_t db_desc_out;
+ u_int32_t ls_req_in;
+ u_int32_t ls_req_out;
+ u_int32_t ls_upd_in;
+ u_int32_t ls_upd_out;
+ u_int32_t ls_ack_in;
+ u_int32_t ls_ack_out;
+ u_int32_t discarded;
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(ospf6_interface)
if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) {
prefix2str(&route->prefix, buf, sizeof(buf));
- zlog_debug(" route %s add", buf);
+ zlog_debug(" route %s add with nh count %u", buf,
+ listcount(route->nh_list));
}
ospf6_route_add(route, oa->route_table);
/* ospf6 age functions */
/* calculate birth */
-static void ospf6_lsa_age_set(struct ospf6_lsa *lsa)
+void ospf6_lsa_age_set(struct ospf6_lsa *lsa)
{
struct timeval now;
extern int config_write_ospf6_debug_lsa(struct vty *vty);
extern void install_element_ospf6_debug_lsa(void);
+extern void ospf6_lsa_age_set(struct ospf6_lsa *lsa);
#endif /* OSPF6_LSA_H */
DEFINE_MTYPE(OSPF6D, OSPF6_SPFTREE, "OSPF6 SPF tree")
DEFINE_MTYPE(OSPF6D, OSPF6_NEXTHOP, "OSPF6 nexthop")
DEFINE_MTYPE(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info")
+DEFINE_MTYPE(OSPF6D, OSPF6_PATH, "OSPF6 Path")
DEFINE_MTYPE(OSPF6D, OSPF6_OTHER, "OSPF6 other")
DECLARE_MTYPE(OSPF6_SPFTREE)
DECLARE_MTYPE(OSPF6_NEXTHOP)
DECLARE_MTYPE(OSPF6_EXTERNAL_INFO)
+DECLARE_MTYPE(OSPF6_PATH)
DECLARE_MTYPE(OSPF6_OTHER)
#endif /* _QUAGGA_OSPF6_MEMORY_H */
backupseen++;
}
+ oi->hello_in++;
+
/* Execute neighbor events */
thread_execute(master, hello_received, on, 0);
if (twoway)
dbdesc->reserved2 = 0;
}
+ oi->db_desc_in++;
+
if (ntohl(oh->router_id) < ntohl(ospf6->router_id))
ospf6_dbdesc_recv_master(oh, on);
else if (ntohl(ospf6->router_id) < ntohl(oh->router_id))
return;
}
+ oi->ls_req_in++;
+
/* Process each request */
for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header));
p + sizeof(struct ospf6_lsreq_entry) <= OSPF6_MESSAGE_END(oh);
lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
+ sizeof(struct ospf6_header));
+ oi->ls_upd_in++;
+
/* Process LSAs */
for (p = (char *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
p < OSPF6_MESSAGE_END(oh)
return;
}
+ oi->ls_ack_in++;
+
for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header));
p + sizeof(struct ospf6_lsa_header) <= OSPF6_MESSAGE_END(oh);
p += sizeof(struct ospf6_lsa_header)) {
oh->type = OSPF6_MESSAGE_TYPE_HELLO;
oh->length = htons(p - sendbuf);
+ oi->hello_out++;
+
ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh);
return 0;
}
else
dst = &on->linklocal_addr;
+ on->ospf6_if->db_desc_out++;
+
ospf6_send(on->ospf6_if->linklocal_addr, dst, on->ospf6_if, oh);
return 0;
oh->type = OSPF6_MESSAGE_TYPE_LSREQ;
oh->length = htons(p - sendbuf);
+ on->ospf6_if->ls_req_out++;
+
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
ospf6_send(on->ospf6_if->linklocal_addr, &allspfrouters6,
on->ospf6_if, oh);
{
if (on) {
+ on->ospf6_if->ls_upd_out++;
+
if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) ||
(on->ospf6_if->state == OSPF6_INTERFACE_DR) ||
(on->ospf6_if->state == OSPF6_INTERFACE_BDR)) {
&on->linklocal_addr, on->ospf6_if, oh);
}
} else if (oi) {
+
+ oi->ls_upd_out++;
+
if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) ||
(oi->state == OSPF6_INTERFACE_DR) ||
(oi->state == OSPF6_INTERFACE_BDR)) {
lsupdate->lsa_number = htonl(lsa_cnt);
ospf6_send_lsupdate(NULL, oi, oh);
- zlog_debug("%s: LSUpdate length %d",
- __PRETTY_FUNCTION__, ntohs(oh->length));
+ if (IS_OSPF6_DEBUG_MESSAGE(
+ OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
+ zlog_debug("%s: LSUpdate length %d",
+ __PRETTY_FUNCTION__,
+ ntohs(oh->length));
memset(sendbuf, 0, iobuflen);
oh = (struct ospf6_header *)sendbuf;
oh->type = OSPF6_MESSAGE_TYPE_LSACK;
oh->length = htons(p - sendbuf);
+ on->ospf6_if->ls_ack_out++;
+
ospf6_send(on->ospf6_if->linklocal_addr,
&on->linklocal_addr,
on->ospf6_if, oh);
oh->type = OSPF6_MESSAGE_TYPE_LSACK;
oh->length = htons(p - sendbuf);
+ on->ospf6_if->ls_ack_out++;
+
ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr,
on->ospf6_if, oh);
}
if (ospf6_nexthop_is_set(nh)) {
nh_new = ospf6_nexthop_create();
ospf6_nexthop_copy(nh_new, nh);
- listnode_add(dst, nh_new);
+ listnode_add_sort(dst, nh_new);
}
}
}
if (!ospf6_route_find_nexthop(dst, nh)) {
nh_new = ospf6_nexthop_create();
ospf6_nexthop_copy(nh_new, nh);
- listnode_add(dst, nh_new);
+ listnode_add_sort(dst, nh_new);
}
}
}
{
struct listnode *anode, *bnode;
struct ospf6_nexthop *anh, *bnh;
+ bool identical = false;
if (a && b) {
if (listcount(a->nh_list) == listcount(b->nh_list)) {
for (ALL_LIST_ELEMENTS_RO(a->nh_list, anode, anh)) {
+ identical = false;
for (ALL_LIST_ELEMENTS_RO(b->nh_list, bnode,
- bnh))
- if (!ospf6_nexthop_is_same(anh, bnh))
- return (1);
+ bnh)) {
+ if (ospf6_nexthop_is_same(anh, bnh))
+ identical = true;
+ }
+ /* Currnet List A element not found List B
+ * Non-Identical lists return */
+ if (identical == false)
+ return 1;
}
- return (0);
+ return 0;
} else
- return (1);
+ return 1;
}
/* One of the routes doesn't exist ? */
return (1);
return (-1);
}
-static int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b)
+int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b)
{
- if ((a)->ifindex == (b)->ifindex &&
- IN6_ARE_ADDR_EQUAL(&(a)->address, &(b)->address))
+ if (a->ifindex < b->ifindex)
+ return -1;
+ else if (a->ifindex > b->ifindex)
return 1;
+ 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)
+{
+ if (a->origin.adv_router < b->origin.adv_router)
+ return -1;
+ else if (a->origin.adv_router > b->origin.adv_router)
+ return 1;
+ else
+ return 0;
+}
+
+void ospf6_path_free(struct ospf6_path *op)
+{
+ if (op->nh_list)
+ list_delete_and_null(&op->nh_list);
+ XFREE(MTYPE_OSPF6_PATH, op);
+}
+
+struct ospf6_path *ospf6_path_dup(struct ospf6_path *path)
+{
+ struct ospf6_path *new;
+
+ new = XCALLOC(MTYPE_OSPF6_PATH, sizeof(struct ospf6_path));
+ memcpy(new, path, sizeof(struct ospf6_path));
+ new->nh_list = list_new();
+ new->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp;
+ new->nh_list->del = (void (*) (void *))ospf6_nexthop_delete;
+
+ return new;
+}
+
struct ospf6_route *ospf6_route_create(void)
{
struct ospf6_route *route;
route->nh_list = list_new();
route->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp;
route->nh_list->del = (void (*) (void *))ospf6_nexthop_delete;
+ route->paths = list_new();
+ route->paths->cmp = (int (*)(void *, void *))ospf6_path_cmp;
+ route->paths->del = (void (*)(void *))ospf6_path_free;
return route;
}
if (route) {
if (route->nh_list)
list_delete_and_null(&route->nh_list);
+ if (route->paths)
+ list_delete_and_null(&route->paths);
XFREE(MTYPE_OSPF6_ROUTE, route);
}
}
for (target = ospf6_route_lookup(&route->prefix, table); target;
target = target->next) {
- if (ospf6_route_is_identical(target, route))
+ if (target->type == route->type &&
+ (memcmp(&target->prefix, &route->prefix,
+ sizeof(struct prefix)) == 0) &&
+ target->path.type == route->path.type &&
+ target->path.cost == route->path.cost &&
+ target->path.u.cost_e2 == route->path.u.cost_e2 &&
+ ospf6_route_cmp_nexthops(target, route) == 0)
return target;
}
return NULL;
vty_out(vty, "Metric: %d (%d)\n", route->path.cost,
route->path.u.cost_e2);
+ vty_out(vty, "Paths count: %u\n", route->paths->count);
vty_out(vty, "Nexthop count: %u\n", route->nh_list->count);
/* Nexthops */
vty_out(vty, "Nexthop:\n");
u_int32_t cost_config;
} u;
u_int32_t tag;
+
+ /* nh list for this path */
+ struct list *nh_list;
};
#define OSPF6_PATH_TYPE_NONE 0
/* path */
struct ospf6_path path;
+ /* List of Paths. */
+ struct list *paths;
+
/* nexthop */
struct list *nh_list;
};
int size);
extern struct ospf6_nexthop *ospf6_nexthop_create(void);
+extern int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b);
extern void ospf6_nexthop_delete(struct ospf6_nexthop *nh);
extern void ospf6_clear_nexthops(struct list *nh_list);
extern int ospf6_num_nexthops(struct list *nh_list);
extern void install_element_ospf6_debug_route(void);
extern void ospf6_route_init(void);
extern void ospf6_clean(void);
+extern void ospf6_path_free(struct ospf6_path *op);
+extern struct ospf6_path *ospf6_path_dup(struct ospf6_path *path);
#endif /* OSPF6_ROUTE_H */
if (len)
id = htonl(*offset);
offset += len;
- offsetlen -= len;
+ //offsetlen -= len; // Add back in if we need it again
if (exact) {
if (v->magic & OSPFv3WWASTABLE) {
len = (offsetlen < 1 ? 0 : 1);
if (len)
instid = *offset;
- offset += len;
- offsetlen -= len;
+ //offset += len; // Add back in if we ever start using again
+ //offsetlen -= len;
if (exact) {
oi = ospf6_interface_lookup_by_ifindex(ifindex);
len = (offsetlen < 1 ? 0 : 1);
if (len)
rtrid = htonl(*offset);
- offset += len;
- offsetlen -= len;
+ //offset += len; // Add back in if we ever start looking at data
+ //offsetlen -= len;
if (exact) {
oi = ospf6_interface_lookup_by_ifindex(ifindex);
v->options[2] = *(u_char *)(OSPF6_LSA_HEADER_END(lsa->header) + 3);
v->nh_list = list_new();
+ v->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp;
v->nh_list->del = (void (*) (void *))ospf6_nexthop_delete;
v->parent = NULL;
}
static struct ospf6_lsa *ospf6_lsdesc_lsa(caddr_t lsdesc,
- struct ospf6_vertex *v,
- uint32_t link_id)
+ struct ospf6_vertex *v)
{
- struct ospf6_lsa *lsa;
+ struct ospf6_lsa *lsa = NULL;
u_int16_t type = 0;
u_int32_t id = 0, adv_router = 0;
if (VERTEX_IS_TYPE(NETWORK, v)) {
type = htons(OSPF6_LSTYPE_ROUTER);
- id = link_id;
+ id = htonl(0);
adv_router = NETWORK_LSDESC_GET_NBR_ROUTERID(lsdesc);
} else {
if (ROUTER_LSDESC_IS_TYPE(POINTTOPOINT, lsdesc)) {
type = htons(OSPF6_LSTYPE_ROUTER);
- id = link_id;
+ id = htonl(0);
adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID(lsdesc);
} else if (ROUTER_LSDESC_IS_TYPE(TRANSIT_NETWORK, lsdesc)) {
type = htons(OSPF6_LSTYPE_NETWORK);
}
}
- lsa = ospf6_lsdb_lookup(type, id, adv_router, v->area->lsdb);
-
+ if (type == htons(OSPF6_LSTYPE_NETWORK))
+ lsa = ospf6_lsdb_lookup(type, id, adv_router, v->area->lsdb);
+ else
+ lsa = ospf6_create_single_router_lsa(v->area, v->area->lsdb,
+ adv_router);
if (IS_OSPF6_DEBUG_SPF(PROCESS)) {
char ibuf[16], abuf[16];
inet_ntop(AF_INET, &id, ibuf, sizeof(ibuf));
inet_ntop(AF_INET, &adv_router, abuf, sizeof(abuf));
if (lsa)
- zlog_debug(" Link to: %s , V %s id %u", lsa->name,
- v->name, link_id);
+ zlog_debug(" Link to: %s len %u, V %s", lsa->name,
+ ntohs(lsa->header->length), v->name);
else
- zlog_debug(" Link to: [%s Id:%s Adv:%s] No LSA , V %s id %u",
+ zlog_debug(" Link to: [%s Id:%s Adv:%s] No LSA , V %s",
ospf6_lstype_name(type), ibuf, abuf,
- v->name, link_id);
+ v->name);
}
return lsa;
struct ospf6_vertex *root, *v, *w;
int size;
caddr_t lsdesc;
- struct ospf6_lsa *lsa, *self_rtr_lsa = NULL, *rtr_lsa = NULL;
- const struct route_node *end = NULL;
+ struct ospf6_lsa *lsa;
struct in6_addr address;
- struct ospf6_lsdb *lsdb = NULL;
ospf6_spf_table_finish(result_table);
/* Install the calculating router itself as the root of the SPF tree */
/* construct root vertex */
- lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_ROUTER), htonl(0), router_id,
- oa->lsdb_self);
+ lsa = ospf6_create_single_router_lsa(oa, oa->lsdb_self, router_id);
if (lsa == NULL) {
if (IS_OSPF6_DEBUG_SPF(PROCESS))
zlog_debug("%s: No router LSA for area %s\n", __func__,
return;
}
- self_rtr_lsa = lsa;
-
/* initialize */
candidate_list = pqueue_create();
candidate_list->cmp = ospf6_vertex_cmp;
&& ospf6_router_is_stub_router(v->lsa)))
continue;
- if (VERTEX_IS_TYPE(ROUTER, v)) {
- /* First fetch root Router LSAs from lsdb_self */
- if (v->lsa == self_rtr_lsa)
- lsdb = oa->lsdb_self;
- else
- lsdb = v->area->lsdb;
-
- /* Iterating multiple ROUTER LSAs from same adv router
- * with different Link State ID */
- end = ospf6_lsdb_head(lsdb, 2,
- htons(OSPF6_LSTYPE_ROUTER),
- v->lsa->header->adv_router,
- &rtr_lsa);
- while (rtr_lsa) {
- if (IS_OSPF6_DEBUG_SPF(PROCESS))
- zlog_debug("%s: Next LSA %s to process"
- ,__PRETTY_FUNCTION__,
- rtr_lsa->name);
- size = sizeof(struct ospf6_router_lsdesc);
- /* For each LS description in the just-added vertex V's LSA */
- for (lsdesc = OSPF6_LSA_HEADER_END(
- rtr_lsa->header) + 4;
- lsdesc + size <= OSPF6_LSA_END(
- rtr_lsa->header);
- lsdesc += size) {
- lsa = ospf6_lsdesc_lsa(lsdesc, v,
- rtr_lsa->header->id);
- if (lsa == NULL)
- continue;
-
- if (OSPF6_LSA_IS_MAXAGE(lsa))
- continue;
-
- if (!ospf6_lsdesc_backlink(lsa,
- lsdesc, v))
- continue;
-
- w = ospf6_vertex_create(lsa);
- w->area = oa;
- w->parent = v;
- w->link_id = rtr_lsa->header->id;
-
- if (VERTEX_IS_TYPE(ROUTER, v)) {
- w->cost = v->cost
- + ROUTER_LSDESC_GET_METRIC(lsdesc);
- w->hops =
- v->hops
- + (VERTEX_IS_TYPE(NETWORK, w)
- ? 0 : 1);
- } else /* NETWORK */ {
- w->cost = v->cost;
- w->hops = v->hops + 1;
- }
-
- /* nexthop calculation */
- if (w->hops == 0)
- ospf6_add_nexthop(w->nh_list,
- ROUTER_LSDESC_GET_IFID(lsdesc)
- , NULL);
- else if (w->hops == 1 && v->hops == 0)
- ospf6_nexthop_calc(w, v, lsdesc);
- else {
- ospf6_copy_nexthops(w->nh_list,
- v->nh_list);
- }
-
- /* add new candidate to the candidate_list */
- if (IS_OSPF6_DEBUG_SPF(PROCESS))
- zlog_debug(
- " New candidate: %s hops %d cost %d",
- w->name, w->hops,
- w->cost);
- pqueue_enqueue(w, candidate_list);
- }
- /* Fetch next Link state ID Router LSA */
- rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
- }
- } else {
- /* For each LS description in the just-added vertex V's LSA */
- size = (VERTEX_IS_TYPE(ROUTER, v)
- ? sizeof(struct ospf6_router_lsdesc)
- : sizeof(struct ospf6_network_lsdesc));
- for (lsdesc = OSPF6_LSA_HEADER_END(v->lsa->header) + 4;
- lsdesc + size <= OSPF6_LSA_END(v->lsa->header);
- lsdesc += size) {
- lsa = ospf6_lsdesc_lsa(lsdesc, v, v->link_id);
- if (lsa == NULL)
- continue;
-
- if (OSPF6_LSA_IS_MAXAGE(lsa))
- continue;
-
- if (!ospf6_lsdesc_backlink(lsa, lsdesc, v))
- continue;
-
- w = ospf6_vertex_create(lsa);
- w->area = oa;
- w->parent = v;
- if (VERTEX_IS_TYPE(ROUTER, v)) {
- w->cost = v->cost
+ /* For each LS description in the just-added vertex V's LSA */
+ size = (VERTEX_IS_TYPE(ROUTER, v)
+ ? sizeof(struct ospf6_router_lsdesc)
+ : sizeof(struct ospf6_network_lsdesc));
+ for (lsdesc = OSPF6_LSA_HEADER_END(v->lsa->header) + 4;
+ lsdesc + size <= OSPF6_LSA_END(v->lsa->header);
+ lsdesc += size) {
+ lsa = ospf6_lsdesc_lsa(lsdesc, v);
+ if (lsa == NULL)
+ continue;
+
+ if (OSPF6_LSA_IS_MAXAGE(lsa))
+ continue;
+
+ if (!ospf6_lsdesc_backlink(lsa, lsdesc, v))
+ continue;
+
+ w = ospf6_vertex_create(lsa);
+ w->area = oa;
+ w->parent = v;
+ if (VERTEX_IS_TYPE(ROUTER, v)) {
+ w->cost = v->cost
+ ROUTER_LSDESC_GET_METRIC(lsdesc);
- w->hops =
- v->hops
- + (VERTEX_IS_TYPE(NETWORK, w) ?
- 0 : 1);
- } else /* NETWORK */ {
- w->cost = v->cost;
- w->hops = v->hops + 1;
- }
-
- /* nexthop calculation */
- if (w->hops == 0)
- ospf6_add_nexthop(w->nh_list,
+ w->hops =
+ v->hops
+ + (VERTEX_IS_TYPE(NETWORK, w) ? 0 : 1);
+ } else {
+ /* NETWORK */
+ w->cost = v->cost;
+ w->hops = v->hops + 1;
+ }
+
+ /* nexthop calculation */
+ if (w->hops == 0)
+ ospf6_add_nexthop(
+ w->nh_list,
ROUTER_LSDESC_GET_IFID(lsdesc), NULL);
- else if (w->hops == 1 && v->hops == 0)
- ospf6_nexthop_calc(w, v, lsdesc);
- else {
- ospf6_copy_nexthops(w->nh_list,
- v->nh_list);
- }
-
- /* add new candidate to the candidate_list */
- if (IS_OSPF6_DEBUG_SPF(PROCESS))
- zlog_debug(
+ else if (w->hops == 1 && v->hops == 0)
+ ospf6_nexthop_calc(w, v, lsdesc);
+ else
+ ospf6_copy_nexthops(w->nh_list, v->nh_list);
+
+
+ /* add new candidate to the candidate_list */
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug(
" New candidate: %s hops %d cost %d",
w->name, w->hops, w->cost);
- pqueue_enqueue(w, candidate_list);
- }
+ pqueue_enqueue(w, candidate_list);
}
}
+
pqueue_delete(candidate_list);
+ ospf6_remove_temp_router_lsa(oa);
+
oa->spf_calculation++;
}
install_element(OSPF6_NODE, &ospf6_timers_throttle_spf_cmd);
install_element(OSPF6_NODE, &no_ospf6_timers_throttle_spf_cmd);
}
+
+/* Create Aggregated Large Router-LSA from multiple Link-State IDs
+ * RFC 5340 A 4.3:
+ * When more than one router-LSA is received from a single router,
+ * the links are processed as if concatenated into a single LSA.*/
+struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area,
+ struct ospf6_lsdb *lsdb,
+ uint32_t adv_router)
+{
+ struct ospf6_lsa *lsa = NULL;
+ struct ospf6_lsa *rtr_lsa = NULL;
+ struct ospf6_lsa_header *lsa_header = NULL;
+ uint8_t *new_header = NULL;
+ const struct route_node *end = NULL;
+ uint16_t lsa_length, total_lsa_length = 0, num_lsa = 0;
+ u_int16_t type = 0;
+ char ifbuf[16];
+ uint32_t interface_id;
+ caddr_t lsd;
+
+ lsa_length = sizeof(struct ospf6_lsa_header) +
+ sizeof(struct ospf6_router_lsa);
+ total_lsa_length = lsa_length;
+ type = htons(OSPF6_LSTYPE_ROUTER);
+
+ /* First check Aggregated LSA formed earlier in Cache */
+ lsa = ospf6_lsdb_lookup(type, htonl(0), adv_router,
+ area->temp_router_lsa_lsdb);
+ if (lsa)
+ return lsa;
+
+ inet_ntop(AF_INET, &adv_router, ifbuf, sizeof(ifbuf));
+
+ /* Determine total LSA length from all link state ids */
+ end = ospf6_lsdb_head(lsdb, 2, type, adv_router, &rtr_lsa);
+ while (rtr_lsa) {
+ lsa = rtr_lsa;
+ if (OSPF6_LSA_IS_MAXAGE(rtr_lsa)) {
+ rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
+ continue;
+ }
+ lsa_header = (struct ospf6_lsa_header *) rtr_lsa->header;
+ total_lsa_length += (ntohs(lsa_header->length)
+ - lsa_length);
+ num_lsa++;
+ rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
+ }
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: adv_router %s num_lsa %u to convert.",
+ __PRETTY_FUNCTION__, ifbuf, num_lsa);
+ if (num_lsa == 1)
+ return lsa;
+
+ if (num_lsa == 0) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: adv_router %s not found in LSDB.",
+ __PRETTY_FUNCTION__, ifbuf);
+ return NULL;
+ }
+
+ /* Allocate memory for this LSA */
+ new_header = XMALLOC(MTYPE_OSPF6_LSA_HEADER, total_lsa_length);
+ if (!new_header)
+ return NULL;
+
+ /* LSA information structure */
+ lsa = (struct ospf6_lsa *)XCALLOC(MTYPE_OSPF6_LSA,
+ sizeof(struct ospf6_lsa));
+ if (!lsa) {
+ free(new_header);
+ return NULL;
+ }
+
+ lsa->header = (struct ospf6_lsa_header *)new_header;
+
+ lsa->lsdb = area->temp_router_lsa_lsdb;
+
+ /* Fill Larger LSA Payload */
+ end = ospf6_lsdb_head(lsdb, 2, type, adv_router, &rtr_lsa);
+ if (rtr_lsa) {
+ if (!OSPF6_LSA_IS_MAXAGE(rtr_lsa)) {
+ /* Append first Link State ID LSA */
+ lsa_header = (struct ospf6_lsa_header *)rtr_lsa->header;
+ memcpy(new_header, lsa_header,
+ ntohs(lsa_header->length));
+ /* Assign new lsa length as aggregated length. */
+ ((struct ospf6_lsa_header *)new_header)->length =
+ htons(total_lsa_length);
+ new_header += ntohs(lsa_header->length);
+ num_lsa--;
+ }
+ }
+
+ /* Print LSA Name */
+ ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name));
+
+ rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
+ while (rtr_lsa) {
+ if (OSPF6_LSA_IS_MAXAGE(rtr_lsa)) {
+ rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
+ continue;
+ }
+
+ if (IS_OSPF6_DEBUG_SPF(PROCESS)) {
+ lsd = OSPF6_LSA_HEADER_END(rtr_lsa->header) + 4;
+ interface_id = ROUTER_LSDESC_GET_IFID(lsd);
+ inet_ntop(AF_INET, &interface_id, ifbuf, sizeof(ifbuf));
+ zlog_debug("%s: Next Router LSA %s to aggreat with len %u interface_id %s",
+ __PRETTY_FUNCTION__, rtr_lsa->name,
+ ntohs(lsa_header->length), ifbuf);
+ }
+
+ /* Append Next Link State ID LSA */
+ lsa_header = (struct ospf6_lsa_header *) rtr_lsa->header;
+ memcpy(new_header, (OSPF6_LSA_HEADER_END(rtr_lsa->header) + 4),
+ (ntohs(lsa_header->length) - lsa_length));
+ new_header += (ntohs(lsa_header->length) - lsa_length);
+ num_lsa--;
+
+ rtr_lsa = ospf6_lsdb_next(end, rtr_lsa);
+ }
+
+ /* Calculate birth of this lsa */
+ ospf6_lsa_age_set(lsa);
+
+ /* Store Aggregated LSA into area temp lsdb */
+ ospf6_lsdb_add(lsa, area->temp_router_lsa_lsdb);
+
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s: LSA %s id %u type 0%x len %u num_lsa %u",
+ __PRETTY_FUNCTION__, lsa->name,
+ ntohl(lsa->header->id), ntohs(lsa->header->type),
+ ntohs(lsa->header->length), num_lsa);
+
+ return lsa;
+}
+
+void ospf6_remove_temp_router_lsa(struct ospf6_area *area)
+{
+ struct ospf6_lsa *lsa = NULL;
+
+ for (ALL_LSDB(area->temp_router_lsa_lsdb, lsa)) {
+ if (IS_OSPF6_DEBUG_SPF(PROCESS))
+ zlog_debug("%s Remove LSA %s lsa->lock %u lsdb count %u",
+ __PRETTY_FUNCTION__,
+ lsa->name, lsa->lock,
+ area->temp_router_lsa_lsdb->count);
+ ospf6_lsdb_remove(lsa, area->temp_router_lsa_lsdb);
+ }
+}
extern void install_element_ospf6_debug_spf(void);
extern void ospf6_spf_init(void);
extern void ospf6_spf_reason_string(unsigned int reason, char *buf, int size);
+extern struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area,
+ struct ospf6_lsdb *lsdb,
+ uint32_t adv_router);
+extern void ospf6_remove_temp_router_lsa(struct ospf6_area *area);
#endif /* OSPF6_SPF_H */
static void ospf6_top_brouter_hook_add(struct ospf6_route *route)
{
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ char buf[PREFIX2STR_BUFFER];
+
+ prefix2str(&route->prefix, buf, sizeof(buf));
+ zlog_debug("%s: brouter %s add with nh count %u",
+ __PRETTY_FUNCTION__, buf, listcount(route->nh_list));
+ }
ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix));
ospf6_asbr_lsentry_add(route);
ospf6_abr_originate_summary(route);
static void ospf6_top_brouter_hook_remove(struct ospf6_route *route)
{
+ if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
+ char buf[PREFIX2STR_BUFFER];
+
+ prefix2str(&route->prefix, buf, sizeof(buf));
+ zlog_debug("%s: brouter %s del with nh count %u",
+ __PRETTY_FUNCTION__, buf, listcount(route->nh_list));
+ }
route->flag |= OSPF6_ROUTE_REMOVE;
ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix));
ospf6_asbr_lsentry_remove(route);
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_OSPF6;
api.safi = SAFI_UNICAST;
api.prefix = *dest;
if (!CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) {
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_OSPF6;
api.safi = SAFI_UNICAST;
api.prefix = *dest;
if (CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) {
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_OSPF6;
api.safi = SAFI_UNICAST;
api.prefix = *dest;
return CMD_SUCCESS;
}
+DEFUN_HIDDEN (show_ipv6_ospf6_database_aggr_router,
+ show_ipv6_ospf6_database_aggr_router_cmd,
+ "show ipv6 ospf6 database aggr adv-router A.B.C.D",
+ SHOW_STR
+ IPV6_STR
+ OSPF6_STR
+ "Display Link state database\n"
+ "Aggregated Router LSA\n"
+ "Search by Advertising Router\n"
+ "Specify Advertising Router as IPv4 address notation\n")
+{
+ int level = OSPF6_LSDB_SHOW_LEVEL_DETAIL;
+ uint16_t type = htons(OSPF6_LSTYPE_ROUTER);
+ int idx_ipv4 = 6;
+ struct listnode *i;
+ struct ospf6 *o = ospf6;
+ struct ospf6_area *oa;
+ struct ospf6_lsdb *lsdb;
+ uint32_t adv_router = 0;
+
+ inet_pton(AF_INET, argv[idx_ipv4]->arg, &adv_router);
+
+ for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) {
+ if (adv_router == o->router_id)
+ lsdb = oa->lsdb_self;
+ else
+ lsdb = oa->lsdb;
+ if (ospf6_create_single_router_lsa(oa, lsdb,
+ adv_router) == NULL) {
+ vty_out(vty, "Adv router is not found in LSDB.");
+ return CMD_SUCCESS;
+ }
+ ospf6_lsdb_show(vty, level, &type, NULL, NULL,
+ oa->temp_router_lsa_lsdb);
+ /* Remove the temp cache */
+ ospf6_remove_temp_router_lsa(oa);
+ }
+
+ vty_out(vty, "\n");
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_ipv6_ospf6_database_type_id,
show_ipv6_ospf6_database_type_id_cmd,
"show ipv6 ospf6 database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> linkstate-id A.B.C.D [<detail|dump|internal>]",
install_element(
VIEW_NODE,
&show_ipv6_ospf6_database_type_self_originated_linkstate_id_cmd);
+ install_element(VIEW_NODE, &show_ipv6_ospf6_database_aggr_router_cmd);
/* Make ospf protocol socket. */
ospf6_serv_sock();
-$Id: OSPF-ALIGNMENT.txt,v 1.1 2004/11/17 17:59:52 gdt Exp $
-
Greg Troxel <gdt@ir.bbn.com>
2004-11-17
struct router_lsa *rlsa;
struct in_addr *best = NULL;
- LSDB_LOOP(ROUTER_LSDB(area), rn, lsa)
- {
+ LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) {
/* sanity checks */
if (!lsa || (lsa->data->type != OSPF_ROUTER_LSA)
|| IS_LSA_SELF(lsa))
inet_ntoa(area->area_id));
LSDB_LOOP(NSSA_LSDB(area), rn, lsa)
- ospf_abr_translate_nssa(area, lsa);
+ ospf_abr_translate_nssa(area, lsa);
}
if (IS_DEBUG_OSPF_NSSA)
and we would want to flush any residuals anyway */
LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa)
- if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) {
- UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED);
- if (IS_DEBUG_OSPF_NSSA)
- zlog_debug(
- "ospf_abr_unapprove_translates(): "
- "approved unset on link id %s",
- inet_ntoa(lsa->data->id));
- }
+ if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) {
+ UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED);
+ if (IS_DEBUG_OSPF_NSSA)
+ zlog_debug(
+ "ospf_abr_unapprove_translates(): "
+ "approved unset on link id %s",
+ inet_ntoa(lsa->data->id));
+ }
if (IS_DEBUG_OSPF_NSSA)
zlog_debug("ospf_abr_unapprove_translates(): Stop");
"considering area %s",
inet_ntoa(area->area_id));
LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa)
- if (ospf_lsa_is_self_originated(ospf, lsa)) {
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug(
- "ospf_abr_unapprove_summaries(): "
- "approved unset on summary link id %s",
- inet_ntoa(lsa->data->id));
- UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED);
- }
+ if (ospf_lsa_is_self_originated(ospf, lsa)) {
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug(
+ "ospf_abr_unapprove_summaries(): "
+ "approved unset on summary link id %s",
+ inet_ntoa(lsa->data->id));
+ UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED);
+ }
LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa)
- if (ospf_lsa_is_self_originated(ospf, lsa)) {
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug(
- "ospf_abr_unapprove_summaries(): "
- "approved unset on asbr-summary link id %s",
- inet_ntoa(lsa->data->id));
- UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED);
- }
+ if (ospf_lsa_is_self_originated(ospf, lsa)) {
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug(
+ "ospf_abr_unapprove_summaries(): "
+ "approved unset on asbr-summary link id %s",
+ inet_ntoa(lsa->data->id));
+ UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED);
+ }
}
if (IS_DEBUG_OSPF_EVENT)
zlog_debug("ospf_abr_remove_unapproved_translates(): Start");
LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa)
- ospf_abr_remove_unapproved_translates_apply(ospf, lsa);
+ ospf_abr_remove_unapproved_translates_apply(ospf, lsa);
if (IS_DEBUG_OSPF_NSSA)
zlog_debug("ospf_abr_remove_unapproved_translates(): Stop");
inet_ntoa(area->area_id));
LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa)
- if (ospf_lsa_is_self_originated(ospf, lsa))
- if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED))
- ospf_lsa_flush_area(lsa, area);
+ if (ospf_lsa_is_self_originated(ospf, lsa))
+ if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED))
+ ospf_lsa_flush_area(lsa, area);
LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa)
- if (ospf_lsa_is_self_originated(ospf, lsa))
- if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED))
- ospf_lsa_flush_area(lsa, area);
+ if (ospf_lsa_is_self_originated(ospf, lsa))
+ if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED))
+ ospf_lsa_flush_area(lsa, area);
}
if (IS_DEBUG_OSPF_EVENT)
/* Check msg type. */
if (mask & Power2[OSPF_ROUTER_LSA])
LSDB_LOOP(ROUTER_LSDB(area), rn, lsa)
- apiserver_sync_callback(lsa, (void *)¶m, seqnum);
+ apiserver_sync_callback(
+ lsa, (void *)¶m, seqnum);
if (mask & Power2[OSPF_NETWORK_LSA])
LSDB_LOOP(NETWORK_LSDB(area), rn, lsa)
- apiserver_sync_callback(lsa, (void *)¶m, seqnum);
+ apiserver_sync_callback(
+ lsa, (void *)¶m, seqnum);
if (mask & Power2[OSPF_SUMMARY_LSA])
LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa)
- apiserver_sync_callback(lsa, (void *)¶m, seqnum);
+ apiserver_sync_callback(
+ lsa, (void *)¶m, seqnum);
if (mask & Power2[OSPF_ASBR_SUMMARY_LSA])
LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa)
- apiserver_sync_callback(lsa, (void *)¶m, seqnum);
+ apiserver_sync_callback(
+ lsa, (void *)¶m, seqnum);
if (mask & Power2[OSPF_OPAQUE_LINK_LSA])
LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa)
- apiserver_sync_callback(lsa, (void *)¶m, seqnum);
+ apiserver_sync_callback(
+ lsa, (void *)¶m, seqnum);
if (mask & Power2[OSPF_OPAQUE_AREA_LSA])
LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa)
- apiserver_sync_callback(lsa, (void *)¶m, seqnum);
+ apiserver_sync_callback(
+ lsa, (void *)¶m, seqnum);
}
}
if (ospf->lsdb) {
if (mask & Power2[OSPF_AS_EXTERNAL_LSA])
LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa)
- apiserver_sync_callback(lsa, (void *)¶m, seqnum);
+ apiserver_sync_callback(lsa, (void *)¶m,
+ seqnum);
}
/* For AS-external opaque LSAs */
if (ospf->lsdb) {
if (mask & Power2[OSPF_OPAQUE_AS_LSA])
LSDB_LOOP(OPAQUE_AS_LSDB(ospf), rn, lsa)
- apiserver_sync_callback(lsa, (void *)¶m, seqnum);
+ apiserver_sync_callback(lsa, (void *)¶m,
+ seqnum);
}
/* Send a reply back to client with return code */
case OSPF_OPAQUE_LINK_LSA:
for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area))
LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa)
- apiserver_flush_opaque_type_callback(lsa, (void *)¶m, 0);
+ apiserver_flush_opaque_type_callback(
+ lsa, (void *)¶m, 0);
break;
case OSPF_OPAQUE_AREA_LSA:
for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area))
LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa)
- apiserver_flush_opaque_type_callback(lsa, (void *)¶m, 0);
+ apiserver_flush_opaque_type_callback(
+ lsa, (void *)¶m, 0);
break;
case OSPF_OPAQUE_AS_LSA:
LSDB_LOOP(OPAQUE_LINK_LSDB(ospf), rn, lsa)
- apiserver_flush_opaque_type_callback(lsa, (void *)¶m, 0);
+ apiserver_flush_opaque_type_callback(lsa,
+ (void *)¶m, 0);
break;
default:
break;
/* Calculate external route for each AS-external-LSA */
LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa)
- ospf_ase_calculate_route(ospf, lsa);
+ ospf_ase_calculate_route(ospf, lsa);
/* This version simple adds to the table all NSSA areas */
if (ospf->anyNSSA)
if (area->external_routing == OSPF_AREA_NSSA)
LSDB_LOOP(NSSA_LSDB(area), rn, lsa)
- ospf_ase_calculate_route(ospf, lsa);
+ ospf_ase_calculate_route(ospf,
+ lsa);
}
/* kevinm: And add the NSSA routes in ospf_top */
LSDB_LOOP(NSSA_LSDB(ospf), rn, lsa)
- ospf_ase_calculate_route(ospf, lsa);
+ ospf_ase_calculate_route(ospf, lsa);
/* Compare old and new external routing table and install the
difference info zebra/kernel */
monotime(&stop_time);
- zlog_info("SPF Processing Time(usecs): External Routes: %lld\n",
- (stop_time.tv_sec - start_time.tv_sec) * 1000000LL
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_info("SPF Processing Time(usecs): External Routes: %lld\n",
+ (stop_time.tv_sec - start_time.tv_sec) * 1000000LL
+ (stop_time.tv_usec - start_time.tv_usec));
}
return 0;
struct route_node *rn;
LSDB_LOOP(lsdb_rt, rn, lsa)
- process_summary_lsa(area, rt, rtrs, lsa);
+ process_summary_lsa(area, rt, rtrs, lsa);
}
int ospf_area_is_transit(struct ospf_area *area)
struct route_node *rn;
LSDB_LOOP(lsdb_rt, rn, lsa)
- process_transit_summary_lsa(area, rt, rtrs, lsa);
+ process_transit_summary_lsa(area, rt, rtrs, lsa);
}
void ospf_ia_routing(struct ospf *ospf, struct route_table *rt,
DEFINE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd))
DEFINE_HOOK(ospf_vl_delete, (struct ospf_vl_data * vd), (vd))
+int ospf_interface_neighbor_count(struct ospf_interface *oi)
+{
+ int count = 0;
+ struct route_node *rn;
+ struct ospf_neighbor *nbr = NULL;
+
+ for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) {
+ nbr = rn->info;
+ if (nbr) {
+ /* Do not show myself. */
+ if (nbr == oi->nbr_self)
+ continue;
+ /* Down state is not shown. */
+ if (nbr->state == NSM_Down)
+ continue;
+ count++;
+ }
+ }
+
+ return count;
+}
+
int ospf_if_get_output_cost(struct ospf_interface *oi)
{
/* If all else fails, use default OSPF cost */
extern struct crypt_key *ospf_crypt_key_new(void);
extern void ospf_crypt_key_add(struct list *, struct crypt_key *);
extern int ospf_crypt_key_delete(struct list *, u_char);
-
extern u_char ospf_default_iftype(struct interface *ifp);
+extern int ospf_interface_neighbor_count(struct ospf_interface *oi);
/* Set all multicast memberships appropriately based on the type and
state of the interface. */
if (area->external_routing != OSPF_AREA_NSSA && !type7)
continue;
- LSDB_LOOP(NSSA_LSDB(area), rn, lsa)
- {
+ LSDB_LOOP (NSSA_LSDB(area), rn, lsa) {
if (lsa->data->id.s_addr
== type5->data->id.s_addr) {
type7 = lsa;
for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) {
LSDB_LOOP(ROUTER_LSDB(area), rn, lsa)
- ospf_lsa_maxage_walker_remover(ospf, lsa);
+ ospf_lsa_maxage_walker_remover(ospf, lsa);
LSDB_LOOP(NETWORK_LSDB(area), rn, lsa)
- ospf_lsa_maxage_walker_remover(ospf, lsa);
+ ospf_lsa_maxage_walker_remover(ospf, lsa);
LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa)
- ospf_lsa_maxage_walker_remover(ospf, lsa);
+ ospf_lsa_maxage_walker_remover(ospf, lsa);
LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa)
- ospf_lsa_maxage_walker_remover(ospf, lsa);
+ ospf_lsa_maxage_walker_remover(ospf, lsa);
LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa)
- ospf_lsa_maxage_walker_remover(ospf, lsa);
+ ospf_lsa_maxage_walker_remover(ospf, lsa);
LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa)
- ospf_lsa_maxage_walker_remover(ospf, lsa);
+ ospf_lsa_maxage_walker_remover(ospf, lsa);
LSDB_LOOP(NSSA_LSDB(area), rn, lsa)
- ospf_lsa_maxage_walker_remover(ospf, lsa);
+ ospf_lsa_maxage_walker_remover(ospf, lsa);
}
/* for AS-external-LSAs. */
if (ospf->lsdb) {
LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa)
- ospf_lsa_maxage_walker_remover(ospf, lsa);
+ ospf_lsa_maxage_walker_remover(ospf, lsa);
LSDB_LOOP(OPAQUE_AS_LSDB(ospf), rn, lsa)
- ospf_lsa_maxage_walker_remover(ospf, lsa);
+ ospf_lsa_maxage_walker_remover(ospf, lsa);
}
OSPF_TIMER_ON(ospf->t_maxage_walker, ospf_lsa_maxage_walker,
}
LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa)
- ospf_lsa_flush_schedule(ospf, lsa);
+ ospf_lsa_flush_schedule(ospf, lsa);
LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa)
- ospf_lsa_flush_schedule(ospf, lsa);
+ ospf_lsa_flush_schedule(ospf, lsa);
LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa)
- ospf_lsa_flush_schedule(ospf, lsa);
+ ospf_lsa_flush_schedule(ospf, lsa);
LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa)
- ospf_lsa_flush_schedule(ospf, lsa);
+ ospf_lsa_flush_schedule(ospf, lsa);
}
if (need_to_flush_ase) {
LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa)
- ospf_lsa_flush_schedule(ospf, lsa);
+ ospf_lsa_flush_schedule(ospf, lsa);
LSDB_LOOP(OPAQUE_AS_LSDB(ospf), rn, lsa)
- ospf_lsa_flush_schedule(ospf, lsa);
+ ospf_lsa_flush_schedule(ospf, lsa);
}
/*
#include "ospfd/ospf_lsdb.h"
#include "ospfd/ospf_neighbor.h"
#include "ospfd/ospf_packet.h"
-
+#include "ospfd/ospf_dump.h"
/* Join to the OSPF ALL SPF ROUTERS multicast group. */
int ospf_if_add_allspfrouters(struct ospf *top, struct prefix *p,
"on # of multicast group memberships has been exceeded?",
top->fd, inet_ntoa(p->u.prefix4), ifindex,
safe_strerror(errno));
- else
- zlog_debug(
- "interface %s [%u] join AllSPFRouters Multicast group.",
- inet_ntoa(p->u.prefix4), ifindex);
+ else {
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("interface %s [%u] join AllSPFRouters Multicast group.",
+ inet_ntoa(p->u.prefix4), ifindex);
+ }
return ret;
}
"ifindex %u, AllSPFRouters): %s",
top->fd, inet_ntoa(p->u.prefix4), ifindex,
safe_strerror(errno));
- else
- zlog_debug(
- "interface %s [%u] leave AllSPFRouters Multicast group.",
- inet_ntoa(p->u.prefix4), ifindex);
+ else {
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("interface %s [%u] leave AllSPFRouters Multicast group.",
+ inet_ntoa(p->u.prefix4), ifindex);
+ }
return ret;
}
ospf_proactively_arp(nbr);
LSDB_LOOP(ROUTER_LSDB(area), rn, lsa)
- ospf_db_summary_add(nbr, lsa);
+ ospf_db_summary_add(nbr, lsa);
LSDB_LOOP(NETWORK_LSDB(area), rn, lsa)
- ospf_db_summary_add(nbr, lsa);
+ ospf_db_summary_add(nbr, lsa);
LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa)
- ospf_db_summary_add(nbr, lsa);
+ ospf_db_summary_add(nbr, lsa);
LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa)
- ospf_db_summary_add(nbr, lsa);
+ ospf_db_summary_add(nbr, lsa);
/* Process only if the neighbor is opaque capable. */
if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) {
LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa)
- ospf_db_summary_add(nbr, lsa);
+ ospf_db_summary_add(nbr, lsa);
LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa)
- ospf_db_summary_add(nbr, lsa);
+ ospf_db_summary_add(nbr, lsa);
}
if (CHECK_FLAG(nbr->options, OSPF_OPTION_NP)) {
LSDB_LOOP(NSSA_LSDB(area), rn, lsa)
- ospf_db_summary_add(nbr, lsa);
+ ospf_db_summary_add(nbr, lsa);
}
if (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK
&& area->external_routing == OSPF_AREA_DEFAULT)
LSDB_LOOP(EXTERNAL_LSDB(nbr->oi->ospf), rn, lsa)
- ospf_db_summary_add(nbr, lsa);
+ ospf_db_summary_add(nbr, lsa);
if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)
&& (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK
&& area->external_routing == OSPF_AREA_DEFAULT))
LSDB_LOOP(OPAQUE_AS_LSDB(nbr->oi->ospf), rn, lsa)
- ospf_db_summary_add(nbr, lsa);
+ ospf_db_summary_add(nbr, lsa);
return 0;
}
oi->ospf);
}
- zlog_info(
- "nsm_change_state(%s, %s -> %s): "
- "scheduling new router-LSA origination",
- inet_ntoa(nbr->router_id),
- lookup_msg(ospf_nsm_state_msg, old_state, NULL),
- lookup_msg(ospf_nsm_state_msg, state, NULL));
+ if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL))
+ zlog_info("%s:(%s, %s -> %s): "
+ "scheduling new router-LSA origination",
+ __PRETTY_FUNCTION__, inet_ntoa(nbr->router_id),
+ lookup_msg(ospf_nsm_state_msg, old_state, NULL),
+ lookup_msg(ospf_nsm_state_msg, state, NULL));
ospf_router_lsa_update_area(oi->area);
ospf_opaque_lsa_flush_schedule(lsa);
}
- /* Remove "oipt" from its owner's self-originated LSA list. */
- switch (oipt->lsa_type) {
- case OSPF_OPAQUE_LINK_LSA: {
- struct ospf_interface *oi =
- (struct ospf_interface *)(oipt->owner);
- listnode_delete(oi->opaque_lsa_self, oipt);
- break;
- }
- case OSPF_OPAQUE_AREA_LSA: {
- struct ospf_area *area = (struct ospf_area *)(oipt->owner);
- listnode_delete(area->opaque_lsa_self, oipt);
- break;
- }
- case OSPF_OPAQUE_AS_LSA: {
- struct ospf *top = (struct ospf *)(oipt->owner);
- listnode_delete(top->opaque_lsa_self, oipt);
- break;
- }
- default:
- zlog_warn("free_opaque_info_per_type: Unexpected LSA-type(%u)",
- oipt->lsa_type);
- break; /* This case may not exist. */
- }
-
OSPF_TIMER_OFF(oipt->t_opaque_lsa_self);
list_delete_and_null(&oipt->id_list);
XFREE(MTYPE_OPAQUE_INFO_PER_TYPE, oipt);
if (IPV4_ADDR_CMP(&nbr->router_id, &oi->ospf->router_id)
> 0) {
/* We're Slave---obey */
- zlog_info(
- "Packet[DD]: Neighbor %s Negotiation done (Slave).",
- inet_ntoa(nbr->router_id));
+ if (CHECK_FLAG(oi->ospf->config,
+ OSPF_LOG_ADJACENCY_DETAIL))
+ zlog_info("Packet[DD]: Neighbor %s Negotiation done (Slave).",
+ inet_ntoa(nbr->router_id));
+
nbr->dd_seqnum = ntohl(dd->dd_seqnum);
/* Reset I/MS */
} else {
/* We're Master, ignore the initial DBD from
* Slave */
- zlog_info(
- "Packet[DD]: Neighbor %s: Initial DBD from Slave, "
- "ignoring.",
- inet_ntoa(nbr->router_id));
+ if (CHECK_FLAG(oi->ospf->config,
+ OSPF_LOG_ADJACENCY_DETAIL))
+ zlog_info(
+ "Packet[DD]: Neighbor %s: Initial DBD from Slave, "
+ "ignoring.",
+ inet_ntoa(nbr->router_id));
break;
}
}
char msgbuf[16];
ospf_nbr_state_message(on, msgbuf, sizeof(msgbuf));
- zlog_info("ospfTrapNbrStateChange trap sent: %s now %s",
- inet_ntoa(on->address.u.prefix4), msgbuf);
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_info("%s: trap sent: %s now %s", __PRETTY_FUNCTION__,
+ inet_ntoa(on->address.u.prefix4), msgbuf);
oid_copy_addr(index, &(on->address.u.prefix4), IN_ADDR_SIZE);
index[IN_ADDR_SIZE] = 0;
{
oid index[sizeof(oid) * (IN_ADDR_SIZE + 1)];
- zlog_info("ospfTrapIfStateChange trap sent: %s now %s",
- inet_ntoa(oi->address->u.prefix4),
- lookup_msg(ospf_ism_state_msg, oi->state, NULL));
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_info("%s: trap sent: %s now %s", __PRETTY_FUNCTION__,
+ inet_ntoa(oi->address->u.prefix4),
+ lookup_msg(ospf_ism_state_msg, oi->state, NULL));
oid_copy_addr(index, &(oi->address->u.prefix4), IN_ADDR_SIZE);
index[IN_ADDR_SIZE] = 0;
}
if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("SPF: calculation timer delay = %ld", delay);
-
- zlog_info("SPF: Scheduled in %ld msec", delay);
+ zlog_debug("SPF: calculation timer delay = %ld msec", delay);
ospf->t_spf_calc = NULL;
thread_add_timer_msec(master, ospf_spf_calculate_timer, ospf, delay,
if (ospf->vrf_id != VRF_UNKNOWN)
ospf->oi_running = 1;
if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("Config command 'router ospf %d' received, vrf %s id %d oi_running %u",
+ zlog_debug("Config command 'router ospf %d' received, vrf %s id %u oi_running %u",
instance, ospf->name ? ospf->name : "NIL",
ospf->vrf_id, ospf->oi_running);
VTY_PUSH_CONTEXT(OSPF_NODE, ospf);
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx_ipv4 = 2;
- struct interface *ifp;
+ struct interface *ifp = NULL;
struct in_addr addr = {.s_addr = INADDR_ANY};
int ret;
struct ospf_if_params *params;
ospf_passive_interface_default(ospf, OSPF_IF_PASSIVE);
return CMD_SUCCESS;
}
+ if (ospf->vrf_id != VRF_UNKNOWN)
+ ifp = if_get_by_name(argv[1]->arg, ospf->vrf_id, 0);
- ifp = if_get_by_name(argv[1]->arg, ospf->vrf_id, 0);
if (ifp == NULL) {
vty_out(vty, "interface %s not found.\n",
(char *)argv[1]->arg);
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
}
params = IF_DEF_PARAMS(ifp);
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx_ipv4 = 3;
- struct interface *ifp;
+ struct interface *ifp = NULL;
struct in_addr addr = {.s_addr = INADDR_ANY};
struct ospf_if_params *params;
int ret;
return CMD_SUCCESS;
}
- ifp = if_get_by_name(argv[2]->arg, ospf->vrf_id, 0);
+ if (ospf->vrf_id != VRF_UNKNOWN)
+ ifp = if_get_by_name(argv[2]->arg, ospf->vrf_id, 0);
+
if (ifp == NULL) {
vty_out(vty, "interface %s not found.\n",
- (char *)argv[1]->arg);
- return CMD_WARNING;
+ (char *)argv[2]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
}
params = IF_DEF_PARAMS(ifp);
ret = ospf_network_set(ospf, &p, area_id, format);
if (ret == 0) {
vty_out(vty, "There is already same network statement.\n");
- return CMD_WARNING;
+ return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
"Use message-digest authentication\n")
{
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
- int idx_ipv4_number = 1;
+ int idx = 0;
struct ospf_area *area;
struct in_addr area_id;
int format;
- VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg);
+ argv_find(argv, argc, "area", &idx);
+ VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx + 1]->arg);
area = ospf_area_get(ospf, area_id);
ospf_area_display_format_set(ospf, area, format);
{
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
- json_object *json_vrf = NULL;
- json_object *json_interface_sub = NULL;
+ json_object *json_vrf = NULL, *json_intf_array = NULL;
+ json_object *json_interface_sub = NULL, *json_interface = NULL;
if (use_json) {
if (use_vrf)
json_vrf = json_object_new_object();
else
json_vrf = json;
+ json_intf_array = json_object_new_array();
}
if (ospf->instance) {
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
if (intf_name == NULL) {
+ if (use_json)
+ json_object_object_add(json_vrf, "interfaces",
+ json_intf_array);
/* Show All Interfaces.*/
FOR_ALL_INTERFACES (vrf, ifp) {
if (ospf_oi_count(ifp)) {
- if (use_json)
+ if (use_json) {
+ json_interface =
+ json_object_new_object();
json_interface_sub =
json_object_new_object();
-
+ }
show_ip_ospf_interface_sub(vty, ospf, ifp,
json_interface_sub,
use_json);
- if (use_json)
+ if (use_json) {
+ json_object_array_add(json_intf_array,
+ json_interface);
json_object_object_add(
- json_vrf, ifp->name,
+ json_interface, ifp->name,
json_interface_sub);
+ }
}
}
} else {
else
vty_out(vty, "No such interface name\n");
} else {
- if (use_json)
+ if (use_json) {
json_interface_sub = json_object_new_object();
+ json_interface = json_object_new_object();
+ json_object_object_add(json_vrf, "interfaces",
+ json_intf_array);
+ }
show_ip_ospf_interface_sub(
vty, ospf, ifp, json_interface_sub, use_json);
- if (use_json)
- json_object_object_add(json_vrf, ifp->name,
+ if (use_json) {
+ json_object_array_add(json_intf_array,
+ json_interface);
+ json_object_object_add(json_interface,
+ ifp->name,
json_interface_sub);
+ }
}
}
{
struct ospf_interface *oi;
struct listnode *node;
- json_object *json_vrf = NULL;
+ json_object *json_vrf = NULL, *json_nbr_array = NULL;
+ json_object *json_nbr_sub = NULL;
if (use_json) {
if (use_vrf)
json_vrf = json_object_new_object();
else
json_vrf = json;
+ json_nbr_array = json_object_new_array();
}
if (ospf->instance) {
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
if (!use_json)
show_ip_ospf_neighbour_header(vty);
+ else
+ json_object_object_add(json_vrf, "neighbors",
+ json_nbr_array);
- for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi))
- show_ip_ospf_neighbor_sub(vty, oi, json_vrf, use_json);
+ for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
+ if (ospf_interface_neighbor_count(oi) == 0)
+ continue;
+ if (use_json) {
+ json_nbr_sub = json_object_new_object();
+ json_object_array_add(json_nbr_array, json_nbr_sub);
+ }
+ show_ip_ospf_neighbor_sub(vty, oi, json_nbr_sub, use_json);
+ }
if (use_json) {
if (use_vrf) {
ospf_show_vrf_name(ospf, vty, json, use_vrf);
- /*ifp = if_lookup_by_name(argv[arg_base]->arg, ospf->vrf_id);*/
ifp = if_lookup_by_name_all_vrf(argv[arg_base]->arg);
if (!ifp) {
if (use_json)
show_database_header[type]);
LSDB_LOOP(AREA_LSDB(area, type), rn, lsa)
- show_lsa_summary(vty, lsa, self);
+ show_lsa_summary(vty, lsa, self);
vty_out(vty, "\n");
}
vty_out(vty, "%s\n", show_database_header[type]);
LSDB_LOOP(AS_LSDB(ospf, type), rn, lsa)
- show_lsa_summary(vty, lsa, self);
+ show_lsa_summary(vty, lsa, self);
vty_out(vty, "\n");
}
DEFUN (ospf_redistribute_source,
ospf_redistribute_source_cmd,
- "redistribute " FRR_REDIST_STR_OSPFD " [<metric (0-16777214)|metric-type (1-2)|route-map WORD>]",
+ "redistribute " FRR_REDIST_STR_OSPFD " [{metric (0-16777214)|metric-type (1-2)|route-map WORD}]",
REDIST_STR
FRR_REDIST_HELP_STR_OSPFD
"Metric for redistributed routes\n"
if (!str2metric(argv[idx]->arg, &metric))
return CMD_WARNING_CONFIG_FAILED;
}
+ idx = 1;
/* Get metric type. */
- else if (argv_find(argv, argc, "(1-2)", &idx)) {
+ if (argv_find(argv, argc, "(1-2)", &idx)) {
if (!str2metric_type(argv[idx]->arg, &type))
return CMD_WARNING_CONFIG_FAILED;
}
+ idx = 1;
/* Get route-map */
- else if (argv_find(argv, argc, "WORD", &idx)) {
+ if (argv_find(argv, argc, "WORD", &idx)) {
ospf_routemap_set(red, argv[idx]->arg);
} else
ospf_routemap_unset(red);
DEFUN (no_ospf_redistribute_source,
no_ospf_redistribute_source_cmd,
- "no redistribute " FRR_REDIST_STR_OSPFD " [<metric (0-16777214)|metric-type (1-2)|route-map WORD>]",
+ "no redistribute " FRR_REDIST_STR_OSPFD " [{metric (0-16777214)|metric-type (1-2)|route-map WORD}]",
NO_STR
REDIST_STR
FRR_REDIST_HELP_STR_OSPFD
/* Default information originate. */
DEFUN (ospf_default_information_originate,
ospf_default_information_originate_cmd,
- "default-information originate [<always|metric (0-16777214)|metric-type (1-2)|route-map WORD>]",
+ "default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map WORD}]",
"Control distribution of default information\n"
"Distribute a default route\n"
"Always advertise default route\n"
/* Check whether "always" was specified */
if (argv_find(argv, argc, "always", &idx))
default_originate = DEFAULT_ORIGINATE_ALWAYS;
+ idx = 1;
/* Get metric value */
- else if (argv_find(argv, argc, "(0-16777214)", &idx)) {
+ if (argv_find(argv, argc, "(0-16777214)", &idx)) {
if (!str2metric(argv[idx]->arg, &metric))
return CMD_WARNING_CONFIG_FAILED;
}
+ idx = 1;
/* Get metric type. */
- else if (argv_find(argv, argc, "(1-2)", &idx)) {
+ if (argv_find(argv, argc, "(1-2)", &idx)) {
if (!str2metric_type(argv[idx]->arg, &type))
return CMD_WARNING_CONFIG_FAILED;
}
+ idx = 1;
/* Get route-map */
- else if (argv_find(argv, argc, "WORD", &idx))
+ if (argv_find(argv, argc, "WORD", &idx))
ospf_routemap_set(red, argv[idx]->arg);
else
ospf_routemap_unset(red);
DEFUN (no_ospf_default_information_originate,
no_ospf_default_information_originate_cmd,
- "no default-information originate [<always|metric (0-16777214)|metric-type (1-2)|route-map WORD>]",
+ "no default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map WORD}]",
NO_STR
"Control distribution of default information\n"
"Distribute a default route\n"
}
if (uj) {
+ /* Keep Non-pretty format */
vty_out(vty, "%s\n",
- json_object_to_json_string_ext(json,
- JSON_C_TO_STRING_PRETTY));
+ json_object_to_json_string(json));
json_object_free(json);
}
if (ospf) {
ret = show_ip_ospf_route_common(vty, ospf, json, use_vrf);
+ /* Keep Non-pretty format */
if (uj)
- vty_out(vty, "%s\n", json_object_to_json_string_ext(
- json, JSON_C_TO_STRING_PRETTY));
+ vty_out(vty, "%s\n", json_object_to_json_string(json));
}
if (uj)
for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) {
json_object *json_vrf = NULL;
const char *name = NULL;
- int vrf_id_ui = 0;
+ int64_t vrf_id_ui = 0;
count++;
else
name = ospf->name;
- vrf_id_ui = (ospf->vrf_id == VRF_UNKNOWN) ? -1 : ospf->vrf_id;
+ vrf_id_ui = (ospf->vrf_id == VRF_UNKNOWN) ? -1 :
+ (int64_t) ospf->vrf_id;
if (uj) {
json_object_int_add(json_vrf, "vrfId", vrf_id_ui);
if (red->dmetric.value >= 0)
vty_out(vty, " metric %d",
red->dmetric.value);
+
if (red->dmetric.type == EXTERNAL_METRIC_TYPE_1)
vty_out(vty, " metric-type 1");
+ else if (red->dmetric.type ==
+ EXTERNAL_METRIC_TYPE_2)
+ vty_out(vty, " metric-type 2");
if (ROUTEMAP_NAME(red))
vty_out(vty, " route-map %s",
vty_out(vty, \
"%% You can't configure %s to backbone\n", \
NAME); \
+ return CMD_WARNING; \
} \
}
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
+ api.nh_vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
api.instance = ospf->instance;
api.safi = SAFI_UNICAST;
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
+ api.nh_vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
api.instance = ospf->instance;
api.safi = SAFI_UNICAST;
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
+ api.nh_vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
api.instance = ospf->instance;
api.safi = SAFI_UNICAST;
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
+ api.nh_vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
api.instance = ospf->instance;
api.safi = SAFI_UNICAST;
#include "log.h"
#include "sockunion.h" /* for inet_aton () */
#include "zclient.h"
+#include "routemap.h"
#include "plist.h"
#include "sockopt.h"
#include "bfd.h"
struct ospf_lsa *lsa;
LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa)
- if (IS_LSA_SELF(lsa))
- ospf_lsa_flush_schedule(ospf, lsa);
+ if (IS_LSA_SELF(lsa))
+ ospf_lsa_flush_schedule(ospf, lsa);
}
ospf->router_id = router_id;
struct route_node *rn;
struct ospf_lsa *lsa;
- LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa)
- {
+ LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) {
/* AdvRouter and Router ID is the same. */
if (IPV4_ADDR_SAME(&lsa->data->adv_router,
&ospf->router_id)) {
new->name = XSTRDUP(MTYPE_OSPF_TOP, name);
vrf = vrf_lookup_by_name(new->name);
if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("%s: Create new ospf instance with vrf_name %s vrf_id %d",
+ zlog_debug("%s: Create new ospf instance with vrf_name %s vrf_id %u",
__PRETTY_FUNCTION__, name, new->vrf_id);
if (vrf)
ospf_vrf_link(new, vrf);
for (ALL_LIST_ELEMENTS(om->ospf, node, nnode, ospf))
ospf_finish(ospf);
+ /* Cleanup route maps */
+ route_map_add_hook(NULL);
+ route_map_delete_hook(NULL);
+ route_map_event_hook(NULL);
+ route_map_finish();
+
+ /* reverse prefix_list_init */
+ prefix_list_add_hook(NULL);
+ prefix_list_delete_hook(NULL);
+ prefix_list_reset();
+
+ /* Cleanup vrf info */
+ ospf_vrf_terminate();
+
/* Deliberately go back up, hopefully to thread scheduler, as
* One or more ospf_finish()'s may have deferred shutdown to a timer
* thread
stream_free(ospf->ibuf);
LSDB_LOOP(OPAQUE_AS_LSDB(ospf), rn, lsa)
- ospf_discard_from_db(ospf, ospf->lsdb, lsa);
+ ospf_discard_from_db(ospf, ospf->lsdb, lsa);
LSDB_LOOP(EXTERNAL_LSDB(ospf), rn, lsa)
- ospf_discard_from_db(ospf, ospf->lsdb, lsa);
+ ospf_discard_from_db(ospf, ospf->lsdb, lsa);
ospf_lsdb_delete_all(ospf->lsdb);
ospf_lsdb_free(ospf->lsdb);
/* Free LSDBs. */
LSDB_LOOP(ROUTER_LSDB(area), rn, lsa)
- ospf_discard_from_db(area->ospf, area->lsdb, lsa);
+ ospf_discard_from_db(area->ospf, area->lsdb, lsa);
LSDB_LOOP(NETWORK_LSDB(area), rn, lsa)
- ospf_discard_from_db(area->ospf, area->lsdb, lsa);
+ ospf_discard_from_db(area->ospf, area->lsdb, lsa);
LSDB_LOOP(SUMMARY_LSDB(area), rn, lsa)
- ospf_discard_from_db(area->ospf, area->lsdb, lsa);
+ ospf_discard_from_db(area->ospf, area->lsdb, lsa);
LSDB_LOOP(ASBR_SUMMARY_LSDB(area), rn, lsa)
- ospf_discard_from_db(area->ospf, area->lsdb, lsa);
+ ospf_discard_from_db(area->ospf, area->lsdb, lsa);
LSDB_LOOP(NSSA_LSDB(area), rn, lsa)
- ospf_discard_from_db(area->ospf, area->lsdb, lsa);
+ ospf_discard_from_db(area->ospf, area->lsdb, lsa);
LSDB_LOOP(OPAQUE_AREA_LSDB(area), rn, lsa)
- ospf_discard_from_db(area->ospf, area->lsdb, lsa);
+ ospf_discard_from_db(area->ospf, area->lsdb, lsa);
LSDB_LOOP(OPAQUE_LINK_LSDB(area), rn, lsa)
- ospf_discard_from_db(area->ospf, area->lsdb, lsa);
+ ospf_discard_from_db(area->ospf, area->lsdb, lsa);
- ospf_opaque_type10_lsa_term(area);
ospf_lsdb_delete_all(area->lsdb);
ospf_lsdb_free(area->lsdb);
rn = route_node_get(ospf->networks, (struct prefix *)p);
if (rn->info) {
- /* There is already same network statement. */
+ network = rn->info;
route_unlock_node(rn);
- return 0;
+
+ if (IPV4_ADDR_SAME(&area_id, &network->area_id)) {
+ return 1;
+ } else {
+ /* There is already same network statement. */
+ return 0;
+ }
}
rn->info = network = ospf_network_new(area_id);
static int ospf_vrf_new(struct vrf *vrf)
{
if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("%s: VRF Created: %s(%d)", __PRETTY_FUNCTION__,
+ zlog_debug("%s: VRF Created: %s(%u)", __PRETTY_FUNCTION__,
vrf->name, vrf->vrf_id);
return 0;
static int ospf_vrf_delete(struct vrf *vrf)
{
if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("%s: VRF Deletion: %s(%d)", __PRETTY_FUNCTION__,
+ zlog_debug("%s: VRF Deletion: %s(%u)", __PRETTY_FUNCTION__,
vrf->name, vrf->vrf_id);
return 0;
vrf_id_t old_vrf_id = VRF_DEFAULT;
if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("%s: VRF %s id %d enabled",
+ zlog_debug("%s: VRF %s id %u enabled",
__PRETTY_FUNCTION__, vrf->name, vrf->vrf_id);
ospf = ospf_lookup_by_name(vrf->name);
/* We have instance configured, link to VRF and make it "up". */
ospf_vrf_link(ospf, vrf);
if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("%s: ospf linked to vrf %s vrf_id %d (old id %d)",
+ zlog_debug("%s: ospf linked to vrf %s vrf_id %u (old id %u)",
__PRETTY_FUNCTION__, vrf->name, ospf->vrf_id,
old_vrf_id);
{
int result;
struct in_addr source_addr;
+ int ret = CMD_SUCCESS;
VTY_DECLVAR_CONTEXT(interface, ifp);
result = inet_pton(AF_INET, source, &source_addr);
case PIM_SUCCESS:
break;
case PIM_IFACE_NOT_FOUND:
+ ret = CMD_WARNING_CONFIG_FAILED;
vty_out(vty, "Pim not enabled on this interface\n");
break;
case PIM_UPDATE_SOURCE_DUP:
+ ret = CMD_WARNING;
vty_out(vty, "%% Source already set to %s\n", source);
break;
default:
+ ret = CMD_WARNING_CONFIG_FAILED;
vty_out(vty, "%% Source set failed\n");
}
- return result ? CMD_WARNING_CONFIG_FAILED : CMD_SUCCESS;
+ return ret;
}
DEFUN (interface_pim_use_source,
enum pim_msdp_err result;
struct in_addr peer_addr;
struct in_addr local_addr;
+ int ret = CMD_SUCCESS;
result = inet_pton(AF_INET, peer, &peer_addr);
if (result <= 0) {
case PIM_MSDP_ERR_NONE:
break;
case PIM_MSDP_ERR_OOM:
+ ret = CMD_WARNING_CONFIG_FAILED;
vty_out(vty, "%% Out of memory\n");
break;
case PIM_MSDP_ERR_PEER_EXISTS:
+ ret = CMD_WARNING;
vty_out(vty, "%% Peer exists\n");
break;
case PIM_MSDP_ERR_MAX_MESH_GROUPS:
+ ret = CMD_WARNING_CONFIG_FAILED;
vty_out(vty, "%% Only one mesh-group allowed currently\n");
break;
default:
+ ret = CMD_WARNING_CONFIG_FAILED;
vty_out(vty, "%% peer add failed\n");
}
- return result ? CMD_WARNING_CONFIG_FAILED : CMD_SUCCESS;
+ return ret;
}
DEFUN_HIDDEN (ip_msdp_peer,
{
enum pim_msdp_err result;
struct in_addr mbr_ip;
+ int ret = CMD_SUCCESS;
result = inet_pton(AF_INET, mbr, &mbr_ip);
if (result <= 0) {
case PIM_MSDP_ERR_NONE:
break;
case PIM_MSDP_ERR_OOM:
+ ret = CMD_WARNING_CONFIG_FAILED;
vty_out(vty, "%% Out of memory\n");
break;
case PIM_MSDP_ERR_MG_MBR_EXISTS:
+ ret = CMD_WARNING;
vty_out(vty, "%% mesh-group member exists\n");
break;
case PIM_MSDP_ERR_MAX_MESH_GROUPS:
+ ret = CMD_WARNING_CONFIG_FAILED;
vty_out(vty, "%% Only one mesh-group allowed currently\n");
break;
default:
+ ret = CMD_WARNING_CONFIG_FAILED;
vty_out(vty, "%% member add failed\n");
}
- return result ? CMD_WARNING_CONFIG_FAILED : CMD_SUCCESS;
+ return ret;
}
DEFUN (ip_msdp_mesh_group_member,
if (!pim_ifp->sec_addr_list) {
zlog_err("%s: failure: secondary addresslist",
__PRETTY_FUNCTION__);
+ return if_list_clean(pim_ifp);
}
pim_ifp->sec_addr_list->del = (void (*)(void *))pim_sec_addr_free;
pim_ifp->sec_addr_list->cmp =
ch = THREAD_ARG(t);
+ if (PIM_DEBUG_TRACE)
+ zlog_debug("%s: IFCHANNEL%s %s Prune Pending Timer Popped",
+ __PRETTY_FUNCTION__,
+ pim_str_sg_dump(&ch->sg),
+ pim_ifchannel_ifjoin_name(ch->ifjoin_state,
+ ch->flags));
+
if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) {
ifp = ch->interface;
pim_ifp = ifp->info;
* message on RP path upon prune timer expiry.
*/
ch->ifjoin_state = PIM_IFJOIN_PRUNE;
- if (ch->upstream)
+ if (ch->upstream) {
+ struct pim_upstream *parent =
+ ch->upstream->parent;
+
pim_upstream_update_join_desired(pim_ifp->pim,
ch->upstream);
+
+ pim_jp_agg_single_upstream_send(&parent->rpf,
+ parent,
+ true);
+ }
}
/* from here ch may have been deleted */
- } else {
- zlog_warn(
- "%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
- __PRETTY_FUNCTION__, pim_str_sg_dump(&ch->sg),
- pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags));
}
return 0;
{
struct pim_instance *pim = pim_instance_init(vrf);
- zlog_debug("VRF Created: %s(%d)", vrf->name, vrf->vrf_id);
+ zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id);
if (pim == NULL) {
zlog_err("%s %s: pim class init failure ", __FILE__,
__PRETTY_FUNCTION__);
{
struct pim_instance *pim = vrf->info;
- zlog_debug("VRF Deletion: %s(%d)", vrf->name, vrf->vrf_id);
+ zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id);
pim_ssmpingd_destroy(pim);
pim_instance_terminate(pim);
void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient,
struct pim_nexthop_cache *pnc, int command)
{
- struct stream *s;
struct prefix *p;
int ret;
- /* Check socket. */
- if (!zclient || zclient->sock < 0)
- return;
-
p = &(pnc->rpf.rpf_addr);
- s = zclient->obuf;
- stream_reset(s);
- zclient_create_header(s, command, pim->vrf_id);
- /* get update for all routes for a prefix */
- stream_putc(s, 0);
-
- stream_putw(s, PREFIX_FAMILY(p));
- stream_putc(s, p->prefixlen);
- switch (PREFIX_FAMILY(p)) {
- case AF_INET:
- stream_put_in_addr(s, &p->u.prefix4);
- break;
- case AF_INET6:
- stream_put(s, &(p->u.prefix6), 16);
- break;
- default:
- break;
- }
- stream_putw_at(s, 0, stream_get_endp(s));
-
- ret = zclient_send_message(zclient);
+ ret = zclient_send_rnh(zclient, command, p, false, pim->vrf_id);
if (ret < 0)
zlog_warn("sendmsg_nexthop: zclient_send_message() failed");
-
if (PIM_DEBUG_PIM_NHT) {
char buf[PREFIX2STR_BUFFER];
prefix2str(p, buf, sizeof(buf));
char buf[PREFIX2STR_BUFFER];
prefix2str(&p, buf, sizeof(buf));
zlog_debug(
- "%s: NHT Update for %s(%s) num_nh %d num_pim_nh %d vrf:%d up %ld rp %d",
+ "%s: NHT Update for %s(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d",
__PRETTY_FUNCTION__, buf, pim->vrf->name, nexthop_num,
pnc->nexthop_num, vrf_id, pnc->upstream_hash->count,
listcount(pnc->rp_list));
if (PIM_DEBUG_ZEBRA) {
zlog_debug(
- "%s: %s index %d(%d) flags %ld metric %d mtu %d operative %d",
+ "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d",
__PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id,
(long)ifp->flags, ifp->metric, ifp->mtu,
if_is_operative(ifp));
if (PIM_DEBUG_ZEBRA) {
zlog_debug(
- "%s: %s index %d(%d) flags %ld metric %d mtu %d operative %d",
+ "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d",
__PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id,
(long)ifp->flags, ifp->metric, ifp->mtu,
if_is_operative(ifp));
if (PIM_DEBUG_ZEBRA) {
zlog_debug(
- "%s: %s index %d(%d) flags %ld metric %d mtu %d operative %d",
+ "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d",
__PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id,
(long)ifp->flags, ifp->metric, ifp->mtu,
if_is_operative(ifp));
if (PIM_DEBUG_ZEBRA) {
zlog_debug(
- "%s: %s index %d(%d) flags %ld metric %d mtu %d operative %d",
+ "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d",
__PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id,
(long)ifp->flags, ifp->metric, ifp->mtu,
if_is_operative(ifp));
if (PIM_DEBUG_ZEBRA) {
char buf[BUFSIZ];
prefix2str(p, buf, BUFSIZ);
- zlog_debug("%s: %s(%d) connected IP address %s flags %u %s",
+ zlog_debug("%s: %s(%u) connected IP address %s flags %u %s",
__PRETTY_FUNCTION__, c->ifp->name, vrf_id, buf,
c->flags, CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)
? "secondary"
char buf[BUFSIZ];
prefix2str(p, buf, BUFSIZ);
zlog_debug(
- "%s: %s(%d) disconnected IP address %s flags %u %s",
+ "%s: %s(%u) disconnected IP address %s flags %u %s",
__PRETTY_FUNCTION__, c->ifp->name, vrf_id, buf,
c->flags,
CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)
zlog_info("zclient_init cleared redistribution request");
}
- zassert(zclient->redist_default == ZEBRA_ROUTE_PIM);
-
/* Request all redistribution */
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
if (i == zclient->redist_default)
-$Id: README.txt,v 1.1 2004/08/27 15:57:35 gdt Exp $
-
This directory contains files for use with the pkgsrc framework
(http://www.pkgsrc.org) used with NetBSD and other operating systems.
Eventually it will be hooked into automake such that they can be
%{!?with_multipath: %global with_multipath 256 }
%{!?frr_user: %global frr_user frr }
%{!?vty_group: %global vty_group frrvty }
-%{!?with_fpm: %global with_fpm 0 }
+%{!?with_fpm: %global with_fpm 1 }
%{!?with_watchfrr: %global with_watchfrr 1 }
%{!?with_bgp_vnc: %global with_bgp_vnc 0 }
%{!?with_pimd: %global with_pimd 1 }
%{_libdir}/lib*.so.0
%attr(755,root,root) %{_libdir}/lib*.so.0.*
%endif
+%if %{with_fpm}
+%attr(755,root,root) %{_libdir}/frr/modules/zebra_fpm.so
+%endif
%attr(755,root,root) %{_libdir}/frr/modules/zebra_irdp.so
%{_bindir}/*
%config(noreplace) /etc/frr/[!v]*.conf*
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_RIP;
api.safi = SAFI_UNICAST;
!
! RIPd sample configuration file
!
-! $Id: ripd.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $
-!
hostname ripd
password zebra
!
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_RIPNG;
api.safi = SAFI_UNICAST;
api.prefix = rp->p;
!
! RIPngd sample configuration file
!
-! $Id: ripngd.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $
-!
hostname ripngd
password zebra
!
--- /dev/null
+Makefile
+Makefile.in
+*.o
+tags
+TAGS
+.deps
+.nfs*
+*.lo
+*.la
+*.a
+*.libs
+.arch-inventory
+.arch-ids
+*~
+*.loT
+*clippy.c
+sharpd
+sharpd.conf
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_SHARP;
api.safi = SAFI_UNICAST;
memcpy(&api.prefix, p, sizeof(*p));
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
+ api.nh_vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_SHARP;
api.safi = SAFI_UNICAST;
memcpy(&api.prefix, p, sizeof(*p));
lib/test_memory \
lib/test_nexthop_iter \
lib/test_privs \
+ lib/test_ringbuf \
lib/test_srcdest_table \
lib/test_segv \
lib/test_sig \
lib_test_privs_SOURCES = lib/test_privs.c
lib_test_srcdest_table_SOURCES = lib/test_srcdest_table.c \
helpers/c/prng.c
+lib_test_ringbuf_SOURCES = lib/test_ringbuf.c
lib_test_segv_SOURCES = lib/test_segv.c
lib_test_sig_SOURCES = lib/test_sig.c
lib_test_stream_SOURCES = lib/test_stream.c
lib_test_memory_LDADD = $(ALL_TESTS_LDADD)
lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD)
lib_test_privs_LDADD = $(ALL_TESTS_LDADD)
+lib_test_ringbuf_LDADD = $(ALL_TESTS_LDADD)
lib_test_srcdest_table_LDADD = $(ALL_TESTS_LDADD)
lib_test_segv_LDADD = $(ALL_TESTS_LDADD)
lib_test_sig_LDADD = $(ALL_TESTS_LDADD)
lib/cli/test_cli.py \
lib/cli/test_cli.refout \
lib/test_nexthop_iter.py \
+ lib/test_ringbuf.py \
lib/test_srcdest_table.py \
lib/test_stream.py \
lib/test_stream.refout \
#include "privs.h"
#include "queue.h"
#include "filter.h"
+#include "frr_pthread.h"
-#include "bgpd/bgpd.h"
+#include "bgpd/bgpd.c"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_packet.h"
struct aspath *asp;
size_t datalen;
+ bgp_pthreads_init();
+ frr_pthread_get(PTHREAD_KEEPALIVES)->running = true;
+
asp = make_aspath(t->segment->asdata, t->segment->len, 0);
peer.curr = stream_new(BGP_MAX_PACKET_SIZE);
#include "memory.h"
#include "queue.h"
#include "filter.h"
+#include "frr_pthread.h"
-#include "bgpd/bgpd.h"
+#include "bgpd/bgpd.c"
#include "bgpd/bgp_open.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_packet.h"
vrf_init(NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN);
+ bgp_pthreads_init();
+ frr_pthread_get(PTHREAD_KEEPALIVES)->running = true;
+
if (fileno(stdout) >= 0)
tty = isatty(fileno(stdout));
/*
- * $Id: main.c,v 1.1 2005/04/25 16:42:24 paul Exp $
- *
* This file is part of Quagga.
*
* Quagga is free software; you can redistribute it and/or modify it
/*
- * $Id: heavy.c,v 1.3 2005/04/25 16:42:24 paul Exp $
- *
* This file is part of Quagga.
*
* Quagga is free software; you can redistribute it and/or modify it
/*
- * $Id: heavy-thread.c,v 1.2 2005/04/25 16:42:24 paul Exp $
- *
* This file is part of Quagga.
*
* Quagga is free software; you can redistribute it and/or modify it
/*
- * $Id: test-privs.c,v 1.1 2005/10/11 03:48:28 paul Exp $
- *
* This file is part of Quagga.
*
* Quagga is free software; you can redistribute it and/or modify it
--- /dev/null
+/*
+ * Circular buffer tests.
+ * Copyright (C) 2017 Cumulus Networks
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+#include <memory.h>
+#include "ringbuf.h"
+
+static void validate_state(struct ringbuf *buf, size_t size, size_t contains)
+{
+ assert(buf->size == size);
+ assert(ringbuf_remain(buf) == contains);
+ assert(ringbuf_space(buf) == buf->size - contains);
+ assert(buf->empty != (bool)contains);
+}
+
+int main(int argc, char **argv)
+{
+ struct ringbuf *soil = ringbuf_new(BUFSIZ);
+
+ validate_state(soil, BUFSIZ, 0);
+
+ /* verify reset functionality on clean buffer */
+ printf("Validating reset on empty buffer...\n");
+ ringbuf_reset(soil);
+
+ validate_state(soil, BUFSIZ, 0);
+
+ /* put one byte */
+ printf("Validating write...\n");
+ uint8_t walnut = 47;
+ assert(ringbuf_put(soil, &walnut, sizeof(walnut)) == 1);
+
+ validate_state(soil, BUFSIZ, 1);
+
+ /* validate read limitations */
+ printf("Validating read limits...\n");
+ uint8_t nuts[2];
+ assert(ringbuf_get(soil, &nuts, sizeof(nuts)) == 1);
+
+ /* reset */
+ printf("Validating reset on full buffer...\n");
+ ringbuf_reset(soil);
+ validate_state(soil, BUFSIZ, 0);
+
+ /* copy stack garbage to buffer */
+ printf("Validating big write...\n");
+ uint8_t compost[BUFSIZ];
+ assert(ringbuf_put(soil, &compost, sizeof(compost)) == BUFSIZ);
+
+ validate_state(soil, BUFSIZ, BUFSIZ);
+ assert(soil->start == 0);
+ assert(soil->end == 0);
+
+ /* read 15 bytes of garbage */
+ printf("Validating read...\n");
+ assert(ringbuf_get(soil, &compost, 15) == 15);
+
+ validate_state(soil, BUFSIZ, BUFSIZ - 15);
+ assert(soil->start == 15);
+ assert(soil->end == 0);
+
+ /* put another 10 bytes and validate wraparound */
+ printf("Validating wraparound...\n");
+ assert(ringbuf_put(soil, &compost[BUFSIZ/2], 10) == 10);
+
+ validate_state(soil, BUFSIZ, BUFSIZ - 15 + 10);
+ assert(soil->start == 15);
+ assert(soil->end == 10);
+
+ /* put another 15 bytes and validate state */
+ printf("Validating size limits...\n");
+ assert(ringbuf_put(soil, &compost, 15) == 5);
+ validate_state(soil, BUFSIZ, BUFSIZ);
+
+ /* read entire buffer */
+ printf("Validating big read...\n");
+ assert(ringbuf_get(soil, &compost, BUFSIZ) == BUFSIZ);
+
+ validate_state(soil, BUFSIZ, 0);
+ assert(soil->empty = true);
+ assert(soil->start == soil->end);
+ assert(soil->start == 15);
+
+ /* read empty buffer */
+ printf("Validating empty read...\n");
+ assert(ringbuf_get(soil, &compost, 1) == 0);
+ validate_state(soil, BUFSIZ, 0);
+
+ /* reset, validate state */
+ printf("Validating reset...\n");
+ ringbuf_reset(soil);
+ validate_state(soil, BUFSIZ, 0);
+ assert(soil->start == 0);
+ assert(soil->end == 0);
+
+ /* wipe, validate state */
+ printf("Validating wipe...\n");
+ memset(&compost, 0x00, sizeof(compost));
+ ringbuf_wipe(soil);
+ assert(memcmp(&compost, soil->data, sizeof(compost)) == 0);
+
+ /* validate maximum write */
+ printf("Validating very big write...\n");
+ const char flower[BUFSIZ * 2];
+ assert(ringbuf_put(soil, &flower, sizeof(flower)) == BUFSIZ);
+
+ validate_state(soil, BUFSIZ, BUFSIZ);
+
+ /* wipe, validate state */
+ printf("Validating wipe...\n");
+ memset(&compost, 0x00, sizeof(compost));
+ ringbuf_wipe(soil);
+ assert(memcmp(&compost, soil->data, sizeof(compost)) == 0);
+
+ /* validate simple data encode / decode */
+ const char *organ = "seed";
+ printf("Encoding: '%s'\n", organ);
+ assert(ringbuf_put(soil, organ, strlen(organ)) == 4);
+ char water[strlen(organ) + 1];
+ assert(ringbuf_get(soil, &water, strlen(organ)) == 4);
+ water[strlen(organ)] = '\0';
+ printf("Retrieved: '%s'\n", water);
+
+ validate_state(soil, BUFSIZ, 0);
+
+ /* validate simple data encode / decode across ring boundary */
+ soil->start = soil->size - 2;
+ soil->end = soil->start;
+ const char *phloem = "root";
+ printf("Encoding: '%s'\n", phloem);
+ assert(ringbuf_put(soil, phloem, strlen(phloem)) == 4);
+ char xylem[strlen(phloem) + 1];
+ assert(ringbuf_get(soil, &xylem, 100) == 4);
+ xylem[strlen(phloem)] = '\0';
+ printf("Retrieved: '%s'\n", xylem);
+
+ ringbuf_wipe(soil);
+
+ /* validate simple data peek across ring boundary */
+ soil->start = soil->size - 2;
+ soil->end = soil->start;
+ const char *cytoplasm = "tree";
+ printf("Encoding: '%s'\n", cytoplasm);
+ assert(ringbuf_put(soil, cytoplasm, strlen(cytoplasm)) == 4);
+ char chloroplast[strlen(cytoplasm) + 1];
+ assert(ringbuf_peek(soil, 2, &chloroplast[0], 100) == 2);
+ assert(ringbuf_peek(soil, 0, &chloroplast[2], 2) == 2);
+ chloroplast[strlen(cytoplasm)] = '\0';
+ assert(!strcmp(chloroplast, "eetr"));
+ printf("Retrieved: '%s'\n", chloroplast);
+
+ printf("Deleting...\n");
+ ringbuf_del(soil);
+
+ printf("Creating new buffer...\n");
+ soil = ringbuf_new(15);
+ soil->start = soil->end = 7;
+
+ /* validate data encode of excessive data */
+ const char *twenty = "vascular plants----";
+ char sixteen[16];
+ printf("Encoding: %s\n", twenty);
+ assert(ringbuf_put(soil, twenty, strlen(twenty)) == 15);
+ assert(ringbuf_get(soil, sixteen, 20));
+ sixteen[15] = '\0';
+ printf("Retrieved: %s\n", sixteen);
+ assert(!strcmp(sixteen, "vascular plants"));
+
+ printf("Deleting...\n");
+ ringbuf_del(soil);
+
+ printf("Done.\n");
+ return 0;
+}
--- /dev/null
+import frrtest
+
+class TestRingbuf(frrtest.TestExitNonzero):
+ program = './test_ringbuf'
#include "frr_zmq.h"
DEFINE_MTYPE_STATIC(LIB, TESTBUF, "zmq test buffer")
+DEFINE_MTYPE_STATIC(LIB, ZMQMSG, "zmq message")
static struct thread_master *master;
XFREE(MTYPE_TESTBUF, data);
}
+static int recv_delim(void *zmqsock)
+{
+ /* receive delim */
+ zmq_msg_t zdelim;
+ int more;
+ zmq_msg_init(&zdelim);
+ zmq_msg_recv(&zdelim, zmqsock, 0);
+ more = zmq_msg_more(&zdelim);
+ zmq_msg_close(&zdelim);
+ return more;
+}
+static void send_delim(void *zmqsock)
+{
+ /* Send delim */
+ zmq_msg_t zdelim;
+ zmq_msg_init(&zdelim);
+ zmq_msg_send(&zdelim, zmqsock, ZMQ_SNDMORE);
+ zmq_msg_close(&zdelim);
+}
static void run_client(int syncfd)
{
int i, j;
char dummy;
void *zmqctx = NULL;
void *zmqsock;
+ int more;
read(syncfd, &dummy, 1);
zmqctx = zmq_ctx_new();
zmq_ctx_set(zmqctx, ZMQ_IPV6, 1);
- zmqsock = zmq_socket(zmqctx, ZMQ_REQ);
+ zmqsock = zmq_socket(zmqctx, ZMQ_DEALER);
if (zmq_connect(zmqsock, "tcp://127.0.0.1:17171")) {
perror("zmq_connect");
exit(1);
/* single-part */
for (i = 0; i < 8; i++) {
- snprintf(buf, sizeof(buf), "msg #%d %c%c%c",
- i, 'a' + i, 'b' + i, 'c' + i);
+ snprintf(buf, sizeof(buf), "msg #%d %c%c%c", i, 'a' + i,
+ 'b' + i, 'c' + i);
printf("client send: %s\n", buf);
fflush(stdout);
- zmq_send(zmqsock, buf, strlen(buf) + 1, 0);
- zmq_recv(zmqsock, buf, sizeof(buf), 0);
- printf("client recv: %s\n", buf);
+ send_delim(zmqsock);
+ zmq_send(zmqsock, buf, strlen(buf) + 1, 0);
+ more = recv_delim(zmqsock);
+ while (more) {
+ zmq_recv(zmqsock, buf, sizeof(buf), 0);
+ printf("client recv: %s\n", buf);
+ size_t len = sizeof(more);
+ if (zmq_getsockopt(zmqsock, ZMQ_RCVMORE, &more, &len))
+ break;
+ }
}
/* multipart */
for (i = 2; i < 5; i++) {
- int more;
-
printf("---\n");
+ send_delim(zmqsock);
+ zmq_msg_t part;
for (j = 1; j <= i; j++) {
- zmq_msg_t part;
char *dyn = XMALLOC(MTYPE_TESTBUF, 32);
snprintf(dyn, 32, "part %d/%d", j, i);
zmq_msg_send(&part, zmqsock, j < i ? ZMQ_SNDMORE : 0);
}
- zmq_msg_t part;
+ recv_delim(zmqsock);
do {
char *data;
} while (more);
zmq_msg_close(&part);
}
+
+ /* write callback */
+ printf("---\n");
+ snprintf(buf, 32, "Done receiving");
+ printf("client send: %s\n", buf);
+ fflush(stdout);
+ send_delim(zmqsock);
+ zmq_send(zmqsock, buf, strlen(buf) + 1, 0);
+ /* wait for message from server */
+ more = recv_delim(zmqsock);
+ while (more) {
+ zmq_recv(zmqsock, buf, sizeof(buf), 0);
+ printf("client recv: %s\n", buf);
+ size_t len = sizeof(more);
+ if (zmq_getsockopt(zmqsock, ZMQ_RCVMORE, &more, &len))
+ break;
+ }
+
zmq_close(zmqsock);
zmq_ctx_term(zmqctx);
}
static struct frrzmq_cb *cb;
+static void recv_id_and_delim(void *zmqsock, zmq_msg_t *msg_id)
+{
+ /* receive id */
+ zmq_msg_init(msg_id);
+ zmq_msg_recv(msg_id, zmqsock, 0);
+ /* receive delim */
+ recv_delim(zmqsock);
+}
+static void send_id_and_delim(void *zmqsock, zmq_msg_t *msg_id)
+{
+ /* Send Id */
+ zmq_msg_send(msg_id, zmqsock, ZMQ_SNDMORE);
+ send_delim(zmqsock);
+}
+static void serverwritefn(void *arg, void *zmqsock)
+{
+ zmq_msg_t *msg_id = (zmq_msg_t *)arg;
+ char buf[32] = "Test write callback";
+ size_t i;
+
+ for (i = 0; i < strlen(buf); i++)
+ buf[i] = toupper(buf[i]);
+ printf("server send: %s\n", buf);
+ fflush(stdout);
+ send_id_and_delim(zmqsock, msg_id);
+ zmq_send(zmqsock, buf, strlen(buf) + 1, 0);
+
+ /* send just once */
+ frrzmq_thread_cancel(&cb, &cb->write);
+
+ zmq_msg_close(msg_id);
+ XFREE(MTYPE_ZMQMSG, msg_id);
+}
static void serverpartfn(void *arg, void *zmqsock, zmq_msg_t *msg,
- unsigned partnum)
+ unsigned partnum)
{
+ static int num = 0;
int more = zmq_msg_more(msg);
char *in = zmq_msg_data(msg);
size_t i;
zmq_msg_t reply;
char *out;
+ /* Id */
+ if (partnum == 0) {
+ send_id_and_delim(zmqsock, msg);
+ return;
+ }
+ /* Delim */
+ if (partnum == 1)
+ return;
+
+
printf("server recv part %u (more: %d): %s\n", partnum, more, in);
fflush(stdout);
- /* REQ-REP doesn't allow sending a reply here */
- if (more)
- return;
out = XMALLOC(MTYPE_TESTBUF, strlen(in) + 1);
for (i = 0; i < strlen(in); i++)
zmq_msg_init_data(&reply, out, strlen(out) + 1, msg_buf_free, NULL);
zmq_msg_send(&reply, zmqsock, ZMQ_SNDMORE);
+ if (more)
+ return;
+
out = XMALLOC(MTYPE_TESTBUF, 32);
snprintf(out, 32, "msg# was %u", partnum);
zmq_msg_init_data(&reply, out, strlen(out) + 1, msg_buf_free, NULL);
zmq_msg_send(&reply, zmqsock, 0);
+
+ zmq_msg_close(&reply);
+
+ if (++num < 7)
+ return;
+
+ /* write callback test */
+ char buf[32];
+ zmq_msg_t *msg_id = XMALLOC(MTYPE_ZMQMSG, sizeof(zmq_msg_t));
+ recv_id_and_delim(zmqsock, msg_id);
+ zmq_recv(zmqsock, buf, sizeof(buf), 0);
+ printf("server recv: %s\n", buf);
+ fflush(stdout);
+
+ frrzmq_thread_add_write_msg(master, serverwritefn, NULL, msg_id,
+ zmqsock, &cb);
}
static void serverfn(void *arg, void *zmqsock)
{
static int num = 0;
+ zmq_msg_t msg_id;
char buf[32];
size_t i;
+
+ recv_id_and_delim(zmqsock, &msg_id);
zmq_recv(zmqsock, buf, sizeof(buf), 0);
printf("server recv: %s\n", buf);
fflush(stdout);
for (i = 0; i < strlen(buf); i++)
buf[i] = toupper(buf[i]);
+ send_id_and_delim(zmqsock, &msg_id);
+ zmq_msg_close(&msg_id);
zmq_send(zmqsock, buf, strlen(buf) + 1, 0);
if (++num < 4)
return;
/* change to multipart callback */
- frrzmq_thread_cancel(cb);
+ frrzmq_thread_cancel(&cb, &cb->read);
+ frrzmq_thread_cancel(&cb, &cb->write);
- cb = frrzmq_thread_add_read_part(master, serverpartfn, NULL, zmqsock);
+ frrzmq_thread_add_read_part(master, serverpartfn, NULL, NULL, zmqsock,
+ &cb);
}
static void sigchld(void)
{
printf("child exited.\n");
- frrzmq_thread_cancel(cb);
+ frrzmq_thread_cancel(&cb, &cb->read);
+ frrzmq_thread_cancel(&cb, &cb->write);
}
static struct quagga_signal_t sigs[] = {
signal_init(master, array_size(sigs), sigs);
frrzmq_init();
- zmqsock = zmq_socket(frrzmq_context, ZMQ_REP);
+ zmqsock = zmq_socket(frrzmq_context, ZMQ_ROUTER);
if (zmq_bind(zmqsock, "tcp://*:17171")) {
perror("zmq_bind");
exit(1);
}
- cb = frrzmq_thread_add_read_msg(master, serverfn, NULL, zmqsock);
+ frrzmq_thread_add_read_msg(master, serverfn, NULL, NULL, zmqsock, &cb);
write(syncfd, &dummy, sizeof(dummy));
while (thread_fetch(master, &t))
server recv: msg #3 def
client recv: MSG #3 DEF
client send: msg #4 efg
-server recv part 0 (more: 0): msg #4 efg
+server recv part 2 (more: 0): msg #4 efg
client recv: MSG #4 EFG
+client recv: msg# was 2
client send: msg #5 fgh
-client recv: msg# was 0
+server recv part 2 (more: 0): msg #5 fgh
+client recv: MSG #5 FGH
+client recv: msg# was 2
client send: msg #6 ghi
-server recv part 0 (more: 0): msg #6 ghi
+server recv part 2 (more: 0): msg #6 ghi
client recv: MSG #6 GHI
+client recv: msg# was 2
client send: msg #7 hij
-client recv: msg# was 0
+server recv part 2 (more: 0): msg #7 hij
+client recv: MSG #7 HIJ
+client recv: msg# was 2
---
client send: part 1/2
client send: part 2/2
-server recv part 0 (more: 1): part 1/2
-server recv part 1 (more: 0): part 2/2
+server recv part 2 (more: 1): part 1/2
+server recv part 3 (more: 0): part 2/2
+client recv (more: 1): PART 1/2
client recv (more: 1): PART 2/2
-client recv (more: 0): msg# was 1
+client recv (more: 0): msg# was 3
---
client send: part 1/3
client send: part 2/3
client send: part 3/3
-server recv part 0 (more: 1): part 1/3
-server recv part 1 (more: 1): part 2/3
-server recv part 2 (more: 0): part 3/3
+server recv part 2 (more: 1): part 1/3
+server recv part 3 (more: 1): part 2/3
+server recv part 4 (more: 0): part 3/3
+client recv (more: 1): PART 1/3
+client recv (more: 1): PART 2/3
client recv (more: 1): PART 3/3
-client recv (more: 0): msg# was 2
+client recv (more: 0): msg# was 4
---
client send: part 1/4
client send: part 2/4
client send: part 3/4
client send: part 4/4
-server recv part 0 (more: 1): part 1/4
-server recv part 1 (more: 1): part 2/4
-server recv part 2 (more: 1): part 3/4
-server recv part 3 (more: 0): part 4/4
+server recv part 2 (more: 1): part 1/4
+server recv part 3 (more: 1): part 2/4
+server recv part 4 (more: 1): part 3/4
+server recv part 5 (more: 0): part 4/4
+client recv (more: 1): PART 1/4
+client recv (more: 1): PART 2/4
+client recv (more: 1): PART 3/4
client recv (more: 1): PART 4/4
-client recv (more: 0): msg# was 3
+client recv (more: 0): msg# was 5
+---
+client send: Done receiving
+server recv: Done receiving
+server send: TEST WRITE CALLBACK
+client recv: TEST WRITE CALLBACK
child exited.
# The list of daemons to watch is automatically generated by the init script.
watchfrr_enable=yes
-watchfrr_options=(-d -r /usr/sbin/servicebBfrrbBrestartbB%s -s /usr/sbin/servicebBfrrbBstartbB%s -k /usr/sbin/servicebBfrrbBstopbB%s -b bB -t 30)
+watchfrr_options=(-d -r /usr/sbin/servicebBfrrbBrestartbB%s -s /usr/sbin/servicebBfrrbBstartbB%s -k /usr/sbin/servicebBfrrbBstopbB%s -b bB)
# If valgrind_enable is 'yes' the frr daemons will be started via valgrind.
# The use case for doing so is tracking down memory leaks, etc in frr.
new_ctx = False
log.debug('LINE %-50s: entering new context, %-50s', line, ctx_keys)
- elif "vni " in line:
+ # The 'vni' keyword under 'router bgp X/address-family l2vpn evpn' creates
+ # a sub-context but the 'vni' keyword in other places (such as 'vrf BLUE')
+ # does not.
+ elif ("vni " in line and
+ len(ctx_keys) == 2 and
+ ctx_keys[0].startswith('router bgp') and
+ ctx_keys[1] == 'address-family l2vpn evpn'):
+
main_ctx_key = []
# Save old context first
DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CMD, "Vtysh cmd copy")
+/* Destination for vtysh output */
+FILE *outputfile;
+
/* Struct VTY. */
struct vty *vty;
bufvalid += nread;
- end = memmem(buf, bufvalid - buf, terminator,
- sizeof(terminator));
- if (end + sizeof(terminator) + 1 > bufvalid)
+ if (bufvalid - buf >= 4)
+ end = memmem(bufvalid - 4, 4, terminator,
+ sizeof(terminator));
+
+ if (end && end + sizeof(terminator) + 1 > bufvalid)
/* found \0\0\0 but return code hasn't been read yet */
end = NULL;
if (end)
ret = end[sizeof(terminator)];
- while (bufvalid > buf && (end > buf || !end)) {
- size_t textlen = (end ? end : bufvalid) - buf;
+ /*
+ * calculate # bytes we have, up to & not including the
+ * terminator if present
+ */
+ size_t textlen = (end ? end : bufvalid) - buf;
+
+ /* feed line processing callback if present */
+ while (callback && bufvalid > buf && (end > buf || !end)) {
+ textlen = (end ? end : bufvalid) - buf;
char *eol = memchr(buf, '\n', textlen);
if (eol)
/* line break */
/* continue reading */
break;
- /* eol is at a line end now, either \n => \0 or \0\0\0
- */
+ /* eol is at line end now, either \n => \0 or \0\0\0 */
assert(eol && eol <= bufvalid);
if (fp) {
end -= eol - buf;
}
+ /* else if no callback, dump raw */
+ if (!callback) {
+ if (fp)
+ fwrite(buf, 1, textlen, fp);
+ memmove(buf, buf + textlen, bufvalid - buf - textlen);
+ bufvalid -= textlen;
+ }
+
if (bufvalid == buf + bufsz) {
char *new;
bufsz *= 2;
fprintf(stdout, "%% Command incomplete.\n");
break;
case CMD_SUCCESS_DAEMON: {
- /* FIXME: Don't open pager for exit commands. popen() causes
- * problems
- * if exited from vtysh at all. This hack shouldn't cause any
- * problem
- * but is really ugly. */
- if (pager && vtysh_pager_name
+ /*
+ * FIXME: Don't open pager for exit commands. popen() causes
+ * problems if exited from vtysh at all. This hack shouldn't
+ * cause any problem but is really ugly.
+ */
+ fp = outputfile;
+ if (pager && vtysh_pager_name && outputfile == stdout
&& (strncmp(line, "exit", 4) != 0)) {
fp = popen(vtysh_pager_name, "w");
if (fp == NULL) {
perror("popen failed for pager");
- fp = stdout;
+ fp = outputfile;
} else
closepager = 1;
- } else
- fp = stdout;
+ }
if (!strcmp(cmd->string, "configure terminal")) {
for (i = 0; i < array_size(vtysh_client); i++) {
if (vline == NULL) {
if (pager && vtysh_pager_name && fp
- && closepager) {
+ && fp != outputfile && closepager) {
if (pclose(fp) == -1) {
perror("pclose failed for pager");
}
(*cmd->func)(cmd, vty, 0, NULL);
}
}
- if (pager && vtysh_pager_name && fp && closepager) {
+ if (pager && vtysh_pager_name && fp && closepager && fp != outputfile) {
if (pclose(fp) == -1) {
perror("pclose failed for pager");
}
switch (vty->node) {
case LDP_IPV4_IFACE_NODE:
if (strncmp(vty_buf_copy, " ", 3)) {
- fprintf(stdout, " end\n");
+ fprintf(outputfile, " end\n");
vty->node = LDP_IPV4_NODE;
}
break;
case LDP_IPV6_IFACE_NODE:
if (strncmp(vty_buf_copy, " ", 3)) {
- fprintf(stdout, " end\n");
+ fprintf(outputfile, " end\n");
vty->node = LDP_IPV6_NODE;
}
break;
case LDP_PSEUDOWIRE_NODE:
if (strncmp(vty_buf_copy, " ", 2)) {
- fprintf(stdout, " end\n");
+ fprintf(outputfile, " end\n");
vty->node = LDP_L2VPN_NODE;
}
break;
}
if (vty_buf_trimmed[0] == '!' || vty_buf_trimmed[0] == '#') {
- fprintf(stdout, "%s", vty->buf);
+ fprintf(outputfile, "%s", vty->buf);
continue;
}
vline = cmd_make_strvec(vty->buf);
if (vline == NULL) {
- fprintf(stdout, "%s", vty->buf);
+ fprintf(outputfile, "%s", vty->buf);
continue;
}
|| prev_node == BGP_IPV6M_NODE
|| prev_node == BGP_EVPN_NODE)
&& (tried == 1)) {
- fprintf(stdout, "exit-address-family\n");
+ fprintf(outputfile, "exit-address-family\n");
} else if ((prev_node == BGP_EVPN_VNI_NODE)
&& (tried == 1)) {
- fprintf(stdout, "exit-vni\n");
+ fprintf(outputfile, "exit-vni\n");
} else if ((prev_node == KEYCHAIN_KEY_NODE)
&& (tried == 1)) {
- fprintf(stdout, "exit\n");
+ fprintf(outputfile, "exit\n");
} else if (tried) {
- fprintf(stdout, "end\n");
+ fprintf(outputfile, "end\n");
}
}
/* If command didn't succeed in any node, continue with return
u_int i;
int cmd_stat = CMD_SUCCESS;
- fprintf(stdout, "%s", vty->buf);
+ fprintf(outputfile, "%s", vty->buf);
for (i = 0; i < array_size(vtysh_client); i++) {
if (cmd->daemon & vtysh_client[i].flag) {
cmd_stat = vtysh_client_execute(
&vtysh_client[i], vty->buf,
- stdout);
+ outputfile);
if (cmd_stat != CMD_SUCCESS)
break;
}
}
}
/* This is the end */
- fprintf(stdout, "\nend\n");
+ fprintf(outputfile, "\nend\n");
vty_close(vty);
XFREE(MTYPE_VTYSH_CMD, vty_buf_copy);
if (cmd->daemon & vtysh_client[i].flag) {
cmd_stat = vtysh_client_execute(
&vtysh_client[i], vty->buf,
- stdout);
+ outputfile);
/*
* CMD_WARNING - Can mean that the
* command was
fprintf(stdout, "Thread statistics for %s:\n",
vtysh_client[i].name);
ret = vtysh_client_execute(&vtysh_client[i], line,
- stdout);
+ outputfile);
fprintf(stdout, "\n");
}
return ret;
fprintf(stdout, "Work queue statistics for %s:\n",
vtysh_client[i].name);
ret = vtysh_client_execute(&vtysh_client[i], line,
- stdout);
+ outputfile);
fprintf(stdout, "\n");
}
}
ret = vtysh_client_execute(&vtysh_client[i], "show work-queues\n",
- stdout);
+ outputfile);
return ret;
}
for (i = 0; i < array_size(vtysh_client); i++)
if (vtysh_client[i].fd >= 0) {
- fprintf(stdout, headline, vtysh_client[i].name);
+ fprintf(outputfile, headline, vtysh_client[i].name);
ret = vtysh_client_execute(&vtysh_client[i], line,
- stdout);
+ outputfile);
fprintf(stdout, "\n");
}
{
u_int i;
char line[] = "do write terminal\n";
- FILE *fp = NULL;
+ FILE *fp = outputfile;
- if (vtysh_pager_name) {
+ if (fp == stdout && vtysh_pager_name) {
fp = popen(vtysh_pager_name, "w");
if (fp == NULL) {
perror("popen");
exit(1);
}
- } else
- fp = stdout;
+ }
- vty_out(vty, "Building configuration...\n");
- vty_out(vty, "\nCurrent configuration:\n");
- vty_out(vty, "!\n");
+ fprintf(outputfile, "Building configuration...\n");
+ fprintf(outputfile, "\nCurrent configuration:\n");
+ fprintf(outputfile, "!\n");
for (i = 0; i < array_size(vtysh_client); i++)
if ((argc < 3)
vtysh_config_dump(fp);
- if (vtysh_pager_name && fp) {
+ if (vtysh_pager_name && fp && fp != outputfile) {
fflush(fp);
if (pclose(fp) == -1) {
perror("pclose");
fp = NULL;
}
- vty_out(vty, "end\n");
+ fprintf(outputfile, "end\n");
return CMD_SUCCESS;
}
char line[] = "do write memory\n";
u_int i;
- fprintf(stdout,
+ fprintf(outputfile,
"Note: this version of vtysh never writes vtysh.conf\n");
/* If integrated frr.conf explicitely set. */
if (i < array_size(vtysh_client) && vtysh_client[i].fd != -1)
ret = vtysh_client_execute(&vtysh_client[i],
"do write integrated",
- stdout);
+ outputfile);
if (ret != CMD_SUCCESS) {
printf("\nWarning: attempting direct configuration write without "
return ret;
}
- fprintf(stdout, "Building Configuration...\n");
+ fprintf(outputfile, "Building Configuration...\n");
for (i = 0; i < array_size(vtysh_client); i++)
- ret = vtysh_client_execute(&vtysh_client[i], line, stdout);
+ ret = vtysh_client_execute(&vtysh_client[i], line, outputfile);
return ret;
}
lines = strtol(argv[idx_number]->arg, &endptr, 10);
if (lines < 0 || lines > 512 || *endptr != '\0') {
- vty_out(vty, "length is malformed\n");
+ fprintf(outputfile, "length is malformed\n");
return CMD_WARNING;
}
for (i = 0; i < array_size(vtysh_client); i++)
if (vtysh_client[i].fd >= 0)
- vty_out(vty, " %s", vtysh_client[i].name);
- vty_out(vty, "\n");
+ fprintf(outputfile, " %s", vtysh_client[i].name);
+ fprintf(outputfile, "\n");
return CMD_SUCCESS;
}
return cmd_list_cmds(vty, argc == 2);
}
+DEFUN (vtysh_output_file,
+ vtysh_output_file_cmd,
+ "output file FILE",
+ "Direct vtysh output to file\n"
+ "Direct vtysh output to file\n"
+ "Path to dump output to\n")
+{
+ const char *path = argv[argc - 1]->arg;
+ outputfile = fopen(path, "a");
+ if (!outputfile) {
+ fprintf(stdout, "Failed to open file '%s': %s\n", path,
+ safe_strerror(errno));
+ outputfile = stdout;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_vtysh_output_file,
+ no_vtysh_output_file_cmd,
+ "no output file [FILE]",
+ NO_STR
+ "Direct vtysh output to file\n"
+ "Direct vtysh output to file\n"
+ "Path to dump output to\n")
+{
+ if (outputfile != stdout) {
+ fclose(outputfile);
+ outputfile = stdout;
+ }
+ return CMD_SUCCESS;
+}
+
DEFUN(find,
find_cmd,
"find COMMAND...",
{
install_element(node, &config_list_cmd);
install_element(node, &find_cmd);
+ install_element(node, &vtysh_output_file_cmd);
+ install_element(node, &no_vtysh_output_file_cmd);
}
/* Making connection to protocol daemon. */
.completions = vtysh_autocomplete},
{.completions = NULL}};
+void vtysh_uninit()
+{
+ if (outputfile != stdout)
+ fclose(outputfile);
+}
+
void vtysh_init_vty(void)
{
/* Make vty structure. */
vty->type = VTY_SHELL;
vty->node = VIEW_NODE;
+ /* set default output */
+ outputfile = stdout;
+
/* Initialize commands. */
cmd_init(0);
cmd_variable_handler_register(vtysh_var_handler);
extern char vtydir[];
void vtysh_init_vty(void);
+void vtysh_uninit(void);
void vtysh_init_cmd(void);
extern int vtysh_connect_all(const char *optional_daemon_name);
void vtysh_readline_init(void);
while (vtysh_rl_gets())
vtysh_execute(line_read);
+ vtysh_uninit();
+
history_truncate_file(history_file, 1000);
printf("\n");
#define FUZZY(X) ((X)+JITTER((X)/20))
#define DEFAULT_PERIOD 5
-#define DEFAULT_TIMEOUT 10
+#define DEFAULT_TIMEOUT 90
#define DEFAULT_RESTART_TIMEOUT 20
#define DEFAULT_LOGLEVEL LOG_INFO
#define DEFAULT_MIN_RESTART 60
FILE *fp;
fp = fopen(DAEMON_VTY_DIR "/watchfrr.started", "w");
- fclose(fp);
+ if (fp)
+ fclose(fp);
#if defined HAVE_SYSTEMD
zlog_notice(
"Watchfrr: Notifying Systemd we are up and running");
break;
}
- rib_add(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0,
- &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0);
+ rib_add(afi, SAFI_UNICAST, ifp->vrf_id, ifp->vrf_id,
+ ZEBRA_ROUTE_CONNECT, 0, 0,
+ &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0, 0);
- rib_add(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0,
- &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0);
+ rib_add(afi, SAFI_MULTICAST, ifp->vrf_id, ifp->vrf_id,
+ ZEBRA_ROUTE_CONNECT, 0, 0,
+ &p, NULL, &nh, RT_TABLE_MAIN, ifp->metric, 0, 0, 0);
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
char buf[PREFIX_STRLEN];
* head.
*/
rib_delete(afi, SAFI_UNICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0,
- &p, NULL, &nh, 0, 0, false);
+ &p, NULL, &nh, 0, 0, false, NULL);
rib_delete(afi, SAFI_MULTICAST, ifp->vrf_id, ZEBRA_ROUTE_CONNECT, 0,
- 0, &p, NULL, &nh, 0, 0, false);
+ 0, &p, NULL, &nh, 0, 0, false, NULL);
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
char buf[PREFIX_STRLEN];
return (ecmd.speed_hi << 16) | ecmd.speed;
}
+uint32_t kernel_get_speed(struct interface *ifp)
+{
+ return get_iflink_speed(ifp->name);
+}
+
static int netlink_extract_bridge_info(struct rtattr *link_data,
struct zebra_l2info_bridge *bridge_info)
{
return netlink_address(RTM_DELADDR, AF_INET, ifp, ifc);
}
+int kernel_address_add_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+ return netlink_address (RTM_NEWADDR, AF_INET6, ifp, ifc);
+}
+
+int kernel_address_delete_ipv6 (struct interface *ifp, struct connected *ifc)
+{
+ return netlink_address (RTM_DELADDR, AF_INET6, ifp, ifc);
+}
+
int netlink_interface_addr(struct sockaddr_nl *snl, struct nlmsghdr *h,
ns_id_t ns_id, int startup)
{
DEFINE_HOOK(zebra_if_config_wr, (struct vty *vty, struct interface *ifp),
(vty, ifp))
+
static void if_down_del_nbr_connected(struct interface *ifp);
+static int if_zebra_speed_update(struct thread *thread)
+{
+ struct interface *ifp = THREAD_ARG(thread);
+ struct zebra_if *zif = ifp->info;
+ uint32_t new_speed;
+
+ zif->speed_update = NULL;
+
+ new_speed = kernel_get_speed(ifp);
+ if (new_speed != ifp->speed) {
+ zlog_info("%s: %s old speed: %u new speed: %u",
+ __PRETTY_FUNCTION__, ifp->name,
+ ifp->speed, new_speed);
+ ifp->speed = new_speed;
+ if_add_update(ifp);
+ }
+
+ return 1;
+}
+
static void zebra_if_node_destroy(route_table_delegate_t *delegate,
struct route_table *table,
struct route_node *node)
route_table_init_with_delegate(&zebra_if_table_delegate);
ifp->info = zebra_if;
+
+ /*
+ * Some platforms are telling us that the interface is
+ * up and ready to go. When we check the speed we
+ * sometimes get the wrong value. Wait a couple
+ * of seconds and ask again. Hopefully it's all settled
+ * down upon startup.
+ */
+ thread_add_timer(zebrad.master, if_zebra_speed_update,
+ ifp, 15, &zebra_if->speed_update);
return 0;
}
list_delete_and_null(&rtadv->AdvPrefixList);
#endif /* HAVE_RTADV */
+ THREAD_OFF(zebra_if->speed_update);
+
XFREE(MTYPE_TMP, zebra_if);
}
struct prefix cp;
struct route_node *rn;
struct zebra_if *zebra_if;
+ struct listnode *node;
+ struct listnode *last = NULL;
zebra_if = ifp->info;
- if (ifp->connected) {
- struct listnode *node;
- struct listnode *last = NULL;
+ if (!ifp->connected)
+ return;
- while ((node = (last ? last->next
- : listhead(ifp->connected)))) {
- ifc = listgetdata(node);
+ while ((node = (last ? last->next
+ : listhead(ifp->connected)))) {
+ ifc = listgetdata(node);
- cp = *CONNECTED_PREFIX(ifc);
- apply_mask(&cp);
+ cp = *CONNECTED_PREFIX(ifc);
+ apply_mask(&cp);
- if (cp.family == AF_INET
- && (rn = route_node_lookup(zebra_if->ipv4_subnets,
- &cp))) {
- struct listnode *anode;
- struct listnode *next;
- struct listnode *first;
- struct list *addr_list;
+ if (cp.family == AF_INET
+ && (rn = route_node_lookup(zebra_if->ipv4_subnets,
+ &cp))) {
+ struct listnode *anode;
+ struct listnode *next;
+ struct listnode *first;
+ struct list *addr_list;
- route_unlock_node(rn);
- addr_list = (struct list *)rn->info;
+ route_unlock_node(rn);
+ addr_list = (struct list *)rn->info;
- /* Remove addresses, secondaries first. */
- first = listhead(addr_list);
+ /* Remove addresses, secondaries first. */
+ first = listhead(addr_list);
+ if (first)
for (anode = first->next; anode || first;
anode = next) {
if (!anode) {
last = node;
}
- /* Free chain list and respective route node. */
- list_delete_and_null(&addr_list);
- rn->info = NULL;
- route_unlock_node(rn);
- } else if (cp.family == AF_INET6) {
- connected_down(ifp, ifc);
+ /* Free chain list and respective route node. */
+ list_delete_and_null(&addr_list);
+ rn->info = NULL;
+ route_unlock_node(rn);
+ } else if (cp.family == AF_INET6) {
+ connected_down(ifp, ifc);
- zebra_interface_address_delete_update(ifp, ifc);
+ zebra_interface_address_delete_update(ifp, ifc);
- UNSET_FLAG(ifc->conf, ZEBRA_IFC_REAL);
- UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_REAL);
+ UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED);
- if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED))
- last = node;
- else {
- listnode_delete(ifp->connected, ifc);
- connected_free(ifc);
- }
- } else {
+ if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED))
last = node;
+ else {
+ listnode_delete(ifp->connected, ifc);
+ connected_free(ifc);
}
+ } else {
+ last = node;
}
}
}
zebra_interface_vrf_update_add(ifp, old_vrf_id);
/* Install connected routes (in new VRF). */
- if_install_connected(ifp);
+ if (if_is_operative(ifp))
+ if_install_connected(ifp);
static_ifindex_update(ifp, true);
#define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */
u_char inFastRexmit; /* True if we're rexmits faster than usual */
- u_char configured; /* Has operator configured RA? */
+
+ /* Track if RA was configured by BGP or by the Operator or both */
+ u_char ra_configured; /* Was RA configured? */
+#define BGP_RA_CONFIGURED (1<<0) /* BGP configured RA? */
+#define VTY_RA_CONFIGURED (1<<1) /* Operator configured RA? */
+#define VTY_RA_INTERVAL_CONFIGURED (1<<2) /* Operator configured RA interval */
int
NumFastReXmitsRemain; /* Loaded first with number of fast
rexmits to do */
/* Link fields - for sub-interfaces. */
ifindex_t link_ifindex;
struct interface *link;
+
+ struct thread *speed_update;
};
DECLARE_HOOK(zebra_if_extra_info, (struct vty *vty, struct interface *ifp),
#define IS_ZEBRA_IF_VRF_SLAVE(ifp) \
(((struct zebra_if *)(ifp->info))->zif_slave_type == ZEBRA_IF_SLAVE_VRF)
+extern void zebra_if_init(void);
+
extern struct interface *if_lookup_by_index_per_ns(struct zebra_ns *,
u_int32_t);
extern struct interface *if_lookup_by_name_per_ns(struct zebra_ns *,
return 0;
}
+#ifndef HAVE_NETLINK
static int if_ioctl_ipv6(u_long request, caddr_t buffer)
{
int sock;
}
return 0;
}
+#endif /* ! HAVE_NETLINK */
/*
* get interface metric
int ifr6_ifindex;
};
#endif /* _LINUX_IN6_H */
-
/* Interface's address add/delete functions. */
int if_prefix_add_ipv6(struct interface *ifp, struct connected *ifc)
{
- int ret;
- struct prefix_ipv6 *p;
- struct in6_ifreq ifreq;
-
- p = (struct prefix_ipv6 *)ifc->address;
-
- memset(&ifreq, 0, sizeof(struct in6_ifreq));
-
- memcpy(&ifreq.ifr6_addr, &p->prefix, sizeof(struct in6_addr));
- ifreq.ifr6_ifindex = ifp->ifindex;
- ifreq.ifr6_prefixlen = p->prefixlen;
-
- ret = if_ioctl_ipv6(SIOCSIFADDR, (caddr_t)&ifreq);
-
- return ret;
+#ifdef HAVE_NETLINK
+ return kernel_address_add_ipv6 (ifp, ifc);
+#endif /* HAVE_NETLINK */
}
int if_prefix_delete_ipv6(struct interface *ifp, struct connected *ifc)
{
- int ret;
- struct prefix_ipv6 *p;
- struct in6_ifreq ifreq;
-
- p = (struct prefix_ipv6 *)ifc->address;
-
- memset(&ifreq, 0, sizeof(struct in6_ifreq));
-
- memcpy(&ifreq.ifr6_addr, &p->prefix, sizeof(struct in6_addr));
- ifreq.ifr6_ifindex = ifp->ifindex;
- ifreq.ifr6_prefixlen = p->prefixlen;
-
- ret = if_ioctl_ipv6(SIOCDIFADDR, (caddr_t)&ifreq);
-
- return ret;
+#ifdef HAVE_NETLINK
+ return kernel_address_delete_ipv6 (ifp, ifc);
+#endif /* HAVE_NETLINK */
}
#else /* LINUX_IPV6 */
#ifdef HAVE_STRUCT_IN6_ALIASREQ
int ipforward(void)
{
+ int ret = 0;
FILE *fp;
int ipforwarding = 0;
char buf[10];
1 => ip forwarding enabled
2 => ip forwarding off. */
if (fgets(buf, 6, fp))
- sscanf(buf, "Ip: %d", &ipforwarding);
+ ret = sscanf(buf, "Ip: %d", &ipforwarding);
fclose(fp);
- if (ipforwarding == 1)
+ if (ret == 1 && ipforwarding == 1)
return 1;
return 0;
int ipforward_ipv6(void)
{
+ int ret = 0;
FILE *fp;
char buf[5];
int ipforwarding = 0;
return -1;
if (fgets(buf, 2, fp))
- sscanf(buf, "%d", &ipforwarding);
+ ret = sscanf(buf, "%d", &ipforwarding);
fclose(fp);
+
+ if (ret != 1)
+ return 0;
+
return ipforwarding;
}
/* IPv6 forwarding control MIB. */
int mib_ipv6[MIB_SIZ] = {CTL_NET, PF_INET6,
-#if defined(KAME)
+#if defined(BSD_V6_SYSCTL)
IPPROTO_IPV6, IPV6CTL_FORWARDING
-#else /* NOT KAME */
+#else /* NOT BSD_V6_SYSCTL */
IPPROTO_IP, IP6CTL_FORWARDING
-#endif /* KAME */
+#endif /* BSD_V6_SYSCTL */
};
int ipforward_ipv6(void)
#include "zebra/zserv.h"
#include "zebra/zebra_ns.h"
#include "zebra/zebra_vrf.h"
+#include "zebra/rt.h"
#include "zebra/debug.h"
#include "zebra/kernel_netlink.h"
#include "zebra/rt_netlink.h"
if (rtm->rtm_type == RTM_CHANGE)
rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- NULL, 0, 0, true);
+ NULL, 0, 0, true, NULL);
if (!nh.type) {
nh.type = NEXTHOP_TYPE_IPV4;
if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD
|| rtm->rtm_type == RTM_CHANGE)
- rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT,
+ rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- &nh, 0, 0, 0, 0);
+ &nh, 0, 0, 0, 0, 0);
else
rib_delete(AFI_IP, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- &nh, 0, 0, true);
+ &nh, 0, 0, true, NULL);
}
if (dest.sa.sa_family == AF_INET6) {
/* One day we might have a debug section here like one in the
if (rtm->rtm_type == RTM_CHANGE)
rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- NULL, 0, 0, true);
+ NULL, 0, 0, true, NULL);
if (!nh.type) {
nh.type = ifindex ? NEXTHOP_TYPE_IPV6_IFINDEX
if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD
|| rtm->rtm_type == RTM_CHANGE)
- rib_add(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT,
+ rib_add(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- &nh, 0, 0, 0, 0);
+ &nh, 0, 0, 0, 0, 0);
else
rib_delete(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT,
ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL,
- &nh, 0, 0, true);
+ &nh, 0, 0, true, NULL);
}
}
struct label_manager lbl_mgr;
+extern struct zebra_privs_t zserv_privs;
+
DEFINE_MGROUP(LBL_MGR, "Label Manager");
DEFINE_MTYPE_STATIC(LBL_MGR, LM_CHUNK, "Label Manager Chunk");
s = zserv->obuf;
stream_reset(s);
- zserv_create_header(s, cmd, vrf_id);
+ zclient_create_header(s, cmd, vrf_id);
/* result */
stream_putc(s, 1);
/* Set default values. */
zclient = zclient_new_notify(zebrad.master, &zclient_options_default);
+ zclient->privs = &zserv_privs;
zclient->sock = -1;
zclient->t_connect = NULL;
lm_zclient_connect(NULL);
zebrad.master = frr_init();
/* Zebra related initialize. */
- zebra_init();
+ zserv_init();
rib_init();
zebra_if_init();
zebra_debug_init();
router_id_cmd_init();
+
+ /*
+ * Initialize NS( and implicitly the VRF module), and make kernel
+ * routing socket. */
+ zebra_ns_init();
+
zebra_vty_init();
access_list_init();
prefix_list_init();
/* For debug purpose. */
/* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */
- /* Initialize NS( and implicitly the VRF module), and make kernel
- * routing socket. */
- zebra_ns_init();
-
#if defined(HANDLE_ZAPI_FUZZING)
if (fuzzing) {
zserv_read_file(fuzzing);
rib_delete(afi, SAFI_UNICAST, re->vrf_id, ZEBRA_ROUTE_TABLE,
re->table, re->flags, &p, NULL, re->nexthop,
- zebrad.rtm_table_default, re->metric, false);
+ zebrad.rtm_table_default, re->metric, false, NULL);
return 0;
}
/* VRF identifier. */
vrf_id_t vrf_id;
+ vrf_id_t nh_vrf_id;
/* Which routing table */
uint32_t table;
/* to simplify NHT logic when NHs change, instead of doing a NH by NH cmp */
#define ROUTE_ENTRY_NEXTHOPS_CHANGED 0x2
#define ROUTE_ENTRY_CHANGED 0x4
-#define ROUTE_ENTRY_SELECTED_FIB 0x8
-#define ROUTE_ENTRY_LABELS_CHANGED 0x10
+#define ROUTE_ENTRY_LABELS_CHANGED 0x8
/* Nexthop information. */
u_char nexthop_num;
*/
struct route_entry *routes;
+ struct route_entry *selected_fib;
+
/*
* Flags, see below.
*/
/* NOTE:
* All rib_add function will not just add prefix into RIB, but
* also implicitly withdraw equal prefix of same type. */
-extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
- u_short instance, int flags, struct prefix *p,
+extern int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, vrf_id_t nh_vrf_id,
+ int type, u_short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
u_int32_t table_id, u_int32_t metric, u_int32_t mtu,
- uint8_t distance);
+ uint8_t distance, route_tag_t tag);
extern int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *,
struct prefix_ipv6 *src_p, struct route_entry *);
extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
u_short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- u_int32_t table_id, u_int32_t metric, bool fromkernel);
+ u_int32_t table_id, u_int32_t metric, bool fromkernel,
+ struct ethaddr *rmac);
extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t,
union g_addr *,
DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason),
(rn, reason))
+
+extern void zebra_vty_init(void);
+extern int static_config(struct vty *vty, struct zebra_vrf *zvrf,
+ afi_t afi, safi_t safi, const char *cmd);
+extern pid_t pid;
+
#endif /*_ZEBRA_RIB_H */
* semantics so we will end up with a delete than
* a re-add.
*/
-extern void kernel_route_rib(struct prefix *p, struct prefix *src_p,
- struct route_entry *old, struct route_entry *new);
+extern void kernel_route_rib(struct route_node *rn, struct prefix *p,
+ struct prefix *src_p, struct route_entry *old,
+ struct route_entry *new);
/*
* So route install/failure may not be immediately known
* so let's separate it out and allow the result to
* be passed back up.
*/
-extern void kernel_route_rib_pass_fail(struct prefix *p,
+extern void kernel_route_rib_pass_fail(struct route_node *rn,
+ struct prefix *p,
struct route_entry *re,
enum southbound_results res);
extern int kernel_address_add_ipv4(struct interface *, struct connected *);
extern int kernel_address_delete_ipv4(struct interface *, struct connected *);
+extern int kernel_address_add_ipv6 (struct interface *, struct connected *);
+extern int kernel_address_delete_ipv6 (struct interface *, struct connected *);
extern int kernel_neigh_update(int, int, uint32_t, char *, int);
extern int kernel_interface_set_master(struct interface *master,
struct interface *slave);
extern int mpls_kernel_init(void);
+extern uint32_t kernel_get_speed(struct interface *ifp);
extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute);
extern int kernel_add_vtep(vni_t vni, struct interface *ifp,
struct in_addr *vtep_ip);
struct ethaddr *mac);
extern int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip);
+/*
+ * Southbound Initialization routines to get initial starting
+ * state.
+ */
+extern void interface_list(struct zebra_ns *zns);
+extern void kernel_init(struct zebra_ns *zns);
+extern void kernel_terminate(struct zebra_ns *zns);
+extern void macfdb_read(struct zebra_ns *zns);
+extern void macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp,
+ struct interface *br_if);
+extern void neigh_read(struct zebra_ns *zns);
+extern void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *ifp);
+extern void route_read(struct zebra_ns *zns);
+
#endif /* _ZEBRA_RT_H */
int metric = 0;
u_int32_t mtu = 0;
uint8_t distance = 0;
+ route_tag_t tag = 0;
void *dest = NULL;
void *gate = NULL;
if (tb[RTA_PRIORITY])
metric = *(int *)RTA_DATA(tb[RTA_PRIORITY]);
+#if defined(SUPPORT_REALMS)
+ if (tb[RTA_FLOW])
+ tag = *(uint32_t *)RTA_DATA(tb[RTA_FLOW]);
+#endif
+
if (tb[RTA_METRICS]) {
struct rtattr *mxrta[RTAX_MAX + 1];
afi = AFI_IP6;
if (h->nlmsg_type == RTM_NEWROUTE) {
+ struct interface *ifp;
+ vrf_id_t nh_vrf_id = vrf_id;
+
if (!tb[RTA_MULTIPATH]) {
struct nexthop nh;
size_t sz = (afi == AFI_IP) ? 4 : 16;
if (gate)
memcpy(&nh.gate, gate, sz);
- rib_add(afi, SAFI_UNICAST, vrf_id, proto,
- 0, flags, &p, NULL, &nh, table, metric, mtu, distance);
+ if (index) {
+ ifp = if_lookup_by_index(index,
+ VRF_UNKNOWN);
+ if (ifp)
+ nh_vrf_id = ifp->vrf_id;
+ }
+
+ rib_add(afi, SAFI_UNICAST, vrf_id, nh_vrf_id, proto,
+ 0, flags, &p, NULL, &nh, table, metric,
+ mtu, distance, tag);
} else {
/* This is a multipath route */
re->metric = metric;
re->mtu = mtu;
re->vrf_id = vrf_id;
+ re->nh_vrf_id = vrf_id;
re->table = table;
re->nexthop_num = 0;
re->uptime = time(NULL);
+ re->tag = tag;
for (;;) {
if (len < (int)sizeof(*rtnh)
break;
index = rtnh->rtnh_ifindex;
+ if (index) {
+ /*
+ * Yes we are looking this up
+ * for every nexthop and just
+ * using the last one looked
+ * up right now
+ */
+ ifp = if_lookup_by_index(index,
+ VRF_UNKNOWN);
+ if (ifp)
+ re->nh_vrf_id = ifp->vrf_id;
+ }
gate = 0;
if (rtnh->rtnh_len > sizeof(*rtnh)) {
memset(tb, 0, sizeof(tb));
memcpy(&nh.gate, gate, sz);
rib_delete(afi, SAFI_UNICAST, vrf_id,
proto, 0, flags, &p, NULL, &nh,
- table, metric, true);
+ table, metric, true, NULL);
} else {
/* XXX: need to compare the entire list of nexthops
* here for NLM_F_APPEND stupidity */
rib_delete(afi, SAFI_UNICAST, vrf_id,
proto, 0, flags, &p, NULL, NULL,
- table, metric, true);
+ table, metric, true, NULL);
}
}
struct rtmsg *rtmsg,
size_t req_size, int cmd)
{
- struct nexthop_label *nh_label;
+ struct mpls_label_stack *nh_label;
mpls_lse_t out_lse[MPLS_MAX_LABELS];
char label_buf[256];
routedesc, inet6_ntoa(nexthop->gate.ipv6),
label_buf, nexthop->ifindex);
}
- if (nexthop->type == NEXTHOP_TYPE_IFINDEX
- || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) {
+
+ /*
+ * We have the ifindex so we should always send it
+ * This is especially useful if we are doing route
+ * leaking.
+ */
+ if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)
addattr32(nlmsg, req_size, RTA_OIF, nexthop->ifindex);
+ if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+ || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) {
if (cmd == RTM_NEWROUTE) {
if (nexthop->rmap_src.ipv4.s_addr)
addattr_l(nlmsg, req_size, RTA_PREFSRC,
}
if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
- addattr32(nlmsg, req_size, RTA_OIF, nexthop->ifindex);
-
if (cmd == RTM_NEWROUTE) {
if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6))
addattr_l(nlmsg, req_size, RTA_PREFSRC,
struct rtmsg *rtmsg,
union g_addr **src)
{
- struct nexthop_label *nh_label;
+ struct mpls_label_stack *nh_label;
mpls_lse_t out_lse[MPLS_MAX_LABELS];
char label_buf[256];
routedesc, inet6_ntoa(nexthop->gate.ipv6),
label_buf, nexthop->ifindex);
}
+
+ /*
+ * We have figured out the ifindex so we should always send it
+ * This is especially useful if we are doing route
+ * leaking.
+ */
+ if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)
+ rtnh->rtnh_ifindex = nexthop->ifindex;
+
/* ifindex */
if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
|| nexthop->type == NEXTHOP_TYPE_IFINDEX) {
- rtnh->rtnh_ifindex = nexthop->ifindex;
-
if (nexthop->rmap_src.ipv4.s_addr)
*src = &nexthop->rmap_src;
else if (nexthop->src.ipv4.s_addr)
"nexthop via if %u",
routedesc, nexthop->ifindex);
} else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
- rtnh->rtnh_ifindex = nexthop->ifindex;
-
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
"netlink_route_multipath() (%s): "
* by the routing protocol and for communicating with protocol peers.
*/
addattr32(&req.n, sizeof req, RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC);
-
+#if defined(SUPPORT_REALMS)
+ if (re->tag > 0 && re->tag <= 255)
+ addattr32(&req.n, sizeof req, RTA_FLOW, re->tag);
+#endif
/* Table corresponding to this route. */
if (re->table < 256)
req.r.rtm_table = re->table;
return suc;
}
-void kernel_route_rib(struct prefix *p, struct prefix *src_p,
- struct route_entry *old, struct route_entry *new)
+void kernel_route_rib(struct route_node *rn, struct prefix *p,
+ struct prefix *src_p, struct route_entry *old,
+ struct route_entry *new)
{
int ret = 0;
ret = netlink_route_multipath(RTM_NEWROUTE, p,
src_p, new, 0);
}
- kernel_route_rib_pass_fail(p, new,
+ kernel_route_rib_pass_fail(rn, p, new,
(!ret) ?
SOUTHBOUND_INSTALL_SUCCESS :
SOUTHBOUND_INSTALL_FAILURE);
if (old) {
ret = netlink_route_multipath(RTM_DELROUTE, p, src_p, old, 0);
- kernel_route_rib_pass_fail(p, old,
+ kernel_route_rib_pass_fail(rn, p, old,
(!ret) ?
SOUTHBOUND_DELETE_SUCCESS :
SOUTHBOUND_DELETE_FAILURE);
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
#ifdef __OpenBSD__
-static int kernel_rtm_add_labels(struct nexthop_label *nh_label,
+static int kernel_rtm_add_labels(struct mpls_label_stack *nh_label,
struct sockaddr_mpls *smpls)
{
if (nh_label->num_labels > 1) {
return 0;
}
-void kernel_route_rib(struct prefix *p, struct prefix *src_p,
- struct route_entry *old, struct route_entry *new)
+void kernel_route_rib(struct route_node *rn, struct prefix *p,
+ struct prefix *src_p, struct route_entry *old,
+ struct route_entry *new)
{
int route = 0;
zlog_err("Can't lower privileges");
if (new) {
- kernel_route_rib_pass_fail(p, new,
+ kernel_route_rib_pass_fail(rn, p, new,
(!route) ?
SOUTHBOUND_INSTALL_SUCCESS :
SOUTHBOUND_INSTALL_FAILURE);
} else {
- kernel_route_rib_pass_fail(p, old,
+ kernel_route_rib_pass_fail(rn, p, old,
(!route) ?
SOUTHBOUND_DELETE_SUCCESS :
SOUTHBOUND_DELETE_FAILURE);
return 0;
}
+uint32_t kernel_get_speed(struct interface *ifp)
+{
+ return ifp->speed;
+}
+
#endif /* !HAVE_NETLINK */
zif = ifp->info;
if (enable) {
+ SET_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED);
ipv6_nd_suppress_ra_set(ifp, RA_ENABLE);
if (ra_interval
- && (ra_interval * 1000) < zif->rtadv.MaxRtrAdvInterval)
+ && (ra_interval * 1000) < zif->rtadv.MaxRtrAdvInterval
+ && !CHECK_FLAG(zif->rtadv.ra_configured,
+ VTY_RA_INTERVAL_CONFIGURED))
zif->rtadv.MaxRtrAdvInterval = ra_interval * 1000;
} else {
- if (!zif->rtadv.configured) {
+ UNSET_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED);
+ if (!CHECK_FLAG(zif->rtadv.ra_configured,
+ VTY_RA_INTERVAL_CONFIGURED))
zif->rtadv.MaxRtrAdvInterval =
RTADV_MAX_RTR_ADV_INTERVAL;
+ if (!CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED))
ipv6_nd_suppress_ra_set(ifp, RA_SUPPRESS);
- }
}
stream_failure:
return;
return CMD_WARNING_CONFIG_FAILED;
}
- ipv6_nd_suppress_ra_set(ifp, RA_SUPPRESS);
- zif->rtadv.configured = 0;
+ if (!CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED))
+ ipv6_nd_suppress_ra_set(ifp, RA_SUPPRESS);
+
+ UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED);
return CMD_SUCCESS;
}
}
ipv6_nd_suppress_ra_set(ifp, RA_ENABLE);
- zif->rtadv.configured = 1;
+ SET_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED);
return CMD_SUCCESS;
}
if (interval % 1000)
zns->rtadv.adv_msec_if_count++;
+ SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED);
zif->rtadv.MaxRtrAdvInterval = interval;
zif->rtadv.MinRtrAdvInterval = 0.33 * interval;
zif->rtadv.AdvIntervalTimer = 0;
/* convert to milliseconds */
interval = interval * 1000;
+ SET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED);
zif->rtadv.MaxRtrAdvInterval = interval;
zif->rtadv.MinRtrAdvInterval = 0.33 * interval;
zif->rtadv.AdvIntervalTimer = 0;
if (zif->rtadv.MaxRtrAdvInterval % 1000)
zns->rtadv.adv_msec_if_count--;
- zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL;
- zif->rtadv.MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL;
+ UNSET_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED);
+
+ if (CHECK_FLAG(zif->rtadv.ra_configured, BGP_RA_CONFIGURED))
+ zif->rtadv.MaxRtrAdvInterval = 10000;
+ else
+ zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL;
+
zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval;
+ zif->rtadv.MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL;
return CMD_SUCCESS;
}
if (!(if_is_loopback(ifp)
|| CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK))) {
- if (zif->rtadv.AdvSendAdvertisements)
+ if (zif->rtadv.AdvSendAdvertisements
+ && CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_CONFIGURED))
vty_out(vty, " no ipv6 nd suppress-ra\n");
}
interval = zif->rtadv.MaxRtrAdvInterval;
- if (interval % 1000)
- vty_out(vty, " ipv6 nd ra-interval msec %d\n", interval);
- else if (interval != RTADV_MAX_RTR_ADV_INTERVAL)
- vty_out(vty, " ipv6 nd ra-interval %d\n", interval / 1000);
+ if (CHECK_FLAG(zif->rtadv.ra_configured, VTY_RA_INTERVAL_CONFIGURED)) {
+ if (interval % 1000)
+ vty_out(vty, " ipv6 nd ra-interval msec %d\n",
+ interval);
+ else if (interval != RTADV_MAX_RTR_ADV_INTERVAL)
+ vty_out(vty, " ipv6 nd ra-interval %d\n",
+ interval / 1000);
+ }
if (zif->rtadv.AdvIntervalOption)
vty_out(vty, " ipv6 nd adv-interval-option\n");
#include "vty.h"
#include "zebra/rib.h"
-#include "zebra/zserv.h"
+#include "zebra/rt.h"
/* Thank you, Solaris, for polluting application symbol namespace. */
#undef hook_register
nh.type = NEXTHOP_TYPE_IPV4;
nh.gate.ipv4.s_addr = routeEntry->ipRouteNextHop;
- rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0,
- zebra_flags, &prefix, NULL, &nh, 0, 0, 0, 0);
+ rib_add(AFI_IP, SAFI_UNICAST, VRF_DEFAULT, VRF_DEFAULT,
+ ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &prefix, NULL,
+ &nh, 0, 0, 0, 0, 0);
}
void route_read(struct zebra_ns *zns)
#ifdef GNU_LINUX
#include "vty.h"
-#include "zebra/zserv.h"
+#include "zebra/rt.h"
#include "zebra/rt_netlink.h"
void route_read(struct zebra_ns *zns)
#include "log.h"
#include "vrf.h"
-#include "zebra/zserv.h"
#include "zebra/rt.h"
#include "zebra/kernel_socket.h"
!
! zebra sample configuration file
!
-! $Id: zebra.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $
-!
hostname Router
password zebra
enable password zebra
nbyte = stream_read_try(ibuf, zfpm_g->sock,
FPM_MSG_HDR_LEN - already);
if (nbyte == 0 || nbyte == -1) {
- zfpm_connection_down("closed socket in read");
+ if (nbyte == -1) {
+ char buffer[1024];
+
+ sprintf(buffer, "closed socket in read(%d): %s",
+ errno, safe_strerror(errno));
+ zfpm_connection_down(buffer);
+ }
+ else
+ zfpm_connection_down("closed socket in read");
return 0;
}
nbyte = stream_read_try(ibuf, zfpm_g->sock, msg_len - already);
if (nbyte == 0 || nbyte == -1) {
- zfpm_connection_down("failed to read message");
+ if (nbyte == -1) {
+ char buffer[1024];
+
+ sprintf(buffer, "failed to read message(%d) %s",
+ errno, safe_strerror(errno));
+ zfpm_connection_down(buffer);
+ }
+ else
+ zfpm_connection_down("failed to read message");
return 0;
}
*/
struct route_entry *zfpm_route_for_update(rib_dest_t *dest)
{
- struct route_entry *re;
-
- RE_DEST_FOREACH_ROUTE (dest, re) {
- if (!CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB))
- continue;
-
- return re;
- }
-
- /*
- * We have no route for this destination.
- */
- return NULL;
+ return dest->selected_fib;
}
/*
ifindex_t ifindex, mpls_label_t out_label);
static int nhlfe_del(zebra_nhlfe_t *snhlfe);
static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe,
- struct nexthop_label *nh_label);
+ struct mpls_label_stack *nh_label);
static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp,
enum lsp_types_t type);
static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf,
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_FEC_UPDATE, VRF_DEFAULT);
+ zclient_create_header(s, ZEBRA_FEC_UPDATE, VRF_DEFAULT);
stream_putw(s, rn->p.family);
stream_put_prefix(s, &rn->p);
* Update label for NHLFE entry.
*/
static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe,
- struct nexthop_label *nh_label)
+ struct mpls_label_stack *nh_label)
{
nhlfe->nexthop->nh_label->label[0] = nh_label->label[0];
}
stream_reset(s);
- zserv_create_header(s, ZEBRA_IPMR_ROUTE_STATS, zvrf_id(zvrf));
+ zclient_create_header(s, ZEBRA_IPMR_ROUTE_STATS, zvrf_id(zvrf));
stream_put_in_addr(s, &mroute.sg.src);
stream_put_in_addr(s, &mroute.sg.grp);
stream_put(s, &mroute.lastused, sizeof(mroute.lastused));
#include "zebra_ns.h"
#include "zebra_vrf.h"
#include "zebra_memory.h"
+#include "rt.h"
+#include "zebra_vxlan.h"
DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space")
#endif
zns->if_table = route_table_init();
+ zebra_vxlan_ns_init(zns);
kernel_init(zns);
interface_list(zns);
route_read(zns);
struct zebra_ns *zns = (struct zebra_ns *)(*info);
route_table_finish(zns->if_table);
+ zebra_vxlan_ns_disable(zns);
#if defined(HAVE_RTADV)
rtadv_terminate(zns);
#endif
zebra_vrf_init();
- zebra_ns_enable(0, (void **)&dzns);
+ zebra_ns_enable(NS_DEFAULT, (void **)&dzns);
return 0;
}
struct route_table *if_table;
+ /* L3-VNI hash table (for EVPN). Only in default instance */
+ struct hash *l3vni_table;
+
#if defined(HAVE_RTADV)
struct rtadv rtadv;
#endif /* HAVE_RTADV */
};
-#define NS_DEFAULT 0
-#define NS_UNKNOWN UINT16_MAX
-
struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id);
int zebra_ns_init(void);
} else {
zlog_debug(
"MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d "
- "with src %s/%d and vrf %d %s event",
+ "with src %s/%d and vrf %u %s event",
inet_ntop(dp->family, &dp->u.prefix, buf[0],
INET6_ADDRSTRLEN),
dp->prefixlen,
ptm_cb.out_data);
zebra_ptm_send_message(ptm_cb.out_data, data_len);
+ return 0;
+
stream_failure:
+ ptm_lib_cleanup_msg(ptm_hdl, out_ctxt);
return 0;
}
zebra_ptm_send_message(ptm_cb.out_data, data_len);
+ return 0;
+
stream_failure:
+ ptm_lib_cleanup_msg(ptm_hdl, out_ctxt);
return 0;
}
{
struct stream *s;
unsigned int pid;
- void *out_ctxt;
+ void *out_ctxt = NULL;
char tmp_buf[64];
int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF;
SET_FLAG(ptm_cb.client_flags[client->proto],
ZEBRA_PTM_BFD_CLIENT_FLAG_REG);
+
+ return 0;
+
stream_failure:
+ if (out_ctxt)
+ ptm_lib_cleanup_msg(ptm_hdl, out_ctxt);
return 0;
}
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, cmd, vrf_id);
+ zclient_create_header(s, cmd, vrf_id);
if (ifp)
stream_putl(s, ifp->ifindex);
else
s = client->obuf;
stream_reset(s);
- zserv_create_header(
- s, cmd, VRF_DEFAULT); // Pending: adjust when multi-vrf bfd work
+ zclient_create_header(s, cmd, VRF_DEFAULT);
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
#include "zebra/zebra_rnh.h"
#include "zebra/interface.h"
#include "zebra/connected.h"
+#include "zebra/zebra_vxlan.h"
DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason),
(rn, reason))
if (src)
nexthop->src.ipv4 = *src;
nexthop->ifindex = ifindex;
- ifp = if_lookup_by_index(nexthop->ifindex, re->vrf_id);
+ ifp = if_lookup_by_index(nexthop->ifindex, re->nh_vrf_id);
/*Pending: need to think if null ifp here is ok during bootup?
There was a crash because ifp here was coming to be NULL */
if (ifp)
- if (connected_is_unnumbered(ifp)) {
+ if (connected_is_unnumbered(ifp) ||
+ CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) {
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK);
}
break;
}
+ /* Copy labels of the resolved route */
+ if (newhop->nh_label)
+ nexthop_add_labels(resolved_hop, newhop->nh_label_type,
+ newhop->nh_label->num_labels,
+ &newhop->nh_label->label[0]);
+
resolved_hop->rparent = nexthop;
nexthop_add(&nexthop->resolved, resolved_hop);
}
struct prefix p;
struct route_table *table;
struct route_node *rn;
- struct route_entry *match;
+ struct route_entry *match = NULL;
int resolved;
struct nexthop *newhop;
struct interface *ifp;
+ rib_dest_t *dest;
if ((nexthop->type == NEXTHOP_TYPE_IPV4)
|| nexthop->type == NEXTHOP_TYPE_IPV6)
if (set) {
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
- zebra_deregister_rnh_static_nexthops(re->vrf_id,
+ zebra_deregister_rnh_static_nexthops(re->nh_vrf_id,
nexthop->resolved, top);
nexthops_free(nexthop->resolved);
nexthop->resolved = NULL;
* address in the routing table.
*/
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) {
- ifp = if_lookup_by_index(nexthop->ifindex, re->vrf_id);
+ ifp = if_lookup_by_index(nexthop->ifindex, re->nh_vrf_id);
if (ifp && connected_is_unnumbered(ifp)) {
if (if_is_operative(ifp))
return 1;
break;
}
/* Lookup table. */
- table = zebra_vrf_table(afi, SAFI_UNICAST, re->vrf_id);
+ table = zebra_vrf_table(afi, SAFI_UNICAST, re->nh_vrf_id);
if (!table)
return 0;
&& !nh_resolve_via_default(p.family))
return 0;
- RNODE_FOREACH_RE (rn, match) {
- if (CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED))
- continue;
-
- /* if the next hop is imported from another table, skip
- * it */
- if (match->type == ZEBRA_ROUTE_TABLE)
- continue;
- if (CHECK_FLAG(match->status, ROUTE_ENTRY_SELECTED_FIB))
- break;
- }
+ dest = rib_dest_from_rnode(rn);
+ if (dest && dest->selected_fib &&
+ !CHECK_FLAG(dest->selected_fib->status,
+ ROUTE_ENTRY_REMOVED) &&
+ dest->selected_fib->type != ZEBRA_ROUTE_TABLE)
+ match = dest->selected_fib;
/* If there is no selected route or matched route is EGP, go up
tree. */
struct prefix p;
struct route_table *table;
struct route_node *rn;
- struct route_entry *match;
+ struct route_entry *match = NULL;
struct nexthop *newhop;
/* Lookup table. */
rn = route_node_match(table, (struct prefix *)&p);
while (rn) {
+ rib_dest_t *dest;
+
route_unlock_node(rn);
- /* Pick up selected route. */
- RNODE_FOREACH_RE (rn, match) {
- if (CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED))
- continue;
- if (CHECK_FLAG(match->status, ROUTE_ENTRY_SELECTED_FIB))
- break;
- }
+ dest = rib_dest_from_rnode(rn);
+ if (dest && dest->selected_fib &&
+ !CHECK_FLAG(dest->selected_fib->status, ROUTE_ENTRY_REMOVED))
+ match = dest->selected_fib;
/* If there is no selected route or matched route is EGP, go up
tree. */
{
struct route_table *table;
struct route_node *rn;
- struct route_entry *match;
+ struct route_entry *match = NULL;
struct nexthop *nexthop;
+ rib_dest_t *dest;
/* Lookup table. */
table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id);
/* Unlock node. */
route_unlock_node(rn);
+ dest = rib_dest_from_rnode(rn);
- RNODE_FOREACH_RE (rn, match) {
- if (CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED))
- continue;
- if (CHECK_FLAG(match->status, ROUTE_ENTRY_SELECTED_FIB))
- break;
- }
+ if (dest && dest->selected_fib &&
+ !CHECK_FLAG(dest->selected_fib->status, ROUTE_ENTRY_REMOVED))
+ match = dest->selected_fib;
if (!match)
return NULL;
{
struct route_table *table;
struct route_node *rn;
- struct route_entry *match;
+ struct route_entry *match = NULL;
struct nexthop *nexthop;
int nexthops_active;
+ rib_dest_t *dest;
/* Lookup table. */
table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id);
/* Unlock node. */
route_unlock_node(rn);
+ dest = rib_dest_from_rnode(rn);
/* Find out if a "selected" RR for the discovered RIB entry exists ever.
*/
- RNODE_FOREACH_RE (rn, match) {
- if (CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED))
- continue;
- if (CHECK_FLAG(match->status, ROUTE_ENTRY_SELECTED_FIB))
- break;
- }
+ if (dest && dest->selected_fib &&
+ !CHECK_FLAG(dest->selected_fib->status, ROUTE_ENTRY_REMOVED))
+ match = dest->selected_fib;
/* None such found :( */
if (!match)
family = 0;
switch (nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
- ifp = if_lookup_by_index(nexthop->ifindex, re->vrf_id);
+ ifp = if_lookup_by_index(nexthop->ifindex, re->nh_vrf_id);
if (ifp && if_is_operative(ifp))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
family = AFI_IP;
- if (nexthop_active(AFI_IP, re, nexthop, set, rn))
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_EVPN_RVTEP))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else if (nexthop_active(AFI_IP, re, nexthop, set, rn))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
if (rn->p.family != AF_INET)
family = AFI_IP6;
if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
- ifp = if_lookup_by_index(nexthop->ifindex, re->vrf_id);
+ ifp = if_lookup_by_index(nexthop->ifindex,
+ re->nh_vrf_id);
if (ifp && if_is_operative(ifp))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr));
/* It'll get set if required inside */
- ret = zebra_route_map_check(family, re->type, p, nexthop, re->vrf_id,
+ ret = zebra_route_map_check(family, re->type, p, nexthop, re->nh_vrf_id,
re->tag);
if (ret == RMAP_DENYMATCH) {
if (IS_ZEBRA_DEBUG_RIB) {
zlog_debug(
"%u:%s: Filtering out with NH out %s due to route map",
re->vrf_id, buf,
- ifindex2ifname(nexthop->ifindex, re->vrf_id));
+ ifindex2ifname(nexthop->ifindex,
+ re->nh_vrf_id));
}
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
}
return 1;
}
-void kernel_route_rib_pass_fail(struct prefix *p, struct route_entry *re,
+void kernel_route_rib_pass_fail(struct route_node *rn, struct prefix *p,
+ struct route_entry *re,
enum southbound_results res)
{
struct nexthop *nexthop;
char buf[PREFIX_STRLEN];
+ rib_dest_t *dest;
+
+ dest = rib_dest_from_rnode(rn);
switch (res) {
case SOUTHBOUND_INSTALL_SUCCESS:
+ dest->selected_fib = re;
for (ALL_NEXTHOPS(re->nexthop, nexthop)) {
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
continue;
p, ZAPI_ROUTE_INSTALLED);
break;
case SOUTHBOUND_INSTALL_FAILURE:
+ /*
+ * I am not sure this is the right thing to do here
+ * but the code always set selected_fib before
+ * this assignment was moved here.
+ */
+ dest->selected_fib = re;
+
zsend_route_notify_owner(re->type, re->instance, re->vrf_id,
p, ZAPI_ROUTE_FAIL_INSTALL);
zlog_warn("%u:%s: Route install failed", re->vrf_id,
prefix2str(p, buf, sizeof(buf)));
break;
case SOUTHBOUND_DELETE_SUCCESS:
+ /*
+ * The case where selected_fib is not re is
+ * when we have received a system route
+ * that is overriding our installed route
+ * as such we should leave the selected_fib
+ * pointer alone
+ */
+ if (dest->selected_fib == re)
+ dest->selected_fib = NULL;
for (ALL_NEXTHOPS(re->nexthop, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
break;
case SOUTHBOUND_DELETE_FAILURE:
+ /*
+ * Should we set this to NULL if the
+ * delete fails?
+ */
+ dest->selected_fib = NULL;
zlog_warn("%u:%s: Route Deletion failure", re->vrf_id,
prefix2str(p, buf, sizeof(buf)));
break;
* the kernel.
*/
hook_call(rib_update, rn, "installing in kernel");
- kernel_route_rib(p, src_p, old, re);
+ kernel_route_rib(rn, p, src_p, old, re);
zvrf->installs++;
return;
* the kernel.
*/
hook_call(rib_update, rn, "uninstalling from kernel");
- kernel_route_rib(p, src_p, re, NULL);
+ kernel_route_rib(rn, p, src_p, re, NULL);
zvrf->removals++;
return;
static void rib_uninstall(struct route_node *rn, struct route_entry *re)
{
rib_table_info_t *info = srcdest_rnode_table_info(rn);
+ rib_dest_t *dest = rib_dest_from_rnode(rn);
- if (CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB)) {
+ if (dest && dest->selected_fib == re) {
if (info->safi == SAFI_UNICAST)
hook_call(rib_update, rn, "rib_uninstall");
/* If labeled-unicast route, uninstall transit LSP. */
if (zebra_rib_labeled_unicast(re))
zebra_mpls_lsp_uninstall(info->zvrf, rn, re);
-
- UNSET_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB);
}
if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) {
static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn,
struct route_entry *new)
{
+ rib_dest_t *dest = rib_dest_from_rnode(rn);
+
hook_call(rib_update, rn, "new route selected");
/* Update real nexthop. This may actually determine if nexthop is active
return;
}
- SET_FLAG(new->status, ROUTE_ENTRY_SELECTED_FIB);
if (IS_ZEBRA_DEBUG_RIB) {
char buf[SRCDEST2STR_BUFFER];
srcdest_rnode2str(rn, buf, sizeof(buf));
if (!RIB_SYSTEM_ROUTE(new))
rib_install_kernel(rn, new, NULL);
+ else
+ dest->selected_fib = new;
UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED);
}
static void rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn,
struct route_entry *old)
{
+ rib_dest_t *dest = rib_dest_from_rnode(rn);
hook_call(rib_update, rn, "removing existing route");
/* Uninstall from kernel. */
if (!RIB_SYSTEM_ROUTE(old))
rib_uninstall_kernel(rn, old);
-
- UNSET_FLAG(old->status, ROUTE_ENTRY_SELECTED_FIB);
+ else {
+ /*
+ * We are setting this to NULL here
+ * because that is what we traditionally
+ * have been doing. I am not positive
+ * that this is the right thing to do
+ * but let's leave the code alone
+ * for the RIB_SYSTEM_ROUTE case
+ */
+ dest->selected_fib = NULL;
+ }
/* Update nexthop for route, reset changed flag. */
nexthop_active_update(rn, old, 1);
{
struct nexthop *nexthop = NULL;
int nh_active = 0;
- int installed = 1;
+ rib_dest_t *dest = rib_dest_from_rnode(rn);
/*
* We have to install or update if a new route has been selected or
zebra_mpls_lsp_install(zvrf, rn, new);
rib_install_kernel(rn, new, old);
+ } else {
+ /*
+ * We do not need to install the
+ * selected route because it
+ * is already isntalled by
+ * the system( ie not us )
+ * so just mark it as winning
+ * we do need to ensure that
+ * if we uninstall a route
+ * from ourselves we don't
+ * over write this pointer
+ */
+ dest->selected_fib = NULL;
}
-
/* If install succeeded or system route, cleanup flags
* for prior route. */
- if (installed && new != old) {
+ if (new != old) {
if (RIB_SYSTEM_ROUTE(new)) {
if (!RIB_SYSTEM_ROUTE(old))
rib_uninstall_kernel(rn, old);
NEXTHOP_FLAG_FIB);
}
}
-
- /* Update for redistribution. */
- if (installed)
- SET_FLAG(new->status, ROUTE_ENTRY_SELECTED_FIB);
}
/*
* failed, we
* may need to uninstall and delete for redistribution.
*/
- if (!nh_active || !installed) {
+ if (!nh_active) {
if (IS_ZEBRA_DEBUG_RIB) {
char buf[SRCDEST2STR_BUFFER];
srcdest_rnode2str(rn, buf, sizeof(buf));
if (!RIB_SYSTEM_ROUTE(old))
rib_uninstall_kernel(rn, old);
- UNSET_FLAG(new->status, ROUTE_ENTRY_SELECTED_FIB);
+ else
+ dest->selected_fib = NULL;
}
} else {
/*
* to add routes.
*/
if (!RIB_SYSTEM_ROUTE(new)) {
- int in_fib = 0;
+ bool in_fib = false;
for (ALL_NEXTHOPS(new->nexthop, nexthop))
if (CHECK_FLAG(nexthop->flags,
NEXTHOP_FLAG_FIB)) {
- in_fib = 1;
+ in_fib = true;
break;
}
if (!in_fib)
/* Update prior route. */
if (new != old) {
- UNSET_FLAG(old->status, ROUTE_ENTRY_SELECTED_FIB);
-
/* Set real nexthop. */
nexthop_active_update(rn, old, 1);
UNSET_FLAG(old->status, ROUTE_ENTRY_CHANGED);
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug("%u:%s: Processing rn %p", vrf_id, buf, rn);
+ /*
+ * we can have rn's that have a NULL info pointer
+ * (dest). As such let's not let the deref happen
+ * additionally we know RNODE_FOREACH_RE_SAFE
+ * will not iterate so we are ok.
+ */
+ if (dest)
+ old_fib = dest->selected_fib;
+
RNODE_FOREACH_RE_SAFE (rn, re, next) {
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug(
assert(old_selected == NULL);
old_selected = re;
}
- /* Currently in fib */
- if (CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB)) {
- assert(old_fib == NULL);
- old_fib = re;
- }
/* Skip deleted entries from selection */
if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
{
struct route_table *table;
struct route_node *rn;
- struct route_entry *re;
unsigned changed = 0;
+ rib_dest_t *dest;
if (NULL == (table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id))) {
zlog_err("%s: zebra_vrf_table() returned NULL", __func__);
/* Unlock node. */
route_unlock_node(rn);
+ dest = rib_dest_from_rnode(rn);
/* Check all RE entries. In case any changes have to be done, requeue
* the RN into RIBQ head. If the routing message about the new connected
* route (generated by the IP address we are going to assign very soon)
* revalidation
* of the rest of the RE.
*/
- RNODE_FOREACH_RE (rn, re) {
- if (CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB)
- && !RIB_SYSTEM_ROUTE(re)) {
- changed = 1;
- if (IS_ZEBRA_DEBUG_RIB) {
- char buf[PREFIX_STRLEN];
- zlog_debug(
- "%u:%s: freeing way for connected prefix",
- re->vrf_id,
- prefix2str(&rn->p, buf, sizeof(buf)));
- route_entry_dump(&rn->p, NULL, re);
- }
- rib_uninstall(rn, re);
+ if (dest->selected_fib && !RIB_SYSTEM_ROUTE(dest->selected_fib)) {
+ changed = 1;
+ if (IS_ZEBRA_DEBUG_RIB) {
+ char buf[PREFIX_STRLEN];
+
+ zlog_debug("%u:%s: freeing way for connected prefix",
+ dest->selected_fib->vrf_id,
+ prefix2str(&rn->p, buf, sizeof(buf)));
+ route_entry_dump(&rn->p, NULL, dest->selected_fib);
}
+ rib_uninstall(rn, dest->selected_fib);
}
if (changed)
rib_queue_add(rn);
void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
u_short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- u_int32_t table_id, u_int32_t metric, bool fromkernel)
+ u_int32_t table_id, u_int32_t metric, bool fromkernel,
+ struct ethaddr *rmac)
{
struct route_table *table;
struct route_node *rn;
struct route_entry *same = NULL;
struct nexthop *rtnh;
char buf2[INET6_ADDRSTRLEN];
+ rib_dest_t *dest;
assert(!src_p || afi == AFI_IP6);
return;
}
+ dest = rib_dest_from_rnode(rn);
+ fib = dest->selected_fib;
+
/* Lookup same type route. */
RNODE_FOREACH_RE (rn, re) {
if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED))
continue;
- if (CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB))
- fib = re;
-
if (re->type != type)
continue;
if (re->instance != instance)
UNSET_FLAG(rtnh->flags,
NEXTHOP_FLAG_FIB);
- UNSET_FLAG(fib->status,
- ROUTE_ENTRY_SELECTED_FIB);
+ /*
+ * This is a non FRR route
+ * as such we should mark
+ * it as deleted
+ */
+ dest->selected_fib = NULL;
} else {
/* This means someone else, other than Zebra,
* has deleted
return;
}
+
+ if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) {
+ struct nexthop *tmp_nh;
+
+ for (ALL_NEXTHOPS(re->nexthop, tmp_nh)) {
+ struct ipaddr vtep_ip;
+
+ memset(&vtep_ip, 0, sizeof(struct ipaddr));
+ vtep_ip.ipa_type = IPADDR_V4;
+ memcpy(&(vtep_ip.ipaddr_v4),
+ &(tmp_nh->gate.ipv4),
+ sizeof(struct in_addr));
+ zebra_vxlan_evpn_vrf_route_del(re->vrf_id, rmac,
+ &vtep_ip, p);
+ }
+ }
rib_delnode(rn, same);
}
}
-int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, u_short instance,
- int flags, struct prefix *p, struct prefix_ipv6 *src_p,
- const struct nexthop *nh, u_int32_t table_id, u_int32_t metric,
- u_int32_t mtu, uint8_t distance)
+int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, vrf_id_t nh_vrf_id,
+ int type, u_short instance, int flags, struct prefix *p,
+ struct prefix_ipv6 *src_p, const struct nexthop *nh,
+ u_int32_t table_id, u_int32_t metric,
+ u_int32_t mtu, uint8_t distance, route_tag_t tag)
{
struct route_entry *re;
struct nexthop *nexthop;
re->mtu = mtu;
re->table = table_id;
re->vrf_id = vrf_id;
+ re->nh_vrf_id = nh_vrf_id;
re->nexthop_num = 0;
re->uptime = time(NULL);
+ re->tag = tag;
/* Add nexthop. */
nexthop = nexthop_new();
{
struct route_node *rn;
rib_table_info_t *info;
- struct route_entry *re;
+ rib_dest_t *dest;
if (!table)
return;
info = table->info;
- for (rn = route_top(table); rn; rn = srcdest_route_next(rn))
- RNODE_FOREACH_RE (rn, re) {
- if (!CHECK_FLAG(re->status, ROUTE_ENTRY_SELECTED_FIB))
- continue;
+ for (rn = route_top(table); rn; rn = srcdest_route_next(rn)) {
+ dest = rib_dest_from_rnode(rn);
+ if (dest && dest->selected_fib) {
if (info->safi == SAFI_UNICAST)
hook_call(rib_update, rn, NULL);
- if (!RIB_SYSTEM_ROUTE(re))
- rib_uninstall_kernel(rn, re);
+ if (!RIB_SYSTEM_ROUTE(dest->selected_fib))
+ rib_uninstall_kernel(rn, dest->selected_fib);
}
+ }
}
/* Routing information base initialize. */
state->type = re->type;
state->distance = re->distance;
state->metric = re->metric;
+ state->vrf_id = re->vrf_id;
+ state->nh_vrf_id = re->vrf_id;
route_entry_copy_nexthops(state, re->nexthop);
rnh->state = state;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, cmd, vrf_id);
+ zclient_create_header(s, cmd, vrf_id);
stream_putw(s, rn->p.family);
switch (rn->p.family) {
union g_addr src, *psrc;
if ((inet_pton(AF_INET6, arg, &src.ipv6) == 1)
- || (src.ipv4.s_addr && (inet_pton(AF_INET, arg, &src.ipv4) == 1))) {
+ || (inet_pton(AF_INET, arg, &src.ipv4) == 1)) {
psrc = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(union g_addr));
*psrc = src;
return psrc;
struct nh_rmap_obj nh_obj;
nh_obj.nexthop = nexthop;
- nh_obj.vrf_id = re->vrf_id;
+ nh_obj.vrf_id = re->nh_vrf_id;
nh_obj.source_protocol = re->type;
nh_obj.metric = re->metric;
nh_obj.tag = re->tag;
#ifndef __ZEBRA_ROUTEMAP_H__
#define __ZEBRA_ROUTEMAP_H__
+extern void zebra_route_map_init(void);
extern void zebra_routemap_config_write_protocol(struct vty *vty);
extern char *zebra_get_import_table_route_map(afi_t afi, uint32_t table);
extern void zebra_add_import_table_route_map(afi_t afi, const char *rmap_name,
nh_p.family = AF_INET;
nh_p.prefixlen = IPV4_MAX_BITLEN;
nh_p.u.prefix4 = si->addr.ipv4;
- zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn);
+ zebra_register_rnh_static_nh(si->nh_vrf_id, &nh_p, rn);
break;
case STATIC_IPV4_GATEWAY_IFNAME:
nexthop = route_entry_nexthop_ipv4_ifindex_add(
nh_p.family = AF_INET6;
nh_p.prefixlen = IPV6_MAX_BITLEN;
nh_p.u.prefix6 = si->addr.ipv6;
- zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn);
+ zebra_register_rnh_static_nh(si->nh_vrf_id, &nh_p, rn);
break;
case STATIC_IPV6_GATEWAY_IFNAME:
nexthop = route_entry_nexthop_ipv6_ifindex_add(
*/
if (si->type == STATIC_IPV4_GATEWAY
|| si->type == STATIC_IPV6_GATEWAY)
- zebra_evaluate_rnh(si->vrf_id, nh_p.family, 1,
+ zebra_evaluate_rnh(si->nh_vrf_id, nh_p.family, 1,
RNH_NEXTHOP_TYPE, &nh_p);
else
rib_queue_add(rn);
re->metric = 0;
re->mtu = 0;
re->vrf_id = si->vrf_id;
+ re->nh_vrf_id = si->nh_vrf_id;
re->table =
- si->vrf_id
+ (si->vrf_id != VRF_DEFAULT)
? (zebra_vrf_lookup_by_id(si->vrf_id))->table_id
: zebrad.rtm_table_default;
re->nexthop_num = 0;
nh_p.family = AF_INET;
nh_p.prefixlen = IPV4_MAX_BITLEN;
nh_p.u.prefix4 = si->addr.ipv4;
- zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn);
+ zebra_register_rnh_static_nh(si->nh_vrf_id, &nh_p, rn);
break;
case STATIC_IPV4_GATEWAY_IFNAME:
nexthop = route_entry_nexthop_ipv4_ifindex_add(
nh_p.family = AF_INET6;
nh_p.prefixlen = IPV6_MAX_BITLEN;
nh_p.u.prefix6 = si->addr.ipv6;
- zebra_register_rnh_static_nh(si->vrf_id, &nh_p, rn);
+ zebra_register_rnh_static_nh(si->nh_vrf_id, &nh_p, rn);
break;
case STATIC_IPV6_GATEWAY_IFNAME:
nexthop = route_entry_nexthop_ipv6_ifindex_add(
if (si->type == STATIC_IPV4_GATEWAY
|| si->type == STATIC_IPV6_GATEWAY) {
rib_addnode(rn, re, 0);
- zebra_evaluate_rnh(si->vrf_id, nh_p.family, 1,
+ zebra_evaluate_rnh(si->nh_vrf_id, nh_p.family, 1,
RNH_NEXTHOP_TYPE, &nh_p);
} else
rib_addnode(rn, re, 1);
}
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) {
+ rib_dest_t *dest = rib_dest_from_rnode(rn);
+
/* If there are other active nexthops, do an update. */
if (re->nexthop_active_num > 1) {
/* Update route in kernel if it's in fib */
- if (CHECK_FLAG(re->status,
- ROUTE_ENTRY_SELECTED_FIB))
+ if (dest->selected_fib)
rib_install_kernel(rn, re, re);
/* Update redistribution if it's selected */
if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
p, (struct prefix *)src_p, re);
/* Remove from kernel if fib route becomes
* inactive */
- if (CHECK_FLAG(re->status,
- ROUTE_ENTRY_SELECTED_FIB))
+ if (dest->selected_fib)
rib_uninstall_kernel(rn, re);
}
}
struct prefix_ipv6 *src_p, union g_addr *gate,
const char *ifname, enum static_blackhole_type bh_type,
route_tag_t tag, u_char distance, struct zebra_vrf *zvrf,
+ struct zebra_vrf *nh_zvrf,
struct static_nh_label *snh_label)
{
struct route_node *rn;
si->bh_type = bh_type;
si->tag = tag;
si->vrf_id = zvrf_id(zvrf);
+ si->nh_vrf_id = zvrf_id(nh_zvrf);
+
if (ifname)
strlcpy(si->ifname, ifname, sizeof(si->ifname));
si->ifindex = IFINDEX_INTERNAL;
else {
struct interface *ifp;
- ifp = if_lookup_by_name(ifname, zvrf_id(zvrf));
+ ifp = if_lookup_by_name(ifname, zvrf_id(nh_zvrf));
if (ifp && ifp->ifindex != IFINDEX_INTERNAL) {
si->ifindex = ifp->ifindex;
static_install_route(afi, safi, p, src_p, si);
/* VRF identifier. */
vrf_id_t vrf_id;
+ vrf_id_t nh_vrf_id;
/* Administrative distance. */
u_char distance;
const char *ifname,
enum static_blackhole_type bh_type, route_tag_t tag,
u_char distance, struct zebra_vrf *zvrf,
+ struct zebra_vrf *nh_zvrf,
struct static_nh_label *snh_label);
extern int static_delete_route(afi_t, safi_t safi, u_char type,
route_table_finish(zvrf->rnh_table[afi]);
route_table_finish(zvrf->import_check_table[afi]);
}
+
+ /* cleanup evpn states for vrf */
+ zebra_vxlan_vrf_delete(zvrf);
+
list_delete_all_node(zvrf->rid_all_sorted_list);
list_delete_all_node(zvrf->rid_lo_sorted_list);
XFREE(MTYPE_ZEBRA_VRF, zvrf);
info->afi = afi;
info->safi = SAFI_UNICAST;
table->info = info;
+ table->cleanup = zebra_rtable_node_cleanup;
zvrf->other_table[afi][table_id] = table;
}
if (!zvrf)
continue;
- if (strcmp(zvrf_name(zvrf), VRF_DEFAULT_NAME)) {
+ if (vrf->vrf_id != VRF_DEFAULT)
vty_out(vty, "vrf %s\n", zvrf_name(zvrf));
+
+ static_config(vty, zvrf, AFI_IP, SAFI_UNICAST, "ip route");
+ static_config(vty, zvrf, AFI_IP, SAFI_MULTICAST, "ip mroute");
+ static_config(vty, zvrf, AFI_IP6, SAFI_UNICAST, "ipv6 route");
+
+ if (vrf->vrf_id != VRF_DEFAULT && zvrf->l3vni)
+ vty_out(vty, " vni %u\n", zvrf->l3vni);
+
+ if (vrf->vrf_id != VRF_DEFAULT)
vty_out(vty, "!\n");
- }
}
return 0;
}
#include <zebra/zebra_ns.h>
#include <zebra/zebra_pw.h>
+#include <lib/vxlan.h>
/* MPLS (Segment Routing) global block */
typedef struct mpls_srgb_t_ {
*/
int advertise_gw_macip;
+ /* l3-vni info */
+ vni_t l3vni;
+
/* Route Installs */
uint64_t installs;
uint64_t removals;
#include "zebra/zebra_vty_clippy.c"
#endif
#include "zebra/zserv.h"
+#include "zebra/router-id.h"
+#include "zebra/ipforward.h"
+#include "zebra/zebra_vxlan_private.h"
extern int allow_delete;
static void vty_show_ip_route_summary_prefix(struct vty *vty,
struct route_table *table);
+/*
+ * special macro to allow us to get the correct zebra_vrf
+ */
+#define ZEBRA_DECLVAR_CONTEXT(A, B) \
+ struct vrf *A = VTY_GET_CONTEXT(vrf); \
+ struct zebra_vrf *B = \
+ (vrf) ? vrf->info : NULL; \
+
/* VNI range as per RFC 7432 */
#define CMD_VNI_RANGE "(1-16777215)"
/* General function for static route. */
-static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi,
- const char *negate, const char *dest_str,
- const char *mask_str, const char *src_str,
- const char *gate_str, const char *ifname,
- const char *flag_str, const char *tag_str,
- const char *distance_str, const char *vrf_id_str,
- const char *label_str)
+static int zebra_static_route_leak(struct vty *vty,
+ struct zebra_vrf *zvrf,
+ struct zebra_vrf *nh_zvrf,
+ afi_t afi, safi_t safi,
+ const char *negate, const char *dest_str,
+ const char *mask_str, const char *src_str,
+ const char *gate_str, const char *ifname,
+ const char *flag_str, const char *tag_str,
+ const char *distance_str,
+ const char *label_str)
{
int ret;
u_char distance;
struct in_addr mask;
enum static_blackhole_type bh_type = 0;
route_tag_t tag = 0;
- struct zebra_vrf *zvrf;
u_char type;
struct static_nh_label snh_label;
if (tag_str)
tag = strtoul(tag_str, NULL, 10);
- /* VRF id */
- zvrf = zebra_vrf_lookup_by_name(vrf_id_str);
-
- if (!zvrf) {
- vty_out(vty, "%% vrf %s is not defined\n", vrf_id_str);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
/* Labels */
memset(&snh_label, 0, sizeof(struct static_nh_label));
if (label_str) {
if (!negate)
static_add_route(afi, safi, type, &p, src_p, gatep, ifname,
- bh_type, tag, distance, zvrf, &snh_label);
+ bh_type, tag, distance, zvrf, nh_zvrf,
+ &snh_label);
else
static_delete_route(afi, safi, type, &p, src_p, gatep, ifname,
tag, distance, zvrf, &snh_label);
return CMD_SUCCESS;
}
+static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi,
+ const char *negate, const char *dest_str,
+ const char *mask_str, const char *src_str,
+ const char *gate_str, const char *ifname,
+ const char *flag_str, const char *tag_str,
+ const char *distance_str, const char *vrf_id_str,
+ const char *label_str)
+{
+ struct zebra_vrf *zvrf;
+
+ /* VRF id */
+ zvrf = zebra_vrf_lookup_by_name(vrf_id_str);
+
+ if (!zvrf) {
+ vty_out(vty, "%% vrf %s is not defined\n", vrf_id_str);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return zebra_static_route_leak(vty, zvrf, zvrf, afi, safi,
+ negate, dest_str, mask_str, src_str,
+ gate_str, ifname, flag_str, tag_str,
+ distance_str, label_str);
+}
+
+
/* Static unicast routes for multicast RPF lookup. */
DEFPY (ip_mroute_dist,
ip_mroute_dist_cmd,
tag_str, distance_str, vrf, label);
}
+DEFPY(ip_route_blackhole_vrf,
+ ip_route_blackhole_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ MPLS_LABEL_HELPSTR)
+{
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+ struct zebra_vrf *zvrf = vrf->info;
+
+ return zebra_static_route_leak(vty, zvrf, zvrf,
+ AFI_IP, SAFI_UNICAST, no, prefix,
+ mask_str, NULL, NULL, NULL, flag,
+ tag_str, distance_str, label);
+}
+
DEFPY(ip_route_address_interface,
ip_route_address_interface_cmd,
"[no] ip route\
|(1-255)$distance \
|vrf NAME \
|label WORD \
+ |nexthop-vrf NAME \
}]",
NO_STR IP_STR
"Establish static routes\n"
"Tag value\n"
"Distance value for this route\n"
VRF_CMD_HELP_STR
- MPLS_LABEL_HELPSTR)
+ MPLS_LABEL_HELPSTR
+ VRF_CMD_HELP_STR)
{
+ struct zebra_vrf *zvrf;
+ struct zebra_vrf *nh_zvrf;
+
const char *flag = NULL;
if (ifname && !strncasecmp(ifname, "Null0", 5)) {
flag = "Null0";
ifname = NULL;
}
- return zebra_static_route(vty, AFI_IP, SAFI_UNICAST, no, prefix,
- mask_str, NULL, gate_str, ifname, flag,
- tag_str, distance_str, vrf, label);
+
+ zvrf = zebra_vrf_lookup_by_name(vrf);
+ if (!zvrf) {
+ vty_out(vty, "%% vrf %s is not defined\n",
+ vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (nexthop_vrf)
+ nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+ else
+ nh_zvrf = zvrf;
+
+ if (!nh_zvrf) {
+ vty_out(vty, "%% nexthop vrf %s is not defined\n",
+ nexthop_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return zebra_static_route_leak(vty, zvrf, nh_zvrf,
+ AFI_IP, SAFI_UNICAST, no, prefix,
+ mask_str, NULL, gate_str, ifname, flag,
+ tag_str, distance_str, label);
+}
+
+DEFPY(ip_route_address_interface_vrf,
+ ip_route_address_interface_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ A.B.C.D$gate \
+ INTERFACE$ifname \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |nexthop-vrf NAME \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "IP gateway address\n"
+ "IP gateway interface name. Specify 'Null0' (case-insensitive) for a \
+ null route.\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ MPLS_LABEL_HELPSTR
+ VRF_CMD_HELP_STR)
+{
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+ const char *flag = NULL;
+ struct zebra_vrf *zvrf = vrf->info;
+ struct zebra_vrf *nh_zvrf;
+
+ if (ifname && !strncasecmp(ifname, "Null0", 5)) {
+ flag = "Null0";
+ ifname = NULL;
+ }
+
+ nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+ if (!nh_zvrf) {
+ vty_out(vty, "%% nexthop vrf %s is not defined\n",
+ nexthop_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return zebra_static_route_leak(vty, zvrf, nh_zvrf,
+ AFI_IP, SAFI_UNICAST, no, prefix,
+ mask_str, NULL, gate_str, ifname, flag,
+ tag_str, distance_str, label);
}
DEFPY(ip_route,
|(1-255)$distance \
|vrf NAME \
|label WORD \
+ |nexthop-vrf NAME \
}]",
NO_STR IP_STR
"Establish static routes\n"
"Tag value\n"
"Distance value for this route\n"
VRF_CMD_HELP_STR
- MPLS_LABEL_HELPSTR)
+ MPLS_LABEL_HELPSTR
+ VRF_CMD_HELP_STR)
{
+ struct zebra_vrf *zvrf;
+ struct zebra_vrf *nh_zvrf;
const char *flag = NULL;
+
if (ifname && !strncasecmp(ifname, "Null0", 5)) {
flag = "Null0";
ifname = NULL;
}
- return zebra_static_route(vty, AFI_IP, SAFI_UNICAST, no, prefix,
- mask_str, NULL, gate_str, ifname, flag,
- tag_str, distance_str, vrf, label);
+
+ zvrf = zebra_vrf_lookup_by_name(vrf);
+ if (!zvrf) {
+ vty_out(vty, "%% vrf %s is not defined\n",
+ vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (nexthop_vrf)
+ nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+ else
+ nh_zvrf = zvrf;
+
+ if (!nh_zvrf) {
+ vty_out(vty, "%% nexthop vrf %s is not defined\n",
+ nexthop_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+
+ return zebra_static_route_leak(vty, zvrf, nh_zvrf,
+ AFI_IP, SAFI_UNICAST, no, prefix,
+ mask_str, NULL, gate_str, ifname, flag,
+ tag_str, distance_str, label);
+}
+
+DEFPY(ip_route_vrf,
+ ip_route_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <A.B.C.D$gate|INTERFACE$ifname> \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |nexthop-vrf NAME \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "IP gateway address\n"
+ "IP gateway interface name\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ MPLS_LABEL_HELPSTR
+ VRF_CMD_HELP_STR)
+{
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+ struct zebra_vrf *zvrf = vrf->info;
+ struct zebra_vrf *nh_zvrf;
+
+ const char *flag = NULL;
+ if (ifname && !strncasecmp(ifname, "Null0", 5)) {
+ flag = "Null0";
+ ifname = NULL;
+ }
+
+ nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+ if (!nh_zvrf) {
+ vty_out(vty, "%% nexthop vrf %s is not defined\n",
+ nexthop_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return zebra_static_route_leak(vty, zvrf, nh_zvrf,
+ AFI_IP, SAFI_UNICAST, no, prefix,
+ mask_str, NULL, gate_str, ifname, flag,
+ tag_str, distance_str, label);
}
/* New RIB. Detailed information for IPv4 route. */
vty_out(vty, "\"");
vty_out(vty, ", distance %u, metric %u", re->distance,
re->metric);
- if (re->tag)
+ if (re->tag) {
vty_out(vty, ", tag %u", re->tag);
+#if defined(SUPPORT_REALMS)
+ if (re->tag > 0 && re->tag <= 255)
+ vty_out(vty, "(realm)");
+#endif
+ }
if (re->mtu)
vty_out(vty, ", mtu %u", re->mtu);
if (re->vrf_id != VRF_DEFAULT) {
if (nexthop->ifindex)
vty_out(vty, ", via %s",
ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ re->nh_vrf_id));
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
if (nexthop->ifindex)
vty_out(vty, ", via %s",
ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ re->nh_vrf_id));
break;
case NEXTHOP_TYPE_IFINDEX:
vty_out(vty, " directly connected, %s",
ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ re->nh_vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
vty_out(vty, " unreachable");
default:
break;
}
+
+ if (re->vrf_id != re->nh_vrf_id) {
+ struct vrf *vrf =
+ vrf_lookup_by_id(re->nh_vrf_id);
+
+ vty_out(vty, "(vrf %s)", vrf->name);
+ }
+
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
vty_out(vty, " (duplicate nexthop removed)");
json_object_string_add(
json_nexthop, "interfaceName",
ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ re->nh_vrf_id));
}
break;
case NEXTHOP_TYPE_IPV6:
json_object_string_add(
json_nexthop, "interfaceName",
ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ re->nh_vrf_id));
}
break;
json_object_string_add(
json_nexthop, "interfaceName",
ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ re->nh_vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
json_object_boolean_true_add(json_nexthop,
break;
}
+ if (re->nh_vrf_id != re->vrf_id) {
+ struct vrf *vrf =
+ vrf_lookup_by_id(re->nh_vrf_id);
+
+ json_object_string_add(json_nexthop,
+ "vrf",
+ vrf->name);
+ }
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
json_object_boolean_true_add(json_nexthop,
"duplicate");
if (nexthop->ifindex)
vty_out(vty, ", %s",
ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ re->nh_vrf_id));
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
if (nexthop->ifindex)
vty_out(vty, ", %s",
ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ re->nh_vrf_id));
break;
case NEXTHOP_TYPE_IFINDEX:
vty_out(vty, " is directly connected, %s",
- ifindex2ifname(nexthop->ifindex, re->vrf_id));
+ ifindex2ifname(nexthop->ifindex,
+ re->nh_vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
vty_out(vty, " unreachable");
default:
break;
}
+
+ if (re->nh_vrf_id != re->vrf_id) {
+ struct vrf *vrf =
+ vrf_lookup_by_id(re->nh_vrf_id);
+
+ vty_out(vty, "(vrf %s)", vrf->name);
+ }
+
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
vty_out(vty, " inactive");
u_short ospf_instance_id)
{
struct route_table *table;
+ rib_dest_t *dest;
struct route_node *rn;
struct route_entry *re;
int first = 1;
/* Show all routes. */
for (rn = route_top(table); rn; rn = route_next(rn)) {
+ dest = rib_dest_from_rnode(rn);
+
RNODE_FOREACH_RE (rn, re) {
if (use_fib
- && !CHECK_FLAG(re->status,
- ROUTE_ENTRY_SELECTED_FIB))
+ && re != dest->selected_fib)
continue;
if (tag && re->tag != tag)
return CMD_SUCCESS;
zebra_rnh_ip_default_route = 1;
- zebra_evaluate_rnh(0, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL);
+ zebra_evaluate_rnh(VRF_DEFAULT, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL);
return CMD_SUCCESS;
}
return CMD_SUCCESS;
zebra_rnh_ip_default_route = 0;
- zebra_evaluate_rnh(0, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL);
+ zebra_evaluate_rnh(VRF_DEFAULT, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL);
return CMD_SUCCESS;
}
return CMD_SUCCESS;
zebra_rnh_ipv6_default_route = 1;
- zebra_evaluate_rnh(0, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL);
+ zebra_evaluate_rnh(VRF_DEFAULT, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL);
return CMD_SUCCESS;
}
return CMD_SUCCESS;
zebra_rnh_ipv6_default_route = 0;
- zebra_evaluate_rnh(0, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL);
+ zebra_evaluate_rnh(VRF_DEFAULT, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL);
return CMD_SUCCESS;
}
}
/* Write static route configuration. */
-static int static_config(struct vty *vty, afi_t afi, safi_t safi,
- const char *cmd)
+int static_config(struct vty *vty, struct zebra_vrf *zvrf,
+ afi_t afi, safi_t safi, const char *cmd)
{
+ char spacing[100];
struct route_node *rn;
struct static_route *si;
struct route_table *stable;
- struct vrf *vrf;
- struct zebra_vrf *zvrf;
char buf[SRCDEST2STR_BUFFER];
int write = 0;
- RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
- if (!(zvrf = vrf->info))
- continue;
- if ((stable = zvrf->stable[afi][safi]) == NULL)
- continue;
+ if ((stable = zvrf->stable[afi][safi]) == NULL)
+ return write;
- for (rn = route_top(stable); rn; rn = srcdest_route_next(rn))
- for (si = rn->info; si; si = si->next) {
- vty_out(vty, "%s %s", cmd,
- srcdest_rnode2str(rn, buf, sizeof buf));
+ sprintf(spacing, "%s%s",
+ (zvrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ",
+ cmd);
- switch (si->type) {
- case STATIC_IPV4_GATEWAY:
- vty_out(vty, " %s",
- inet_ntoa(si->addr.ipv4));
- break;
- case STATIC_IPV6_GATEWAY:
- vty_out(vty, " %s",
- inet_ntop(AF_INET6,
- &si->addr.ipv6, buf,
- sizeof buf));
- break;
- case STATIC_IFNAME:
- vty_out(vty, " %s", si->ifname);
- break;
- case STATIC_BLACKHOLE:
- switch (si->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;
- }
+ for (rn = route_top(stable); rn; rn = srcdest_route_next(rn))
+ for (si = rn->info; si; si = si->next) {
+ vty_out(vty, "%s %s", spacing,
+ srcdest_rnode2str(rn, buf, sizeof buf));
+
+ switch (si->type) {
+ case STATIC_IPV4_GATEWAY:
+ vty_out(vty, " %s",
+ inet_ntoa(si->addr.ipv4));
+ break;
+ case STATIC_IPV6_GATEWAY:
+ vty_out(vty, " %s",
+ inet_ntop(AF_INET6,
+ &si->addr.ipv6, buf,
+ sizeof buf));
+ break;
+ case STATIC_IFNAME:
+ vty_out(vty, " %s", si->ifname);
+ break;
+ case STATIC_BLACKHOLE:
+ switch (si->bh_type) {
+ case STATIC_BLACKHOLE_DROP:
+ vty_out(vty, " blackhole");
break;
- case STATIC_IPV4_GATEWAY_IFNAME:
- vty_out(vty, " %s %s",
- inet_ntop(AF_INET,
- &si->addr.ipv4, buf,
- sizeof buf),
- si->ifname);
+ case STATIC_BLACKHOLE_NULL:
+ vty_out(vty, " Null0");
break;
- case STATIC_IPV6_GATEWAY_IFNAME:
- vty_out(vty, " %s %s",
- inet_ntop(AF_INET6,
- &si->addr.ipv6, buf,
- sizeof buf),
- si->ifname);
+ case STATIC_BLACKHOLE_REJECT:
+ vty_out(vty, " reject");
break;
}
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ vty_out(vty, " %s %s",
+ inet_ntop(AF_INET,
+ &si->addr.ipv4, buf,
+ sizeof buf),
+ si->ifname);
+ break;
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ vty_out(vty, " %s %s",
+ inet_ntop(AF_INET6,
+ &si->addr.ipv6, buf,
+ sizeof buf),
+ si->ifname);
+ break;
+ }
- if (si->tag)
- vty_out(vty, " tag %" ROUTE_TAG_PRI,
- si->tag);
+ if (si->tag)
+ vty_out(vty, " tag %" ROUTE_TAG_PRI,
+ si->tag);
- if (si->distance
- != ZEBRA_STATIC_DISTANCE_DEFAULT)
- vty_out(vty, " %d", si->distance);
+ if (si->distance
+ != ZEBRA_STATIC_DISTANCE_DEFAULT)
+ vty_out(vty, " %d", si->distance);
- if (si->vrf_id != VRF_DEFAULT)
- vty_out(vty, " vrf %s",
- zvrf_name(zvrf));
+ if (si->nh_vrf_id != si->vrf_id) {
+ struct vrf *vrf;
- /* Label information */
- if (si->snh_label.num_labels)
- vty_out(vty, " label %s",
- mpls_label2str(
- si->snh_label
- .num_labels,
- si->snh_label.label,
- buf, sizeof buf, 0));
+ vrf = vrf_lookup_by_id(si->nh_vrf_id);
+ vty_out(vty, " nexthop-vrf %s",
+ (vrf) ? vrf->name : "Unknown");
+ }
- vty_out(vty, "\n");
+ /* Label information */
+ if (si->snh_label.num_labels)
+ vty_out(vty, " label %s",
+ mpls_label2str(si->snh_label.num_labels,
+ si->snh_label.label,
+ buf, sizeof buf, 0));
- write = 1;
- }
- }
+ vty_out(vty, "\n");
+
+ write = 1;
+ }
return write;
}
tag_str, distance_str, vrf, label);
}
+DEFPY(ipv6_route_blackhole_vrf,
+ ipv6_route_blackhole_vrf_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <Null0|reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ }]",
+ NO_STR
+ IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "Null interface\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n"
+ MPLS_LABEL_HELPSTR)
+{
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+ struct zebra_vrf *zvrf = vrf->info;
+
+ return zebra_static_route_leak(vty, zvrf, zvrf,
+ AFI_IP6, SAFI_UNICAST, no, prefix_str,
+ NULL, from_str, NULL, NULL, flag,
+ tag_str, distance_str, label);
+}
+
DEFPY(ipv6_route_address_interface,
ipv6_route_address_interface_cmd,
"[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
|(1-255)$distance \
|vrf NAME \
|label WORD \
+ |nexthop-vrf NAME \
}]",
NO_STR
IPV6_STR
"Tag value\n"
"Distance value for this prefix\n"
VRF_CMD_HELP_STR
- MPLS_LABEL_HELPSTR)
+ MPLS_LABEL_HELPSTR
+ VRF_CMD_HELP_STR)
{
- return zebra_static_route(vty, AFI_IP6, SAFI_UNICAST, no, prefix_str,
- NULL, from_str, gate_str, ifname, NULL,
- tag_str, distance_str, vrf, label);
+ struct zebra_vrf *zvrf;
+ struct zebra_vrf *nh_zvrf;
+
+ nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+ if (!nh_zvrf) {
+ vty_out(vty, "%% nexthop vrf %s is not defined\n",
+ nexthop_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ zvrf = zebra_vrf_lookup_by_name(vrf);
+ if (!nh_zvrf) {
+ vty_out(vty, "%% nexthop vrf %s is not defined\n",
+ vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return zebra_static_route_leak(vty, zvrf, nh_zvrf,
+ AFI_IP6, SAFI_UNICAST, no, prefix_str,
+ NULL, from_str, gate_str, ifname, NULL,
+ tag_str, distance_str, label);
+}
+
+DEFPY(ipv6_route_address_interface_vrf,
+ ipv6_route_address_interface_vrf_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ X:X::X:X$gate \
+ INTERFACE$ifname \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |nexthop-vrf NAME \
+ }]",
+ NO_STR
+ IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "IPv6 gateway address\n"
+ "IPv6 gateway interface name\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n"
+ MPLS_LABEL_HELPSTR
+ VRF_CMD_HELP_STR)
+{
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+ struct zebra_vrf *zvrf = vrf->info;
+ struct zebra_vrf *nh_zvrf;
+
+ nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+ if (!nh_zvrf) {
+ vty_out(vty, "%% nexthop vrf %s is not defined\n",
+ nexthop_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return zebra_static_route_leak(vty, zvrf, nh_zvrf,
+ AFI_IP6, SAFI_UNICAST, no, prefix_str,
+ NULL, from_str, gate_str, ifname, NULL,
+ tag_str, distance_str, label);
}
DEFPY(ipv6_route,
|(1-255)$distance \
|vrf NAME \
|label WORD \
+ |nexthop-vrf NAME \
}]",
NO_STR
IPV6_STR
"Tag value\n"
"Distance value for this prefix\n"
VRF_CMD_HELP_STR
- MPLS_LABEL_HELPSTR)
+ MPLS_LABEL_HELPSTR
+ VRF_CMD_HELP_STR)
{
- return zebra_static_route(vty, AFI_IP6, SAFI_UNICAST, no, prefix_str,
- NULL, from_str, gate_str, ifname, NULL,
- tag_str, distance_str, vrf, label);
-}
+ struct zebra_vrf *zvrf;
+ struct zebra_vrf *nh_zvrf;
-/*
- * Show IPv6 mroute command.Used to dump
- * the Multicast routing table.
- */
-DEFUN (show_ipv6_mroute,
+ nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+ if (!nh_zvrf) {
+ vty_out(vty, "%% nexthop vrf %s is not defined\n",
+ nexthop_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ zvrf = zebra_vrf_lookup_by_name(vrf);
+ if (!nh_zvrf) {
+ vty_out(vty, "%% nexthop vrf %s is not defined\n",
+ vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return zebra_static_route_leak(vty, zvrf, nh_zvrf,
+ AFI_IP6, SAFI_UNICAST, no, prefix_str,
+ NULL, from_str, gate_str, ifname, NULL,
+ tag_str, distance_str, label);
+}
+
+DEFPY(ipv6_route_vrf,
+ ipv6_route_vrf_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <X:X::X:X$gate|INTERFACE$ifname> \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |nexthop-vrf NAME \
+ }]",
+ NO_STR
+ IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "IPv6 gateway address\n"
+ "IPv6 gateway interface name\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n"
+ MPLS_LABEL_HELPSTR
+ VRF_CMD_HELP_STR)
+{
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+ struct zebra_vrf *zvrf = vrf->info;
+ struct zebra_vrf *nh_zvrf;
+
+ nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+ if (!nh_zvrf) {
+ vty_out(vty, "%% nexthop vrf %s is not defined\n",
+ nexthop_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return zebra_static_route_leak(vty, zvrf, nh_zvrf,
+ AFI_IP6, SAFI_UNICAST, no, prefix_str,
+ NULL, from_str, gate_str, ifname, NULL,
+ tag_str, distance_str, label);
+}
+
+/*
+ * Show IPv6 mroute command.Used to dump
+ * the Multicast routing table.
+ */
+DEFUN (show_ipv6_mroute,
show_ipv6_mroute_cmd,
"show ipv6 mroute [vrf NAME]",
SHOW_STR
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (!(zvrf = vrf->info))
continue;
- if (!zvrf_id(zvrf))
+ if (zvrf_id(zvrf) == VRF_DEFAULT)
continue;
vty_out(vty, "vrf %s ", zvrf_name(zvrf));
return CMD_SUCCESS;
}
+DEFUN (vrf_vni_mapping,
+ vrf_vni_mapping_cmd,
+ "vni " CMD_VNI_RANGE,
+ "VNI\n"
+ "VNI-ID\n")
+{
+ int ret = 0;
+
+ ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
+ vni_t vni = strtoul(argv[1]->arg, NULL, 10);
+ char err[ERR_STR_SZ];
+
+ assert(vrf);
+ assert(zvrf);
+
+ ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
+ if (ret != 0) {
+ vty_out(vty, "%s\n", err);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_vrf_vni_mapping,
+ no_vrf_vni_mapping_cmd,
+ "no vni " CMD_VNI_RANGE,
+ NO_STR
+ "VNI\n"
+ "VNI-ID")
+{
+ int ret = 0;
+ char err[ERR_STR_SZ];
+ vni_t vni = strtoul(argv[2]->arg, NULL, 10);
+
+ ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
+
+ assert(vrf);
+ assert(zvrf);
+
+ ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
+ if (ret != 0) {
+ vty_out(vty, "%s\n", err);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* show vrf */
+DEFUN (show_vrf_vni,
+ show_vrf_vni_cmd,
+ "show vrf vni [json]",
+ SHOW_STR
+ "VRF\n"
+ "VNI\n"
+ JSON_STR)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+ json_object *json = NULL;
+ json_object *json_vrfs = NULL;
+ u_char uj = use_json(argc, argv);
+
+ if (uj) {
+ json = json_object_new_object();
+ json_vrfs = json_object_new_array();
+ }
+
+ RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+ zvrf = vrf->info;
+ if (!zvrf)
+ continue;
+
+ if (!zvrf->l3vni)
+ continue;
+
+ if (!uj) {
+ vty_out(vty, "vrf: %s VNI: %u",
+ zvrf_name(zvrf),
+ zvrf->l3vni);
+ vty_out(vty, "\n");
+ } else {
+ json_object *json_vrf = NULL;
+
+ json_vrf = json_object_new_object();
+ json_object_string_add(json_vrf, "vrf",
+ zvrf_name(zvrf));
+ json_object_int_add(json_vrf, "l3vni",
+ zvrf->l3vni);
+ json_object_array_add(json_vrfs, json_vrf);
+ }
+ }
+
+ if (uj) {
+ json_object_object_add(json, "vrfs", json_vrfs);
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_evpn_vni,
show_evpn_vni_cmd,
"show evpn vni [json]",
return CMD_SUCCESS;
}
+DEFUN (show_evpn_l3vni,
+ show_evpn_l3vni_cmd,
+ "show evpn l3vni [json]",
+ SHOW_STR
+ "EVPN\n"
+ "L3 VNI\n"
+ JSON_STR)
+{
+ u_char uj = use_json(argc, argv);
+
+ zebra_vxlan_print_l3vnis(vty, uj);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_l3vni_vni,
+ show_evpn_l3vni_vni_cmd,
+ "show evpn l3vni " CMD_VNI_RANGE "[json]",
+ SHOW_STR
+ "EVPN\n"
+ "L3 VxLAN Network Identifier\n"
+ "VNI number\n"
+ JSON_STR)
+{
+ vni_t vni;
+ u_char uj = use_json(argc, argv);
+
+ vni = strtoul(argv[3]->arg, NULL, 10);
+ zebra_vxlan_print_l3vni(vty, vni, uj);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_rmac_l3vni_mac,
+ show_evpn_rmac_l3vni_mac_cmd,
+ "show evpn rmac l3vni " CMD_VNI_RANGE " mac WORD [json]",
+ SHOW_STR
+ "EVPN\n"
+ "RMAC\n"
+ "L3-VNI\n"
+ "VNI number\n"
+ "MAC\n"
+ "mac-address (e.g. 0a:0a:0a:0a:0a:0a)\n"
+ JSON_STR)
+{
+ vni_t l3vni = 0;
+ struct ethaddr mac;
+ u_char uj = use_json(argc, argv);
+
+ l3vni = strtoul(argv[4]->arg, NULL, 10);
+ if (!prefix_str2mac(argv[6]->arg, &mac)) {
+ vty_out(vty, "%% Malformed MAC address\n");
+ return CMD_WARNING;
+ }
+ zebra_vxlan_print_specific_rmac_l3vni(vty, l3vni, &mac, uj);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_rmac_l3vni,
+ show_evpn_rmac_l3vni_cmd,
+ "show evpn rmac l3vni " CMD_VNI_RANGE "[json]",
+ SHOW_STR
+ "EVPN\n"
+ "RMAC\n"
+ "L3-VNI\n"
+ "VNI number\n"
+ JSON_STR)
+{
+ vni_t l3vni = 0;
+ u_char uj = use_json(argc, argv);
+
+ l3vni = strtoul(argv[4]->arg, NULL, 10);
+ zebra_vxlan_print_rmacs_l3vni(vty, l3vni, uj);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_rmac_l3vni_all,
+ show_evpn_rmac_l3vni_all_cmd,
+ "show evpn rmac l3vni all [json]",
+ SHOW_STR
+ "EVPN\n"
+ "RMAC addresses\n"
+ "L3-VNI\n"
+ "All VNIs\n"
+ JSON_STR)
+{
+ u_char uj = use_json(argc, argv);
+
+ zebra_vxlan_print_rmacs_all_l3vni(vty, uj);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_nh_l3vni_ip,
+ show_evpn_nh_l3vni_ip_cmd,
+ "show evpn next-hops l3vni " CMD_VNI_RANGE " ip WORD [json]",
+ SHOW_STR
+ "EVPN\n"
+ "Remote Vteps\n"
+ "L3-VNI\n"
+ "VNI number\n"
+ "Ip address\n"
+ "Host address (ipv4 or ipv6)\n"
+ JSON_STR)
+{
+ vni_t l3vni;
+ struct ipaddr ip;
+ u_char uj = use_json(argc, argv);
+
+ l3vni = strtoul(argv[4]->arg, NULL, 10);
+ if (str2ipaddr(argv[6]->arg, &ip) != 0) {
+ if (!uj)
+ vty_out(vty, "%% Malformed Neighbor address\n");
+ return CMD_WARNING;
+ }
+ zebra_vxlan_print_specific_nh_l3vni(vty, l3vni, &ip, uj);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_nh_l3vni,
+ show_evpn_nh_l3vni_cmd,
+ "show evpn next-hops l3vni " CMD_VNI_RANGE "[json]",
+ SHOW_STR
+ "EVPN\n"
+ "Remote Vteps\n"
+ "L3-VNI\n"
+ "VNI number\n"
+ JSON_STR)
+{
+ vni_t l3vni;
+ u_char uj = use_json(argc, argv);
+
+ l3vni = strtoul(argv[4]->arg, NULL, 10);
+ zebra_vxlan_print_nh_l3vni(vty, l3vni, uj);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_nh_l3vni_all,
+ show_evpn_nh_l3vni_all_cmd,
+ "show evpn next-hops l3vni all [json]",
+ SHOW_STR
+ "EVPN\n"
+ "Remote VTEPs\n"
+ "L3-VNI\n"
+ "All VNIs\n"
+ JSON_STR)
+{
+ u_char uj = use_json(argc, argv);
+
+ zebra_vxlan_print_nh_all_l3vni(vty, uj);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_evpn_mac_vni,
show_evpn_mac_vni_cmd,
"show evpn mac vni " CMD_VNI_RANGE "[json]",
{
int write = 0;
- write += static_config(vty, AFI_IP, SAFI_UNICAST, "ip route");
- write += static_config(vty, AFI_IP, SAFI_MULTICAST, "ip mroute");
- write += static_config(vty, AFI_IP6, SAFI_UNICAST, "ipv6 route");
-
write += zebra_import_table_config(vty);
+
return write;
}
return 1;
}
+#ifdef HAVE_NETLINK
+/* Display default rtm_table for all clients. */
+DEFUN (show_table,
+ show_table_cmd,
+ "show table",
+ SHOW_STR
+ "default routing table to use for all clients\n")
+{
+ vty_out(vty, "table %d\n", zebrad.rtm_table_default);
+ return CMD_SUCCESS;
+}
+
+DEFUN (config_table,
+ config_table_cmd,
+ "table TABLENO",
+ "Configure target kernel routing table\n"
+ "TABLE integer\n")
+{
+ zebrad.rtm_table_default = strtol(argv[1]->arg, (char **)0, 10);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_config_table,
+ no_config_table_cmd,
+ "no table [TABLENO]",
+ NO_STR
+ "Configure target kernel routing table\n"
+ "TABLE integer\n")
+{
+ zebrad.rtm_table_default = 0;
+ return CMD_SUCCESS;
+}
+#endif
+
+DEFUN (show_zebra,
+ show_zebra_cmd,
+ "show zebra",
+ SHOW_STR
+ ZEBRA_STR)
+{
+ struct vrf *vrf;
+
+ vty_out(vty,
+ " Route Route Neighbor LSP LSP\n");
+ vty_out(vty,
+ "VRF Installs Removals Updates Installs Removals\n");
+
+ RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+ struct zebra_vrf *zvrf = vrf->info;
+
+ vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64
+ " %10" PRIu64 " %10" PRIu64 "\n",
+ vrf->name, zvrf->installs, zvrf->removals,
+ zvrf->neigh_updates, zvrf->lsp_installs,
+ zvrf->lsp_removals);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (ip_forwarding,
+ ip_forwarding_cmd,
+ "ip forwarding",
+ IP_STR
+ "Turn on IP forwarding\n")
+{
+ int ret;
+
+ ret = ipforward();
+ if (ret == 0)
+ ret = ipforward_on();
+
+ if (ret == 0) {
+ vty_out(vty, "Can't turn on IP forwarding\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ip_forwarding,
+ no_ip_forwarding_cmd,
+ "no ip forwarding",
+ NO_STR
+ IP_STR
+ "Turn off IP forwarding\n")
+{
+ int ret;
+
+ ret = ipforward();
+ if (ret != 0)
+ ret = ipforward_off();
+
+ if (ret != 0) {
+ vty_out(vty, "Can't turn off IP forwarding\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Only display ip forwarding is enabled or not. */
+DEFUN (show_ip_forwarding,
+ show_ip_forwarding_cmd,
+ "show ip forwarding",
+ SHOW_STR
+ IP_STR
+ "IP forwarding status\n")
+{
+ int ret;
+
+ ret = ipforward();
+
+ if (ret == 0)
+ vty_out(vty, "IP forwarding is off\n");
+ else
+ vty_out(vty, "IP forwarding is on\n");
+ return CMD_SUCCESS;
+}
+
+/* Only display ipv6 forwarding is enabled or not. */
+DEFUN (show_ipv6_forwarding,
+ show_ipv6_forwarding_cmd,
+ "show ipv6 forwarding",
+ SHOW_STR
+ "IPv6 information\n"
+ "Forwarding status\n")
+{
+ int ret;
+
+ ret = ipforward_ipv6();
+
+ switch (ret) {
+ case -1:
+ vty_out(vty, "ipv6 forwarding is unknown\n");
+ break;
+ case 0:
+ vty_out(vty, "ipv6 forwarding is %s\n", "off");
+ break;
+ case 1:
+ vty_out(vty, "ipv6 forwarding is %s\n", "on");
+ break;
+ default:
+ vty_out(vty, "ipv6 forwarding is %s\n", "off");
+ break;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_forwarding,
+ ipv6_forwarding_cmd,
+ "ipv6 forwarding",
+ IPV6_STR
+ "Turn on IPv6 forwarding\n")
+{
+ int ret;
+
+ ret = ipforward_ipv6();
+ if (ret == 0)
+ ret = ipforward_ipv6_on();
+
+ if (ret == 0) {
+ vty_out(vty, "Can't turn on IPv6 forwarding\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_forwarding,
+ no_ipv6_forwarding_cmd,
+ "no ipv6 forwarding",
+ NO_STR
+ IPV6_STR
+ "Turn off IPv6 forwarding\n")
+{
+ int ret;
+
+ ret = ipforward_ipv6();
+ if (ret != 0)
+ ret = ipforward_ipv6_off();
+
+ if (ret != 0) {
+ vty_out(vty, "Can't turn off IPv6 forwarding\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Table configuration write function. */
+static int config_write_table(struct vty *vty)
+{
+ if (zebrad.rtm_table_default)
+ vty_out(vty, "table %d\n", zebrad.rtm_table_default);
+ return 0;
+}
+
+/* IPForwarding configuration write function. */
+static int config_write_forwarding(struct vty *vty)
+{
+ /* FIXME: Find better place for that. */
+ router_id_write(vty);
+
+ if (!ipforward())
+ vty_out(vty, "no ip forwarding\n");
+ if (!ipforward_ipv6())
+ vty_out(vty, "no ipv6 forwarding\n");
+ vty_out(vty, "!\n");
+ return 0;
+}
+
/* IP node for static routes. */
static struct cmd_node ip_node = {IP_NODE, "", 1};
static struct cmd_node protocol_node = {PROTOCOL_NODE, "", 1};
+/* table node for routing tables. */
+static struct cmd_node table_node = {TABLE_NODE,
+ "", /* This node has no interface. */
+ 1};
+static struct cmd_node forwarding_node = {FORWARDING_NODE,
+ "", /* This node has no interface. */
+ 1};
/* Route VTY. */
void zebra_vty_init(void)
{
+ /* Install configuration write function. */
+ install_node(&table_node, config_write_table);
+ install_node(&forwarding_node, config_write_forwarding);
+
+ install_element(VIEW_NODE, &show_ip_forwarding_cmd);
+ install_element(CONFIG_NODE, &ip_forwarding_cmd);
+ install_element(CONFIG_NODE, &no_ip_forwarding_cmd);
+ install_element(ENABLE_NODE, &show_zebra_cmd);
+
+#ifdef HAVE_NETLINK
+ install_element(VIEW_NODE, &show_table_cmd);
+ install_element(CONFIG_NODE, &config_table_cmd);
+ install_element(CONFIG_NODE, &no_config_table_cmd);
+#endif /* HAVE_NETLINK */
+
+ install_element(VIEW_NODE, &show_ipv6_forwarding_cmd);
+ install_element(CONFIG_NODE, &ipv6_forwarding_cmd);
+ install_element(CONFIG_NODE, &no_ipv6_forwarding_cmd);
+
+ /* Route-map */
+ zebra_route_map_init();
+
install_node(&ip_node, zebra_ip_config);
install_node(&protocol_node, config_write_protocol);
install_element(CONFIG_NODE, &ip_multicast_mode_cmd);
install_element(CONFIG_NODE, &no_ip_multicast_mode_cmd);
install_element(CONFIG_NODE, &ip_route_blackhole_cmd);
+ install_element(VRF_NODE, &ip_route_blackhole_vrf_cmd);
install_element(CONFIG_NODE, &ip_route_address_interface_cmd);
+ install_element(VRF_NODE, &ip_route_address_interface_vrf_cmd);
install_element(CONFIG_NODE, &ip_route_cmd);
+ install_element(VRF_NODE, &ip_route_vrf_cmd);
install_element(CONFIG_NODE, &ip_zebra_import_table_distance_cmd);
install_element(CONFIG_NODE, &no_ip_zebra_import_table_cmd);
install_element(CONFIG_NODE, &zebra_workqueue_timer_cmd);
install_element(CONFIG_NODE, &no_zebra_packet_process_cmd);
install_element(VIEW_NODE, &show_vrf_cmd);
+ install_element(VIEW_NODE, &show_vrf_vni_cmd);
install_element(VIEW_NODE, &show_route_cmd);
install_element(VIEW_NODE, &show_route_detail_cmd);
install_element(VIEW_NODE, &show_route_summary_cmd);
install_element(VIEW_NODE, &show_ip_rpf_addr_cmd);
install_element(CONFIG_NODE, &ipv6_route_blackhole_cmd);
+ install_element(VRF_NODE, &ipv6_route_blackhole_vrf_cmd);
install_element(CONFIG_NODE, &ipv6_route_address_interface_cmd);
+ install_element(VRF_NODE, &ipv6_route_address_interface_vrf_cmd);
install_element(CONFIG_NODE, &ipv6_route_cmd);
+ install_element(VRF_NODE, &ipv6_route_vrf_cmd);
install_element(CONFIG_NODE, &ip_nht_default_route_cmd);
install_element(CONFIG_NODE, &no_ip_nht_default_route_cmd);
install_element(CONFIG_NODE, &ipv6_nht_default_route_cmd);
install_element(VIEW_NODE, &show_evpn_vni_cmd);
install_element(VIEW_NODE, &show_evpn_vni_vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_l3vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_l3vni_vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_rmac_l3vni_mac_cmd);
+ install_element(VIEW_NODE, &show_evpn_rmac_l3vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_rmac_l3vni_all_cmd);
+ install_element(VIEW_NODE, &show_evpn_nh_l3vni_ip_cmd);
+ install_element(VIEW_NODE, &show_evpn_nh_l3vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_nh_l3vni_all_cmd);
install_element(VIEW_NODE, &show_evpn_mac_vni_cmd);
install_element(VIEW_NODE, &show_evpn_mac_vni_all_cmd);
install_element(VIEW_NODE, &show_evpn_mac_vni_all_vtep_cmd);
install_element(VIEW_NODE, &show_evpn_neigh_vni_all_cmd);
install_element(VIEW_NODE, &show_evpn_neigh_vni_neigh_cmd);
install_element(VIEW_NODE, &show_evpn_neigh_vni_vtep_cmd);
+
+ install_element(CONFIG_NODE, &no_vrf_vni_mapping_cmd);
+ install_element(VRF_NODE, &vrf_vni_mapping_cmd);
+ install_element(VRF_NODE, &no_vrf_vni_mapping_cmd);
+
}
#include "zebra/zebra_l2.h"
#include "lib/json.h"
+DEFINE_MTYPE_STATIC(ZEBRA, HOST_PREFIX, "host prefix");
DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash");
+DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash");
DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP");
DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC");
DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor");
static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt);
static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
void **args);
+static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty,
+ json_object *json);
+static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty,
+ json_object *json);
static void zvni_print_mac(zebra_mac_t *mac, void *ctxt);
static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt);
static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt);
struct ethaddr *macaddr, u_char flags);
static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n);
static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n);
-static zebra_vni_t *zvni_map_svi(struct interface *ifp,
+static zebra_vni_t *zvni_from_svi(struct interface *ifp,
struct interface *br_if);
static struct interface *zvni_map_to_svi(vlanid_t vid,
struct interface *br_if);
+/* l3-vni next-hop neigh related APIs */
+static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip);
+static void *zl3vni_nh_alloc(void *p);
+static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni,
+ struct ipaddr *vtep_ip,
+ struct ethaddr *rmac);
+static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n);
+static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n);
+static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n);
+
+/* l3-vni rmac related APIs */
+static void zl3vni_print_rmac_hash(struct hash_backet *, void *);
+static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac);
+static void *zl3vni_rmac_alloc(void *p);
+static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac);
+static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac);
+static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac);
+static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac);
+
+/* l3-vni related APIs*/
+static int is_vni_l3(vni_t);
+static zebra_l3vni_t *zl3vni_lookup(vni_t vni);
+static void *zl3vni_alloc(void *p);
+static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id);
+static int zl3vni_del(zebra_l3vni_t *zl3vni);
+static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t);
+static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni);
+static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni);
+static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni);
+static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni);
+
static unsigned int mac_hash_keymake(void *p);
static int mac_cmp(const void *p1, const void *p2);
static void *zvni_mac_alloc(void *p);
json_object_object_add(json, vni_str, json_vni);
}
+/* print a specific next hop for an l3vni */
+static void zl3vni_print_nh(zebra_neigh_t *n,
+ struct vty *vty,
+ json_object *json)
+{
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ struct listnode *node = NULL;
+ struct prefix *p = NULL;
+ json_object *json_hosts = NULL;
+
+ if (!json) {
+ vty_out(vty, "Ip: %s\n",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ vty_out(vty, " RMAC: %s\n",
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
+ vty_out(vty, " Host-List:\n");
+ for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p))
+ vty_out(vty, " %s\n",
+ prefix2str(p, buf2, sizeof(buf2)));
+ } else {
+ json_hosts = json_object_new_array();
+ json_object_string_add(json, "ip",
+ ipaddr2str(&(n->ip), buf2,
+ sizeof(buf2)));
+ json_object_string_add(json, "rmac",
+ prefix_mac2str(&n->emac, buf2,
+ sizeof(buf2)));
+ for (ALL_LIST_ELEMENTS_RO(n->host_list, node, p))
+ json_object_array_add(json_hosts,
+ json_object_new_string(
+ prefix2str(p, buf2,
+ sizeof(buf2))));
+ json_object_object_add(json, "hosts", json_hosts);
+ }
+}
+
+/* Print a specific RMAC entry */
+static void zl3vni_print_rmac(zebra_mac_t *zrmac,
+ struct vty *vty,
+ json_object *json)
+{
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[PREFIX_STRLEN];
+ struct listnode *node = NULL;
+ struct prefix *p = NULL;
+ json_object *json_hosts = NULL;
+
+ if (!json) {
+ vty_out(vty, "MAC: %s\n",
+ prefix_mac2str(&zrmac->macaddr, buf1, sizeof(buf1)));
+ vty_out(vty, " Remote VTEP: %s\n",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ vty_out(vty, " Host-List:\n");
+ for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p))
+ vty_out(vty, " %s\n",
+ prefix2str(p, buf2, sizeof(buf2)));
+ } else {
+ json_hosts = json_object_new_array();
+ json_object_string_add(json, "Rmac",
+ prefix_mac2str(&zrmac->macaddr,
+ buf1,
+ sizeof(buf1)));
+ json_object_string_add(json, "vtep-ip",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ for (ALL_LIST_ELEMENTS_RO(zrmac->host_list, node, p))
+ json_object_array_add(json_hosts,
+ json_object_new_string(
+ prefix2str(p, buf2,
+ sizeof(buf2))));
+ json_object_object_add(json, "hosts", json_hosts);
+ }
+}
+
/*
* Print a specific MAC entry.
*/
}
}
+static void zl3vni_print_nh_hash(struct hash_backet *backet,
+ void *ctx)
+{
+ struct nh_walk_ctx *wctx = NULL;
+ struct vty *vty = NULL;
+ struct json_object *json_vni = NULL;
+ struct json_object *json_nh = NULL;
+ zebra_neigh_t *n = NULL;
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ wctx = (struct nh_walk_ctx *)ctx;
+ vty = wctx->vty;
+ json_vni = wctx->json;
+ if (json_vni)
+ json_nh = json_object_new_object();
+ n = (zebra_neigh_t *)backet->data;
+ if (!n)
+ return;
+
+ if (!json_vni) {
+ vty_out(vty, "%-15s %-17s %6d\n",
+ ipaddr2str(&(n->ip), buf2, sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1)),
+ listcount(n->host_list));
+ } else {
+ json_object_string_add(json_nh, "nexthop-ip",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ json_object_string_add(json_nh, "rmac",
+ prefix_mac2str(&n->emac, buf1,
+ sizeof(buf1)));
+ json_object_int_add(json_nh, "refCnt", listcount(n->host_list));
+ json_object_object_add(json_vni,
+ ipaddr2str(&(n->ip), buf2, sizeof(buf2)),
+ json_nh);
+ }
+}
+
+static void zl3vni_print_nh_hash_all_vni(struct hash_backet *backet,
+ void **args)
+{
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ json_object *json_vni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ uint32_t num_nh = 0;
+ struct nh_walk_ctx wctx;
+ char vni_str[VNI_STR_LEN];
+
+ vty = (struct vty *)args[0];
+ json = (struct json_object *)args[1];
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni) {
+ if (json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ num_nh = hashcount(zl3vni->nh_table);
+ if (!num_nh)
+ return;
+
+ if (json) {
+ json_vni = json_object_new_object();
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+ }
+
+ if (json == NULL) {
+ vty_out(vty, "\nVNI %u #Next-Hops %u\n\n",
+ zl3vni->vni, num_nh);
+ vty_out(vty, "%-15s %-17s %6s\n", "IP",
+ "RMAC", "Refcnt");
+ } else
+ json_object_int_add(json_vni, "numNh", num_nh);
+
+ memset(&wctx, 0, sizeof(struct nh_walk_ctx));
+ wctx.vty = vty;
+ wctx.json = json_vni;
+ hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx);
+ if (json)
+ json_object_object_add(json, vni_str, json_vni);
+}
+
+static void zl3vni_print_rmac_hash_all_vni(struct hash_backet *backet,
+ void **args)
+{
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ json_object *json_vni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ u_int32_t num_rmacs;
+ struct rmac_walk_ctx wctx;
+ char vni_str[VNI_STR_LEN];
+
+ vty = (struct vty *)args[0];
+ json = (struct json_object *)args[1];
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni) {
+ if (json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ num_rmacs = hashcount(zl3vni->rmac_table);
+ if (!num_rmacs)
+ return;
+
+ if (json) {
+ json_vni = json_object_new_object();
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+ }
+
+ if (json == NULL) {
+ vty_out(vty, "\nVNI %u #MACs %u\n\n",
+ zl3vni->vni, num_rmacs);
+ vty_out(vty, "%-17s %-21s %-6s\n", "MAC",
+ "Remote VTEP", "Refcnt");
+ } else
+ json_object_int_add(json_vni, "numRmacs", num_rmacs);
+
+ /* assign per-vni to wctx->json object to fill macs
+ * under the vni. Re-assign primary json object to fill
+ * next vni information.
+ */
+ memset(&wctx, 0, sizeof(struct rmac_walk_ctx));
+ wctx.vty = vty;
+ wctx.json = json_vni;
+ hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx);
+ if (json)
+ json_object_object_add(json, vni_str, json_vni);
+}
+
+static void zl3vni_print_rmac_hash(struct hash_backet *backet,
+ void *ctx)
+{
+ zebra_mac_t *zrmac = NULL;
+ struct rmac_walk_ctx *wctx = NULL;
+ struct vty *vty = NULL;
+ struct json_object *json = NULL;
+ struct json_object *json_rmac = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+
+ wctx = (struct rmac_walk_ctx *)ctx;
+ vty = wctx->vty;
+ json = wctx->json;
+ if (json)
+ json_rmac = json_object_new_object();
+ zrmac = (zebra_mac_t *)backet->data;
+ if (!zrmac)
+ return;
+
+ if (!json) {
+ vty_out(vty, "%-17s %-21s %-6d\n",
+ prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)),
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip),
+ listcount(zrmac->host_list));
+ } else {
+ json_object_string_add(json_rmac, "rmac",
+ prefix_mac2str(&zrmac->macaddr, buf,
+ sizeof(buf)));
+ json_object_string_add(json_rmac, "vtep-ip",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ json_object_int_add(json_rmac, "refcnt",
+ listcount(zrmac->host_list));
+ json_object_object_add(json,
+ prefix_mac2str(&zrmac->macaddr, buf,
+ sizeof(buf)),
+ json_rmac);
+ }
+}
+
+/* print a specific L3 VNI entry */
+static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ zebra_vni_t *zvni = NULL;
+ json_object *json_vni_list = NULL;
+ struct listnode *node = NULL, *nnode = NULL;
+
+ vty = ctx[0];
+ json = ctx[1];
+
+ if (!json) {
+ vty_out(vty, "VNI: %u\n", zl3vni->vni);
+ vty_out(vty, " Local Vtep Ip: %s",
+ inet_ntoa(zl3vni->local_vtep_ip));
+ vty_out(vty, " Vxlan-Intf: %s\n",
+ zl3vni_vxlan_if_name(zl3vni));
+ vty_out(vty, " SVI-If: %s\n",
+ zl3vni_svi_if_name(zl3vni));
+ vty_out(vty, " State: %s\n",
+ zl3vni_state2str(zl3vni));
+ vty_out(vty, " Vrf: %s\n",
+ zl3vni_vrf_name(zl3vni));
+ vty_out(vty, " Rmac: %s\n",
+ zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
+ vty_out(vty, " L2-VNIs: ");
+ for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni))
+ vty_out(vty, "%u ", zvni->vni);
+ vty_out(vty, "\n");
+ } else {
+ json_vni_list = json_object_new_array();
+ json_object_int_add(json, "vni", zl3vni->vni);
+ json_object_string_add(json, "local-vtep-ip",
+ inet_ntoa(zl3vni->local_vtep_ip));
+ json_object_string_add(json, "vxlan-intf",
+ zl3vni_vxlan_if_name(zl3vni));
+ json_object_string_add(json, "svi-if",
+ zl3vni_svi_if_name(zl3vni));
+ json_object_string_add(json, "state",
+ zl3vni_state2str(zl3vni));
+ json_object_string_add(json, "vrf",
+ zl3vni_vrf_name(zl3vni));
+ json_object_string_add(json, "rmac",
+ zl3vni_rmac2str(zl3vni, buf,
+ sizeof(buf)));
+ for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) {
+ json_object_array_add(json_vni_list,
+ json_object_new_int(zvni->vni));
+ }
+ json_object_object_add(json, "l2-vnis", json_vni_list);
+ }
+}
+
/*
* Print a specific VNI entry.
*/
vty = ctxt[0];
json = ctxt[1];
- if (json == NULL)
+ if (json == NULL) {
vty_out(vty, "VNI: %u\n", zvni->vni);
- else
+ vty_out(vty, " VRF: %s\n", vrf_id_to_name(zvni->vrf_id));
+ } else {
json_object_int_add(json, "vni", zvni->vni);
+ json_object_string_add(json, "vrf",
+ vrf_id_to_name(zvni->vrf_id));
+ }
if (!zvni->vxlan_if) { // unexpected
if (json == NULL)
}
}
+/* print a L3 VNI hash entry */
+static void zl3vni_print_hash(struct hash_backet *backet,
+ void *ctx[])
+{
+ char buf[ETHER_ADDR_STRLEN];
+ struct vty *vty = NULL;
+ json_object *json = NULL;
+ json_object *json_vni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ vty = (struct vty *)ctx[0];
+ json = (json_object *)ctx[1];
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni)
+ return;
+
+ if (!json) {
+ vty_out(vty, "%-10u %-15s %-20s %-20s %-5s %-37s %-18s\n",
+ zl3vni->vni,
+ inet_ntoa(zl3vni->local_vtep_ip),
+ zl3vni_vxlan_if_name(zl3vni),
+ zl3vni_svi_if_name(zl3vni),
+ zl3vni_state2str(zl3vni),
+ zl3vni_vrf_name(zl3vni),
+ zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
+ } else {
+ char vni_str[VNI_STR_LEN];
+
+ snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni);
+ json_vni = json_object_new_object();
+ json_object_int_add(json_vni, "vni", zl3vni->vni);
+ json_object_string_add(json_vni, "local-ip",
+ inet_ntoa(zl3vni->local_vtep_ip));
+ json_object_string_add(json_vni, "vxlan-if",
+ zl3vni_vxlan_if_name(zl3vni));
+ json_object_string_add(json_vni, "svi-if",
+ zl3vni_svi_if_name(zl3vni));
+ json_object_string_add(json_vni, "state",
+ zl3vni_state2str(zl3vni));
+ json_object_string_add(json_vni, "vrf",
+ zl3vni_vrf_name(zl3vni));
+ json_object_string_add(json_vni, "rmac",
+ zl3vni_rmac2str(zl3vni, buf,
+ sizeof(buf)));
+ json_object_object_add(json, vni_str, json_vni);
+ }
+
+}
+
/*
* Print a VNI hash entry - called for display of all VNIs.
*/
num_macs = num_valid_macs(zvni);
num_neigh = hashcount(zvni->neigh_table);
if (json == NULL)
- vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni,
+ vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u %-37s\n",
+ zvni->vni,
zvni->vxlan_if ? zvni->vxlan_if->name : "unknown",
inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh,
- num_vteps);
+ num_vteps,
+ vrf_id_to_name(zvni->vrf_id));
else {
char vni_str[VNI_STR_LEN];
snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni);
struct ipaddr *ip, u_char flags,
u_int16_t cmd)
{
- struct zserv *client;
- struct stream *s;
- int ipa_len;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ int ipa_len;
+ struct zserv *client = NULL;
+ struct stream *s = NULL;
client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
/* BGP may not be running. */
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, cmd, VRF_DEFAULT);
+ zclient_create_header(s, cmd, VRF_DEFAULT);
stream_putl(s, vni);
stream_put(s, macaddr->octet, ETH_ALEN);
if (ip) {
stream_putc(s, flags); /* sticky mac/gateway mac */
+
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Send MACIP %s flags 0x%x MAC %s IP %s VNI %u to %s",
+ "Send MACIP %s flags 0x%x MAC %s IP %s L2-VNI %u to %s",
(cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
flags, prefix_mac2str(macaddr, buf, sizeof(buf)),
ipaddr2str(ip, buf2, sizeof(buf2)), vni,
if (IS_ZEBRA_NEIGH_INACTIVE(n)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "neigh %s (MAC %s) on VNI %u is now ACTIVE",
+ "neigh %s (MAC %s) on L2-VNI %u is now ACTIVE",
ipaddr2str(&n->ip, buf2,
sizeof(buf2)),
prefix_mac2str(&n->emac, buf,
if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "neigh %s (MAC %s) on VNI %u is now INACTIVE",
+ "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE",
ipaddr2str(&n->ip, buf2,
sizeof(buf2)),
prefix_mac2str(&n->emac, buf,
if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "neigh %s (MAC %s) on VNI %u INACTIVE",
+ "neigh %s (MAC %s) on L2-VNI %u is now INACTIVE",
ipaddr2str(&n->ip, buf2,
sizeof(buf2)),
prefix_mac2str(&n->emac, buf,
static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
struct ethaddr *macaddr, struct ipaddr *ip)
{
- struct zebra_if *zif = NULL;
- struct zebra_l2info_vxlan *vxl = NULL;
- zebra_neigh_t *n = NULL;
- zebra_mac_t *mac = NULL;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *mac = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
zif = zvni->vxlan_if->info;
if (!zif)
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "SVI %s(%u) VNI %u, sending GW MAC %s IP %s add to BGP",
+ "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP",
ifp->name, ifp->ifindex, zvni->vni,
prefix_mac2str(macaddr, buf, sizeof(buf)),
ipaddr2str(ip, buf2, sizeof(buf2)));
static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
struct ipaddr *ip)
{
- zebra_neigh_t *n = NULL;
- zebra_mac_t *mac = NULL;
char buf1[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *mac = NULL;
/* If the neigh entry is not present nothing to do*/
n = zvni_neigh_lookup(zvni, ip);
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP",
+ "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s del to BGP",
ifp->name, ifp->ifindex, zvni->vni,
prefix_mac2str(&(n->emac), buf1, sizeof(buf1)),
ipaddr2str(ip, buf2, sizeof(buf2)));
* Map SVI and associated bridge to a VNI. This is invoked upon getting
* neighbor notifications, to see if they are of interest.
*/
-static zebra_vni_t *zvni_map_svi(struct interface *ifp, struct interface *br_if)
+static zebra_vni_t *zvni_from_svi(struct interface *ifp,
+ struct interface *br_if)
{
struct zebra_ns *zns;
struct route_node *rn;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_VNI_ADD, VRF_DEFAULT);
+ zclient_create_header(s, ZEBRA_VNI_ADD, VRF_DEFAULT);
stream_putl(s, zvni->vni);
stream_put_in_addr(s, &zvni->local_vtep_ip);
+ stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Send VNI_ADD %u %s to %s",
+ zlog_debug("Send VNI_ADD %u %s tenant vrf %s to %s",
zvni->vni, inet_ntoa(zvni->local_vtep_ip),
+ vrf_id_to_name(zvni->vrf_id),
zebra_route_string(client->proto));
client->vniadd_cnt++;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_VNI_DEL, VRF_DEFAULT);
+ zclient_create_header(s, ZEBRA_VNI_DEL, VRF_DEFAULT);
stream_putl(s, vni);
/* Write packet size. */
/* Walk VxLAN interfaces and create VNI hash. */
zns = zebra_ns_lookup(NS_DEFAULT);
for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ vni_t vni;
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
- zebra_vni_t *zvni;
- vni_t vni;
ifp = (struct interface *)rn->info;
if (!ifp)
zif = ifp->info;
if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
continue;
- vxl = &zif->l2info.vxl;
+ vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Create VNI hash for intf %s(%u) VNI %u local IP %s",
- ifp->name, ifp->ifindex, vni,
- inet_ntoa(vxl->vtep_ip));
+ if (is_vni_l3(vni)) {
+ zebra_l3vni_t *zl3vni = NULL;
- /* VNI hash entry is not expected to exist. */
- zvni = zvni_lookup(vni);
- if (zvni) {
- zlog_err(
- "VNI hash already present for IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- continue;
- }
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("create L3-VNI hash for Intf %s(%u) L3-VNI %u",
+ ifp->name, ifp->ifindex, vni);
- zvni = zvni_add(vni);
- if (!zvni) {
- zlog_err(
- "Failed to add VNI hash, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return;
- }
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return;
+ }
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->vxlan_if = ifp;
+ /* associate with vxlan_if */
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ zl3vni->vxlan_if = ifp;
- /* Inform BGP if interface is up and mapped to bridge. */
- if (if_is_operative(ifp) && zif->brslave_info.br_if)
- zvni_send_add_to_client(zvni);
+ /*
+ * we need to associate with SVI.
+ * we can associate with svi-if only after association
+ * with vxlan-intf is complete
+ */
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+
+ } else {
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct interface *vlan_if = NULL;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Create L2-VNI hash for intf %s(%u) L2-VNI %u local IP %s",
+ ifp->name, ifp->ifindex, vni,
+ inet_ntoa(vxl->vtep_ip));
+
+ /* VNI hash entry is not expected to exist. */
+ zvni = zvni_lookup(vni);
+ if (zvni) {
+ zlog_err(
+ "VNI hash already present for IF %s(%u) L2-VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ continue;
+ }
+
+ zvni = zvni_add(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to add VNI hash, IF %s(%u) L2-VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return;
+ }
+
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if) {
+ zvni->vrf_id = vlan_if->vrf_id;
+ zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
+ if (zl3vni)
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+ }
+
+
+ /* Inform BGP if intf is up and mapped to bridge. */
+ if (if_is_operative(ifp) && zif->brslave_info.br_if)
+ zvni_send_add_to_client(zvni);
+ }
}
}
*/
static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf)
{
- zebra_vni_t *zvni;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
zvni = (zebra_vni_t *)backet->data;
if (!zvni)
return;
+ /* remove from l3-vni list */
+ zl3vni = zl3vni_from_vrf(zvni->vrf_id);
+ if (zl3vni)
+ listnode_delete(zl3vni->l2vnis, zvni);
+
/* Free up all neighbors and MACs, if any. */
zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
zvni_del(zvni);
}
+/* cleanup L3VNI */
+static void zl3vni_cleanup_all(struct hash_backet *backet,
+ void *args)
+{
+ zebra_l3vni_t *zl3vni = NULL;
-/* Public functions */
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni)
+ return;
-/*
- * Display Neighbors for a VNI (VTY command handler).
- */
-void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
- vni_t vni, u_char use_json)
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+}
+
+static int is_host_present_in_host_list(struct list *list,
+ struct prefix *host)
{
- zebra_vni_t *zvni;
- u_int32_t num_neigh;
- struct neigh_walk_ctx wctx;
- json_object *json = NULL;
+ struct listnode *node = NULL;
+ struct prefix *p = NULL;
- if (!is_evpn_enabled())
- return;
- zvni = zvni_lookup(vni);
- if (!zvni) {
- if (use_json)
- vty_out(vty, "{}\n");
- else
- vty_out(vty, "%% VNI %u does not exist\n", vni);
- return;
+ for (ALL_LIST_ELEMENTS_RO(list, node, p)) {
+ if (prefix_same(p, host))
+ return 1;
}
- num_neigh = hashcount(zvni->neigh_table);
- if (!num_neigh)
- return;
-
- if (use_json)
- json = json_object_new_object();
+ return 0;
+}
- /* Since we have IPv6 addresses to deal with which can vary widely in
- * size, we try to be a bit more elegant in display by first computing
- * the maximum width.
- */
- memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
- wctx.zvni = zvni;
- wctx.vty = vty;
- wctx.addr_width = 15;
- wctx.json = json;
- hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
+static void host_list_add_host(struct list *list,
+ struct prefix *host)
+{
+ struct prefix *p = NULL;
- if (!use_json) {
- vty_out(vty,
- "Number of ARPs (local and remote) known for this VNI: %u\n",
- num_neigh);
- vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP",
- "Type", "MAC", "Remote VTEP");
- } else
- json_object_int_add(json, "numArpNd", num_neigh);
+ p = XCALLOC(MTYPE_HOST_PREFIX, sizeof(struct prefix));
+ memcpy(p, host, sizeof(struct prefix));
- hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
- if (use_json) {
- vty_out(vty, "%s\n", json_object_to_json_string_ext(
- json, JSON_C_TO_STRING_PRETTY));
- json_object_free(json);
- }
+ listnode_add_sort(list, p);
+}
+
+static void host_list_delete_host(struct list *list,
+ struct prefix *host)
+{
+ struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
+ struct prefix *p = NULL;
+
+ for (ALL_LIST_ELEMENTS(list, node, nnode, p)) {
+ if (prefix_same(p, host)) {
+ XFREE(MTYPE_HOST_PREFIX, p);
+ node_to_del = node;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(list, node_to_del);
+}
+
+/*
+ * Look up MAC hash entry.
+ */
+static zebra_mac_t *zl3vni_rmac_lookup(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac)
+{
+ zebra_mac_t tmp;
+ zebra_mac_t *pmac;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.macaddr, rmac, ETH_ALEN);
+ pmac = hash_lookup(zl3vni->rmac_table, &tmp);
+
+ return pmac;
+}
+
+/*
+ * Callback to allocate RMAC hash entry.
+ */
+static void *zl3vni_rmac_alloc(void *p)
+{
+ const zebra_mac_t *tmp_rmac = p;
+ zebra_mac_t *zrmac;
+
+ zrmac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t));
+ *zrmac = *tmp_rmac;
+
+ return ((void *)zrmac);
+}
+
+/*
+ * Add RMAC entry to l3-vni
+ */
+static zebra_mac_t *zl3vni_rmac_add(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac)
+{
+ zebra_mac_t tmp_rmac;
+ zebra_mac_t *zrmac = NULL;
+
+ memset(&tmp_rmac, 0, sizeof(zebra_mac_t));
+ memcpy(&tmp_rmac.macaddr, rmac, ETH_ALEN);
+ zrmac = hash_get(zl3vni->rmac_table, &tmp_rmac, zl3vni_rmac_alloc);
+ assert(zrmac);
+
+ zrmac->host_list = list_new();
+ zrmac->host_list->cmp = (int (*)(void *, void *))prefix_cmp;
+
+ SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE);
+ SET_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC);
+
+ return zrmac;
+}
+
+/*
+ * Delete MAC entry.
+ */
+static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac)
+{
+ zebra_mac_t *tmp_rmac;
+
+ if (zrmac->host_list)
+ list_delete_and_null(&zrmac->host_list);
+ zrmac->host_list = NULL;
+
+ tmp_rmac = hash_release(zl3vni->rmac_table, zrmac);
+ if (tmp_rmac)
+ XFREE(MTYPE_MAC, tmp_rmac);
+
+ return 0;
+}
+
+/*
+ * Install remote RMAC into the kernel.
+ */
+static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac)
+{
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) ||
+ !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC)))
+ return 0;
+
+ zif = zl3vni->vxlan_if->info;
+ if (!zif)
+ return -1;
+
+ vxl = &zif->l2info.vxl;
+
+ return kernel_add_mac(zl3vni->vxlan_if, vxl->access_vlan,
+ &zrmac->macaddr,
+ zrmac->fwd_info.r_vtep_ip, 0);
+}
+
+/*
+ * Uninstall remote RMAC from the kernel.
+ */
+static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni,
+ zebra_mac_t *zrmac)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE)) ||
+ !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC)))
+ return 0;
+
+ if (!zl3vni->vxlan_if) {
+ zlog_err(
+ "RMAC %s on L3-VNI %u hash %p couldn't be uninstalled - no vxlan_if",
+ prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)),
+ zl3vni->vni, zl3vni);
+ return -1;
+ }
+
+ zif = zl3vni->vxlan_if->info;
+ if (!zif)
+ return -1;
+
+ vxl = &zif->l2info.vxl;
+
+ return kernel_del_mac(zl3vni->vxlan_if, vxl->access_vlan,
+ &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0);
+}
+
+/* handle rmac add */
+static int zl3vni_remote_rmac_add(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ zebra_mac_t *zrmac = NULL;
+
+ zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
+ if (!zrmac) {
+
+ zrmac = zl3vni_rmac_add(zl3vni, rmac);
+ if (!zrmac) {
+ zlog_warn(
+ "Failed to add RMAC %s L3VNI %u Remote VTEP %s",
+ prefix_mac2str(rmac, buf,
+ sizeof(buf)),
+ zl3vni->vni, ipaddr2str(vtep_ip, buf1,
+ sizeof(buf1)));
+ return -1;
+ }
+ memset(&zrmac->fwd_info, 0, sizeof(zrmac->fwd_info));
+ zrmac->fwd_info.r_vtep_ip = vtep_ip->ipaddr_v4;
+
+ /* install rmac in kernel */
+ zl3vni_rmac_install(zl3vni, zrmac);
+ }
+
+ if (!is_host_present_in_host_list(zrmac->host_list, host_prefix))
+ host_list_add_host(zrmac->host_list, host_prefix);
+ return 0;
+}
+
+
+/* handle rmac delete */
+static int zl3vni_remote_rmac_del(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac,
+ struct prefix *host_prefix)
+{
+ zebra_mac_t *zrmac = NULL;
+
+ zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
+ if (!zrmac)
+ return -1;
+
+ host_list_delete_host(zrmac->host_list, host_prefix);
+ if (list_isempty(zrmac->host_list)) {
+
+ /* uninstall from kernel */
+ zl3vni_rmac_uninstall(zl3vni, zrmac);
+
+ /* del the rmac entry */
+ zl3vni_rmac_del(zl3vni, zrmac);
+ }
+ return 0;
+}
+
+/*
+ * Look up nh hash entry on a l3-vni.
+ */
+static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip)
+{
+ zebra_neigh_t tmp;
+ zebra_neigh_t *n;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.ip, ip, sizeof(struct ipaddr));
+ n = hash_lookup(zl3vni->nh_table, &tmp);
+
+ return n;
+}
+
+
+/*
+ * Callback to allocate NH hash entry on L3-VNI.
+ */
+static void *zl3vni_nh_alloc(void *p)
+{
+ const zebra_neigh_t *tmp_n = p;
+ zebra_neigh_t *n;
+
+ n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t));
+ *n = *tmp_n;
+
+ return ((void *)n);
+}
+
+/*
+ * Add neighbor entry.
+ */
+static zebra_neigh_t *zl3vni_nh_add(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip,
+ struct ethaddr *mac)
+{
+ zebra_neigh_t tmp_n;
+ zebra_neigh_t *n = NULL;
+
+ memset(&tmp_n, 0, sizeof(zebra_neigh_t));
+ memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
+ n = hash_get(zl3vni->nh_table, &tmp_n, zl3vni_nh_alloc);
+ assert(n);
+
+ n->host_list = list_new();
+ n->host_list->cmp = (int (*)(void *, void *))prefix_cmp;
+
+ memcpy(&n->emac, mac, ETH_ALEN);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE_NH);
+
+ return n;
+}
+
+/*
+ * Delete neighbor entry.
+ */
+static int zl3vni_nh_del(zebra_l3vni_t *zl3vni,
+ zebra_neigh_t *n)
+{
+ zebra_neigh_t *tmp_n;
+
+ if (n->host_list)
+ list_delete_and_null(&n->host_list);
+ n->host_list = NULL;
+
+ tmp_n = hash_release(zl3vni->nh_table, n);
+ if (tmp_n)
+ XFREE(MTYPE_NEIGH, tmp_n);
+
+ return 0;
+}
+
+/*
+ * Install remote nh as neigh into the kernel.
+ */
+static int zl3vni_nh_install(zebra_l3vni_t *zl3vni,
+ zebra_neigh_t *n)
+{
+ if (!is_l3vni_oper_up(zl3vni))
+ return -1;
+
+ if (!(n->flags & ZEBRA_NEIGH_REMOTE) ||
+ !(n->flags & ZEBRA_NEIGH_REMOTE_NH))
+ return 0;
+
+ return kernel_add_neigh(zl3vni->svi_if, &n->ip, &n->emac);
+}
+
+/*
+ * Uninstall remote nh from the kernel.
+ */
+static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni,
+ zebra_neigh_t *n)
+{
+ if (!is_l3vni_oper_up(zl3vni))
+ return -1;
+
+ if (!(n->flags & ZEBRA_NEIGH_REMOTE) ||
+ !(n->flags & ZEBRA_NEIGH_REMOTE_NH))
+ return 0;
+
+ return kernel_del_neigh(zl3vni->svi_if, &n->ip);
+}
+
+/* add remote vtep as a neigh entry */
+static int zl3vni_remote_nh_add(zebra_l3vni_t *zl3vni,
+ struct ipaddr *vtep_ip,
+ struct ethaddr *rmac,
+ struct prefix *host_prefix)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ zebra_neigh_t *nh = NULL;
+
+ nh = zl3vni_nh_lookup(zl3vni, vtep_ip);
+ if (!nh) {
+ nh = zl3vni_nh_add(zl3vni, vtep_ip, rmac);
+ if (!nh) {
+
+ zlog_warn(
+ "Failed to add NH as Neigh (IP %s MAC %s L3-VNI %u)",
+ ipaddr2str(vtep_ip, buf1,
+ sizeof(buf1)),
+ prefix_mac2str(rmac, buf,
+ sizeof(buf)),
+ zl3vni->vni);
+ return -1;
+ }
+
+ /* install the nh neigh in kernel */
+ zl3vni_nh_install(zl3vni, nh);
+ }
+
+ if (!is_host_present_in_host_list(nh->host_list, host_prefix))
+ host_list_add_host(nh->host_list, host_prefix);
+
+ return 0;
+}
+
+/* handle nh neigh delete */
+static int zl3vni_remote_nh_del(zebra_l3vni_t *zl3vni,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ zebra_neigh_t *nh = NULL;
+
+ nh = zl3vni_nh_lookup(zl3vni, vtep_ip);
+ if (!nh)
+ return -1;
+
+ host_list_delete_host(nh->host_list, host_prefix);
+ if (list_isempty(nh->host_list)) {
+
+ /* uninstall from kernel */
+ zl3vni_nh_uninstall(zl3vni, nh);
+
+ /* delete the nh entry */
+ zl3vni_nh_del(zl3vni, nh);
+ }
+
+ return 0;
+}
+
+/* handle neigh update from kernel - the only thing of interest is to
+ * readd stale entries.
+ */
+static int zl3vni_local_nh_add_update(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip, u_int16_t state)
+{
+#ifdef GNU_LINUX
+ zebra_neigh_t *n = NULL;
+
+ n = zl3vni_nh_lookup(zl3vni, ip);
+ if (!n)
+ return 0;
+
+ /* all next hop neigh are remote and installed by frr.
+ * If the kernel has aged this entry, re-install.
+ */
+ if (state & NUD_STALE)
+ zl3vni_nh_install(zl3vni, n);
+#endif
+ return 0;
+}
+
+/* handle neigh delete from kernel */
+static int zl3vni_local_nh_del(zebra_l3vni_t *zl3vni,
+ struct ipaddr *ip)
+{
+ zebra_neigh_t *n = NULL;
+
+ n = zl3vni_nh_lookup(zl3vni, ip);
+ if (!n)
+ return 0;
+
+ /* all next hop neigh are remote and installed by frr.
+ * If we get an age out notification for these neigh entries, we have to
+ * install it back
+ */
+ zl3vni_nh_install(zl3vni, n);
+
+ return 0;
+}
+
+/*
+ * Hash function for L3 VNI.
+ */
+static unsigned int l3vni_hash_keymake(void *p)
+{
+ const zebra_l3vni_t *zl3vni = p;
+
+ return jhash_1word(zl3vni->vni, 0);
+}
+
+/*
+ * Compare 2 L3 VNI hash entries.
+ */
+static int l3vni_hash_cmp(const void *p1, const void *p2)
+{
+ const zebra_l3vni_t *zl3vni1 = p1;
+ const zebra_l3vni_t *zl3vni2 = p2;
+
+ return (zl3vni1->vni == zl3vni2->vni);
+}
+
+/*
+ * Callback to allocate L3 VNI hash entry.
+ */
+static void *zl3vni_alloc(void *p)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ const zebra_l3vni_t *tmp_l3vni = p;
+
+ zl3vni = XCALLOC(MTYPE_ZL3VNI, sizeof(zebra_l3vni_t));
+ zl3vni->vni = tmp_l3vni->vni;
+ return ((void *)zl3vni);
+}
+
+/*
+ * Look up L3 VNI hash entry.
+ */
+static zebra_l3vni_t *zl3vni_lookup(vni_t vni)
+{
+ struct zebra_ns *zns;
+ zebra_l3vni_t tmp_l3vni;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+ memset(&tmp_l3vni, 0, sizeof(zebra_l3vni_t));
+ tmp_l3vni.vni = vni;
+ zl3vni = hash_lookup(zns->l3vni_table, &tmp_l3vni);
+
+ return zl3vni;
+}
+
+/*
+ * Add L3 VNI hash entry.
+ */
+static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id)
+{
+ zebra_l3vni_t tmp_zl3vni;
+ struct zebra_ns *zns = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+
+ memset(&tmp_zl3vni, 0, sizeof(zebra_l3vni_t));
+ tmp_zl3vni.vni = vni;
+
+ zl3vni = hash_get(zns->l3vni_table, &tmp_zl3vni, zl3vni_alloc);
+ assert(zl3vni);
+
+ zl3vni->vrf_id = vrf_id;
+ zl3vni->svi_if = NULL;
+ zl3vni->vxlan_if = NULL;
+ zl3vni->l2vnis = list_new();
+ zl3vni->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp;
+
+ /* Create hash table for remote RMAC */
+ zl3vni->rmac_table =
+ hash_create(mac_hash_keymake, mac_cmp,
+ "Zebra L3-VNI RMAC-Table");
+
+ /* Create hash table for neighbors */
+ zl3vni->nh_table = hash_create(neigh_hash_keymake, neigh_cmp,
+ "Zebra L3-VNI next-hop table");
+
+ return zl3vni;
+}
+
+/*
+ * Delete L3 VNI hash entry.
+ */
+static int zl3vni_del(zebra_l3vni_t *zl3vni)
+{
+ struct zebra_ns *zns;
+ zebra_l3vni_t *tmp_zl3vni;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+
+ /* free the list of l2vnis */
+ list_delete_and_null(&zl3vni->l2vnis);
+ zl3vni->l2vnis = NULL;
+
+ /* Free the rmac table */
+ hash_free(zl3vni->rmac_table);
+ zl3vni->rmac_table = NULL;
+
+ /* Free the nh table */
+ hash_free(zl3vni->nh_table);
+ zl3vni->nh_table = NULL;
+
+ /* Free the VNI hash entry and allocated memory. */
+ tmp_zl3vni = hash_release(zns->l3vni_table, zl3vni);
+ if (tmp_zl3vni)
+ XFREE(MTYPE_ZL3VNI, tmp_zl3vni);
+
+ return 0;
+}
+
+static int is_vni_l3(vni_t vni)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni)
+ return 1;
+ return 0;
+}
+
+static struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni)
+{
+ struct zebra_ns *zns = NULL;
+ struct route_node *rn = NULL;
+ struct interface *ifp = NULL;
+
+ /* loop through all vxlan-interface */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ ifp = (struct interface *)rn->info;
+ if (!ifp)
+ continue;
+
+ zif = ifp->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+
+ vxl = &zif->l2info.vxl;
+ if (vxl->vni == zl3vni->vni) {
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ return ifp;
+ }
+ }
+
+ return NULL;
+}
+
+static struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni)
+{
+ struct zebra_if *zif = NULL; /* zebra_if for vxlan_if */
+ struct zebra_l2info_vxlan *vxl = NULL; /* l2 info for vxlan_if */
+
+ if (!zl3vni->vxlan_if)
+ return NULL;
+
+ zif = zl3vni->vxlan_if->info;
+ if (!zif)
+ return NULL;
+
+ vxl = &zif->l2info.vxl;
+
+ return zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
+}
+
+static zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id)
+{
+ struct zebra_vrf *zvrf = NULL;
+
+ zvrf = zebra_vrf_lookup_by_id(vrf_id);
+ if (!zvrf)
+ return NULL;
+
+ return zl3vni_lookup(zvrf->l3vni);
+}
+
+/*
+ * Map SVI and associated bridge to a VNI. This is invoked upon getting
+ * neighbor notifications, to see if they are of interest.
+ */
+static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp,
+ struct interface *br_if)
+{
+ int found = 0;
+ vlanid_t vid = 0;
+ u_char bridge_vlan_aware = 0;
+ zebra_l3vni_t *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;
+
+ if (!br_if)
+ return NULL;
+
+ /* Make sure the linked interface is a bridge. */
+ if (!IS_ZEBRA_IF_BRIDGE(br_if))
+ return NULL;
+
+ /* 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) {
+ struct zebra_l2info_vlan *vl;
+
+ if (!IS_ZEBRA_IF_VLAN(ifp))
+ return NULL;
+
+ zif = ifp->info;
+ assert(zif);
+ vl = &zif->l2info.vl;
+ 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;
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ zl3vni = zl3vni_lookup(vxl->vni);
+ return zl3vni;
+}
+
+/*
+ * Inform BGP about l3-vni.
+ */
+static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni)
+{
+ struct stream *s = NULL;
+ struct zserv *client = NULL;
+ struct ethaddr rmac;
+ char buf[ETHER_ADDR_STRLEN];
+
+ client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ /* get the rmac */
+ memset(&rmac, 0, sizeof(struct ethaddr));
+ zl3vni_get_rmac(zl3vni, &rmac);
+
+ s = client->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_L3VNI_ADD,
+ zl3vni_vrf_id(zl3vni));
+ stream_putl(s, zl3vni->vni);
+ stream_put(s, &rmac, sizeof(struct ethaddr));
+ stream_put_in_addr(s, &zl3vni->local_vtep_ip);
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s to %s",
+ zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
+ prefix_mac2str(&rmac, buf, sizeof(buf)),
+ inet_ntoa(zl3vni->local_vtep_ip),
+ zebra_route_string(client->proto));
+
+ client->l3vniadd_cnt++;
+ return zebra_server_send_message(client);
+}
+
+/*
+ * Inform BGP about local l3-VNI deletion.
+ */
+static int zl3vni_send_del_to_client(zebra_l3vni_t *zl3vni)
+{
+ struct stream *s = NULL;
+ struct zserv *client = NULL;
+
+ client = zebra_find_client(ZEBRA_ROUTE_BGP, 0);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = client->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_L3VNI_DEL,
+ zl3vni_vrf_id(zl3vni));
+ stream_putl(s, zl3vni->vni);
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Send L3_VNI_DEL %u VRF %s to %s",
+ zl3vni->vni,
+ vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
+ zebra_route_string(client->proto));
+
+ client->l3vnidel_cnt++;
+ return zebra_server_send_message(client);
+}
+
+static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni)
+{
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("L3-VNI %u is UP - send add to BGP",
+ zl3vni->vni);
+
+ /* send l3vni add to BGP */
+ zl3vni_send_add_to_client(zl3vni);
+}
+
+static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni)
+{
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("L3-VNI %u is Down - Send del to BGP",
+ zl3vni->vni);
+
+ /* send l3-vni del to BGP*/
+ zl3vni_send_del_to_client(zl3vni);
+}
+
+static void zvni_add_to_l3vni_list(struct hash_backet *backet,
+ void *ctxt)
+{
+ zebra_vni_t *zvni = (zebra_vni_t *) backet->data;
+ zebra_l3vni_t *zl3vni = (zebra_l3vni_t *) ctxt;
+
+ if (zvni->vrf_id == zl3vni_vrf_id(zl3vni))
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+}
+
+/*
+ * handle transition of vni from l2 to l3 and vice versa
+ */
+static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf,
+ vni_t vni, int add)
+{
+ zebra_vni_t *zvni = NULL;
+
+ /* There is a possibility that VNI notification was already received
+ * from kernel and we programmed it as L2-VNI
+ * In such a case we need to delete this L2-VNI first, so
+ * that it can be reprogrammed as L3-VNI in the system. It is also
+ * possible that the vrf-vni mapping is removed from FRR while the vxlan
+ * interface is still present in kernel. In this case to keep it
+ * symmetric, we will delete the l3-vni and reprogram it as l2-vni
+ */
+ if (add) {
+ /* Locate hash entry */
+ zvni = zvni_lookup(vni);
+ if (!zvni)
+ return 0;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Del L2-VNI %u - transition to L3-VNI",
+ vni);
+
+ /* Delete VNI from BGP. */
+ zvni_send_del_to_client(zvni->vni);
+
+ /* Free up all neighbors and MAC, if any. */
+ zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all(zvni, 0);
+
+ /* Delete the hash entry. */
+ if (zvni_del(zvni)) {
+ zlog_err("Failed to del VNI hash %p, VNI %u",
+ zvni, zvni->vni);
+ return -1;
+ }
+ } else {
+ /* TODO_MITESH: This needs to be thought through. We don't have
+ * enough information at this point to reprogram the vni as
+ * l2-vni. One way is to store the required info in l3-vni and
+ * used it solely for this purpose
+ */
+ }
+
+ return 0;
+}
+
+/* delete and uninstall rmac hash entry */
+static void zl3vni_del_rmac_hash_entry(struct hash_backet *backet,
+ void *ctx)
+{
+ zebra_mac_t *zrmac = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zrmac = (zebra_mac_t *)backet->data;
+ zl3vni = (zebra_l3vni_t *)ctx;
+ zl3vni_rmac_uninstall(zl3vni, zrmac);
+ zl3vni_rmac_del(zl3vni, zrmac);
+}
+
+/* delete and uninstall nh hash entry */
+static void zl3vni_del_nh_hash_entry(struct hash_backet *backet,
+ void *ctx)
+{
+ zebra_neigh_t *n = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ n = (zebra_neigh_t *)backet->data;
+ zl3vni = (zebra_l3vni_t *)ctx;
+ zl3vni_nh_uninstall(zl3vni, n);
+ zl3vni_nh_del(zl3vni, n);
+}
+
+/* Public functions */
+
+/* handle evpn route in vrf table */
+void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_vrf(vrf_id);
+ if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+ return;
+
+ /* add the next hop neighbor */
+ zl3vni_remote_nh_add(zl3vni, vtep_ip, rmac, host_prefix);
+
+ /* add the rmac */
+ zl3vni_remote_rmac_add(zl3vni, rmac, vtep_ip, host_prefix);
+}
+
+/* handle evpn vrf route delete */
+void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_vrf(vrf_id);
+ if (!zl3vni)
+ return;
+
+ /* delete the next hop entry */
+ zl3vni_remote_nh_del(zl3vni, vtep_ip, host_prefix);
+
+ /* delete the rmac entry */
+ zl3vni_remote_rmac_del(zl3vni, rmac, host_prefix);
+}
+
+void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty,
+ vni_t l3vni,
+ struct ethaddr *rmac,
+ u_char use_json)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ zebra_mac_t *zrmac = NULL;
+ json_object *json = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ zl3vni = zl3vni_lookup(l3vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% L3-VNI %u doesnt exist\n",
+ l3vni);
+ return;
+ }
+
+ zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
+ if (!zrmac) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty,
+ "%% Requested RMAC doesnt exist in L3-VNI %u",
+ l3vni);
+ return;
+ }
+
+ zl3vni_print_rmac(zrmac, vty, json);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_rmacs_l3vni(struct vty *vty,
+ vni_t l3vni,
+ u_char use_json)
+{
+ zebra_l3vni_t *zl3vni;
+ u_int32_t num_rmacs;
+ struct rmac_walk_ctx wctx;
+ json_object *json = NULL;
+
+ if (!is_evpn_enabled())
+ return;
+
+ zl3vni = zl3vni_lookup(l3vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni);
+ return;
+ }
+ num_rmacs = hashcount(zl3vni->rmac_table);
+ if (!num_rmacs)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ memset(&wctx, 0, sizeof(struct rmac_walk_ctx));
+ wctx.vty = vty;
+ wctx.json = json;
+ if (!use_json) {
+ vty_out(vty,
+ "Number of Remote RMACs known for this VNI: %u\n",
+ num_rmacs);
+ vty_out(vty, "%-17s %-21s %-6s\n", "MAC",
+ "Remote VTEP", "Refcnt");
+ } else
+ json_object_int_add(json, "numRmacs", num_rmacs);
+
+ hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty,
+ u_char use_json)
+{
+ struct zebra_ns *zns = NULL;
+ json_object *json = NULL;
+ void *args[2];
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(zns->l3vni_table,
+ (void (*)(struct hash_backet *,
+ void *))zl3vni_print_rmac_hash_all_vni,
+ args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty,
+ vni_t l3vni,
+ struct ipaddr *ip,
+ u_char use_json)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ zebra_neigh_t *n = NULL;
+ json_object *json = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ zl3vni = zl3vni_lookup(l3vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni);
+ return;
+ }
+
+ n = zl3vni_nh_lookup(zl3vni, ip);
+ if (!n) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty,
+ "%% Requested next-hop not present for L3-VNI %u",
+ l3vni);
+ return;
+ }
+
+ zl3vni_print_nh(n, vty, json);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_nh_l3vni(struct vty *vty,
+ vni_t l3vni,
+ u_char use_json)
+{
+ u_int32_t num_nh;
+ struct nh_walk_ctx wctx;
+ json_object *json = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ if (!is_evpn_enabled())
+ return;
+
+ zl3vni = zl3vni_lookup(l3vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni);
+ return;
+ }
+
+ num_nh = hashcount(zl3vni->nh_table);
+ if (!num_nh)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ wctx.vty = vty;
+ wctx.json = json;
+ if (!use_json) {
+ vty_out(vty,
+ "Number of NH Neighbors known for this VNI: %u\n",
+ num_nh);
+ vty_out(vty, "%-15s %-17s %6s\n", "IP",
+ "RMAC", "Refcnt");
+ } else
+ json_object_int_add(json, "numNh", num_nh);
+
+ hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+void zebra_vxlan_print_nh_all_l3vni(struct vty *vty,
+ u_char use_json)
+{
+ struct zebra_ns *zns = NULL;
+ json_object *json = NULL;
+ void *args[2];
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(zns->l3vni_table,
+ (void (*)(struct hash_backet *,
+ void *))zl3vni_print_nh_hash_all_vni,
+ args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return;
+}
+
+/*
+ * Display L3 VNI information (VTY command handler).
+ */
+void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, u_char use_json)
+{
+ void *args[2];
+ json_object *json = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ args[0] = vty;
+ args[1] = json;
+ zl3vni_print(zl3vni, (void *)args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+/*
+ * Display L3 VNI hash table (VTY command handler).
+ */
+void zebra_vxlan_print_l3vnis(struct vty *vty, u_char use_json)
+{
+ u_int32_t num_vnis;
+ void *args[2];
+ json_object *json = NULL;
+ struct zebra_ns *zns = NULL;
+
+ if (!is_evpn_enabled()) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ assert(zns);
+
+ num_vnis = hashcount(zns->l3vni_table);
+ if (!num_vnis) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return;
+ }
+
+ if (use_json) {
+ json = json_object_new_object();
+ json_object_int_add(json, "numVnis", num_vnis);
+ } else {
+ vty_out(vty, "Number of L3 VNIs: %u\n", num_vnis);
+ vty_out(vty, "%-10s %-15s %-20s %-20s %-5s %-37s %-18s\n",
+ "VNI", "Local-ip", "Vx-intf", "L3-SVI", "State",
+ "VRF", "Rmac");
+ }
+
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(zns->l3vni_table,
+ (void (*)(struct hash_backet *, void *))zl3vni_print_hash,
+ args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+/*
+ * Display Neighbors for a VNI (VTY command handler).
+ */
+void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
+ vni_t vni, u_char use_json)
+{
+ zebra_vni_t *zvni;
+ u_int32_t num_neigh;
+ struct neigh_walk_ctx wctx;
+ json_object *json = NULL;
+
+ if (!is_evpn_enabled())
+ return;
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+ num_neigh = hashcount(zvni->neigh_table);
+ if (!num_neigh)
+ return;
+
+ if (use_json)
+ json = json_object_new_object();
+
+ /* Since we have IPv6 addresses to deal with which can vary widely in
+ * size, we try to be a bit more elegant in display by first computing
+ * the maximum width.
+ */
+ memset(&wctx, 0, sizeof(struct neigh_walk_ctx));
+ wctx.zvni = zvni;
+ wctx.vty = vty;
+ wctx.addr_width = 15;
+ wctx.json = json;
+ hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
+
+ if (!use_json) {
+ vty_out(vty,
+ "Number of ARPs (local and remote) known for this VNI: %u\n",
+ num_neigh);
+ vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP",
+ "Type", "MAC", "Remote VTEP");
+ } else
+ json_object_int_add(json, "numArpNd", num_neigh);
+
+ hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
vty_out(vty, "Advertise gateway mac-ip: %s\n",
zvrf->advertise_gw_macip ? "Yes" : "No");
vty_out(vty, "Number of VNIs: %u\n", num_vnis);
- vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI",
+ vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s %-37s\n", "VNI",
"VxLAN IF", "VTEP IP", "# MACs", "# ARPs",
- "# Remote VTEPs");
+ "# Remote VTEPs", "VRF");
}
args[0] = vty;
args[1] = json;
int zebra_vxlan_local_neigh_del(struct interface *ifp,
struct interface *link_if, struct ipaddr *ip)
{
- zebra_vni_t *zvni;
- zebra_neigh_t *n;
char buf[INET6_ADDRSTRLEN];
char buf2[ETHER_ADDR_STRLEN];
- zebra_mac_t *zmac;
+ zebra_neigh_t *n = NULL;
+ zebra_vni_t *zvni = NULL;
+ zebra_mac_t *zmac = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* check if this is a remote neigh entry corresponding to remote
+ * next-hop
+ */
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni)
+ return zl3vni_local_nh_del(zl3vni, ip);
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
*/
- zvni = zvni_map_svi(ifp, link_if);
+ zvni = zvni_from_svi(ifp, link_if);
if (!zvni)
return 0;
+
if (!zvni->vxlan_if) {
zlog_err(
"VNI %u hash %p doesn't have intf upon local neighbor DEL",
}
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Del neighbor %s intf %s(%u) -> VNI %u",
+ zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u",
ipaddr2str(ip, buf, sizeof(buf)),
ifp->name, ifp->ifindex, zvni->vni);
struct ethaddr *macaddr, u_int16_t state,
u_char ext_learned)
{
- zebra_vni_t *zvni;
- zebra_neigh_t *n;
- zebra_mac_t *zmac, *old_zmac;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
+ zebra_vni_t *zvni = NULL;
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *zmac = NULL, *old_zmac = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* check if this is a remote neigh entry corresponding to remote
+ * next-hop
+ */
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni)
+ return zl3vni_local_nh_add_update(zl3vni, ip, state);
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
*/
- zvni = zvni_map_svi(ifp, link_if);
+ zvni = zvni_from_svi(ifp, link_if);
if (!zvni)
return 0;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x "
- "%s-> VNI %u",
+ "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s-> L2-VNI %u",
ipaddr2str(ip, buf2, sizeof(buf2)),
prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
ifp->ifindex, state, ext_learned ? "ext-learned " : "",
/* Inform BGP. */
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("neigh %s (MAC %s) is now ACTIVE on VNI %u",
+ zlog_debug("neigh %s (MAC %s) is now ACTIVE on L2-VNI %u",
ipaddr2str(ip, buf2, sizeof(buf2)),
prefix_mac2str(macaddr, buf, sizeof(buf)),
zvni->vni);
-
ZEBRA_NEIGH_SET_ACTIVE(n);
+
return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, 0);
}
struct interface *ifp = NULL;
struct zebra_if *zif = NULL;
+ memset(&macaddr, 0, sizeof(struct ethaddr));
+ memset(&ip, 0, sizeof(struct ipaddr));
+ memset(&vtep_ip, 0, sizeof(struct in_addr));
+
s = client->ibuf;
while (l < length) {
struct interface *ifp = NULL;
struct zebra_if *zif = NULL;
+ memset(&macaddr, 0, sizeof(struct ethaddr));
+ memset(&ip, 0, sizeof(struct ipaddr));
+ memset(&vtep_ip, 0, sizeof(struct in_addr));
+
if (!EVPN_ENABLED(zvrf)) {
zlog_warn("%s: EVPN Not turned on yet we have received a remote_macip add zapi callback",
__PRETTY_FUNCTION__);
svi_if_link = if_lookup_by_index_per_ns(
zebra_ns_lookup(NS_DEFAULT),
svi_if_zif->link_ifindex);
- zvni = zvni_map_svi(svi_if, svi_if_link);
+ zvni = zvni_from_svi(svi_if, svi_if_link);
}
} else if (IS_ZEBRA_IF_BRIDGE(svi_if)) {
/*
* If it is a vlan unaware bridge then svi is the bridge
* itself
*/
- zvni = zvni_map_svi(svi_if, svi_if);
+ zvni = zvni_from_svi(svi_if, svi_if);
}
} else if (IS_ZEBRA_IF_VLAN(ifp)) {
struct zebra_if *svi_if_zif =
svi_if_link = if_lookup_by_index_per_ns(
zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex);
if (svi_if_zif && svi_if_link)
- zvni = zvni_map_svi(ifp, svi_if_link);
+ zvni = zvni_from_svi(ifp, svi_if_link);
} else if (IS_ZEBRA_IF_BRIDGE(ifp)) {
- zvni = zvni_map_svi(ifp, ifp);
+ zvni = zvni_from_svi(ifp, ifp);
}
if (!zvni)
}
/*
- * Handle SVI interface going down. At this point, this is a NOP since
- * the kernel deletes the neighbor entries on this SVI (if any).
+ * Handle SVI interface going down.
+ * SVI can be associated to either L3-VNI or L2-VNI.
+ * For L2-VNI: At this point, this is a NOP since
+ * the kernel deletes the neighbor entries on this SVI (if any).
+ * We only need to update the vrf corresponding to zvni.
+ * For L3-VNI: L3-VNI is operationally down, update mac-ip routes and delete
+ * from bgp
*/
int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if)
{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni) {
+
+ /* process l3-vni down */
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ /* remove association with svi-if */
+ zl3vni->svi_if = NULL;
+ } else {
+ zebra_vni_t *zvni = NULL;
+
+ /* since we dont have svi corresponding to zvni, we associate it
+ * to default vrf. Note: the corresponding neigh entries on the
+ * SVI would have already been deleted */
+ zvni = zvni_from_svi(ifp, link_if);
+ if (zvni) {
+ zvni->vrf_id = VRF_DEFAULT;
+
+ /* update the tenant vrf in BGP */
+ zvni_send_add_to_client(zvni);
+ }
+ }
return 0;
}
/*
- * Handle SVI interface coming up. This may or may not be of interest,
- * but if this is a SVI on a VxLAN bridge, we need to install any remote
- * neighbor entries (which will be used for EVPN ARP suppression).
+ * Handle SVI interface coming up.
+ * SVI can be associated to L3-VNI (l3vni vxlan interface) or L2-VNI (l2-vni
+ * vxlan intf).
+ * For L2-VNI: we need to install any remote neighbors entried (used for
+ * apr-suppression)
+ * For L3-VNI: SVI will be used to get the rmac to be used with L3-VNI
*/
int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
{
- zebra_vni_t *zvni;
- struct neigh_walk_ctx n_wctx;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
- zvni = zvni_map_svi(ifp, link_if);
- if (!zvni)
- return 0;
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni) {
- if (!zvni->vxlan_if) {
- zlog_err("VNI %u hash %p doesn't have intf upon SVI up",
- zvni->vni, zvni);
- return -1;
- }
+ /* associate with svi */
+ zl3vni->svi_if = ifp;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("SVI %s(%u) VNI %u is UP, installing neighbors",
- ifp->name, ifp->ifindex, zvni->vni);
+ /* process oper-up */
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ } else {
+
+ /* process SVI up for l2-vni */
+ struct neigh_walk_ctx n_wctx;
+
+ zvni = zvni_from_svi(ifp, link_if);
+ if (!zvni)
+ return 0;
+
+ if (!zvni->vxlan_if) {
+ zlog_err("VNI %u hash %p doesn't have intf upon SVI up",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("SVI %s(%u) VNI %u VRF %s is UP, installing neighbors",
+ ifp->name, ifp->ifindex, zvni->vni,
+ vrf_id_to_name(ifp->vrf_id));
- /* Install any remote neighbors for this VNI. */
- memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
- n_wctx.zvni = zvni;
- hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx);
+ /* update the vrf information for l2-vni and inform bgp */
+ zvni->vrf_id = ifp->vrf_id;
+ zvni_send_add_to_client(zvni);
+
+ /* Install any remote neighbors for this VNI. */
+ memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+ n_wctx.zvni = zvni;
+ hash_iterate(zvni->neigh_table,
+ zvni_install_neigh_hash,
+ &n_wctx);
+ }
return 0;
}
/*
- * Handle VxLAN interface down - update BGP if required, and do
- * internal cleanup.
+ * Handle VxLAN interface down
*/
int zebra_vxlan_if_down(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Intf %s(%u) VNI %u is DOWN",
- ifp->name, ifp->ifindex, vni);
- /* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return -1;
- }
+ if (is_vni_l3(vni)) {
- assert(zvni->vxlan_if == ifp);
+ /* process-if-down for l3-vni */
+ zebra_l3vni_t *zl3vni = NULL;
- /* Delete this VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L3-VNI %u is DOWN",
+ ifp->name, ifp->ifindex, vni);
- /* Free up all neighbors and MACs, if any. */
- zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at DOWN, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
- /* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 1);
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ } else {
+ /* process if-down for l2-vni */
+ zebra_vni_t *zvni;
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L2-VNI %u is DOWN",
+ ifp->name, ifp->ifindex, vni);
+
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ assert(zvni->vxlan_if == ifp);
+
+ /* Delete this VNI from BGP. */
+ zvni_send_del_to_client(zvni->vni);
+
+ /* Free up all neighbors and MACs, if any. */
+ zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all(zvni, 1);
+ }
return 0;
}
*/
int zebra_vxlan_if_up(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Intf %s(%u) VNI %u is UP",
- ifp->name, ifp->ifindex, vni);
+ if (is_vni_l3(vni)) {
- /* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to locate VNI hash at UP, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return -1;
- }
+ /* Handle L3-VNI add */
+ zebra_l3vni_t *zl3vni = NULL;
- assert(zvni->vxlan_if == ifp);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L3-VNI %u is UP",
+ ifp->name, ifp->ifindex, vni);
- /* If part of a bridge, inform BGP about this VNI. */
- /* Also, read and populate local MACs and neighbors. */
- if (zif->brslave_info.br_if) {
- zvni_send_add_to_client(zvni);
- zvni_read_mac_neigh(zvni, ifp);
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at UP, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ /* we need to associate with SVI, if any, we can associate with
+ * svi-if only after association with vxlan-intf is complete
+ */
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ } else {
+ /* Handle L2-VNI add */
+
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct interface *vlan_if = NULL;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L2-VNI %u is UP",
+ ifp->name, ifp->ifindex, vni);
+
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to locate VNI hash at UP, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ assert(zvni->vxlan_if == ifp);
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if) {
+ zvni->vrf_id = vlan_if->vrf_id;
+ zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
+ if (zl3vni)
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+ }
+
+ /* If part of a bridge, inform BGP about this VNI. */
+ /* Also, read and populate local MACs and neighbors. */
+ if (zif->brslave_info.br_if) {
+ zvni_send_add_to_client(zvni);
+ zvni_read_mac_neigh(zvni, ifp);
+ }
}
return 0;
*/
int zebra_vxlan_if_del(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Del VNI %u intf %s(%u)",
- vni, ifp->name, ifp->ifindex);
+ if (is_vni_l3(vni)) {
- /* Locate hash entry; it is expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to locate VNI hash at del, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return 0;
- }
+ /* process if-del for l3-vni */
+ zebra_l3vni_t *zl3vni = NULL;
- /* Delete VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Del L3-VNI %u intf %s(%u)",
+ vni, ifp->name, ifp->ifindex);
- /* Free up all neighbors and MAC, if any. */
- zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return 0;
+ }
- /* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 0);
+ /* process oper-down for l3-vni */
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
- /* Delete the hash entry. */
- if (zvni_del(zvni)) {
- zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u",
- zvni, ifp->name, ifp->ifindex, zvni->vni);
- return -1;
+ /* remove the association with vxlan_if */
+ memset(&zl3vni->local_vtep_ip, 0, sizeof(struct in_addr));
+ zl3vni->vxlan_if = NULL;
+ } else {
+
+ /* process if-del for l2-vni*/
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Del L2-VNI %u intf %s(%u)",
+ vni, ifp->name, ifp->ifindex);
+
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to locate VNI hash at del, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return 0;
+ }
+
+ /* remove from l3-vni list */
+ zl3vni = zl3vni_from_vrf(zvni->vrf_id);
+ if (zl3vni)
+ listnode_delete(zl3vni->l2vnis, zvni);
+
+ /* Delete VNI from BGP. */
+ zvni_send_del_to_client(zvni->vni);
+
+ /* Free up all neighbors and MAC, if any. */
+ zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all(zvni, 0);
+
+ /* Delete the hash entry. */
+ if (zvni_del(zvni)) {
+ zlog_err("Failed to del VNI hash %p, IF %s(%u) VNI %u",
+ zvni, ifp->name, ifp->ifindex, zvni->vni);
+ return -1;
+ }
}
return 0;
*/
int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- /* Update VNI hash. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_err(
- "Failed to find VNI hash on update, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return -1;
- }
+ if (is_vni_l3(vni)) {
+ zebra_l3vni_t *zl3vni = NULL;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Update VNI %u intf %s(%u) VLAN %u local IP %s "
- "master %u chg 0x%x",
- vni, ifp->name, ifp->ifindex,
- vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
- zif->brslave_info.bridge_ifindex, chgflags);
-
- /* Removed from bridge? Cleanup and return */
- if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
- && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
- /* Delete from client, remove all remote VTEPs */
- /* Also, free up all MACs and neighbors. */
- zvni_send_del_to_client(zvni->vni);
- zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
- zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
- zvni_vtep_del_all(zvni, 1);
- return 0;
- }
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ zlog_err(
+ "Failed to find L3-VNI hash on update, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Update L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x",
+ vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex, chgflags);
+
+ /* Removed from bridge? Cleanup and return */
+ if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
+ && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ return 0;
+ }
- /* Handle other changes. */
- if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
- /* Remove all existing local neighbors and MACs for this VNI
- * (including from BGP)
+ /* access-vlan change - process oper down, associate with new
+ * svi_if and then process oper up again
*/
- zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
- zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
- }
+ if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
+ if (if_is_operative(ifp)) {
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ zl3vni->svi_if = NULL;
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(
+ zl3vni);
+ }
+ }
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->vxlan_if = ifp;
+ /* if we have a valid new master, process l3-vni oper up */
+ if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) {
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ }
+ } else {
+ zebra_vni_t *zvni = NULL;
- /* Take further actions needed. Note that if we are here, there is a
- * change of interest.
- */
- /* If down or not mapped to a bridge, we're done. */
- if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
- return 0;
+ /* Update VNI hash. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
- /* Inform BGP, if there is a change of interest. */
- if (chgflags
- & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE))
- zvni_send_add_to_client(zvni);
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Update L2-VNI %u intf %s(%u) VLAN %u local IP %s master %u chg 0x%x",
+ vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex, chgflags);
+
+ /* Removed from bridge? Cleanup and return */
+ if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
+ && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
+ /* Delete from client, remove all remote VTEPs */
+ /* Also, free up all MACs and neighbors. */
+ zvni_send_del_to_client(zvni->vni);
+ zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC);
+ zvni_vtep_del_all(zvni, 1);
+ return 0;
+ }
- /* If there is a valid new master or a VLAN mapping change, read and
- * populate local MACs and neighbors. Also, reinstall any remote MACs
- * and neighbors for this VNI (based on new VLAN).
- */
- if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
- zvni_read_mac_neigh(zvni, ifp);
- else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
- struct mac_walk_ctx m_wctx;
- struct neigh_walk_ctx n_wctx;
+ /* Handle other changes. */
+ if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
+ /* Remove all existing local neigh and MACs for this VNI
+ * (including from BGP)
+ */
+ zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
+ zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC);
+ }
- zvni_read_mac_neigh(zvni, ifp);
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
- memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
- m_wctx.zvni = zvni;
- hash_iterate(zvni->mac_table, zvni_install_mac_hash, &m_wctx);
+ /* Take further actions needed.
+ * Note that if we are here, there is a change of interest.
+ */
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return 0;
- memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
- n_wctx.zvni = zvni;
- hash_iterate(zvni->neigh_table, zvni_install_neigh_hash,
- &n_wctx);
+ /* Inform BGP, if there is a change of interest. */
+ if (chgflags
+ & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE))
+ zvni_send_add_to_client(zvni);
+
+ /* If there is a valid new master or a VLAN mapping change,
+ * read and populate local MACs and neighbors.
+ * Also, reinstall any remote MACs and neighbors
+ * for this VNI (based on new VLAN).
+ */
+ if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
+ zvni_read_mac_neigh(zvni, ifp);
+ else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
+ struct mac_walk_ctx m_wctx;
+ struct neigh_walk_ctx n_wctx;
+
+ zvni_read_mac_neigh(zvni, ifp);
+
+ memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
+ m_wctx.zvni = zvni;
+ hash_iterate(zvni->mac_table,
+ zvni_install_mac_hash,
+ &m_wctx);
+
+ memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx));
+ n_wctx.zvni = zvni;
+ hash_iterate(zvni->neigh_table, zvni_install_neigh_hash,
+ &n_wctx);
+ }
}
return 0;
*/
int zebra_vxlan_if_add(struct interface *ifp)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
vxl = &zif->l2info.vxl;
vni = vxl->vni;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "Add VNI %u intf %s(%u) VLAN %u local IP %s master %u",
- vni, ifp->name, ifp->ifindex,
- vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
- zif->brslave_info.bridge_ifindex);
+ if (is_vni_l3(vni)) {
- /* Create or update VNI hash. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zvni = zvni_add(vni);
- if (!zvni) {
+ /* process if-add for l3-vni*/
+ zebra_l3vni_t *zl3vni = NULL;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Add L3-VNI %u intf %s(%u) VLAN %u local IP %s master %u",
+ vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex);
+
+ /*
+ * we expect the l3-vni has entry to be present here.
+ * The only place l3-vni is created in zebra is vrf-vni mapping
+ * command. This might change when we have the switchd support
+ * for l3-vxlan interface.
+ */
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
zlog_err(
- "Failed to add VNI hash, IF %s(%u) VNI %u",
+ "Failed to locate L3-VNI hash at del, IF %s(%u) VNI %u",
ifp->name, ifp->ifindex, vni);
+ return 0;
+ }
+
+ /* associate with vxlan_if */
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ zl3vni->vxlan_if = ifp;
+
+ /* Associate with SVI, if any. We can associate with svi-if only
+ * after association with vxlan_if is complete */
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ } else {
+
+ /* process if-add for l2-vni */
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct interface *vlan_if = NULL;
+
+ /* Create or update VNI hash. */
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ zvni = zvni_add(vni);
+ if (!zvni) {
+ zlog_err(
+ "Failed to add VNI hash, IF %s(%u) VNI %u",
+ ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+ }
+
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if) {
+ zvni->vrf_id = vlan_if->vrf_id;
+ zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
+ if (zl3vni)
+ listnode_add_sort(zl3vni->l2vnis, zvni);
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %s master %u",
+ vni,
+ vlan_if ? vrf_id_to_name(vlan_if->vrf_id) :
+ "Default",
+ ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa(vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex);
+
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return 0;
+
+ /* Inform BGP */
+ zvni_send_add_to_client(zvni);
+
+ /* Read and populate local MACs and neighbors */
+ zvni_read_mac_neigh(zvni, ifp);
+ }
+
+ return 0;
+}
+
+int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
+ vni_t vni,
+ char *err, int err_str_sz,
+ int add)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_vrf *zvrf_default = NULL;
+
+ zvrf_default = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+ if (!zvrf_default)
+ return -1;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("vrf %s vni %u %s",
+ zvrf_name(zvrf),
+ vni,
+ add ? "ADD" : "DEL");
+
+ if (add) {
+
+ zebra_vxlan_handle_vni_transition(zvrf, vni, add);
+
+ /* check if the vni is already present under zvrf */
+ if (zvrf->l3vni) {
+ snprintf(err, err_str_sz,
+ "VNI is already configured under the vrf");
+ return -1;
+ }
+
+ /* check if this VNI is already present in the system */
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
+ snprintf(err, err_str_sz,
+ "VNI is already configured as L3-VNI");
+ return -1;
+ }
+
+ /* add the L3-VNI to the global table */
+ zl3vni = zl3vni_add(vni, zvrf_id(zvrf));
+ if (!zl3vni) {
+ snprintf(err, err_str_sz,
+ "Could not add L3-VNI");
+ return -1;
+ }
+
+ /* associate the vrf with vni */
+ zvrf->l3vni = vni;
+
+ /* associate with vxlan-intf;
+ * we need to associate with the vxlan-intf first
+ */
+ zl3vni->vxlan_if = zl3vni_map_to_vxlan_if(zl3vni);
+
+ /* associate with corresponding SVI interface, we can associate
+ * with svi-if only after vxlan interface association is
+ * complete
+ */
+ zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
+
+ /* formulate l2vni list */
+ hash_iterate(zvrf_default->vni_table,
+ zvni_add_to_l3vni_list, zl3vni);
+
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+
+ } else {
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ snprintf(err, err_str_sz, "VNI doesn't exist");
return -1;
}
+
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ /* delete and uninstall all rmacs */
+ hash_iterate(zl3vni->rmac_table,
+ zl3vni_del_rmac_hash_entry,
+ zl3vni);
+
+ /* delete and uninstall all next-hops */
+ hash_iterate(zl3vni->nh_table,
+ zl3vni_del_nh_hash_entry,
+ zl3vni);
+
+ zvrf->l3vni = 0;
+ zl3vni_del(zl3vni);
+
+ zebra_vxlan_handle_vni_transition(zvrf, vni, add);
}
+ return 0;
+}
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->vxlan_if = ifp;
+int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf)
+{
+ zebra_l3vni_t *zl3vni = NULL;
- /* If down or not mapped to a bridge, we're done. */
- if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ zl3vni = zl3vni_from_vrf(zvrf_id(zvrf));
+ if (!zl3vni)
return 0;
- /* Inform BGP */
- zvni_send_add_to_client(zvni);
-
- /* Read and populate local MACs and neighbors */
- zvni_read_mac_neigh(zvni, ifp);
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ zl3vni_del(zl3vni);
+ zebra_vxlan_handle_vni_transition(zvrf, zl3vni->vni, 0);
return 0;
}
int zebra_vxlan_advertise_all_vni(struct zserv *client,
u_short length, struct zebra_vrf *zvrf)
{
- struct stream *s;
- int advertise;
+ struct stream *s = NULL;
+ int advertise = 0;
+ struct zebra_ns *zns = NULL;
if (zvrf_id(zvrf) != VRF_DEFAULT) {
zlog_err("EVPN VNI Adv for non-default VRF %u",
* kernel and free entries.
*/
hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
+
+ /* cleanup all l3vnis */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns)
+ return -1;
+
+ hash_iterate(zns->l3vni_table, zl3vni_cleanup_all, NULL);
}
stream_failure:
hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
hash_free(zvrf->vni_table);
}
+
+/* init the l3vni table */
+void zebra_vxlan_ns_init(struct zebra_ns *zns)
+{
+ zns->l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp,
+ "Zebra VRF L3 VNI table");
+}
+
+/* free l3vni table */
+void zebra_vxlan_ns_disable(struct zebra_ns *zns)
+{
+ hash_free(zns->l3vni_table);
+}
+
+/* get the l3vni svi ifindex */
+ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_vrf(vrf_id);
+ if (!zl3vni || !is_l3vni_oper_up(zl3vni))
+ return 0;
+
+ return zl3vni->svi_if->ifindex;
+}
#define VNI_STR_LEN 32
+extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id);
+extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf);
+extern void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni,
+ struct ipaddr *ip, u_char uj);
+extern void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni,
+ struct ethaddr *rmac,
+ u_char use_json);
extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
vni_t vni, u_char use_json);
extern void zebra_vxlan_print_macs_all_vni(struct vty *vty,
vni_t vni, u_char use_json);
extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
u_char use_json);
+extern void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t vni, u_char
+ use_json);
+extern void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, u_char use_json);
+extern void zebra_vxlan_print_nh_l3vni(struct vty *vty, vni_t vni, u_char
+ use_json);
+extern void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, u_char use_json);
+extern void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni,
+ u_char use_json);
+extern void zebra_vxlan_print_l3vnis(struct vty *vty,
+ u_char use_json);
extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
int add);
extern int zebra_vxlan_advertise_all_vni(struct zserv *client,
u_short length,
struct zebra_vrf *zvrf);
+extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
+ char *err,
+ int err_str_sz, int add);
extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf);
extern void zebra_vxlan_close_tables(struct zebra_vrf *);
+extern void zebra_vxlan_ns_init(struct zebra_ns *zns);
+extern void zebra_vxlan_ns_disable(struct zebra_ns *zns);
+extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ipaddr *ip,
+ struct prefix *host_prefix);
+extern void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ipaddr *vtep_ip,
+ struct prefix *host_prefix);
#endif /* _ZEBRA_VXLAN_H */
{
}
+void zebra_vxlan_print_rmacs_l3vni(struct vty*, vni_t, u_char)
+{
+}
+
+void zebra_vxlan_print_rmacs_all_l3vni(struct vty*, u_char)
+{
+}
+
+void zebra_vxlan_print_nh_l3vni(struct vty*, vni_t, u_char)
+{
+}
+
+void zebra_vxlan_print_nh_all_l3vni(struct vty*, u_char)
+{
+}
+
+void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni)
+{
+}
+
+void zebra_vxlan_print_l3vnis(struct vty *vty)
+{
+}
+
int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
{
return 0;
#include "if.h"
#include "linklist.h"
+#include "zebra_vxlan.h"
+
+#define ERR_STR_SZ 256
/* definitions */
typedef struct zebra_vni_t_ zebra_vni_t;
typedef struct zebra_vtep_t_ zebra_vtep_t;
typedef struct zebra_mac_t_ zebra_mac_t;
typedef struct zebra_neigh_t_ zebra_neigh_t;
+typedef struct zebra_l3vni_t_ zebra_l3vni_t;
/*
* VTEP info
/* Local IP */
struct in_addr local_vtep_ip;
+ /* tenant VRF, if any */
+ vrf_id_t vrf_id;
+
/* List of local or remote MAC */
struct hash *mac_table;
struct hash *neigh_table;
};
+/* L3 VNI hash table */
+struct zebra_l3vni_t_ {
+
+ /* VNI key */
+ vni_t vni;
+
+ /* vrf_id */
+ vrf_id_t vrf_id;
+
+ /* Local IP */
+ struct in_addr local_vtep_ip;
+
+ /* kernel interface for l3vni */
+ struct interface *vxlan_if;
+
+ /* SVI interface corresponding to the l3vni */
+ struct interface *svi_if;
+
+ /* list of L2 VNIs associated with the L3 VNI */
+ struct list *l2vnis;
+
+ /* list of remote router-macs */
+ struct hash *rmac_table;
+
+ /* list of remote vtep-ip neigh */
+ struct hash *nh_table;
+};
+
+/* get the vx-intf name for l3vni */
+static inline const char *zl3vni_vxlan_if_name(zebra_l3vni_t *zl3vni)
+{
+ return zl3vni->vxlan_if ? zl3vni->vxlan_if->name : "None";
+}
+
+/* get the svi intf name for l3vni */
+static inline const char *zl3vni_svi_if_name(zebra_l3vni_t *zl3vni)
+{
+ return zl3vni->svi_if ? zl3vni->svi_if->name : "None";
+}
+
+/* get the vrf name for l3vni */
+static inline const char *zl3vni_vrf_name(zebra_l3vni_t *zl3vni)
+{
+ return vrf_id_to_name(zl3vni->vrf_id);
+}
+
+/* get the rmac string */
+static inline const char *zl3vni_rmac2str(zebra_l3vni_t *zl3vni, char *buf,
+ int size)
+{
+ char *ptr;
+
+ if (!buf)
+ ptr = (char *)XMALLOC(MTYPE_TMP,
+ ETHER_ADDR_STRLEN * sizeof(char));
+ else {
+ assert(size >= ETHER_ADDR_STRLEN);
+ ptr = buf;
+ }
+
+ if (zl3vni->svi_if)
+ snprintf(ptr, (ETHER_ADDR_STRLEN),
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ (uint8_t)zl3vni->svi_if->hw_addr[0],
+ (uint8_t)zl3vni->svi_if->hw_addr[1],
+ (uint8_t)zl3vni->svi_if->hw_addr[2],
+ (uint8_t)zl3vni->svi_if->hw_addr[3],
+ (uint8_t)zl3vni->svi_if->hw_addr[4],
+ (uint8_t)zl3vni->svi_if->hw_addr[5]);
+ else
+ snprintf(ptr, ETHER_ADDR_STRLEN, "None");
+
+ return ptr;
+}
+
+/*
+ * l3-vni is oper up when:
+ * 0. if EVPN is enabled (advertise-all-vni cfged)
+ * 1. it is associated to a vxlan-intf
+ * 2. Associated vxlan-intf is oper up
+ * 3. it is associated to an SVI
+ * 4. associated SVI is oper up
+ */
+static inline int is_l3vni_oper_up(zebra_l3vni_t *zl3vni)
+{
+ return (is_evpn_enabled() && zl3vni &&
+ (zl3vni->vrf_id != VRF_UNKNOWN) &&
+ zl3vni->vxlan_if && if_is_operative(zl3vni->vxlan_if) &&
+ zl3vni->svi_if && if_is_operative(zl3vni->svi_if));
+}
+
+static inline const char *zl3vni_state2str(zebra_l3vni_t *zl3vni)
+{
+ if (!zl3vni)
+ return NULL;
+
+ if (is_l3vni_oper_up(zl3vni))
+ return "Up";
+ else
+ return "Down";
+
+ return NULL;
+}
+
+static inline vrf_id_t zl3vni_vrf_id(zebra_l3vni_t *zl3vni)
+{
+ return zl3vni->vrf_id;
+}
+
+static inline void zl3vni_get_rmac(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac)
+{
+ if (!zl3vni)
+ return;
+
+ if (!is_l3vni_oper_up(zl3vni))
+ return;
+
+ if (zl3vni->svi_if && if_is_operative(zl3vni->svi_if))
+ memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN);
+}
+
/*
* MAC hash table.
*
#define ZEBRA_MAC_REMOTE 0x02
#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */
#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */
+#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */
/* Local or remote info. */
union {
/* List of neigh associated with this mac */
struct list *neigh_list;
+
+ /* list of hosts pointing to this remote RMAC */
+ struct list *host_list;
};
/*
struct json_object *json; /* Used for JSON Output */
};
+struct rmac_walk_ctx {
+ struct vty *vty;
+ struct json_object *json;
+};
+
enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 };
#define IS_ZEBRA_NEIGH_ACTIVE(n) n->state == ZEBRA_NEIGH_ACTIVE
u_int32_t flags;
#define ZEBRA_NEIGH_LOCAL 0x01
#define ZEBRA_NEIGH_REMOTE 0x02
+#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */
enum zebra_neigh_state state;
/* Remote VTEP IP - applicable only for remote neighbors. */
struct in_addr r_vtep_ip;
+
+ /* list of hosts pointing to this remote NH entry */
+ struct list *host_list;
};
/*
struct json_object *json; /* Used for JSON Output */
};
+/* context for neigh hash walk - update l3vni and rmac */
+struct neigh_l3info_walk_ctx {
+
+ zebra_vni_t *zvni;
+ zebra_l3vni_t *zl3vni;
+ int add;
+};
+
+struct nh_walk_ctx {
+
+ struct vty *vty;
+ struct json_object *json;
+};
+
#endif /* _ZEBRA_VXLAN_PRIVATE_H */
#include "zebra/router-id.h"
#include "zebra/redistribute.h"
#include "zebra/debug.h"
-#include "zebra/ipforward.h"
#include "zebra/zebra_rnh.h"
#include "zebra/rt_netlink.h"
#include "zebra/interface.h"
return 0;
}
-void zserv_create_header(struct stream *s, uint16_t cmd, vrf_id_t vrf_id)
-{
- /* length placeholder, caller can update */
- stream_putw(s, ZEBRA_HEADER_SIZE);
- stream_putc(s, ZEBRA_HEADER_MARKER);
- stream_putc(s, ZSERV_VERSION);
- stream_putw(s, vrf_id);
- stream_putw(s, cmd);
-}
-
static void zserv_encode_interface(struct stream *s, struct interface *ifp)
{
/* Interface information. */
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_INTERFACE_ADD, ifp->vrf_id);
+ zclient_create_header(s, ZEBRA_INTERFACE_ADD, ifp->vrf_id);
zserv_encode_interface(s, ifp);
client->ifadd_cnt++;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_INTERFACE_DELETE, ifp->vrf_id);
+ zclient_create_header(s, ZEBRA_INTERFACE_DELETE, ifp->vrf_id);
zserv_encode_interface(s, ifp);
client->ifdel_cnt++;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_VRF_ADD, zvrf_id(zvrf));
+ zclient_create_header(s, ZEBRA_VRF_ADD, zvrf_id(zvrf));
zserv_encode_vrf(s, zvrf);
client->vrfadd_cnt++;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_VRF_DELETE, zvrf_id(zvrf));
+ zclient_create_header(s, ZEBRA_VRF_DELETE, zvrf_id(zvrf));
zserv_encode_vrf(s, zvrf);
client->vrfdel_cnt++;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_INTERFACE_LINK_PARAMS, ifp->vrf_id);
+ zclient_create_header(s, ZEBRA_INTERFACE_LINK_PARAMS, ifp->vrf_id);
/* Add Interface Index */
stream_putl(s, ifp->ifindex);
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, cmd, ifp->vrf_id);
+ zclient_create_header(s, cmd, ifp->vrf_id);
stream_putl(s, ifp->ifindex);
/* Interface address flag. */
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, cmd, ifp->vrf_id);
+ zclient_create_header(s, cmd, ifp->vrf_id);
stream_putl(s, ifp->ifindex);
/* Prefix information. */
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_INTERFACE_VRF_UPDATE, ifp->vrf_id);
+ zclient_create_header(s, ZEBRA_INTERFACE_VRF_UPDATE, ifp->vrf_id);
/* Fill in the ifIndex of the interface and its new VRF (id) */
stream_putl(s, ifp->ifindex);
- stream_putw(s, vrf_id);
+ stream_putl(s, vrf_id);
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, cmd, ifp->vrf_id);
+ zclient_create_header(s, cmd, ifp->vrf_id);
zserv_encode_interface(s, ifp);
if (cmd == ZEBRA_INTERFACE_UP)
memset(&api, 0, sizeof(api));
api.vrf_id = re->vrf_id;
+ api.nh_vrf_id = re->nh_vrf_id;
api.type = re->type;
api.instance = re->instance;
api.flags = re->flags;
stream_reset(s);
/* Fill in result. */
- zserv_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, zvrf_id(zvrf));
+ zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, zvrf_id(zvrf));
stream_put_in_addr(s, &addr);
if (re) {
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_ROUTE_NOTIFY_OWNER, vrf_id);
+ zclient_create_header(s, ZEBRA_ROUTE_NOTIFY_OWNER, vrf_id);
stream_put(s, ¬e, sizeof(note));
stream_reset(s);
/* Message type. */
- zserv_create_header(s, ZEBRA_ROUTER_ID_UPDATE, vrf_id);
+ zclient_create_header(s, ZEBRA_ROUTER_ID_UPDATE, vrf_id);
/* Prefix information. */
stream_putc(s, p->family);
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_PW_STATUS_UPDATE, pw->vrf_id);
+ zclient_create_header(s, ZEBRA_PW_STATUS_UPDATE, pw->vrf_id);
stream_write(s, pw->ifname, IF_NAMESIZE);
stream_putl(s, pw->ifindex);
stream_putl(s, pw->status);
struct route_entry *re;
struct nexthop *nexthop = NULL;
int i, ret;
+ vrf_id_t vrf_id = 0;
s = client->ibuf;
if (zapi_route_decode(s, &api) < 0)
return -1;
/* Allocate new route. */
+ vrf_id = zvrf_id(zvrf);
re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
re->type = api.type;
re->instance = api.instance;
re->flags = api.flags;
re->uptime = time(NULL);
- re->vrf_id = zvrf_id(zvrf);
+ re->vrf_id = vrf_id;
+ re->nh_vrf_id = api.nh_vrf_id;
re->table = zvrf->table_id;
if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) {
for (i = 0; i < api.nexthop_num; i++) {
api_nh = &api.nexthops[i];
+ ifindex_t ifindex = 0;
switch (api_nh->type) {
case NEXTHOP_TYPE_IFINDEX:
nexthop = route_entry_nexthop_ipv4_add(
re, &api_nh->gate.ipv4, NULL);
break;
- case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV4_IFINDEX: {
+
+ struct ipaddr vtep_ip;
+
+ memset(&vtep_ip, 0, sizeof(struct ipaddr));
+ if (CHECK_FLAG(api.flags,
+ ZEBRA_FLAG_EVPN_ROUTE)) {
+ ifindex =
+ get_l3vni_svi_ifindex(vrf_id);
+ } else {
+ ifindex = api_nh->ifindex;
+ }
+
nexthop = route_entry_nexthop_ipv4_ifindex_add(
re, &api_nh->gate.ipv4, NULL,
- api_nh->ifindex);
+ ifindex);
+
+ /* if this an EVPN route entry,
+ program the nh as neigh
+ */
+ if (CHECK_FLAG(api.flags,
+ ZEBRA_FLAG_EVPN_ROUTE)) {
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_EVPN_RVTEP);
+ vtep_ip.ipa_type = IPADDR_V4;
+ memcpy(&(vtep_ip.ipaddr_v4),
+ &(api_nh->gate.ipv4),
+ sizeof(struct in_addr));
+ zebra_vxlan_evpn_vrf_route_add(
+ vrf_id,
+ &api.rmac,
+ &vtep_ip,
+ &api.prefix);
+ }
break;
+ }
case NEXTHOP_TYPE_IPV6:
nexthop = route_entry_nexthop_ipv6_add(
re, &api_nh->gate.ipv6);
rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance,
api.flags, &api.prefix, src_p, NULL, zvrf->table_id,
- api.metric, false);
+ api.metric, false, &api.rmac);
/* Stats */
switch (api.prefix.family) {
/* VRF ID */
re->vrf_id = zvrf_id(zvrf);
+ re->nh_vrf_id = zvrf_id(zvrf);
/* Nexthop parse. */
if (CHECK_FLAG(message, ZAPI_MESSAGE_NEXTHOP)) {
table_id = zvrf->table_id;
rib_delete(AFI_IP, api.safi, zvrf_id(zvrf), api.type, api.instance,
- api.flags, &p, NULL, NULL, table_id, 0, false);
+ api.flags, &p, NULL, NULL, table_id, 0, false, NULL);
client->v4_route_del_cnt++;
stream_failure:
/* VRF ID */
re->vrf_id = zvrf_id(zvrf);
+ re->nh_vrf_id = zvrf_id(zvrf);
/* We need to give nh-addr, nh-ifindex with the same next-hop object
* to the re to ensure that IPv6 multipathing works; need to coalesce
/* VRF ID */
re->vrf_id = zvrf_id(zvrf);
+ re->nh_vrf_id = zvrf_id(zvrf);
+
re->table = zvrf->table_id;
ret = rib_add_multipath(AFI_IP6, safi, &p, src_pp, re);
src_pp = NULL;
rib_delete(AFI_IP6, api.safi, zvrf_id(zvrf), api.type, api.instance,
- api.flags, &p, src_pp, NULL, client->rtm_table, 0, false);
+ api.flags, &p, src_pp, NULL, client->rtm_table, 0, false,
+ NULL);
client->v6_route_del_cnt++;
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_LABEL_MANAGER_CONNECT, vrf_id);
+ zclient_create_header(s, ZEBRA_LABEL_MANAGER_CONNECT, vrf_id);
/* result */
stream_putc(s, result);
s = client->obuf;
stream_reset(s);
- zserv_create_header(s, ZEBRA_GET_LABEL_CHUNK, vrf_id);
+ zclient_create_header(s, ZEBRA_GET_LABEL_CHUNK, vrf_id);
if (lmc) {
/* keep */
int ifindex;
vrf_id_t vrf_id;
- STREAM_GETW(s, vrf_id);
+ STREAM_GETL(s, vrf_id);
STREAM_GETL(s, ifindex);
master = if_lookup_by_index(ifindex, vrf_id);
- STREAM_GETW(s, vrf_id);
+ STREAM_GETL(s, vrf_id);
STREAM_GETL(s, ifindex);
slave = if_lookup_by_index(ifindex, vrf_id);
STREAM_GETW(client->ibuf, length);
STREAM_GETC(client->ibuf, marker);
STREAM_GETC(client->ibuf, version);
- STREAM_GETW(client->ibuf, vrf_id);
+ STREAM_GETL(client->ibuf, vrf_id);
STREAM_GETW(client->ibuf, command);
if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION) {
vty_out(vty, "Interface Down Notifications: %d\n", client->ifdown_cnt);
vty_out(vty, "VNI add notifications: %d\n", client->vniadd_cnt);
vty_out(vty, "VNI delete notifications: %d\n", client->vnidel_cnt);
+ vty_out(vty, "L3-VNI add notifications: %d\n", client->l3vniadd_cnt);
+ vty_out(vty, "L3-VNI delete notifications: %d\n", client->l3vnidel_cnt);
vty_out(vty, "MAC-IP add notifications: %d\n", client->macipadd_cnt);
vty_out(vty, "MAC-IP delete notifications: %d\n", client->macipdel_cnt);
return NULL;
}
-#ifdef HAVE_NETLINK
-/* Display default rtm_table for all clients. */
-DEFUN (show_table,
- show_table_cmd,
- "show table",
- SHOW_STR
- "default routing table to use for all clients\n")
-{
- vty_out(vty, "table %d\n", zebrad.rtm_table_default);
- return CMD_SUCCESS;
-}
-
-DEFUN (config_table,
- config_table_cmd,
- "table TABLENO",
- "Configure target kernel routing table\n"
- "TABLE integer\n")
-{
- zebrad.rtm_table_default = strtol(argv[1]->arg, (char **)0, 10);
- return CMD_SUCCESS;
-}
-
-DEFUN (no_config_table,
- no_config_table_cmd,
- "no table [TABLENO]",
- NO_STR
- "Configure target kernel routing table\n"
- "TABLE integer\n")
-{
- zebrad.rtm_table_default = 0;
- return CMD_SUCCESS;
-}
-#endif
-
-DEFUN (ip_forwarding,
- ip_forwarding_cmd,
- "ip forwarding",
- IP_STR
- "Turn on IP forwarding\n")
-{
- int ret;
-
- ret = ipforward();
- if (ret == 0)
- ret = ipforward_on();
-
- if (ret == 0) {
- vty_out(vty, "Can't turn on IP forwarding\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_ip_forwarding,
- no_ip_forwarding_cmd,
- "no ip forwarding",
- NO_STR
- IP_STR
- "Turn off IP forwarding\n")
-{
- int ret;
-
- ret = ipforward();
- if (ret != 0)
- ret = ipforward_off();
-
- if (ret != 0) {
- vty_out(vty, "Can't turn off IP forwarding\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN (show_zebra,
- show_zebra_cmd,
- "show zebra",
- SHOW_STR
- ZEBRA_STR)
-{
- struct vrf *vrf;
-
- vty_out(vty,
- " Route Route Neighbor LSP LSP\n");
- vty_out(vty,
- "VRF Installs Removals Updates Installs Removals\n");
- RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
- struct zebra_vrf *zvrf = vrf->info;
- vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64
- " %10" PRIu64 " %10" PRIu64 "\n",
- vrf->name, zvrf->installs, zvrf->removals,
- zvrf->neigh_updates, zvrf->lsp_installs,
- zvrf->lsp_removals);
- }
-
- return CMD_SUCCESS;
-}
-
/* This command is for debugging purpose. */
DEFUN (show_zebra_client,
show_zebra_client_cmd,
return CMD_SUCCESS;
}
-/* Table configuration write function. */
-static int config_write_table(struct vty *vty)
-{
- if (zebrad.rtm_table_default)
- vty_out(vty, "table %d\n", zebrad.rtm_table_default);
- return 0;
-}
-
-/* table node for routing tables. */
-static struct cmd_node table_node = {TABLE_NODE,
- "", /* This node has no interface. */
- 1};
-
-/* Only display ip forwarding is enabled or not. */
-DEFUN (show_ip_forwarding,
- show_ip_forwarding_cmd,
- "show ip forwarding",
- SHOW_STR
- IP_STR
- "IP forwarding status\n")
-{
- int ret;
-
- ret = ipforward();
-
- if (ret == 0)
- vty_out(vty, "IP forwarding is off\n");
- else
- vty_out(vty, "IP forwarding is on\n");
- return CMD_SUCCESS;
-}
-
-/* Only display ipv6 forwarding is enabled or not. */
-DEFUN (show_ipv6_forwarding,
- show_ipv6_forwarding_cmd,
- "show ipv6 forwarding",
- SHOW_STR
- "IPv6 information\n"
- "Forwarding status\n")
-{
- int ret;
-
- ret = ipforward_ipv6();
-
- switch (ret) {
- case -1:
- vty_out(vty, "ipv6 forwarding is unknown\n");
- break;
- case 0:
- vty_out(vty, "ipv6 forwarding is %s\n", "off");
- break;
- case 1:
- vty_out(vty, "ipv6 forwarding is %s\n", "on");
- break;
- default:
- vty_out(vty, "ipv6 forwarding is %s\n", "off");
- break;
- }
- return CMD_SUCCESS;
-}
-
-DEFUN (ipv6_forwarding,
- ipv6_forwarding_cmd,
- "ipv6 forwarding",
- IPV6_STR
- "Turn on IPv6 forwarding\n")
-{
- int ret;
-
- ret = ipforward_ipv6();
- if (ret == 0)
- ret = ipforward_ipv6_on();
-
- if (ret == 0) {
- vty_out(vty, "Can't turn on IPv6 forwarding\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_ipv6_forwarding,
- no_ipv6_forwarding_cmd,
- "no ipv6 forwarding",
- NO_STR
- IPV6_STR
- "Turn off IPv6 forwarding\n")
-{
- int ret;
-
- ret = ipforward_ipv6();
- if (ret != 0)
- ret = ipforward_ipv6_off();
-
- if (ret != 0) {
- vty_out(vty, "Can't turn off IPv6 forwarding\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return CMD_SUCCESS;
-}
-
-/* IPForwarding configuration write function. */
-static int config_write_forwarding(struct vty *vty)
-{
- /* FIXME: Find better place for that. */
- router_id_write(vty);
-
- if (!ipforward())
- vty_out(vty, "no ip forwarding\n");
- if (!ipforward_ipv6())
- vty_out(vty, "no ipv6 forwarding\n");
- vty_out(vty, "!\n");
- return 0;
-}
-
-/* table node for routing tables. */
-static struct cmd_node forwarding_node = {FORWARDING_NODE,
- "", /* This node has no interface. */
- 1};
-
#if defined(HANDLE_ZAPI_FUZZING)
void zserv_read_file(char *input)
{
}
#endif
-/* Initialisation of zebra and installation of commands. */
-void zebra_init(void)
+void zserv_init(void)
{
/* Client list init. */
zebrad.client_list = list_new();
zebrad.client_list->del = (void (*)(void *))zebra_client_free;
- /* Install configuration write function. */
- install_node(&table_node, config_write_table);
- install_node(&forwarding_node, config_write_forwarding);
-
- install_element(VIEW_NODE, &show_ip_forwarding_cmd);
- install_element(CONFIG_NODE, &ip_forwarding_cmd);
- install_element(CONFIG_NODE, &no_ip_forwarding_cmd);
- install_element(ENABLE_NODE, &show_zebra_cmd);
install_element(ENABLE_NODE, &show_zebra_client_cmd);
install_element(ENABLE_NODE, &show_zebra_client_summary_cmd);
-
-#ifdef HAVE_NETLINK
- install_element(VIEW_NODE, &show_table_cmd);
- install_element(CONFIG_NODE, &config_table_cmd);
- install_element(CONFIG_NODE, &no_config_table_cmd);
-#endif /* HAVE_NETLINK */
-
- install_element(VIEW_NODE, &show_ipv6_forwarding_cmd);
- install_element(CONFIG_NODE, &ipv6_forwarding_cmd);
- install_element(CONFIG_NODE, &no_ipv6_forwarding_cmd);
-
- /* Route-map */
- zebra_route_map_init();
}
u_int32_t bfd_client_reg_cnt;
u_int32_t vniadd_cnt;
u_int32_t vnidel_cnt;
+ u_int32_t l3vniadd_cnt;
+ u_int32_t l3vnidel_cnt;
u_int32_t macipadd_cnt;
u_int32_t macipdel_cnt;
extern unsigned int multipath_num;
/* Prototypes. */
-extern void zebra_init(void);
-extern void zebra_if_init(void);
+extern void zserv_init(void);
extern void zebra_zserv_socket_init(char *path);
-extern void hostinfo_get(void);
-extern void rib_init(void);
-extern void interface_list(struct zebra_ns *);
-extern void route_read(struct zebra_ns *);
-extern void macfdb_read(struct zebra_ns *);
-extern void macfdb_read_for_bridge(struct zebra_ns *, struct interface *,
- struct interface *);
-extern void neigh_read(struct zebra_ns *);
-extern void neigh_read_for_vlan(struct zebra_ns *, struct interface *);
-extern void kernel_init(struct zebra_ns *);
-extern void kernel_terminate(struct zebra_ns *);
-extern void zebra_route_map_init(void);
-extern void zebra_vty_init(void);
extern int zsend_vrf_add(struct zserv *, struct zebra_vrf *);
extern int zsend_vrf_delete(struct zserv *, struct zebra_vrf *);
vrf_id_t vrf_id, struct prefix *p,
enum zapi_route_notify_owner note);
-extern pid_t pid;
-
-extern void zserv_create_header(struct stream *s, uint16_t cmd,
- vrf_id_t vrf_id);
extern void zserv_nexthop_num_warn(const char *, const struct prefix *,
const unsigned int);
extern int zebra_server_send_message(struct zserv *client);