- SUBGRP_FOREACH_ADJ_SAFE
- AF_FOREACH
- FOREACH_AFI_SAFI
+ # ospfd
+ - LSDB_LOOP
-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
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api.nexthop_num = 1;
api_nh->ifindex = ifindex;
-
- switch (family) {
+ api_nh->vrf_id = VRF_DEFAULT;
+ switch (family) {
case AF_INET:
uchar_to_inaddr(&api_nh->gate.ipv4, gate);
if (IPV4_ADDR_SAME (&api_nh->gate.ipv4, &quagga_prefix.u.prefix4) &&
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;
+ /* Check if this is a Gateway MAC-IP advertisement */
+ attr->default_gw = bgp_attr_default_gw(attr);
+
+ /* Extract the Rmac, if any */
+ bgp_attr_rmac(attr, &attr->rmac);
+
return BGP_ATTR_PARSE_PROCEED;
}
void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
struct prefix *p, struct prefix_rd *prd,
- mpls_label_t *label, int addpath_encode,
- u_int32_t addpath_tx_id, struct attr *attr)
+ mpls_label_t *label, u_int32_t num_labels,
+ int addpath_encode, u_int32_t addpath_tx_id,
+ struct attr *attr)
{
if (safi == SAFI_MPLS_VPN) {
if (addpath_encode)
stream_put(s, &p->u.prefix, PSIZE(p->prefixlen));
} else if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
/* EVPN prefix - contents depend on type */
- bgp_evpn_encode_prefix(s, p, prd, label, attr, addpath_encode,
- addpath_tx_id);
+ bgp_evpn_encode_prefix(s, p, prd, label, num_labels,
+ attr, addpath_encode, addpath_tx_id);
} else if (safi == SAFI_LABELED_UNICAST) {
/* Prefix write with label. */
stream_put_labeled_prefix(s, p, label);
struct bpacket_attr_vec_arr *vecarr,
struct prefix *p, afi_t afi, safi_t safi,
struct peer *from, struct prefix_rd *prd,
- mpls_label_t *label, int addpath_encode,
- u_int32_t addpath_tx_id)
+ mpls_label_t *label, u_int32_t num_labels,
+ int addpath_encode, u_int32_t addpath_tx_id)
{
size_t cp;
size_t aspath_sizep;
mpattrlen_pos = bgp_packet_mpattr_start(s, peer, afi, safi,
vecarr, attr);
- bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label,
+ bgp_packet_mpattr_prefix(s, afi, safi, p, prd,
+ label, num_labels,
addpath_encode, addpath_tx_id, attr);
bgp_packet_mpattr_end(s, mpattrlen_pos);
}
#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);
void bgp_packet_mpunreach_prefix(struct stream *s, struct prefix *p, afi_t afi,
safi_t safi, struct prefix_rd *prd,
- mpls_label_t *label, int addpath_encode,
- u_int32_t addpath_tx_id, struct attr *attr)
+ mpls_label_t *label, u_int32_t num_labels,
+ int addpath_encode, u_int32_t addpath_tx_id,
+ struct attr *attr)
{
u_char wlabel[3] = {0x80, 0x00, 0x00};
- if (safi == SAFI_LABELED_UNICAST)
+ if (safi == SAFI_LABELED_UNICAST) {
label = (mpls_label_t *)wlabel;
+ num_labels = 1;
+ }
- return bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label,
+ return bgp_packet_mpattr_prefix(s, afi, safi, p, prd,
+ label, num_labels,
addpath_encode, addpath_tx_id, attr);
}
/* Static MAC for EVPN */
u_char sticky;
+ /* Flag for default gateway extended community in EVPN */
+ u_char default_gw;
+
/* route tag */
route_tag_t tag;
/* EVPN MAC Mobility sequence number, if any. */
u_int32_t mm_seqnum;
+
+ /* EVPN local router-mac */
+ struct ethaddr rmac;
};
/* rmap_change_flags definition */
struct bpacket_attr_vec_arr *vecarr,
struct prefix *, afi_t, safi_t,
struct peer *, struct prefix_rd *,
- mpls_label_t *, int, u_int32_t);
+ mpls_label_t *, u_int32_t,
+ int, u_int32_t);
extern void bgp_dump_routes_attr(struct stream *, struct attr *,
struct prefix *);
extern int attrhash_cmp(const void *, const void *);
struct attr *attr);
extern void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
struct prefix *p, struct prefix_rd *prd,
- mpls_label_t *label, int addpath_encode,
+ mpls_label_t *label, u_int32_t num_labels,
+ int addpath_encode,
u_int32_t addpath_tx_id, struct attr *);
extern size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi,
struct prefix *p);
safi_t safi);
extern void bgp_packet_mpunreach_prefix(struct stream *s, struct prefix *p,
afi_t afi, safi_t safi,
- struct prefix_rd *prd, mpls_label_t *,
+ struct prefix_rd *prd,
+ mpls_label_t *, u_int32_t,
int, u_int32_t, struct attr *);
extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt);
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);
+ }
+}
+
+/*
+ * return true if attr contains default gw extended community
+ */
+uint8_t bgp_attr_default_gw(struct attr *attr)
+{
+ struct ecommunity *ecom;
+ int i;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return 0;
+
+ /* If there is a default gw extendd community return true otherwise
+ * return 0 */
+ for (i = 0; i < ecom->size; i++) {
+ u_char *pnt;
+ u_char type, sub_type;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if ((type == ECOMMUNITY_ENCODE_OPAQUE
+ && sub_type == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW))
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* 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);
+extern uint8_t bgp_attr_default_gw(struct attr *attr);
#endif /* _QUAGGA_BGP_ATTR_EVPN_H */
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));
}
if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)))
snprintf(buf + strlen(buf), size - strlen(buf),
- ", community %s", community_str(attr->community,
- false));
+ ", community %s",
+ community_str(attr->community, false));
if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
snprintf(buf + strlen(buf), size - strlen(buf),
const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi,
struct prefix_rd *prd,
union prefixconstptr pu,
- mpls_label_t *label, int addpath_valid,
- u_int32_t addpath_id, char *str, int size)
+ mpls_label_t *label, u_int32_t num_labels,
+ int addpath_valid, u_int32_t addpath_id,
+ char *str, int size)
{
char rd_buf[RD_ADDRSTRLEN];
char pfx_buf[PREFIX_STRLEN];
addpath_id);
tag_buf[0] = '\0';
- if (bgp_labeled_safi(safi) && label) {
- u_int32_t label_value;
+ if (bgp_labeled_safi(safi) && num_labels) {
- label_value = decode_label(label);
- sprintf(tag_buf, " label %u", label_value);
+ if (safi == SAFI_EVPN) {
+ char tag_buf2[20];
+
+ bgp_evpn_label2str(label, num_labels, tag_buf2, 20);
+ sprintf(tag_buf, " label %s", tag_buf2);
+ } else {
+ u_int32_t label_value;
+
+ label_value = decode_label(label);
+ sprintf(tag_buf, " label %u", label_value);
+ }
}
if (prd)
extern int bgp_debug_count(void);
extern const char *bgp_debug_rdpfxpath2str(afi_t, safi_t, struct prefix_rd *,
union prefixconstptr, mpls_label_t *,
- int, u_int32_t, char *, int);
+ u_int32_t, int, u_int32_t, char *,
+ int);
const char *bgp_notify_admin_message(char *buf, size_t bufsz, u_char *data,
size_t datalen);
tunneltype = ntohs(tunneltype);
len = sprintf(str_buf + str_pnt, "ET:%d",
tunneltype);
+ } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
+ len = sprintf(str_buf + str_pnt,
+ "Default Gateway");
} else
unk_ecom = 1;
} 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;
+
+ return jhash(pnt, 8, 0x5abc1234);
+}
+
+/*
+ * 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.
*/
{
struct 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);
+ return jhash(pnt, 8, 0xdeadbeef);
}
/*
}
}
+/*
+ * 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);
static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
struct prefix_evpn *p,
struct in_addr remote_vtep_ip, int add,
- u_char sticky)
+ u_char flags)
{
struct stream *s;
int ipa_len;
}
stream_put_in_addr(s, &remote_vtep_ip);
- /* TX MAC sticky status */
+ /* TX flags - MAC sticky status and/or gateway mac */
if (add)
- stream_putc(s, sticky);
+ stream_putc(s, flags);
stream_putw_at(s, 0, stream_get_endp(s));
if (bgp_debug_zebra(NULL))
- zlog_debug("Tx %s MACIP, VNI %u %sMAC %s IP %s remote VTEP %s",
+ zlog_debug("Tx %s MACIP, VNI %u MAC %s IP %s (flags: 0x%x) remote VTEP %s",
add ? "ADD" : "DEL", vpn->vni,
- sticky ? "sticky " : "",
prefix_mac2str(&p->prefix.mac, buf1, sizeof(buf1)),
ipaddr2str(&p->prefix.ip, buf3, sizeof(buf3)),
+ flags,
inet_ntop(AF_INET, &remote_vtep_ip, buf2,
sizeof(buf2)));
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_default_gw;
+ struct ecommunity ecom_rmac;
struct ecommunity_val eval;
struct ecommunity_val eval_sticky;
+ struct ecommunity_val eval_default_gw;
+ 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);
+ }
+
+ if (attr->default_gw) {
+ memset(&ecom_default_gw, 0, sizeof(ecom_default_gw));
+ encode_default_gw_extcomm(&eval_default_gw);
+ ecom_default_gw.size = 1;
+ ecom_default_gw.val = (uint8_t *)eval_default_gw.val;
+ attr->ecommunity = ecommunity_merge(attr->ecommunity,
+ &ecom_default_gw);
+ }
+
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
}
/* Install EVPN route into zebra. */
static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn,
struct prefix_evpn *p,
- struct in_addr remote_vtep_ip, u_char sticky)
+ struct in_addr remote_vtep_ip, u_char flags)
{
int ret;
if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
ret = bgp_zebra_send_remote_macip(bgp, vpn, p, remote_vtep_ip,
- 1, sticky);
+ 1, flags);
else
ret = bgp_zebra_send_remote_vtep(bgp, vpn, p, 1);
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
int ret = 0;
+ u_char flags = 0;
/* Compute the best path. */
bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], &old_and_new,
&& !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)
&& !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED)
&& !bgp->addpath_tx_used[afi][safi]) {
- if (bgp_zebra_has_route_changed(rn, old_select))
+ if (bgp_zebra_has_route_changed(rn, old_select)) {
+ if (old_select->attr->sticky)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
+ if (old_select->attr->default_gw)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
ret = evpn_zebra_install(bgp, vpn,
(struct prefix_evpn *)&rn->p,
old_select->attr->nexthop,
- old_select->attr->sticky);
+ flags);
+ }
UNSET_FLAG(old_select->flags, BGP_INFO_MULTIPATH_CHG);
bgp_zebra_clear_route_change_flags(rn);
return ret;
if (new_select && new_select->type == ZEBRA_ROUTE_BGP
&& new_select->sub_type == BGP_ROUTE_NORMAL) {
+ flags = 0;
+ if (new_select->attr->sticky)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
+ if (new_select->attr->default_gw)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
ret = evpn_zebra_install(bgp, vpn, (struct prefix_evpn *)&rn->p,
new_select->attr->nexthop,
- new_select->attr->sticky);
+ flags);
/* If an old best existed and it was a "local" route, the only
* reason
* it would be supplanted is due to MAC mobility procedures. So,
return ret;
}
+/*
+ * Return true if the local ri for this rn is of type gateway mac
+ */
+static int evpn_route_is_def_gw(struct bgp *bgp, struct bgp_node *rn)
+{
+ struct bgp_info *tmp_ri = NULL;
+ struct bgp_info *local_ri = NULL;
+
+ local_ri = NULL;
+ for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) {
+ if (tmp_ri->peer == bgp->peer_self
+ && tmp_ri->type == ZEBRA_ROUTE_BGP
+ && tmp_ri->sub_type == BGP_ROUTE_STATIC)
+ local_ri = tmp_ri;
+ }
+
+ if (!local_ri)
+ return 0;
+
+ return local_ri->attr->default_gw;
+}
+
/*
* Return true if the local ri for this rn has sticky set
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);
+
+ /* Type-5 routes advertise the L3-VNI */
+ bgp_info_extra_get(ri);
+ vni2label(bgp_vrf->l3vni, &label);
+ memcpy(&ri->extra->label, &label, sizeof(label));
+ ri->extra->num_labels = 1;
+
+ /* 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,
+ struct attr* src_attr)
+{
+ 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 0;
+
+ /* Build path attribute for this route - use the source attr, if
+ * present, else treat as locally originated.
+ */
+ if (src_attr)
+ bgp_attr_dup(&attr, src_attr);
+ else {
+ memset(&attr, 0, sizeof(struct attr));
+ bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
+ }
+ /* Set nexthop to ourselves and fill in the Router MAC. */
+ 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 */
+ if (!src_attr)
+ 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.
struct bgp_info *tmp_ri;
struct bgp_info *local_ri, *remote_ri;
struct attr *attr_new;
- mpls_label_t label = MPLS_INVALID_LABEL;
+ mpls_label_t label[BGP_MAX_LABELS];
+ u_int32_t num_labels = 1;
int route_change = 1;
u_char sticky = 0;
+ struct prefix_evpn *evp;
*ri = NULL;
+ evp = (struct prefix_evpn *)&rn->p;
+ memset(&label, 0, sizeof(label));
/* See if this is an update of an existing route, or a new add. Also,
* identify if already known from remote, and if so, the one with the
* SVI) advertised in EVPN.
* This will ensure that local routes are preferred for g/w macs
*/
- if (remote_ri && !CHECK_FLAG(flags, ZEBRA_MAC_TYPE_GW)) {
+ if (remote_ri && !CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) {
u_int32_t cur_seqnum;
/* Add MM extended community to route. */
bgp_info_extra_get(tmp_ri);
/* The VNI goes into the 'label' field of the route */
- vni2label(vpn->vni, &label);
+ vni2label(vpn->vni, &label[0]);
+ /* Type-2 routes may carry a second VNI - the L3-VNI */
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
+ vni_t l3vni;
+
+ l3vni = bgpevpn_get_l3vni(vpn);
+ if (l3vni) {
+ vni2label(l3vni, &label[1]);
+ num_labels++;
+ }
+ }
- memcpy(&tmp_ri->extra->label, &label, BGP_LABEL_BYTES);
+ memcpy(&tmp_ri->extra->label, label, sizeof(label));
+ tmp_ri->extra->num_labels = num_labels;
bgp_info_add(rn, tmp_ri);
} else {
tmp_ri = local_ri;
attr.nexthop = vpn->originator_ip;
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.sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? 1 : 0;
+ attr.default_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? 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 0;
+
+ /* 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_def_gw;
+ struct attr attr_ip6;
+ struct attr attr_sticky_ip6;
+ struct attr attr_def_gw_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_def_gw, 0, sizeof(struct attr));
+ memset(&attr_ip6, 0, sizeof(struct attr));
+ memset(&attr_sticky_ip6, 0, sizeof(struct attr));
+ memset(&attr_def_gw_ip6, 0, sizeof(struct attr));
/* Build path-attribute - all type-2 routes for this VNI will share the
* same path attribute.
*/
bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
bgp_attr_default_set(&attr_sticky, BGP_ORIGIN_IGP);
+ bgp_attr_default_set(&attr_def_gw, BGP_ORIGIN_IGP);
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);
+ attr_def_gw.nexthop = vpn->originator_ip;
+ attr_def_gw.mp_nexthop_global_in = vpn->originator_ip;
+ attr_def_gw.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr_def_gw.default_gw = 1;
+ bgpevpn_get_rmac(vpn, &attr_def_gw.rmac);
+ bgp_attr_default_set(&attr_ip6, BGP_ORIGIN_IGP);
+ bgp_attr_default_set(&attr_sticky_ip6, BGP_ORIGIN_IGP);
+ bgp_attr_default_set(&attr_def_gw_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);
+ attr_def_gw_ip6.nexthop = vpn->originator_ip;
+ attr_def_gw_ip6.mp_nexthop_global_in = vpn->originator_ip;
+ attr_def_gw_ip6.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr_def_gw_ip6.default_gw = 1;
+ bgpevpn_get_rmac(vpn, &attr_def_gw_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_def_gw, AFI_IP);
+ build_evpn_route_extcomm(vpn, &attr_ip6, AFI_IP6);
+ build_evpn_route_extcomm(vpn, &attr_sticky_ip6, AFI_IP6);
+ build_evpn_route_extcomm(vpn, &attr_def_gw_ip6, AFI_IP);
/* 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 if (evpn_route_is_def_gw(bgp, rn))
+ update_evpn_route_entry(bgp, vpn, afi, safi, rn,
+ &attr_def_gw, 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 if (evpn_route_is_def_gw(bgp, rn))
+ update_evpn_route_entry(bgp, vpn, afi, safi, rn,
+ &attr_def_gw_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.
/* Unintern temporary. */
aspath_unintern(&attr.aspath);
+ aspath_unintern(&attr_ip6.aspath);
aspath_unintern(&attr_sticky.aspath);
+ aspath_unintern(&attr_sticky_ip6.aspath);
+ aspath_unintern(&attr_def_gw.aspath);
+ aspath_unintern(&attr_def_gw_ip6.aspath);
return 0;
}
}
/*
- * Install route entry into the VNI routing table and invoke route selection.
+ * Install route entry into the VRF routing table and invoke route selection.
*/
-static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
- struct prefix_evpn *p,
- struct bgp_info *parent_ri)
+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;
-
- /* Create (or fetch) route within the VNI. */
+ 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,
+ sizeof(ri->extra->label));
+ ri->extra->num_labels = parent_ri->extra->num_labels;
+ }
+ 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.
+ */
+static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p,
+ struct bgp_info *parent_ri)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ struct attr *attr_new;
+ int ret;
+
+ /* Create (or fetch) route within the VNI. */
/* NOTE: There is no RD here. */
rn = bgp_node_get(vpn->route_table, (struct prefix *)p);
SET_FLAG(ri->flags, BGP_INFO_VALID);
bgp_info_extra_get(ri);
ri->extra->parent = parent_ri;
- if (parent_ri->extra)
+ if (parent_ri->extra) {
memcpy(&ri->extra->label, &parent_ri->extra->label,
- BGP_LABEL_BYTES);
+ sizeof(ri->extra->label));
+ ri->extra->num_labels = parent_ri->extra->num_labels;
+ }
bgp_info_add(rn, ri);
} else {
if (attrhash_cmp(ri->attr, parent_ri->attr)
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;
}
+/* 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);
+}
+
/*
* Update and advertise local routes for a VNI. Invoked upon router-id
* change. Note that the processing is done only on the global route table
struct prefix_evpn p;
u_char ipaddr_len;
u_char macaddr_len;
- mpls_label_t *label_pnt;
+ mpls_label_t label[BGP_MAX_LABELS]; /* holds the VNI(s) as in packet */
+ u_int32_t num_labels = 0;
int ret;
/* Type-2 route should be either 33, 37 or 49 bytes or an
}
pfx += ipaddr_len;
- /* Get the VNI (in MPLS label field). */
- /* Note: We ignore the second VNI, if any. */
- label_pnt = (mpls_label_t *)pfx;
+ /* Get the VNI(s). Stored as bytes here. */
+ num_labels++;
+ memset(label, 0, sizeof(label));
+ memcpy(&label[0], pfx, BGP_LABEL_BYTES);
+ pfx += BGP_LABEL_BYTES;
+ psize -= (33 + ipaddr_len);
+ /* Do we have a second VNI? */
+ if (psize) {
+ num_labels++;
+ memcpy(&label[1], pfx, BGP_LABEL_BYTES);
+ /*
+ * If in future, we are required to access additional fields,
+ * we MUST increment pfx by BGP_LABEL_BYTES in before reading the next field
+ */
+ }
/* Process the route. */
if (attr)
ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, label_pnt, 0, NULL);
+ &prd, &label[0], num_labels, 0, NULL);
else
ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, label_pnt, NULL);
+ &prd, &label[0], num_labels, NULL);
return ret;
}
if (attr)
ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, NULL, 0, NULL);
+ &prd, NULL, 0, 0, NULL);
else
ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, NULL, NULL);
+ &prd, NULL, 0, NULL);
return ret;
}
struct bgp_route_evpn evpn;
u_char ippfx_len;
u_int32_t eth_tag;
- mpls_label_t *label_pnt;
+ mpls_label_t label; /* holds the VNI as in the packet */
int ret;
/* Type-5 route should be 34 or 58 bytes:
/* 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;
+ /* Get the VNI (in MPLS label field). Stored as bytes here. */
+ memset(&label, 0, sizeof(label));
+ memcpy(&label, pfx, BGP_LABEL_BYTES);
+
+ /*
+ * If in future, we are required to access additional fields,
+ * we MUST increment pfx by BGP_LABEL_BYTES in before reading the next field
+ */
/* Process the route. */
if (!withdraw)
ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, label_pnt, 0, &evpn);
+ &prd, &label, 1, 0, &evpn);
else
ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, label_pnt, &evpn);
+ &prd, &label, 1, &evpn);
return ret;
}
static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p,
- struct prefix_rd *prd, mpls_label_t *label,
+ struct prefix_rd *prd,
+ mpls_label_t *label, u_int32_t num_labels,
struct attr *attr)
{
int len;
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);
stream_put(s, &temp, 16);
}
- if (label)
+ if (num_labels)
stream_put(s, label, 3);
else
stream_put3(s, 0);
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 prefix *p,
+ afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct prefix_evpn evp;
+ char buf[PREFIX_STRLEN];
+
+ /* NOTE: Check needed as this is called per-route also. */
+ if (!advertise_type5_routes(bgp_vrf, afi))
+ return;
+
+ build_type5_prefix_from_ip_prefix(&evp, 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(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;
+
+ /* Bail out early if we don't have to advertise type-5 routes. */
+ 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->p, afi, safi);
+
+}
+
+/*
+ * Advertise IP prefix as type-5 route. The afi/safi and src_attr passed
+ * to this function correspond to those of the source IP prefix (best
+ * path in the case of the attr. In the case of a local prefix (when we
+ * are advertising local subnets), the src_attr will be NULL.
+ */
+void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct prefix *p,
+ struct attr *src_attr,
+ afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct prefix_evpn evp;
+ char buf[PREFIX_STRLEN];
+
+ /* NOTE: Check needed as this is called per-route also. */
+ if (!advertise_type5_routes(bgp_vrf, afi))
+ return;
+
+ /* only advertise subnet routes as type-5 */
+ if (is_host_route(p))
+ return;
+
+ build_type5_prefix_from_ip_prefix(&evp, p);
+ ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr);
+ if (ret)
+ zlog_err(
+ "%u: Failed to create type-5 route for prefix %s",
+ bgp_vrf->vrf_id,
+ prefix2str(p, buf, sizeof(buf)));
+}
+
+/* Inject all prefixes of a particular address-family (currently, IPv4 or
+ * IPv6 unicast) into EVPN as type-5 routes. This is invoked when the
+ * advertisement is enabled.
+ */
+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;
+ struct bgp_info *ri;
+
+ /* Bail out early if we don't have to advertise type-5 routes. */
+ 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)) {
+ /* Need to identify the "selected" route entry to use its
+ * attribute.
+ * TODO: Support for AddPath for EVPN.
+ */
+ for (ri = rn->info; ri; ri = ri->next) {
+ if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED)) {
+
+ /* apply the route-map */
+ if (bgp_vrf->adv_cmd_rmap[afi][safi].map) {
+ int ret = 0;
+
+ ret =
+ route_map_apply(
+ bgp_vrf->adv_cmd_rmap[afi][safi].map,
+ &rn->p, RMAP_BGP, ri);
+ if (ret == RMAP_DENYMATCH)
+ continue;
+ }
+
+ bgp_evpn_advertise_type5_route(bgp_vrf, &rn->p,
+ ri->attr,
+ afi, safi);
+ break;
+ }
+ }
+ }
+}
+
+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
}
/*
- * Function to display "tag" in route as a VNI.
+ * TODO: Hardcoded for a maximum of 2 VNIs right now
*/
-char *bgp_evpn_label2str(mpls_label_t *label, char *buf, int len)
+char *bgp_evpn_label2str(mpls_label_t *label, u_int32_t num_labels,
+ char *buf, int len)
{
- vni_t vni;
-
- vni = label2vni(label);
- snprintf(buf, len, "%u", vni);
+ vni_t vni1, vni2;
+
+ vni1 = label2vni(label);
+ if (num_labels == 2) {
+ vni2 = label2vni(label+1);
+ snprintf(buf, len, "%u/%u", vni1, vni2);
+ } else
+ snprintf(buf, len, "%u", vni1);
return buf;
}
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]:[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)",
* Encode EVPN prefix in Update (MP_REACH)
*/
void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p,
- struct prefix_rd *prd, mpls_label_t *label,
+ struct prefix_rd *prd,
+ mpls_label_t *label, u_int32_t num_labels,
struct attr *attr, int addpath_encode,
u_int32_t addpath_tx_id)
{
struct prefix_evpn *evp = (struct prefix_evpn *)p;
- int ipa_len = 0;
+ int len, ipa_len = 0;
if (addpath_encode)
stream_putl(s, addpath_tx_id);
ipa_len = IPV4_MAX_BYTELEN;
else if (IS_EVPN_PREFIX_IPADDR_V6(evp))
ipa_len = IPV6_MAX_BYTELEN;
- stream_putc(s, 33 + ipa_len); // 1 VNI
+ /* RD, ESI, EthTag, MAC+len, IP len, [IP], 1 VNI */
+ len = 8 + 10 + 4 + 1 + 6 + 1 + ipa_len + 3;
+ if (ipa_len && num_labels > 1) /* There are 2 VNIs */
+ len += 3;
+ stream_putc(s, len);
stream_put(s, prd->val, 8); /* RD */
stream_put(s, 0, 10); /* ESI */
stream_putl(s, 0); /* Ethernet Tag ID */
stream_putc(s, 8 * ETH_ALEN); /* Mac Addr Len - bits */
stream_put(s, evp->prefix.mac.octet, 6); /* Mac Addr */
stream_putc(s, 8 * ipa_len); /* IP address Length */
- if (ipa_len)
- stream_put(s, &evp->prefix.ip.ip.addr,
- ipa_len); /* IP */
- stream_put(s, label,
- BGP_LABEL_BYTES); /* VNI is contained in 'tag' */
+ if (ipa_len) /* IP */
+ stream_put(s, &evp->prefix.ip.ip.addr, ipa_len);
+ /* 1st label is the L2 VNI */
+ stream_put(s, label, BGP_LABEL_BYTES);
+ /* Include 2nd label (L3 VNI) if advertising MAC+IP */
+ if (ipa_len && num_labels > 1)
+ stream_put(s, label+1, BGP_LABEL_BYTES);
break;
case BGP_EVPN_IMET_ROUTE:
case BGP_EVPN_IP_PREFIX_ROUTE:
/* TODO: AddPath support. */
- evpn_mpattr_encode_type5(s, p, prd, label, attr);
+ evpn_mpattr_encode_type5(s, p, prd, label, num_labels, attr);
break;
default:
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);
char buf2[INET6_ADDRSTRLEN];
zlog_err(
- "%u:Failed to create Type-2 route, VNI %u %s MAC %s IP %s",
+ "%u:Failed to create Type-2 route, VNI %u %s MAC %s IP %s (flags: 0x%x)",
bgp->vrf_id, vpn->vni,
- CHECK_FLAG(flags, ZEBRA_MAC_TYPE_STICKY) ? "sticky gateway"
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? "sticky gateway"
: "",
prefix_mac2str(mac, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)));
+ ipaddr2str(ip, buf2, sizeof(buf2)),
+ flags);
+ return -1;
+ }
+
+ 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;
}
* 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;
+}
+
+static inline void vni2label(vni_t vni, mpls_label_t *label)
+{
+ u_char *tag = (u_char *)label;
+
+ tag[0] = (vni >> 16) & 0xFF;
+ tag[1] = (vni >> 8) & 0xFF;
+ tag[2] = vni & 0xFF;
+}
+
+static inline vni_t label2vni(mpls_label_t *label)
+{
+ u_char *tag = (u_char *)label;
+ vni_t vni;
+
+ vni = ((u_int32_t)*tag++ << 16);
+ vni |= (u_int32_t)*tag++ << 8;
+ vni |= (u_int32_t)(*tag & 0xFF);
+
+ return vni;
+}
+
+extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf,
+ struct prefix *p,
+ struct attr *src_attr,
+ afi_t afi, safi_t safi);
+extern void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf,
+ struct prefix *p,
+ 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_label2str(mpls_label_t *label, u_int32_t num_labels,
+ char *buf, int len);
extern char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len);
extern void bgp_evpn_route2json(struct prefix_evpn *p, json_object *json);
extern void bgp_evpn_encode_prefix(struct stream *s, struct prefix *p,
- struct prefix_rd *prd, mpls_label_t *label,
+ struct prefix_rd *prd,
+ mpls_label_t *label, u_int32_t num_labels,
struct attr *attr, int addpath_encode,
u_int32_t addpath_tx_id);
extern int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
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" */
/* Flag to indicate if we are advertising the g/w mac ip for this VNI*/
u_int8_t advertise_gw_macip;
+ /* Flag to indicate if we are advertising subnet for this VNI */
+ u_int8_t advertise_subnet;
+
/* Id for deriving the RD automatically for this VNI */
u_int16_t rd_id;
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));
|| is_export_rt_configured(vpn));
}
-static inline void vni2label(vni_t vni, mpls_label_t *label)
+static inline void encode_rmac_extcomm(struct ecommunity_val *eval,
+ struct ethaddr *rmac)
{
- u_char *tag = (u_char *)label;
- tag[0] = (vni >> 16) & 0xFF;
- tag[1] = (vni >> 8) & 0xFF;
- tag[2] = vni & 0xFF;
+ 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 vni_t label2vni(mpls_label_t *label)
+static inline void encode_default_gw_extcomm(struct ecommunity_val *eval)
{
- u_char *tag = (u_char *)label;
- vni_t vni;
-
- vni = ((u_int32_t)*tag++ << 16);
- vni |= (u_int32_t)*tag++ << 8;
- vni |= (u_int32_t)(*tag & 0xFF);
-
- return vni;
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_OPAQUE;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_DEF_GW;
}
static inline void encode_mac_mobility_extcomm(int static_mac, u_int32_t seq,
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++);
+ 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)
{
case ECOMMUNITY_ENCODE_AS:
eas.as = (*pnt++ << 8);
eas.as |= (*pnt++);
- pnt = ptr_get_be32(pnt, &eas.val);
+ ptr_get_be32(pnt, &eas.val);
snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n");
vty_out(vty,
"EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n");
- vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n\n");
+ vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
+ vty_out(vty, "EVPN type-5 prefix: [5]:[ESI]:[EthTag]:[IPlen]:[IP]\n\n");
vty_out(vty, "%s", ri_header);
}
+static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf,
+ json_object *json)
+{
+ char buf1[INET6_ADDRSTRLEN];
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+ json_object *json_import_rtl = NULL;
+ json_object *json_export_rtl = NULL;
+
+ json_import_rtl = json_export_rtl = 0;
+
+ if (json) {
+ json_import_rtl = json_object_new_array();
+ json_export_rtl = json_object_new_array();
+ json_object_int_add(json, "vni", bgp_vrf->l3vni);
+ json_object_string_add(json, "type", "L3");
+ json_object_string_add(json, "kernelFlag", "Yes");
+ json_object_string_add(
+ json, "rd",
+ prefix_rd2str(&bgp_vrf->vrf_prd, buf1, RD_ADDRSTRLEN));
+ json_object_string_add(json, "originatorIp",
+ inet_ntoa(bgp_vrf->originator_ip));
+ json_object_string_add(json, "advertiseGatewayMacip", "n/a");
+ } else {
+ vty_out(vty, "VNI: %d", bgp_vrf->l3vni);
+ vty_out(vty, " (known to the kernel)");
+ vty_out(vty, "\n");
+
+ vty_out(vty, " Type: %s\n", "L3");
+ vty_out(vty, " Tenant VRF: %s\n",
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ vty_out(vty, " RD: %s\n",
+ prefix_rd2str(&bgp_vrf->vrf_prd, buf1, RD_ADDRSTRLEN));
+ vty_out(vty, " Originator IP: %s\n",
+ inet_ntoa(bgp_vrf->originator_ip));
+ vty_out(vty, " Advertise-gw-macip : %s\n", "n/a");
+ }
+
+ if (!json)
+ vty_out(vty, " Import Route Target:\n");
+
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json)
+ json_object_array_add(json_import_rtl,
+ json_object_new_string(ecom_str));
+ else
+ vty_out(vty, " %s\n", ecom_str);
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+
+ if (json)
+ json_object_object_add(json, "importRts", json_import_rtl);
+ else
+ vty_out(vty, " Export Route Target:\n");
+
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json)
+ json_object_array_add(json_export_rtl,
+ json_object_new_string(ecom_str));
+ else
+ vty_out(vty, " %s\n", ecom_str);
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+
+ if (json)
+ json_object_object_add(json, "exportRts", json_export_rtl);
+}
+
static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
{
char buf1[RD_ADDRSTRLEN];
json_import_rtl = json_object_new_array();
json_export_rtl = json_object_new_array();
json_object_int_add(json, "vni", vpn->vni);
+ json_object_string_add(json, "type", "L2");
json_object_string_add(json, "kernelFlag",
is_vni_live(vpn) ? "Yes" : "No");
json_object_string_add(
vty_out(vty, " (known to the kernel)");
vty_out(vty, "\n");
+ vty_out(vty, " Type: %s\n", "L2");
+ 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, sizeof(buf1)));
vty_out(vty, " Originator IP: %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)
json_object_object_add(json, vni_str, json_vni);
}
-static void show_vni_entry(struct hash_backet *backet, void *args[])
+static void show_l3vni_entry(struct vty *vty, struct bgp *bgp,
+ json_object *json)
{
- struct vty *vty;
- json_object *json;
json_object *json_vni;
json_object *json_import_rtl;
json_object *json_export_rtl;
+ char buf1[10];
+ char buf2[INET6_ADDRSTRLEN];
+ char rt_buf[25];
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ if (!bgp->l3vni)
+ return;
+
+ if (json) {
+ json_vni = json_object_new_object();
+ json_import_rtl = json_object_new_array();
+ json_export_rtl = json_object_new_array();
+ }
+
+ /* if an l3vni is present in bgp it is live */
+ buf1[0] = '\0';
+ sprintf(buf1, "*");
+
+ if (json) {
+ json_object_int_add(json_vni, "vni", bgp->l3vni);
+ json_object_string_add(json_vni, "type", "L3");
+ json_object_string_add(json_vni, "inKernel", "True");
+ json_object_string_add(json_vni, "originatorIp",
+ inet_ntoa(bgp->originator_ip));
+ json_object_string_add(
+ json_vni, "rd",
+ prefix_rd2str(&bgp->vrf_prd, buf2, RD_ADDRSTRLEN));
+ } else {
+ vty_out(vty, "%-1s %-10u %-4s %-21s",
+ buf1, bgp->l3vni, "L3",
+ prefix_rd2str(&bgp->vrf_prd, buf2, RD_ADDRSTRLEN));
+ }
+
+ for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json) {
+ json_object_array_add(json_import_rtl,
+ json_object_new_string(ecom_str));
+ } else {
+ if (listcount(bgp->vrf_import_rtl) > 1)
+ sprintf(rt_buf, "%s, ...", ecom_str);
+ else
+ sprintf(rt_buf, "%s", ecom_str);
+ vty_out(vty, " %-25s", rt_buf);
+ }
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+
+ /* If there are multiple import RTs we break here and show only
+ * one */
+ if (!json)
+ break;
+ }
+
+ if (json)
+ json_object_object_add(json_vni, "importRTs", json_import_rtl);
+
+ for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json) {
+ json_object_array_add(json_export_rtl,
+ json_object_new_string(ecom_str));
+ } else {
+ if (listcount(bgp->vrf_export_rtl) > 1)
+ sprintf(rt_buf, "%s, ...", ecom_str);
+ else
+ sprintf(rt_buf, "%s", ecom_str);
+ vty_out(vty, " %-25s", rt_buf);
+ }
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+
+ /* If there are multiple export RTs we break here and show only
+ * one */
+ if (!json)
+ break;
+ }
+
+ if (!json)
+ vty_out(vty, "%-37s", vrf_id_to_name(bgp->vrf_id));
+
+ if (json) {
+ char vni_str[VNI_STR_LEN];
+
+ json_object_object_add(json_vni, "exportRTs", json_export_rtl);
+ snprintf(vni_str, VNI_STR_LEN, "%u", bgp->l3vni);
+ json_object_object_add(json, vni_str, json_vni);
+ } else {
+ vty_out(vty, "\n");
+ }
+}
+
+static void show_vni_entry(struct hash_backet *backet, void *args[])
+{
+ struct vty *vty;
+ json_object *json;
+ 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[RD_ADDRSTRLEN];
if (json) {
json_object_int_add(json_vni, "vni", vpn->vni);
+ json_object_string_add(json_vni, "type", "L2");
json_object_string_add(json_vni, "inKernel",
is_vni_live(vpn) ? "True" : "False");
json_object_string_add(json_vni, "originatorIp",
inet_ntoa(vpn->originator_ip));
+ json_object_string_add(json_vni, "originatorIp",
+ inet_ntoa(vpn->originator_ip));
json_object_string_add(
json_vni, "rd",
prefix_rd2str(&vpn->prd, buf2, sizeof(buf2)));
} else {
- vty_out(vty, "%-1s %-10u %-15s %-21s", buf1, vpn->vni,
- inet_ntoa(vpn->originator_ip),
- prefix_rd2str(&vpn->prd, buf2, sizeof(buf2)));
+ vty_out(vty, "%-1s %-10u %-4s %-21s",
+ buf1, vpn->vni, "L2",
+ prefix_rd2str(&vpn->prd, buf2, RD_ADDRSTRLEN));
}
for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
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\n");
+ "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
+ vty_out(vty,
+ "EVPN type-5 prefix: [5]:[ESI]:[EthTag]:[IPlen]:[IP]\n\n");
rd_header = 0;
}
static void evpn_show_vni(struct vty *vty, struct bgp *bgp, vni_t vni,
json_object *json)
{
+ u_char found = 0;
struct bgpevpn *vpn;
vpn = bgp_evpn_lookup_vni(bgp, vni);
- if (!vpn) {
+ if (vpn) {
+ found = 1;
+ display_vni(vty, vpn, json);
+ } else {
+ struct bgp *bgp_temp;
+ struct listnode *node = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_temp)) {
+ if (bgp_temp->l3vni == vni) {
+ found = 1;
+ display_l3vni(vty, bgp_temp, json);
+ }
+ }
+ }
+
+ if (!found) {
if (json) {
vty_out(vty, "{}\n");
} else {
return;
}
}
-
- display_vni(vty, vpn, json);
}
/*
static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp,
json_object *json)
{
- u_int32_t num_vnis;
void *args[2];
+ struct bgp *bgp_temp = NULL;
+ struct listnode *node;
- num_vnis = hashcount(bgp->vnihash);
- if (!num_vnis)
- return;
- if (json) {
- json_object_int_add(json, "numVnis", num_vnis);
- } else {
- vty_out(vty, "Number of VNIs: %u\n", num_vnis);
+ if (!json) {
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 %-4s %-21s %-25s %-25s %-37s\n", "VNI",
+ "Type", "RD", "Import RT",
+ "Export RT", "Tenant VRF");
}
+ /* print all L2 VNIS */
args[0] = vty;
args[1] = json;
hash_iterate(bgp->vnihash,
(void (*)(struct hash_backet *, void *))show_vni_entry,
args);
+
+ /* print all L3 VNIs */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_temp))
+ show_l3vni_entry(vty, bgp_temp, json);
+
}
/*
return;
}
+/*
+ * evpn - enable advertisement of default g/w
+ */
+static void evpn_set_advertise_subnet(struct bgp *bgp,
+ struct bgpevpn *vpn)
+{
+ if (vpn->advertise_subnet)
+ return;
+
+ vpn->advertise_subnet = 1;
+ bgp_zebra_advertise_subnet(bgp, vpn->advertise_subnet, vpn->vni);
+}
+
+/*
+ * evpn - disable advertisement of default g/w
+ */
+static void evpn_unset_advertise_subnet(struct bgp *bgp,
+ struct bgpevpn *vpn)
+{
+ if (!vpn->advertise_subnet)
+ return;
+
+ vpn->advertise_subnet = 0;
+ bgp_zebra_advertise_subnet(bgp, vpn->advertise_subnet, vpn->vni);
+}
+
/*
* EVPN (VNI advertisement) enabled. Register with zebra.
*/
if (vpn->advertise_gw_macip)
vty_out(vty, " advertise-default-gw\n");
+ if (vpn->advertise_subnet)
+ vty_out(vty, " advertise-subnet\n");
+
vty_out(vty, " exit-vni\n");
}
}
return CMD_SUCCESS;
}
+DEFUN (bgp_evpn_advertise_vni_subnet,
+ bgp_evpn_advertise_vni_subnet_cmd,
+ "advertise-subnet",
+ "Advertise the subnet corresponding to VNI\n")
+{
+ struct bgp *bgp_vrf = NULL;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!vpn)
+ return CMD_WARNING;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ if (!(advertise_type5_routes(bgp_vrf, AFI_IP) ||
+ advertise_type5_routes(bgp_vrf, AFI_IP6))) {
+ vty_out(vty,
+ "%%Please enable ip prefix advertisement under l2vpn evpn in %s",
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ return CMD_WARNING;
+ }
+
+ evpn_set_advertise_subnet(bgp, vpn);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_vni_subnet,
+ no_bgp_evpn_advertise_vni_subnet_cmd,
+ "no advertise-subnet",
+ NO_STR
+ "Advertise All local VNIs\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!vpn)
+ return CMD_WARNING;
+
+ evpn_unset_advertise_subnet(bgp, vpn);
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_evpn_advertise_type5,
+ bgp_evpn_advertise_type5_cmd,
+ "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]",
+ "Advertise prefix routes\n"
+ BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR
+ "route-map for filtering specific routes\n"
+ "Name of the route map\n")
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
+ int idx_afi = 0;
+ int idx_safi = 0;
+ int idx_rmap = 0;
+ afi_t afi = 0;
+ safi_t safi = 0;
+ int ret = 0;
+ int rmap_changed = 0;
+
+ argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
+ argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+ ret = argv_find(argv, argc, "route-map", &idx_rmap);
+ if (ret) {
+ if (!bgp_vrf->adv_cmd_rmap[afi][safi].name)
+ rmap_changed = 1;
+ else if (strcmp(argv[idx_rmap + 1]->arg,
+ bgp_vrf->adv_cmd_rmap[afi][safi].name) != 0)
+ rmap_changed = 1;
+ } else if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
+ rmap_changed = 1;
+ }
+
+ 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 (!rmap_changed && CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV4_IN_EVPN))
+ return CMD_WARNING;
+ SET_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 (!rmap_changed && CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV6_IN_EVPN))
+ return CMD_WARNING;
+ SET_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
+ }
+
+ if (rmap_changed) {
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
+ if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_vrf->adv_cmd_rmap[afi][safi].name);
+ bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
+ bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
+ }
+ }
+
+ /* set the route-map for advertise command */
+ if (ret && argv[idx_rmap + 1]->arg) {
+ bgp_vrf->adv_cmd_rmap[afi][safi].name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME,
+ argv[idx_rmap + 1]->arg);
+ bgp_vrf->adv_cmd_rmap[afi][safi].map =
+ route_map_lookup_by_name(argv[idx_rmap + 1]->arg);
+ }
+
+ /* advertise type-5 routes */
+ 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);
+ }
+ }
+
+ /* clear the route-map information for advertise ipv4/ipv6 unicast */
+ if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_vrf->adv_cmd_rmap[afi][safi].name);
+ bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
+ bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
+ }
+
+ return CMD_SUCCESS;
+}
+
/*
* Display VNI information - for all or a specific VNI
*/
"VNI number\n"
JSON_STR)
{
- struct bgp *bgp;
+ struct bgp *bgp_def;
vni_t vni;
int idx = 0;
u_char uj = 0;
json_object *json = NULL;
+ u_int32_t num_l2vnis = 0;
+ u_int32_t num_l3vnis = 0;
+ uint32_t num_vnis = 0;
+ struct listnode *node = NULL;
+ struct bgp *bgp_temp = NULL;
uj = use_json(argc, argv);
- bgp = bgp_get_default();
- if (!bgp)
+ bgp_def = bgp_get_default();
+ if (!bgp_def)
return CMD_WARNING;
if (!argv_find(argv, argc, "evpn", &idx))
json = json_object_new_object();
if ((uj && argc == ((idx + 1) + 2)) || (!uj && argc == (idx + 1) + 1)) {
+
+ num_l2vnis = hashcount(bgp_def->vnihash);
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_temp)) {
+ if (bgp_temp->l3vni)
+ num_l3vnis++;
+ }
+ num_vnis = num_l2vnis + num_l3vnis;
if (uj) {
json_object_string_add(json, "advertiseGatewayMacip",
- bgp->advertise_gw_macip
+ bgp_def->advertise_gw_macip
? "Enabled"
: "Disabled");
json_object_string_add(json, "advertiseAllVnis",
- bgp->advertise_all_vni
+ is_evpn_enabled()
? "Enabled"
: "Disabled");
+ json_object_int_add(json, "numVnis", num_vnis);
+ json_object_int_add(json, "numL2Vnis", num_l2vnis);
+ json_object_int_add(json, "numL3Vnis", num_l3vnis);
} else {
vty_out(vty, "Advertise Gateway Macip: %s\n",
- bgp->advertise_gw_macip ? "Enabled"
+ bgp_def->advertise_gw_macip ? "Enabled"
: "Disabled");
-
- /* Display all VNIs */
vty_out(vty, "Advertise All VNI flag: %s\n",
- bgp->advertise_all_vni ? "Enabled"
- : "Disabled");
+ is_evpn_enabled() ? "Enabled" : "Disabled");
+ vty_out(vty, "Number of L2 VNIs: %u\n", num_l2vnis);
+ vty_out(vty, "Number of L3 VNIs: %u\n", num_l3vnis);
}
-
- evpn_show_all_vnis(vty, bgp, json);
+ evpn_show_all_vnis(vty, bgp_def, json);
} else {
int vni_idx = 0;
/* Display specific VNI */
vni = strtoul(argv[vni_idx + 1]->arg, NULL, 10);
- evpn_show_vni(vty, bgp, vni, json);
+ evpn_show_vni(vty, bgp_def, vni, json);
}
if (uj) {
*/
DEFUN(show_bgp_l2vpn_evpn_route,
show_bgp_l2vpn_evpn_route_cmd,
- "show bgp l2vpn evpn route [type <macip|multicast>] [json]",
+ "show bgp l2vpn evpn route [type <macip|multicast|prefix>] [json]",
SHOW_STR
BGP_STR
L2VPN_HELP_STR
"Specify Route type\n"
"MAC-IP (Type-2) route\n"
"Multicast (Type-3) route\n"
+ "Prefix route\n"
JSON_STR)
{
struct bgp *bgp;
type = BGP_EVPN_MAC_IP_ROUTE;
else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0)
type = BGP_EVPN_IMET_ROUTE;
+ else if (strncmp(argv[type_idx + 1]->arg, "pr", 2) == 0)
+ type = BGP_EVPN_IP_PREFIX_ROUTE;
else
return CMD_WARNING;
}
json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
}
-
return CMD_SUCCESS;
}
*/
DEFUN(show_bgp_l2vpn_evpn_route_rd,
show_bgp_l2vpn_evpn_route_rd_cmd,
- "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|multicast>] [json]",
+ "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|multicast|prefix>] [json]",
SHOW_STR
BGP_STR
L2VPN_HELP_STR
"Specify Route type\n"
"MAC-IP (Type-2) route\n"
"Multicast (Type-3) route\n"
+ "Prefix route\n"
JSON_STR)
{
struct bgp *bgp;
type = BGP_EVPN_MAC_IP_ROUTE;
else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0)
type = BGP_EVPN_IMET_ROUTE;
+ else if (strncmp(argv[type_idx + 1]->arg, "pr", 2) == 0)
+ type = BGP_EVPN_IP_PREFIX_ROUTE;
else
return CMD_WARNING;
}
}
}
- evpn_show_route_rd_macip(vty, bgp, &prd, &mac, &ip, json);
+ evpn_show_route_rd_macip(vty, bgp, &prd, &mac, &ip, 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 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;
- if (uj) {
- vty_out(vty, "%s\n", json_object_to_json_string_ext(
- json, JSON_C_TO_STRING_PRETTY));
- json_object_free(json);
- }
+ evpn_show_vrf_routes(vty, bgp_vrf);
return CMD_SUCCESS;
}
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 vni [json]",
+ SHOW_STR
+ BGP_STR
+ "show bgp vrf\n"
+ "VRF Name\n"
+ "L3-VNI\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,
&no_bgp_evpn_advertise_default_gw_vni_cmd);
+ install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_vni_subnet_cmd);
+ install_element(BGP_EVPN_VNI_NODE,
+ &no_bgp_evpn_advertise_vni_subnet_cmd);
#endif
}
#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 */
if (!rn || !ri || !to)
return MPLS_INVALID_LABEL;
- remote_label = ri->extra ? ri->extra->label : MPLS_INVALID_LABEL;
+ remote_label = ri->extra ? ri->extra->label[0] : MPLS_INVALID_LABEL;
from = ri->peer;
reflect =
((from->sort == BGP_PEER_IBGP) && (to->sort == BGP_PEER_IBGP));
if (attr) {
bgp_update(peer, &p, addpath_id, attr, packet->afi,
SAFI_UNICAST, ZEBRA_ROUTE_BGP,
- BGP_ROUTE_NORMAL, NULL, &label, 0, NULL);
+ BGP_ROUTE_NORMAL, NULL, &label, 1, 0, NULL);
} else {
bgp_withdraw(peer, &p, addpath_id, attr, packet->afi,
SAFI_UNICAST, ZEBRA_ROUTE_BGP,
- BGP_ROUTE_NORMAL, NULL, &label, NULL);
+ BGP_ROUTE_NORMAL, NULL, &label, 1, NULL);
}
}
#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 */
if (attr) {
bgp_update(peer, &p, addpath_id, attr, packet->afi,
SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP,
- BGP_ROUTE_NORMAL, &prd, &label, 0, NULL);
+ BGP_ROUTE_NORMAL, &prd, &label, 1, 0, NULL);
} else {
bgp_withdraw(peer, &p, addpath_id, attr, packet->afi,
SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP,
- BGP_ROUTE_NORMAL, &prd, &label, NULL);
+ BGP_ROUTE_NORMAL, &prd, &label, 1, NULL);
}
}
/* Packet length consistency check. */
return CMD_WARNING;
}
table = bgp->rib[afi][SAFI_MPLS_VPN];
- return bgp_show_table_rd(vty, bgp, SAFI_MPLS_VPN,
- table, prd, type, output_arg, use_json);
+ return bgp_show_table_rd(vty, bgp, SAFI_MPLS_VPN, table, prd, type,
+ output_arg, use_json);
}
DEFUN (show_bgp_ip_vpn_all_rd,
if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
if (argv_find(argv, argc, "rd", &idx)) {
- ret = str2prefix_rd(argv[idx+1]->arg, &prd);
+ ret = str2prefix_rd(argv[idx + 1]->arg, &prd);
if (!ret) {
vty_out(vty,
"%% Malformed Route Distinguisher\n");
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_rd.h"
-#ifdef MPLS_LABEL_MAX
-#undef MPLS_LABEL_MAX
-#endif
-
-typedef enum {
- MPLS_LABEL_IPV4_EXPLICIT_NULL = 0, /* [RFC3032] */
- MPLS_LABEL_ROUTER_ALERT = 1, /* [RFC3032] */
- MPLS_LABEL_IPV6_EXPLICIT_NULL = 2, /* [RFC3032] */
- MPLS_LABEL_IMPLICIT_NULL = 3, /* [RFC3032] */
- MPLS_LABEL_UNASSIGNED4 = 4,
- MPLS_LABEL_UNASSIGNED5 = 5,
- MPLS_LABEL_UNASSIGNED6 = 6,
- MPLS_LABEL_ELI = 7, /* Entropy Indicator [RFC6790] */
- MPLS_LABEL_UNASSIGNED8 = 8,
- MPLS_LABEL_UNASSIGNED9 = 9,
- MPLS_LABEL_UNASSIGNED10 = 10,
- MPLS_LABEL_UNASSIGNED11 = 11,
- MPLS_LABEL_GAL = 13, /* [RFC5586] */
- MPLS_LABEL_OAM_ALERT = 14, /* [RFC3429] */
- MPLS_LABEL_EXTENSION = 15, /* [RFC7274] */
- MPLS_LABEL_MAX = 1048575,
- MPLS_LABEL_ILLEGAL = 0xFFFFFFFF /* for internal use only */
-} mpls_special_label_t;
-
#define MPLS_LABEL_IS_SPECIAL(label) ((label) <= MPLS_LABEL_EXTENSION)
#define MPLS_LABEL_IS_NULL(label) \
((label) == MPLS_LABEL_IPV4_EXPLICIT_NULL \
void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
{
- struct stream *s;
struct bgp_node *rn = NULL;
struct bgp_nexthop_cache *bnc;
struct nexthop *nexthop;
struct nexthop *oldnh;
struct nexthop *nhlist_head = NULL;
struct nexthop *nhlist_tail = NULL;
- uint32_t metric;
- u_char nexthop_num;
- struct prefix p;
int i;
struct bgp *bgp;
+ struct zapi_route nhr;
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;
}
- s = zclient->ibuf;
-
- memset(&p, 0, sizeof(struct prefix));
- p.family = stream_getw(s);
- p.prefixlen = stream_getc(s);
- switch (p.family) {
- case AF_INET:
- p.u.prefix4.s_addr = stream_get_ipv4(s);
- break;
- case AF_INET6:
- stream_get(&p.u.prefix6, s, 16);
- break;
- default:
- break;
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug("%s: Failure to decode nexthop update",
+ __PRETTY_FUNCTION__);
+ return;
}
if (command == ZEBRA_NEXTHOP_UPDATE)
rn = bgp_node_lookup(
- bgp->nexthop_cache_table[family2afi(p.family)], &p);
+ bgp->nexthop_cache_table[family2afi(nhr.prefix.family)],
+ &nhr.prefix);
else if (command == ZEBRA_IMPORT_CHECK_UPDATE)
rn = bgp_node_lookup(
- bgp->import_check_table[family2afi(p.family)], &p);
+ bgp->import_check_table[family2afi(nhr.prefix.family)],
+ &nhr.prefix);
if (!rn || !rn->info) {
if (BGP_DEBUG(nht, NHT)) {
char buf[PREFIX2STR_BUFFER];
- prefix2str(&p, buf, sizeof(buf));
+ prefix2str(&nhr.prefix, buf, sizeof(buf));
zlog_debug("parse nexthop update(%s): rn not found",
buf);
}
bgp_unlock_node(rn);
bnc->last_update = bgp_clock();
bnc->change_flags = 0;
- stream_getc(s); // Distance but not currently used
- metric = stream_getl(s);
- nexthop_num = stream_getc(s);
/* debug print the input */
if (BGP_DEBUG(nht, NHT)) {
char buf[PREFIX2STR_BUFFER];
- prefix2str(&p, buf, sizeof(buf));
+ prefix2str(&nhr.prefix, buf, sizeof(buf));
zlog_debug(
- "%d: Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x",
- vrf_id, buf, metric, bnc->metric, nexthop_num,
+ "%u: Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x",
+ vrf_id, buf, nhr.metric, bnc->metric, nhr.nexthop_num,
bnc->nexthop_num, bnc->flags);
}
- if (metric != bnc->metric)
+ if (nhr.metric != bnc->metric)
bnc->change_flags |= BGP_NEXTHOP_METRIC_CHANGED;
- if (nexthop_num != bnc->nexthop_num)
+ if (nhr.nexthop_num != bnc->nexthop_num)
bnc->change_flags |= BGP_NEXTHOP_CHANGED;
- if (nexthop_num) {
+ if (nhr.nexthop_num) {
/* notify bgp fsm if nbr ip goes from invalid->valid */
if (!bnc->nexthop_num)
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
bnc->flags |= BGP_NEXTHOP_VALID;
- bnc->metric = metric;
- bnc->nexthop_num = nexthop_num;
-
- for (i = 0; i < nexthop_num; i++) {
- nexthop = nexthop_new();
- nexthop->type = stream_getc(s);
- switch (nexthop->type) {
- case NEXTHOP_TYPE_IPV4:
- nexthop->gate.ipv4.s_addr = stream_get_ipv4(s);
- nexthop->ifindex = stream_getl(s);
- break;
- case NEXTHOP_TYPE_IFINDEX:
- nexthop->ifindex = stream_getl(s);
- break;
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- nexthop->gate.ipv4.s_addr = stream_get_ipv4(s);
- nexthop->ifindex = stream_getl(s);
- break;
- case NEXTHOP_TYPE_IPV6:
- stream_get(&nexthop->gate.ipv6, s, 16);
- nexthop->ifindex = stream_getl(s);
- break;
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- stream_get(&nexthop->gate.ipv6, s, 16);
- nexthop->ifindex = stream_getl(s);
- break;
- default:
- /* do nothing */
- break;
- }
+ bnc->metric = nhr.metric;
+ bnc->nexthop_num = nhr.nexthop_num;
+
+ for (i = 0; i < nhr.nexthop_num; i++) {
+ nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
if (BGP_DEBUG(nht, NHT)) {
char buf[NEXTHOP_STRLEN];
bnc->nexthop = nhlist_head;
} else {
bnc->flags &= ~BGP_NEXTHOP_VALID;
- bnc->nexthop_num = nexthop_num;
+ bnc->nexthop_num = nhr.nexthop_num;
/* notify bgp fsm if nbr ip goes from valid->invalid */
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
*/
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(
#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[];
{
struct bgp_info_extra *new;
new = XCALLOC(MTYPE_BGP_ROUTE_EXTRA, sizeof(struct bgp_info_extra));
- new->label = MPLS_INVALID_LABEL;
+ new->label[0] = MPLS_INVALID_LABEL;
+ new->num_labels = 0;
return new;
}
/* If one path has a label but the other does not, do not treat
* them as equals for multipath
*/
- if ((new->extra &&bgp_is_valid_label(&new->extra->label))
+ if ((new->extra &&bgp_is_valid_label(&new->extra->label[0]))
!= (exist->extra
- && bgp_is_valid_label(&exist->extra->label))) {
+ && bgp_is_valid_label(&exist->extra->label[0]))) {
if (debug)
zlog_debug(
"%s: %s and %s cannot be multipath, one has a label while the other does not",
if (old) {
merge = community_merge(community_dup(old), gshut);
- if (old->refcnt== 0)
+ if (old->refcnt == 0)
community_free(old);
new = community_uniq_sort(merge);
}
if (bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
- if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) {
+ if (peer->sort == BGP_PEER_IBGP
+ || peer->sort == BGP_PEER_CONFED) {
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
attr->local_pref = BGP_GSHUT_LOCAL_PREF;
} else {
subgrp))
subgroup_announce_reset_nhop(
(peer_cap_enhe(peer, afi, safi)
- ? AF_INET6
- : p->family),
+ ? AF_INET6
+ : p->family),
attr);
}
/* If IPv6/MP and nexthop does not have any override and happens
: NULL);
/* First update is deferred until ORF or ROUTE-REFRESH is received */
- if (onlypeer && CHECK_FLAG(onlypeer->af_sflags[afi][safi],
- PEER_STATUS_ORF_WAIT_REFRESH))
+ if (onlypeer
+ && CHECK_FLAG(onlypeer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_WAIT_REFRESH))
return 0;
memset(&attr, 0, sizeof(struct attr));
struct bgp_process_queue {
struct bgp *bgp;
- STAILQ_HEAD(, bgp_node)pqueue;
+ STAILQ_HEAD(, bgp_node) pqueue;
#define BGP_PROCESS_QUEUE_EOIU_MARKER (1 << 0)
unsigned int flags;
unsigned int queued;
rn->flags,
BGP_NODE_REGISTERED_FOR_LABEL))
bgp_unregister_for_label(rn);
- label_ntop(MPLS_IMP_NULL_LABEL, 1,
+ label_ntop(MPLS_LABEL_IMPLICIT_NULL, 1,
&rn->local_label);
bgp_set_valid_label(&rn->local_label);
} else
bgp_register_for_label(rn, new_select);
}
- } else if (CHECK_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) {
+ } else if (CHECK_FLAG(rn->flags,
+ BGP_NODE_REGISTERED_FOR_LABEL)) {
bgp_unregister_for_label(rn);
}
} else if (CHECK_FLAG(rn->flags, BGP_NODE_REGISTERED_FOR_LABEL)) {
}
}
+ /* 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->p, new_select->attr, afi, safi);
+ else if (old_select)
+ bgp_evpn_withdraw_type5_route(bgp, &rn->p, afi, safi);
+ }
+
/* Clear any route change flags. */
bgp_zebra_clear_route_change_flags(rn);
{
struct bgp_process_queue *pqnode;
- pqnode = XCALLOC(MTYPE_BGP_PROCESS_QUEUE, sizeof(struct bgp_process_queue));
+ pqnode = XCALLOC(MTYPE_BGP_PROCESS_QUEUE,
+ sizeof(struct bgp_process_queue));
/* unlocked in bgp_processq_del */
pqnode->bgp = bgp_lock(bgp);
return;
/* Add route nodes to an existing work queue item until reaching the
- limit only if is from the same BGP view and it's not an EOIU marker */
+ limit only if is from the same BGP view and it's not an EOIU marker
+ */
if (work_queue_item_count(wq)) {
struct work_queue_item *item = work_queue_last_item(wq);
pqnode = item->data;
- if (CHECK_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER) ||
- pqnode->bgp != bgp || pqnode->queued >= ARBITRARY_PROCESS_QLEN)
+ if (CHECK_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER)
+ || pqnode->bgp != bgp
+ || pqnode->queued >= ARBITRARY_PROCESS_QLEN)
pqnode = bgp_processq_alloc(bgp);
else
pqnode_reuse = 1;
int bgp_update(struct peer *peer, struct prefix *p, u_int32_t addpath_id,
struct attr *attr, afi_t afi, safi_t safi, int type,
int sub_type, struct prefix_rd *prd, mpls_label_t *label,
- int soft_reconfig, struct bgp_route_evpn *evpn)
+ u_int32_t num_labels, int soft_reconfig,
+ struct bgp_route_evpn *evpn)
{
int ret;
int aspath_loop_count = 0;
struct attr *attr_new;
struct bgp_info *ri;
struct bgp_info *new;
+ struct bgp_info_extra *extra;
const char *reason;
char pfx_buf[BGP_PRD_PATH_STRLEN];
- char label_buf[20];
int connected = 0;
int do_loop_check = 1;
int has_valid_label = 0;
bgp = peer->bgp;
rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd);
- has_valid_label = bgp_is_valid_label(label);
-
- if (has_valid_label)
- sprintf(label_buf, "label %u", label_pton(label));
+ /* TODO: Check to see if we can get rid of "is_valid_label" */
+ if (afi == AFI_L2VPN && safi == SAFI_EVPN)
+ has_valid_label = (num_labels > 0) ? 1 : 0;
+ else
+ has_valid_label = bgp_is_valid_label(label);
/* When peer's soft reconfiguration enabled. Record input packet in
Adj-RIBs-In. */
/* 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)
if (peer->sort == BGP_PEER_EBGP) {
- /* If we receive the graceful-shutdown community from an eBGP peer we
- * must lower local-preference */
- if (new_attr.community &&
- community_include(new_attr.community, COMMUNITY_GSHUT)) {
+ /* If we receive the graceful-shutdown community from an eBGP
+ * peer we must lower local-preference */
+ if (new_attr.community
+ && community_include(new_attr.community, COMMUNITY_GSHUT)) {
new_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
new_attr.local_pref = BGP_GSHUT_LOCAL_PREF;
- /* If graceful-shutdown is configured then add the GSHUT community to
- * all paths received from eBGP peers */
- } else if (bgp_flag_check(peer->bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+ /* If graceful-shutdown is configured then add the GSHUT
+ * community to all paths received from eBGP peers */
+ } else if (bgp_flag_check(peer->bgp,
+ BGP_FLAG_GRACEFUL_SHUTDOWN)) {
bgp_attr_add_gshut_community(&new_attr);
}
}
/* next hop check. */
- if (!CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD) &&
- bgp_update_martian_nexthop(bgp, afi, safi, &new_attr)) {
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)
+ && bgp_update_martian_nexthop(bgp, afi, safi, &new_attr)) {
reason = "martian or self next-hop;";
bgp_attr_flush(&new_attr);
goto filtered;
&& attrhash_cmp(ri->attr, attr_new)
&& (!has_valid_label
|| memcmp(&(bgp_info_extra_get(ri))->label, label,
- BGP_LABEL_BYTES)
+ num_labels * sizeof(mpls_label_t))
== 0)
&& (overlay_index_equal(
afi, ri, evpn == NULL ? NULL : &evpn->eth_s_id,
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(
afi, safi, prd, p, label,
- addpath_id ? 1 : 0, addpath_id,
- pfx_buf, sizeof(pfx_buf));
+ num_labels, addpath_id ? 1 : 0,
+ addpath_id, pfx_buf,
+ sizeof(pfx_buf));
zlog_debug("%s rcvd %s", peer->host,
pfx_buf);
}
bgp_debug_rdpfxpath2str(
afi, safi, prd, p, label,
- addpath_id ? 1 : 0, addpath_id,
- pfx_buf, sizeof(pfx_buf));
+ num_labels, addpath_id ? 1 : 0,
+ addpath_id, pfx_buf,
+ sizeof(pfx_buf));
zlog_debug(
"%s rcvd %s...duplicate ignored",
peer->host, pfx_buf);
if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) {
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(
- afi, safi, prd, p, label,
+ afi, safi, prd, p, label, num_labels,
addpath_id ? 1 : 0, addpath_id, pfx_buf,
sizeof(pfx_buf));
zlog_debug(
/* Received Logging. */
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label,
- addpath_id ? 1 : 0, addpath_id,
- pfx_buf, sizeof(pfx_buf));
+ num_labels, addpath_id ? 1 : 0,
+ addpath_id, pfx_buf,
+ sizeof(pfx_buf));
zlog_debug("%s rcvd %s", peer->host, pfx_buf);
}
/* Update MPLS label */
if (has_valid_label) {
- memcpy(&(bgp_info_extra_get(ri))->label, label,
- BGP_LABEL_BYTES);
- bgp_set_valid_label(&(bgp_info_extra_get(ri))->label);
+ extra = bgp_info_extra_get(ri);
+ memcpy(&extra->label, label,
+ num_labels * sizeof(mpls_label_t));
+ extra->num_labels = num_labels;
+ if (!(afi == AFI_L2VPN && safi == SAFI_EVPN))
+ bgp_set_valid_label(&extra->label[0]);
}
#if ENABLE_BGP_VNC
connected = 0;
if (bgp_find_or_add_nexthop(bgp, afi, ri, NULL,
- connected) ||
- CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD))
+ connected)
+ || CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD))
bgp_info_set_flag(rn, ri, BGP_INFO_VALID);
else {
if (BGP_DEBUG(nht, NHT)) {
peer->rcvd_attr_printed = 1;
}
- bgp_debug_rdpfxpath2str(afi, safi, prd, p, label,
+ bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
addpath_id ? 1 : 0, addpath_id, pfx_buf,
sizeof(pfx_buf));
zlog_debug("%s rcvd %s", peer->host, pfx_buf);
/* Update MPLS label */
if (has_valid_label) {
- memcpy(&(bgp_info_extra_get(new))->label, label,
- BGP_LABEL_BYTES);
- bgp_set_valid_label(&(bgp_info_extra_get(new))->label);
+ extra = bgp_info_extra_get(new);
+ memcpy(&extra->label, label, num_labels * sizeof(mpls_label_t));
+ extra->num_labels = num_labels;
+ if (!(afi == AFI_L2VPN && safi == SAFI_EVPN))
+ bgp_set_valid_label(&extra->label[0]);
}
/* Update Overlay Index */
else
connected = 0;
- if (bgp_find_or_add_nexthop(bgp, afi, new, NULL, connected) ||
- CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD))
+ if (bgp_find_or_add_nexthop(bgp, afi, new, NULL, connected)
+ || CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD))
bgp_info_set_flag(rn, new, BGP_INFO_VALID);
else {
if (BGP_DEBUG(nht, NHT)) {
peer->rcvd_attr_printed = 1;
}
- bgp_debug_rdpfxpath2str(afi, safi, prd, p, label,
+ bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
addpath_id ? 1 : 0, addpath_id, pfx_buf,
sizeof(pfx_buf));
zlog_debug("%s rcvd UPDATE about %s -- DENIED due to: %s",
int bgp_withdraw(struct peer *peer, struct prefix *p, u_int32_t addpath_id,
struct attr *attr, afi_t afi, safi_t safi, int type,
int sub_type, struct prefix_rd *prd, mpls_label_t *label,
- struct bgp_route_evpn *evpn)
+ u_int32_t num_labels, struct bgp_route_evpn *evpn)
{
struct bgp *bgp;
char pfx_buf[BGP_PRD_PATH_STRLEN];
if (!bgp_adj_in_unset(rn, peer, addpath_id)) {
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(
- afi, safi, prd, p, label,
+ afi, safi, prd, p, label, num_labels,
addpath_id ? 1 : 0, addpath_id, pfx_buf,
sizeof(pfx_buf));
zlog_debug(
/* Logging. */
if (bgp_debug_update(peer, p, NULL, 1)) {
- bgp_debug_rdpfxpath2str(afi, safi, prd, p, label,
+ bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
addpath_id ? 1 : 0, addpath_id, pfx_buf,
sizeof(pfx_buf));
zlog_debug("%s rcvd UPDATE about %s -- withdrawn", peer->host,
if (ri && !CHECK_FLAG(ri->flags, BGP_INFO_HISTORY))
bgp_rib_withdraw(rn, ri, peer, afi, safi, prd);
else if (bgp_debug_update(peer, p, NULL, 1)) {
- bgp_debug_rdpfxpath2str(afi, safi, prd, p, label,
+ bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
addpath_id ? 1 : 0, addpath_id, pfx_buf,
sizeof(pfx_buf));
zlog_debug("%s Can't find the route %s", peer->host, pfx_buf);
continue;
struct bgp_info *ri = rn->info;
- mpls_label_t label = (ri && ri->extra)
- ? ri->extra->label
- : MPLS_INVALID_LABEL;
+ u_int32_t num_labels = 0;
+ mpls_label_t *label_pnt = NULL;
+
+ if (ri && ri->extra)
+ num_labels = ri->extra->num_labels;
+ if (num_labels)
+ label_pnt = &ri->extra->label[0];
ret = bgp_update(peer, &rn->p, ain->addpath_rx_id,
ain->attr, afi, safi, ZEBRA_ROUTE_BGP,
- BGP_ROUTE_NORMAL, prd, &label, 1,
- NULL);
+ BGP_ROUTE_NORMAL, prd, label_pnt,
+ num_labels, 1, NULL);
if (ret < 0) {
bgp_unlock_node(rn);
* an error SHOULD
* be logged locally, and the prefix SHOULD be
* ignored.
- */
+ */
zlog_err(
"%s: IPv4 unicast NLRI is multicast address %s, ignoring",
peer->host, inet_ntoa(p.u.prefix4));
if (attr)
ret = bgp_update(peer, &p, addpath_id, attr, afi, safi,
ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- NULL, NULL, 0, NULL);
+ NULL, NULL, 0, 0, NULL);
else
ret = bgp_withdraw(peer, &p, addpath_id, attr, afi,
safi, ZEBRA_ROUTE_BGP,
- BGP_ROUTE_NORMAL, NULL, NULL, NULL);
+ BGP_ROUTE_NORMAL, NULL, NULL, 0,
+ NULL);
/* Address family configuration mismatch or maximum-prefix count
overflow. */
#if ENABLE_BGP_VNC
mpls_label_t label = 0;
#endif
+ u_int32_t num_labels = 0;
union gw_addr add;
assert(bgp_static);
+ if (bgp_static->label != MPLS_INVALID_LABEL)
+ num_labels = 1;
rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p,
&bgp_static->prd);
ri->uptime = bgp_clock();
#if ENABLE_BGP_VNC
if (ri->extra)
- label = decode_label(&ri->extra->label);
+ label = decode_label(&ri->extra->label[0]);
#endif
/* Process change. */
attr_new, rn);
SET_FLAG(new->flags, BGP_INFO_VALID);
new->extra = bgp_info_extra_new();
- new->extra->label = bgp_static->label;
+ if (num_labels) {
+ new->extra->label[0] = bgp_static->label;
+ new->extra->num_labels = num_labels;
+ }
#if ENABLE_BGP_VNC
label = decode_label(&bgp_static->label);
#endif
/* 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);
-
- if (!bgp_static->backdoor)
- bgp_static_update(bgp, &p, bgp_static, afi, safi);
- return CMD_SUCCESS;
-}
+ /* Update BGP RIB. */
+ if (!bgp_static->backdoor)
+ bgp_static_withdraw(bgp, &p, afi, safi);
-/* 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;
+ /* Clear configuration. */
+ bgp_static_free(bgp_static);
+ rn->info = NULL;
+ bgp_unlock_node(rn);
+ bgp_unlock_node(rn);
+ } else {
- /* 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;
- }
+ /* Set BGP static route configuration. */
+ rn = bgp_node_get(bgp->route[afi][safi], &p);
- apply_mask(&p);
+ if (rn->info) {
+ /* Configuration change. */
+ bgp_static = rn->info;
- 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;
- }
+ /* 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;
+ }
- bgp_static = rn->info;
+ /* Check previous routes are installed into BGP. */
+ if (bgp_static->valid
+ && bgp_static->backdoor != backdoor)
+ need_update = 1;
+
+ 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;
+
+ 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
void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
const union g_addr *nexthop, ifindex_t ifindex,
enum nexthop_types_t nhtype, uint32_t metric,
- u_char type, u_short instance,
- route_tag_t tag)
+ u_char type, u_short instance, route_tag_t tag)
{
struct bgp_info *new;
struct bgp_info *bi;
/* Make default attribute. */
bgp_attr_default_set(&attr, BGP_ORIGIN_INCOMPLETE);
- switch(nhtype) {
+ switch (nhtype) {
case NEXTHOP_TYPE_IFINDEX:
break;
case NEXTHOP_TYPE_IPV4:
switch (af) {
case AF_INET:
sprintf(nexthop, "%s",
- inet_ntop(af, &attr->mp_nexthop_global_in,
- buf, BUFSIZ));
+ inet_ntop(af, &attr->mp_nexthop_global_in, buf,
+ BUFSIZ));
break;
case AF_INET6:
sprintf(nexthop, "%s",
- inet_ntop(af, &attr->mp_nexthop_global,
- buf, BUFSIZ));
+ inet_ntop(af, &attr->mp_nexthop_global, buf,
+ BUFSIZ));
break;
default:
sprintf(nexthop, "?");
if (json_paths) {
json_nexthop_global = json_object_new_object();
+ json_object_string_add(json_nexthop_global, "afi",
+ (af == AF_INET) ? "ip" : "ipv6");
json_object_string_add(json_nexthop_global,
- "afi",
- (af == AF_INET) ?
- "ip" : "ipv6");
- json_object_string_add(json_nexthop_global,
- (af == AF_INET) ?
- "ip" : "ipv6",
+ (af == AF_INET) ? "ip" : "ipv6",
nexthop);
json_object_boolean_true_add(json_nexthop_global,
"used");
json_object_string_add(json_nexthop_global, "ip",
inet_ntoa(attr->nexthop));
- json_object_string_add(json_nexthop_global,
- "afi", "ipv4");
+ json_object_string_add(json_nexthop_global, "afi",
+ "ipv4");
json_object_boolean_true_add(json_nexthop_global,
"used");
} else
vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
}
/* IPv4 Next Hop */
- else if (p->family == AF_INET
- && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
+ else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
if (json_paths) {
json_nexthop_global = json_object_new_object();
- if ((safi == SAFI_MPLS_VPN)
- || (safi == SAFI_EVPN))
- json_object_string_add(json_nexthop_global,
- "ip",
- inet_ntoa(attr->mp_nexthop_global_in));
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
+ json_object_string_add(
+ json_nexthop_global, "ip",
+ inet_ntoa(attr->mp_nexthop_global_in));
else
- json_object_string_add(json_nexthop_global,
- "ip",
- inet_ntoa(attr->nexthop));
+ json_object_string_add(
+ json_nexthop_global, "ip",
+ inet_ntoa(attr->nexthop));
- json_object_string_add(json_nexthop_global,
- "afi", "ipv4");
+ json_object_string_add(json_nexthop_global, "afi",
+ "ipv4");
json_object_boolean_true_add(json_nexthop_global,
"used");
} else {
- if ((safi == SAFI_MPLS_VPN)
- || (safi == SAFI_EVPN))
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
vty_out(vty, "%-16s",
- inet_ntoa(
- attr->mp_nexthop_global_in));
+ inet_ntoa(attr->mp_nexthop_global_in));
else
- vty_out(vty, "%-16s",
- inet_ntoa(attr->nexthop));
+ vty_out(vty, "%-16s", inet_ntoa(attr->nexthop));
}
}
/* IPv6 Next Hop */
- else if (p->family == AF_INET6
- || BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
+ else if (p->family == AF_INET6 || BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
int len;
char buf[BUFSIZ];
if (json_paths) {
json_nexthop_global = json_object_new_object();
- json_object_string_add(json_nexthop_global, "ip",
- inet_ntop(AF_INET6,
- &attr->mp_nexthop_global, buf,
- BUFSIZ));
- json_object_string_add(json_nexthop_global,
- "afi", "ipv6");
- json_object_string_add(json_nexthop_global,
- "scope", "global");
+ json_object_string_add(
+ json_nexthop_global, "ip",
+ inet_ntop(AF_INET6, &attr->mp_nexthop_global,
+ buf, BUFSIZ));
+ json_object_string_add(json_nexthop_global, "afi",
+ "ipv6");
+ json_object_string_add(json_nexthop_global, "scope",
+ "global");
/* We display both LL & GL if both have been
* received */
if ((attr->mp_nexthop_len == 32)
|| (binfo->peer->conf_if)) {
- json_nexthop_ll =
- json_object_new_object();
+ json_nexthop_ll = json_object_new_object();
json_object_string_add(
json_nexthop_ll, "ip",
- inet_ntop(
- AF_INET6,
- &attr->mp_nexthop_local,
- buf, BUFSIZ));
- json_object_string_add(json_nexthop_ll,
- "afi", "ipv6");
- json_object_string_add(json_nexthop_ll,
- "scope",
+ inet_ntop(AF_INET6,
+ &attr->mp_nexthop_local, buf,
+ BUFSIZ));
+ json_object_string_add(json_nexthop_ll, "afi",
+ "ipv6");
+ json_object_string_add(json_nexthop_ll, "scope",
"link-local");
- if ((IPV6_ADDR_CMP(
- &attr->mp_nexthop_global,
- &attr->mp_nexthop_local)
+ if ((IPV6_ADDR_CMP(&attr->mp_nexthop_global,
+ &attr->mp_nexthop_local)
!= 0)
&& !attr->mp_nexthop_prefer_global)
json_object_boolean_true_add(
- json_nexthop_ll,
- "used");
+ json_nexthop_ll, "used");
else
json_object_boolean_true_add(
- json_nexthop_global,
- "used");
+ json_nexthop_global, "used");
} else
json_object_boolean_true_add(
json_nexthop_global, "used");
&& !attr->mp_nexthop_prefer_global)
|| (binfo->peer->conf_if)) {
if (binfo->peer->conf_if) {
- len = vty_out(
- vty, "%s",
- binfo->peer->conf_if);
+ len = vty_out(vty, "%s",
+ binfo->peer->conf_if);
len = 16 - len; /* len of IPv6
addr + max
len of def
ifname */
if (len < 1)
- vty_out(vty, "\n%*s",
- 36, " ");
+ vty_out(vty, "\n%*s", 36, " ");
else
- vty_out(vty, "%*s", len,
- " ");
+ vty_out(vty, "%*s", len, " ");
} else {
len = vty_out(
vty, "%s",
len = 16 - len;
if (len < 1)
- vty_out(vty, "\n%*s",
- 36, " ");
+ vty_out(vty, "\n%*s", 36, " ");
else
- vty_out(vty, "%*s", len,
- " ");
+ vty_out(vty, "%*s", len, " ");
}
} else {
- len = vty_out(vty, "%s",
- inet_ntop(AF_INET6,
- &attr->mp_nexthop_global,
- buf, BUFSIZ));
+ len = vty_out(
+ vty, "%s",
+ inet_ntop(AF_INET6,
+ &attr->mp_nexthop_global, buf,
+ BUFSIZ));
len = 16 - len;
if (len < 1)
/* MED/Metric */
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
if (json_paths)
- json_object_int_add(json_path, "med",
- attr->med);
+ json_object_int_add(json_path, "med", attr->med);
else
vty_out(vty, "%10u", attr->med);
else if (!json_paths)
if (json_paths) {
char buf[BUFSIZ];
- json_object_string_add(json_path, "peerId",
- sockunion2str(&binfo->peer->su,
- buf,
- SU_ADDRSTRLEN));
+ json_object_string_add(
+ json_path, "peerId",
+ sockunion2str(&binfo->peer->su, buf, SU_ADDRSTRLEN));
}
/* Print aspath */
/* Print origin */
if (json_paths)
- json_object_string_add(
- json_path, "origin",
- bgp_origin_long_str[attr->origin]);
+ json_object_string_add(json_path, "origin",
+ bgp_origin_long_str[attr->origin]);
else
vty_out(vty, "%s", bgp_origin_str[attr->origin]);
if (attr) {
if (((p->family == AF_INET)
&& ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP)))
- || (safi == SAFI_EVPN
- && !BGP_ATTR_NEXTHOP_AFI_IP6(attr))
+ || (safi == SAFI_EVPN && !BGP_ATTR_NEXTHOP_AFI_IP6(attr))
|| (!BGP_ATTR_NEXTHOP_AFI_IP6(attr))) {
if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
|| safi == SAFI_EVPN) {
}
}
- label = decode_label(&binfo->extra->label);
+ label = decode_label(&binfo->extra->label[0]);
if (bgp_is_valid_label(&label)) {
if (json) {
bgp_damp_reuse_time_vty(vty, binfo, timebuf, BGP_UPTIME_LEN,
use_json, json);
else
- vty_out(vty, "%s ", bgp_damp_reuse_time_vty(vty, binfo, timebuf,
- BGP_UPTIME_LEN,
- use_json, json));
+ vty_out(vty, "%s ",
+ bgp_damp_reuse_time_vty(vty, binfo, timebuf,
+ BGP_UPTIME_LEN, use_json,
+ json));
/* Print attribute */
attr = binfo->attr;
peer_uptime(bdi->start_time, timebuf, BGP_UPTIME_LEN, use_json,
json);
else
- vty_out(vty, "%s ", peer_uptime(bdi->start_time, timebuf,
- BGP_UPTIME_LEN, 0, NULL));
+ vty_out(vty, "%s ",
+ peer_uptime(bdi->start_time, timebuf, BGP_UPTIME_LEN, 0,
+ NULL));
if (CHECK_FLAG(binfo->flags, BGP_INFO_DAMPED)
&& !CHECK_FLAG(binfo->flags, BGP_INFO_HISTORY)) {
#if defined(HAVE_CUMULUS)
if (!json_paths && safi == SAFI_EVPN) {
- char tag_buf[20];
+ char tag_buf[30];
bgp_evpn_route2str((struct prefix_evpn *)p, buf2, sizeof(buf2));
vty_out(vty, " Route %s", buf2);
tag_buf[0] = '\0';
- if (binfo->extra) {
- bgp_evpn_label2str(&binfo->extra->label, tag_buf,
+ if (binfo->extra && binfo->extra->num_labels) {
+ bgp_evpn_label2str(binfo->extra->label,
+ binfo->extra->num_labels, tag_buf,
sizeof(tag_buf));
vty_out(vty, " VNI %s", tag_buf);
}
/* Line2 display Next-hop, Neighbor, Router-id */
/* Display the nexthop */
- if ((p->family == AF_INET || p->family == AF_ETHERNET ||
- p->family == AF_EVPN)
+ if ((p->family == AF_INET || p->family == AF_ETHERNET
+ || p->family == AF_EVPN)
&& (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
|| safi == SAFI_EVPN
|| !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) {
if (attr->community) {
if (json_paths) {
if (!attr->community->json)
- community_str(attr->community,
- true);
+ community_str(attr->community, true);
json_object_lock(attr->community->json);
json_object_object_add(json_path, "community",
attr->community->json);
/* Remote Label */
#if defined(HAVE_CUMULUS)
- if (binfo->extra && bgp_is_valid_label(&binfo->extra->label)
+ if (binfo->extra && bgp_is_valid_label(&binfo->extra->label[0])
&& safi != SAFI_EVPN)
#else
- if (binfo->extra && bgp_is_valid_label(&binfo->extra->label))
+ if (binfo->extra && bgp_is_valid_label(&binfo->extra->label[0]))
#endif
{
- mpls_label_t label = label_pton(&binfo->extra->label);
+ mpls_label_t label =
+ label_pton(&binfo->extra->label[0]);
if (json_paths)
json_object_int_add(json_path, "remoteLabel",
label);
static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
const char *prefix, afi_t afi, safi_t safi,
enum bgp_show_type type);
-static int bgp_show_regexp(struct vty *vty, struct bgp *bgp,
- const char *regstr, afi_t afi,
- safi_t safi, enum bgp_show_type type);
+static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
+ afi_t afi, safi_t safi, enum bgp_show_type type);
static int bgp_show_community(struct vty *vty, struct bgp *bgp,
const char *comstr, int exact, afi_t afi,
safi_t safi);
static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
struct bgp_table *table, enum bgp_show_type type,
- void *output_arg, u_char use_json,
- char *rd, int is_last,
- unsigned long *output_cum, unsigned long *total_cum)
+ void *output_arg, u_char use_json, char *rd,
+ int is_last, unsigned long *output_cum,
+ unsigned long *total_cum,
+ unsigned long *json_header_depth)
{
struct bgp_info *ri;
struct bgp_node *rn;
if (output_cum && *output_cum != 0)
header = 0;
- if (use_json && header) {
+ if (use_json && !*json_header_depth) {
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));
- if (rd)
+ *json_header_depth = 2;
+ if (rd) {
vty_out(vty, " \"routeDistinguishers\" : {");
+ ++*json_header_depth;
+ }
json_paths = json_object_new_object();
}
|| type == bgp_show_type_flap_neighbor
|| type == bgp_show_type_dampend_paths
|| type == bgp_show_type_damp_neighbor) {
- if (!(ri->extra
- && ri->extra->damp_info))
+ if (!(ri->extra && ri->extra->damp_info))
continue;
}
if (type == bgp_show_type_regexp) {
binfo.peer = ri->peer;
binfo.attr = &dummy_attr;
- ret = route_map_apply(rmap, &rn->p,
- RMAP_BGP, &binfo);
+ ret = route_map_apply(rmap, &rn->p, RMAP_BGP,
+ &binfo);
if (ret == RMAP_DENYMATCH)
continue;
}
if (ri->peer == NULL
|| ri->peer->su_remote == NULL
- || !sockunion_same(ri->peer->su_remote,
- su))
+ || !sockunion_same(ri->peer->su_remote, su))
continue;
}
if (type == bgp_show_type_cidr_only) {
struct community *com = output_arg;
if (!ri->attr->community
- || !community_cmp(ri->attr->community,
- com))
+ || !community_cmp(ri->attr->community, com))
continue;
}
if (type == bgp_show_type_community_list) {
struct community_list *list = output_arg;
- if (!community_list_match(
- ri->attr->community, list))
+ if (!community_list_match(ri->attr->community,
+ list))
continue;
}
- if (type
- == bgp_show_type_community_list_exact) {
+ if (type == bgp_show_type_community_list_exact) {
struct community_list *list = output_arg;
if (!community_list_exact_match(
if (type == bgp_show_type_lcommunity_list) {
struct community_list *list = output_arg;
- if (!lcommunity_list_match(
- ri->attr->lcommunity, list))
+ if (!lcommunity_list_match(ri->attr->lcommunity,
+ list))
continue;
}
if (type == bgp_show_type_lcommunity_all) {
if (type == bgp_show_type_dampend_paths
|| type == bgp_show_type_damp_neighbor)
vty_out(vty, BGP_SHOW_DAMP_HEADER);
- else if (
- type == bgp_show_type_flap_statistics
- || type == bgp_show_type_flap_neighbor)
+ else if (type == bgp_show_type_flap_statistics
+ || type == bgp_show_type_flap_neighbor)
vty_out(vty, BGP_SHOW_FLAP_HEADER);
else
vty_out(vty, BGP_SHOW_HEADER);
if (type == bgp_show_type_dampend_paths
|| type == bgp_show_type_damp_neighbor)
damp_route_vty_out(vty, &rn->p, ri, display,
- safi, use_json,
- json_paths);
+ safi, use_json, json_paths);
else if (type == bgp_show_type_flap_statistics
|| type == bgp_show_type_flap_neighbor)
flap_route_vty_out(vty, &rn->p, ri, display,
- safi, use_json,
- json_paths);
+ safi, use_json, json_paths);
else
- route_vty_out(vty, &rn->p, ri, display,
- safi, json_paths);
+ route_vty_out(vty, &rn->p, ri, display, safi,
+ json_paths);
display++;
}
p = &rn->p;
sprintf(buf2, "%s/%d",
- inet_ntop(p->family, &p->u.prefix,
- buf, BUFSIZ),
+ inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ),
p->prefixlen);
if (first)
vty_out(vty, "\"%s\": ", buf2);
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 (is_last)
- vty_out(vty, " } }\n");
- else
- vty_out(vty, " }, ");
+ if (json_paths)
+ json_object_free(json_paths);
+ if (rd) {
+ vty_out(vty, " }%s ", (is_last ? "" : ","));
+ }
+ if (is_last) {
+ unsigned long i;
+ for (i = 0; i < *json_header_depth; ++i)
+ vty_out(vty, " } ");
+ }
} else {
if (is_last) {
/* No route is displayed */
struct bgp_node *rn, *next;
unsigned long output_cum = 0;
unsigned long total_cum = 0;
+ unsigned long json_header_depth = 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);
memcpy(&prd, &(rn->p), sizeof(struct prefix_rd));
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);
+ output_arg, use_json, rd, next == NULL,
+ &output_cum, &total_cum,
+ &json_header_depth);
+ if (next == NULL)
+ show_msg = false;
}
}
- if (use_json)
- vty_out(vty, " } }");
+ 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);
+ }
return CMD_SUCCESS;
}
static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
enum bgp_show_type type, void *output_arg, u_char use_json)
{
struct bgp_table *table;
+ unsigned long json_header_depth = 0;
if (bgp == NULL) {
bgp = bgp_get_default();
safi = SAFI_UNICAST;
return bgp_show_table(vty, bgp, safi, table, type, output_arg, use_json,
- NULL, 1, NULL, NULL);
+ NULL, 1, NULL, NULL, &json_header_depth);
}
static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi,
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, sizeof(buf1))
- : ""),
+ ? prefix_rd2str(prd, buf1, sizeof(buf1))
+ : ""),
((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) ? ":"
: "",
buf2, p->prefixlen);
if (display)
json_object_object_add(json, "paths", json_paths);
- 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_ext(
+ json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
} else {
if (!display) {
if (argv_find(argv, argc, "community", &idx)) {
/* show a specific community */
if (argv_find(argv, argc, "local-AS", &idx_community_type)
- || argv_find(argv, argc, "no-advertise", &idx_community_type)
+ || argv_find(argv, argc, "no-advertise",
+ &idx_community_type)
|| argv_find(argv, argc, "no-export", &idx_community_type)
- || argv_find(argv, argc, "graceful-shutdown", &idx_community_type)
+ || argv_find(argv, argc, "graceful-shutdown",
+ &idx_community_type)
|| argv_find(argv, argc, "AA:NN", &idx_community_type)) {
if (argv_find(argv, argc, "exact-match", &idx))
exact_match = 1;
- return bgp_show_community(vty, bgp, argv[idx_community_type]->arg,
+ return bgp_show_community(vty, bgp,
+ argv[idx_community_type]->arg,
exact_match, afi, safi);
}
}
return CMD_SUCCESS;
}
-static int bgp_show_regexp(struct vty *vty, struct bgp *bgp,
- const char *regstr, afi_t afi,
- safi_t safi, enum bgp_show_type type)
+static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
+ afi_t afi, safi_t safi, enum bgp_show_type type)
{
regex_t *regex;
int rc;
ip_str);
vty_out(vty, "%s\n",
json_object_to_json_string_ext(
- json_no, JSON_C_TO_STRING_PRETTY));
+ json_no,
+ JSON_C_TO_STRING_PRETTY));
json_object_free(json_no);
} else
vty_out(vty,
json_object_string_add(json_no, "warning",
"No such neighbor");
vty_out(vty, "%s\n",
- json_object_to_json_string_ext(json_no,
- JSON_C_TO_STRING_PRETTY));
+ json_object_to_json_string_ext(
+ json_no, JSON_C_TO_STRING_PRETTY));
json_object_free(json_no);
} else
vty_out(vty, "No such neighbor\n");
ts->counts[BGP_STATS_UNAGGREGATEABLE]++;
/* announced address space */
if (space)
- ts->total_space += pow(2.0,
- space - rn->p.prefixlen);
+ ts->total_space +=
+ pow(2.0, space - rn->p.prefixlen);
} else if (prn->info)
ts->counts[BGP_STATS_MAX_AGGREGATEABLE]++;
if (afi == AFI_IP6) {
vty_out(vty, "%30s: ", "/32 equivalent ");
vty_out(vty, "%12g\n",
- ts.total_space * pow(2.0, -128+32));
+ ts.total_space * pow(2.0, -128 + 32));
vty_out(vty, "%30s: ", "/48 equivalent ");
vty_out(vty, "%12g\n",
- ts.total_space * pow(2.0, -128+48));
+ ts.total_space * pow(2.0, -128 + 48));
} else {
vty_out(vty, "%30s: ", "% announced ");
vty_out(vty, "%12.2f\n",
ts.total_space * 100. * pow(2.0, -32));
vty_out(vty, "%30s: ", "/8 equivalent ");
vty_out(vty, "%12.2f\n",
- ts.total_space * pow(2.0, -32+8));
+ ts.total_space * pow(2.0, -32 + 8));
vty_out(vty, "%30s: ", "/24 equivalent ");
vty_out(vty, "%12.2f\n",
- ts.total_space * pow(2.0, -32+24));
+ ts.total_space * pow(2.0, -32 + 24));
}
break;
default:
pcounts.table = peer->bgp->rib[afi][safi];
/* in-place call via thread subsystem so as to record execution time
- * * stats for the thread-walk (i.e. ensure this can't be blamed on
- * * on just vty_read()).
- * */
+ * stats for the thread-walk (i.e. ensure this can't be blamed on
+ * on just vty_read()).
+ */
thread_execute(bm->master, bgp_peer_count_walker, &pcounts, 0);
if (use_json) {
json, "recommended",
"Please report this bug, with the above command output");
}
- 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_ext(
+ json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
} else {
"bgpOriginatingDefaultNetwork",
"0.0.0.0");
} else {
- vty_out(vty, "BGP table version is %" PRIu64
- ", local router ID is %s\n",
+ vty_out(vty,
+ "BGP table version is %" PRIu64
+ ", local router ID is %s\n",
table->version, inet_ntoa(bgp->router_id));
vty_out(vty, BGP_SHOW_SCODE_HEADER);
vty_out(vty, BGP_SHOW_OCODE_HEADER);
}
if (adj->attr) {
- bgp_attr_dup(&attr,
- adj->attr);
+ bgp_attr_dup(&attr, adj->attr);
ret = bgp_output_modifier(
- peer, &rn->p,
- &attr, afi,
- safi,
- rmap_name);
+ peer, &rn->p, &attr,
+ afi, safi, rmap_name);
if (ret != RMAP_DENY) {
route_vty_out_tmp(
- vty,
- &rn->p,
- &attr,
- safi,
+ vty, &rn->p,
+ &attr, safi,
use_json,
json_ar);
output_count++;
output_count);
}
if (use_json) {
- 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_ext(
+ json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
}
}
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");
}
}
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)
- ? AF_INET
- : AF_INET6;
- inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, local_buf,
- PREFIX_STRLEN);
- sprintf(buf, "%s/%u", local_buf,p->u.prefix_evpn.ip_prefix_length);
+ uint8_t family = IS_EVPN_PREFIX_IPADDR_V4((
+ struct prefix_evpn *)p)
+ ? AF_INET
+ : AF_INET6;
+ inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr,
+ local_buf, PREFIX_STRLEN);
+ sprintf(buf, "%s/%u", local_buf,
+ p->u.prefix_evpn.ip_prefix_length);
} else {
prefix2str(p, buf, sizeof(buf));
}
- if (bgp_static->gatewayIp.family == AF_INET ||
- bgp_static->gatewayIp.family == AF_INET6)
+ if (bgp_static->gatewayIp.family == AF_INET
+ || bgp_static->gatewayIp.family == AF_INET6)
inet_ntop(bgp_static->gatewayIp.family,
&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);
#define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n"
#define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n"
+/* Maximum number of labels we can process or send with a prefix. We
+ * really do only 1 for MPLS (BGP-LU) but we can do 2 for EVPN-VxLAN.
+ */
+#define BGP_MAX_LABELS 2
+
/* Ancillary information to struct bgp_info,
* used for uncommonly used data (aggregation, MPLS, etc.)
* and lazily allocated to save memory.
/* Nexthop reachability check. */
u_int32_t igpmetric;
- /* MPLS label. */
- mpls_label_t label;
+ /* MPLS label(s) - VNI(s) for EVPN-VxLAN */
+ mpls_label_t label[BGP_MAX_LABELS];
+ u_int32_t num_labels;
#if ENABLE_BGP_VNC
union {
/* this is primarily for MPLS-VPN */
extern int bgp_update(struct peer *, struct prefix *, u_int32_t, struct attr *,
afi_t, safi_t, int, int, struct prefix_rd *,
- mpls_label_t *, int, struct bgp_route_evpn *);
+ mpls_label_t *, u_int32_t, int, struct bgp_route_evpn *);
extern int bgp_withdraw(struct peer *, struct prefix *, u_int32_t,
struct attr *, afi_t, safi_t, int, int,
- struct prefix_rd *, mpls_label_t *,
+ struct prefix_rd *, mpls_label_t *, u_int32_t,
struct bgp_route_evpn *);
/* for bgp_nexthop and bgp_damp */
extern void bgp_process_queues_drain_immediate(void);
/* for encap/vpn */
-extern struct bgp_node *bgp_afi_node_get(struct bgp_table *, afi_t, safi_t,
- struct prefix *, struct prefix_rd *);
extern struct bgp_node *bgp_afi_node_lookup(struct bgp_table *table, afi_t afi,
safi_t safi, struct prefix *p,
struct prefix_rd *prd);
p.prefixlen = ETH_ALEN * 8;
p.u.prefix_eth = prefix->u.prefix_evpn.mac;
- return (access_list_apply(alist, &p)
- == FILTER_DENY
+ return (access_list_apply(alist, &p) == FILTER_DENY
? RMAP_NOMATCH
: RMAP_MATCH);
}
vni = *((vni_t *)rule);
bgp_info = (struct bgp_info *)object;
- if (vni == label2vni(&bgp_info->extra->label))
+ if (vni == label2vni(&bgp_info->extra->label[0]))
return RMAP_MATCH;
}
"evpn vni", route_match_vni, route_match_vni_compile,
route_match_vni_free};
+/* `match evpn route-type' */
+
+/* Match function should return 1 if match is success else return
+ zero. */
+static route_map_result_t route_match_evpn_route_type(void *rule,
+ struct prefix *prefix,
+ route_map_object_t type,
+ void *object)
+{
+ u_char route_type = 0;
+
+ if (type == RMAP_BGP) {
+ route_type = *((u_char *)rule);
+
+ if (route_type == prefix->u.prefix_evpn.route_type)
+ return RMAP_MATCH;
+ }
+
+ return RMAP_NOMATCH;
+}
+
+/* Route map `route-type' match statement. */
+static void *route_match_evpn_route_type_compile(const char *arg)
+{
+ u_char *route_type = NULL;
+
+ route_type = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(u_char));
+
+ if (strncmp(arg, "ma", 2) == 0)
+ *route_type = BGP_EVPN_MAC_IP_ROUTE;
+ else if (strncmp(arg, "mu", 2) == 0)
+ *route_type = BGP_EVPN_IMET_ROUTE;
+ else
+ *route_type = BGP_EVPN_IP_PREFIX_ROUTE;
+
+ return route_type;
+}
+
+/* Free route map's compiled `route-type' value. */
+static void route_match_evpn_route_type_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for evpn route-type matching. */
+struct route_map_rule_cmd route_match_evpn_route_type_cmd = {
+ "evpn route-type", route_match_evpn_route_type,
+ route_match_evpn_route_type_compile,
+ route_match_evpn_route_type_free};
+
/* `match local-preference LOCAL-PREF' */
/* Match function return 1 if match is success else return zero. */
}
}
}
+
+ /* for type5 command route-maps */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (bgp->adv_cmd_rmap[afi][safi].name &&
+ strcmp(rmap_name, bgp->adv_cmd_rmap[afi][safi].name) == 0) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "Processing route_map %s update on advertise type5 route command",
+ rmap_name);
+ bgp_evpn_withdraw_type5_routes(bgp, afi, safi);
+ bgp_evpn_advertise_type5_routes(bgp, afi, safi);
+ }
+ }
}
static int bgp_route_map_process_update_cb(char *rmap_name)
RMAP_EVENT_FILTER_DELETED);
}
+DEFUN (match_evpn_route_type,
+ match_evpn_route_type_cmd,
+ "match evpn route-type <macip | multicast | prefix>",
+ MATCH_STR
+ EVPN_HELP_STR
+ "Match route-type\n"
+ "mac-ip route\n"
+ "IMET route\n"
+ "prefix route\n")
+{
+ return bgp_route_match_add(vty, "evpn route-type", argv[3]->arg,
+ RMAP_EVENT_MATCH_ADDED);
+}
+
+DEFUN (no_match_evpn_route_type,
+ no_match_evpn_route_type_cmd,
+ "no match evpn route-type <macip | multicast | prefix>",
+ NO_STR
+ MATCH_STR
+ EVPN_HELP_STR
+ "Match route-type\n"
+ "mac-ip route\n"
+ "IMET route\n"
+ "prefix route\n")
+{
+ return bgp_route_match_delete(vty, "evpn route-type", argv[4]->arg,
+ RMAP_EVENT_MATCH_DELETED);
+}
+
+
DEFUN (match_evpn_vni,
match_evpn_vni_cmd,
"match evpn vni (1-16777215)",
"Use peer address (for BGP only)\n")
{
int (*func)(struct vty *, struct route_map_index *, const char *,
- const char *) = strmatch(argv[0]->text, "no")
- ? generic_set_delete
- : generic_set_add;
+ const char *) = strmatch(argv[0]->text, "no")
+ ? generic_set_delete
+ : generic_set_add;
return func(vty, VTY_GET_CONTEXT(route_map_index), "ip next-hop",
"peer-address");
"Don't modify existing Next hop address\n")
{
int (*func)(struct vty *, struct route_map_index *, const char *,
- const char *) = strmatch(argv[0]->text, "no")
- ? generic_set_delete
- : generic_set_add;
+ const char *) = strmatch(argv[0]->text, "no")
+ ? generic_set_delete
+ : generic_set_add;
return func(vty, VTY_GET_CONTEXT(route_map_index), "ip next-hop",
"unchanged");
buffer_putstr(b, "no-export");
continue;
}
- if (strncmp(argv[i]->arg, "graceful-shutdown", strlen(argv[i]->arg))
+ if (strncmp(argv[i]->arg, "graceful-shutdown",
+ strlen(argv[i]->arg))
== 0) {
buffer_putstr(b, "graceful-shutdown");
continue;
route_map_install_match(&route_match_tag_cmd);
route_map_install_match(&route_match_mac_address_cmd);
route_map_install_match(&route_match_evpn_vni_cmd);
+ route_map_install_match(&route_match_evpn_route_type_cmd);
route_map_install_set(&route_set_ip_nexthop_cmd);
route_map_install_set(&route_set_local_pref_cmd);
install_element(RMAP_NODE, &no_match_mac_address_cmd);
install_element(RMAP_NODE, &match_evpn_vni_cmd);
install_element(RMAP_NODE, &no_match_evpn_vni_cmd);
+ install_element(RMAP_NODE, &match_evpn_route_type_cmd);
+ install_element(RMAP_NODE, &no_match_evpn_route_type_cmd);
install_element(RMAP_NODE, &match_aspath_cmd);
install_element(RMAP_NODE, &no_match_aspath_cmd);
{
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];
int addpath_overhead = 0;
u_int32_t addpath_tx_id = 0;
struct prefix_rd *prd = NULL;
- mpls_label_t label = MPLS_INVALID_LABEL;
+ mpls_label_t label = MPLS_INVALID_LABEL, *label_pnt = NULL;
+ u_int32_t num_labels = 0;
if (!subgrp)
return NULL;
* attr. */
total_attr_len = bgp_packet_attribute(
NULL, peer, s, adv->baa->attr, &vecarr, NULL,
- afi, safi, from, NULL, NULL, 0, 0);
+ afi, safi, from, NULL, NULL, 0, 0, 0);
space_remaining =
STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s))
if (rn->prn)
prd = (struct prefix_rd *)&rn->prn->p;
- if (safi == SAFI_LABELED_UNICAST)
+ if (safi == SAFI_LABELED_UNICAST) {
label = bgp_adv_label(rn, binfo, peer, afi,
safi);
- else if (binfo && binfo->extra)
- label = binfo->extra->label;
+ label_pnt = &label;
+ num_labels = 1;
+ } else if (binfo && binfo->extra) {
+ label_pnt = &binfo->extra->label[0];
+ num_labels = binfo->extra->num_labels;
+ }
if (stream_empty(snlri))
mpattrlen_pos = bgp_packet_mpattr_start(
adv->baa->attr);
bgp_packet_mpattr_prefix(snlri, afi, safi, &rn->p, prd,
- &label, addpath_encode,
- addpath_tx_id, adv->baa->attr);
+ label_pnt, num_labels,
+ addpath_encode, addpath_tx_id,
+ adv->baa->attr);
}
num_pfx++;
send_attr_printed = 1;
}
- bgp_debug_rdpfxpath2str(afi, safi, prd, &rn->p, &label,
+ bgp_debug_rdpfxpath2str(afi, safi, prd, &rn->p,
+ label_pnt, num_labels,
addpath_encode, addpath_tx_id,
pfx_buf, sizeof(pfx_buf));
zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s",
}
bgp_packet_mpunreach_prefix(s, &rn->p, afi, safi, prd,
- NULL, addpath_encode,
+ NULL, 0, addpath_encode,
addpath_tx_id, NULL);
}
if (bgp_debug_update(NULL, &rn->p, subgrp->update_group, 0)) {
char pfx_buf[BGP_PRD_PATH_STRLEN];
- bgp_debug_rdpfxpath2str(afi, safi, prd, &rn->p, NULL,
+ bgp_debug_rdpfxpath2str(afi, safi, prd, &rn->p, NULL, 0,
addpath_encode, addpath_tx_id,
pfx_buf, sizeof(pfx_buf));
zlog_debug("u%" PRIu64 ":s%" PRIu64
stream_putw(s, 0);
total_attr_len = bgp_packet_attribute(
NULL, peer, s, attr, &vecarr, &p, afi, safi, from, NULL, NULL,
- addpath_encode, BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+ 0, addpath_encode, BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
/* Set Total Path Attribute Length. */
stream_putw_at(s, pos, total_attr_len);
mp_start = stream_get_endp(s);
mplen_pos = bgp_packet_mpunreach_start(s, afi, safi);
bgp_packet_mpunreach_prefix(
- s, &p, afi, safi, NULL, NULL, addpath_encode,
+ s, &p, afi, safi, NULL, NULL, 0, addpath_encode,
BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE, NULL);
/* Set the mp_unreach attr's length */
#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);
}
ALIAS_HIDDEN(no_bgp_maxpaths, no_bgp_maxpaths_hidden_cmd,
- "no maximum-paths [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]", NO_STR
+ "no maximum-paths [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]",
+ NO_STR
"Forward packets over multiple paths\n"
"Number of paths\n")
struct listnode *node;
struct bgp_redist *red;
- for (afi = AFI_IP; afi < AFI_MAX; afi++) {
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
- red_list = bgp->redist[afi][i];
- if (!red_list)
- continue;
+ red_list = bgp->redist[afi][i];
+ if (!red_list)
+ continue;
- for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) {
+ for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) {
bgp_redistribute_resend(bgp, afi, i,
red->instance);
}
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"
+ "Apply administrative shutdown to newly configured peers\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",
return CMD_WARNING_CONFIG_FAILED;
}
- ret = peer_group_unbind(bgp, peer, group);
+ ret = peer_delete(peer);
return bgp_vty_return(vty, ret);
}
SET_FLAG(flags, PEER_FLAG_NEXTHOP_UNCHANGED);
SET_FLAG(flags, PEER_FLAG_MED_UNCHANGED);
} else {
- if (!CHECK_FLAG(flags, PEER_FLAG_AS_PATH_UNCHANGED) &&
- peer_af_flag_check(peer, afi, safi,
- PEER_FLAG_AS_PATH_UNCHANGED)) {
+ if (!CHECK_FLAG(flags, PEER_FLAG_AS_PATH_UNCHANGED)
+ && peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED)) {
peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_AS_PATH_UNCHANGED);
}
- if (!CHECK_FLAG(flags, PEER_FLAG_NEXTHOP_UNCHANGED) &&
- peer_af_flag_check(peer, afi, safi,
- PEER_FLAG_NEXTHOP_UNCHANGED)) {
+ if (!CHECK_FLAG(flags, PEER_FLAG_NEXTHOP_UNCHANGED)
+ && peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)) {
peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_NEXTHOP_UNCHANGED);
}
- if (!CHECK_FLAG(flags, PEER_FLAG_MED_UNCHANGED) &&
- peer_af_flag_check(peer, afi, safi,
- PEER_FLAG_MED_UNCHANGED)) {
+ if (!CHECK_FLAG(flags, PEER_FLAG_MED_UNCHANGED)
+ && peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MED_UNCHANGED)) {
peer_af_flag_unset_vty(vty, peer_str, afi, safi,
PEER_FLAG_MED_UNCHANGED);
}
if (argc == 3) {
VTY_DECLVAR_CONTEXT(bgp, bgp);
safi_t safi = bgp_vty_safi_from_str(argv[2]->text);
- if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT &&
- safi != SAFI_UNICAST && safi != SAFI_MULTICAST) {
- vty_out(vty, "Only Unicast and Multicast SAFIs supported in non-core instances.\n");
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT
+ && safi != SAFI_UNICAST && safi != SAFI_MULTICAST
+ && safi != SAFI_EVPN) {
+ vty_out(vty,
+ "Only Unicast/Multicast/EVPN SAFIs supported in non-core instances.\n");
return CMD_WARNING_CONFIG_FAILED;
}
vty->node = bgp_node_type(AFI_IP, safi);
if (argc == 3) {
VTY_DECLVAR_CONTEXT(bgp, bgp);
safi_t safi = bgp_vty_safi_from_str(argv[2]->text);
- if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT &&
- safi != SAFI_UNICAST && safi != SAFI_MULTICAST) {
- vty_out(vty, "Only Unicast and Multicast SAFIs supported in non-core instances.\n");
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT
+ && safi != SAFI_UNICAST && safi != SAFI_MULTICAST
+ && safi != SAFI_EVPN) {
+ vty_out(vty,
+ "Only Unicast/Multicast/EVPN SAFIs supported in non-core instances.\n");
return CMD_WARNING_CONFIG_FAILED;
}
vty->node = bgp_node_type(AFI_IP6, safi);
"Address Family modifier\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
- if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
- vty_out(vty, "Only Unicast and Multicast SAFIs supported in non-core instances.\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
vty->node = BGP_EVPN_NODE;
return CMD_SUCCESS;
}
"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) {
json_object_int_add(json, "totalVrfs", count);
- 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_ext(
+ json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
} else {
if (count)
vty);
}
-DEFUN(show_bgp_martian_nexthop_db,
- show_bgp_martian_nexthop_db_cmd,
- "show bgp martian next-hop",
- SHOW_STR
- BGP_STR
+DEFUN(show_bgp_martian_nexthop_db, show_bgp_martian_nexthop_db_cmd,
+ "show bgp [<view|vrf> VIEWVRFNAME] martian next-hop",
+ SHOW_STR BGP_STR BGP_INSTANCE_HELP_STR
"martian next-hops\n"
"martian next-hop database\n")
{
struct bgp *bgp = NULL;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "view", &idx)
+ || argv_find(argv, argc, "vrf", &idx))
+ bgp = bgp_lookup_by_name(argv[idx + 1]->arg);
+ else
+ bgp = bgp_get_default();
- bgp = bgp_get_default();
if (!bgp) {
vty_out(vty, "%% No BGP process is configured\n");
return CMD_WARNING;
/* Other attributes */
if ((count = community_count()))
vty_out(vty, "%ld BGP community entries, using %s of memory\n",
- count, mtype_memstr(memstrbuf, sizeof(memstrbuf),
- count * sizeof(struct community)));
+ count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct community)));
if ((count = mtype_stats_alloc(MTYPE_ECOMMUNITY)))
vty_out(vty, "%ld BGP community entries, using %s of memory\n",
- count, mtype_memstr(memstrbuf, sizeof(memstrbuf),
- count * sizeof(struct ecommunity)));
+ count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct ecommunity)));
if ((count = mtype_stats_alloc(MTYPE_LCOMMUNITY)))
vty_out(vty,
"%ld BGP large-community entries, using %s of memory\n",
- count, mtype_memstr(memstrbuf, sizeof(memstrbuf),
- count * sizeof(struct lcommunity)));
+ count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct lcommunity)));
if ((count = mtype_stats_alloc(MTYPE_CLUSTER)))
vty_out(vty, "%ld Cluster lists, using %s of memory\n", count,
count * sizeof(struct hash_backet)));
if ((count = mtype_stats_alloc(MTYPE_BGP_REGEXP)))
vty_out(vty, "%ld compiled regexes, using %s of memory\n",
- count, mtype_memstr(memstrbuf, sizeof(memstrbuf),
- count * sizeof(regex_t)));
+ count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(regex_t)));
return CMD_SUCCESS;
}
json_object_string_add(bestpath, "asPath", "confed");
if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
- if (bgp_flag_check(bgp,
- BGP_FLAG_MULTIPATH_RELAX_AS_SET))
- json_object_string_add(bestpath,
- "multiPathRelax",
+ if (bgp_flag_check(bgp, BGP_FLAG_MULTIPATH_RELAX_AS_SET))
+ json_object_string_add(bestpath, "multiPathRelax",
"as-set");
else
- json_object_string_add(bestpath,
- "multiPathRelax",
+ json_object_string_add(bestpath, "multiPathRelax",
"true");
} else
- json_object_string_add(bestpath,
- "multiPathRelax",
- "false");
+ json_object_string_add(bestpath, "multiPathRelax", "false");
if (bgp_flag_check(bgp, BGP_FLAG_COMPARE_ROUTER_ID))
json_object_string_add(bestpath, "compareRouterId", "true");
if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED)
|| bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST)) {
if (bgp_flag_check(bgp, BGP_FLAG_MED_CONFED))
- json_object_string_add(bestpath, "med",
- "confed");
+ json_object_string_add(bestpath, "med", "confed");
if (bgp_flag_check(bgp, BGP_FLAG_MED_MISSING_AS_WORST))
json_object_string_add(bestpath, "med",
"missing-as-worst");
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;
+ vrf_id_ui = (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, "peerGroupCount", ents);
json_object_int_add(
json, "peerGroupMemory",
- ents * sizeof(struct
- peer_group));
+ ents
+ * sizeof(struct
+ peer_group));
}
if (CHECK_FLAG(bgp->af_flags[afi][safi],
vty_out(vty,
"RIB entries %ld, using %s of memory\n",
ents,
- mtype_memstr(memstrbuf,
- sizeof(memstrbuf),
- ents * sizeof(struct
- bgp_node)));
+ mtype_memstr(
+ memstrbuf, sizeof(memstrbuf),
+ ents
+ * sizeof(struct
+ bgp_node)));
/* Peer related usage */
ents = listcount(bgp->peer);
mtype_memstr(
memstrbuf,
sizeof(memstrbuf),
- ents * sizeof(struct
- peer_group)));
+ ents
+ * sizeof(struct
+ peer_group)));
if (CHECK_FLAG(bgp->af_flags[afi][safi],
BGP_CONFIG_DAMPENING))
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)");
bgp_show_bestpath_json(bgp, json);
- 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_ext(
+ json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
} else {
if (count)
}
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");
}
paf = peer_af_find(p, afi, safi);
if (paf && PAF_SUBGRP(paf)) {
- vty_out(vty, " Update group %" PRIu64
- ", subgroup %" PRIu64 "\n",
+ vty_out(vty,
+ " Update group %" PRIu64 ", subgroup %" PRIu64
+ "\n",
PAF_UPDGRP(paf)->id, PAF_SUBGRP(paf)->id);
vty_out(vty, " Packet Queue length %d\n",
bpacket_queue_virtual_length(paf));
/* 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,
"bgpTimerConfiguredKeepAliveIntervalMsecs",
p->keepalive * 1000);
} else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
- || (bgp->default_keepalive !=
- BGP_DEFAULT_KEEPALIVE)) {
+ || (bgp->default_keepalive
+ != BGP_DEFAULT_KEEPALIVE)) {
json_object_int_add(json_neigh,
"bgpTimerConfiguredHoldTimeMsecs",
bgp->default_holdtime);
vty_out(vty, ", keepalive interval is %d seconds\n",
p->keepalive);
} else if ((bgp->default_holdtime != BGP_DEFAULT_HOLDTIME)
- || (bgp->default_keepalive !=
- BGP_DEFAULT_KEEPALIVE)) {
+ || (bgp->default_keepalive
+ != BGP_DEFAULT_KEEPALIVE)) {
vty_out(vty, " Configured hold time is %d",
bgp->default_holdtime);
vty_out(vty, ", keepalive interval is %d seconds\n",
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) {
} else
vty_out(vty,
" Reduce the no. of prefix from %s, will restart in %ld seconds\n",
- p->host, thread_timer_remain_second(
- p->t_pmax_restart));
+ p->host,
+ thread_timer_remain_second(
+ p->t_pmax_restart));
} else {
if (use_json)
json_object_boolean_true_add(
}
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));
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
} else {
vty_out(vty, "\n");
}
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);
+ : (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);
vrf = pg = NULL;
int idx = 0;
- vrf = argv_find(argv, argc, "VIEWVRFNAME", &idx) ? argv[idx]->arg : NULL;
+ vrf = argv_find(argv, argc, "VIEWVRFNAME", &idx) ? argv[idx]->arg
+ : NULL;
pg = argv_find(argv, argc, "PGNAME", &idx) ? argv[idx]->arg : NULL;
return bgp_show_peer_group_vty(vty, vrf, pg);
install_element(BGP_NODE, &bgp_listen_range_cmd);
install_element(BGP_NODE, &no_bgp_listen_range_cmd);
+ /* "bgp default 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);
"Address Family modifier\n"
extern void bgp_vty_init(void);
-extern const char *afi_safi_print(afi_t, safi_t);
-extern const char *afi_safi_json(afi_t, safi_t);
-extern void bgp_config_write_update_delay(struct vty *, struct bgp *);
+extern const char *afi_safi_print(afi_t afi, safi_t safi);
+extern const char *afi_safi_json(afi_t afi, safi_t safi);
+extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp);
extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp);
extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp);
extern void bgp_config_write_listen(struct vty *vty, struct bgp *bgp);
/* Now perform the add/update. */
bgp_redistribute_add(bgp, &api.prefix, &nexthop, ifindex,
- nhtype, api.metric, api.type,
- api.instance, api.tag);
+ nhtype, api.metric, api.type, api.instance,
+ api.tag);
} else {
bgp_redistribute_delete(bgp, &api.prefix, api.type,
api.instance);
/* 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.type = ZEBRA_ROUTE_BGP;
api.safi = safi;
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;
+ api_nh->vrf_id = bgp->vrf_id;
+ /* 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;
}
if (mpinfo->extra
- && bgp_is_valid_label(&mpinfo->extra->label)) {
+ && bgp_is_valid_label(&mpinfo->extra->label[0])
+ && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) {
has_valid_label = 1;
- label = label_pton(&mpinfo->extra->label);
+ label = label_pton(&mpinfo->extra->label[0]);
api_nh->label_num = 1;
api_nh->labels[0] = 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.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)
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT && 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)
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT && is_evpn_enabled())
bgp_zebra_advertise_all_vni(bgp, 0);
/* Deregister for router-id, interfaces, redistributed routes. */
zclient_send_interface_radv_req(zclient, bgp->vrf_id, peer->ifp, 0, 0);
}
+int bgp_zebra_advertise_subnet(struct bgp *bgp, int advertise, vni_t vni)
+{
+ struct stream *s = NULL;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
+ return 0;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_ADVERTISE_SUBNET, bgp->vrf_id);
+ stream_putc(s, advertise);
+ stream_put3(s, vni);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
int bgp_zebra_advertise_gw_macip(struct bgp *bgp, int advertise, vni_t vni)
{
struct stream *s = NULL;
*/
}
+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)
{
struct stream *s;
vni_t vni;
struct bgp *bgp;
- struct in_addr vtep_ip;
+ struct in_addr vtep_ip = {INADDR_ANY};
+ 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);
}
return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip);
}
+static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient,
+ zebra_size_t length,
+ vrf_id_t vrf_id)
+{
+ struct stream *s = NULL;
+ struct bgp *bgp_vrf = NULL;
+ struct prefix p;
+ char buf[PREFIX_STRLEN];
+
+ memset(&p, 0, sizeof(struct prefix));
+ s = zclient->ibuf;
+ stream_get(&p, s, sizeof(struct prefix));
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp_vrf)
+ return;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Recv prefix %s %s on vrf %s",
+ prefix2str(&p, buf, sizeof(buf)),
+ (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL",
+ vrf_id_to_name(vrf_id));
+
+ if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) {
+
+ if (p.family == AF_INET)
+ return bgp_evpn_advertise_type5_route(
+ bgp_vrf, &p, NULL, AFI_IP, SAFI_UNICAST);
+ else
+ return bgp_evpn_advertise_type5_route(
+ bgp_vrf, &p, NULL, AFI_IP6, SAFI_UNICAST);
+
+ } else {
+ if (p.family == AF_INET)
+ return bgp_evpn_withdraw_type5_route(
+ bgp_vrf, &p, AFI_IP, SAFI_UNICAST);
+ else
+ return bgp_evpn_withdraw_type5_route(
+ bgp_vrf, &p, AFI_IP6, SAFI_UNICAST);
+ }
+}
+
extern struct zebra_privs_t bgpd_privs;
void bgp_zebra_init(struct thread_master *master)
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;
+ zclient->local_ip_prefix_add = bgp_zebra_process_local_ip_prefix;
+ zclient->local_ip_prefix_del = bgp_zebra_process_local_ip_prefix;
}
void bgp_zebra_destroy(void)
vrf_id_t);
extern struct interface *if_lookup_by_ipv6_exact(struct in6_addr *, ifindex_t,
vrf_id_t);
-
+extern int bgp_zebra_advertise_subnet(struct bgp *bgp, int advertise,
+ vni_t vni);
extern int bgp_zebra_advertise_gw_macip(struct bgp *, int, vni_t);
extern int bgp_zebra_advertise_all_vni(struct bgp *, int);
#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;
* - 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);
if (rn->info != NULL) {
/* Special handling for 2-level routing
* tables. */
- if (safi == SAFI_MPLS_VPN
- || safi == SAFI_ENCAP
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
|| safi == SAFI_EVPN) {
- for (nrn = bgp_table_top((
- struct bgp_table
- *)(rn->info));
- nrn;
- nrn = bgp_route_next(nrn))
- bgp_process(bgp, nrn,
- afi, safi);
+ for (nrn = bgp_table_top(
+ (struct bgp_table *)(rn->info));
+ nrn; nrn = bgp_route_next(nrn))
+ bgp_process(bgp, nrn, afi, safi);
} else
bgp_process(bgp, rn, afi, safi);
}
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;
peer->afc[afi][safi] = 1;
if (peer->group)
- peer_group2peer_config_copy_af(peer->group, peer,
- afi, safi);
+ peer_group2peer_config_copy_af(peer->group, peer, afi, safi);
if (!active && peer_active(peer)) {
bgp_timer_set(peer);
ret |= peer_activate_af(peer, afi, safi);
}
- /* If this is the first peer to be activated for this afi/labeled-unicast
- * recalc bestpaths to trigger label allocation */
- if (safi == SAFI_LABELED_UNICAST && !bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) {
+ /* If this is the first peer to be activated for this
+ * afi/labeled-unicast recalc bestpaths to trigger label allocation */
+ if (safi == SAFI_LABELED_UNICAST
+ && !bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) {
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_info("peer(s) are now active for labeled-unicast, allocate MPLS labels");
+ zlog_info(
+ "peer(s) are now active for labeled-unicast, allocate MPLS labels");
bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 1;
bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST);
bgp = peer->bgp;
- /* If this is the last peer to be deactivated for this afi/labeled-unicast
- * recalc bestpaths to trigger label deallocation */
- if (safi == SAFI_LABELED_UNICAST &&
- bgp->allocate_mpls_labels[afi][SAFI_UNICAST] &&
- !bgp_afi_safi_peer_exists(bgp, afi, safi)) {
+ /* If this is the last peer to be deactivated for this
+ * afi/labeled-unicast recalc bestpaths to trigger label deallocation */
+ if (safi == SAFI_LABELED_UNICAST
+ && bgp->allocate_mpls_labels[afi][SAFI_UNICAST]
+ && !bgp_afi_safi_peer_exists(bgp, afi, safi)) {
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_info("peer(s) are no longer active for labeled-unicast, deallocate MPLS labels");
+ zlog_info(
+ "peer(s) are no longer active for labeled-unicast, deallocate MPLS labels");
bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 0;
bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST);
}
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;
}
} else if (peer->afc[afi][safi])
peer_deactivate(peer, afi, safi);
- }
+ }
if (peer->group) {
assert(group && peer->group == group);
XSTRDUP(MTYPE_BGP_PEER_HOST, cmd_domainname_get());
bgp->peer = list_new();
bgp->peer->cmp = (int (*)(void *, void *))peer_cmp;
- bgp->peerhash = hash_create(peer_hash_key_make,
- peer_hash_same,
+ bgp->peerhash = hash_create(peer_hash_key_make, peer_hash_same,
"BGP Peer Hash");
bgp->peerhash->max_size = BGP_PEER_MAX_HASH_SIZE;
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);
}
/* Track if addpath TX is in use */
- if (flag & (PEER_FLAG_ADDPATH_TX_ALL_PATHS
- | PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS)) {
+ if (flag
+ & (PEER_FLAG_ADDPATH_TX_ALL_PATHS
+ | PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS)) {
bgp = peer->bgp;
addpath_tx_used = 0;
} else {
if (!peer_af_flag_check(peer, afi, safi,
PEER_FLAG_SEND_COMMUNITY)
- && (!g_peer || peer_af_flag_check(g_peer, afi, safi,
- PEER_FLAG_SEND_COMMUNITY))
+ && (!g_peer
+ || peer_af_flag_check(g_peer, afi, safi,
+ PEER_FLAG_SEND_COMMUNITY))
&& !peer_af_flag_check(peer, afi, safi,
PEER_FLAG_SEND_EXT_COMMUNITY)
&& (!g_peer
PEER_FLAG_SEND_EXT_COMMUNITY))
&& !peer_af_flag_check(peer, afi, safi,
PEER_FLAG_SEND_LARGE_COMMUNITY)
- && (!g_peer || peer_af_flag_check(
- g_peer, afi, safi,
- PEER_FLAG_SEND_LARGE_COMMUNITY))) {
+ && (!g_peer
+ || peer_af_flag_check(
+ g_peer, afi, safi,
+ PEER_FLAG_SEND_LARGE_COMMUNITY))) {
vty_out(vty, " no neighbor %s send-community all\n",
addr);
} else {
if (!peer_af_flag_check(peer, afi, safi,
PEER_FLAG_SEND_COMMUNITY)
- && (!g_peer || peer_af_flag_check(
- g_peer, afi, safi,
- PEER_FLAG_SEND_COMMUNITY))) {
+ && (!g_peer
+ || peer_af_flag_check(
+ g_peer, afi, safi,
+ PEER_FLAG_SEND_COMMUNITY))) {
vty_out(vty,
" no neighbor %s send-community\n",
addr);
bgp_config_write_filter(vty, peer, afi, safi);
/* atribute-unchanged. */
- if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED) ||
- peer_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_UNCHANGED) ||
- peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) {
-
- if (!peer_group_active(peer) ||
- peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_AS_PATH_UNCHANGED) ||
- peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_NEXTHOP_UNCHANGED) ||
- peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_MED_UNCHANGED)) {
+ if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED)
+ || peer_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_UNCHANGED)
+ || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) {
+
+ if (!peer_group_active(peer)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MED_UNCHANGED)) {
vty_out(vty,
" neighbor %s attribute-unchanged%s%s%s\n",
/* 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 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(PTHREAD_IO, &attr, NULL);
- frr_pthread_run(PTHREAD_KEEPALIVES, &attr, NULL);
+ frr_pthread_run(io, NULL);
+ frr_pthread_run(ka, 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)
/* $FRR indent$ */
/* clang-format off */
#define BGP_MAXMED_ADMIN_UNCONFIGURED 0 /* Off by default */
- u_int32_t
- maxmed_admin_value; /* Max-med value when administrative in on
+ u_int32_t maxmed_admin_value; /* Max-med value when administrative in on
*/
+ /* $FRR indent$ */
+ /* clang-format off */
#define BGP_MAXMED_VALUE_DEFAULT 4294967294 /* Maximum by default */
- u_char maxmed_active; /* 1/0 if max-med is active or not */
- u_int32_t maxmed_value; /* Max-med value when its active */
+ u_char maxmed_active; /* 1/0 if max-med is active or not */
+ u_int32_t maxmed_value; /* Max-med value when its active */
/* BGP update delay on startup */
struct thread *t_update_delay;
/* 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;
+
+ /* route map for advertise ipv4/ipv6 unicast (type-5 routes) */
+ struct bgp_rmap adv_cmd_rmap[AFI_MAX][SAFI_MAX];
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
#define PEER_CONFIG_ROUTEADV (1 << 2) /* route advertise */
#define PEER_GROUP_CONFIG_TIMER (1 << 3) /* timers from peer-group */
-#define PEER_OR_GROUP_TIMER_SET(peer) \
- (CHECK_FLAG(peer->config, PEER_CONFIG_TIMER) \
+#define PEER_OR_GROUP_TIMER_SET(peer) \
+ (CHECK_FLAG(peer->config, PEER_CONFIG_TIMER) \
|| CHECK_FLAG(peer->config, PEER_GROUP_CONFIG_TIMER))
_Atomic uint32_t holdtime;
/* 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 */
_Atomic uint32_t refresh_in; /* Route Refresh input count */
_Atomic uint32_t refresh_out; /* Route Refresh output count */
_Atomic uint32_t dynamic_cap_in; /* Dynamic Capability input count. */
- _Atomic uint32_t dynamic_cap_out; /* Dynamic Capability output count. */
+ _Atomic uint32_t dynamic_cap_out; /* Dynamic Capability output count. */
/* BGP state count */
u_int32_t established; /* Established */
/* 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
extern int bgp_map_afi_safi_iana2int(iana_afi_t pkt_afi, iana_safi_t pkt_safi,
afi_t *afi, safi_t *safi);
extern int bgp_map_afi_safi_int2iana(afi_t afi, safi_t safi,
- iana_afi_t *pkt_afi, iana_safi_t *pkt_safi);
+ iana_afi_t *pkt_afi,
+ iana_safi_t *pkt_safi);
extern struct peer_af *peer_af_create(struct peer *, afi_t, safi_t);
extern struct peer_af *peer_af_find(struct peer *, afi_t, safi_t);
/***********************************************************************
* VNC Configuration/CLI
***********************************************************************/
-#define VNC_VTY_CONFIG_CHECK(bgp) \
- { \
- switch (bgp_rfapi_is_vnc_configured(bgp)) { \
- case EPERM: \
- vty_out(vty, "VNC operations only permitted on default BGP instance.\n"); \
- return CMD_WARNING_CONFIG_FAILED; \
- break; \
- case ENXIO: \
- vty_out(vty, "VNC not configured.\n"); \
- return CMD_WARNING_CONFIG_FAILED; \
- break; \
- default: \
- break; \
- } \
+#define VNC_VTY_CONFIG_CHECK(bgp) \
+ { \
+ switch (bgp_rfapi_is_vnc_configured(bgp)) { \
+ case EPERM: \
+ vty_out(vty, \
+ "VNC operations only permitted on default BGP instance.\n"); \
+ return CMD_WARNING_CONFIG_FAILED; \
+ break; \
+ case ENXIO: \
+ vty_out(vty, "VNC not configured.\n"); \
+ return CMD_WARNING_CONFIG_FAILED; \
+ break; \
+ default: \
+ break; \
+ } \
}
DEFUN (vnc_advertise_un_method,
} else {
rspint = strtoul(argv[1]->arg, NULL, 10);
if (rspint > INT32_MAX)
- rspint =
- INT32_MAX; /* is really an int, not an unsigned
- int */
+ rspint = INT32_MAX; /* is really an int, not an unsigned
+ int */
}
bgp->rfapi_cfg->default_response_lifetime = rspint;
/* add to tail of list */
listnode_add(bgp->rfapi_cfg->nve_groups_sequential, rfg);
}
- rfg->label = MPLS_LABEL_ILLEGAL;
+ rfg->label = MPLS_LABEL_NONE;
QOBJ_REG(rfg, rfapi_nve_group_cfg);
return rfg;
return CMD_WARNING_CONFIG_FAILED;
}
- if (argv[idx-1]->text[0] == 'z')
+ if (argv[idx - 1]->text[0] == 'z')
is_bgp = 0;
- idx += 2; /* skip afi and keyword */
+ idx += 2; /* skip afi and keyword */
if (is_bgp) {
- if (idx == argc ||
- strmatch(argv[idx]->arg,
- rfg->plist_export_bgp_name[afi])) {
+ if (idx == argc
+ || strmatch(argv[idx]->arg,
+ rfg->plist_export_bgp_name[afi])) {
if (rfg->plist_export_bgp_name[afi])
free(rfg->plist_export_bgp_name[afi]);
rfg->plist_export_bgp_name[afi] = NULL;
vnc_direct_bgp_reexport_group_afi(bgp, rfg, afi);
}
} else {
- if (idx == argc ||
- strmatch(argv[idx]->arg,
- rfg->plist_export_zebra_name[afi])) {
+ if (idx == argc
+ || strmatch(argv[idx]->arg,
+ rfg->plist_export_zebra_name[afi])) {
if (rfg->plist_export_zebra_name[afi])
free(rfg->plist_export_zebra_name[afi]);
rfg->plist_export_zebra_name[afi] = NULL;
return CMD_WARNING_CONFIG_FAILED;
}
- if (argv[idx-1]->text[0] == 'z')
+ if (argv[idx - 1]->text[0] == 'z')
is_bgp = 0;
idx = argc - 1;
switch (argv[idx]->text[0]) {
case 'z':
is_bgp = 0;
- /* fall thru */
+ /* fall thru */
case 'b':
idx += 2;
break;
- default: /* route-map */
+ default: /* route-map */
idx++;
break;
}
if (is_bgp) {
- if (idx == argc ||
- strmatch(argv[idx]->arg, rfg->routemap_export_bgp_name)) {
+ if (idx == argc
+ || strmatch(argv[idx]->arg,
+ rfg->routemap_export_bgp_name)) {
if (rfg->routemap_export_bgp_name)
free(rfg->routemap_export_bgp_name);
rfg->routemap_export_bgp_name = NULL;
vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP6);
}
} else {
- if (idx == argc ||
- strmatch(argv[idx]->arg,
- rfg->routemap_export_zebra_name)) {
+ if (idx == argc
+ || strmatch(argv[idx]->arg,
+ rfg->routemap_export_zebra_name)) {
if (rfg->routemap_export_zebra_name)
free(rfg->routemap_export_zebra_name);
rfg->routemap_export_zebra_name = NULL;
if (rfg->rfd)
clear_vnc_vrf_closer(rfg);
bgp_rfapi_delete_nve_group(vty, bgp, rfg);
- }
- else /* must be delete all */
+ } else /* must be delete all */
for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->nve_groups_sequential,
node, nnode, rfg)) {
if (rfg->rfd)
struct rfapi_nve_group_cfg *rfg;
VTY_DECLVAR_CONTEXT(bgp, bgp);
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
+ vty_out(vty, "Can't configure vrf-policy within a BGP VRF instance\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
/* Search for name */
rfg = bgp_rfapi_cfg_match_byname(bgp, argv[1]->arg,
RFAPI_GROUP_CFG_VRF);
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
+ /* silently return */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ return CMD_SUCCESS;
+
return bgp_rfapi_delete_named_nve_group(vty, bgp, argv[2]->arg,
RFAPI_GROUP_CFG_VRF);
}
vnc_redistribute_prechange(bgp);
}
- rfg->label = MPLS_LABEL_ILLEGAL;
+ rfg->label = MPLS_LABEL_NONE;
if (bgp->rfapi_cfg->rfg_redist == rfg) {
vnc_redistribute_postchange(bgp);
if (rfg->plist_export_bgp_name[afi]) {
vty_out(vty,
" export %s%s prefix-list %s\n",
- (rfg->type == RFAPI_GROUP_CFG_VRF ? "" : "bgp "),
+ (rfg->type == RFAPI_GROUP_CFG_VRF
+ ? ""
+ : "bgp "),
afistr,
rfg->plist_export_bgp_name
[afi]);
if (rfg->plist_export_zebra_name[afi]) {
vty_out(vty,
" export %s%s prefix-list %s\n",
- (rfg->type == RFAPI_GROUP_CFG_VRF ? "" : "zebra "),
+ (rfg->type == RFAPI_GROUP_CFG_VRF
+ ? ""
+ : "zebra "),
afistr,
rfg->plist_export_zebra_name
[afi]);
if (rfg->routemap_export_bgp_name) {
vty_out(vty, " export %sroute-map %s\n",
- (rfg->type == RFAPI_GROUP_CFG_VRF ? "" : "bgp "),
+ (rfg->type == RFAPI_GROUP_CFG_VRF
+ ? ""
+ : "bgp "),
rfg->routemap_export_bgp_name);
}
if (rfg->routemap_export_zebra_name) {
vty_out(vty, " export %sroute-map %s\n",
- (rfg->type == RFAPI_GROUP_CFG_VRF ? "" : "zebra "),
+ (rfg->type == RFAPI_GROUP_CFG_VRF
+ ? ""
+ : "zebra "),
rfg->routemap_export_zebra_name);
}
if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) {
}
}
- 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) {
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",
prefix2str(&rfg->vn_prefix, buf,
sizeof(buf));
- vty_out(vty, " prefix %s %s\n",
- "vn", buf);
+ vty_out(vty, " prefix %s %s\n", "vn",
+ buf);
}
if (rfg->un_prefix.family && rfg->un_node) {
prefix2str(&rfg->un_prefix, buf,
sizeof(buf));
- vty_out(vty, " prefix %s %s\n",
- "un", buf);
+ vty_out(vty, " prefix %s %s\n", "un",
+ buf);
}
value);
} else
- vty_out(vty,
- " rd %s\n",
- prefix_rd2str(&rfg->rd,
- buf,
- sizeof(buf)));
+ 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"
* 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;
/* save backref to rfapi handle */
assert(bgp_info_extra_get(new));
new->extra->vnc.export.rfapi_handle = (void *)rfd;
- encode_label(label_val, &new->extra->label);
+ encode_label(label_val, &new->extra->label[0]);
/* debug */
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);
rfapi_time(&new->extra->vnc.import.create_time);
}
if (label)
- encode_label(*label, &new->extra->label);
+ encode_label(*label, &new->extra->label[0]);
new->type = type;
new->sub_type = sub_type;
new->peer = peer;
vo->v.l2addr.local_nve_id = bi->extra->vnc.import.rd.val[1];
/* label comes from MP_REACH_NLRI label */
- vo->v.l2addr.label = decode_label(&bi->extra->label);
+ vo->v.l2addr.label = decode_label(&bi->extra->label[0]);
new->vn_options = vo;
if (bi->extra)
label = decode_label(
- &bi->extra->label);
+ &bi->extra->label[0]);
(*rfapiBgpInfoFilteredImportFunction(
safi))(
it, /* which import table */
vo->v.l2addr.local_nve_id = bi->extra->vnc.import.rd.val[1];
/* label comes from MP_REACH_NLRI label */
- vo->v.l2addr.label = decode_label(&bi->extra->label);
+ vo->v.l2addr.label = decode_label(&bi->extra->label[0]);
rfapi_vn_options_free(
ri->vn_options); /* maybe free old version */
}
if (bi->extra != NULL)
- vty_out(vty, " label=%u", decode_label(&bi->extra->label));
+ vty_out(vty, " label=%u", decode_label(&bi->extra->label[0]));
if (!rfapiGetVncLifetime(bi->attr, &lifetime)) {
vty_out(vty, " life=%d", lifetime);
char *p = line;
int r;
int has_macaddr = 0;
- struct ethaddr macaddr;
+ struct ethaddr macaddr = {{0}};
struct rfapi_l2address_option l2o_buf;
uint8_t l2hid = 0; /* valid if has_macaddr */
inet_ntop(pfx_vn.family, &pfx_vn.u.prefix, buf_ntop,
BUFSIZ));
if (bi->extra) {
- u_int32_t l = decode_label(&bi->extra->label);
+ u_int32_t l = decode_label(&bi->extra->label[0]);
snprintf(buf_vn, BUFSIZ, "Label: %d", l);
} else /* should never happen */
{
}
}
if (tun_type != BGP_ENCAP_TYPE_MPLS && bi->extra) {
- u_int32_t l = decode_label(&bi->extra->label);
+ u_int32_t l = decode_label(&bi->extra->label[0]);
if (!MPLS_LABEL_IS_NULL(l)) {
fp(out, " Label: %d", l);
if (nlines == 1)
iattr, /* bgp_update copies this attr */
afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT,
BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */
- NULL, /* tag not used for unicast */
+ NULL, 0, /* tag not used for unicast */
0, NULL); /* EVPN not used */
bgp_attr_unintern(&iattr);
}
NULL, /* attr, ignored */
afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT,
BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */
- NULL, NULL); /* tag not used for unicast */
+ NULL, 0, NULL); /* tag not used for unicast */
}
static void vnc_direct_bgp_vpn_enable_ce(struct bgp *bgp, afi_t afi)
ZEBRA_ROUTE_VNC_DIRECT,
BGP_ROUTE_REDISTRIBUTE,
NULL, /* RD not used for unicast */
- NULL,
+ NULL, 0,
NULL); /* tag not used for unicast */
}
}
afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT,
BGP_ROUTE_REDISTRIBUTE,
NULL, /* RD not used for unicast */
- NULL, NULL); /* tag not used for unicast */
+ NULL, 0, NULL); /* tag not used for unicast */
/*
* yuck!
* - but consistent with rest of function
afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT,
BGP_ROUTE_REDISTRIBUTE,
NULL, /* RD not used for unicast */
- NULL, NULL); /* tag not used for unicast */
+ NULL, 0, NULL); /* tag not used for unicast */
}
}
}
unicast */
NULL, /* tag not used for
unicast */
- 0, NULL); /* EVPN not used */
+ 0, 0, NULL); /* EVPN not used */
bgp_attr_unintern(&iattr);
}
BGP_ROUTE_REDISTRIBUTE,
NULL, /* RD not used for
unicast */
- NULL, NULL); /* tag not
+ NULL, 0, NULL); /* tag not
used for
unicast */
}
BGP_ROUTE_REDISTRIBUTE,
NULL, /* RD not used for unicast */
NULL, /* tag not used for unicast */
- 0, NULL); /* EVPN not used */
+ 0, 0, NULL); /* EVPN not used */
bgp_attr_unintern(&iattr);
ZEBRA_ROUTE_VNC_DIRECT,
BGP_ROUTE_REDISTRIBUTE,
NULL, /* RD not used for unicast */
- NULL,
+ NULL, 0,
NULL); /* tag not used for unicast */
return;
}
BGP_ROUTE_REDISTRIBUTE,
NULL, /* RD not used for
unicast */
- NULL, NULL); /* tag not
+ NULL, 0, NULL); /* tag not
used for
unicast,
EVPN
afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH,
BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */
NULL, /* tag not used for unicast, EVPN neither */
- 0, NULL); /* EVPN not used */
+ 0, 0, NULL); /* EVPN not used */
bgp_attr_unintern(&iattr);
}
NULL, /* attr, ignored */
family2afi(eti->node->p.family), SAFI_UNICAST, eti->type,
eti->subtype, NULL, /* RD not used for unicast */
- NULL, NULL); /* tag not used for unicast, EVPN neither */
+ NULL, 0, NULL); /* tag not used for unicast, EVPN neither */
/*
* Free the eti
NULL, /* tag not used for
unicast, EVPN
neither */
- 0, NULL); /* EVPN not used */
+ 0, 0, NULL); /* EVPN not used */
bgp_attr_unintern(&iattr);
}
}
ZEBRA_ROUTE_VNC_DIRECT_RH,
BGP_ROUTE_REDISTRIBUTE,
NULL, /* RD not used for unicast */
- NULL, NULL); /* tag not used for
+ NULL, 0, NULL); /* tag not used for
unicast, EVPN
neither */
}
ecommunity_merge(new_ecom, bi->attr->ecommunity);
if (bi->extra)
- label = decode_label(&bi->extra->label);
+ label = decode_label(&bi->extra->label[0]);
add_vnc_route(&vncHDResolveNve, bgp, SAFI_MPLS_VPN,
prefix, /* unicast route prefix */
prd = &bi_interior->extra->vnc.import
.rd;
label = decode_label(
- &bi_interior->extra->label);
+ &bi_interior->extra->label[0]);
} else
prd = NULL;
prd = &bi_interior->extra->vnc.import
.rd;
label = decode_label(
- &bi_interior->extra->label);
+ &bi_interior->extra->label[0]);
} else
prd = NULL;
if (bi_interior->extra) {
prd = &bi_interior->extra->vnc.import.rd;
label = decode_label(
- &bi_interior->extra->label);
+ &bi_interior->extra->label[0]);
} else
prd = NULL;
if (bi->extra) {
prd = &bi->extra->vnc.import.rd;
label = decode_label(
- &bi->extra->label);
+ &bi->extra->label[0]);
} else
prd = NULL;
prd = &bi_interior->extra->vnc.import
.rd;
label = decode_label(
- &bi_interior->extra->label);
+ &bi_interior->extra->label[0]);
} else
prd = NULL;
if (bi_interior->extra) {
prd = &bi_interior->extra->vnc.import.rd;
label = decode_label(
- &bi_interior->extra->label);
+ &bi_interior->extra->label[0]);
} else
prd = NULL;
if (bi_interior->extra) {
prd = &bi_interior->extra->vnc.import.rd;
- label = decode_label(&bi_interior->extra->label);
+ label = decode_label(&bi_interior->extra->label[0]);
} else
prd = NULL;
if (bi->extra) {
prd = &bi->extra->vnc.import.rd;
- label = decode_label(&bi->extra->label);
+ label = decode_label(
+ &bi->extra->label[0]);
} else
prd = NULL;
#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",
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];
+ api_nh->vrf_id = VRF_DEFAULT;
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;
##
AC_PREREQ(2.60)
-AC_INIT(frr, 3.1-dev, [https://github.com/frrouting/frr/issues])
+AC_INIT(frr, 4.1-dev, [https://github.com/frrouting/frr/issues])
PACKAGE_URL="https://frrouting.org/"
AC_SUBST(PACKAGE_URL)
PACKAGE_FULLNAME="FRRouting"
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,
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 ---------------------
doc/eigrpd.8
doc/ripngd.8
doc/pimd.8
+ doc/mtracebis.8
doc/nhrpd.8
doc/vtysh.1
doc/watchfrr.8
(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.
Architecture: any
Depends: ${shlibs:Depends}, logrotate (>= 3.2-11), ${misc:Depends}
Pre-Depends: adduser
-Conflicts: zebra, zebra-pj
+Conflicts: zebra, zebra-pj, quagga
Replaces: zebra, zebra-pj
Suggests: snmpd
Description: BGP/OSPF/RIP/RIPng/ISIS/PIM/LDP routing daemon forked from Quagga
# WANT_xxxx --> Set to 1 for enable, 0 for disable
# The following are the defaults. They can be overridden by setting a
# env variable to a different value
-#
-# export WANT_LDP=1
-# export WANT_PIM=1
-# export WANT_OSPFAPI=1
-# export WANT_TCP_ZEBRA=0
-# export WANT_BGP_VNC=0
-# export WANT_CUMULUS_MODE=0
-# export WANT_MULTIPATH=1
-#
+
+WANT_LDP ?= 1
+WANT_PIM ?= 1
+WANT_OSPFAPI ?= 1
+WANT_TCP_ZEBRA ?= 0
+WANT_BGP_VNC ?= 1
+WANT_CUMULUS_MODE ?= 0
+WANT_MULTIPATH ?= 1
+WANT_SNMP ?= 0
+
# If multipath is enabled (WANT_MULTIPATH=1), then set number of multipaths here
# Please be aware that 0 is NOT disabled, but treated as unlimited
-# export MULTIPATH=256
-#
-# Set the following to the value required (or leave undefined for the default below)
+
+MULTIPATH ?= 256
+
+# Set the following to the value required (or leave alone for the default below)
# WANT_FRR_USER is used for the username and groupname of the FRR user account
-# export WANT_FRR_USER=frr
-# export WANT_FRR_VTY_GROUP=frrvty
+
+WANT_FRR_USER ?= frr
+WANT_FRR_VTY_GROUP ?= frrvty
+
#
####################################
USE_SNMP=--enable-snmp
$(warning "DEBIAN: SNMP enabled, sorry for your inconvenience")
else
+ USE_SNMP=--disable-snmp
$(warning "DEBIAN: SNMP disabled, see README.Debian")
endif
-ifneq ($(WANT_LDP), 0)
+ifeq ($(WANT_LDP), 1)
USE_LDP=--enable-ldpd
else
USE_LDP=--disable-ldpd
endif
-ifneq ($(WANT_PIM), 0)
+ifeq ($(WANT_PIM), 1)
USE_PIM=--enable-pimd
else
USE_PIM=--disable-pimd
endif
-ifneq ($(WANT_OSPFAPI), 0)
+ifeq ($(WANT_OSPFAPI), 1)
USE_OSPFAPI=--enable-ospfapi=yes
else
USE_OSPFAPI=--enable-ospfapi=no
ifeq ($(WANT_TCP_ZEBRA),1)
USE_TCP_ZEBRA=--enable-tcp-zebra
+else
+ USE_TCP_ZEBRA=--disable-tcp-zebra
endif
-ifneq ($(WANT_BGP_VNC), 0)
+ifeq ($(WANT_BGP_VNC), 1)
USE_BGP_VNC=--enable-bgp-vnc=yes
else
USE_BGP_VNC=--enable-bgp-vnc=no
endif
-ifndef WANT_FRR_USER
- USE_FRR_USER=--enable-user=frr
- USE_FRR_GROUP=--enable-group=frr
-else
- USE_FRR_USER=$(WANT_FRR_USER)
- USE_FRR_GROUP=$(WANT_FRR_USER)
-endif
-
-ifndef WANT_FRR_VTY_GROUP
- USE_FRR_VTY_GROUP=--enable-vty-group=frrvty
-else
- USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
-endif
+USE_FRR_USER=--enable-user=$(WANT_FRR_USER)
+USE_FRR_GROUP=--enable-group=$(WANT_FRR_USER)
+USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
-ifneq ($(WANT_MULTIPATH), 0)
- ifdef MULTIPATH
- USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
- else
- USE_MULTIPATH=--enable-multipath=256
- endif
+ifeq ($(WANT_MULTIPATH), 1)
+ USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
else
USE_MULTIPATH=--disable-multipath
endif
-ifeq ($(WANT_CUMULUS_NODE), 1)
+ifeq ($(WANT_CUMULUS_MODE), 1)
USE_CUMULUS=--enable-cumulus=yes
else
USE_CUMULUS=--enable-cumulus=no
perl -pi -e 's#^!log file #!log file /var/log/frr/#' debian/tmp/usr/share/doc/frr/examples/*sample*
# installing the Frr specific SNMP MIB
+ifeq ($(WANT_SNMP), 1)
install -D -m 644 ./zebra/GNOME-PRODUCT-ZEBRA-MIB debian/tmp/usr/share/snmp/mibs/GNOME-PRODUCT-ZEBRA-MIB
+else
+ mkdir -p debian/tmp/usr/share/snmp/mibs
+endif
# cleaning .la files
sed -i "/dependency_libs/ s/'.*'/''/" debian/tmp/usr/lib/*.la
sed -i "/dependency_libs/ s/'.*'/''/" debian/tmp/usr/lib/frr/modules/*.la
-
Architecture: any
Depends: ${shlibs:Depends}, logrotate (>= 3.2-11), ${misc:Depends}
Pre-Depends: adduser
-Conflicts: zebra, zebra-pj
+Conflicts: zebra, zebra-pj, quagga
Replaces: zebra, zebra-pj
Suggests: snmpd
Description: BGP/OSPF/RIP/RIPng/ISIS/PIM/LDP routing daemon forked from Quagga
etc/frr/
usr/bin/vtysh
+usr/bin/mtracebis
usr/include/frr/
usr/lib/
tools/frr etc/init.d/
usr/share/man/man8/zebra.8
usr/share/man/man8/isisd.8
usr/share/man/man8/watchfrr.8
+usr/share/man/man8/mtracebis.8
usr/share/snmp/mibs/
tools/etc/* etc/
tools/*.service lib/systemd/system
# WANT_xxxx --> Set to 1 for enable, 0 for disable
# The following are the defaults. They can be overridden by setting a
# env variable to a different value
-#
-# export WANT_LDP=1
-# export WANT_PIM=1
-# export WANT_OSPFAPI=1
-# export WANT_TCP_ZEBRA=0
-# export WANT_BGP_VNC=0
-# export WANT_CUMULUS_MODE=0
-# export WANT_MULTIPATH=1
-#
+
+WANT_LDP ?= 1
+WANT_PIM ?= 1
+WANT_OSPFAPI ?= 1
+WANT_TCP_ZEBRA ?= 0
+WANT_BGP_VNC ?= 1
+WANT_CUMULUS_MODE ?= 0
+WANT_MULTIPATH ?= 1
+WANT_SNMP ?= 0
+
# If multipath is enabled (WANT_MULTIPATH=1), then set number of multipaths here
# Please be aware that 0 is NOT disabled, but treated as unlimited
-# export MULTIPATH=256
-#
-# Set the following to the value required (or leave undefined for the default below)
+
+MULTIPATH ?= 256
+
+# Set the following to the value required (or leave alone for the default below)
# WANT_FRR_USER is used for the username and groupname of the FRR user account
-# export WANT_FRR_USER=frr
-# export WANT_FRR_VTY_GROUP=frrvty
+
+WANT_FRR_USER ?= frr
+WANT_FRR_VTY_GROUP ?= frrvty
+
#
####################################
USE_SNMP=--enable-snmp
$(warning "DEBIAN: SNMP enabled, sorry for your inconvenience")
else
+ USE_SNMP=--disable-snmp
$(warning "DEBIAN: SNMP disabled, see README.Debian")
endif
-ifneq ($(WANT_LDP), 0)
+ifeq ($(WANT_LDP), 1)
USE_LDP=--enable-ldpd
else
USE_LDP=--disable-ldpd
endif
-ifneq ($(WANT_PIM), 0)
+ifeq ($(WANT_PIM), 1)
USE_PIM=--enable-pimd
else
USE_PIM=--disable-pimd
endif
-ifneq ($(WANT_OSPFAPI), 0)
+ifeq ($(WANT_OSPFAPI), 1)
USE_OSPFAPI=--enable-ospfapi=yes
else
USE_OSPFAPI=--enable-ospfapi=no
ifeq ($(WANT_TCP_ZEBRA),1)
USE_TCP_ZEBRA=--enable-tcp-zebra
+else
+ USE_TCP_ZEBRA=--disable-tcp-zebra
endif
-ifneq ($(WANT_BGP_VNC), 0)
+ifeq ($(WANT_BGP_VNC), 1)
USE_BGP_VNC=--enable-bgp-vnc=yes
else
USE_BGP_VNC=--enable-bgp-vnc=no
endif
-ifndef WANT_FRR_USER
- USE_FRR_USER=--enable-user=frr
- USE_FRR_GROUP=--enable-group=frr
-else
- USE_FRR_USER=$(WANT_FRR_USER)
- USE_FRR_GROUP=$(WANT_FRR_USER)
-endif
+USE_FRR_USER=--enable-user=$(WANT_FRR_USER)
+USE_FRR_GROUP=--enable-group=$(WANT_FRR_USER)
+USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
-ifndef WANT_FRR_VTY_GROUP
- USE_FRR_VTY_GROUP=--enable-vty-group=frrvty
-else
- USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
-endif
-
-ifneq ($(WANT_MULTIPATH), 0)
- ifdef MULTIPATH
- USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
- else
- USE_MULTIPATH=--enable-multipath=256
- endif
+ifeq ($(WANT_MULTIPATH), 1)
+ USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
else
USE_MULTIPATH=--disable-multipath
endif
-ifeq ($(WANT_CUMULUS_NODE), 1)
+ifeq ($(WANT_CUMULUS_MODE), 1)
USE_CUMULUS=--enable-cumulus=yes
else
USE_CUMULUS=--enable-cumulus=no
perl -pi -e 's#^!log file #!log file /var/log/frr/#' debian/tmp/usr/share/doc/frr/examples/*sample*
# installing the Frr specific SNMP MIB
+ifeq ($(WANT_SNMP), 1)
install -D -m 644 ./zebra/GNOME-PRODUCT-ZEBRA-MIB debian/tmp/usr/share/snmp/mibs/GNOME-PRODUCT-ZEBRA-MIB
+else
+ mkdir -p debian/tmp/usr/share/snmp/mibs/
+endif
# cleaning .la files
sed -i "/dependency_libs/ s/'.*'/''/" debian/tmp/usr/lib/*.la
etc/frr/
usr/bin/vtysh
+usr/bin/mtracebis
usr/include/frr/
usr/lib/
tools/frr usr/lib/frr
usr/share/man/man8/isisd.8
usr/share/man/man8/watchfrr.8
usr/share/man/man8/frr-args.8
+usr/share/man/man8/mtracebis.8
usr/share/snmp/mibs/
tools/etc/* etc/
tools/*.service lib/systemd/system
# WANT_xxxx --> Set to 1 for enable, 0 for disable
# The following are the defaults. They can be overridden by setting a
# env variable to a different value
-#
-# export WANT_LDP=1
-# export WANT_PIM=1
-# export WANT_OSPFAPI=1
-# export WANT_TCP_ZEBRA=0
-# export WANT_BGP_VNC=0
-# export WANT_CUMULUS_MODE=0
-# export WANT_MULTIPATH=1
-#
+
+WANT_LDP ?= 1
+WANT_PIM ?= 1
+WANT_OSPFAPI ?= 1
+WANT_TCP_ZEBRA ?= 0
+WANT_BGP_VNC ?= 1
+WANT_CUMULUS_MODE ?= 0
+WANT_MULTIPATH ?= 1
+WANT_SNMP ?= 0
+
# If multipath is enabled (WANT_MULTIPATH=1), then set number of multipaths here
# Please be aware that 0 is NOT disabled, but treated as unlimited
-# export MULTIPATH=256
-#
-# Set the following to the value required (or leave undefined for the default below)
+
+MULTIPATH ?= 256
+
+# Set the following to the value required (or leave alone for the default below)
# WANT_FRR_USER is used for the username and groupname of the FRR user account
-# export WANT_FRR_USER=frr
-# export WANT_FRR_VTY_GROUP=frrvty
+
+WANT_FRR_USER ?= frr
+WANT_FRR_VTY_GROUP ?= frrvty
+
#
####################################
USE_SNMP=--enable-snmp
$(warning "DEBIAN: SNMP enabled, sorry for your inconvenience")
else
+ USE_SNMP=--disable-snmp
$(warning "DEBIAN: SNMP disabled, see README.Debian")
endif
-ifneq ($(WANT_LDP), 0)
+ifeq ($(WANT_LDP), 1)
USE_LDP=--enable-ldpd
else
USE_LDP=--disable-ldpd
endif
-ifneq ($(WANT_PIM), 0)
+ifeq ($(WANT_PIM), 1)
USE_PIM=--enable-pimd
else
USE_PIM=--disable-pimd
endif
-ifneq ($(WANT_OSPFAPI), 0)
+ifeq ($(WANT_OSPFAPI), 1)
USE_OSPFAPI=--enable-ospfapi=yes
else
USE_OSPFAPI=--enable-ospfapi=no
ifeq ($(WANT_TCP_ZEBRA),1)
USE_TCP_ZEBRA=--enable-tcp-zebra
+else
+ USE_TCP_ZEBRA=--disable-tcp-zebra
endif
-ifneq ($(WANT_BGP_VNC), 0)
+ifeq ($(WANT_BGP_VNC), 1)
USE_BGP_VNC=--enable-bgp-vnc=yes
else
USE_BGP_VNC=--enable-bgp-vnc=no
endif
-ifndef WANT_FRR_USER
- USE_FRR_USER=--enable-user=frr
- USE_FRR_GROUP=--enable-group=frr
-else
- USE_FRR_USER=$(WANT_FRR_USER)
- USE_FRR_GROUP=$(WANT_FRR_USER)
-endif
-
-ifndef WANT_FRR_VTY_GROUP
- USE_FRR_VTY_GROUP=--enable-vty-group=frrvty
-else
- USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
-endif
+USE_FRR_USER=--enable-user=$(WANT_FRR_USER)
+USE_FRR_GROUP=--enable-group=$(WANT_FRR_USER)
+USE_FRR_VTY_GROUP=--enable-vty-group=$(WANT_FRR_VTY_GROUP)
-ifneq ($(WANT_MULTIPATH), 0)
- ifdef MULTIPATH
- USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
- else
- USE_MULTIPATH=--enable-multipath=256
- endif
+ifeq ($(WANT_MULTIPATH), 1)
+ USE_MULTIPATH=--enable-multipath=$(MULTIPATH)
else
USE_MULTIPATH=--disable-multipath
endif
-ifeq ($(WANT_CUMULUS_NODE), 1)
+ifeq ($(WANT_CUMULUS_MODE), 1)
USE_CUMULUS=--enable-cumulus=yes
else
USE_CUMULUS=--enable-cumulus=no
sudo addgroup --system --gid 92 frr
sudo addgroup --system --gid 85 frrvty
- sudo adduser --system --ingroup frr --home /var/run/frr/ \
+ sudo adduser --system --ingroup frr --home /var/opt/frr/ \
--gecos "FRR suite" --shell /bin/false frr
sudo usermod -a -G frrvty frr
./bootstrap.sh
./configure \
--enable-exampledir=/usr/share/doc/frr/examples/ \
- --localstatedir=/var/run/frr \
+ --localstatedir=/var/opt/frr \
--sbindir=/usr/lib/frr \
--sysconfdir=/etc/frr \
--enable-vtysh \
### Create empty FRR configuration files
sudo install -m 755 -o frr -g frr -d /var/log/frr
+ sudo install -m 755 -o frr -g frr -d /var/opt/frr
sudo install -m 775 -o frr -g frrvty -d /etc/frr
sudo install -m 640 -o frr -g frr /dev/null /etc/frr/zebra.conf
sudo install -m 640 -o frr -g frr /dev/null /etc/frr/bgpd.conf
### Troubleshooting
-**Local state directory**
-
-The local state directory must exist and have the correct permissions applied
-for the frrouting daemons to start. In the above ./configure example the
-local state directory is set to /var/run/frr (--localstatedir=/var/run/frr)
-Debian considers /var/run/frr to be temporary and this is removed after a
-reboot.
-
-When using a different local state directory you need to create the new
-directory and change the ownership to the frr user, for example:
-
- mkdir /var/opt/frr
- chown frr /var/opt/frr
-
**Shared library error**
If you try and start any of the frrouting daemons you may see the below error
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) $< "$@"
if PIMD
man_MANS += pimd.8
+man_MANS += mtracebis.8
endif
if BGPD
ripd.8.in \
ripngd.8.in \
pimd.8.in \
+ mtracebis.8.in \
nhrpd.8.in \
vtysh.1.in \
watchfrr.8.in \
--- /dev/null
+OSPF Segment Routing
+====================
+
+This is an EXPERIMENTAL support of draft
+`draft-ietf-ospf-segment-routing-extensions-24`.
+DON'T use it for production network.
+
+Implementation details
+----------------------
+
+Concepts
+~~~~~~~~
+
+Segment Routing used 3 differents OPAQUE LSA in OSPF to carry the various
+information:
+
+* **Router Information:** flood the Segment Routing capabilities of the node.
+ This include the supported algorithms, the Segment Routing Global Block
+ (SRGB) and the Maximum Stack Depth (MSD).
+* **Extended Link:** flood the Adjaceny and Lan Adjacency Segment Identifier
+* **Extended Prefix:** flood the Prefix Segment Identifier
+
+The implementation follow previous TE and Router Information codes. It used the
+OPAQUE LSA functions defined in ospf_opaque.[c,h] as well as the OSPF API. This
+latter is mandatory for the implementation as it provides the Callback to
+Segment Routing functions (see below) when an Extended Link / Prefix or Router
+Information LSA s are received.
+
+Overview
+~~~~~~~~
+
+Following files where modified or added:
+
+* ospd_ri.[c,h] have been modified to add the new TLVs for Segment Routing.
+* ospf_ext.[c,h] implement RFC7684 as base support of Extended Link and Prefix
+ Opaque LSA.
+* ospf_sr.[c,h] implement the earth of Segment Routing. It adds a new Segment
+ Routing database to manage Segment Identifiers per Link and Prefix and
+ Segment Routing enable node, Callback functions to process incoming LSA and
+ install MPLS FIB entry through Zebra.
+
+The figure below shows the relation between the various files:
+
+* ospf_sr.c centralized all the Segment Routing processing. It receives Opaque
+ LSA Router Information (4.0.0.0) from ospf_ri.c and Extended Prefix
+ (7.0.0.X) Link (8.0.0.X) from ospf_ext.c. Once received, it parse TLVs and
+ SubTLVs and store information in SRDB (which is defined in ospf_sr.h). For
+ each received LSA, NHLFE is computed and send to Zebra to add/remove new
+ MPLS labels entries and FEC. New CLI configurations are also centralized in
+ ospf_sr.c. This CLI will trigger the flooding of new LSA Router Information
+ (4.0.0.0), Extended Prefix (7.0.0.X) and Link (8.0.0.X) by ospf_ri.c,
+ respectively ospf_ext.c.
+* ospf_ri.c send back to ospf_sr.c received Router Information LSA and update
+ Self Router Information LSA with paramters provided by ospf_sr.c i.e. SRGB
+ and MSD. It use ospf_opaque.c functions to send/received these Opaque LSAs.
+* ospf_ext.c send back to ospf_sr.c received Extended Prefix and Link Opaque
+ LSA and send self Extended Prefix and Link Opaque LSA through ospf_opaque.c
+ functions.
+
+::
+
+ +-----------+ +-------+
+ | | | |
+ | ospf_sr.c +-----+ SRDB |
+ +-----------+ +--+ | |
+ | +-^-------^-+ | +-------+
+ | | | | |
+ | | | | |
+ | | | | +--------+
+ | | | | |
+ +---v----------+ | | | +-----v-------+
+ | | | | | | |
+ | ospf_ri.c +--+ | +-------+ ospf_ext.c |
+ | LSA 4.0.0.0 | | | LSA 7.0.0.X |
+ | | | | LSA 8.0.0.X |
+ +---^----------+ | | |
+ | | +-----^-------+
+ | | |
+ | | |
+ | +--------v------------+ |
+ | | | |
+ | | ZEBRA: Labels + FEC | |
+ | | | |
+ | +---------------------+ |
+ | |
+ | |
+ | +---------------+ |
+ | | | |
+ +---------> ospf_opaque.c <---------+
+ | |
+ +---------------+
+
+ Figure 1: Overview of Segment Routing interaction
+
+Module interactions
+~~~~~~~~~~~~~~~~~~~
+
+To process incoming LSA, the code is based on the capability to call `hook()`
+functions when LSA are inserted or delete to / from the LSDB and the
+possibility to register particular treatment for Opaque LSA. The first point
+is provided by the OSPF API feature and the second by the Opaque implementation
+itself. Indeed, it is possible to register callback function for a given Opaque
+LSA ID (see `ospf_register_opaque_functab()` function defined in
+`ospf_opaque.c`). Each time a new LSA is added to the LSDB, the
+`new_lsa_hook()` function previously register for this LSA type is called. For
+Opaque LSA it is the `ospf_opaque_lsa_install_hook()`. For deletion, it is
+`ospf_opaque_lsa_delete_hook()`.
+
+Note that incoming LSA which is already present in the LSDB will be inserted
+after the old instance of this LSA remove from the LSDB. Thus, after the first
+time, each incoming LSA will trigger a `delete` following by an `install`. This
+is not very helpfull to handle real LSA deletion. In fact, LSA deletion is done
+by Flushing LSA i.e. flood LSA after seting its age to MAX_AGE. Then, a garbage
+function has the role to remove all LSA with `age == MAX_AGE` in the LSDB. So,
+to handle LSA Flush, the best is to look to the LSA age to determine if it is
+an installation or a future deletion i.e. the flushed LSA is first store in the
+LSDB with MAX_AGE waiting for the garbage collector function.
+
+Router Information LSAs
+^^^^^^^^^^^^^^^^^^^^^^^
+
+To activate Segment Routing, new CLI command `segment-routing on` has been
+introduced. When this command is activated, function
+`ospf_router_info_update_sr()` is called to indicate to Router Information
+process that Segment Routing TLVs must be flood. Same function is called to
+modify the Segment Routing Global Block (SRGB) and Maximum Stack Depth (MSD)
+TLV. Only Shortest Path First (SPF) Algorithm is supported, so no possiblity
+to modify this TLV is offer by the code.
+
+When Opaque LSA Tyep 4 i.e. Router Information are stored in LSDB, function
+`ospf_opaque_lsa_install_hook()` will call the previously registered function
+`ospf_router_info_lsa_update()`. In turn, the function will simply trigger
+`ospf_sr_ri_lsa_update()` or `ospf_sr_ri_lsa_delete` in function of the LSA
+age. Before, it verifies that the LSA Opaque Type is 4 (Router Information).
+Self Opaque LSA are not send back to the Segment Routing functions as
+information are already stored.
+
+Extended Link Prefix LSAs
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Like for Router Information, Segment Routing is activate at the Extended
+Link/Prefix level with new `segment-routing on` command. This trigger
+automtically the flooding of Extended Link LSA for all ospf interface where
+adjacency is full. For Extended Prefix LSA, the new CLI command
+`segment-routing prefix ...` will trigger the flooding of Prefix SID
+TLV/SubTLVs.
+
+When Opaque LSA Type 7 i.e. Extended Prefix and Type 8 i.e. Extended Link are
+store in the LSDB, `ospf_ext_pref_update_lsa()` respectively
+`ospf_ext_link_update_lsa()` are called like for Router Information LSA. In
+turn, they respectively trigger `ospf_sr_ext_prefix_lsa_update()` /
+`ospf_sr_ext_link_lsa_update()` or `ospf_sr_ext_prefix_lsa_delete()` /
+`ospf_sr_ext_link_lsa_delete()` if the LSA age is equal to MAX_AGE.
+
+Zebra
+^^^^^
+
+When a new MPLS entry or new Forwarding Equivalent Class (FEC) must be added or
+deleted in the data plane, `add_sid_nhlfe()` respectively `del_sid_nhlfe()` are
+called. Once check the validity of labels, they are send to ZEBRA layer through
+`ZEBRA_MPLS_LABELS_ADD` command, respectively `ZEBRA_MPLS_LABELS_DELETE`
+command for deletion. This is completed by a new labelled route through
+`ZEBRA_ROUTE_ADD` command, respectively `ZEBRA_ROUTE_DELETE` command.
+
+Configuration
+-------------
+
+Linux Kernel
+~~~~~~~~~~~~
+
+In order to use OSPF Segment Routing, you must setup MPLS data plane. Up to
+know, only Linux Kernel version >= 4.5 is supported.
+
+First, the MPLS modules aren't loaded by default, so you'll need to load them
+yourself:
+
+::
+
+ modprobe mpls_router
+ modprobe mpls_gso
+ modprobe mpls_iptunnel
+
+Then, you must activate MPLS on the interface you would used:
+
+::
+
+ sysctl -w net.mpls.conf.enp0s9.input=1
+ sysctl -w net.mpls.conf.lo.input=1
+ sysctl -w net.mpls.platform_labels=1048575
+
+The last line fix the maximum MPLS label value.
+
+Once OSPFd start with Segment Routing, you could check that MPLS routes are
+enable with:
+
+::
+
+ ip -M route
+ ip route
+
+The first command show the MPLS LFIB table while the second show the FIB
+table which contains route with MPLS label encapsulation.
+
+If you disable Penultimate Hop Popping with the `no-php-flag` (see below), you
+MUST check that RP filter is not enable for the interface you intend to use,
+especially the `lo` one. For that purpose, disable RP filtering with:
+
+::
+
+ systcl -w net.ipv4.conf.all.rp_filter=0
+ sysctl -w net.ipv4.conf.lo.rp_filter=0
+
+OSPFd
+~~~~~
+
+Here it is a simple example of configuration to enable Segment Routing. Note
+that `opaque capability` and `router information` must be set to activate
+Opaque LSA prior to Segment
+Routing.
+
+::
+
+ router ospf
+ ospf router-id 192.168.1.11
+ capability opaque
+ mpls-te on
+ mpls-te router-address 192.168.1.11
+ router-info area 0.0.0.0
+ segment-routing on
+ segment-routing global-block 10000 19999
+ segment-routing node-msd 8
+ segment-routing prefix 192.168.1.11/32 index 1100
+
+The first segment-routing statement enable it. The Second one set the SRGB,
+third line the MSD and finally, set the Prefix SID index for a given prefix.
+Note that only prefix of Loopback interface could be configured with a Prefix
+SID. It is possible to add `no-php-flag` at the end of the prefix command to
+disbale Penultimate Hop Popping. This advertises peers that they MUST NOT pop
+the MPLS label prior to sending the packet.
+
+Known limitations
+-----------------
+
+* Runs only within default VRF
+* Only single Area is supported. ABR is not yet supported
+* Only SPF algorithm is supported
+* Extended Prefix Range is not supported
+* MPLS table are not flush at startup. Thus, restarting zebra process is
+ mandatory to remove old MPLS entries in the data plane after a crash of
+ ospfd daemon
+* Due to a bug in OSPF Opaque, LSA are not flood when enable Segment Routing
+ through CLI once OSPFd started. You must configure Segment Routing within
+ configuration file before launching OSPFd
+* With NO Penultimate Hop Popping, it is not possible to express a Segment
+ Path with an Adjacency SID due to the impossibility for the Linux Kernel to
+ perform double POP instruction.
+
+Credits
+-------
+
+* Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+* Author: Olivier Dugeon <olivier.dugeon@orange.com>
+* Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com
+
+This work has been performed in the framework of the H2020-ICT-2014
+project 5GEx (Grant Agreement no. 671636), which is partially funded
+by the European Commission.
+
+
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 information
+@cite{RFC4364} and @cite{RFC4659}, and Encapsulation attribute
@cite{RFC5512} is supported.
-@deffn {Command} {show ip bgp vpnv4 all} {}
-@deffnx {Command} {show ipv6 bgp vpn all} {}
+@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 ip bgp encap all} {}
-@deffnx {Command} {show ipv6 bgp encap all} {}
-Print active IPV4 or IPV6 routes advertised via the Encapsulation SAFI.
-@end deffn
-
-@deffn {Command} {show bgp ipv4 encap summary} {}
-@deffnx {Command} {show bgp ipv4 vpn summary} {}
-@deffnx {Command} {show bgp ipv6 encap summary} {}
+@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
@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
!
interface eth0
ip address 10.2.2.2/24
- mpls-te on
- mpls-te link metric 10
- mpls-te link max-bw 1.25e+06
- mpls-te link max-rsv-bw 1.25e+06
- mpls-te link unrsv-bw 0 1.25e+06
- mpls-te link unrsv-bw 1 1.25e+06
- mpls-te link unrsv-bw 2 1.25e+06
- mpls-te link unrsv-bw 3 1.25e+06
- mpls-te link unrsv-bw 4 1.25e+06
- mpls-te link unrsv-bw 5 1.25e+06
- mpls-te link unrsv-bw 6 1.25e+06
- mpls-te link unrsv-bw 7 1.25e+06
- mpls-te link rsc-clsclr 0xab
+ 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
- mpls-te on
- mpls-te link metric 10
- mpls-te link max-bw 1.25e+06
- mpls-te link max-rsv-bw 1.25e+06
- mpls-te link unrsv-bw 0 1.25e+06
- mpls-te link unrsv-bw 1 1.25e+06
- mpls-te link unrsv-bw 2 1.25e+06
- mpls-te link unrsv-bw 3 1.25e+06
- mpls-te link unrsv-bw 4 1.25e+06
- mpls-te link unrsv-bw 5 1.25e+06
- mpls-te link unrsv-bw 6 1.25e+06
- mpls-te link unrsv-bw 7 1.25e+06
- mpls-te link rsc-clsclr 0xab
- mpls-te neighbor 10.1.1.2 as 65000
+ 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
--- /dev/null
+.\" This file was originally generated by help2man 1.44.1.
+.TH MTRACEBIS "8" "February 2018" "mtracebis 0.1" "System Administration Utilities"
+.SH NAME
+mtracebis \- a program to initiate multicast traceroute "mtrace" queries
+.SH SYNOPSIS
+mtracebis
+<multicast source>
+.SH DESCRIPTION
+.B mtracebis
+is a program used to test multicast connectivity in a multicast and multicast
+traceroute enabled IP network.
+.PP
+Initial version of the program requires multicast source IP address and
+initiates a weak traceroute across the network. This tests whether the
+interfaces towards the source are multicast enabled. First query sent is a
+full query, capable of crossing the network all the way to the source. If this
+fails, hop-by-hop queries are initiated.
+.PP
+Hop-by-hop queries start by requesting only a response from the nearest router.
+Following that, next query is extended to the next two routers, and so on...
+until a set of routers is tested for connectivity.
+.SH SEE ALSO
+See the project homepage at <@PACKAGE_URL@>.
+.SH AUTHORS
+Copyright 2018 Mladen Sablic
* Opaque LSA::
* OSPF Traffic Engineering::
* Router Information::
+* Segment Routing::
* Debugging OSPF::
* OSPF Configuration Examples::
@end menu
Show Router Capabilities PCE parameters.
@end deffn
+@node Segment Routing
+@section Segment Routing
+
+This is an EXPERIMENTAL support of Segment Routing as per draft
+ draft-ietf-ospf-segment-routing-extensions-24.txt for MPLS dataplane.
+
+@deffn {OSPF Command} {segment-routing on} {}
+@deffnx {OSPF Command} {no segment-routing} {}
+Enable Segment Routing. Even if this also activate routing information support,
+it is preferable to also activate routing information, and set accordingly the
+Area or AS flooding.
+@end deffn
+
+@deffn {OSPF Command} {segment-routing global-block (0-1048575) (0-1048575)} {}
+@deffnx {OSPF Command} {no segment-routing global-block} {}
+Fix the Segment Routing Global Block i.e. the label range used by MPLS to store
+label in the MPLS FIB.
+@end deffn
+
+@deffn {OSPF Command} {segment-routing node-msd (1-16)} {}
+@deffnx {OSPF Command} {no segment-routing node-msd} {}
+Fix the Maximum Stack Depth supported by the router. The value depend of the
+MPLS dataplane. E.g. for Linux kernel, since version 4.13 it is 32.
+@end deffn
+
+@deffn {OSPF Command} {segment-routing prefix A.B.C.D/M index (0-65535)} {}
+@deffnx {OSPF Command} {segment-routing prefix A.B.C.D/M index (0-65535) no-php-flag} {}
+@deffnx {OSPF Command} {no segment-routing prefix A.B.C.D/M} {}
+Set the Segment Rounting index for the specifyed prefix. Note
+that, only prefix with /32 corresponding to a loopback interface are
+currently supported. The 'no-php-flag' means NO Penultimate Hop Popping that
+allows SR node to request to its neighbor to not pop the label.
+@end deffn
+
+@deffn {Command} {show ip ospf database segment-routing} {}
+@deffnx {Command} {show ip ospf database segment-routing adv-router @var{adv-router}} {}
+@deffnx {Command} {show ip ospf database segment-routing self-originate} {}
+Show Segment Routing Data Base, all SR nodes, specific advertized router or self router.
+@end deffn
+
@node Debugging OSPF
@section Debugging OSPF
!
interface eth0
ip address 198.168.1.1/24
- mpls-te on
- mpls-te link metric 10
- mpls-te link max-bw 1.25e+06
- mpls-te link max-rsv-bw 1.25e+06
- mpls-te link unrsv-bw 0 1.25e+06
- mpls-te link unrsv-bw 1 1.25e+06
- mpls-te link unrsv-bw 2 1.25e+06
- mpls-te link unrsv-bw 3 1.25e+06
- mpls-te link unrsv-bw 4 1.25e+06
- mpls-te link unrsv-bw 5 1.25e+06
- mpls-te link unrsv-bw 6 1.25e+06
- mpls-te link unrsv-bw 7 1.25e+06
- mpls-te link rsc-clsclr 0xab
+ 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
- mpls-te on
- mpls-te link metric 10
- mpls-te link max-bw 1.25e+06
- mpls-te link max-rsv-bw 1.25e+06
- mpls-te link unrsv-bw 0 1.25e+06
- mpls-te link unrsv-bw 1 1.25e+06
- mpls-te link unrsv-bw 2 1.25e+06
- mpls-te link unrsv-bw 3 1.25e+06
- mpls-te link unrsv-bw 4 1.25e+06
- mpls-te link unrsv-bw 5 1.25e+06
- mpls-te link unrsv-bw 6 1.25e+06
- mpls-te link unrsv-bw 7 1.25e+06
- mpls-te link rsc-clsclr 0xab
- mpls-te neighbor 192.168.2.2 as 65000
+ 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
--- /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
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
@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
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 }. Both the Encapsulation Subsequent Address Family Identifier
-(SAFI) and the Tunnel Encapsulation Attribute, @cite{RFC5512, The BGP
+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.
@node General VNC Configuration
@subsection General VNC Configuration
-@deffn {VNC} {vnc advertise-un-method encap-safi|encap-attr} {}
-Advertise NVE underlay-network IP addresses using the encapsulation SAFI
-(@code{encap-safi}) or the UN address sub-TLV of the Tunnel Encapsulation attribute
-(@code{encap-attr}). When @code{encap-safi} is used, neighbors under
-@code{address-family encap} and/or @code{address-family encapv6} must be
-configured. The default is @code{encap-attr}.
+@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
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 and @w{ENCAP} @w{SAFI} (when @code{vnc
-advertise-un-method encap-safi} is set) routes must have
+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}.
neighbor 192.168.1.101 remote-as 64512
neighbor 192.168.1.102 remote-as 64512
- address-family vpnv4
+ address-family ipv4 vpn
neighbor 192.168.1.101 activate
neighbor 192.168.1.102 activate
exit-address-family
neighbor 192.168.1.100 remote-as 64512
neighbor 192.168.1.102 remote-as 64512
- address-family vpnv4
+ address-family ipv4 vpn
neighbor 192.168.1.100 activate
neighbor 192.168.1.102 activate
exit-address-family
neighbor 192.168.1.101 remote-as 64512
neighbor 192.168.1.102 remote-as 64512
- address-family vpnv4
+ address-family ipv4 vpn
neighbor 192.168.1.100 activate
neighbor 192.168.1.101 activate
exit-address-family
neighbor 172.16.2.2 route-reflector-client
exit-address-family
!
- address-family vpnv4 unicast
+ address-family ipv4 vpn
neighbor 192.168.1.102 activate
neighbor 192.168.1.103 activate
neighbor 192.168.1.104 activate
no neighbor 192.168.1.103 activate
exit-address-family
!
- address-family vpnv4 unicast
+ address-family ipv4 vpn
neighbor 192.168.1.101 activate
neighbor 192.168.1.102 activate
neighbor 192.168.1.103 activate
neighbor 192.168.1.102 route-reflector-client
exit-address-family
- address-family vpnv4
+ address-family ipv4 vpn
neighbor 192.168.1.101 activate
neighbor 192.168.1.102 activate
neighbor 192.168.1.100 remote-as 64512
- address-family vpnv4
+ address-family ipv4 vpn
neighbor 192.168.1.100 activate
exit-address-family
neighbor 192.168.1.100 remote-as 64512
- address-family vpnv4
+ address-family ipv4 vpn
neighbor 192.168.1.100 activate
exit-address-family
neighbor 192.168.1.100 remote-as 64512
- address-family vpnv4
+ address-family ipv4 vpn
neighbor 192.168.1.100 activate
exit-address-family
neighbor 192.168.1.100 remote-as 64512
- address-family vpnv4
+ address-family ipv4 vpn
neighbor 192.168.1.100 activate
exit-address-family
neighbor 192.168.1.102 description iBGP-client-192-168-1-102
neighbor 192.168.1.102 route-reflector-client
- address-family vpnv4
+ 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.100 remote-as 64512
neighbor 192.168.1.104 remote-as 64512
- address-family vpnv4
+ address-family ipv4 vpn
neighbor 192.168.1.100 activate
neighbor 192.168.1.104 activate
exit-address-family
neighbor 192.168.1.100 remote-as 64512
neighbor 192.168.1.104 remote-as 64512
- address-family vpnv4
+ address-family ipv4 vpn
neighbor 192.168.1.100 activate
neighbor 192.168.1.104 activate
exit-address-family
if (count >= MULTIPATH_NUM)
break;
api_nh = &api.nexthops[count];
+ api_nh->vrf_id = VRF_DEFAULT;
if (te->adv_router->src.s_addr) {
api_nh->gate.ipv4 = te->adv_router->src;
api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
# find all DEFUNs
defun_re = re.compile(
- r'^(DEF(UN(_NOSH|_HIDDEN)?|PY)\s*\(.*?)^(?=\s*\{)',
+ r'^((DEF(UN(_NOSH|_HIDDEN)?|PY)|ALIAS)\s*\(.*?)^(?=\s*\{)',
re.M | re.S)
define_re = re.compile(
r'((^#\s*define[^\n]+[^\\]\n)+)',
* 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 */
if (ip) {
listnode_delete(circuit->ip_addrs, ip);
+ prefix_ipv4_free(ip);
if (circuit->area)
lsp_regenerate_schedule(circuit->area,
circuit->is_type, 0);
}
if (ip6) {
listnode_delete(circuit->ipv6_link, ip6);
+ prefix_ipv6_free(ip6);
found = 1;
}
} else {
}
if (ip6) {
listnode_delete(circuit->ipv6_non_link, ip6);
+ prefix_ipv6_free(ip6);
found = 1;
}
}
void isis_circuit_prepare(struct isis_circuit *circuit)
{
-#ifdef GNU_LINUX
+#if ISIS_METHOD != ISIS_METHOD_DLPI
thread_add_read(master, isis_receive, circuit, circuit->fd,
&circuit->t_read);
#else
struct isis_area *area = circuit->area;
bool change = circuit->ip_router != ip_router
|| circuit->ipv6_router != ipv6_router;
- bool was_enabled = !!circuit->area;
area->ip_circuits += ip_router - circuit->ip_router;
area->ipv6_circuits += ipv6_router - circuit->ipv6_router;
if (!ip_router && !ipv6_router)
isis_csm_state_change(ISIS_DISABLE, circuit, area);
- else if (!was_enabled)
- isis_csm_state_change(ISIS_ENABLE, circuit, area);
else
lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
}
(lsp->level == IS_LEVEL_1) ? L1_LINK_STATE : L2_LINK_STATE;
struct isis_lsp_hdr *hdr = &lsp->hdr;
struct stream *stream = lsp->pdu;
- size_t orig_getp, orig_endp;
+ size_t orig_getp = 0, orig_endp = 0;
if (keep) {
orig_getp = stream_get_getp(lsp->pdu);
bool sane = true;
for (uint8_t i = 0; i < tlv_len; i++) {
if ((unsigned char)tlvs->hostname[i] > 127
- || !isprint(tlvs->hostname[i])) {
+ || !isprint((int)tlvs->hostname[i])) {
sane = false;
tlvs->hostname[i] = '?';
}
if (count >= MULTIPATH_NUM)
break;
api_nh = &api.nexthops[count];
+ api_nh->vrf_id = VRF_DEFAULT;
/* FIXME: can it be ? */
if (nexthop->ip.s_addr != INADDR_ANY) {
api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
}
api_nh = &api.nexthops[count];
+ api_nh->vrf_id = VRF_DEFAULT;
api_nh->gate.ipv6 = nexthop6->ip6;
api_nh->ifindex = nexthop6->ifindex;
api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
/* do not accept invalid labels */
if (label > MPLS_LABEL_MAX ||
(label <= MPLS_LABEL_RESERVED_MAX &&
- label != MPLS_LABEL_IPV4NULL &&
- label != MPLS_LABEL_IPV6NULL &&
- label != MPLS_LABEL_IMPLNULL)) {
+ label != MPLS_LABEL_IPV4_EXPLICIT_NULL &&
+ label != MPLS_LABEL_IPV6_EXPLICIT_NULL &&
+ label != MPLS_LABEL_IMPLICIT_NULL)) {
session_shutdown(nbr, S_BAD_TLV_VAL,
msg.id, msg.type);
goto err;
case MAP_TYPE_PREFIX:
switch (me->map.fec.prefix.af) {
case AF_INET:
- if (label == MPLS_LABEL_IPV6NULL) {
+ if (label == MPLS_LABEL_IPV6_EXPLICIT_NULL) {
session_shutdown(nbr, S_BAD_TLV_VAL,
msg.id, msg.type);
goto err;
goto next;
break;
case AF_INET6:
- if (label == MPLS_LABEL_IPV4NULL) {
+ if (label == MPLS_LABEL_IPV4_EXPLICIT_NULL) {
session_shutdown(nbr, S_BAD_TLV_VAL,
msg.id, msg.type);
goto err;
switch (fn->fec.type) {
case FEC_TYPE_IPV4:
if (!(ldeconf->ipv4.flags & F_LDPD_AF_EXPNULL))
- return (MPLS_LABEL_IMPLNULL);
+ return (MPLS_LABEL_IMPLICIT_NULL);
if (lde_acl_check(ldeconf->ipv4.acl_label_expnull_for,
AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix,
fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT)
- return (MPLS_LABEL_IMPLNULL);
- return (MPLS_LABEL_IPV4NULL);
+ return (MPLS_LABEL_IMPLICIT_NULL);
+ return MPLS_LABEL_IPV4_EXPLICIT_NULL;
case FEC_TYPE_IPV6:
if (!(ldeconf->ipv6.flags & F_LDPD_AF_EXPNULL))
- return (MPLS_LABEL_IMPLNULL);
+ return (MPLS_LABEL_IMPLICIT_NULL);
if (lde_acl_check(ldeconf->ipv6.acl_label_expnull_for,
AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix,
fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT)
- return (MPLS_LABEL_IMPLNULL);
- return (MPLS_LABEL_IPV6NULL);
+ return (MPLS_LABEL_IMPLICIT_NULL);
+ return MPLS_LABEL_IPV6_EXPLICIT_NULL;
default:
fatalx("lde_update_label: unexpected fec type");
break;
/* explicitly withdraw all null labels */
RB_FOREACH(ln, nbr_tree, &lde_nbrs) {
- lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IMPLNULL);
+ lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IMPLICIT_NULL);
if (ln->v4_enabled)
- lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV4NULL);
+ lde_send_labelwithdraw_wcard(
+ ln,
+ MPLS_LABEL_IPV4_EXPLICIT_NULL);
if (ln->v6_enabled)
- lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV6NULL);
+ lde_send_labelwithdraw_wcard(
+ ln,
+ MPLS_LABEL_IPV6_EXPLICIT_NULL);
}
/* update label of connected routes */
case NO_LABEL:
snprintf(buf, TF_LEN, "-");
break;
- case MPLS_LABEL_IMPLNULL:
+ case MPLS_LABEL_IMPLICIT_NULL:
snprintf(buf, TF_LEN, "imp-null");
break;
- case MPLS_LABEL_IPV4NULL:
- case MPLS_LABEL_IPV6NULL:
+ case MPLS_LABEL_IPV4_EXPLICIT_NULL:
+ case MPLS_LABEL_IPV6_EXPLICIT_NULL:
snprintf(buf, TF_LEN, "exp-null");
break;
default:
"ldp l2vpn", // LDP_L2VPN_NODE,
"ldp", // LDP_PSEUDOWIRE_NODE,
"isis", // ISIS_NODE,
- "pim", // PIM_NODE,
"masc", // MASC_NODE,
"irdp", // IRDP_NODE,
"static ip", // IP_NODE,
case KEYCHAIN_NODE:
case MASC_NODE:
case RMAP_NODE:
- case PIM_NODE:
case VTY_NODE:
vty->node = CONFIG_NODE;
break;
case KEYCHAIN_NODE:
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
- case PIM_NODE:
case VTY_NODE:
case LINK_PARAMS_NODE:
vty_config_unlock(vty);
return CMD_SUCCESS;
}
- if (!isalnum(argv[idx_8]->arg[0])) {
+ if (!isalnum((int)argv[idx_8]->arg[0])) {
vty_out(vty,
"Please specify string starting with alphanumeric\n");
return CMD_WARNING_CONFIG_FAILED;
}
}
- if (!isalnum(argv[idx_8]->arg[0])) {
+ if (!isalnum((int)argv[idx_8]->arg[0])) {
vty_out(vty,
"Please specify string starting with alphanumeric\n");
return CMD_WARNING_CONFIG_FAILED;
LDP_L2VPN_NODE, /* LDP L2VPN node */
LDP_PSEUDOWIRE_NODE, /* LDP Pseudowire node */
ISIS_NODE, /* ISIS protocol mode */
- PIM_NODE, /* PIM protocol mode */
MASC_NODE, /* MASC for multicast. */
IRDP_NODE, /* ICMP Router Discovery Protocol mode. */
IP_NODE, /* Static ip route node. */
#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 OSPF_RI_STR "OSPF Router Information specific commands\n"
#define PCE_STR "PCE Router Information specific commands\n"
#define MPLS_STR "MPLS information\n"
+#define SR_STR "Segment-Routing specific commands\n"
#define WATCHFRR_STR "watchfrr information\n"
#define ZEBRA_STR "Zebra information\n"
token->varname[i] = '_';
break;
default:
- token->varname[i] = tolower(varname[i]);
+ token->varname[i] = tolower((int)varname[i]);
}
token->varname[len] = '\0';
}
*/
%{
-/* ignore harmless bug in old versions of flex */
+/* ignore harmless bugs in old versions of flex */
#pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
#include "command_parse.h"
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++)
* code documentation in it.
*/
-/* ignore harmless bug in old versions of flex */
+/* ignore harmless bugs in old versions of flex */
#pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wunused-value"
#include "config.h"
#include <Python.h>
/*
- * 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
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);
argv[idx_number_3]->arg);
}
+DEFUN (no_accept_lifetime,
+ no_accept_lifetime_cmd,
+ "no accept-lifetime",
+ NO_STR
+ "Unset accept-lifetime\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(key, key);
+
+ if (key->accept.start)
+ key->accept.start = 0;
+ if (key->accept.end)
+ key->accept.end = 0;
+ if (key->accept.duration)
+ key->accept.duration = 0;
+
+ return CMD_SUCCESS;
+}
+
DEFUN (send_lifetime_day_month_day_month,
send_lifetime_day_month_day_month_cmd,
"send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)",
argv[idx_number_3]->arg);
}
+DEFUN (no_send_lifetime,
+ no_send_lifetime_cmd,
+ "no send-lifetime",
+ NO_STR
+ "Unset send-lifetime\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(key, key);
+
+ if (key->send.start)
+ key->send.start = 0;
+ if (key->send.end)
+ key->send.end = 0;
+ if (key->send.duration)
+ key->send.duration = 0;
+
+ return CMD_SUCCESS;
+}
+
static struct cmd_node keychain_node = {KEYCHAIN_NODE, "%s(config-keychain)# ",
1};
&accept_lifetime_duration_day_month_cmd);
install_element(KEYCHAIN_KEY_NODE,
&accept_lifetime_duration_month_day_cmd);
+ install_element(KEYCHAIN_KEY_NODE,
+ &no_accept_lifetime_cmd);
install_element(KEYCHAIN_KEY_NODE,
&send_lifetime_day_month_day_month_cmd);
&send_lifetime_duration_day_month_cmd);
install_element(KEYCHAIN_KEY_NODE,
&send_lifetime_duration_month_day_cmd);
+ install_element(KEYCHAIN_KEY_NODE,
+ &no_send_lifetime_cmd);
}
DESC_ENTRY(ZEBRA_VRF_UNREGISTER),
DESC_ENTRY(ZEBRA_VRF_ADD),
DESC_ENTRY(ZEBRA_VRF_DELETE),
+ DESC_ENTRY(ZEBRA_VRF_LABEL),
DESC_ENTRY(ZEBRA_INTERFACE_VRF_UPDATE),
DESC_ENTRY(ZEBRA_BFD_CLIENT_REGISTER),
DESC_ENTRY(ZEBRA_INTERFACE_ENABLE_RADV),
DESC_ENTRY(ZEBRA_RELEASE_LABEL_CHUNK),
DESC_ENTRY(ZEBRA_ADVERTISE_ALL_VNI),
DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW),
+ DESC_ENTRY(ZEBRA_ADVERTISE_SUBNET),
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),
DESC_ENTRY(ZEBRA_MACIP_DEL),
+ DESC_ENTRY(ZEBRA_IP_PREFIX_ROUTE_ADD),
+ DESC_ENTRY(ZEBRA_IP_PREFIX_ROUTE_DEL),
DESC_ENTRY(ZEBRA_REMOTE_MACIP_ADD),
DESC_ENTRY(ZEBRA_REMOTE_MACIP_DEL),
DESC_ENTRY(ZEBRA_PW_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>
#include <arpa/inet.h>
+#ifdef MPLS_LABEL_MAX
+#undef MPLS_LABEL_MAX
+#endif
+
/* Well-known MPLS label values (RFC 3032 etc). */
-#define MPLS_V4_EXP_NULL_LABEL 0
-#define MPLS_RA_LABEL 1
-#define MPLS_V6_EXP_NULL_LABEL 2
-#define MPLS_IMP_NULL_LABEL 3
-#define MPLS_ENTROPY_LABEL_INDICATOR 7
-#define MPLS_GAL_LABEL 13
-#define MPLS_OAM_ALERT_LABEL 14
-#define MPLS_EXTENSION_LABEL 15
+#define MPLS_LABEL_IPV4_EXPLICIT_NULL 0 /* [RFC3032] */
+#define MPLS_LABEL_ROUTER_ALERT 1 /* [RFC3032] */
+#define MPLS_LABEL_IPV6_EXPLICIT_NULL 2 /* [RFC3032] */
+#define MPLS_LABEL_IMPLICIT_NULL 3 /* [RFC3032] */
+#define MPLS_LABEL_ELI 7 /* [RFC6790] */
+#define MPLS_LABEL_GAL 13 /* [RFC5586] */
+#define MPLS_LABEL_OAM_ALERT 14 /* [RFC3429] */
+#define MPLS_LABEL_EXTENSION 15 /* [RFC7274] */
+#define MPLS_LABEL_MAX 1048575
+#define MPLS_LABEL_NONE 0xFFFFFFFF /* for internal use only */
/* Minimum and maximum label values */
-#define MPLS_MIN_RESERVED_LABEL 0
-#define MPLS_MAX_RESERVED_LABEL 15
-#define MPLS_MIN_UNRESERVED_LABEL 16
-#define MPLS_MAX_UNRESERVED_LABEL 1048575
+#define MPLS_LABEL_RESERVED_MIN 0
+#define MPLS_LABEL_RESERVED_MAX 15
+#define MPLS_LABEL_UNRESERVED_MIN 16
+#define MPLS_LABEL_UNRESERVED_MAX 1048575
/* Default min and max SRGB label range */
-#define MPLS_DEFAULT_MIN_SRGB_LABEL 16000
-#define MPLS_DEFAULT_MAX_SRGB_LABEL 23999
+/* Even if the SRGB allows to manage different Label space between routers,
+ * if an operator want to use the same SRGB for all its router, we must fix
+ * a common range. However, Cisco start its SRGB at 16000 and Juniper ends
+ * its SRGB at 16384 for OSPF. Thus, by fixing the minimum SRGB label to
+ * 8000 we could deal with both Cisco and Juniper.
+ */
+#define MPLS_DEFAULT_MIN_SRGB_LABEL 8000
+#define MPLS_DEFAULT_MAX_SRGB_LABEL 50000
+#define MPLS_DEFAULT_MIN_SRGB_SIZE 5000
+#define MPLS_DEFAULT_MAX_SRGB_SIZE 20000
/* Maximum # labels that can be pushed. */
#define MPLS_MAX_LABELS 16
#define IS_MPLS_RESERVED_LABEL(label) \
- (label >= MPLS_MIN_RESERVED_LABEL && label <= MPLS_MAX_RESERVED_LABEL)
+ (label >= MPLS_LABEL_RESERVED_MIN && label <= MPLS_LABEL_RESERVED_MAX)
#define IS_MPLS_UNRESERVED_LABEL(label) \
- (label >= MPLS_MIN_UNRESERVED_LABEL \
- && label <= MPLS_MAX_UNRESERVED_LABEL)
+ (label >= MPLS_LABEL_UNRESERVED_MIN \
+ && label <= MPLS_LABEL_UNRESERVED_MAX)
/* Definitions for a MPLS label stack entry (RFC 3032). This encodes the
* label, EXP, BOS and TTL fields.
/* 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
ZEBRA_LSP_NONE = 0, /* No LSP. */
ZEBRA_LSP_STATIC = 1, /* Static LSP. */
ZEBRA_LSP_LDP = 2, /* LDP LSP. */
- ZEBRA_LSP_BGP = 3 /* BGP LSP. */
+ ZEBRA_LSP_BGP = 3, /* BGP LSP. */
+ ZEBRA_LSP_SR = 4, /* Segment Routing LSP. */
+ ZEBRA_LSP_SHARP = 5, /* Identifier for test protocol */
};
/* Functions for basic label operations. */
static inline char *label2str(mpls_label_t label, char *buf, size_t len)
{
switch (label) {
- case MPLS_V4_EXP_NULL_LABEL:
+ case MPLS_LABEL_IPV4_EXPLICIT_NULL:
strlcpy(buf, "IPv4 Explicit Null", len);
return (buf);
- case MPLS_RA_LABEL:
+ case MPLS_LABEL_ROUTER_ALERT:
strlcpy(buf, "Router Alert", len);
return (buf);
- case MPLS_V6_EXP_NULL_LABEL:
+ case MPLS_LABEL_IPV6_EXPLICIT_NULL:
strlcpy(buf, "IPv6 Explict Null", len);
return (buf);
- case MPLS_IMP_NULL_LABEL:
+ case MPLS_LABEL_IMPLICIT_NULL:
strlcpy(buf, "implicit-null", len);
return (buf);
- case MPLS_ENTROPY_LABEL_INDICATOR:
+ case MPLS_LABEL_ELI:
strlcpy(buf, "Entropy Label Indicator", len);
return (buf);
- case MPLS_GAL_LABEL:
+ case MPLS_LABEL_GAL:
strlcpy(buf, "Generic Associated Channel", len);
return (buf);
- case MPLS_OAM_ALERT_LABEL:
+ case MPLS_LABEL_OAM_ALERT:
strlcpy(buf, "OAM Alert", len);
return (buf);
- case MPLS_EXTENSION_LABEL:
+ case MPLS_LABEL_EXTENSION:
strlcpy(buf, "Extension", len);
return (buf);
default:
}
}
-/* constants used by ldpd */
-#define MPLS_LABEL_IPV4NULL 0 /* IPv4 Explicit NULL Label */
-#define MPLS_LABEL_RTALERT 1 /* Router Alert Label */
-#define MPLS_LABEL_IPV6NULL 2 /* IPv6 Explicit NULL Label */
-#define MPLS_LABEL_IMPLNULL 3 /* Implicit NULL Label */
- /* MPLS_LABEL_RESERVED 4-15 */ /* Values 4-15 are reserved */
-#define MPLS_LABEL_RESERVED_MAX 15
-#define MPLS_LABEL_MAX ((1 << 20) - 1)
#endif
*/
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;
for (nh1 = nh; nh1; nh1 = nh1->next) {
nexthop = nexthop_new();
+ nexthop->vrf_id = nh1->vrf_id;
nexthop->ifindex = nh1->ifindex;
nexthop->type = nh1->type;
nexthop->flags = nh1->flags;
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;
struct nexthop *prev;
+ /*
+ * What vrf is this nexthop associated with?
+ */
+ vrf_id_t vrf_id;
+
/* Interface index. */
ifindex_t ifindex;
#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
(nexthop); \
(nexthop) = nexthop_next(nexthop)
-extern int zebra_rnh_ip_default_route;
-extern int zebra_rnh_ipv6_default_route;
-
-static inline int nh_resolve_via_default(int family)
-{
- if (((family == AF_INET) && zebra_rnh_ip_default_route)
- || ((family == AF_INET6) && zebra_rnh_ipv6_default_route))
- return 1;
- else
- return 0;
-}
-
struct nexthop *nexthop_new(void);
void nexthop_add(struct nexthop **target, struct nexthop *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",
#define ETH_ALEN 6
#endif
-/* for compatibility */
-#ifdef ETHER_ADDR_LEN
-#undef ETHER_ADDR_LEN
-#endif
-#define ETHER_ADDR_LEN 6 CPP_WARN("ETHER_ADDR_LEN is being replaced by ETH_ALEN.\\n")
-
#define ETHER_ADDR_STRLEN (3*ETH_ALEN)
/*
* there isn't a portable ethernet address type. We define our
#define IPV4_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000)
#define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000)
#define IPV4_CLASS_DE(a) ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000)
+#define IPV4_MC_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffffff00) == 0xe0000000)
/* Max bit/byte length of IPv6 address. */
#define IPV6_MAX_BYTELEN 16
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 */
}
/* remove leading spaces */
for (i = j = 0; i < csv_field_len(fld); i++) {
- if (!isspace(hdr[i])) {
+ if (!isspace((int)hdr[i])) {
client_name[j] = hdr[i];
j++;
}
{
int rc, len;
char client_name[32];
- int cmd_id, type, ver, msglen;
+ int cmd_id = 0, type = 0, ver = 0, msglen = 0;
csv_t *csv;
ptm_lib_msg_ctxt_t *p_ctxt = NULL;
--- /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_ */
size_t offset = strlen(timebuf);
snprintf(timebuf + offset, sizeof(timebuf) - offset, ".%ld",
- tv->tv_usec);
+ (long int)tv->tv_usec);
return timebuf;
}
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)
return vrf;
}
-/* Delete a VRF. This is called in vrf_terminate(). */
+/* Delete a VRF. This is called when the underlying VRF goes away, a
+ * pre-configured VRF is deleted or when shutting down (vrf_terminate()).
+ */
void vrf_delete(struct vrf *vrf)
{
if (debug_vrf)
if (vrf_is_enabled(vrf))
vrf_disable(vrf);
+ /* If the VRF is user configured, it'll stick around, just remove
+ * the ID mapping. Interfaces assigned to this VRF should've been
+ * removed already as part of the VRF going down.
+ */
+ if (vrf_is_user_cfged(vrf)) {
+ if (vrf->vrf_id != VRF_UNKNOWN) {
+ /* Delete any VRF interfaces - should be only
+ * the VRF itself, other interfaces should've
+ * been moved out of the VRF.
+ */
+ if_terminate(vrf);
+ RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf);
+ vrf->vrf_id = VRF_UNKNOWN;
+ }
+ return;
+ }
+
if (vrf_master.vrf_delete_hook)
(*vrf_master.vrf_delete_hook)(vrf);
return (RB_FIND(vrf_id_head, &vrfs_by_id, &vrf));
}
-/*
- * Check whether the VRF is enabled.
- */
-static int vrf_is_enabled(struct vrf *vrf)
-{
- return vrf && CHECK_FLAG(vrf->status, VRF_ACTIVE);
-}
-
/*
* Enable a VRF - that is, let the VRF be ready to use.
* The VRF_ENABLE_HOOK callback will be called to inform
(*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));
}
}
zlog_debug("%s: Shutting down vrf subsystem",
__PRETTY_FUNCTION__);
- while ((vrf = RB_ROOT(vrf_id_head, &vrfs_by_id)) != NULL)
+ while ((vrf = RB_ROOT(vrf_id_head, &vrfs_by_id)) != NULL) {
+ /* Clear configured flag and invoke delete. */
+ UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
- while ((vrf = RB_ROOT(vrf_name_head, &vrfs_by_name)) != NULL)
+ }
+ while ((vrf = RB_ROOT(vrf_name_head, &vrfs_by_name)) != NULL) {
+ /* Clear configured flag and invoke delete. */
+ UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
+ }
}
/* Create a socket for the VRF. */
return CMD_WARNING_CONFIG_FAILED;
}
+ /* Clear configured flag and invoke delete. */
+ UNSET_FLAG(vrfp->status, VRF_CONFIGURED);
vrf_delete(vrfp);
return CMD_SUCCESS;
/* 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
/* Zebra internal VRF status */
u_char status;
-#define VRF_ACTIVE (1 << 0)
+#define VRF_ACTIVE (1 << 0) /* VRF is up in kernel */
+#define VRF_CONFIGURED (1 << 1) /* VRF has some FRR configuration */
/* Interfaces belonging to this VRF */
struct if_name_head ifaces_by_name;
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) \
(V) = vrf->vrf_id; \
} while (0)
+/*
+ * Check whether the VRF is enabled.
+ */
+static inline int vrf_is_enabled(struct vrf *vrf)
+{
+ return vrf && CHECK_FLAG(vrf->status, VRF_ACTIVE);
+}
+
+/* check if the vrf is user configured */
+static inline int vrf_is_user_cfged(struct vrf *vrf)
+{
+ return vrf && CHECK_FLAG(vrf->status, VRF_CONFIGURED);
+}
+
+/* Mark that VRF has user configuration */
+static inline void vrf_set_user_cfged(struct vrf *vrf)
+{
+ SET_FLAG(vrf->status, VRF_CONFIGURED);
+}
+
+/* Mark that VRF no longer has any user configuration */
+static inline void vrf_reset_user_cfged(struct vrf *vrf)
+{
+ UNSET_FLAG(vrf->status, VRF_CONFIGURED);
+}
+
/*
* Utilities to obtain the user data
*/
case KEYCHAIN_NODE:
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
- case PIM_NODE:
case VTY_NODE:
case BGP_EVPN_VNI_NODE:
vty_config_unlock(vty);
case KEYCHAIN_NODE:
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
- case PIM_NODE:
case VTY_NODE:
vty_config_unlock(vty);
vty->node = ENABLE_NODE;
/*
- * $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) {
return 0;
}
+void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi,
+ mpls_label_t label, enum lsp_types_t ltype)
+{
+ struct stream *s;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_VRF_LABEL, vrf_id);
+ stream_putl(s, label);
+ stream_putc(s, afi);
+ stream_putc(s, ltype);
+ stream_putw_at(s, 0, stream_get_endp(s));
+ zclient_send_message(zclient);
+}
+
/* Send register requests to zebra daemon for the information in a VRF. */
void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id)
{
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);
for (i = 0; i < api->nexthop_num; i++) {
api_nh = &api->nexthops[i];
+ stream_putl(s, api_nh->vrf_id);
stream_putc(s, api_nh->type);
switch (api_nh->type) {
case NEXTHOP_TYPE_BLACKHOLE:
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);
for (i = 0; i < api->nexthop_num; i++) {
api_nh = &api->nexthops[i];
+ STREAM_GETL(s, api_nh->vrf_id);
STREAM_GETC(s, api_nh->type);
switch (api_nh->type) {
case NEXTHOP_TYPE_BLACKHOLE:
return false;
}
+struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh)
+{
+ struct nexthop *n = nexthop_new();
+
+ n->type = znh->type;
+ n->vrf_id = znh->vrf_id;
+ n->ifindex = znh->ifindex;
+ n->gate = znh->gate;
+
+ /*
+ * This function does not currently handle labels
+ */
+
+ return n;
+}
+
+bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
+{
+ uint32_t i;
+
+ memset(nhr, 0, sizeof(*nhr));
+
+ STREAM_GETW(s, nhr->prefix.family);
+ STREAM_GETC(s, nhr->prefix.prefixlen);
+ switch(nhr->prefix.family) {
+ case AF_INET:
+ STREAM_GET(&nhr->prefix.u.prefix4.s_addr, s, IPV4_MAX_BYTELEN);
+ break;
+ case AF_INET6:
+ STREAM_GET(&nhr->prefix.u.prefix6, s, IPV6_MAX_BYTELEN);
+ break;
+ default:
+ break;
+ }
+
+ STREAM_GETC(s, nhr->distance);
+ STREAM_GETL(s, nhr->metric);
+ STREAM_GETC(s, nhr->nexthop_num);
+
+ for (i = 0; i < nhr->nexthop_num ; i++) {
+ STREAM_GETC(s, nhr->nexthops[i].type);
+ switch (nhr->nexthops[i].type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ STREAM_GET(&nhr->nexthops[i].gate.ipv4.s_addr,
+ s, IPV4_MAX_BYTELEN);
+ STREAM_GETL(s, nhr->nexthops[i].ifindex);
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ STREAM_GETL(s, nhr->nexthops[i].ifindex);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ STREAM_GET(&nhr->nexthops[i].gate.ipv6,
+ s, IPV6_MAX_BYTELEN);
+ STREAM_GETL(s, nhr->nexthops[i].ifindex);
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ break;
+ }
+ }
+
+ return true;
+stream_failure:
+ return false;
+}
+
/*
* send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE
* for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will
{
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);
__func__, *start, *end, keep, response_keep);
}
/* sanity */
- if (*start > *end || *start < MPLS_MIN_UNRESERVED_LABEL
- || *end > MPLS_MAX_UNRESERVED_LABEL) {
+ if (*start > *end || *start < MPLS_LABEL_UNRESERVED_MIN
+ || *end > MPLS_LABEL_UNRESERVED_MAX) {
zlog_err("%s: Invalid Label chunk: %u - %u", __func__, *start,
*end);
return -1;
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->local_macip_del)(command, zclient, length,
vrf_id);
break;
+ case ZEBRA_IP_PREFIX_ROUTE_ADD:
+ if (zclient->local_ip_prefix_add)
+ (*zclient->local_ip_prefix_add)(command, zclient,
+ length, vrf_id);
+ break;
+ case ZEBRA_IP_PREFIX_ROUTE_DEL:
+ if (zclient->local_ip_prefix_del)
+ (*zclient->local_ip_prefix_del)(command, zclient,
+ length, vrf_id);
+ break;
case ZEBRA_PW_STATUS_UPDATE:
if (zclient->pw_status_update)
(*zclient->pw_status_update)(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_VRF_UNREGISTER,
ZEBRA_VRF_ADD,
ZEBRA_VRF_DELETE,
+ ZEBRA_VRF_LABEL,
ZEBRA_INTERFACE_VRF_UPDATE,
ZEBRA_BFD_CLIENT_REGISTER,
ZEBRA_INTERFACE_ENABLE_RADV,
ZEBRA_FEC_UNREGISTER,
ZEBRA_FEC_UPDATE,
ZEBRA_ADVERTISE_DEFAULT_GW,
+ ZEBRA_ADVERTISE_SUBNET,
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,
ZEBRA_MACIP_DEL,
+ ZEBRA_IP_PREFIX_ROUTE_ADD,
+ ZEBRA_IP_PREFIX_ROUTE_DEL,
ZEBRA_REMOTE_MACIP_ADD,
ZEBRA_REMOTE_MACIP_DEL,
ZEBRA_PW_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);
+ void (*local_ip_prefix_add)(int, struct zclient *, uint16_t, vrf_id_t);
+ void (*local_ip_prefix_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;
};
struct zapi_nexthop {
enum nexthop_types_t type;
+ vrf_id_t vrf_id;
ifindex_t ifindex;
union {
union g_addr gate;
u_int32_t mtu;
vrf_id_t vrf_id;
+
+ struct ethaddr rmac;
};
/* Zebra IPv4 route message API. */
};
/* Zebra MAC types */
-#define ZEBRA_MAC_TYPE_STICKY 0x01 /* Sticky MAC*/
-#define ZEBRA_MAC_TYPE_GW 0x02 /* gateway (SVI) mac*/
+#define ZEBRA_MACIP_TYPE_STICKY 0x01 /* Sticky MAC*/
+#define ZEBRA_MACIP_TYPE_GW 0x02 /* gateway (SVI) mac*/
struct zclient_options {
bool receive_notify;
extern void redist_add_instance(struct redist_proto *, u_short);
extern void redist_del_instance(struct redist_proto *, u_short);
+/*
+ * Send to zebra that the specified vrf is using label to resolve
+ * itself for L3VPN's. Repeated calls of this function with
+ * different labels will cause an effective update of the
+ * label for lookup. If you pass in MPLS_LABEL_NONE
+ * we will cause a delete action and remove this label pop
+ * operation.
+ *
+ * The underlying AF_MPLS doesn't care about afi's
+ * but we can make the zebra_vrf keep track of what
+ * we have installed and play some special games
+ * to get them both installed.
+ */
+extern void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id,
+ afi_t afi, mpls_label_t label,
+ enum lsp_types_t ltype);
+
extern void zclient_send_reg_requests(struct zclient *, vrf_id_t);
extern void zclient_send_dereg_requests(struct zclient *, vrf_id_t);
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,
enum zapi_route_notify_owner *note);
+extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
+extern bool zapi_nexthop_update_decode(struct stream *s,
+ struct zapi_route *nhr);
static inline void zapi_route_set_blackhole(struct zapi_route *api,
enum blackhole_type bh_type)
{
api->nexthop_num = 1;
api->nexthops[0].type = NEXTHOP_TYPE_BLACKHOLE;
+ api->nexthops[0].vrf_id = VRF_DEFAULT;
api->nexthops[0].bh_type = bh_type;
SET_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP);
};
#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.prefix = *p;
switch (type) {
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api.nexthop_num = 1;
api_nh = &api.nexthops[0];
+ api_nh->vrf_id = VRF_DEFAULT;
switch (api.prefix.family) {
case AF_INET:
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;
/* Time stamps. */
struct timeval ts_spf; /* SPF calculation time stamp. */
+
+ uint32_t full_nbrs; /* Fully adjacent neighbors. */
};
#define OSPF6_AREA_ENABLE 0x01
ospf6_lsa_originate_process(lsa, ospf6);
}
+int ospf6_orig_as_external_lsa(struct thread *thread)
+{
+ struct ospf6_interface *oi;
+ struct ospf6_lsa *lsa;
+ uint32_t type, adv_router;
+
+ oi = (struct ospf6_interface *)THREAD_ARG(thread);
+ oi->thread_as_extern_lsa = NULL;
+
+ if (oi->state == OSPF6_INTERFACE_DOWN)
+ return 0;
+
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ adv_router = oi->area->ospf6->router_id;
+ for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, adv_router, lsa)) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("%s: Send update of AS-External LSA %s seq 0x%x",
+ __PRETTY_FUNCTION__, lsa->name,
+ ntohl(lsa->header->seqnum));
+
+ ospf6_flood_interface(NULL, lsa, oi);
+ }
+
+ return 0;
+}
+
static route_tag_t ospf6_as_external_lsa_get_tag(struct ospf6_lsa *lsa)
{
struct ospf6_as_external_lsa *external;
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);
}
ospf6->rmap[type].map = NULL;
}
+static int ospf6_asbr_routemap_update_timer(struct thread *thread)
+{
+ void **arg;
+ int arg_type;
+
+ arg = THREAD_ARG(thread);
+ arg_type = (int)(intptr_t)arg[1];
+
+ ospf6->t_distribute_update = NULL;
+
+ if (ospf6->rmap[arg_type].name)
+ ospf6->rmap[arg_type].map = route_map_lookup_by_name(
+ ospf6->rmap[arg_type].name);
+ if (ospf6->rmap[arg_type].map) {
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("%s: route-map %s update, reset redist %s",
+ __PRETTY_FUNCTION__,
+ ospf6->rmap[arg_type].name,
+ ZROUTE_NAME(arg_type));
+
+ ospf6_zebra_no_redistribute(arg_type);
+ ospf6_zebra_redistribute(arg_type);
+ }
+
+ XFREE(MTYPE_OSPF6_DIST_ARGS, arg);
+ return 0;
+}
+
+void ospf6_asbr_distribute_list_update(int type)
+{
+ void **args = NULL;
+
+ if (ospf6->t_distribute_update)
+ return;
+
+ args = XCALLOC(MTYPE_OSPF6_DIST_ARGS, sizeof(void *)*2);
+
+ args[0] = ospf6;
+ args[1] = (void *)((ptrdiff_t)type);
+
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("%s: trigger redistribute %s reset thread",
+ __PRETTY_FUNCTION__, ZROUTE_NAME(type));
+
+ ospf6->t_distribute_update = NULL;
+ thread_add_timer_msec(master, ospf6_asbr_routemap_update_timer,
+ (void **)args, OSPF_MIN_LS_INTERVAL,
+ &ospf6->t_distribute_update);
+}
+
static void ospf6_asbr_routemap_update(const char *mapname)
{
int type;
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_asbr_distribute_list_update(type);
+ }
+ } else
ospf6->rmap[type].map = NULL;
}
}
+static void ospf6_asbr_routemap_event(route_map_event_t event, const char *name)
+{
+ int type;
+
+ if (ospf6 == NULL)
+ return;
+ for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
+ if ((ospf6->rmap[type].name) &&
+ (strcmp(ospf6->rmap[type].name, name) == 0)) {
+ ospf6_asbr_distribute_list_update(type);
+ }
+ }
+}
+
int ospf6_asbr_is_asbr(struct ospf6 *o)
{
return o->external_table->count;
match = ospf6_route_lookup(prefix, ospf6->external_table);
if (match) {
info = match->route_option;
-
/* copy result of route-map */
if (ospf6->rmap[type].map) {
if (troute.path.metric_type)
if (IS_OSPF6_DEBUG_ASBR) {
inet_ntop(AF_INET, &prefix_id.u.prefix4, ibuf,
sizeof(ibuf));
- zlog_debug("Advertise as AS-External Id:%s", ibuf);
+ prefix2str(prefix, pbuf, sizeof(pbuf));
+ zlog_debug("Advertise as AS-External Id:%s prefix %s metric %u",
+ ibuf, pbuf, match->path.metric_type);
}
match->path.origin.id = htonl(info->id);
if (IS_OSPF6_DEBUG_ASBR) {
inet_ntop(AF_INET, &prefix_id.u.prefix4, ibuf, sizeof(ibuf));
- zlog_debug("Advertise as AS-External Id:%s", ibuf);
+ prefix2str(prefix, pbuf, sizeof(pbuf));
+ zlog_debug("Advertise as AS-External Id:%s prefix %s metric %u",
+ ibuf, pbuf, route->path.metric_type);
}
route->path.origin.id = htonl(info->id);
route_map_add_hook(ospf6_asbr_routemap_update);
route_map_delete_hook(ospf6_asbr_routemap_update);
+ route_map_event_hook(ospf6_asbr_routemap_event);
route_map_set_metric_hook(generic_set_add);
route_map_no_set_metric_hook(generic_set_delete);
void ospf6_asbr_terminate(void)
{
+ /* Cleanup route maps */
+ route_map_add_hook(NULL);
+ route_map_delete_hook(NULL);
+ route_map_event_hook(NULL);
route_map_finish();
}
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);
+extern void ospf6_asbr_distribute_list_update(int type);
#endif /* OSPF6_ASBR_H */
struct timeval now;
struct ospf6_lsa *old;
- if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)
- || IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type))
- zlog_debug("Install LSA: %s", lsa->name);
-
/* Remove the old instance from all neighbors' Link state
retransmission list (RFC2328 13.2 last paragraph) */
old = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id,
ospf6_lsa_checksum(lsa->header);
}
+ if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)
+ || IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type))
+ zlog_debug("%s Install LSA: %s age %d seqnum %x in LSDB.",
+ __PRETTY_FUNCTION__, lsa->name,
+ ntohs(lsa->header->age),
+ ntohl(lsa->header->seqnum));
+
/* actually install */
lsa->installed = now;
ospf6_lsdb_add(lsa, lsa->lsdb);
/* RFC2740 section 3.5.2. Sending Link State Update packets */
/* RFC2328 section 13.3 Next step in the flooding procedure */
-static void ospf6_flood_interface(struct ospf6_neighbor *from,
+void ospf6_flood_interface(struct ospf6_neighbor *from,
struct ospf6_lsa *lsa,
struct ospf6_interface *oi)
{
continue;
}
- /* (d) add retrans-list, schedule retransmission */
- if (is_debug)
- zlog_debug("Add retrans-list of this neighbor");
- ospf6_increment_retrans_count(lsa);
- ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->retrans_list);
- thread_add_timer(master, ospf6_lsupdate_send_neighbor, on,
- on->ospf6_if->rxmt_interval,
- &on->thread_send_lsupdate);
- retrans_added++;
+ if (ospf6->inst_shutdown) {
+ if (is_debug)
+ zlog_debug("%s: Send LSA %s (age %d) update now",
+ __PRETTY_FUNCTION__, lsa->name,
+ ntohs(lsa->header->age));
+ ospf6_lsupdate_send_neighbor_now(on, lsa);
+ continue;
+ } else {
+ /* (d) add retrans-list, schedule retransmission */
+ if (is_debug)
+ zlog_debug("Add retrans-list of this neighbor");
+ ospf6_increment_retrans_count(lsa);
+ ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->retrans_list);
+ thread_add_timer(master, ospf6_lsupdate_send_neighbor,
+ on, on->ospf6_if->rxmt_interval,
+ &on->thread_send_lsupdate);
+ retrans_added++;
+ }
}
/* (2) examin next interface if not added to retrans-list */
zlog_debug("Received is duplicated LSA");
SET_FLAG(new->flag, OSPF6_LSA_DUPLICATE);
}
+ if (old->header->adv_router ==
+ from->ospf6_if->area->ospf6->router_id
+ && OSPF6_LSA_IS_MAXAGE(new)) {
+ ospf6_acknowledge_lsa(new, ismore_recent, from);
+ ospf6_lsa_delete(new);
+ if (is_debug)
+ zlog_debug("%s: Received is self orig MAXAGE LSA %s, discard (ismore_recent %d)",
+ __PRETTY_FUNCTION__, old->name,
+ ismore_recent);
+ return;
+ }
}
/* if no database copy or received is more recent */
"Send back directly and then discard");
}
+ /* Neighbor router sent recent age for LSA,
+ * Router could be restarted while current copy is
+ * MAXAGEd and not removed.*/
+ if (OSPF6_LSA_IS_MAXAGE(old) &&
+ !OSPF6_LSA_IS_MAXAGE(new)) {
+
+ if (is_debug)
+ zlog_debug("%s: Current copy of LSA %s is MAXAGE, but new has recent Age.",
+ old->name,
+ __PRETTY_FUNCTION__);
+
+ ospf6_lsa_purge(old);
+ if (new->header->adv_router
+ != from->ospf6_if->area->
+ ospf6->router_id)
+ ospf6_flood(from, new);
+
+ ospf6_install_lsa(new);
+ return;
+ }
+
/* XXX, MinLSArrival check !? RFC 2328 13 (8) */
ospf6_lsdb_add(ospf6_lsa_copy(old),
from->lsupdate_list);
thread_add_event(master, ospf6_lsupdate_send_neighbor,
from, 0, &from->thread_send_lsupdate);
+
ospf6_lsa_delete(new);
return;
}
}
}
-
DEFUN (debug_ospf6_flooding,
debug_ospf6_flooding_cmd,
"debug ospf6 flooding",
extern int config_write_ospf6_debug_flood(struct vty *vty);
extern void install_element_ospf6_debug_flood(void);
+extern void ospf6_flood_interface(struct ospf6_neighbor *from,
+ struct ospf6_lsa *lsa,
+ struct ospf6_interface *oi);
+extern int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on,
+ struct ospf6_lsa *lsa);
#endif /* OSPF6_FLOOD_H */
THREAD_OFF(oi->thread_network_lsa);
THREAD_OFF(oi->thread_link_lsa);
THREAD_OFF(oi->thread_intra_prefix_lsa);
+ THREAD_OFF(oi->thread_as_extern_lsa);
}
static struct in6_addr *
OSPF6_NETWORK_LSA_EXECUTE(oi);
OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi);
OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area);
+ OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi);
} else if (prev_state == OSPF6_INTERFACE_DR
|| next_state == OSPF6_INTERFACE_DR) {
OSPF6_NETWORK_LSA_SCHEDULE(oi);
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);
struct thread *thread_network_lsa;
struct thread *thread_link_lsa;
struct thread *thread_intra_prefix_lsa;
+ struct thread *thread_as_extern_lsa;
struct ospf6_route_table *route_connected;
/* 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);
0, &(oi)->thread_intra_prefix_lsa); \
} while (0)
+#define OSPF6_AS_EXTERN_LSA_SCHEDULE(oi) \
+ do { \
+ if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \
+ thread_add_event( \
+ master, \
+ ospf6_orig_as_external_lsa, oi, \
+ 0, &(oi)->thread_as_extern_lsa); \
+ } while (0)
+
#define OSPF6_NETWORK_LSA_EXECUTE(oi) \
do { \
THREAD_OFF((oi)->thread_network_lsa); \
thread_execute(master, ospf6_network_lsa_originate, oi, 0); \
} while (0)
+
#define OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi) \
do { \
THREAD_OFF((oi)->thread_intra_prefix_lsa); \
0); \
} while (0)
+#define OSPF6_AS_EXTERN_LSA_EXECUTE(oi) \
+ do { \
+ THREAD_OFF((oi)->thread_as_extern_lsa); \
+ thread_execute(master, ospf6_orig_as_external_lsa, oi, 0); \
+ } while (0)
/* Function Prototypes */
extern char *ospf6_router_lsdesc_lookup(u_char type, u_int32_t interface_id,
extern int ospf6_intra_prefix_lsa_originate_stub(struct thread *);
extern void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa);
extern void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa);
-
+extern int ospf6_orig_as_external_lsa(struct thread *thread);
extern void ospf6_intra_route_calculation(struct ospf6_area *oa);
extern void ospf6_intra_brouter_calculation(struct ospf6_area *oa);
/* 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;
return 0;
}
+void ospf6_flush_self_originated_lsas_now(void)
+{
+ struct listnode *node;
+ struct ospf6_area *oa;
+ struct ospf6_lsa *lsa;
+ const struct route_node *end = NULL;
+ uint32_t type, adv_router;
+
+ ospf6->inst_shutdown = 1;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
+ end = ospf6_lsdb_head(oa->lsdb_self, 0, 0,
+ ospf6->router_id, &lsa);
+ while (lsa) {
+ /* RFC 2328 (14.1): Set MAXAGE */
+ lsa->header->age = htons(OSPF_LSA_MAXAGE);
+ /* Flood MAXAGE LSA*/
+ ospf6_flood(NULL, lsa);
+
+ lsa = ospf6_lsdb_next(end, lsa);
+ }
+ }
+
+ type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
+ adv_router = ospf6->router_id;
+ for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, adv_router, lsa)) {
+ /* RFC 2328 (14.1): Set MAXAGE */
+ lsa->header->age = htons(OSPF_LSA_MAXAGE);
+ ospf6_flood(NULL, lsa);
+ }
+}
/* Fletcher Checksum -- Refer to RFC1008. */
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);
+extern void ospf6_flush_self_originated_lsas_now(void);
#endif /* OSPF6_LSA_H */
}
if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type))
zlog_debug("Remove MaxAge %s", lsa->name);
+
if (CHECK_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED)) {
UNSET_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED);
/*
ospf6_asbr_terminate();
ospf6_lsa_terminate();
+ /* reverse access_list_init */
+ access_list_reset();
+
+ /* reverse prefix_list_init */
+ prefix_list_add_hook(NULL);
+ prefix_list_delete_hook(NULL);
+ prefix_list_reset();
+
vrf_terminate();
if (zclient) {
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_DIST_ARGS, "OSPF6 Distribute arguments")
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_DIST_ARGS)
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)) {
return 0;
}
+int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on,
+ struct ospf6_lsa *lsa)
+{
+ struct ospf6_header *oh;
+ struct ospf6_lsupdate *lsupdate;
+ u_char *p;
+ int lsa_cnt = 0;
+
+ memset(sendbuf, 0, iobuflen);
+ oh = (struct ospf6_header *)sendbuf;
+ lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
+ + sizeof(struct ospf6_header));
+
+ p = (u_char *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
+ ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
+ memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
+ p += OSPF6_LSA_SIZE(lsa->header);
+ lsa_cnt++;
+
+ oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
+ oh->length = htons(p - sendbuf);
+ lsupdate->lsa_number = htonl(lsa_cnt);
+
+ if (IS_OSPF6_DEBUG_FLOODING ||
+ IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
+ zlog_debug("%s: Send lsupdate with lsa %s (age %u)",
+ __PRETTY_FUNCTION__, lsa->name,
+ ntohs(lsa->header->age));
+
+ ospf6_send_lsupdate(on, NULL, oh);
+
+ return 0;
+}
+
int ospf6_lsupdate_send_interface(struct thread *thread)
{
struct ospf6_interface *oi;
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);
}
OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(on->ospf6_if);
}
OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(on->ospf6_if->area);
+
+ if (prev_state == OSPF6_NEIGHBOR_LOADING &&
+ next_state == OSPF6_NEIGHBOR_FULL) {
+ OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if);
+ on->ospf6_if->area->full_nbrs++;
+ }
+
+ if (prev_state == OSPF6_NEIGHBOR_FULL)
+ on->ospf6_if->area->full_nbrs--;
}
if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE
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);
}
}
}
if (i >= entries)
return;
+ nexthops[i].vrf_id = VRF_DEFAULT;
nexthops[i].ifindex = nh->ifindex;
if (!IN6_IS_ADDR_UNSPECIFIED(&nh->address)) {
nexthops[i].gate.ipv6 = nh->address;
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)
return -1;
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 */
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);
+
+ /*
+ * We assume at this point in time that rtr_lsa is
+ * a valid pointer.
+ */
+ assert(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);
struct ospf6_area *oa;
QOBJ_UNREG(o);
+
+ ospf6_flush_self_originated_lsas_now();
ospf6_disable(ospf6);
for (ALL_LIST_ELEMENTS(o->area_list, node, nnode, oa))
THREAD_OFF(o->maxage_remover);
THREAD_OFF(o->t_spf_calc);
THREAD_OFF(o->t_ase_calc);
+ THREAD_OFF(o->t_distribute_update);
}
}
int ret;
const char *router_id_str;
u_int32_t router_id;
+ struct ospf6_area *oa;
+ struct listnode *node;
argv_find(argv, argc, "A.B.C.D", &idx);
router_id_str = argv[idx]->arg;
}
o->router_id_static = router_id;
- if (o->router_id == 0)
- o->router_id = router_id;
+
+ for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) {
+ if (oa->full_nbrs) {
+ vty_out(vty,
+ "For this router-id change to take effect,"
+ " save config and restart ospf6d\n");
+ return CMD_SUCCESS;
+ }
+ }
+
+ o->router_id = router_id;
return CMD_SUCCESS;
}
V4NOTATION_STR)
{
VTY_DECLVAR_CONTEXT(ospf6, o);
+ struct ospf6_area *oa;
+ struct listnode *node;
+
o->router_id_static = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) {
+ if (oa->full_nbrs) {
+ vty_out(vty,
+ "For this router-id change to take effect,"
+ " save config and restart ospf6d\n");
+ return CMD_SUCCESS;
+ }
+ }
o->router_id = 0;
+ if (o->router_id_zebra.s_addr)
+ o->router_id = (uint32_t)o->router_id_zebra.s_addr;
return CMD_SUCCESS;
}
VTY_DECLVAR_CONTEXT(ospf6, o);
int idx = 0;
+ o->distance_intra = 0;
+ o->distance_inter = 0;
+ o->distance_external = 0;
+
if (argv_find(argv, argc, "intra-area", &idx))
o->distance_intra = atoi(argv[idx + 1]->arg);
idx = 0;
/* static router id */
u_int32_t router_id_static;
+ struct in_addr router_id_zebra;
+
/* start time */
struct timeval starttime;
struct thread *t_spf_calc; /* SPF calculation timer. */
struct thread *t_ase_calc; /* ASE calculation timer. */
struct thread *maxage_remover;
+ struct thread *t_distribute_update; /* Distirbute update timer. */
u_int32_t ref_bandwidth;
struct route_table *distance_table;
+ /* Used during ospf instance going down send LSDB
+ * update to neighbors immediatly */
+ uint8_t inst_shutdown;
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(ospf6)
/* information about zebra. */
struct zclient *zclient = NULL;
-struct in_addr router_id_zebra;
-
/* Router-id update message from zebra. */
static int ospf6_router_id_update_zebra(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
struct ospf6 *o = ospf6;
zebra_router_id_update_read(zclient->ibuf, &router_id);
- router_id_zebra = router_id.u.prefix4;
if (o == NULL)
return 0;
+ o->router_id_zebra = router_id.u.prefix4;
+
if (o->router_id == 0)
- o->router_id = (u_int32_t)router_id_zebra.s_addr;
+ o->router_id = (uint32_t)o->router_id_zebra.s_addr;
return 0;
}
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;
unsigned long conf_debug_ospf_zebra = 0;
unsigned long conf_debug_ospf_nssa = 0;
unsigned long conf_debug_ospf_te = 0;
+unsigned long conf_debug_ospf_ext = 0;
+unsigned long conf_debug_ospf_sr = 0;
/* Enable debug option variables -- valid only session. */
unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0};
unsigned long term_debug_ospf_zebra = 0;
unsigned long term_debug_ospf_nssa = 0;
unsigned long term_debug_ospf_te = 0;
-
+unsigned long term_debug_ospf_ext = 0;
+unsigned long term_debug_ospf_sr = 0;
const char *ospf_redist_string(u_int route_type)
{
return CMD_SUCCESS;
}
+DEFUN (debug_ospf_sr,
+ debug_ospf_sr_cmd,
+ "debug ospf sr",
+ DEBUG_STR
+ OSPF_STR
+ "OSPF-SR information\n")
+{
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_ON(sr, SR);
+ TERM_DEBUG_ON(sr, SR);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_ospf_sr,
+ no_debug_ospf_sr_cmd,
+ "no debug ospf sr",
+ NO_STR
+ DEBUG_STR
+ OSPF_STR
+ "OSPF-SR information\n")
+{
+ if (vty->node == CONFIG_NODE)
+ CONF_DEBUG_OFF(sr, SR);
+ TERM_DEBUG_OFF(sr, SR);
+ return CMD_SUCCESS;
+}
+
DEFUN (no_debug_ospf,
no_debug_ospf_cmd,
"no debug ospf",
write = 1;
}
+ /* debug ospf te */
+ if (IS_CONF_DEBUG_OSPF(te, TE) == OSPF_DEBUG_TE) {
+ vty_out(vty, "debug ospf%s te\n", str);
+ write = 1;
+ }
+
+ /* debug ospf sr */
+ if (IS_CONF_DEBUG_OSPF(sr, SR) == OSPF_DEBUG_SR) {
+ vty_out(vty, "debug ospf%s sr\n", str);
+ write = 1;
+ }
+
return write;
}
install_element(ENABLE_NODE, &debug_ospf_event_cmd);
install_element(ENABLE_NODE, &debug_ospf_nssa_cmd);
install_element(ENABLE_NODE, &debug_ospf_te_cmd);
+ install_element(ENABLE_NODE, &debug_ospf_sr_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_event_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_nssa_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_te_cmd);
+ install_element(ENABLE_NODE, &no_debug_ospf_sr_cmd);
install_element(ENABLE_NODE, &show_debugging_ospf_instance_cmd);
install_element(ENABLE_NODE, &debug_ospf_packet_cmd);
install_element(CONFIG_NODE, &debug_ospf_event_cmd);
install_element(CONFIG_NODE, &debug_ospf_nssa_cmd);
install_element(CONFIG_NODE, &debug_ospf_te_cmd);
+ install_element(CONFIG_NODE, &debug_ospf_sr_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_event_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_nssa_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_te_cmd);
+ install_element(CONFIG_NODE, &no_debug_ospf_sr_cmd);
install_element(CONFIG_NODE, &debug_ospf_instance_nsm_cmd);
install_element(CONFIG_NODE, &debug_ospf_instance_lsa_cmd);
#define OSPF_DEBUG_EVENT 0x01
#define OSPF_DEBUG_NSSA 0x02
#define OSPF_DEBUG_TE 0x04
+#define OSPF_DEBUG_EXT 0x08
+#define OSPF_DEBUG_SR 0x10
/* Macro for setting debug option. */
#define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b)
/* Macro for checking debug option. */
#define IS_DEBUG_OSPF_PACKET(a, b) (term_debug_ospf_packet[a] & OSPF_DEBUG_##b)
#define IS_DEBUG_OSPF(a, b) (term_debug_ospf_##a & OSPF_DEBUG_##b)
-#define IS_DEBUG_OSPF_EVENT IS_DEBUG_OSPF(event,EVENT)
+#define IS_DEBUG_OSPF_EVENT IS_DEBUG_OSPF(event, EVENT)
-#define IS_DEBUG_OSPF_NSSA IS_DEBUG_OSPF(nssa,NSSA)
+#define IS_DEBUG_OSPF_NSSA IS_DEBUG_OSPF(nssa, NSSA)
-#define IS_DEBUG_OSPF_TE IS_DEBUG_OSPF(te,TE)
+#define IS_DEBUG_OSPF_TE IS_DEBUG_OSPF(te, TE)
+
+#define IS_DEBUG_OSPF_EXT IS_DEBUG_OSPF(ext, EXT)
+
+#define IS_DEBUG_OSPF_SR IS_DEBUG_OSPF(sr, SR)
#define IS_CONF_DEBUG_OSPF_PACKET(a, b) \
(conf_debug_ospf_packet[a] & OSPF_DEBUG_##b)
extern unsigned long term_debug_ospf_zebra;
extern unsigned long term_debug_ospf_nssa;
extern unsigned long term_debug_ospf_te;
+extern unsigned long term_debug_ospf_ext;
+extern unsigned long term_debug_ospf_sr;
/* Message Strings. */
extern char *ospf_lsa_type_str[];
--- /dev/null
+/*
+ * This is an implementation of RFC7684 OSPFv2 Prefix/Link Attribute
+ * Advertisement
+ *
+ * Module name: Extended Prefix/Link Opaque LSA
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ *
+ * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "memory.h"
+#include "command.h"
+#include "vty.h"
+#include "stream.h"
+#include "log.h"
+#include "thread.h"
+#include "hash.h"
+#include "sockunion.h" /* for inet_aton() */
+#include "network.h"
+#include "if.h"
+#include "libospf.h" /* for ospf interface types */
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ext.h"
+
+/* Following structure are internal use only. */
+
+/*
+ * Global variable to manage Extended Prefix/Link Opaque LSA on this node.
+ * Note that all parameter values are stored in network byte order.
+ */
+static struct ospf_ext_lp OspfEXT;
+
+/*
+ * -----------------------------------------------------------------------
+ * Followings are initialize/terminate functions for Extended Prefix/Link
+ * Opaque LSA handling.
+ * -----------------------------------------------------------------------
+ */
+
+/* Extended Prefix Opaque LSA related callback functions */
+static void ospf_ext_pref_ism_change(struct ospf_interface *oi, int old_status);
+static void ospf_ext_pref_show_info(struct vty *vty, struct ospf_lsa *lsa);
+static int ospf_ext_pref_lsa_originate(void *arg);
+static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa);
+static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti,
+ enum lsa_opcode opcode);
+/* Extended Link Opaque LSA related callback functions */
+static int ospf_ext_link_new_if(struct interface *ifp);
+static int ospf_ext_link_del_if(struct interface *ifp);
+static void ospf_ext_link_ism_change(struct ospf_interface *oi, int old_status);
+static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status);
+static void ospf_ext_link_show_info(struct vty *vty, struct ospf_lsa *lsa);
+static int ospf_ext_link_lsa_originate(void *arg);
+static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa);
+static void ospf_ext_link_lsa_schedule(struct ext_itf *exti,
+ enum lsa_opcode opcode);
+static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op);
+static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa);
+static int ospf_ext_pref_lsa_update(struct ospf_lsa *lsa);
+static void del_ext_info(void *val);
+
+/*
+ * Extended Link/Prefix initialization
+ *
+ * @param - none
+ *
+ * @return - 0 if OK, <> 0 otherwise
+ */
+int ospf_ext_init(void)
+{
+ int rc = 0;
+
+ memset(&OspfEXT, 0, sizeof(struct ospf_ext_lp));
+ OspfEXT.enabled = false;
+ /* Only Area flooding is supported yet */
+ OspfEXT.scope = OSPF_OPAQUE_AREA_LSA;
+ /* Initialize interface list */
+ OspfEXT.iflist = list_new();
+ OspfEXT.iflist->del = del_ext_info;
+
+ zlog_info("EXT (%s): Register Extended Link Opaque LSA", __func__);
+ rc = ospf_register_opaque_functab(
+ OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_EXTENDED_LINK_LSA,
+ ospf_ext_link_new_if, /* new if */
+ ospf_ext_link_del_if, /* del if */
+ ospf_ext_link_ism_change, /* ism change */
+ ospf_ext_link_nsm_change, /* nsm change */
+ NULL, /* Write router config. */
+ NULL, /* Write interface conf. */
+ NULL, /* Write debug config. */
+ ospf_ext_link_show_info, /* Show LSA info */
+ ospf_ext_link_lsa_originate, /* Originate LSA */
+ ospf_ext_link_lsa_refresh, /* Refresh LSA */
+ ospf_ext_link_lsa_update, /* new_lsa_hook */
+ NULL); /* del_lsa_hook */
+
+ if (rc != 0) {
+ zlog_warn("EXT (%s): Failed to register Extended Link LSA",
+ __func__);
+ return rc;
+ }
+
+ zlog_info("EXT (%s): Register Extended Prefix Opaque LSA", __func__);
+ rc = ospf_register_opaque_functab(
+ OspfEXT.scope, OPAQUE_TYPE_EXTENDED_PREFIX_LSA,
+ NULL, /* new if handle by link */
+ NULL, /* del if handle by link */
+ ospf_ext_pref_ism_change, /* ism change */
+ NULL, /* nsm change */
+ ospf_sr_config_write_router, /* Write router config. */
+ NULL, /* Write interface conf. */
+ NULL, /* Write debug config. */
+ ospf_ext_pref_show_info, /* Show LSA info */
+ ospf_ext_pref_lsa_originate, /* Originate LSA */
+ ospf_ext_pref_lsa_refresh, /* Refresh LSA */
+ ospf_ext_pref_lsa_update, /* new_lsa_hook */
+ NULL); /* del_lsa_hook */
+ if (rc != 0) {
+ zlog_warn("EXT (%s): Failed to register Extended Prefix LSA",
+ __func__);
+ return rc;
+ }
+
+ return rc;
+}
+
+/*
+ * Extended Link/Prefix termination function
+ *
+ * @param - none
+ * @return - none
+ */
+void ospf_ext_term(void)
+{
+
+ if ((OspfEXT.scope != OSPF_OPAQUE_AREA_LSA)
+ && (OspfEXT.scope != OSPF_OPAQUE_AS_LSA))
+ zlog_warn(
+ "EXT: Unable to unregister Extended Prefix "
+ "Opaque LSA functions: Wrong scope!");
+ else
+ ospf_delete_opaque_functab(OspfEXT.scope,
+ OPAQUE_TYPE_EXTENDED_PREFIX_LSA);
+
+ ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA,
+ OPAQUE_TYPE_EXTENDED_LINK_LSA);
+
+ list_delete_and_null(&OspfEXT.iflist);
+ OspfEXT.scope = 0;
+ OspfEXT.enabled = false;
+
+ return;
+}
+
+/*
+ * Extended Link/Prefix finish function
+ *
+ * @param - none
+ * @return - none
+ */
+void ospf_ext_finish(void)
+{
+ // list_delete_all_node(OspfEXT.iflist);
+ OspfEXT.enabled = false;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * Followings are control functions for Extended Prefix/Link Opaque LSA
+ * parameters management.
+ * ---------------------------------------------------------------------
+ */
+
+/* Functions to free memory space */
+static void del_ext_info(void *val)
+{
+ XFREE(MTYPE_OSPF_EXT_PARAMS, val);
+}
+
+/* Increment instance value for Extended Prefix Opaque LSAs Opaque ID field */
+static uint32_t get_ext_pref_instance_value(void)
+{
+ static uint32_t seqno = 0;
+
+ if (seqno < MAX_LEGAL_EXT_INSTANCE_NUM)
+ seqno += 1;
+ else
+ seqno = 1; /* Avoid zero. */
+
+ return seqno;
+}
+
+/* Increment instance value for Extended Link Opaque LSAs Opaque ID field */
+static uint32_t get_ext_link_instance_value(void)
+{
+ static uint32_t seqno = 0;
+
+ if (seqno < MAX_LEGAL_EXT_INSTANCE_NUM)
+ seqno += 1;
+ else
+ seqno = 1; /* Avoid zero. */
+
+ return seqno;
+}
+
+/* Lookup Extended Prefix/Links by ifp from OspfEXT struct iflist */
+static struct ext_itf *lookup_ext_by_ifp(struct interface *ifp)
+{
+ struct listnode *node, *nnode;
+ struct ext_itf *exti;
+
+ for (ALL_LIST_ELEMENTS(OspfEXT.iflist, node, nnode, exti))
+ if (exti->ifp == ifp)
+ return exti;
+
+ return NULL;
+}
+
+/* Lookup Extended Prefix/Links by LSA ID from OspfEXT struct iflist */
+static struct ext_itf *lookup_ext_by_instance(struct ospf_lsa *lsa)
+{
+ struct listnode *node;
+ struct ext_itf *exti;
+ uint32_t key = GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr));
+ uint8_t type = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr));
+
+
+ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
+ if ((exti->instance == key) && (exti->type == type))
+ return exti;
+
+ return NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ * The underlying subsection defines setters and unsetters to create and
+ * delete tlvs and subtlvs
+ * ----------------------------------------------------------------------
+ */
+
+/* Extended Prefix TLV - RFC7684 section 2.1 */
+static void set_ext_prefix(struct ext_itf *exti, uint8_t route_type,
+ uint8_t flags, struct prefix_ipv4 p)
+{
+
+ TLV_TYPE(exti->prefix) = htons(EXT_TLV_PREFIX);
+ /* Warning: Size must be adjust depending of subTLV's */
+ TLV_LEN(exti->prefix) = htons(EXT_TLV_PREFIX_SIZE);
+ exti->prefix.route_type = route_type;
+ exti->prefix.flags = flags;
+ /* Only Address Family Ipv4 (0) is defined in RFC 7684 */
+ exti->prefix.af = 0;
+ exti->prefix.pref_length = p.prefixlen;
+ exti->prefix.address = p.prefix;
+}
+
+/* Extended Link TLV - RFC7684 section 3.1 */
+static void set_ext_link(struct ext_itf *exti, uint8_t type, struct in_addr id,
+ struct in_addr data)
+{
+
+ TLV_TYPE(exti->link) = htons(EXT_TLV_LINK);
+ /* Warning: Size must be adjust depending of subTLV's */
+ TLV_LEN(exti->link) = htons(EXT_TLV_LINK_SIZE);
+ exti->link.link_type = type;
+ exti->link.link_id = id;
+ exti->link.link_data = data;
+}
+
+/* Prefix SID SubTLV - section 5 */
+static void set_prefix_sid(struct ext_itf *exti, uint8_t algorithm,
+ uint32_t value, int value_type, uint8_t flags)
+{
+
+ if ((algorithm != SR_ALGORITHM_SPF)
+ && (algorithm != SR_ALGORITHM_STRICT_SPF)) {
+ zlog_warn(
+ "EXT (%s): unrecognized algorithm, not SPF or S-SPF",
+ __func__);
+ return;
+ }
+
+ /* Update flags according to the type of value field: label or index */
+ if (value_type == SID_LABEL)
+ SET_FLAG(flags, EXT_SUBTLV_PREFIX_SID_VFLG);
+
+ /* set prefix sid subtlv for an extended prefix tlv */
+ TLV_TYPE(exti->node_sid) = htons(EXT_SUBTLV_PREFIX_SID);
+ exti->node_sid.algorithm = algorithm;
+ exti->node_sid.flags = flags;
+ exti->node_sid.mtid = 0; /* Multi-Topology is not supported */
+
+ /* Set Label or Index value */
+ if (value_type == SID_LABEL) {
+ TLV_LEN(exti->node_sid) = htons(SID_LABEL_SIZE);
+ exti->node_sid.value = htonl(SET_LABEL(value));
+ } else {
+ TLV_LEN(exti->node_sid) = htons(SID_INDEX_SIZE);
+ exti->node_sid.value = htonl(value);
+ }
+
+}
+
+/* Adjacency SID SubTLV - section 6.1 */
+static void set_adj_sid(struct ext_itf *exti, bool backup, uint32_t value,
+ int value_type)
+{
+ int index;
+ uint8_t flags;
+
+ /* Determine which ADJ_SID must be set: nominal or backup */
+ if (backup) {
+ flags = EXT_SUBTLV_LINK_ADJ_SID_BFLG;
+ index = 1;
+ } else {
+ index = 0;
+ flags = 0;
+ }
+
+ /* Set Header */
+ TLV_TYPE(exti->adj_sid[index]) = htons(EXT_SUBTLV_ADJ_SID);
+
+ /* Only Local ADJ-SID is supported for the moment */
+ SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG);
+
+ exti->adj_sid[index].mtid = 0; /* Multi-Topology is not supported */
+
+ /* Adjust Length, Flags and Value depending on the type of Label */
+ if (value_type == SID_LABEL) {
+ SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+ TLV_LEN(exti->adj_sid[index]) = htons(SID_LABEL_SIZE);
+ exti->adj_sid[index].value = htonl(SET_LABEL(value));
+ } else {
+ UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+ TLV_LEN(exti->adj_sid[index]) = htons(SID_INDEX_SIZE);
+ exti->adj_sid[index].value = htonl(value);
+ }
+
+ exti->adj_sid[index].flags = flags; /* Set computed flags */
+ exti->adj_sid[index].mtid = 0; /* Multi-Topology is not supported */
+ exti->adj_sid[index].weight = 0; /* Load-Balancing is not supported */
+
+}
+
+/* LAN Adjacency SID SubTLV - section 6.2 */
+static void set_lan_adj_sid(struct ext_itf *exti, bool backup, uint32_t value,
+ int value_type, struct in_addr neighbor_id)
+{
+
+ int index;
+ uint8_t flags;
+
+ /* Determine which ADJ_SID must be set: nominal or backup */
+ if (backup) {
+ flags = EXT_SUBTLV_LINK_ADJ_SID_BFLG;
+ index = 1;
+ } else {
+ index = 0;
+ flags = 0;
+ }
+
+ /* Set Header */
+ TLV_TYPE(exti->lan_sid[index]) = htons(EXT_SUBTLV_ADJ_SID);
+
+ /* Only Local ADJ-SID is supported for the moment */
+ SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG);
+
+ /* Adjust Length, Flags and Value depending on the type of Label */
+ if (value_type == SID_LABEL) {
+ SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+ TLV_LEN(exti->lan_sid[index]) = htons(SID_LABEL_SIZE);
+ exti->lan_sid[index].value = htonl(SET_LABEL(value));
+ } else {
+ UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG);
+ TLV_LEN(exti->lan_sid[index]) = htons(SID_INDEX_SIZE);
+ exti->lan_sid[index].value = htonl(value);
+ }
+
+ exti->lan_sid[index].flags = flags; /* Set computed flags */
+ exti->lan_sid[index].mtid = 0; /* Multi-Topology is not supported */
+ exti->lan_sid[index].weight = 0; /* Load-Balancing is not supported */
+ exti->lan_sid[index].neighbor_id = neighbor_id;
+
+}
+
+/* Experimental SubTLV from Cisco */
+static void set_rmt_itf_addr(struct ext_itf *exti, struct in_addr rmtif)
+{
+
+ TLV_TYPE(exti->rmt_itf_addr) = htons(EXT_SUBTLV_RMT_ITF_ADDR);
+ TLV_LEN(exti->rmt_itf_addr) = htons(sizeof(struct in_addr));
+ exti->rmt_itf_addr.value = rmtif;
+
+}
+
+/*
+ * Update Extended prefix SID index for Loopback interface type
+ *
+ * @param ifname - Loopback interface name
+ * @param index - new value for the prefix SID of this interface
+ * @param p - prefix for this interface or NULL if Extended Prefix
+ * should be remove
+ *
+ * @return instance number if update is OK, 0 otherwise
+ */
+uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index,
+ struct prefix_ipv4 *p, uint8_t flags)
+{
+ int rc = 0;
+ struct ext_itf *exti;
+
+ /* Find Extended Prefix interface */
+ exti = lookup_ext_by_ifp(ifp);
+ if (exti == NULL)
+ return rc;
+
+ if (p != NULL) {
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "EXT (%s): Schedule new prefix %s/%u with "
+ "index %u on interface %s",
+ __func__, inet_ntoa(p->prefix), p->prefixlen,
+ index, ifp->name);
+
+ /* Set first Extended Prefix then the Prefix SID information */
+ set_ext_prefix(exti, OSPF_PATH_INTRA_AREA, EXT_TLV_PREF_NFLG,
+ *p);
+ set_prefix_sid(exti, SR_ALGORITHM_SPF, index, SID_INDEX, flags);
+
+ /* Try to Schedule LSA */
+ SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+ ospf_ext_pref_lsa_schedule(exti, REFRESH_THIS_LSA);
+ else
+ ospf_ext_pref_lsa_schedule(exti, REORIGINATE_THIS_LSA);
+ } else {
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "EXT (%s): Remove prefix for interface %s",
+ __func__, ifp->name);
+
+ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+ ospf_ext_pref_lsa_schedule(exti, FLUSH_THIS_LSA);
+ UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+ UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+ }
+ }
+
+ return SET_OPAQUE_LSID(exti->type, exti->instance);
+}
+
+/*
+ * Used by Segment Routing to activate/deactivate Extended Link/Prefix flooding
+ *
+ * @param enable To activate or not Segment Routing Extended LSA flooding
+ *
+ * @return none
+ */
+void ospf_ext_update_sr(bool enable)
+{
+ struct listnode *node;
+ struct ext_itf *exti;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "EXT (%s): %s Extended LSAs for Segment Routing ",
+ __func__, enable ? "Enable" : "Disable");
+
+ if (enable) {
+ OspfEXT.enabled = true;
+ /* Refresh LSAs if already engaged or originate */
+ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
+ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+ ospf_ext_lsa_schedule(exti, REFRESH_THIS_LSA);
+ else
+ ospf_ext_lsa_schedule(exti,
+ REORIGINATE_THIS_LSA);
+ } else {
+ /* Start by Flushing engaged LSAs */
+ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
+ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+ ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA);
+ /* And then disable Extended Link/Prefix */
+ OspfEXT.enabled = false;
+ }
+}
+/*
+ * -----------------------------------------------------------------------
+ * Followings are callback functions against generic Opaque-LSAs handling
+ * -----------------------------------------------------------------------
+ */
+
+/* Add new Interface in Extended Interface List */
+static int ospf_ext_link_new_if(struct interface *ifp)
+{
+ struct ext_itf *new;
+ int rc = -1;
+
+ if (lookup_ext_by_ifp(ifp) != NULL) {
+ zlog_warn(
+ "EXT (%s): interface %s is already in use",
+ __func__, ifp ? ifp->name : "-");
+ rc = 0; /* Do nothing here. */
+ return rc;
+ }
+
+ new = XCALLOC(MTYPE_OSPF_EXT_PARAMS, sizeof(struct ext_itf));
+ if (new == NULL) {
+ zlog_warn("EXT (%s): XCALLOC: %s", __func__,
+ safe_strerror(errno));
+ return rc;
+ }
+
+ /* initialize new information and link back the interface */
+ new->ifp = ifp;
+ new->flags = EXT_LPFLG_LSA_INACTIVE;
+
+ listnode_add(OspfEXT.iflist, new);
+
+ rc = 0;
+ return rc;
+}
+
+/* Remove existing Interface from Extended Interface List */
+static int ospf_ext_link_del_if(struct interface *ifp)
+{
+ struct ext_itf *exti;
+ int rc = -1;
+
+ exti = lookup_ext_by_ifp(ifp);
+ if (exti != NULL) {
+ struct list *iflist = OspfEXT.iflist;
+
+ /* Dequeue listnode entry from the list. */
+ listnode_delete(iflist, exti);
+
+ XFREE(MTYPE_OSPF_EXT_PARAMS, exti);
+
+ rc = 0;
+ } else {
+ zlog_warn(
+ "EXT (%s): interface %s is not found",
+ __func__, ifp ? ifp->name : "-");
+ }
+
+ return rc;
+}
+
+/*
+ * Determine if an Interface belongs to an Extended Link Adjacency or LAN Adj.
+ * type and allocate new instance value accordingly
+ */
+static void ospf_ext_link_ism_change(struct ospf_interface *oi, int old_status)
+{
+ struct ext_itf *exti;
+
+ /* Get interface information for Segment Routing */
+ exti = lookup_ext_by_ifp(oi->ifp);
+ if (exti == NULL) {
+ zlog_warn(
+ "EXT (%s): Cannot get Extended info. from OI(%s)",
+ __func__, IF_NAME(oi));
+ return;
+ }
+
+ /* Determine if interface is related to Adjacency or LAN Adj. SID */
+ if (oi->type != OSPF_IFTYPE_LOOPBACK) {
+ if (oi->state == ISM_DR)
+ exti->stype = LAN_ADJ_SID;
+ else
+ exti->stype = ADJ_SID;
+
+ exti->instance = get_ext_link_instance_value();
+ exti->type = OPAQUE_TYPE_EXTENDED_LINK_LSA;
+
+ zlog_debug(
+ "EXT (%s): Set %s SID to interface %s ", __func__,
+ exti->stype == ADJ_SID ? "Adj." : "LAN Adj.",
+ oi->ifp->name);
+ }
+}
+
+/*
+ * Determine if an Interface belongs to an Extended Prefix and
+ * allocate new instance value accordingly
+ */
+static void ospf_ext_pref_ism_change(struct ospf_interface *oi, int old_status)
+{
+ struct ext_itf *exti;
+
+ /* Get interface information for Segment Routing */
+ exti = lookup_ext_by_ifp(oi->ifp);
+ if (exti == NULL) {
+ zlog_warn(
+ "EXT (%s): Cannot get Extended info. from OI(%s)",
+ __func__, IF_NAME(oi));
+ return;
+ }
+
+ /* Determine if interface is related to a Node SID */
+ if (oi->type == OSPF_IFTYPE_LOOPBACK) {
+ exti->stype = PREF_SID;
+ exti->instance = get_ext_pref_instance_value();
+ exti->type = OPAQUE_TYPE_EXTENDED_PREFIX_LSA;
+
+ zlog_debug(
+ "EXT (%s): Set Node SID to interface %s ", __func__,
+ oi->ifp->name);
+
+ /* Complete SRDB if the interface belongs to a Prefix */
+ if (OspfEXT.enabled)
+ ospf_sr_update_prefix(oi->ifp, oi->address);
+ }
+}
+
+/*
+ * Finish Extended Link configuration and flood corresponding LSA
+ * when OSPF adjacency on this link fire up
+ */
+static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
+{
+ struct ospf_interface *oi = nbr->oi;
+ struct ext_itf *exti;
+ uint32_t label;
+
+ /* Process Neighbor only when its state is NSM Full */
+ if (nbr->state != NSM_Full)
+ return;
+
+ /* Get interface information for Segment Routing */
+ exti = lookup_ext_by_ifp(oi->ifp);
+ if (exti == NULL) {
+ zlog_warn(
+ "EXT (%s): Cannot get Extended info. from OI(%s)",
+ __func__, IF_NAME(oi));
+ return;
+ }
+
+ if (oi->area == NULL || oi->area->ospf == NULL) {
+ zlog_warn(
+ "EXT (%s): Cannot refer to OSPF from OI(%s)",
+ __func__, IF_NAME(oi));
+ return;
+ }
+
+ /* Keep Area information in combination with SR info. */
+ exti->area = oi->area;
+ OspfEXT.area = oi->area;
+
+ /* Process only Adjacency/LAN SID */
+ if (exti->stype == PREF_SID)
+ return;
+
+ switch (oi->state) {
+ case ISM_PointToPoint:
+ /* Segment ID is an Adjacency one */
+ exti->stype = ADJ_SID;
+
+ /* Set Extended Link TLV with link_id == Nbr Router ID */
+ set_ext_link(exti, OSPF_IFTYPE_POINTOPOINT, nbr->router_id,
+ oi->address->u.prefix4);
+
+ /* Set Extended Link Adjacency SubTLVs, backup first */
+ label = get_ext_link_label_value();
+ set_adj_sid(exti, true, label, SID_LABEL);
+ label = get_ext_link_label_value();
+ set_adj_sid(exti, false, label, SID_LABEL);
+ /* And Remote Interface address */
+ set_rmt_itf_addr(exti, nbr->address.u.prefix4);
+
+ break;
+
+ case ISM_DR:
+ /* Segment ID is a LAN Adjacency for the DR only */
+ exti->stype = LAN_ADJ_SID;
+
+ /* Set Extended Link TLV with link_id == DR */
+ set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi),
+ oi->address->u.prefix4);
+
+ /* Set Extended Link Adjacency SubTLVs, backup first */
+ label = get_ext_link_label_value();
+ set_lan_adj_sid(exti, true, label, SID_LABEL, nbr->router_id);
+ label = get_ext_link_label_value();
+ set_lan_adj_sid(exti, false, label, SID_LABEL, nbr->router_id);
+
+ break;
+
+ case ISM_DROther:
+ case ISM_Backup:
+ /* Segment ID is an Adjacency if not the DR */
+ exti->stype = ADJ_SID;
+
+ /* Set Extended Link TLV with link_id == DR */
+ set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi),
+ oi->address->u.prefix4);
+
+ /* Set Extended Link Adjacency SubTLVs, backup first */
+ label = get_ext_link_label_value();
+ set_adj_sid(exti, true, label, SID_LABEL);
+ label = get_ext_link_label_value();
+ set_adj_sid(exti, false, label, SID_LABEL);
+
+ break;
+
+ default:
+ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+ ospf_ext_link_lsa_schedule(exti, FLUSH_THIS_LSA);
+ UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+ UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+ }
+ return;
+ }
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "EXT (%s): Complete %s SID to interface %s ", __func__,
+ exti->stype == ADJ_SID ? "Adj." : "LAN Adj.",
+ oi->ifp->name);
+
+ /* flood this links params if everything is ok */
+ SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
+ if (OspfEXT.enabled) {
+ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
+ ospf_ext_link_lsa_schedule(exti, REFRESH_THIS_LSA);
+ else
+ ospf_ext_link_lsa_schedule(exti, REORIGINATE_THIS_LSA);
+ }
+
+}
+
+/* Callbacks to handle Extended Link Segment Routing LSA information */
+static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa)
+{
+ /* Sanity Check */
+ if (lsa == NULL) {
+ zlog_warn("EXT (%s): Abort! LSA is NULL", __func__);
+ return -1;
+ }
+
+ /* Process only Opaque LSA */
+ if ((lsa->data->type != OSPF_OPAQUE_AREA_LSA)
+ && (lsa->data->type != OSPF_OPAQUE_AS_LSA))
+ return 0;
+
+ /* Process only Extended Link LSA */
+ if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))
+ != OPAQUE_TYPE_EXTENDED_LINK_LSA)
+ return 0;
+
+ /* Check if Extended is enable */
+ if (!OspfEXT.enabled)
+ return 0;
+
+ /* Call Segment Routing LSA update or deletion */
+ if (!IS_LSA_MAXAGE(lsa))
+ ospf_sr_ext_link_lsa_update(lsa);
+ else
+ ospf_sr_ext_link_lsa_delete(lsa);
+
+ return 0;
+}
+
+/* Callbacks to handle Extended Prefix Segment Routing LSA information */
+static int ospf_ext_pref_lsa_update(struct ospf_lsa *lsa)
+{
+
+ /* Sanity Check */
+ if (lsa == NULL) {
+ zlog_warn("EXT (%s): Abort! LSA is NULL", __func__);
+ return -1;
+ }
+
+ /* Process only Opaque LSA */
+ if ((lsa->data->type != OSPF_OPAQUE_AREA_LSA)
+ && (lsa->data->type != OSPF_OPAQUE_AS_LSA))
+ return 0;
+
+ /* Process only Extended Prefix LSA */
+ if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))
+ != OPAQUE_TYPE_EXTENDED_PREFIX_LSA)
+ return 0;
+
+ /* Check if it is not my LSA */
+ if (IS_LSA_SELF(lsa))
+ return 0;
+
+ /* Check if Extended is enable */
+ if (!OspfEXT.enabled)
+ return 0;
+
+ /* Call Segment Routing LSA update or deletion */
+ if (!IS_LSA_MAXAGE(lsa))
+ ospf_sr_ext_prefix_lsa_update(lsa);
+ else
+ ospf_sr_ext_prefix_lsa_delete(lsa);
+
+ return 0;
+}
+
+/*
+ * -------------------------------------------------------
+ * Followings are OSPF protocol processing functions for
+ * Extended Prefix/Link Opaque LSA
+ * -------------------------------------------------------
+ */
+
+static void build_tlv_header(struct stream *s, struct tlv_header *tlvh)
+{
+ stream_put(s, tlvh, sizeof(struct tlv_header));
+
+}
+
+static void build_tlv(struct stream *s, struct tlv_header *tlvh)
+{
+
+ if ((tlvh != NULL) && (ntohs(tlvh->type) != 0)) {
+ build_tlv_header(s, tlvh);
+ stream_put(s, TLV_DATA(tlvh), TLV_BODY_SIZE(tlvh));
+ }
+
+}
+
+/* Build an Extended Prefix Opaque LSA body for extended prefix TLV */
+static void ospf_ext_pref_lsa_body_set(struct stream *s, struct ext_itf *exti)
+{
+
+ /* Sanity check */
+ if ((exti == NULL) || (exti->stype != PREF_SID))
+ return;
+
+ /* Adjust Extended Prefix TLV size */
+ TLV_LEN(exti->prefix) =
+ htons(ntohs(TLV_LEN(exti->node_sid)) + EXT_TLV_PREFIX_SIZE
+ + TLV_HDR_SIZE);
+
+ /* Build LSA body for an Extended Prefix TLV */
+ build_tlv_header(s, &exti->prefix.header);
+ stream_put(s, TLV_DATA(&exti->prefix.header), EXT_TLV_PREFIX_SIZE);
+ /* Then add Prefix SID SubTLV */
+ build_tlv(s, &exti->node_sid.header);
+
+}
+
+/* Build an Extended Link Opaque LSA body for extended link TLV */
+static void ospf_ext_link_lsa_body_set(struct stream *s, struct ext_itf *exti)
+{
+ size_t size;
+
+ /* Sanity check */
+ if ((exti == NULL)
+ || ((exti->stype != ADJ_SID) && (exti->stype != LAN_ADJ_SID)))
+ return;
+
+ if (exti->stype == ADJ_SID) {
+ /* Adjust Extended Link TLV size for Adj. SID */
+ size = EXT_TLV_LINK_SIZE + 2 * EXT_SUBTLV_ADJ_SID_SIZE
+ + 2 * TLV_HDR_SIZE;
+ if (ntohs(TLV_TYPE(exti->rmt_itf_addr)) != 0)
+ size = size + EXT_SUBTLV_RMT_ITF_ADDR_SIZE
+ + TLV_HDR_SIZE;
+ TLV_LEN(exti->link) = htons(size);
+
+ /* Build LSA body for an Extended Link TLV with Adj. SID */
+ build_tlv_header(s, &exti->link.header);
+ stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE);
+ /* then add Ajacency SubTLVs */
+ build_tlv(s, &exti->adj_sid[1].header);
+ build_tlv(s, &exti->adj_sid[0].header);
+
+ /* Add Cisco experimental SubTLV if interface is PtoP */
+ if (ntohs(TLV_TYPE(exti->rmt_itf_addr)) != 0)
+ build_tlv(s, &exti->rmt_itf_addr.header);
+ } else {
+ /* Adjust Extended Link TLV size for LAN SID */
+ size = EXT_TLV_LINK_SIZE
+ + 2 * (EXT_SUBTLV_LAN_ADJ_SID_SIZE + TLV_HDR_SIZE);
+ TLV_LEN(exti->link) = htons(size);
+
+ /* Build LSA body for an Extended Link TLV with LAN SID */
+ build_tlv_header(s, &exti->link.header);
+ stream_put(s, &exti->link.header, EXT_TLV_LINK_SIZE);
+ /* then add LAN-Ajacency SubTLVs */
+ build_tlv(s, &exti->lan_sid[1].header);
+ build_tlv(s, &exti->lan_sid[0].header);
+ }
+
+}
+
+/* Create new Extended Prefix opaque-LSA for every extended prefix */
+static struct ospf_lsa *ospf_ext_pref_lsa_new(struct ospf_area *area,
+ struct ext_itf *exti)
+{
+ struct stream *s;
+ struct lsa_header *lsah;
+ struct ospf_lsa *new = NULL;
+ struct ospf *top;
+ u_char options, lsa_type;
+ struct in_addr lsa_id;
+ struct in_addr router_id;
+ uint32_t tmp;
+ uint16_t length;
+
+ /* Sanity Check */
+ if (exti == NULL)
+ return NULL;
+
+ /* Create a stream for LSA. */
+ s = stream_new(OSPF_MAX_LSA_SIZE);
+ if (s == NULL) {
+ zlog_warn("EXT (%s): stream_new() error", __func__);
+ return NULL;
+ }
+
+ /* Prepare LSA Header */
+ lsah = (struct lsa_header *)STREAM_DATA(s);
+
+ lsa_type = OspfEXT.scope;
+
+ /*
+ * LSA ID is a variable number identifying different instances of
+ * Extended Prefix Opaque LSA from the same router see RFC 7684
+ */
+ tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, exti->instance);
+ lsa_id.s_addr = htonl(tmp);
+
+ options = OSPF_OPTION_O; /* Don't forget this :-) */
+
+ /* Fix Options and Router ID depending of the flooding scope */
+ if ((OspfEXT.scope == OSPF_OPAQUE_AS_LSA) || (area == NULL)) {
+ options = OSPF_OPTION_E;
+ top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ router_id.s_addr = top ? top->router_id.s_addr : 0;
+ } else {
+ options |= LSA_OPTIONS_GET(area); /* Get area default option */
+ options |= LSA_OPTIONS_NSSA_GET(area);
+ router_id = area->ospf->router_id;
+ }
+
+ /* Set opaque-LSA header fields. */
+ lsa_header_set(s, options, lsa_type, lsa_id, router_id);
+
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "EXT (%s): LSA[Type%u:%s]: Create an Opaque-LSA "
+ "Extended Prefix Opaque LSA instance",
+ __func__, lsa_type, inet_ntoa(lsa_id));
+
+
+ /* Set opaque-LSA body fields. */
+ ospf_ext_pref_lsa_body_set(s, exti);
+
+ /* Set length. */
+ length = stream_get_endp(s);
+ lsah->length = htons(length);
+
+ /* Now, create an OSPF LSA instance. */
+ new = ospf_lsa_new();
+ if (new == NULL) {
+ zlog_warn("EXT (%s): ospf_lsa_new() error", __func__);
+ stream_free(s);
+ return NULL;
+ }
+ new->data = ospf_lsa_data_new(length);
+ if (new->data == NULL) {
+ zlog_warn("EXT (%s): ospf_lsa_data_new() error", __func__);
+ ospf_lsa_unlock(&new);
+ new = NULL;
+ stream_free(s);
+ return NULL;
+ }
+
+ /* Segment Routing belongs only to default VRF */
+ new->vrf_id = VRF_DEFAULT;
+ new->area = area;
+ SET_FLAG(new->flags, OSPF_LSA_SELF);
+ memcpy(new->data, lsah, length);
+ stream_free(s);
+
+ return new;
+}
+
+/* Create new Extended Link opaque-LSA for every extended link TLV */
+static struct ospf_lsa *ospf_ext_link_lsa_new(struct ospf_area *area,
+ struct ext_itf *exti)
+{
+ struct stream *s;
+ struct lsa_header *lsah;
+ struct ospf_lsa *new = NULL;
+ u_char options, lsa_type;
+ struct in_addr lsa_id;
+ uint32_t tmp;
+ uint16_t length;
+
+ /* Sanity Check */
+ if (exti == NULL)
+ return NULL;
+
+ /* Create a stream for LSA. */
+ s = stream_new(OSPF_MAX_LSA_SIZE);
+ if (s == NULL) {
+ zlog_warn("EXT (%s): stream_new() error", __func__);
+ return NULL;
+ }
+ lsah = (struct lsa_header *)STREAM_DATA(s);
+
+ options = OSPF_OPTION_O; /* Don't forget this :-) */
+ options |= LSA_OPTIONS_GET(area); /* Get area default option */
+ options |= LSA_OPTIONS_NSSA_GET(area);
+ /* Extended Link Opaque LSA are only flooded within an area */
+ lsa_type = OSPF_OPAQUE_AREA_LSA;
+
+ /*
+ * LSA ID is a variable number identifying different instances of
+ * Extended Link Opaque LSA from the same router see RFC 7684
+ */
+ tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance);
+ lsa_id.s_addr = htonl(tmp);
+
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "EXT (%s) LSA[Type%u:%s]: Create an Opaque-LSA "
+ "Extended Link Opaque LSA instance",
+ __func__, lsa_type, inet_ntoa(lsa_id));
+
+ /* Set opaque-LSA header fields. */
+ lsa_header_set(s, options, lsa_type, lsa_id, area->ospf->router_id);
+
+ /* Set opaque-LSA body fields. */
+ ospf_ext_link_lsa_body_set(s, exti);
+
+ /* Set length. */
+ length = stream_get_endp(s);
+ lsah->length = htons(length);
+
+ /* Now, create an OSPF LSA instance. */
+ new = ospf_lsa_new();
+ if (new == NULL) {
+ zlog_warn("EXT (%s): ospf_lsa_new() error", __func__);
+ stream_free(s);
+ return NULL;
+ }
+ new->data = ospf_lsa_data_new(length);
+ if (new->data == NULL) {
+ zlog_warn("EXT (%s): ospf_lsa_data_new() error", __func__);
+ ospf_lsa_unlock(&new);
+ new = NULL;
+ stream_free(s);
+ return NULL;
+ }
+
+ /* Segment Routing belongs only to default VRF */
+ new->vrf_id = VRF_DEFAULT;
+ new->area = area;
+ SET_FLAG(new->flags, OSPF_LSA_SELF);
+ memcpy(new->data, lsah, length);
+ stream_free(s);
+
+ return new;
+}
+
+/*
+ * Process the origination of an Extended Prefix Opaque LSA
+ * for every extended prefix TLV
+ */
+static int ospf_ext_pref_lsa_originate1(struct ospf_area *area,
+ struct ext_itf *exti)
+{
+ struct ospf_lsa *new;
+ int rc = -1;
+
+
+ /* Create new Opaque-LSA/Extended Prefix Opaque LSA instance. */
+ new = ospf_ext_pref_lsa_new(area, exti);
+ if (new == NULL) {
+ zlog_warn("EXT (%s): ospf_ext_pref_lsa_new() error", __func__);
+ return rc;
+ }
+
+ /* Install this LSA into LSDB. */
+ if (ospf_lsa_install(area->ospf, NULL /*oi */, new) == NULL) {
+ zlog_warn("EXT (%s): ospf_lsa_install() error", __func__);
+ ospf_lsa_unlock(&new);
+ return rc;
+ }
+
+ /* Now this Extended Prefix Opaque LSA info parameter entry has
+ * associated LSA.
+ */
+ SET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+
+ /* Update new LSA origination count. */
+ area->ospf->lsa_originate_count++;
+
+ /* Flood new LSA through area. */
+ ospf_flood_through_area(area, NULL /*nbr */, new);
+
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+ char area_id[INET_ADDRSTRLEN];
+
+ strncpy(area_id, inet_ntoa(area->area_id), INET_ADDRSTRLEN);
+ zlog_debug(
+ "EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA "
+ "Extended Prefix Opaque LSA: Area(%s), Link(%s)",
+ __func__, new->data->type, inet_ntoa(new->data->id),
+ area_id, exti->ifp->name);
+ ospf_lsa_header_dump(new->data);
+ }
+
+ rc = 0;
+
+ return rc;
+}
+
+/*
+ * Process the origination of an Extended Link Opaque LSA
+ * for every extended link TLV
+ */
+static int ospf_ext_link_lsa_originate1(struct ospf_area *area,
+ struct ext_itf *exti)
+{
+ struct ospf_lsa *new;
+ int rc = -1;
+
+ /* Create new Opaque-LSA/Extended Link Opaque LSA instance. */
+ new = ospf_ext_link_lsa_new(area, exti);
+ if (new == NULL) {
+ zlog_warn("EXT (%s): ospf_ext_link_lsa_new() error", __func__);
+ return rc;
+ }
+
+ /* Install this LSA into LSDB. */
+ if (ospf_lsa_install(area->ospf, NULL /*oi */, new) == NULL) {
+ zlog_warn("EXT (%s): ospf_lsa_install() error", __func__);
+ ospf_lsa_unlock(&new);
+ return rc;
+ }
+
+ /* Now this link-parameter entry has associated LSA. */
+ SET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+
+ /* Update new LSA origination count. */
+ area->ospf->lsa_originate_count++;
+
+ /* Flood new LSA through area. */
+ ospf_flood_through_area(area, NULL /*nbr */, new);
+
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+ char area_id[INET_ADDRSTRLEN];
+
+ strncpy(area_id, inet_ntoa(area->area_id), INET_ADDRSTRLEN);
+ zlog_debug(
+ "EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA "
+ "Extended Link Opaque LSA: Area(%s), Link(%s)",
+ __func__, new->data->type, inet_ntoa(new->data->id),
+ area_id, exti->ifp->name);
+ ospf_lsa_header_dump(new->data);
+ }
+
+ rc = 0;
+
+ return rc;
+}
+
+/* Trigger the origination of Extended Prefix Opaque LSAs */
+static int ospf_ext_pref_lsa_originate(void *arg)
+{
+ struct ospf_area *area = (struct ospf_area *)arg;
+ struct listnode *node;
+ struct ext_itf *exti;
+ int rc = -1;
+
+ if (!OspfEXT.enabled) {
+ zlog_info(
+ "EXT (%s): Segment Routing "
+ "functionality is Disabled now", __func__);
+ rc = 0; /* This is not an error case. */
+ return rc;
+ }
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "EXT (%s): Start Originate Prefix LSA for area %s",
+ __func__, inet_ntoa(area->area_id));
+
+ /* Check if Extended Prefix Opaque LSA is already engaged */
+ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
+
+ /* Process only Prefix SID */
+ if (exti->stype != PREF_SID)
+ continue;
+
+ /* Process only Extended Prefix with valid Area ID */
+ if ((exti->area == NULL)
+ || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id)))
+ continue;
+
+ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+ if (CHECK_FLAG(exti->flags,
+ EXT_LPFLG_LSA_FORCED_REFRESH)) {
+ zlog_warn(
+ "EXT (%s): Refresh instead of "
+ "Originate", __func__);
+ UNSET_FLAG(exti->flags,
+ EXT_LPFLG_LSA_FORCED_REFRESH);
+ ospf_ext_pref_lsa_schedule(exti,
+ REFRESH_THIS_LSA);
+ }
+ continue;
+ }
+
+ /* Ok, let's try to originate an LSA */
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "EXT (%s): Let's finally reoriginate the "
+ "LSA 7.0.0.%u for Itf %s",
+ __func__, exti->instance,
+ exti->ifp ? exti->ifp->name : "");
+ ospf_ext_pref_lsa_originate1(area, exti);
+ }
+
+ rc = 0;
+ return rc;
+}
+
+/* Trigger the origination of Extended Link Opaque LSAs */
+static int ospf_ext_link_lsa_originate(void *arg)
+{
+ struct ospf_area *area = (struct ospf_area *)arg;
+ struct listnode *node;
+ struct ext_itf *exti;
+ int rc = -1;
+
+ if (!OspfEXT.enabled) {
+ zlog_info(
+ "EXT (%s): Segment Routing "
+ "functionality is Disabled now", __func__);
+ rc = 0; /* This is not an error case. */
+ return rc;
+ }
+
+ /* Check if Extended Prefix Opaque LSA is already engaged */
+ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
+ /* Process only Adjacency or LAN SID */
+ if (exti->stype == PREF_SID)
+ continue;
+
+ /* Process only Extended Link with valid Area ID */
+ if ((exti->area == NULL)
+ || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id)))
+ continue;
+
+ /* Check if LSA not already engaged */
+ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
+ if (CHECK_FLAG(exti->flags,
+ EXT_LPFLG_LSA_FORCED_REFRESH)) {
+ zlog_warn(
+ "EXT (%s): Refresh instead of "
+ "Originate", __func__);
+ UNSET_FLAG(exti->flags,
+ EXT_LPFLG_LSA_FORCED_REFRESH);
+ ospf_ext_link_lsa_schedule(exti,
+ REFRESH_THIS_LSA);
+ }
+ continue;
+ }
+
+ /* Ok, let's try to originate an LSA */
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "EXT (%s): Let's finally reoriginate the "
+ "LSA 8.0.0.%u for Itf %s through the Area %s",
+ __func__, exti->instance,
+ exti->ifp ? exti->ifp->name : "-",
+ inet_ntoa(area->area_id));
+ ospf_ext_link_lsa_originate1(area, exti);
+ }
+
+ rc = 0;
+ return rc;
+}
+
+/* Refresh an Extended Prefix Opaque LSA */
+static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa)
+{
+ struct ospf_lsa *new = NULL;
+ struct ospf_area *area = lsa->area;
+ struct ospf *top;
+ struct ext_itf *exti;
+
+ if (!OspfEXT.enabled) {
+ /*
+ * This LSA must have flushed before due to Extended Prefix
+ * Opaque LSA status change.
+ * It seems a slip among routers in the routing domain.
+ */
+ zlog_info(
+ "EXT (%s): Segment Routing functionality is "
+ "Disabled", __func__);
+ /* Flush it anyway. */
+ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ }
+
+ /* Lookup this lsa corresponding Extended parameters */
+ exti = lookup_ext_by_instance(lsa);
+ if (exti == NULL) {
+ zlog_warn("EXT (%s): Invalid parameter LSA ID", __func__);
+ /* Flush it anyway. */
+ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ }
+
+ /* Check if Interface was not disable in the interval */
+ if ((exti != NULL) && !CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) {
+ zlog_warn("EXT (%s): Interface was Disabled: Flush it!",
+ __func__);
+ /* Flush it anyway. */
+ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ }
+
+ /* If the lsa's age reached to MaxAge, start flushing procedure. */
+ if (IS_LSA_MAXAGE(lsa)) {
+ if (exti)
+ UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+ ospf_opaque_lsa_flush_schedule(lsa);
+ return NULL;
+ }
+
+ /* Create new Opaque-LSA/Extended Prefix Opaque LSA instance. */
+ new = ospf_ext_pref_lsa_new(area, exti);
+
+ if (new == NULL) {
+ zlog_warn("EXT (%s): ospf_ext_pref_lsa_new() error", __func__);
+ return NULL;
+ }
+ new->data->ls_seqnum = lsa_seqnum_increment(lsa);
+
+ /*
+ * Install this LSA into LSDB
+ * Given "lsa" will be freed in the next function
+ * As area could be NULL i.e. when using OPAQUE_LSA_AS, we prefer to use
+ * ospf_lookup() to get ospf instance
+ */
+ if (area)
+ top = area->ospf;
+ else
+ top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+
+ if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
+ zlog_warn("EXT (%s): ospf_lsa_install() error", __func__);
+ ospf_lsa_unlock(&new);
+ return NULL;
+ }
+
+ /* Flood updated LSA through the Prefix Area according to the RFC7684 */
+ ospf_flood_through_area(area, NULL /*nbr */, new);
+
+ /* Debug logging. */
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+ zlog_debug(
+ "EXT (%s): LSA[Type%u:%s] Refresh Extended Prefix LSA",
+ __func__, new->data->type, inet_ntoa(new->data->id));
+ ospf_lsa_header_dump(new->data);
+ }
+
+ return new;
+}
+
+/* Refresh an Extended Link Opaque LSA */
+static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa)
+{
+ struct ext_itf *exti;
+ struct ospf_area *area = lsa->area;
+ struct ospf *top = area->ospf;
+ struct ospf_lsa *new = NULL;
+
+ if (!OspfEXT.enabled) {
+ /*
+ * This LSA must have flushed before due to OSPF-SR status
+ * change. It seems a slip among routers in the routing domain.
+ */
+ zlog_info(
+ "EXT (%s): Segment Routing functionality is Disabled",
+ __func__);
+ /* Flush it anyway. */
+ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ }
+
+ /* Lookup this LSA corresponding Extended parameters */
+ exti = lookup_ext_by_instance(lsa);
+ if (exti == NULL) {
+ zlog_warn("EXT (%s): Invalid parameter LSA ID", __func__);
+ /* Flush it anyway. */
+ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ }
+
+ /* Check if Interface was not disable in the interval */
+ if ((exti != NULL) && !CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) {
+ zlog_warn(
+ "EXT (%s): Interface was Disabled: Flush it!",
+ __func__);
+ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ }
+
+ /* If the lsa's age reached to MaxAge, start flushing procedure */
+ if (IS_LSA_MAXAGE(lsa)) {
+ if (exti)
+ UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+ ospf_opaque_lsa_flush_schedule(lsa);
+ return NULL;
+ }
+
+ /* Create new Opaque-LSA/Extended Link instance */
+ new = ospf_ext_link_lsa_new(area, exti);
+ if (new == NULL) {
+ zlog_warn("EXT (%s): Error creating new LSA", __func__);
+ return NULL;
+ }
+ new->data->ls_seqnum = lsa_seqnum_increment(lsa);
+
+ /* Install this LSA into LSDB. */
+ /* Given "lsa" will be freed in the next function */
+ if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
+ zlog_warn("EXT (%s): Error installing new LSA", __func__);
+ ospf_lsa_unlock(&new);
+ return NULL;
+ }
+
+ /* Flood updated LSA through the link Area according to the RFC7684 */
+ ospf_flood_through_area(area, NULL /*nbr */, new);
+
+ /* Debug logging. */
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+ zlog_debug(
+ "EXT (%s): LSA[Type%u:%s]: Refresh Extended Link LSA",
+ __func__, new->data->type, inet_ntoa(new->data->id));
+ ospf_lsa_header_dump(new->data);
+ }
+
+ return new;
+}
+
+/* Schedule Extended Prefix Opaque LSA origination/refreshment/flushing */
+static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti,
+ enum lsa_opcode opcode)
+{
+ struct ospf_lsa lsa;
+ struct lsa_header lsah;
+ struct ospf *top;
+ uint32_t tmp;
+
+ memset(&lsa, 0, sizeof(lsa));
+ memset(&lsah, 0, sizeof(lsah));
+
+ /* Sanity Check */
+ if (exti == NULL)
+ return;
+
+ /* Check if the corresponding link is ready to be flooded */
+ if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)))
+ return;
+
+ zlog_debug(
+ "EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
+ opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
+ opcode == REFRESH_THIS_LSA ? "Refresh" : "",
+ opcode == FLUSH_THIS_LSA ? "Flush" : "",
+ exti->ifp ? exti->ifp->name : "-");
+
+ /* Set LSA header information */
+ if (exti->area == NULL) {
+ zlog_warn(
+ "EXT (%s): Flooding is Area scope but area is not yet "
+ "set", __func__);
+ if (OspfEXT.area == NULL) {
+ top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ OspfEXT.area = ospf_area_lookup_by_area_id(
+ top, OspfEXT.area_id);
+ }
+ exti->area = OspfEXT.area;
+ }
+ lsa.area = exti->area;
+ lsa.data = &lsah;
+ lsah.type = OSPF_OPAQUE_AREA_LSA;
+ tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, exti->instance);
+ lsah.id.s_addr = htonl(tmp);
+
+ switch (opcode) {
+ case REORIGINATE_THIS_LSA:
+ ospf_opaque_lsa_reoriginate_schedule(
+ (void *)exti->area, OSPF_OPAQUE_AREA_LSA,
+ OPAQUE_TYPE_EXTENDED_PREFIX_LSA);
+ break;
+ case REFRESH_THIS_LSA:
+ ospf_opaque_lsa_refresh_schedule(&lsa);
+ break;
+ case FLUSH_THIS_LSA:
+ UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+ ospf_opaque_lsa_flush_schedule(&lsa);
+ break;
+ default:
+ zlog_warn("EXT (%s): Unknown opcode", __func__);
+ break;
+ }
+
+}
+
+/* Schedule Extended Link Opaque LSA origination/refreshment/flushing */
+static void ospf_ext_link_lsa_schedule(struct ext_itf *exti,
+ enum lsa_opcode opcode)
+{
+ struct ospf_lsa lsa;
+ struct lsa_header lsah;
+ struct ospf *top;
+ uint32_t tmp;
+
+ memset(&lsa, 0, sizeof(lsa));
+ memset(&lsah, 0, sizeof(lsah));
+
+ /* Sanity Check */
+ if (exti == NULL)
+ return;
+
+ /* Check if the corresponding link is ready to be flooded */
+ if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)))
+ return;
+
+ zlog_debug(
+ "EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
+ opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
+ opcode == REFRESH_THIS_LSA ? "Refresh" : "",
+ opcode == FLUSH_THIS_LSA ? "Flush" : "",
+ exti->ifp ? exti->ifp->name : "-");
+
+ /* Set LSA header information */
+ if (exti->area == NULL) {
+ zlog_warn(
+ "EXT (%s): Flooding is Area scope but area is not "
+ "yet set", __func__);
+ if (OspfEXT.area == NULL) {
+ top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ OspfEXT.area = ospf_area_lookup_by_area_id(
+ top, OspfEXT.area_id);
+ }
+ exti->area = OspfEXT.area;
+ }
+ lsa.area = exti->area;
+ lsa.data = &lsah;
+ lsah.type = OSPF_OPAQUE_AREA_LSA;
+ tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance);
+ lsah.id.s_addr = htonl(tmp);
+
+ switch (opcode) {
+ case REORIGINATE_THIS_LSA:
+ ospf_opaque_lsa_reoriginate_schedule(
+ (void *)exti->area, OSPF_OPAQUE_AREA_LSA,
+ OPAQUE_TYPE_EXTENDED_LINK_LSA);
+ break;
+ case REFRESH_THIS_LSA:
+ ospf_opaque_lsa_refresh_schedule(&lsa);
+ break;
+ case FLUSH_THIS_LSA:
+ UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
+ ospf_opaque_lsa_flush_schedule(&lsa);
+ break;
+ default:
+ zlog_warn("EXT (%s): Unknown opcode", __func__);
+ break;
+ }
+
+}
+
+/* Schedule Extended Link or Prefix depending of the Type of LSA */
+static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op)
+{
+
+ if (exti->stype == PREF_SID)
+ ospf_ext_pref_lsa_schedule(exti, op);
+ else
+ ospf_ext_link_lsa_schedule(exti, op);
+}
+
+/*
+ * ------------------------------------
+ * Followings are vty show functions.
+ * ------------------------------------
+ */
+
+/* Cisco experimental SubTLV */
+static uint16_t show_vty_ext_link_rmt_itf_addr(struct vty *vty,
+ struct tlv_header *tlvh)
+{
+ struct ext_subtlv_rmt_itf_addr *top;
+
+ top = (struct ext_subtlv_rmt_itf_addr *)tlvh;
+
+ vty_out(vty,
+ " Remote Interface Address Sub-TLV: Length %u\n "
+ "Address: %s\n",
+ ntohs(top->header.length), inet_ntoa(top->value));
+
+ return TLV_SIZE(tlvh);
+}
+
+/* Adjacency SID SubTLV */
+static uint16_t show_vty_ext_link_adj_sid(struct vty *vty,
+ struct tlv_header *tlvh)
+{
+ struct ext_subtlv_adj_sid *top = (struct ext_subtlv_adj_sid *)tlvh;
+
+ vty_out(vty,
+ " Adj-SID Sub-TLV: Length %u\n\tFlags: "
+ "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n",
+ ntohs(top->header.length), top->flags, top->mtid, top->weight,
+ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label"
+ : "Index",
+ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)
+ ? GET_LABEL(ntohl(top->value))
+ : ntohl(top->value));
+
+ return TLV_SIZE(tlvh);
+}
+
+/* LAN Adjacency SubTLV */
+static uint16_t show_vty_ext_link_lan_adj_sid(struct vty *vty,
+ struct tlv_header *tlvh)
+{
+ struct ext_subtlv_lan_adj_sid *top =
+ (struct ext_subtlv_lan_adj_sid *)tlvh;
+
+ vty_out(vty,
+ " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: "
+ "0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: "
+ "%s\n\tLabel: %u\n",
+ ntohs(top->header.length), top->flags, top->mtid, top->weight,
+ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label"
+ : "Index",
+ CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG)
+ ? GET_LABEL(ntohl(top->value))
+ : ntohl(top->value));
+
+ return TLV_SIZE(tlvh);
+}
+
+static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh)
+{
+ vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n",
+ ntohs(tlvh->type), ntohs(tlvh->length));
+
+ return TLV_SIZE(tlvh);
+}
+
+/* Extended Link Sub TLVs */
+static uint16_t show_vty_link_info(struct vty *vty, struct tlv_header *ext)
+{
+ struct ext_tlv_link *top = (struct ext_tlv_link *)ext;
+ struct tlv_header *tlvh;
+ uint16_t length = ntohs(top->header.length) - 3 * sizeof(uint32_t);
+ uint16_t sum = 0;
+
+ vty_out(vty,
+ " Extended Link TLV: Length %u\n Link Type: 0x%x\n"
+ " Link ID: %s\n",
+ ntohs(top->header.length), top->link_type,
+ inet_ntoa(top->link_id));
+ vty_out(vty, " Link data: %s\n", inet_ntoa(top->link_data));
+
+ tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE
+ + EXT_TLV_LINK_SIZE);
+ for (; sum < length; tlvh = TLV_HDR_NEXT(tlvh)) {
+ switch (ntohs(tlvh->type)) {
+ case EXT_SUBTLV_ADJ_SID:
+ sum += show_vty_ext_link_adj_sid(vty, tlvh);
+ break;
+ case EXT_SUBTLV_LAN_ADJ_SID:
+ sum += show_vty_ext_link_lan_adj_sid(vty, tlvh);
+ break;
+ case EXT_SUBTLV_RMT_ITF_ADDR:
+ sum += show_vty_ext_link_rmt_itf_addr(vty, tlvh);
+ break;
+ default:
+ sum += show_vty_unknown_tlv(vty, tlvh);
+ break;
+ }
+ }
+
+ return sum + sizeof(struct ext_tlv_link);
+}
+
+/* Extended Link TLVs */
+static void ospf_ext_link_show_info(struct vty *vty, struct ospf_lsa *lsa)
+{
+ struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+ struct tlv_header *tlvh;
+ uint16_t length = 0, sum = 0;
+
+ /* Initialize TLV browsing */
+ length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+
+ for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+ tlvh = TLV_HDR_NEXT(tlvh)) {
+ switch (ntohs(tlvh->type)) {
+ case EXT_TLV_LINK:
+ sum += show_vty_link_info(vty, tlvh);
+ break;
+ default:
+ sum += show_vty_unknown_tlv(vty, tlvh);
+ break;
+ }
+ }
+
+}
+
+/* Prefix SID SubTLV */
+static uint16_t show_vty_ext_pref_pref_sid(struct vty *vty,
+ struct tlv_header *tlvh)
+{
+ struct ext_subtlv_prefix_sid *top =
+ (struct ext_subtlv_prefix_sid *)tlvh;
+
+ vty_out(vty,
+ " Prefix SID Sub-TLV: Length %u\n\tAlgorithm: "
+ "%u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n",
+ ntohs(top->header.length), top->algorithm, top->flags,
+ top->mtid,
+ CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ? "Label"
+ : "Index",
+ CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG)
+ ? GET_LABEL(ntohl(top->value))
+ : ntohl(top->value));
+
+ return TLV_SIZE(tlvh);
+}
+
+/* Extended Prefix SubTLVs */
+static uint16_t show_vty_pref_info(struct vty *vty, struct tlv_header *ext)
+{
+ struct ext_tlv_prefix *top = (struct ext_tlv_prefix *)ext;
+ struct tlv_header *tlvh;
+ uint16_t length = ntohs(top->header.length) - 2 * sizeof(uint32_t);
+ uint16_t sum = 0;
+
+ vty_out(vty,
+ " Extended Prefix TLV: Length %u\n\tRoute Type: %u\n"
+ "\tAddress Family: 0x%x\n\tFlags: 0x%x\n\tAddress: %s/%u\n",
+ ntohs(top->header.length), top->route_type, top->af, top->flags,
+ inet_ntoa(top->address), top->pref_length);
+
+ tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE
+ + EXT_TLV_PREFIX_SIZE);
+ for (; sum < length; tlvh = TLV_HDR_NEXT(tlvh)) {
+ switch (ntohs(tlvh->type)) {
+ case EXT_SUBTLV_PREFIX_SID:
+ sum += show_vty_ext_pref_pref_sid(vty, tlvh);
+ break;
+ default:
+ sum += show_vty_unknown_tlv(vty, tlvh);
+ break;
+ }
+ }
+
+ return sum + sizeof(struct ext_tlv_prefix);
+}
+
+/* Extended Prefix TLVs */
+static void ospf_ext_pref_show_info(struct vty *vty, struct ospf_lsa *lsa)
+{
+ struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+ struct tlv_header *tlvh;
+ uint16_t length = 0, sum = 0;
+
+ /* Initialize TLV browsing */
+ length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+
+ for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+ tlvh = TLV_HDR_NEXT(tlvh)) {
+ switch (ntohs(tlvh->type)) {
+ case EXT_TLV_PREFIX:
+ sum += show_vty_pref_info(vty, tlvh);
+ break;
+ default:
+ sum += show_vty_unknown_tlv(vty, tlvh);
+ break;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * This is an implementation of RFC7684 OSPFv2 Prefix/Link Attribute
+ * Advertisement
+ *
+ * Module name: Extended Prefix/Link Opaque LSA header definition
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ *
+ * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_OSPF_EXT_PREF_H_
+#define _FRR_OSPF_EXT_PREF_H_
+
+/*
+ * Opaque LSA's link state ID for Extended Prefix/Link is
+ * structured as follows.
+ *
+ * 24 16 8 0
+ * +--------+--------+--------+--------+
+ * | 7/8 |........|........|........|
+ * +--------+--------+--------+--------+
+ * |<-Type->|<------- Instance ------->|
+ *
+ *
+ * Type: IANA has assigned '7' for Extended Prefix Opaque LSA
+ * and '8' for Extended Link Opaque LSA
+ * Instance: User may select arbitrary 24-bit values to identify
+ * different instances of Extended Prefix/Link Opaque LSA
+ *
+ */
+
+/*
+ * 24 16 8 0
+ * +--------+--------+--------+--------+ ---
+ * | LS age |Options | 10,11 | A
+ * +--------+--------+--------+--------+ | Standard (Opaque) LSA header;
+ * | 7/8 | Instance | |
+ * +--------+--------+--------+--------+ | Type 10 or 11 are used for Extended
+ * | Advertising router | | Prefix Opaque LSA
+ * +--------+--------+--------+--------+ |
+ * | LS sequence number | | Type 10 only is used for Extended
+ * +--------+--------+--------+--------+ | Link Opaque LSA
+ * | LS checksum | Length | V
+ * +--------+--------+--------+--------+ ---
+ * | Type | Length | A
+ * +--------+--------+--------+--------+ | TLV part for Extended Prefix/Link
+ * | | | Opaque LSA;
+ * ~ Values ... ~ | Values might be structured as a set
+ * | | V of sub-TLVs.
+ * +--------+--------+--------+--------+ ---
+ */
+
+/* Global use constant numbers */
+
+#define MAX_LEGAL_EXT_INSTANCE_NUM (0xffff)
+#define LEGAL_EXT_INSTANCE_RANGE(i) (0 <= (i) && (i) <= 0xffff)
+
+/* Flags to manage Extended Link/Prefix Opaque LSA */
+#define EXT_LPFLG_LSA_INACTIVE 0x00
+#define EXT_LPFLG_LSA_ACTIVE 0x01
+#define EXT_LPFLG_LSA_ENGAGED 0x02
+#define EXT_LPFLG_LSA_LOOKUP_DONE 0x04
+#define EXT_LPFLG_LSA_FORCED_REFRESH 0x08
+#define EXT_LPFLG_FIB_ENTRY_SET 0x10
+
+/*
+ * Following section defines TLV (tag, length, value) structures,
+ * used in Extended Prefix/Link Opaque LSA.
+ */
+
+/* Extended Prefix TLV Route Types */
+#define EXT_TLV_PREF_ROUTE_UNSPEC 0
+#define EXT_TLV_PREF_ROUTE_INTRA_AREA 1
+#define EXT_TLV_PREF_ROUTE_INTER_AREA 3
+#define EXT_TLV_PREF_ROUTE_AS_EXT 5
+#define EXT_TLV_PREF_ROUTE_NSSA_EXT 7
+
+/*
+ * Extended Prefix and Extended Prefix Range TLVs'
+ * Address family flag for IPv4
+ */
+#define EXT_TLV_PREF_AF_IPV4 0
+
+/* Extended Prefix TLV Flags */
+#define EXT_TLV_PREF_AFLG 0x80
+#define EXT_TLV_PREF_NFLG 0x40
+
+/* Extended Prefix Range TLV Flags */
+#define EXT_TLV_PREF_RANGE_IAFLG 0x80
+
+/* ERO subtlvs Flags */
+#define EXT_SUBTLV_ERO_LFLG 0x80
+
+/* Extended Prefix TLV see RFC 7684 section 2.1 */
+#define EXT_TLV_PREFIX 1
+#define EXT_TLV_PREFIX_SIZE 8
+struct ext_tlv_prefix {
+ struct tlv_header header;
+ uint8_t route_type;
+ uint8_t pref_length;
+ uint8_t af;
+ uint8_t flags;
+ struct in_addr address;
+};
+
+/* Extended Link TLV see RFC 7684 section 3.1 */
+#define EXT_TLV_LINK 1
+#define EXT_TLV_LINK_SIZE 12
+struct ext_tlv_link {
+ struct tlv_header header;
+ uint8_t link_type;
+ uint8_t reserved[3];
+ struct in_addr link_id;
+ struct in_addr link_data;
+};
+
+/* Remote Interface Address Sub-TLV, Cisco experimental use Sub-TLV */
+#define EXT_SUBTLV_RMT_ITF_ADDR 32768
+#define EXT_SUBTLV_RMT_ITF_ADDR_SIZE 4
+struct ext_subtlv_rmt_itf_addr {
+ struct tlv_header header;
+ struct in_addr value;
+};
+
+/* Internal structure to manage Extended Link/Prefix Opaque LSA */
+struct ospf_ext_lp {
+ bool enabled;
+
+ /* Flags to manage this Extended Prefix/Link Opaque LSA */
+ uint32_t flags;
+
+ /*
+ * Scope is area Opaque Type 10 or AS Opaque LSA Type 11 for
+ * Extended Prefix and area Opaque Type 10 for Extended Link
+ */
+ uint8_t scope;
+
+ /* area pointer if flooding is Type 10 Null if flooding is AS scope */
+ struct ospf_area *area;
+ struct in_addr area_id;
+
+ /* List of interface with Segment Routing enable */
+ struct list *iflist;
+};
+
+/* Structure to aggregate interfaces information for Extended Prefix/Link */
+struct ext_itf {
+ /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+ uint32_t instance;
+ uint8_t type; /* Extended Prefix (7) or Link (8) */
+
+ /* Reference pointer to a Zebra-interface. */
+ struct interface *ifp;
+
+ /* Area info in which this SR link belongs to. */
+ struct ospf_area *area;
+
+ /* Flags to manage this link parameters. */
+ uint32_t flags;
+
+ /* SID type: Node, Adjacency or LAN Adjacency */
+ enum sid_type stype;
+
+ /* extended link/prefix TLV information */
+ struct ext_tlv_prefix prefix;
+ struct ext_subtlv_prefix_sid node_sid;
+ struct ext_tlv_link link;
+ struct ext_subtlv_adj_sid adj_sid[2];
+ struct ext_subtlv_lan_adj_sid lan_sid[2];
+
+ /* cisco experimental subtlv */
+ struct ext_subtlv_rmt_itf_addr rmt_itf_addr;
+};
+
+/* Prototypes. */
+extern int ospf_ext_init(void);
+extern void ospf_ext_term(void);
+extern void ospf_ext_finish(void);
+extern void ospf_ext_update_sr(bool enable);
+extern uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp,
+ uint32_t index,
+ struct prefix_ipv4 *p,
+ uint8_t flags);
+#endif /* _FRR_OSPF_EXT_PREF_H_ */
struct ospf_interface *oi;
int lsa_ack_flag = 0;
+ assert(area);
/* All other types are specific to a single area (Area A). The
eligible interfaces are all those interfaces attaching to the
Area A. If Area A is the backbone, this includes all the virtual
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);
}
/*
DEFINE_MTYPE(OSPFD, OSPF_MESSAGE, "OSPF message")
DEFINE_MTYPE(OSPFD, OSPF_MPLS_TE, "OSPF MPLS parameters")
DEFINE_MTYPE(OSPFD, OSPF_PCE_PARAMS, "OSPF PCE parameters")
+DEFINE_MTYPE(OSPFD, OSPF_EXT_PARAMS, "OSPF Extended parameters")
+DEFINE_MTYPE(OSPFD, OSPF_SR_PARAMS, "OSPF Segment Routing parameters")
DECLARE_MTYPE(OSPF_MESSAGE)
DECLARE_MTYPE(OSPF_MPLS_TE)
DECLARE_MTYPE(OSPF_PCE_PARAMS)
+DECLARE_MTYPE(OSPF_SR_PARAMS)
+DECLARE_MTYPE(OSPF_EXT_PARAMS)
#endif /* _QUAGGA_OSPF_MEMORY_H */
#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);
#include "ospfd/ospf_route.h"
#include "ospfd/ospf_ase.h"
#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_te.h"
+#include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ri.h"
+#include "ospfd/ospf_ext.h"
DEFINE_MTYPE_STATIC(OSPFD, OSPF_OPAQUE_FUNCTAB, "OSPF opaque function table")
DEFINE_MTYPE_STATIC(OSPFD, OPAQUE_INFO_PER_TYPE, "OSPF opaque per-type info")
* Followings are initialize/terminate functions for Opaque-LSAs handling.
*------------------------------------------------------------------------*/
-#include "ospfd/ospf_te.h"
-#include "ospfd/ospf_ri.h"
-
#ifdef SUPPORT_OSPF_API
int ospf_apiserver_init(void);
void ospf_apiserver_term(void);
static void ospf_opaque_funclist_term(void);
static void free_opaque_info_per_type(void *val);
static void free_opaque_info_per_id(void *val);
+static void free_opaque_info_owner(void *val);
static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa);
static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa);
if (ospf_mpls_te_init() != 0)
exit(1);
+ /* Segment Routing init */
+ if (ospf_sr_init() != 0)
+ exit(1);
+
if (ospf_router_info_init() != 0)
exit(1);
+ if (ospf_ext_init() != 0)
+ exit(1);
+
#ifdef SUPPORT_OSPF_API
if ((ospf_apiserver_enable) && (ospf_apiserver_init() != 0))
exit(1);
ospf_router_info_term();
+ ospf_ext_term();
+
+ ospf_sr_term();
+
#ifdef SUPPORT_OSPF_API
ospf_apiserver_term();
#endif /* SUPPORT_OSPF_API */
return;
}
+void ospf_opaque_finish(void)
+{
+ ospf_router_info_finish();
+
+ ospf_ext_finish();
+
+ ospf_sr_finish();
+}
+
int ospf_opaque_type9_lsa_init(struct ospf_interface *oi)
{
if (oi->opaque_lsa_self != NULL)
case OPAQUE_TYPE_ROUTER_INFORMATION_LSA:
name = "Router Information LSA";
break;
+ case OPAQUE_TYPE_EXTENDED_PREFIX_LSA:
+ name = "Extended Prefix Opaque LSA";
+ break;
+ case OPAQUE_TYPE_EXTENDED_LINK_LSA:
+ name = "Extended Link Opaque LSA";
+ break;
default:
if (OPAQUE_TYPE_RANGE_UNASSIGNED(opaque_type))
name = "Unassigned";
if (functab->opaque_type == opaque_type) {
/* Cleanup internal control information, if it
* still remains. */
- if (functab->oipt != NULL)
+ if (functab->oipt != NULL) {
free_opaque_info_per_type(
functab->oipt);
+ free_opaque_info_owner(functab->oipt);
+ }
/* Dequeue listnode entry from the list. */
listnode_delete(funclist, functab);
top = ospf_lookup_by_vrf_id(new->vrf_id);
if (new->area != NULL && (top = new->area->ospf) == NULL) {
free_opaque_info_per_type((void *)oipt);
+ free_opaque_info_owner(oipt);
oipt = NULL;
goto out; /* This case may not exist. */
}
"register_opaque_info_per_type: Unexpected LSA-type(%u)",
new->data->type);
free_opaque_info_per_type((void *)oipt);
+ free_opaque_info_owner(oipt);
oipt = NULL;
goto out; /* This case may not exist. */
}
return oipt;
}
+/* Remove "oipt" from its owner's self-originated LSA list. */
+static void free_opaque_info_owner(void *val)
+{
+ struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
+
+ 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_owner: Unexpected LSA-type(%u)",
+ oipt->lsa_type);
+ break; /* This case may not exist. */
+ }
+}
+
static void free_opaque_info_per_type(void *val)
{
struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
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);
lsa_type, delay,
GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)));
- OSPF_OPAQUE_TIMER_ON(oipt->t_opaque_lsa_self, func, oipt, delay * 1000);
+ OSPF_OPAQUE_TIMER_ON(oipt->t_opaque_lsa_self, func, oipt, delay);
out:
return;
#define OPAQUE_TYPE_L1VPN_LSA 5
#define OPAQUE_TYPE_ROUTER_INFORMATION_LSA 4
#define OPAQUE_TYPE_INTER_AS_LSA 6
-#define OPAQUE_TYPE_MAX 6
+#define OPAQUE_TYPE_EXTENDED_PREFIX_LSA 7
+#define OPAQUE_TYPE_EXTENDED_LINK_LSA 8
+#define OPAQUE_TYPE_MAX 8
/* Followings types are proposed in internet-draft documents. */
#define OPAQUE_TYPE_8021_QOSPF 129
extern void ospf_opaque_init(void);
extern void ospf_opaque_term(void);
+extern void ospf_opaque_finish(void);
extern int ospf_opaque_type9_lsa_init(struct ospf_interface *oi);
extern void ospf_opaque_type9_lsa_term(struct ospf_interface *oi);
extern int ospf_opaque_type10_lsa_init(struct ospf_area *area);
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;
}
}
zlog_debug("listcount = %d, [%s]dst %s", listcount(update),
IF_NAME(oi), inet_ntoa(addr));
+ /* Check that we have really something to process */
+ if (listcount(update) == 0)
+ return;
+
op = ospf_ls_upd_packet_new(update, oi);
/* Prepare OSPF common header. */
* with support of RFC5088 PCE Capabilites announcement
*
* Module name: Router Information
- * Version: 0.99.22
- * Created: 2012-02-01 by Olivier Dugeon
- * Copyright (C) 2012 Orange Labs http://www.orange.com/
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Copyright (C) 2012 - 2017 Orange Labs http://www.orange.com/
*
* This file is part of GNU Quagga.
*
#include "thread.h"
#include "hash.h"
#include "sockunion.h" /* for inet_aton() */
+#include "mpls.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.h"
#include "ospfd/ospf_route.h"
#include "ospfd/ospf_ase.h"
#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_sr.h"
#include "ospfd/ospf_ri.h"
-#include "ospfd/ospf_te.h"
/* Store Router Information PCE TLV and SubTLV in network byte order. */
struct ospf_pce_info {
struct ri_pce_subtlv_cap_flag pce_cap_flag;
};
+/*
+ * Store Router Information Segment Routing TLV and SubTLV
+ * in network byte order
+ */
+struct ospf_ri_sr_info {
+ bool enabled;
+ /* Algorithms supported by the node */
+ struct ri_sr_tlv_sr_algorithm algo;
+ /*
+ * Segment Routing Global Block i.e. label range
+ * Only one range supported in this code
+ */
+ struct ri_sr_tlv_sid_label_range range;
+ /* Maximum SID Depth supported by the node */
+ struct ri_sr_tlv_node_msd msd;
+};
+
/* Following structure are internal use only. */
struct ospf_router_info {
bool enabled;
u_int8_t scope;
/* Flags to manage this router information. */
-#define RIFLG_LSA_ENGAGED 0x1
+#define RIFLG_LSA_ENGAGED 0x1
#define RIFLG_LSA_FORCED_REFRESH 0x2
u_int32_t flags;
/* Store PCE capability LSA */
struct ospf_pce_info pce_info;
+
+ /* Store SR capability LSA */
+ struct ospf_ri_sr_info sr_info;
};
/*
static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa);
static void ospf_router_info_lsa_schedule(enum lsa_opcode opcode);
static void ospf_router_info_register_vty(void);
+static int ospf_router_info_lsa_update(struct ospf_lsa *lsa);
static void del_pce_info(void *val);
int ospf_router_info_init(void)
{
+ zlog_info("RI -> Initialize Router Information");
+
memset(&OspfRI, 0, sizeof(struct ospf_router_info));
OspfRI.enabled = false;
OspfRI.registered = 0;
OspfRI.scope = OSPF_OPAQUE_AS_LSA;
+ OspfRI.area_id.s_addr = 0;
OspfRI.flags = 0;
/* Initialize pce domain and neighbor list */
OspfRI.pce_info.pce_neighbor = list_new();
OspfRI.pce_info.pce_neighbor->del = del_pce_info;
+ /* Initialize Segment Routing information structure */
+ OspfRI.sr_info.enabled = false;
+
ospf_router_info_register_vty();
return 0;
if (OspfRI.registered)
return rc;
- zlog_info("Register Router Information with scope %s(%d)",
+ zlog_info("RI -> Register Router Information with scope %s(%d)",
scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS", scope);
rc = ospf_register_opaque_functab(
scope, OPAQUE_TYPE_ROUTER_INFORMATION_LSA,
NULL, /* new interface */
NULL, /* del interface */
- ospf_router_info_ism_change, ospf_router_info_nsm_change,
+ ospf_router_info_ism_change,
+ ospf_router_info_nsm_change,
ospf_router_info_config_write_router,
NULL, /* Config. write interface */
NULL, /* Config. write debug */
- ospf_router_info_show_info, ospf_router_info_lsa_originate,
- ospf_router_info_lsa_refresh, NULL, /* new_lsa_hook */
- NULL); /* del_lsa_hook */
+ ospf_router_info_show_info,
+ ospf_router_info_lsa_originate,
+ ospf_router_info_lsa_refresh,
+ ospf_router_info_lsa_update,
+ NULL); /* del_lsa_hook */
if (rc != 0) {
zlog_warn(
return;
}
+void ospf_router_info_finish(void)
+{
+ list_delete_all_node(OspfRI.pce_info.pce_domain);
+ list_delete_all_node(OspfRI.pce_info.pce_neighbor);
+
+ OspfRI.enabled = false;
+}
+
static void del_pce_info(void *val)
{
XFREE(MTYPE_OSPF_PCE_PARAMS, val);
return;
}
+/* Catch RI LSA flooding Scope for ospf_ext.[h,c] code */
+struct scope_info ospf_router_info_get_flooding_scope(void)
+{
+ struct scope_info flooding_scope;
+
+ if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) {
+ flooding_scope.scope = OSPF_OPAQUE_AS_LSA;
+ flooding_scope.area_id.s_addr = 0;
+ return flooding_scope;
+ }
+ flooding_scope.scope = OSPF_OPAQUE_AREA_LSA;
+ flooding_scope.area_id.s_addr = OspfRI.area_id.s_addr;
+ return flooding_scope;
+}
+
/*------------------------------------------------------------------------*
* Followings are control functions for ROUTER INFORMATION parameters
*management.
return;
}
+/* Segment Routing TLV setter */
+
+/* Algorithm SubTLV - section 3.1 */
+static void set_sr_algorithm(uint8_t algo)
+{
+
+ OspfRI.sr_info.algo.value[0] = algo;
+ for (int i = 1; i < ALGORITHM_COUNT; i++)
+ OspfRI.sr_info.algo.value[i] = SR_ALGORITHM_UNSET;
+
+ /* Set TLV type and length == only 1 Algorithm */
+ TLV_TYPE(OspfRI.sr_info.algo) = htons(RI_SR_TLV_SR_ALGORITHM);
+ TLV_LEN(OspfRI.sr_info.algo) = htons(sizeof(uint8_t));
+
+}
+
+/* unset Aglogithm SubTLV */
+static void unset_sr_algorithm(uint8_t algo)
+{
+
+ for (int i = 0; i < ALGORITHM_COUNT; i++)
+ OspfRI.sr_info.algo.value[i] = SR_ALGORITHM_UNSET;
+
+ /* Unset TLV type and length */
+ TLV_TYPE(OspfRI.sr_info.algo) = htons(0);
+ TLV_LEN(OspfRI.sr_info.algo) = htons(0);
+
+}
+
+/* Segment Routing Global Block SubTLV - section 3.2 */
+static void set_sr_sid_label_range(struct sr_srgb srgb)
+{
+ /* Set Header */
+ TLV_TYPE(OspfRI.sr_info.range) = htons(RI_SR_TLV_SID_LABEL_RANGE);
+ TLV_LEN(OspfRI.sr_info.range) =
+ htons(SUBTLV_SID_LABEL_SIZE + sizeof(uint32_t));
+ /* Set Range Size */
+ OspfRI.sr_info.range.size = htonl(SET_RANGE_SIZE(srgb.range_size));
+ /* Set Lower bound label SubTLV */
+ TLV_TYPE(OspfRI.sr_info.range.lower) = htons(SUBTLV_SID_LABEL);
+ TLV_LEN(OspfRI.sr_info.range.lower) = htons(SID_RANGE_LABEL_LENGTH);
+ OspfRI.sr_info.range.lower.value = htonl(SET_LABEL(srgb.lower_bound));
+
+}
+
+/* Unset this SRGB SubTLV */
+static void unset_sr_sid_label_range(void)
+{
+
+ TLV_TYPE(OspfRI.sr_info.range) = htons(0);
+ TLV_LEN(OspfRI.sr_info.range) = htons(0);
+ TLV_TYPE(OspfRI.sr_info.range.lower) = htons(0);
+ TLV_LEN(OspfRI.sr_info.range.lower) = htons(0);
+
+}
+
+/* Set Maximum Stack Depth for this router */
+static void set_sr_node_msd(uint8_t msd)
+{
+ TLV_TYPE(OspfRI.sr_info.msd) = htons(RI_SR_TLV_NODE_MSD);
+ TLV_LEN(OspfRI.sr_info.msd) = htons(sizeof(uint32_t));
+ OspfRI.sr_info.msd.value = msd;
+
+}
+
+/* Unset this router MSD */
+static void unset_sr_node_msd(void)
+{
+ TLV_TYPE(OspfRI.sr_info.msd) = htons(0);
+ TLV_LEN(OspfRI.sr_info.msd) = htons(0);
+
+}
static void unset_param(struct tlv_header *tlv)
{
&& (ntohs(ori.pce_info.pce_cap_flag.header.type) == 0))
return rc;
+ if ((ori.sr_info.enabled) && (ntohs(TLV_TYPE(ori.sr_info.algo)) == 0)
+ && (ntohs(TLV_TYPE(ori.sr_info.range)) == 0))
+ return rc;
+
rc = 1;
return rc;
}
+/*
+ * Used by Segment Routing to set new TLVs and Sub-TLVs values
+ *
+ * @param enable To activate or not Segment Routing router Information flooding
+ * @param size Size of Label Range i.e. SRGB size
+ * @param lower Lower bound of the Label Range i.e. SRGB first label
+ * @param msd Maximum label Stack Depth suported by the router
+ *
+ * @return none
+ */
+void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, uint8_t msd)
+{
+
+ /* First activate and initialize Router Information is necessary */
+ if (!OspfRI.enabled) {
+ OspfRI.enabled = true;
+ initialize_params(&OspfRI);
+ }
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug("RI-> %s Routing Information for Segment Routing",
+ enable ? "Enable" : "Disable");
+
+ /* Unset or Set SR parameters */
+ if (!enable) {
+ unset_sr_algorithm(SR_ALGORITHM_SPF);
+ unset_sr_sid_label_range();
+ unset_sr_node_msd();
+ OspfRI.sr_info.enabled = false;
+ } else {
+ // Only SR_ALGORITHM_SPF is supported
+ set_sr_algorithm(SR_ALGORITHM_SPF);
+ set_sr_sid_label_range(srgb);
+ if (msd != 0)
+ set_sr_node_msd(msd);
+ else
+ unset_sr_node_msd();
+ OspfRI.sr_info.enabled = true;
+ }
+
+ /* Refresh if already engaged or originate RI LSA */
+ if (CHECK_FLAG(OspfRI.flags, RIFLG_LSA_ENGAGED))
+ ospf_router_info_lsa_schedule(REFRESH_THIS_LSA);
+ else
+ ospf_router_info_lsa_schedule(REORIGINATE_THIS_LSA);
+}
+
/*------------------------------------------------------------------------*
* Followings are callback functions against generic Opaque-LSAs handling.
*------------------------------------------------------------------------*/
/* Build Router Information TLV */
build_tlv(s, &OspfRI.router_cap.header);
- /* Compute PCE Info header first */
- set_pce_header (&OspfRI.pce_info);
+ /* Build Segment Routing TLVs if enabled */
+ if (OspfRI.sr_info.enabled) {
+ /* Build Algorithm TLV */
+ build_tlv(s, &TLV_HDR(OspfRI.sr_info.algo));
+ /* Build SRGB TLV */
+ build_tlv(s, &TLV_HDR(OspfRI.sr_info.range));
+ /* Build MSD TLV */
+ build_tlv(s, &TLV_HDR(OspfRI.sr_info.msd));
+ }
/* Add RI PCE TLV if it is set */
if (OspfRI.pce_info.enabled) {
+ /* Compute PCE Info header first */
+ set_pce_header (&OspfRI.pce_info);
+
/* Build PCE TLV */
build_tlv_header(s, &OspfRI.pce_info.pce_header.header);
return;
}
+/* Callback to handle Segment Routing information */
+static int ospf_router_info_lsa_update(struct ospf_lsa *lsa)
+{
+
+ /* Sanity Check */
+ if (lsa == NULL) {
+ zlog_warn("OSPF-RI (%s): Abort! LSA is NULL", __func__);
+ return -1;
+ }
+
+ /* Process only Opaque LSA */
+ if ((lsa->data->type != OSPF_OPAQUE_AREA_LSA)
+ && (lsa->data->type != OSPF_OPAQUE_AS_LSA))
+ return 0;
+
+ /* Process only Router Information LSA */
+ if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)) !=
+ OPAQUE_TYPE_ROUTER_INFORMATION_LSA)
+ return 0;
+
+ /* Check if it is not my LSA */
+ if (IS_LSA_SELF(lsa))
+ return 0;
+
+ /* Check if Router Info & Segment Routing are enable */
+ if (!OspfRI.enabled || !OspfRI.sr_info.enabled)
+ return 0;
+
+ /* Call Segment Routing LSA update or deletion */
+ if (!IS_LSA_MAXAGE(lsa))
+ ospf_sr_ri_lsa_update(lsa);
+ else
+ ospf_sr_ri_lsa_delete(lsa);
+
+ return 0;
+}
+
/*------------------------------------------------------------------------*
* Followings are vty session control functions.
*------------------------------------------------------------------------*/
return sum;
}
+/* Display Segment Routing Algorithm TLV information */
+static uint16_t show_vty_sr_algorithm(struct vty *vty, struct tlv_header *tlvh)
+{
+ struct ri_sr_tlv_sr_algorithm *algo =
+ (struct ri_sr_tlv_sr_algorithm *)tlvh;
+ int i;
+
+ if (vty != NULL) {
+ vty_out(vty, " Segment Routing Algorithm TLV:\n");
+ for (i = 0; i < ntohs(algo->header.length); i++) {
+ switch (algo->value[i]) {
+ case 0:
+ vty_out(vty, " Algorithm %d: SPF\n", i);
+ break;
+ case 1:
+ vty_out(vty, " Algorithm %d: Strict SPF\n",
+ i);
+ break;
+ default:
+ vty_out(vty,
+ " Algorithm %d: Unknown value %d\n", i,
+ algo->value[i]);
+ break;
+ }
+ }
+ }
+
+ else {
+ zlog_debug(" Segment Routing Algorithm TLV:\n");
+ for (i = 0; i < ntohs(algo->header.length); i++)
+ switch (algo->value[i]) {
+ case 0:
+ zlog_debug(" Algorithm %d: SPF\n", i);
+ break;
+ case 1:
+ zlog_debug(" Algorithm %d: Strict SPF\n", i);
+ break;
+ default:
+ zlog_debug(
+ " Algorithm %d: Unknown value %d\n",
+ i, algo->value[i]);
+ break;
+ }
+ }
+
+ return TLV_SIZE(tlvh);
+}
+
+/* Display Segment Routing SID/Label Range TLV information */
+static uint16_t show_vty_sr_range(struct vty *vty, struct tlv_header *tlvh)
+{
+ struct ri_sr_tlv_sid_label_range *range =
+ (struct ri_sr_tlv_sid_label_range *)tlvh;
+
+ if (vty != NULL) {
+ vty_out(vty,
+ " Segment Routing Range TLV:\n"
+ " Range Size = %d\n"
+ " SID Label = %d\n\n",
+ GET_RANGE_SIZE(ntohl(range->size)),
+ GET_LABEL(ntohl(range->lower.value)));
+ } else {
+ zlog_debug(
+ " Segment Routing Range TLV:\n"
+ " Range Size = %d\n"
+ " SID Label = %d\n\n",
+ GET_RANGE_SIZE(ntohl(range->size)),
+ GET_LABEL(ntohl(range->lower.value)));
+ }
+
+ return TLV_SIZE(tlvh);
+}
+
+/* Display Segment Routing Maximum Stack Depth TLV information */
+static uint16_t show_vty_sr_msd(struct vty *vty, struct tlv_header *tlvh)
+{
+ struct ri_sr_tlv_node_msd *msd = (struct ri_sr_tlv_node_msd *)tlvh;
+
+ if (vty != NULL) {
+ vty_out(vty,
+ " Segment Routing MSD TLV:\n"
+ " Node Maximum Stack Depth = %d\n",
+ msd->value);
+ } else {
+ zlog_debug(
+ " Segment Routing MSD TLV:\n"
+ " Node Maximum Stack Depth = %d\n",
+ msd->value);
+ }
+
+ return TLV_SIZE(tlvh);
+}
+
static void ospf_router_info_show_info(struct vty *vty, struct ospf_lsa *lsa)
{
struct lsa_header *lsah = (struct lsa_header *)lsa->data;
sum += TLV_HDR_SIZE;
sum += show_vty_pce_info(vty, tlvh, length - sum);
break;
+ case RI_SR_TLV_SR_ALGORITHM:
+ sum += show_vty_sr_algorithm(vty, tlvh);
+ break;
+ case RI_SR_TLV_SID_LABEL_RANGE:
+ sum += show_vty_sr_range(vty, tlvh);
+ break;
+ case RI_SR_TLV_NODE_MSD:
+ sum += show_vty_sr_msd(vty, tlvh);
+ break;
+
default:
sum += show_vty_unknown_tlv(vty, tlvh);
break;
struct ri_pce_subtlv_neighbor *neighbor;
struct in_addr tmp;
- if (OspfRI.enabled) {
- if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
- vty_out(vty, " router-info as\n");
- else
- vty_out(vty, " router-info area %s\n",
- inet_ntoa(OspfRI.area_id));
-
- if (OspfRI.pce_info.enabled) {
-
- if (pce->pce_address.header.type != 0)
- vty_out(vty, " pce address %s\n",
- inet_ntoa(pce->pce_address.address.value));
-
- if (pce->pce_cap_flag.header.type != 0)
- vty_out(vty, " pce flag 0x%x\n",
- ntohl(pce->pce_cap_flag.value));
-
- for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) {
- if (domain->header.type != 0) {
- if (domain->type == PCE_DOMAIN_TYPE_AREA) {
- tmp.s_addr = domain->value;
- vty_out(vty, " pce domain area %s\n",
- inet_ntoa(tmp));
- } else {
- vty_out(vty, " pce domain as %d\n",
- ntohl(domain->value));
- }
+ if (!OspfRI.enabled)
+ return;
+
+ if (OspfRI.scope == OSPF_OPAQUE_AS_LSA)
+ vty_out(vty, " router-info as\n");
+ else
+ vty_out(vty, " router-info area %s\n",
+ inet_ntoa(OspfRI.area_id));
+
+ if (OspfRI.pce_info.enabled) {
+
+ if (pce->pce_address.header.type != 0)
+ vty_out(vty, " pce address %s\n",
+ inet_ntoa(pce->pce_address.address.value));
+
+ if (pce->pce_cap_flag.header.type != 0)
+ vty_out(vty, " pce flag 0x%x\n",
+ ntohl(pce->pce_cap_flag.value));
+
+ for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) {
+ if (domain->header.type != 0) {
+ if (domain->type == PCE_DOMAIN_TYPE_AREA) {
+ tmp.s_addr = domain->value;
+ vty_out(vty, " pce domain area %s\n",
+ inet_ntoa(tmp));
+ } else {
+ vty_out(vty, " pce domain as %d\n",
+ ntohl(domain->value));
}
}
+ }
- for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) {
- if (neighbor->header.type != 0) {
- if (neighbor->type == PCE_DOMAIN_TYPE_AREA) {
- tmp.s_addr = neighbor->value;
- vty_out(vty, " pce neighbor area %s\n",
- inet_ntoa(tmp));
- } else {
- vty_out(vty, " pce neighbor as %d\n",
- ntohl(neighbor->value));
- }
+ for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) {
+ if (neighbor->header.type != 0) {
+ if (neighbor->type == PCE_DOMAIN_TYPE_AREA) {
+ tmp.s_addr = neighbor->value;
+ vty_out(vty, " pce neighbor area %s\n",
+ inet_ntoa(tmp));
+ } else {
+ vty_out(vty, " pce neighbor as %d\n",
+ ntohl(neighbor->value));
}
}
-
- if (pce->pce_scope.header.type != 0)
- vty_out(vty, " pce scope 0x%x\n",
- ntohl(OspfRI.pce_info.pce_scope.value));
}
+
+ if (pce->pce_scope.header.type != 0)
+ vty_out(vty, " pce scope 0x%x\n",
+ ntohl(OspfRI.pce_info.pce_scope.value));
}
return;
}
if (CHECK_FLAG(OspfRI.flags, RIFLG_LSA_ENGAGED))
ospf_router_info_lsa_schedule(FLUSH_THIS_LSA);
- /* Unregister the callbacks */
- ospf_router_info_unregister();
-
OspfRI.enabled = false;
return CMD_SUCCESS;
struct ri_pce_subtlv_domain *domain;
struct ri_pce_subtlv_neighbor *neighbor;
- if (OspfRI.enabled) {
+ if ((OspfRI.enabled) && (OspfRI.pce_info.enabled)) {
vty_out(vty, "--- PCE parameters ---\n");
if (pce->pce_address.header.type != 0)
} else {
vty_out(vty,
- " Router Information is disabled on this router\n");
+ " PCE info is disabled on this router\n");
}
return CMD_SUCCESS;
/*
* This is an implementation of RFC4970 Router Information
* with support of RFC5088 PCE Capabilites announcement
+ * and support of draft-ietf-ospf-segment-routing-extensions-18
+ * for Segment Routing Capabilities announcement
+ *
*
* Module name: Router Information
- * Version: 0.99.22
- * Created: 2012-02-01 by Olivier Dugeon
- * Copyright (C) 2012 Orange Labs http://www.orange.com/
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Copyright (C) 2012 - 2017 Orange Labs http://www.orange.com/
*
* This file is part of GNU Zebra.
*
*
* 24 16 8 0
* +--------+--------+--------+--------+
- * | 1 | MBZ |........|........|
+ * | 4 | MBZ |........|........|
* +--------+--------+--------+--------+
* |<-Type->|<Resv'd>|<-- Instance --->|
*
* +--------+--------+--------+--------+ |
* | LS checksum | Length | V
* +--------+--------+--------+--------+ ---
- * | Type | Length | A
- * +--------+--------+--------+--------+ | TLV part for Router Information;
- * Values might be
+ * | Type | Length | A TLV part for Router Information;
+ * +--------+--------+--------+--------+ | Values might be
* | Values ... | V structured as a set of sub-TLVs.
* +--------+--------+--------+--------+ ---
*/
* Following section defines TLV body parts.
*/
-/* Up to now, 8 code point have been assigned to Router Information */
+/* Up to now, 11 code points have been assigned to Router Information */
/* Only type 1 Router Capabilities and 6 PCE are supported with this code */
-#define RI_IANA_MAX_TYPE 8
+#define RI_IANA_MAX_TYPE 11
/* RFC4970: Router Information Capabilities TLV */ /* Mandatory */
#define RI_TLV_CAPABILITIES 1
u_int32_t value;
};
-#define RI_GRACE_RESTART 0x01
-#define RI_GRACE_HELPER 0x02
-#define RI_STUB_SUPPORT 0x04
-#define RI_TE_SUPPORT 0x08
-#define RI_P2P_OVER_LAN 0x10
-#define RI_TE_EXPERIMENTAL 0x20
+/* Capabilities bits are left align */
+#define RI_GRACE_RESTART 0x80000000
+#define RI_GRACE_HELPER 0x40000000
+#define RI_STUB_SUPPORT 0x20000000
+#define RI_TE_SUPPORT 0x10000000
+#define RI_P2P_OVER_LAN 0x08000000
+#define RI_TE_EXPERIMENTAL 0x04000000
#define RI_TLV_LENGTH 4
#define RI_PCE_SUBTLV_CAP_FLAG 5
#define PCE_CAP_GMPLS_LINK 0x0001
-#define PCE_CAP_BIDIRECTIONAL 0x0002
-#define PCE_CAP_DIVERSE_PATH 0x0004
-#define PCE_CAP_LOAD_BALANCE 0x0008
-#define PCE_CAP_SYNCHRONIZED 0x0010
+#define PCE_CAP_BIDIRECTIONAL 0x0002
+#define PCE_CAP_DIVERSE_PATH 0x0004
+#define PCE_CAP_LOAD_BALANCE 0x0008
+#define PCE_CAP_SYNCHRONIZED 0x0010
#define PCE_CAP_OBJECTIVES 0x0020
#define PCE_CAP_ADDITIVE 0x0040
-#define PCE_CAP_PRIORIZATION 0x0080
-#define PCE_CAP_MULTIPLE_REQ 0x0100
+#define PCE_CAP_PRIORIZATION 0x0080
+#define PCE_CAP_MULTIPLE_REQ 0x0100
struct ri_pce_subtlv_cap_flag {
struct tlv_header header; /* Type = 5; Length = n x 4 bytes. */
u_int32_t value;
};
+/* Structure to share flooding scope info for Segment Routing */
+struct scope_info {
+ uint8_t scope;
+ struct in_addr area_id;
+};
+
/* Prototypes. */
extern int ospf_router_info_init(void);
extern void ospf_router_info_term(void);
-
+extern void ospf_router_info_finish(void);
+extern int ospf_router_info_enable(void);
+extern void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb,
+ uint8_t msd);
+extern struct scope_info ospf_router_info_get_flooding_scope(void);
#endif /* _ZEBRA_OSPF_ROUTER_INFO_H */
};
struct ospf_metric {
+ enum { metric_increment, metric_decrement, metric_absolute } type;
bool used;
u_int32_t metric;
};
ei = object;
/* Set metric out value. */
- if (metric->used)
+ if (!metric->used)
+ return RMAP_OKAY;
+ if (metric->type == metric_increment)
+ ei->route_map_set.metric += metric->metric;
+ if (metric->type == metric_decrement)
+ ei->route_map_set.metric -= metric->metric;
+ if (metric->type == metric_absolute)
ei->route_map_set.metric = metric->metric;
+
+ if ((signed int)ei->route_map_set.metric < 1)
+ ei->route_map_set.metric = -1;
+ if (ei->route_map_set.metric > OSPF_LS_INFINITY)
+ ei->route_map_set.metric = OSPF_LS_INFINITY;
}
return RMAP_OKAY;
}
metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(u_int32_t));
metric->used = false;
- /* OSPF doesn't support the +/- in
- set metric <+/-metric> check
- Ignore the +/- component */
- if (!all_digit(arg)) {
- if ((arg[0] == '+' || arg[0] == '-') && all_digit(arg + 1)) {
- zlog_warn("OSPF does not support 'set metric +/-'");
- arg++;
- } else {
- if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt"))
- zlog_warn(
- "OSPF does not support 'set metric +rtt / -rtt'");
-
- return metric;
- }
+ if (all_digit(arg))
+ metric->type = metric_absolute;
+
+ if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt")) {
+ zlog_warn("OSPF does not support 'set metric +rtt / -rtt'");
+ return metric;
+ }
+
+ if ((arg[0] == '+') && all_digit(arg + 1)) {
+ metric->type = metric_increment;
+ arg++;
}
+
+ if ((arg[0] == '-') && all_digit(arg + 1)) {
+ metric->type = metric_decrement;
+ arg++;
+ }
+
metric->metric = strtoul(arg, NULL, 10);
- metric->used = true;
+
+ if (metric->metric)
+ metric->used = true;
return metric;
}
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;
#include "ospfd/ospf_ase.h"
#include "ospfd/ospf_abr.h"
#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_sr.h"
/* Variables to ensure a SPF scheduled log message is printed only once */
ospf_ase_calculate_timer_add(ospf);
-
if (IS_DEBUG_OSPF_EVENT)
zlog_debug("%s: ospf install new route, vrf %s id %u new_table count %lu",
__PRETTY_FUNCTION__,
ospf_abr_task(ospf);
abr_time = monotime_since(&start_time, NULL);
+ /* Schedule Segment Routing update */
+ ospf_sr_update_timer_add(ospf);
+
total_spf_time =
monotime_since(&spf_start_time, &ospf->ts_spf_duration);
}
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,
--- /dev/null
+/*
+ * This is an implementation of Segment Routing
+ * as per draft draft-ietf-ospf-segment-routing-extensions-24
+ *
+ * Module name: Segment Routing
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ *
+ * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <zebra.h>
+
+#include "command.h"
+#include "hash.h"
+#include "if.h"
+#include "if.h"
+#include "jhash.h"
+#include "libospf.h" /* for ospf interface types */
+#include "linklist.h"
+#include "log.h"
+#include "memory.h"
+#include "monotime.h"
+#include "network.h"
+#include "prefix.h"
+#include "sockunion.h" /* for inet_aton() */
+#include "stream.h"
+#include "table.h"
+#include "thread.h"
+#include "vty.h"
+#include "zclient.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_spf.h"
+#include "ospfd/ospf_dump.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_ase.h"
+#include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ri.h"
+#include "ospfd/ospf_ext.h"
+#include "ospfd/ospf_zebra.h"
+
+/*
+ * Global variable to manage Segment Routing on this node.
+ * Note that all parameter values are stored in network byte order.
+ */
+static struct ospf_sr_db OspfSR;
+static void ospf_sr_register_vty(void);
+static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe);
+
+/*
+ * Segment Routing Data Base functions
+ */
+
+/* Hash function for Segment Routing entry */
+static unsigned int sr_hash(void *p)
+{
+ const struct in_addr *rid = p;
+
+ return jhash_1word(rid->s_addr, 0);
+}
+
+/* Compare 2 Router ID hash entries based on SR Node */
+static int sr_cmp(const void *p1, const void *p2)
+{
+ const struct sr_node *srn = p1;
+ const struct in_addr *rid = p2;
+
+ return IPV4_ADDR_SAME(&srn->adv_router, rid);
+}
+
+/* Functions to remove an SR Link */
+static void del_sr_link(void *val)
+{
+ struct sr_link *srl = (struct sr_link *)val;
+
+ del_sid_nhlfe(srl->nhlfe[0]);
+ del_sid_nhlfe(srl->nhlfe[1]);
+ XFREE(MTYPE_OSPF_SR_PARAMS, val);
+
+}
+
+/* Functions to remove an SR Prefix */
+static void del_sr_pref(void *val)
+{
+ struct sr_prefix *srp = (struct sr_prefix *)val;
+
+ del_sid_nhlfe(srp->nhlfe);
+ XFREE(MTYPE_OSPF_SR_PARAMS, val);
+
+}
+
+/* Allocate new Segment Routine node */
+static struct sr_node *sr_node_new(struct in_addr *rid)
+{
+
+ if (rid == NULL)
+ return NULL;
+
+ struct sr_node *new;
+
+ /* Allocate Segment Routing node memory */
+ new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_node));
+
+ /* Sanity Check */
+ if (new == NULL) {
+ zlog_err("SR (%s): Abort! can't create new SR node", __func__);
+ return NULL;
+ }
+
+ /* Default Algorithm, SRGB and MSD */
+ for (int i = 0; i < ALGORITHM_COUNT; i++)
+ new->algo[i] = SR_ALGORITHM_UNSET;
+
+ new->srgb.range_size = 0;
+ new->srgb.lower_bound = 0;
+ new->msd = 0;
+
+ /* Create Link, Prefix and Range TLVs list */
+ new->ext_link = list_new();
+ new->ext_prefix = list_new();
+ new->ext_link->del = del_sr_link;
+ new->ext_prefix->del = del_sr_pref;
+
+ IPV4_ADDR_COPY(&new->adv_router, rid);
+ new->neighbor = NULL;
+ new->instance = 0;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Created new SR node for %s",
+ inet_ntoa(new->adv_router));
+ return new;
+}
+
+/* Delete Segment Routing node */
+static void sr_node_del(struct sr_node *srn)
+{
+ /* Sanity Check */
+ if (srn == NULL)
+ return;
+
+ /* Clean Extended Link */
+ list_delete_and_null(&srn->ext_link);
+
+ /* Clean Prefix List */
+ list_delete_and_null(&srn->ext_prefix);
+
+ XFREE(MTYPE_OSPF_SR_PARAMS, srn);
+}
+
+/* Get SR Node for a given nexthop */
+static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf,
+ struct in_addr nexthop)
+{
+ struct ospf_interface *oi = NULL;
+ struct ospf_neighbor *nbr = NULL;
+ struct listnode *node;
+ struct route_node *rn;
+ struct sr_node *srn;
+ bool found;
+
+ /* Sanity check */
+ if (OspfSR.neighbors == NULL)
+ return NULL;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Search SR-Node for nexthop %s",
+ inet_ntoa(nexthop));
+
+ /* First, search neighbor Router ID for this nexthop */
+ found = false;
+ for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
+ for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) {
+ nbr = rn->info;
+ if ((nbr) && (IPV4_ADDR_SAME(&nexthop, &nbr->src))) {
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ if (!found)
+ return NULL;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Found nexthop Router ID %s",
+ inet_ntoa(nbr->router_id));
+ /* Then, search SR Node */
+ srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, &nbr->router_id);
+
+ return srn;
+}
+
+/*
+ * Segment Routing Initialization functions
+ */
+
+/* Segment Routing starter function */
+static int ospf_sr_start(struct ospf *ospf)
+{
+ struct route_node *rn;
+ struct ospf_lsa *lsa;
+ struct sr_node *srn;
+ int rc = 0;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug("SR (%s): Start Segment Routing", __func__);
+
+ /* Initialize self SR Node */
+ srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id),
+ (void *)sr_node_new);
+
+ /* Sanity Check */
+ if (srn == NULL)
+ return rc;
+
+ /* Complete & Store self SR Node */
+ srn->srgb.range_size = OspfSR.srgb.range_size;
+ srn->srgb.lower_bound = OspfSR.srgb.lower_bound;
+ srn->algo[0] = OspfSR.algo[0];
+ srn->msd = OspfSR.msd;
+ OspfSR.self = srn;
+
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("SR (%s): Update SR-DB from LSDB", __func__);
+
+ /* Start by looking to Router Info & Extended LSA in lsdb */
+ if ((ospf != NULL) && (ospf->backbone != NULL)) {
+ LSDB_LOOP(OPAQUE_AREA_LSDB(ospf->backbone), rn, lsa)
+ {
+ if (IS_LSA_MAXAGE(lsa) || IS_LSA_SELF(lsa))
+ continue;
+ int lsa_id =
+ GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr));
+ switch (lsa_id) {
+ case OPAQUE_TYPE_ROUTER_INFORMATION_LSA:
+ ospf_sr_ri_lsa_update(lsa);
+ break;
+ case OPAQUE_TYPE_EXTENDED_PREFIX_LSA:
+ ospf_sr_ext_prefix_lsa_update(lsa);
+ break;
+ case OPAQUE_TYPE_EXTENDED_LINK_LSA:
+ ospf_sr_ext_link_lsa_update(lsa);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ rc = 1;
+ return rc;
+}
+
+/* Stop Segment Routing */
+static void ospf_sr_stop(void)
+{
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug("SR (%s): Stop Segment Routing", __func__);
+
+ /*
+ * Remove all SR Nodes from the Hash table. Prefix and Link SID will
+ * be remove though list_delete_and_null() call. See sr_node_del()
+ */
+ hash_clean(OspfSR.neighbors, (void *)sr_node_del);
+}
+
+/*
+ * Segment Routing initialize function
+ *
+ * @param - nothing
+ *
+ * @return 0 if OK, -1 otherwise
+ */
+int ospf_sr_init(void)
+{
+ int rc = -1;
+
+ zlog_info("SR (%s): Initialize SR Data Base", __func__);
+
+ memset(&OspfSR, 0, sizeof(struct ospf_sr_db));
+ OspfSR.enabled = false;
+ /* Only AREA flooding is supported in this release */
+ OspfSR.scope = OSPF_OPAQUE_AREA_LSA;
+
+ /* Initialize SRGB, Algorithms and MSD TLVs */
+ /* Only Algorithm SPF is supported */
+ OspfSR.algo[0] = SR_ALGORITHM_SPF;
+ for (int i = 1; i < ALGORITHM_COUNT; i++)
+ OspfSR.algo[i] = SR_ALGORITHM_UNSET;
+
+ OspfSR.srgb.range_size = MPLS_DEFAULT_MAX_SRGB_SIZE;
+ OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL;
+ OspfSR.msd = 0;
+
+ /* Initialize Hash table for neighbor SR nodes */
+ OspfSR.neighbors = hash_create(sr_hash, sr_cmp, "OSPF_SR");
+ if (OspfSR.neighbors == NULL)
+ return rc;
+
+ /* Initialize Route Table for prefix */
+ OspfSR.prefix = route_table_init();
+ if (OspfSR.prefix == NULL)
+ return rc;
+
+ /* Register Segment Routing VTY command */
+ ospf_sr_register_vty();
+
+ rc = 0;
+ return rc;
+}
+
+/*
+ * Segment Routing termination function
+ *
+ * @param - nothing
+ * @return - nothing
+ */
+void ospf_sr_term(void)
+{
+
+ /* Stop Segment Routing */
+ ospf_sr_stop();
+
+ /* Clear SR Node Table */
+ if (OspfSR.neighbors)
+ hash_free(OspfSR.neighbors);
+
+ /* Clear Prefix Table */
+ if (OspfSR.prefix)
+ route_table_finish(OspfSR.prefix);
+
+ OspfSR.enabled = false;
+ OspfSR.self = NULL;
+}
+
+/*
+ * Segment Routing finish function
+ *
+ * @param - nothing
+ * @return - nothing
+ */
+void ospf_sr_finish(void)
+{
+ /* Stop Segment Routing */
+ ospf_sr_stop();
+
+ OspfSR.enabled = false;
+}
+
+/*
+ * Following functions are used to manipulate the
+ * Next Hop Label Forwarding entry (NHLFE)
+ */
+
+/* Compute label from index */
+static mpls_label_t index2label(uint32_t index, struct sr_srgb srgb)
+{
+ mpls_label_t label;
+
+ label = srgb.lower_bound + index;
+ if (label > (srgb.lower_bound + srgb.range_size))
+ return MPLS_INVALID_LABEL;
+ else
+ return label;
+}
+
+/* Get neighbor full structure from address */
+static struct ospf_neighbor *get_neighbor_by_addr(struct ospf *top,
+ struct in_addr addr)
+{
+ struct ospf_neighbor *nbr;
+ struct ospf_interface *oi;
+ struct listnode *node;
+ struct route_node *rn;
+
+ /* Sanity Check */
+ if (top == NULL)
+ return NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(top->oiflist, node, oi))
+ for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) {
+ nbr = rn->info;
+ if (nbr)
+ if (IPV4_ADDR_SAME(&nbr->address.u.prefix4,
+ &addr)
+ || IPV4_ADDR_SAME(&nbr->router_id, &addr)) {
+ route_unlock_node(rn);
+ return nbr;
+ }
+ }
+ return NULL;
+}
+
+/* Get OSPF Path from address */
+static struct ospf_path *get_nexthop_by_addr(struct ospf *top,
+ struct prefix_ipv4 p)
+{
+ struct ospf_route *or;
+ struct ospf_path *path;
+ struct listnode *node;
+ struct route_node *rn;
+
+ /* Sanity Check */
+ if (top == NULL)
+ return NULL;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Search Nexthop for prefix %s/%u",
+ inet_ntoa(p.prefix), p.prefixlen);
+
+ rn = route_node_lookup(top->new_table, (struct prefix *)&p);
+
+ /*
+ * Check if we found an OSPF route. May be NULL if SPF has not
+ * yet populate routing table for this prefix.
+ */
+ if (rn == NULL)
+ return NULL;
+
+ route_unlock_node(rn);
+ or = rn->info;
+ if (or == NULL)
+ return NULL;
+
+ /* Then search path from this route */
+ for (ALL_LIST_ELEMENTS_RO(or->paths, node, path))
+ if (path->nexthop.s_addr != INADDR_ANY || path->ifindex != 0)
+ return path;
+
+ return NULL;
+}
+
+/* Compute NHLFE entry for Extended Link */
+static int compute_link_nhlfe(struct sr_link *srl)
+{
+ struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ struct ospf_neighbor *nh;
+ int rc = 0;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Compute NHLFE for link %s/%u",
+ inet_ntoa(srl->nhlfe[0].prefv4.prefix),
+ srl->nhlfe[0].prefv4.prefixlen);
+
+ /* First determine the OSPF Neighbor */
+ nh = get_neighbor_by_addr(top, srl->nhlfe[0].nexthop);
+
+ /* Neighbor could be not found when OSPF Adjacency just fire up
+ * because SPF don't yet populate routing table. This NHLFE will
+ * be fixed later when SR SPF schedule will be called.
+ */
+ if (nh == NULL)
+ return rc;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Found nexthop NHLFE %s",
+ inet_ntoa(nh->router_id));
+
+ /* Set ifindex for this neighbor */
+ srl->nhlfe[0].ifindex = nh->oi->ifp->ifindex;
+ srl->nhlfe[1].ifindex = nh->oi->ifp->ifindex;
+
+ /* Set Input & Output Label */
+ if (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+ srl->nhlfe[0].label_in = srl->sid[0];
+ else
+ srl->nhlfe[0].label_in =
+ index2label(srl->sid[0], srl->srn->srgb);
+ if (CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+ srl->nhlfe[1].label_in = srl->sid[1];
+ else
+ srl->nhlfe[1].label_in =
+ index2label(srl->sid[1], srl->srn->srgb);
+
+ srl->nhlfe[0].label_out = MPLS_LABEL_IMPLICIT_NULL;
+ srl->nhlfe[1].label_out = MPLS_LABEL_IMPLICIT_NULL;
+
+ rc = 1;
+ return rc;
+}
+
+/*
+ * Compute NHLFE entry for Extended Prefix
+ *
+ * @param srp - Segment Routing Prefix
+ *
+ * @return -1 if next hop is not found, 0 if nexthop has not changed
+ * and 1 if success
+ */
+static int compute_prefix_nhlfe(struct sr_prefix *srp)
+{
+ struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
+ struct ospf_path *nh = NULL;
+ struct sr_node *srnext;
+ int rc = -1;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Compute NHLFE for prefix %s/%u",
+ inet_ntoa(srp->nhlfe.prefv4.prefix),
+ srp->nhlfe.prefv4.prefixlen);
+
+ /* First determine the nexthop */
+ nh = get_nexthop_by_addr(top, srp->nhlfe.prefv4);
+
+ /* Nexthop could be not found when OSPF Adjacency just fire up
+ * because SPF don't yet populate routing table. This NHLFE will
+ * be fixed later when SR SPF schedule will be called.
+ */
+ if (nh == NULL)
+ return rc;
+
+ /* Check if NextHop has changed when call after running a new SPF */
+ if (IPV4_ADDR_SAME(&nh->nexthop, &srp->nhlfe.nexthop)
+ && (nh->ifindex == srp->nhlfe.ifindex))
+ return 0;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Found new next hop for this NHLFE: %s",
+ inet_ntoa(nh->nexthop));
+
+ /*
+ * Get SR-Node for this nexthop. Could be not yet available
+ * as Extende Link / Prefix and Router Information are flooded
+ * after LSA Type 1 & 2 which populate the OSPF Route Table
+ */
+ srnext = get_sr_node_by_nexthop(top, nh->nexthop);
+ if (srnext == NULL)
+ return rc;
+
+ /* And store this information for later update if SR Node is found */
+ srnext->neighbor = OspfSR.self;
+ if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router))
+ srp->nexthop = NULL;
+ else
+ srp->nexthop = srnext;
+
+ /*
+ * SR Node could be known, but SRGB could be not initialize
+ * This is due to the fact that Extended Link / Prefix could
+ * be received before corresponding Router Information LSA
+ */
+ if ((srnext == NULL) || (srnext->srgb.lower_bound == 0)
+ || (srnext->srgb.range_size == 0))
+ return rc;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Found SRGB %u/%u for next hop SR-Node %s",
+ srnext->srgb.range_size, srnext->srgb.lower_bound,
+ inet_ntoa(srnext->adv_router));
+
+ /* Set ip addr & ifindex for this neighbor */
+ IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &nh->nexthop);
+ srp->nhlfe.ifindex = nh->ifindex;
+
+ /* Compute Input Label with self SRGB */
+ srp->nhlfe.label_in = index2label(srp->sid, OspfSR.srgb);
+ /*
+ * and Output Label with Next hop SR Node SRGB or Implicit Null label
+ * if next hop is the destination and request PHP
+ */
+ if ((srp->nexthop == NULL)
+ && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)))
+ srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL;
+ else if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
+ srp->nhlfe.label_out = srp->sid;
+ else
+ srp->nhlfe.label_out = index2label(srp->sid, srnext->srgb);
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Computed new labels in: %u out: %u",
+ srp->nhlfe.label_in, srp->nhlfe.label_out);
+
+ rc = 1;
+ return rc;
+}
+
+/* Send MPLS Label entry to Zebra for installation or deletion */
+static int ospf_zebra_send_mpls_labels(int cmd, struct sr_nhlfe nhlfe)
+{
+ struct stream *s;
+
+ /* Reset stream. */
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, cmd, VRF_DEFAULT);
+ stream_putc(s, ZEBRA_LSP_SR);
+ /* OSPF Segment Routing currently support only IPv4 */
+ stream_putl(s, nhlfe.prefv4.family);
+ stream_put_in_addr(s, &nhlfe.prefv4.prefix);
+ stream_putc(s, nhlfe.prefv4.prefixlen);
+ stream_put_in_addr(s, &nhlfe.nexthop);
+ stream_putl(s, nhlfe.ifindex);
+ stream_putc(s, OSPF_SR_PRIORITY_DEFAULT);
+ stream_putl(s, nhlfe.label_in);
+ stream_putl(s, nhlfe.label_out);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- %s LSP %u/%u for %s/%u via %u",
+ cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete",
+ nhlfe.label_in, nhlfe.label_out,
+ inet_ntoa(nhlfe.prefv4.prefix),
+ nhlfe.prefv4.prefixlen, nhlfe.ifindex);
+
+ return zclient_send_message(zclient);
+}
+
+/* Request zebra to install/remove FEC in FIB */
+static int ospf_zebra_send_mpls_ftn(int cmd, struct sr_nhlfe nhlfe)
+{
+ struct zapi_route api;
+ struct zapi_nexthop *api_nh;
+
+ /* Support only IPv4 */
+ if (nhlfe.prefv4.family != AF_INET)
+ return -1;
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = VRF_DEFAULT;
+ api.type = ZEBRA_ROUTE_OSPF;
+ api.safi = SAFI_UNICAST;
+ memcpy(&api.prefix, &nhlfe.prefv4, sizeof(struct prefix_ipv4));
+
+ if (cmd == ZEBRA_ROUTE_ADD) {
+ /* Metric value. */
+ SET_FLAG(api.message, ZAPI_MESSAGE_METRIC);
+ api.metric = OSPF_SR_DEFAULT_METRIC;
+ /* Nexthop */
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ api_nh = &api.nexthops[0];
+ IPV4_ADDR_COPY(&api_nh->gate.ipv4, &nhlfe.nexthop);
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ api_nh->ifindex = nhlfe.ifindex;
+ /* MPLS labels */
+ SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
+ api_nh->labels[0] = nhlfe.label_out;
+ api_nh->label_num = 1;
+ api.nexthop_num = 1;
+ }
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- %s FEC %u for %s/%u via %u",
+ cmd == ZEBRA_ROUTE_ADD ? "Add" : "Delete",
+ nhlfe.label_out, inet_ntoa(nhlfe.prefv4.prefix),
+ nhlfe.prefv4.prefixlen, nhlfe.ifindex);
+
+ return zclient_route_send(cmd, zclient, &api);
+}
+
+/* Add new NHLFE entry for SID */
+static inline void add_sid_nhlfe(struct sr_nhlfe nhlfe)
+{
+ if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0)) {
+ ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_ADD, nhlfe);
+ if (nhlfe.label_out != MPLS_LABEL_IMPLICIT_NULL)
+ ospf_zebra_send_mpls_ftn(ZEBRA_ROUTE_ADD, nhlfe);
+ }
+}
+
+/* Remove NHLFE entry for SID */
+static inline void del_sid_nhlfe(struct sr_nhlfe nhlfe)
+{
+ if ((nhlfe.label_in != 0) && (nhlfe.label_out != 0)) {
+ ospf_zebra_send_mpls_labels(ZEBRA_MPLS_LABELS_DELETE, nhlfe);
+ if (nhlfe.label_out != MPLS_LABEL_IMPLICIT_NULL)
+ ospf_zebra_send_mpls_ftn(ZEBRA_ROUTE_DELETE, nhlfe);
+ }
+}
+
+/* Update NHLFE entry for SID */
+static inline void update_sid_nhlfe(struct sr_nhlfe n1, struct sr_nhlfe n2)
+{
+
+ del_sid_nhlfe(n1);
+ add_sid_nhlfe(n2);
+}
+
+/*
+ * Functions to parse and get Extended Link / Prefix
+ * TLVs and SubTLVs
+ */
+
+/* Extended Link SubTLVs Getter */
+static struct sr_link *get_ext_link_sid(struct tlv_header *tlvh)
+{
+
+ struct sr_link *srl;
+ struct ext_tlv_link *link = (struct ext_tlv_link *)tlvh;
+ struct ext_subtlv_adj_sid *adj_sid;
+ struct ext_subtlv_lan_adj_sid *lan_sid;
+ struct ext_subtlv_rmt_itf_addr *rmt_itf;
+
+ struct tlv_header *sub_tlvh;
+ uint16_t length = 0, sum = 0, i = 0;
+
+ srl = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_link));
+
+ if (srl == NULL)
+ return NULL;
+
+ /* Initialize TLV browsing */
+ length = ntohs(tlvh->length) - EXT_TLV_LINK_SIZE;
+ sub_tlvh = (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE
+ + EXT_TLV_LINK_SIZE);
+ for (; sum < length; sub_tlvh = TLV_HDR_NEXT(sub_tlvh)) {
+ switch (ntohs(sub_tlvh->type)) {
+ case EXT_SUBTLV_ADJ_SID:
+ adj_sid = (struct ext_subtlv_adj_sid *)sub_tlvh;
+ srl->type = ADJ_SID;
+ i = CHECK_FLAG(adj_sid->flags,
+ EXT_SUBTLV_LINK_ADJ_SID_BFLG)
+ ? 1
+ : 0;
+ srl->flags[i] = adj_sid->flags;
+ if (CHECK_FLAG(adj_sid->flags,
+ EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+ srl->sid[i] = GET_LABEL(ntohl(adj_sid->value));
+ else
+ srl->sid[i] = ntohl(adj_sid->value);
+ IPV4_ADDR_COPY(&srl->nhlfe[i].nexthop, &link->link_id);
+ break;
+ case EXT_SUBTLV_LAN_ADJ_SID:
+ lan_sid = (struct ext_subtlv_lan_adj_sid *)sub_tlvh;
+ srl->type = LAN_ADJ_SID;
+ i = CHECK_FLAG(lan_sid->flags,
+ EXT_SUBTLV_LINK_ADJ_SID_BFLG)
+ ? 1
+ : 0;
+ srl->flags[i] = lan_sid->flags;
+ if (CHECK_FLAG(lan_sid->flags,
+ EXT_SUBTLV_LINK_ADJ_SID_VFLG))
+ srl->sid[i] = GET_LABEL(ntohl(lan_sid->value));
+ else
+ srl->sid[i] = ntohl(lan_sid->value);
+ IPV4_ADDR_COPY(&srl->nhlfe[i].nexthop,
+ &lan_sid->neighbor_id);
+ break;
+ case EXT_SUBTLV_RMT_ITF_ADDR:
+ rmt_itf = (struct ext_subtlv_rmt_itf_addr *)sub_tlvh;
+ IPV4_ADDR_COPY(&srl->nhlfe[0].nexthop, &rmt_itf->value);
+ IPV4_ADDR_COPY(&srl->nhlfe[1].nexthop, &rmt_itf->value);
+ break;
+ default:
+ break;
+ }
+ sum += TLV_SIZE(sub_tlvh);
+ }
+
+ IPV4_ADDR_COPY(&srl->nhlfe[0].prefv4.prefix, &link->link_data);
+ srl->nhlfe[0].prefv4.prefixlen = IPV4_MAX_PREFIXLEN;
+ srl->nhlfe[0].prefv4.family = AF_INET;
+ apply_mask_ipv4(&srl->nhlfe[0].prefv4);
+ IPV4_ADDR_COPY(&srl->nhlfe[1].prefv4.prefix, &link->link_data);
+ srl->nhlfe[1].prefv4.prefixlen = IPV4_MAX_PREFIXLEN;
+ srl->nhlfe[1].prefv4.family = AF_INET;
+ apply_mask_ipv4(&srl->nhlfe[1].prefv4);
+
+ if (IS_DEBUG_OSPF_SR) {
+ zlog_debug(" |- Found primary Adj/Lan Sid %u for %s/%u",
+ srl->sid[0], inet_ntoa(srl->nhlfe[0].prefv4.prefix),
+ srl->nhlfe[0].prefv4.prefixlen);
+ zlog_debug(" |- Found backup Adj/Lan Sid %u for %s/%u",
+ srl->sid[1], inet_ntoa(srl->nhlfe[1].prefv4.prefix),
+ srl->nhlfe[1].prefv4.prefixlen);
+ }
+
+ return srl;
+}
+
+/* Extended Prefix SubTLVs Getter */
+static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh)
+{
+
+ struct sr_prefix *srp;
+ struct ext_tlv_prefix *pref = (struct ext_tlv_prefix *)tlvh;
+ struct ext_subtlv_prefix_sid *psid;
+
+ struct tlv_header *sub_tlvh;
+ uint16_t length = 0, sum = 0;
+
+ srp = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix));
+
+ if (srp == NULL)
+ return NULL;
+
+ /* Initialize TLV browsing */
+ length = ntohs(tlvh->length) - EXT_TLV_PREFIX_SIZE;
+ sub_tlvh = (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE
+ + EXT_TLV_PREFIX_SIZE);
+ for (; sum < length; sub_tlvh = TLV_HDR_NEXT(sub_tlvh)) {
+ switch (ntohs(sub_tlvh->type)) {
+ case EXT_SUBTLV_PREFIX_SID:
+ psid = (struct ext_subtlv_prefix_sid *)sub_tlvh;
+ if (psid->algorithm != SR_ALGORITHM_SPF) {
+ zlog_err(
+ "SR (%s): Unsupported Algorithm",
+ __func__);
+ XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+ return NULL;
+ }
+ srp->type = PREF_SID;
+ srp->flags = psid->flags;
+ if (CHECK_FLAG(psid->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
+ srp->sid = GET_LABEL(ntohl(psid->value));
+ else
+ srp->sid = ntohl(psid->value);
+ IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix,
+ &pref->address);
+ srp->nhlfe.prefv4.prefixlen = pref->pref_length;
+ srp->nhlfe.prefv4.family = AF_INET;
+ apply_mask_ipv4(&srp->nhlfe.prefv4);
+ break;
+ default:
+ break;
+ }
+ sum += TLV_SIZE(sub_tlvh);
+ }
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Found SID %u for prefix %s/%u", srp->sid,
+ inet_ntoa(srp->nhlfe.prefv4.prefix),
+ srp->nhlfe.prefv4.prefixlen);
+ return srp;
+}
+
+/*
+ * Functions to manipulate Segment Routing Link & Prefix structures
+ */
+
+/* Compare two Segment Link: return 0 if equal, 1 otherwise */
+static inline int sr_link_cmp(struct sr_link *srl1, struct sr_link *srl2)
+{
+ if ((srl1->sid[0] == srl2->sid[0]) && (srl1->sid[1] == srl2->sid[1])
+ && (srl1->type == srl2->type) && (srl1->flags[0] == srl2->flags[0])
+ && (srl1->flags[1] == srl2->flags[1]))
+ return 0;
+ else
+ return 1;
+}
+
+/* Compare two Segment Prefix: return 0 if equal, 1 otherwise */
+static inline int sr_prefix_cmp(struct sr_prefix *srp1, struct sr_prefix *srp2)
+{
+ if ((srp1->sid == srp2->sid) && (srp1->flags == srp2->flags))
+ return 0;
+ else
+ return 1;
+}
+
+/* Update Segment Link of given Segment Routing Node */
+static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl,
+ u_char lsa_flags)
+{
+ struct listnode *node;
+ struct sr_link *lk;
+ bool found = false;
+
+ /* Sanity check */
+ if ((srn == NULL) || (srl == NULL))
+ return;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Process Extended Link Adj/Lan-SID");
+
+ /* Process only Local Adj/Lan_Adj SID coming from LSA SELF */
+ if (!CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_LFLG)
+ || !CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_LFLG)
+ || !CHECK_FLAG(lsa_flags, OSPF_LSA_SELF))
+ return;
+
+ /* Search for existing Segment Link */
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, lk))
+ if (lk->instance == srl->instance) {
+ found = true;
+ break;
+ }
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- %s SR Link 8.0.0.%u for SR node %s",
+ found ? "Update" : "Add",
+ GET_OPAQUE_ID(srl->instance),
+ inet_ntoa(srn->adv_router));
+
+ /* if not found, add new Segment Link and install NHLFE */
+ if (!found) {
+ /* Complete SR-Link and add it to SR-Node list */
+ srl->srn = srn;
+ IPV4_ADDR_COPY(&srl->adv_router, &srn->adv_router);
+ listnode_add(srn->ext_link, srl);
+ /* Try to set MPLS table */
+ if (compute_link_nhlfe(srl)) {
+ add_sid_nhlfe(srl->nhlfe[0]);
+ add_sid_nhlfe(srl->nhlfe[1]);
+ }
+ } else {
+ if (sr_link_cmp(lk, srl)) {
+ if (compute_link_nhlfe(srl)) {
+ update_sid_nhlfe(lk->nhlfe[0], srl->nhlfe[0]);
+ update_sid_nhlfe(lk->nhlfe[1], srl->nhlfe[1]);
+ /* Replace Segment List */
+ listnode_delete(srn->ext_link, lk);
+ XFREE(MTYPE_OSPF_SR_PARAMS, lk);
+ srl->srn = srn;
+ IPV4_ADDR_COPY(&srl->adv_router,
+ &srn->adv_router);
+ listnode_add(srn->ext_link, srl);
+ } else {
+ /* New NHLFE was not found.
+ * Just free the SR Link
+ */
+ XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+ }
+ } else {
+ /*
+ * This is just an LSA refresh.
+ * Stop processing and free SR Link
+ */
+ XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+ }
+ }
+}
+
+/* Update Segment Prefix of given Segment Routing Node */
+static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp)
+{
+
+ struct listnode *node;
+ struct sr_prefix *pref;
+ bool found = false;
+
+ /* Sanity check */
+ if (srn == NULL || srp == NULL)
+ return;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Process Extended Prefix SID %u", srp->sid);
+
+ /* Process only Global Prefix SID */
+ if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_LFLG))
+ return;
+
+ /* Search for existing Segment Prefix */
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, pref))
+ if (pref->instance == srp->instance) {
+ found = true;
+ break;
+ }
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- %s SR LSA ID 7.0.0.%u for SR node %s",
+ found ? "Update" : "Add",
+ GET_OPAQUE_ID(srp->instance),
+ inet_ntoa(srn->adv_router));
+
+ /* if not found, add new Segment Prefix and install NHLFE */
+ if (!found) {
+ /* Complete SR-Prefix and add it to SR-Node list */
+ srp->srn = srn;
+ IPV4_ADDR_COPY(&srp->adv_router, &srn->adv_router);
+ listnode_add(srn->ext_prefix, srp);
+ /* Try to set MPLS table */
+ if (compute_prefix_nhlfe(srp) == 1)
+ add_sid_nhlfe(srp->nhlfe);
+ } else {
+ if (sr_prefix_cmp(pref, srp)) {
+ if (compute_prefix_nhlfe(srp) == 1) {
+ update_sid_nhlfe(pref->nhlfe, srp->nhlfe);
+ /* Replace Segment Prefix */
+ listnode_delete(srn->ext_prefix, pref);
+ XFREE(MTYPE_OSPF_SR_PARAMS, pref);
+ srp->srn = srn;
+ IPV4_ADDR_COPY(&srp->adv_router,
+ &srn->adv_router);
+ listnode_add(srn->ext_prefix, srp);
+ } else {
+ /* New NHLFE was not found.
+ * Just free the SR Prefix
+ */
+ XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+ }
+ } else {
+ /* This is just an LSA refresh.
+ * Stop processing and free SR Prefix
+ */
+ XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+ }
+ }
+}
+
+/*
+ * When change the FRR Self SRGB, update the NHLFE Input Label
+ * for all Extended Prefix with SID index through hash_iterate()
+ */
+static void update_in_nhlfe(struct hash_backet *backet, void *args)
+{
+ struct listnode *node;
+ struct sr_node *srn = (struct sr_node *)backet->data;
+ struct sr_prefix *srp;
+ struct sr_nhlfe new;
+
+ /* Process Every Extended Prefix for this SR-Node */
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+ /* Process Self SRN only if NO-PHP is requested */
+ if ((srn == OspfSR.self)
+ && !CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))
+ continue;
+
+ /* Process only SID Index */
+ if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG))
+ continue;
+
+ /* OK. Compute new NHLFE */
+ memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe));
+ new.label_in = index2label(srp->sid, OspfSR.srgb);
+ /* Update MPLS LFIB */
+ update_sid_nhlfe(srp->nhlfe, new);
+ /* Finally update Input Label */
+ srp->nhlfe.label_in = new.label_in;
+ }
+}
+
+/*
+ * When SRGB has changed, update NHLFE Output Label for all Extended Prefix
+ * with SID index which use the given SR-Node as nexthop though hash_iterate()
+ */
+static void update_out_nhlfe(struct hash_backet *backet, void *args)
+{
+ struct listnode *node;
+ struct sr_node *srn = (struct sr_node *)backet->data;
+ struct sr_node *srnext = (struct sr_node *)args;
+ struct sr_prefix *srp;
+ struct sr_nhlfe new;
+
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+ /* Process only SID Index for next hop without PHP */
+ if ((srp->nexthop == NULL)
+ && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)))
+ continue;
+ memcpy(&new, &srp->nhlfe, sizeof(struct sr_nhlfe));
+ new.label_out = index2label(srp->sid, srnext->srgb);
+ update_sid_nhlfe(srp->nhlfe, new);
+ srp->nhlfe.label_out = new.label_out;
+ }
+}
+
+/*
+ * Following functions are call when new Segment Routing LSA are received
+ * - Router Information: ospf_sr_ri_lsa_update() & ospf_sr_ri_lsa_delete()
+ * - Extended Link: ospf_sr_ext_link_update() & ospf_sr_ext_link_delete()
+ * - Extended Prefix: ospf_ext_prefix_update() & ospf_sr_ext_prefix_delete()
+ */
+
+/* Update Segment Routing from Router Information LSA */
+void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa)
+{
+ struct sr_node *srn;
+ struct tlv_header *tlvh;
+ struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+ struct ri_sr_tlv_sid_label_range *ri_srgb;
+ struct ri_sr_tlv_sr_algorithm *algo;
+ struct sr_srgb srgb;
+ uint16_t length = 0, sum = 0;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "SR (%s): Process Router "
+ "Information LSA 4.0.0.%u from %s",
+ __func__,
+ GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ inet_ntoa(lsah->adv_router));
+
+ /* Sanity check */
+ if (IS_LSA_SELF(lsa))
+ return;
+
+ if (OspfSR.neighbors == NULL) {
+ zlog_err("SR (%s): Abort! no valid SR DataBase", __func__);
+ return;
+ }
+
+ /* Get SR Node in hash table from Router ID */
+ srn = hash_get(OspfSR.neighbors, (void *)&(lsah->adv_router),
+ (void *)sr_node_new);
+
+ /* Sanity check */
+ if (srn == NULL) {
+ zlog_err(
+ "SR (%s): Abort! can't create SR node in hash table",
+ __func__);
+ return;
+ }
+
+ if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) {
+ zlog_err(
+ "SR (%s): Abort! Wrong "
+ "LSA ID 4.0.0.%u for SR node %s/%u",
+ __func__,
+ GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ inet_ntoa(lsah->adv_router), srn->instance);
+ return;
+ }
+
+ /* Collect Router Information Sub TLVs */
+ /* Initialize TLV browsing */
+ length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+ srgb.range_size = 0;
+ srgb.lower_bound = 0;
+
+ for (tlvh = TLV_HDR_TOP(lsah); (sum < length) && (tlvh != NULL);
+ tlvh = TLV_HDR_NEXT(tlvh)) {
+ switch (ntohs(tlvh->type)) {
+ case RI_SR_TLV_SR_ALGORITHM:
+ algo = (struct ri_sr_tlv_sr_algorithm *)tlvh;
+ int i;
+
+ for (i = 0; i < ntohs(algo->header.length); i++)
+ srn->algo[i] = algo->value[0];
+ for (; i < ALGORITHM_COUNT; i++)
+ srn->algo[i] = SR_ALGORITHM_UNSET;
+ sum += TLV_SIZE(tlvh);
+ break;
+ case RI_SR_TLV_SID_LABEL_RANGE:
+ ri_srgb = (struct ri_sr_tlv_sid_label_range *)tlvh;
+ srgb.range_size = GET_RANGE_SIZE(ntohl(ri_srgb->size));
+ srgb.lower_bound =
+ GET_LABEL(ntohl(ri_srgb->lower.value));
+ sum += TLV_SIZE(tlvh);
+ break;
+ case RI_SR_TLV_NODE_MSD:
+ srn->msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value;
+ sum += TLV_SIZE(tlvh);
+ break;
+ default:
+ sum += TLV_SIZE(tlvh);
+ break;
+ }
+ }
+
+ /* Check that we collect mandatory parameters */
+ if (srn->algo[0] == SR_ALGORITHM_UNSET || srgb.range_size == 0
+ || srgb.lower_bound == 0) {
+ zlog_warn(
+ "SR (%s): Missing mandatory parameters. Abort!",
+ __func__);
+ hash_release(OspfSR.neighbors, &(srn->adv_router));
+ XFREE(MTYPE_OSPF_SR_PARAMS, srn);
+ return;
+ }
+
+ /* Check if it is a new SR Node or not */
+ if (srn->instance == 0) {
+ /* update LSA ID */
+ srn->instance = ntohl(lsah->id.s_addr);
+ /* Copy SRGB */
+ srn->srgb.range_size = srgb.range_size;
+ srn->srgb.lower_bound = srgb.lower_bound;
+ }
+
+ /* Check if SRGB has changed */
+ if ((srn->srgb.range_size != srgb.range_size)
+ || (srn->srgb.lower_bound != srgb.lower_bound)) {
+ srn->srgb.range_size = srgb.range_size;
+ srn->srgb.lower_bound = srgb.lower_bound;
+ /* Update NHLFE if it is a neighbor SR node */
+ if (srn->neighbor == OspfSR.self)
+ hash_iterate(OspfSR.neighbors,
+ (void (*)(struct hash_backet *,
+ void *))update_out_nhlfe,
+ (void *)srn);
+ }
+
+}
+
+/*
+ * Delete SR Node entry in hash table information corresponding to an expired
+ * Router Information LSA
+ */
+void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa)
+{
+ struct sr_node *srn;
+ struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "SR (%s): Remove SR node %s from lsa_id 4.0.0.%u",
+ __func__, inet_ntoa(lsah->adv_router),
+ GET_OPAQUE_ID(ntohl(lsah->id.s_addr)));
+
+ /* Sanity check */
+ if (OspfSR.neighbors == NULL) {
+ zlog_err("SR (%s): Abort! no valid SR Data Base", __func__);
+ return;
+ }
+
+ /* Release Router ID entry in SRDB hash table */
+ srn = hash_release(OspfSR.neighbors, &(lsah->adv_router));
+
+ /* Sanity check */
+ if (srn == NULL) {
+ zlog_err(
+ "SR (%s): Abort! no entry in SRDB for SR Node %s",
+ __func__, inet_ntoa(lsah->adv_router));
+ return;
+ }
+
+ if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) {
+ zlog_err(
+ "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %s",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ inet_ntoa(lsah->adv_router));
+ return;
+ }
+
+ /* Remove SR node */
+ sr_node_del(srn);
+
+}
+
+/* Update Segment Routing from Extended Link LSA */
+void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa)
+{
+ struct sr_node *srn;
+ struct tlv_header *tlvh;
+ struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+ struct sr_link *srl;
+
+ uint16_t length, sum;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "SR (%s): Process Extended Link LSA 8.0.0.%u from %s",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ inet_ntoa(lsah->adv_router));
+
+ /* Sanity check */
+ if (OspfSR.neighbors == NULL) {
+ zlog_err("SR (%s): Abort! no valid SR DataBase", __func__);
+ return;
+ }
+
+ /* Get SR Node in hash table from Router ID */
+ srn = (struct sr_node *)hash_get(OspfSR.neighbors,
+ (void *)&(lsah->adv_router),
+ (void *)sr_node_new);
+
+ /* Sanity check */
+ if (srn == NULL) {
+ zlog_err(
+ "SR (%s): Abort! can't create SR node in hash table",
+ __func__);
+ return;
+ }
+
+ /* Initialize TLV browsing */
+ length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+ sum = 0;
+ for (tlvh = TLV_HDR_TOP(lsah); (sum < length) && (tlvh != NULL);
+ tlvh = TLV_HDR_NEXT(tlvh)) {
+ if (ntohs(tlvh->type) == EXT_TLV_LINK) {
+ /* Got Extended Link information */
+ srl = get_ext_link_sid(tlvh);
+ /* Update SID if not null */
+ if (srl != NULL) {
+ srl->instance = ntohl(lsah->id.s_addr);
+ update_ext_link_sid(srn, srl, lsa->flags);
+ }
+ }
+ sum += TLV_SIZE(tlvh);
+ }
+}
+
+/* Delete Segment Routing from Extended Link LSA */
+void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa)
+{
+ struct listnode *node;
+ struct sr_link *srl;
+ struct sr_node *srn;
+ struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+ uint32_t instance = ntohl(lsah->id.s_addr);
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "SR (%s): Remove Extended Link LSA 8.0.0.%u from %s",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ inet_ntoa(lsah->adv_router));
+
+ /* Sanity check */
+ if (OspfSR.neighbors == NULL) {
+ zlog_err("SR (%s): Abort! no valid SR DataBase", __func__);
+ return;
+ }
+
+ /* Search SR Node in hash table from Router ID */
+ srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
+ (void *)&(lsah->adv_router));
+
+ /*
+ * SR-Node may be NULL if it has been remove previously when
+ * processing Router Information LSA deletion
+ */
+ if (srn == NULL) {
+ zlog_warn(
+ "SR (%s): Stop! no entry in SRDB for SR Node %s",
+ __func__, inet_ntoa(lsah->adv_router));
+ return;
+ }
+
+ /* Search for corresponding Segment Link */
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl))
+ if (srl->instance == instance)
+ break;
+
+ /* Remove Segment Link if found */
+ if ((srl != NULL) && (srl->instance == instance)) {
+ del_sid_nhlfe(srl->nhlfe[0]);
+ del_sid_nhlfe(srl->nhlfe[1]);
+ listnode_delete(srn->ext_link, srl);
+ XFREE(MTYPE_OSPF_SR_PARAMS, srl);
+ } else {
+ zlog_warn(
+ "SR (%s): Didn't found corresponding SR Link 8.0.0.%u "
+ "for SR Node %s", __func__,
+ GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ inet_ntoa(lsah->adv_router));
+ }
+
+}
+
+/* Update Segment Routing from Extended Prefix LSA */
+void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa)
+{
+ struct sr_node *srn;
+ struct tlv_header *tlvh;
+ struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+ struct sr_prefix *srp;
+
+ uint16_t length, sum;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "SR (%s): Process Extended Prefix LSA "
+ "7.0.0.%u from %s", __func__,
+ GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ inet_ntoa(lsah->adv_router));
+
+ /* Sanity check */
+ if (OspfSR.neighbors == NULL) {
+ zlog_err("SR (%s): Abort! no valid SR DataBase", __func__);
+ return;
+ }
+
+ /* Get SR Node in hash table from Router ID */
+ srn = (struct sr_node *)hash_get(OspfSR.neighbors,
+ (void *)&(lsah->adv_router),
+ (void *)sr_node_new);
+
+ /* Sanity check */
+ if (srn == NULL) {
+ zlog_err(
+ "SR (%s): Abort! can't create SR node in hash table",
+ __func__);
+ return;
+ }
+
+ /* Initialize TLV browsing */
+ length = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+ sum = 0;
+ for (tlvh = TLV_HDR_TOP(lsah); sum < length;
+ tlvh = TLV_HDR_NEXT(tlvh)) {
+ if (ntohs(tlvh->type) == EXT_TLV_LINK) {
+ /* Got Extended Link information */
+ srp = get_ext_prefix_sid(tlvh);
+ /* Update SID if not null */
+ if (srp != NULL) {
+ srp->instance = ntohl(lsah->id.s_addr);
+ update_ext_prefix_sid(srn, srp);
+ }
+ }
+ sum += TLV_SIZE(tlvh);
+ }
+}
+
+/* Delete Segment Routing from Extended Prefix LSA */
+void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa)
+{
+ struct listnode *node;
+ struct sr_prefix *srp;
+ struct sr_node *srn;
+ struct lsa_header *lsah = (struct lsa_header *)lsa->data;
+ uint32_t instance = ntohl(lsah->id.s_addr);
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "SR (%s): Remove Extended Prefix LSA 7.0.0.%u from %s",
+ __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ inet_ntoa(lsah->adv_router));
+
+ /* Sanity check */
+ if (OspfSR.neighbors == NULL) {
+ zlog_err("SR (%s): Abort! no valid SR DataBase", __func__);
+ return;
+ }
+
+ /* Search SR Node in hash table from Router ID */
+ srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
+ (void *)&(lsah->adv_router));
+
+ /*
+ * SR-Node may be NULL if it has been remove previously when
+ * processing Router Information LSA deletion
+ */
+ if (srn == NULL) {
+ zlog_warn(
+ "SR (%s): Stop! no entry in SRDB for SR Node %s",
+ __func__, inet_ntoa(lsah->adv_router));
+ return;
+ }
+
+ /* Search for corresponding Segment Link */
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp))
+ if (srp->instance == instance)
+ break;
+
+ /* Remove Segment Link if found */
+ if ((srp != NULL) && (srp->instance == instance)) {
+ del_sid_nhlfe(srp->nhlfe);
+ listnode_delete(srn->ext_link, srp);
+ XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+ } else {
+ zlog_warn(
+ "SR (%s): Didn't found corresponding SR Prefix "
+ "7.0.0.%u for SR Node %s", __func__,
+ GET_OPAQUE_ID(ntohl(lsah->id.s_addr)),
+ inet_ntoa(lsah->adv_router));
+ }
+
+}
+
+/* Get Label for Extended Link SID */
+/* TODO: To be replace by Zebra Label Manager */
+uint32_t get_ext_link_label_value(void)
+{
+ static uint32_t label = ADJ_SID_MIN - 1;
+
+ if (label < ADJ_SID_MAX)
+ label += 1;
+
+ return label;
+}
+
+/*
+ * Update Prefix SID. Call by ospf_ext_pref_ism_change to
+ * complete initial CLI command at startutp.
+ *
+ * @param ifp - Loopback interface
+ * @param pref - Prefix address of this interface
+ *
+ * @return - void
+ */
+void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p)
+{
+ struct listnode *node;
+ struct sr_prefix *srp;
+
+ /* Sanity Check */
+ if ((ifp == NULL) || (p == NULL))
+ return;
+
+ /*
+ * Search if there is a Segment Prefix that correspond to this
+ * interface or prefix, and update it if found
+ */
+ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) {
+ if ((srp->nhlfe.ifindex == ifp->ifindex)
+ || ((IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix,
+ &p->u.prefix4))
+ && (srp->nhlfe.prefv4.prefixlen == p->prefixlen))) {
+
+ /* Update Interface & Prefix info */
+ srp->nhlfe.ifindex = ifp->ifindex;
+ IPV4_ADDR_COPY(&srp->nhlfe.prefv4.prefix,
+ &p->u.prefix4);
+ srp->nhlfe.prefv4.prefixlen = p->prefixlen;
+ srp->nhlfe.prefv4.family = p->family;
+ IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &p->u.prefix4);
+
+ /* OK. Let's Schedule Extended Prefix LSA */
+ srp->instance = ospf_ext_schedule_prefix_index(ifp,
+ srp->sid, &srp->nhlfe.prefv4, srp->flags);
+
+ /* Install NHLFE if NO-PHP is requested */
+ if (CHECK_FLAG(srp->flags,
+ EXT_SUBTLV_PREFIX_SID_NPFLG)) {
+ srp->nhlfe.label_in = index2label(srp->sid,
+ OspfSR.self->srgb);
+ srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL;
+ add_sid_nhlfe(srp->nhlfe);
+ }
+ }
+ }
+}
+
+/*
+ * Following functions are used to update MPLS LFIB after a SPF run
+ */
+
+static void ospf_sr_nhlfe_update(struct hash_backet *backet, void *args)
+{
+
+ struct sr_node *srn = (struct sr_node *)backet->data;
+ struct listnode *node;
+ struct sr_prefix *srp;
+ struct sr_nhlfe old;
+ int rc;
+
+ /* Sanity Check */
+ if (srn == NULL)
+ return;
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(" |- Update Prefix for SR Node %s",
+ inet_ntoa(srn->adv_router));
+
+ /* Skip Self SR Node */
+ if (srn == OspfSR.self)
+ return;
+
+ /* Update Extended Prefix */
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+
+ /* Backup current NHLFE */
+ memcpy(&old, &srp->nhlfe, sizeof(struct sr_nhlfe));
+
+ /* Compute the new NHLFE */
+ rc = compute_prefix_nhlfe(srp);
+
+ /* Check computation result */
+ switch (rc) {
+ /* next hop is not know, remove old NHLFE to avoid loop */
+ case -1:
+ del_sid_nhlfe(srp->nhlfe);
+ break;
+ /* next hop has not changed, skip it */
+ case 0:
+ break;
+ /* there is a new next hop, update NHLFE */
+ case 1:
+ update_sid_nhlfe(old, srp->nhlfe);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int ospf_sr_update_schedule(struct thread *t)
+{
+
+ struct ospf *ospf;
+ struct timeval start_time, stop_time;
+
+ ospf = THREAD_ARG(t);
+ ospf->t_sr_update = NULL;
+
+ if (!OspfSR.update)
+ return 0;
+
+ monotime(&start_time);
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug("SR (%s): Start SPF update", __func__);
+
+ hash_iterate(OspfSR.neighbors, (void (*)(struct hash_backet *,
+ void *))ospf_sr_nhlfe_update,
+ NULL);
+
+ monotime(&stop_time);
+
+ zlog_info(
+ "SR (%s): SPF Processing Time(usecs): %lld\n",
+ __func__,
+ (stop_time.tv_sec - start_time.tv_sec) * 1000000LL
+ + (stop_time.tv_usec - start_time.tv_usec));
+
+ OspfSR.update = false;
+ return 1;
+}
+
+#define OSPF_SR_UPDATE_INTERVAL 1
+
+void ospf_sr_update_timer_add(struct ospf *ospf)
+{
+
+ if (ospf == NULL)
+ return;
+
+ /* Check if an update is not alreday engage */
+ if (OspfSR.update)
+ return;
+
+ OspfSR.update = true;
+
+ thread_add_timer(master, ospf_sr_update_schedule, ospf,
+ OSPF_SR_UPDATE_INTERVAL, &ospf->t_sr_update);
+}
+
+/*
+ * --------------------------------------
+ * Followings are vty command functions.
+ * --------------------------------------
+ */
+
+/*
+ * Segment Routing Router configuration
+ *
+ * Must be centralize as it concerns both Extended Link/Prefix LSA
+ * and Router Information LSA. Choose to call it from Extended Prefix
+ * write_config() call back.
+ *
+ * @param vty VTY output
+ *
+ * @return none
+ */
+void ospf_sr_config_write_router(struct vty *vty)
+{
+ struct listnode *node;
+ struct sr_prefix *srp;
+
+ if (OspfSR.enabled) {
+ vty_out(vty, " segment-routing on\n");
+
+ if ((OspfSR.srgb.lower_bound != MPLS_DEFAULT_MIN_SRGB_LABEL)
+ || (OspfSR.srgb.range_size != MPLS_DEFAULT_MAX_SRGB_SIZE)) {
+ vty_out(vty, " segment-routing global-block %u %u\n",
+ OspfSR.srgb.lower_bound,
+ OspfSR.srgb.lower_bound +
+ OspfSR.srgb.range_size - 1);
+ }
+ if (OspfSR.msd != 0)
+ vty_out(vty, " segment-routing node-msd %u\n",
+ OspfSR.msd);
+
+ if (OspfSR.self != NULL) {
+ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node,
+ srp)) {
+ vty_out(vty,
+ " segment-routing prefix %s/%u "
+ "index %u%s\n",
+ inet_ntoa(srp->nhlfe.prefv4.prefix),
+ srp->nhlfe.prefv4.prefixlen, srp->sid,
+ CHECK_FLAG(srp->flags,
+ EXT_SUBTLV_PREFIX_SID_NPFLG) ?
+ " no-php-flag" : "");
+ }
+ }
+ }
+}
+
+DEFUN(ospf_sr_enable,
+ ospf_sr_enable_cmd,
+ "segment-routing on",
+ SR_STR
+ "Enable Segment Routing\n")
+{
+
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ if (OspfSR.enabled)
+ return CMD_SUCCESS;
+
+ if (ospf->vrf_id != VRF_DEFAULT) {
+ vty_out(vty, "Segment Routing is only supported in default "
+ "VRF\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("SR: Segment Routing: OFF -> ON");
+
+ /* Start Segment Routing */
+ OspfSR.enabled = true;
+ if (!ospf_sr_start(ospf)) {
+ zlog_warn("SR: Unable to start Segment Routing. Abort!");
+ return CMD_WARNING;
+ }
+
+ /* Set Router Information SR parameters */
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("SR: Activate SR for Router Information LSA");
+
+ ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+ /* Update Ext LSA */
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("SR: Activate SR for Extended Link/Prefix LSA");
+
+ ospf_ext_update_sr(true);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf_sr_enable,
+ no_ospf_sr_enable_cmd,
+ "no segment-routing [on]",
+ NO_STR
+ SR_STR
+ "Disable Segment Routing\n")
+{
+
+ if (!OspfSR.enabled)
+ return CMD_SUCCESS;
+
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("SR: Segment Routing: ON -> OFF");
+
+ /* Start by Disabling Extended Link & Prefix LSA */
+ ospf_ext_update_sr(false);
+
+ /* then, disable Router Information SR parameters */
+ ospf_router_info_update_sr(false, OspfSR.srgb, OspfSR.msd);
+
+ /* Finally, stop Segment Routing */
+ ospf_sr_stop();
+ OspfSR.enabled = false;
+
+ return CMD_SUCCESS;
+}
+
+static int ospf_sr_enabled(struct vty *vty)
+{
+ if (OspfSR.enabled)
+ return 1;
+
+ if (vty)
+ vty_out(vty, "%% OSPF SR is not turned on\n");
+
+ return 0;
+}
+
+DEFUN (sr_sid_label_range,
+ sr_sid_label_range_cmd,
+ "segment-routing global-block (0-1048575) (0-1048575)",
+ SR_STR
+ "Segment Routing Global Block label range\n"
+ "Lower-bound range in decimal (0-1048575)\n"
+ "Upper-bound range in decimal (0-1048575)\n")
+{
+ uint32_t upper;
+ uint32_t lower;
+ uint32_t size;
+ int idx_low = 2;
+ int idx_up = 3;
+
+ if (!ospf_sr_enabled(vty))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Get lower and upper bound */
+ lower = strtoul(argv[idx_low]->arg, NULL, 10);
+ upper = strtoul(argv[idx_up]->arg, NULL, 10);
+ size = upper - lower + 1;
+
+ if (size > MPLS_DEFAULT_MAX_SRGB_SIZE || size <= 0) {
+ vty_out(vty,
+ "Range size cannot be less than 0 or more than %u\n",
+ MPLS_DEFAULT_MAX_SRGB_SIZE);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (upper > MPLS_DEFAULT_MAX_SRGB_LABEL) {
+ vty_out(vty, "Upper-bound cannot exceed %u\n",
+ MPLS_DEFAULT_MAX_SRGB_LABEL);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (upper < MPLS_DEFAULT_MIN_SRGB_LABEL) {
+ vty_out(vty, "Upper-bound cannot be lower than %u\n",
+ MPLS_DEFAULT_MIN_SRGB_LABEL);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check if values have changed */
+ if ((OspfSR.srgb.range_size == size)
+ && (OspfSR.srgb.lower_bound == lower))
+ return CMD_SUCCESS;
+
+ /* Set SID/Label range SRGB */
+ OspfSR.srgb.range_size = size;
+ OspfSR.srgb.lower_bound = lower;
+ if (OspfSR.self != NULL) {
+ OspfSR.self->srgb.range_size = size;
+ OspfSR.self->srgb.lower_bound = lower;
+ }
+
+ /* Set Router Information SR parameters */
+ ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+ /* Update NHLFE entries */
+ hash_iterate(OspfSR.neighbors,
+ (void (*)(struct hash_backet *, void *))update_in_nhlfe,
+ NULL);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_sr_sid_label_range,
+ no_sr_sid_label_range_cmd,
+ "no segment-routing global-block [(0-1048575) (0-1048575)]",
+ NO_STR
+ SR_STR
+ "Segment Routing Global Block label range\n"
+ "Lower-bound range in decimal (0-1048575)\n"
+ "Upper-bound range in decimal (0-1048575)\n")
+{
+
+ if (!ospf_sr_enabled(vty))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Revert to default SRGB value */
+ OspfSR.srgb.range_size = MPLS_DEFAULT_MIN_SRGB_SIZE;
+ OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL;
+ if (OspfSR.self != NULL) {
+ OspfSR.self->srgb.range_size = OspfSR.srgb.range_size;
+ OspfSR.self->srgb.lower_bound = OspfSR.srgb.lower_bound;
+ }
+
+ /* Set Router Information SR parameters */
+ ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+ /* Update NHLFE entries */
+ hash_iterate(OspfSR.neighbors,
+ (void (*)(struct hash_backet *, void *))update_in_nhlfe,
+ NULL);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (sr_node_msd,
+ sr_node_msd_cmd,
+ "segment-routing node-msd (1-16)",
+ SR_STR
+ "Maximum Stack Depth for this router\n"
+ "Maximum number of label that could be stack (1-16)\n")
+{
+ uint32_t msd;
+ int idx = 1;
+
+ if (!ospf_sr_enabled(vty))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Get MSD */
+ argv_find(argv, argc, "(1-16)", &idx);
+ msd = strtoul(argv[idx]->arg, NULL, 10);
+ if (msd < 1 || msd > MPLS_MAX_LABELS) {
+ vty_out(vty, "MSD must be comprise between 1 and %u\n",
+ MPLS_MAX_LABELS);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check if value has changed */
+ if (OspfSR.msd == msd)
+ return CMD_SUCCESS;
+
+ /* Set this router MSD */
+ OspfSR.msd = msd;
+ if (OspfSR.self != NULL)
+ OspfSR.self->msd = msd;
+
+ /* Set Router Information SR parameters */
+ ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_sr_node_msd,
+ no_sr_node_msd_cmd,
+ "no segment-routing node-msd [(1-16)]",
+ NO_STR
+ SR_STR
+ "Maximum Stack Depth for this router\n"
+ "Maximum number of label that could be stack (1-16)\n")
+{
+
+ if (!ospf_sr_enabled(vty))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* unset this router MSD */
+ OspfSR.msd = 0;
+ if (OspfSR.self != NULL)
+ OspfSR.self->msd = 0;
+
+ /* Set Router Information SR parameters */
+ ospf_router_info_update_sr(true, OspfSR.srgb, 0);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (sr_prefix_sid,
+ sr_prefix_sid_cmd,
+ "segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag]",
+ SR_STR
+ "Prefix SID\n"
+ "IPv4 Prefix as A.B.C.D/M\n"
+ "SID index for this prefix in decimal (0-65535)\n"
+ "Index value inside SRGB (lower_bound < index < upper_bound)\n"
+ "Don't request Penultimate Hop Popping (PHP)\n")
+{
+ int idx = 0;
+ struct prefix p;
+ uint32_t index;
+ struct listnode *node;
+ struct sr_prefix *srp, *new;
+ struct interface *ifp;
+
+ if (!ospf_sr_enabled(vty))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Get network prefix */
+ argv_find(argv, argc, "A.B.C.D/M", &idx);
+ if (!str2prefix(argv[idx]->arg, &p)) {
+ vty_out(vty, "Invalid prefix format %s\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Get & verify index value */
+ argv_find(argv, argc, "(0-65535)", &idx);
+ index = strtoul(argv[idx]->arg, NULL, 10);
+ if (index > OspfSR.srgb.range_size - 1) {
+ vty_out(vty, "Index %u must be lower than range size %u\n",
+ index, OspfSR.srgb.range_size);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* check that the index is not already used */
+ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) {
+ if (srp->sid == index) {
+ vty_out(vty, "Index %u is already used\n", index);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ /* Create new Extended Prefix to SRDB if not found */
+ new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix));
+ IPV4_ADDR_COPY(&new->nhlfe.prefv4.prefix, &p.u.prefix4);
+ IPV4_ADDR_COPY(&new->nhlfe.nexthop, &p.u.prefix4);
+ new->nhlfe.prefv4.prefixlen = p.prefixlen;
+ new->nhlfe.prefv4.family = p.family;
+ new->sid = index;
+ /* Set NO PHP flag if present and compute NHLFE */
+ if (argv_find(argv, argc, "no-php-flag", &idx)) {
+ SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG);
+ new->nhlfe.label_in = index2label(new->sid, OspfSR.self->srgb);
+ new->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL;
+ }
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "SR (%s): Add new index %u to Prefix %s/%u",
+ __func__, index, inet_ntoa(new->nhlfe.prefv4.prefix),
+ new->nhlfe.prefv4.prefixlen);
+
+ /* Get Interface and check if it is a Loopback */
+ ifp = if_lookup_prefix(&p, VRF_DEFAULT);
+ if (ifp == NULL) {
+ /*
+ * Interface could be not yet available i.e. when this
+ * command is in the configuration file, OSPF is not yet
+ * ready. In this case, store the prefix SID for latter
+ * update of this Extended Prefix
+ */
+ listnode_add(OspfSR.self->ext_prefix, new);
+ zlog_warn(
+ "Interface for prefix %s/%u not found. Deferred LSA "
+ "flooding", inet_ntoa(p.u.prefix4), p.prefixlen);
+ return CMD_SUCCESS;
+ }
+
+ if (!if_is_loopback(ifp)) {
+ vty_out(vty, "interface %s is not a Loopback\n", ifp->name);
+ XFREE(MTYPE_OSPF_SR_PARAMS, new);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ new->nhlfe.ifindex = ifp->ifindex;
+
+ /* Search if this prefix already exist */
+ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) {
+ if ((IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4)
+ && srp->nhlfe.prefv4.prefixlen == p.prefixlen))
+ break;
+ else
+ srp = NULL;
+ }
+
+ /* Update or Add this new SR Prefix */
+ if (srp) {
+ update_sid_nhlfe(srp->nhlfe, new->nhlfe);
+ listnode_delete(OspfSR.self->ext_prefix, srp);
+ listnode_add(OspfSR.self->ext_prefix, new);
+ } else {
+ listnode_add(OspfSR.self->ext_prefix, new);
+ add_sid_nhlfe(new->nhlfe);
+ }
+
+ /* Finally, update Extended Prefix LSA */
+ new->instance = ospf_ext_schedule_prefix_index(ifp, new->sid,
+ &new->nhlfe.prefv4, new->flags);
+ if (new->instance == 0) {
+ vty_out(vty, "Unable to set index %u for prefix %s/%u\n", index,
+ inet_ntoa(p.u.prefix4), p.prefixlen);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_sr_prefix_sid,
+ no_sr_prefix_sid_cmd,
+ "no segment-routing prefix A.B.C.D/M [index (0-65535) no-php-flag]",
+ NO_STR
+ SR_STR
+ "Prefix SID\n"
+ "IPv4 Prefix as A.B.C.D/M\n"
+ "SID index for this prefix in decimal (0-65535)\n"
+ "Index value inside SRGB (lower_bound < index < upper_bound)\n"
+ "Don't request Penultimate Hop Popping (PHP)\n")
+{
+ int idx = 0;
+ struct prefix p;
+ struct listnode *node;
+ struct sr_prefix *srp;
+ struct interface *ifp;
+ bool found = false;
+ int rc;
+
+ /* Get network prefix */
+ argv_find(argv, argc, "A.B.C.D/M", &idx);
+ rc = str2prefix(argv[idx]->arg, &p);
+ if (!rc) {
+ vty_out(vty, "Invalid prefix format %s\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* check that the prefix is already set */
+ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp))
+ if (IPV4_ADDR_SAME(&srp->nhlfe.prefv4.prefix, &p.u.prefix4)
+ && (srp->nhlfe.prefv4.prefixlen == p.prefixlen)) {
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ vty_out(vty, "Prefix %s is not found. Abort!\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Get Interface */
+ ifp = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT);
+ if (ifp == NULL) {
+ vty_out(vty, "interface for prefix %s not found.\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Update Extended Prefix LSA */
+ if (!ospf_ext_schedule_prefix_index(ifp, 0, NULL, 0)) {
+ vty_out(vty, "No corresponding loopback interface. Abort!\n");
+ return CMD_WARNING;
+ }
+
+ if (IS_DEBUG_OSPF_SR)
+ zlog_debug(
+ "SR (%s): Remove Prefix %s/%u with index %u",
+ __func__, inet_ntoa(srp->nhlfe.prefv4.prefix),
+ srp->nhlfe.prefv4.prefixlen, srp->sid);
+
+ /* Delete NHLFE is NO-PHP is set */
+ if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))
+ del_sid_nhlfe(srp->nhlfe);
+
+ /* OK, all is clean, remove SRP from SRDB */
+ listnode_delete(OspfSR.self->ext_prefix, srp);
+ XFREE(MTYPE_OSPF_SR_PARAMS, srp);
+
+ return CMD_SUCCESS;
+}
+
+
+
+static void show_vty_sr_node(struct vty *vty, struct sr_node *srn)
+{
+
+ struct listnode *node;
+ struct sr_link *srl;
+ struct sr_prefix *srp;
+ struct interface *itf;
+ char pref[16];
+ char sid[22];
+ char label[8];
+
+ /* Sanity Check */
+ if (srn == NULL)
+ return;
+
+ vty_out(vty, "SR-Node: %s", inet_ntoa(srn->adv_router));
+ vty_out(vty, "\tSRGB (Size/Label): %u/%u", srn->srgb.range_size,
+ srn->srgb.lower_bound);
+ vty_out(vty, "\tAlgorithm(s): %s",
+ srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF");
+ for (int i = 1; i < ALGORITHM_COUNT; i++) {
+ if (srn->algo[i] == SR_ALGORITHM_UNSET)
+ continue;
+ vty_out(vty, "/%s",
+ srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF");
+ }
+ if (srn->msd != 0)
+ vty_out(vty, "\tMSD: %u", srn->msd);
+
+ vty_out(vty,
+ "\n\n Prefix or Link Label In Label Out "
+ "Node or Adj. SID Interface Nexthop\n");
+ vty_out(vty,
+ "------------------ -------- --------- "
+ "--------------------- --------- ---------------\n");
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
+ strncpy(pref, inet_ntoa(srp->nhlfe.prefv4.prefix), 16);
+ snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid);
+ if (srp->nhlfe.label_out == MPLS_LABEL_IMPLICIT_NULL)
+ sprintf(label, "pop");
+ else
+ sprintf(label, "%u", srp->nhlfe.label_out);
+ itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT);
+ vty_out(vty, "%15s/%u %8u %9s %21s %9s %15s\n", pref,
+ srp->nhlfe.prefv4.prefixlen, srp->nhlfe.label_in, label,
+ sid, itf ? itf->name : "-",
+ inet_ntoa(srp->nhlfe.nexthop));
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) {
+ strncpy(pref, inet_ntoa(srl->nhlfe[0].prefv4.prefix), 16);
+ snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[0]);
+ if (srl->nhlfe[0].label_out == MPLS_LABEL_IMPLICIT_NULL)
+ sprintf(label, "pop");
+ else
+ sprintf(label, "%u", srl->nhlfe[0].label_out);
+ itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT);
+ vty_out(vty, "%15s/%u %8u %9s %21s %9s %15s\n", pref,
+ srl->nhlfe[0].prefv4.prefixlen, srl->nhlfe[0].label_in,
+ label, sid, itf ? itf->name : "-",
+ inet_ntoa(srl->nhlfe[0].nexthop));
+ snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]);
+ if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL)
+ sprintf(label, "pop");
+ else
+ sprintf(label, "%u", srl->nhlfe[0].label_out);
+ vty_out(vty, "%15s/%u %8u %9s %21s %9s %15s\n", pref,
+ srl->nhlfe[1].prefv4.prefixlen, srl->nhlfe[1].label_in,
+ label, sid, itf ? itf->name : "-",
+ inet_ntoa(srl->nhlfe[1].nexthop));
+ }
+ vty_out(vty, "\n");
+}
+
+static void show_srdb_entry(struct hash_backet *backet, void *args)
+{
+ struct vty *vty = (struct vty *)args;
+ struct sr_node *srn = (struct sr_node *)backet->data;
+
+ show_vty_sr_node(vty, srn);
+}
+
+DEFUN (show_ip_opsf_srdb,
+ show_ip_ospf_srdb_cmd,
+ "show ip ospf database segment-routing [adv-router A.B.C.D|self-originate]",
+ SHOW_STR
+ IP_STR
+ OSPF_STR
+ "Database summary\n"
+ "Show Segment Routing Data Base\n"
+ "Advertising SR node\n"
+ "Advertising SR node ID (as an IP address)\n"
+ "Self-originated SR node\n")
+{
+ int idx = 0;
+ struct in_addr rid;
+ struct sr_node *srn;
+
+ if (!OspfSR.enabled) {
+ vty_out(vty, "Segment Routing is disabled on this router\n");
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "\n OSPF Segment Routing database for ID %s\n\n",
+ inet_ntoa(OspfSR.self->adv_router));
+
+ if (argv_find(argv, argc, "self-originate", &idx)) {
+ srn = OspfSR.self;
+ show_vty_sr_node(vty, srn);
+ return CMD_SUCCESS;
+ }
+
+ if (argv_find(argv, argc, "A.B.C.D", &idx)) {
+ if (!inet_aton(argv[idx]->arg, &rid)) {
+ vty_out(vty,
+ "Specified Router ID %s is invalid\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Get the SR Node from the SRDB */
+ srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
+ (void *)&rid);
+ show_vty_sr_node(vty, srn);
+ return CMD_SUCCESS;
+ }
+
+ /* No parameters have been provided, Iterate through all the SRDB */
+ hash_iterate(
+ OspfSR.neighbors,
+ (void (*)(struct hash_backet *, void *))show_srdb_entry,
+ (void *)vty);
+ return CMD_SUCCESS;
+}
+
+/* Install new CLI commands */
+void ospf_sr_register_vty(void)
+{
+ install_element(VIEW_NODE, &show_ip_ospf_srdb_cmd);
+
+ install_element(OSPF_NODE, &ospf_sr_enable_cmd);
+ install_element(OSPF_NODE, &no_ospf_sr_enable_cmd);
+ install_element(OSPF_NODE, &sr_sid_label_range_cmd);
+ install_element(OSPF_NODE, &no_sr_sid_label_range_cmd);
+ install_element(OSPF_NODE, &sr_node_msd_cmd);
+ install_element(OSPF_NODE, &no_sr_node_msd_cmd);
+ install_element(OSPF_NODE, &sr_prefix_sid_cmd);
+ install_element(OSPF_NODE, &no_sr_prefix_sid_cmd);
+
+}
--- /dev/null
+/*
+ * This is an implementation of Segment Routing
+ * as per draft draft-ietf-ospf-segment-routing-extensions-24
+ *
+ * Module name: Segment Routing header definitions
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ * Author: Anselme Sawadogo <anselmesawadogo@gmail.com>
+ *
+ * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_OSPF_SR_H
+#define _FRR_OSPF_SR_H
+
+/* Default Route priority for OSPF Segment Routing */
+#define OSPF_SR_PRIORITY_DEFAULT 10
+
+/* macros and constants for segment routing */
+#define SET_RANGE_SIZE_MASK 0xffffff00
+#define GET_RANGE_SIZE_MASK 0x00ffffff
+#define SET_LABEL_MASK 0xffffff00
+#define GET_LABEL_MASK 0x00ffffff
+#define SET_RANGE_SIZE(range_size) ((range_size << 8) & SET_RANGE_SIZE_MASK)
+#define GET_RANGE_SIZE(range_size) ((range_size >> 8) & GET_RANGE_SIZE_MASK)
+#define SET_LABEL(label) ((label << 8) & SET_LABEL_MASK)
+#define GET_LABEL(label) ((label >> 8) & GET_LABEL_MASK)
+
+/* Label range for Adj-SID attribution purpose. Start just right after SRGB */
+#define ADJ_SID_MIN MPLS_DEFAULT_MAX_SRGB_LABEL
+#define ADJ_SID_MAX (MPLS_DEFAULT_MAX_SRGB_LABEL + 1000)
+
+#define OSPF_SR_DEFAULT_METRIC 1
+
+/* Segment Routing TLVs as per draft-ietf-ospf-segment-routing-extensions-19 */
+
+/* Segment ID could be a Label (3 bytes) or an Index (4 bytes) */
+#define SID_BASE_SIZE 4
+#define SID_LABEL 3
+#define SID_LABEL_SIZE (SID_BASE_SIZE + SID_LABEL)
+#define SID_INDEX 4
+#define SID_INDEX_SIZE (SID_BASE_SIZE + SID_INDEX)
+
+/* SID/Label Sub TLV - section 2.1 */
+#define SUBTLV_SID_LABEL 1
+#define SUBTLV_SID_LABEL_SIZE 8
+struct subtlv_sid_label {
+ /* Length is 3 (20 rightmost bits MPLS label) or 4 (32 bits SID) */
+ struct tlv_header header;
+ uint32_t value;
+};
+
+/*
+ * Following section defines Segment Routing TLV (tag, length, value)
+ * structures, used in Router Information Opaque LSA.
+ */
+
+/* RI SR-Algorithm TLV - section 3.1 */
+#define RI_SR_TLV_SR_ALGORITHM 8
+struct ri_sr_tlv_sr_algorithm {
+ struct tlv_header header;
+#define SR_ALGORITHM_SPF 0
+#define SR_ALGORITHM_STRICT_SPF 1
+#define SR_ALGORITHM_UNSET 255
+#define ALGORITHM_COUNT 4
+ /* Only 4 algorithms supported in this code */
+ uint8_t value[ALGORITHM_COUNT];
+};
+
+/* RI SID/Label Range TLV - section 3.2 */
+#define RI_SR_TLV_SID_LABEL_RANGE 9
+struct ri_sr_tlv_sid_label_range {
+ struct tlv_header header;
+/* Only 24 upper most bits are significant */
+#define SID_RANGE_LABEL_LENGTH 3
+ uint32_t size;
+ /* A SID/Label sub-TLV will follow. */
+ struct subtlv_sid_label lower;
+};
+
+/* RI Node/MSD TLV as per draft-ietf-ospf-segment-routing-msd-05 */
+#define RI_SR_TLV_NODE_MSD 12
+struct ri_sr_tlv_node_msd {
+ struct tlv_header header;
+ uint8_t subtype; /* always = 1 */
+ uint8_t value;
+ uint16_t padding;
+};
+
+/*
+ * Following section defines Segment Routing TLV (tag, length, value)
+ * structures, used in Extended Prefix/Link Opaque LSA.
+ */
+
+/* Adj-SID and LAN-Ajd-SID subtlvs' flags */
+#define EXT_SUBTLV_LINK_ADJ_SID_BFLG 0x80
+#define EXT_SUBTLV_LINK_ADJ_SID_VFLG 0x40
+#define EXT_SUBTLV_LINK_ADJ_SID_LFLG 0x20
+#define EXT_SUBTLV_LINK_ADJ_SID_SFLG 0x10
+
+/* Prefix SID subtlv Flags */
+#define EXT_SUBTLV_PREFIX_SID_NPFLG 0x40
+#define EXT_SUBTLV_PREFIX_SID_MFLG 0x20
+#define EXT_SUBTLV_PREFIX_SID_EFLG 0x10
+#define EXT_SUBTLV_PREFIX_SID_VFLG 0x08
+#define EXT_SUBTLV_PREFIX_SID_LFLG 0x04
+
+/* SID/Label Binding subtlv Flags */
+#define EXT_SUBTLV_SID_BINDING_MFLG 0x80
+
+/* Extended Prefix Range TLV - section 4 */
+#define EXT_TLV_PREF_RANGE 2
+#define EXT_SUBTLV_PREFIX_RANGE_SIZE 12
+struct ext_tlv_prefix_range {
+ struct tlv_header header;
+ uint8_t pref_length;
+ uint8_t af;
+ uint16_t range_size;
+ uint8_t flags;
+ uint8_t reserved[3];
+ struct in_addr address;
+};
+
+/* Prefix SID Sub-TLV - section 5 */
+#define EXT_SUBTLV_PREFIX_SID 2
+#define EXT_SUBTLV_PREFIX_SID_SIZE 8
+struct ext_subtlv_prefix_sid {
+ struct tlv_header header;
+ uint8_t flags;
+ uint8_t reserved;
+ uint8_t mtid;
+ uint8_t algorithm;
+ uint32_t value;
+};
+
+/* Adj-SID Sub-TLV - section 6.1 */
+#define EXT_SUBTLV_ADJ_SID 2
+#define EXT_SUBTLV_ADJ_SID_SIZE 8
+struct ext_subtlv_adj_sid {
+ struct tlv_header header;
+ uint8_t flags;
+ uint8_t reserved;
+ uint8_t mtid;
+ uint8_t weight;
+ uint32_t value;
+};
+
+/* LAN Adj-SID Sub-TLV - section 6.2 */
+#define EXT_SUBTLV_LAN_ADJ_SID 3
+#define EXT_SUBTLV_LAN_ADJ_SID_SIZE 12
+struct ext_subtlv_lan_adj_sid {
+ struct tlv_header header;
+ uint8_t flags;
+ uint8_t reserved;
+ uint8_t mtid;
+ uint8_t weight;
+ struct in_addr neighbor_id;
+ uint32_t value;
+};
+
+/*
+ * Following section define structure used to manage Segment Routing
+ * information and TLVs / SubTLVs
+ */
+
+/* Structure aggregating SRGB info retrieved from an lsa */
+struct sr_srgb {
+ uint32_t range_size;
+ uint32_t lower_bound;
+};
+
+/* SID type to make difference between loopback interfaces and others */
+enum sid_type { PREF_SID, ADJ_SID, LAN_ADJ_SID };
+
+/* Structure aggregating all OSPF Segment Routing information for the node */
+struct ospf_sr_db {
+ /* Status of Segment Routing: enable or disable */
+ bool enabled;
+
+ /* Ongoing Update following an OSPF SPF */
+ bool update;
+
+ /* Flooding Scope: Area = 10 or AS = 11 */
+ uint8_t scope;
+
+ /* FRR SR node */
+ struct sr_node *self;
+
+ /* List of neighbour SR nodes */
+ struct hash *neighbors;
+
+ /* List of SR prefix */
+ struct route_table *prefix;
+
+ /* Local SR info announced in Router Info LSA */
+
+ /* Algorithms supported by the node */
+ uint8_t algo[ALGORITHM_COUNT];
+ /*
+ * Segment Routing Global Block i.e. label range
+ * Only one range supported in this code
+ */
+ struct sr_srgb srgb;
+ /* Maximum SID Depth supported by the node */
+ uint8_t msd;
+};
+
+/* Structure aggregating all received SR info from LSAs by node */
+struct sr_node {
+ struct in_addr adv_router; /* used to identify sender of LSA */
+ /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+ uint32_t instance;
+
+ uint8_t algo[ALGORITHM_COUNT]; /* Algorithms supported by the node */
+ /* Segment Routing Global Block i.e. label range */
+ struct sr_srgb srgb;
+ uint8_t msd; /* Maximum SID Depth */
+
+ /* List of Prefix & Link advertise by this node */
+ struct list *ext_prefix; /* For Node SID */
+ struct list *ext_link; /* For Adj and LAN SID */
+
+ /* Pointer to FRR SR-Node or NULL if it is not a neighbor */
+ struct sr_node *neighbor;
+};
+
+
+/* Segment Routing - NHLFE info: support IPv4 Only */
+struct sr_nhlfe {
+ struct prefix_ipv4 prefv4;
+ struct in_addr nexthop;
+ ifindex_t ifindex;
+ mpls_label_t label_in;
+ mpls_label_t label_out;
+};
+
+/* Structure aggregating all Segment Routing Link information */
+/* Link are generally advertised by pair: primary + backup */
+struct sr_link {
+ struct in_addr adv_router; /* used to identify sender of LSA */
+ /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+ uint32_t instance;
+
+ /* Flags to manage this link parameters. */
+ uint8_t flags[2];
+
+ /* Segment Routing ID */
+ uint32_t sid[2];
+ enum sid_type type;
+
+ /* SR NHLFE for this link */
+ struct sr_nhlfe nhlfe[2];
+
+ /* Back pointer to SR Node which advertise this Link */
+ struct sr_node *srn;
+};
+
+/* Structure aggregating all Segment Routing Prefix information */
+struct sr_prefix {
+ struct in_addr adv_router; /* used to identify sender of LSA */
+ /* 24-bit Opaque-ID field value according to RFC 7684 specification */
+ uint32_t instance;
+
+ /* Flags to manage this prefix parameters. */
+ uint8_t flags;
+
+ /* Segment Routing ID */
+ uint32_t sid;
+ enum sid_type type;
+
+ /* SR NHLFE for this prefix */
+ struct sr_nhlfe nhlfe;
+
+ /* Back pointer to SR Node which advertise this Prefix */
+ struct sr_node *srn;
+
+ /*
+ * Pointer to SR Node which is the next hop for this Prefix
+ * or NULL if next hop is the destination of the prefix
+ */
+ struct sr_node *nexthop;
+};
+
+/* Prototypes definition */
+/* Segment Routing initialisation functions */
+extern int ospf_sr_init(void);
+extern void ospf_sr_term(void);
+extern void ospf_sr_finish(void);
+/* Segment Routing LSA update & delete functions */
+extern void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa);
+extern void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa);
+extern void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa);
+extern void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa);
+extern void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa);
+extern void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa);
+/* Segment Routing configuration functions */
+extern uint32_t get_ext_link_label_value(void);
+extern void ospf_sr_config_write_router(struct vty *vty);
+extern void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p);
+/* Segment Routing re-routing function */
+extern void ospf_sr_update_timer_add(struct ospf *ospf);
+#endif /* _FRR_OSPF_SR_H */
ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA,
OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA);
+
OspfMplsTE.enabled = false;
ospf_mpls_te_unregister();
return;
}
+void ospf_mpls_te_finish(void)
+{
+ // list_delete_all_node(OspfMplsTE.iflist);
+
+ OspfMplsTE.enabled = false;
+ OspfMplsTE.inter_as = Off;
+}
+
/*------------------------------------------------------------------------*
* Followings are control functions for MPLS-TE parameters management.
*------------------------------------------------------------------------*/
/* Prototypes. */
extern int ospf_mpls_te_init(void);
extern void ospf_mpls_te_term(void);
+extern void ospf_mpls_te_finish(void);
extern struct ospf_mpls_te *get_ospf_mpls_te(void);
extern void ospf_mpls_te_update_if(struct interface *);
extern void ospf_mpls_te_lsa_schedule(struct mpls_te_link *, enum lsa_opcode);
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);
}
if (ospf->vrf_id != VRF_UNKNOWN)
- ifp = if_get_by_name(argv[1]->arg, ospf->vrf_id, 0);
+ 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);
+ (char *)argv[2]->arg);
return CMD_WARNING_CONFIG_FAILED;
}
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"
VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
int idx = 0;
+ ospf->distance_intra = 0;
+ ospf->distance_inter = 0;
+ ospf->distance_external = 0;
+
if (argv_find(argv, argc, "intra-area", &idx))
ospf->distance_intra = atoi(argv[idx + 1]->arg);
idx = 0;
}
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");
vty_out(vty, \
"%% You can't configure %s to backbone\n", \
NAME); \
+ return CMD_WARNING; \
} \
}
api_nh->ifindex = path->ifindex;
api_nh->type = NEXTHOP_TYPE_IFINDEX;
}
+ api_nh->vrf_id = ospf->vrf_id;
count++;
if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) {
#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
ospf_opaque_type11_lsa_term(ospf);
+ ospf_opaque_finish();
+
ospf_flush_self_originated_lsas_now(ospf);
/* Unregister redistribution */
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);
struct thread *t_external_lsa; /* AS-external-LSA origin timer. */
struct thread
*t_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */
+ struct thread *t_sr_update; /* Segment Routing update timer */
unsigned int maxage_delay; /* Delay on Maxage remover timer, sec */
struct thread *t_maxage; /* MaxAge LSA remover timer. */
ospfd/ospf_bfd.c \
ospfd/ospf_dump.c \
ospfd/ospf_dump_api.c \
+ ospfd/ospf_ext.c \
ospfd/ospf_flood.c \
ospfd/ospf_ia.c \
ospfd/ospf_interface.c \
ospfd/ospf_route.c \
ospfd/ospf_routemap.c \
ospfd/ospf_spf.c \
+ ospfd/ospf_sr.c \
ospfd/ospf_te.c \
ospfd/ospf_vty.c \
ospfd/ospf_zebra.c \
ospfd/ospf_apiserver.h \
ospfd/ospf_ase.h \
ospfd/ospf_bfd.h \
+ ospfd/ospf_ext.h \
ospfd/ospf_flood.h \
ospfd/ospf_ia.h \
ospfd/ospf_interface.h \
ospfd/ospf_ri.h \
ospfd/ospf_route.h \
ospfd/ospf_spf.h \
+ ospfd/ospf_sr.h \
ospfd/ospf_te.h \
ospfd/ospf_vty.h \
ospfd/ospf_zebra.h \
Makefile.in
libpim.a
pimd
+mtracebis
test_igmpv3_join
tags
TAGS
clear ip pim interfaces Reset PIM interfaces
clear ip pim oil Rescan PIM OIL (output interface list)
debug igmp IGMP protocol activity
+ debug mtrace Mtrace protocol activity
debug mroute PIM interaction with kernel MFC cache
debug pim PIM protocol activity
debug pim zebra ZEBRA protocol activity
pimd:
show memory pim PIM memory statistics
+vtysh:
+ mtrace Multicast traceroute
-x-
--- /dev/null
+/*
+ * Multicast Traceroute for FRRouting
+ * Copyright (C) 2018 Mladen Sablic
+ *
+ * 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
+ */
+
+#ifdef __linux__
+
+#include "pim_igmp_mtrace.h"
+
+#include "checksum.h"
+#include "mtracebis_routeget.h"
+
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <net/if.h>
+
+#define MTRACEBIS_VERSION "0.1"
+#define MTRACE_TIMEOUT (5)
+
+#define IP_HDR_LEN (sizeof(struct ip))
+#define IP_RA_LEN (4)
+#define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE))
+#define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN)
+
+static const char *progname;
+static void usage(void)
+{
+ fprintf(stderr, "Usage : %s <multicast source>\n", progname);
+}
+static void version(void)
+{
+ fprintf(stderr, "%s %s\n", progname, MTRACEBIS_VERSION);
+}
+
+static int send_query(int fd, struct in_addr to_addr,
+ struct igmp_mtrace *mtrace)
+{
+ struct sockaddr_in to;
+ socklen_t tolen;
+ int sent;
+
+ memset(&to, 0, sizeof(to));
+ to.sin_family = AF_INET;
+ to.sin_addr = to_addr;
+ tolen = sizeof(to);
+
+ sent = sendto(fd, (char *)mtrace, sizeof(*mtrace), MSG_DONTWAIT,
+ (struct sockaddr *)&to, tolen);
+
+ if (sent < 1)
+ return -1;
+ return 0;
+}
+
+static void print_query(struct igmp_mtrace *mtrace)
+{
+ char src_str[INET_ADDRSTRLEN];
+ char dst_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+
+ printf("* Mtrace from %s to %s via group %s\n",
+ inet_ntop(AF_INET, &mtrace->src_addr, src_str, sizeof(src_str)),
+ inet_ntop(AF_INET, &mtrace->dst_addr, dst_str, sizeof(dst_str)),
+ inet_ntop(AF_INET, &mtrace->grp_addr, grp_str, sizeof(grp_str)));
+}
+
+static int recv_response(int fd, long msec, int *hops)
+{
+ int recvd;
+ char mtrace_buf[IP_AND_MTRACE_BUF_LEN];
+ struct ip *ip;
+ struct igmp_mtrace *mtrace;
+ int mtrace_len;
+ int responses;
+ int i;
+ u_short sum;
+
+ recvd = recvfrom(fd, mtrace_buf, IP_AND_MTRACE_BUF_LEN, 0, NULL, 0);
+
+ if (recvd < 1) {
+ fprintf(stderr, "recvfrom error: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (recvd < (int)sizeof(struct ip)) {
+ fprintf(stderr, "no ip header\n");
+ return -1;
+ }
+
+ ip = (struct ip *)mtrace_buf;
+
+ if (ip->ip_v != 4) {
+ fprintf(stderr, "IP not version 4\n");
+ return -1;
+ }
+
+ sum = ip->ip_sum;
+ ip->ip_sum = 0;
+
+ if (sum != in_cksum(ip, ip->ip_hl * 4))
+ return -1;
+
+ mtrace = (struct igmp_mtrace *)(mtrace_buf + (4 * ip->ip_hl));
+
+ mtrace_len = ntohs(ip->ip_len) - ip->ip_hl * 4;
+
+ if (mtrace_len < (int)MTRACE_HDR_SIZE)
+ return -1;
+
+ sum = mtrace->checksum;
+ mtrace->checksum = 0;
+ if (sum != in_cksum(mtrace, mtrace_len)) {
+ fprintf(stderr, "mtrace checksum wrong\n");
+ return -1;
+ }
+
+ if (mtrace->type != PIM_IGMP_MTRACE_RESPONSE)
+ return -1;
+
+
+ responses = mtrace_len - sizeof(struct igmp_mtrace);
+ responses /= sizeof(struct igmp_mtrace_rsp);
+
+ printf("%ld ms received responses from %d hops.\n", msec, responses);
+
+ if (hops)
+ *hops = responses;
+
+ for (i = 0; i < responses; i++) {
+ struct igmp_mtrace_rsp *rsp = &mtrace->rsp[i];
+
+ if (rsp->fwd_code != 0)
+ printf("-%d fwd. code 0x%2x.\n", i, rsp->fwd_code);
+ }
+
+ return 0;
+}
+
+static int wait_for_response(int fd, int *hops)
+{
+ fd_set readfds;
+ struct timeval timeout;
+ int ret = -1;
+ long msec, rmsec, tmsec;
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ memset(&timeout, 0, sizeof(timeout));
+
+ timeout.tv_sec = MTRACE_TIMEOUT;
+
+ tmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
+ do {
+ ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
+ if (ret <= 0)
+ return ret;
+ rmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
+ msec = tmsec - rmsec;
+ } while (recv_response(fd, msec, hops) != 0);
+
+ return ret;
+}
+
+int main(int argc, char *const argv[])
+{
+ struct in_addr mc_source;
+ struct in_addr iface_addr;
+ struct in_addr gw_addr;
+ struct in_addr mtrace_addr;
+ struct igmp_mtrace mtrace;
+ int hops = 255;
+ int rhops;
+ int maxhops = 255;
+ int perhop = 3;
+ int ifindex;
+ int unicast = 1;
+ int ttl = 64;
+ int fd = -1;
+ int ret = -1;
+ int c;
+ int i, j;
+ char ifname[IF_NAMESIZE];
+ char ip_str[INET_ADDRSTRLEN];
+
+ mtrace_addr.s_addr = inet_addr("224.0.1.32");
+
+ uid_t uid = getuid();
+
+ if (uid != 0) {
+ printf("must run as root\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (argc <= 0)
+ progname = "mtracebis";
+ else
+ progname = argv[0];
+
+ if (argc != 2) {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+
+ while (1) {
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'v'},
+ {0, 0, 0, 0} };
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "vh", long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ usage();
+ exit(0);
+ case 'v':
+ version();
+ exit(0);
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (inet_pton(AF_INET, argv[1], &mc_source) != 1) {
+ usage();
+ fprintf(stderr, "%s: %s not a valid IPv4 address\n", argv[0],
+ argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ ifindex = routeget(mc_source, &iface_addr, &gw_addr);
+ if (ifindex < 0) {
+ fprintf(stderr, "%s: failed to get route to source %s\n",
+ argv[0], argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (if_indextoname(ifindex, ifname) == NULL) {
+ fprintf(stderr, "%s: if_indextoname error: %s\n", argv[0],
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /* zero mtrace struct */
+ memset((char *)&mtrace, 0, sizeof(mtrace));
+
+ /* set up query */
+ mtrace.type = PIM_IGMP_MTRACE_QUERY_REQUEST;
+ mtrace.hops = hops;
+ mtrace.checksum = 0;
+ mtrace.grp_addr.s_addr = 0;
+ mtrace.src_addr = mc_source;
+ mtrace.dst_addr = iface_addr;
+ mtrace.rsp_addr = unicast ? iface_addr : mtrace_addr;
+ mtrace.rsp_ttl = ttl;
+ mtrace.qry_id = 0xffffff & time(NULL);
+
+ mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
+
+ fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
+
+ if (fd < 1) {
+ fprintf(stderr, "%s: socket error: %s\n", argv[0],
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
+ strlen(ifname));
+
+ if (ret < 0) {
+ fprintf(stderr, "%s: setsockopt error: %s\n", argv[0],
+ strerror(errno));
+ ret = EXIT_FAILURE;
+ goto close_fd;
+ }
+
+ print_query(&mtrace);
+ if (send_query(fd, gw_addr, &mtrace) < 0) {
+ fprintf(stderr, "%s: sendto error: %s\n", argv[0],
+ strerror(errno));
+ ret = EXIT_FAILURE;
+ goto close_fd;
+ }
+ printf("Querying full reverse path...\n");
+ ret = wait_for_response(fd, NULL);
+ if (ret > 0) {
+ ret = 0;
+ goto close_fd;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "%s: select error: %s\n", argv[0],
+ strerror(errno));
+ ret = EXIT_FAILURE;
+ goto close_fd;
+ }
+ printf(" * ");
+ printf("switching to hop-by-hop:\n");
+ printf("%3d ? (%s)\n", 0,
+ inet_ntop(AF_INET, &mtrace.dst_addr, ip_str, sizeof(ip_str)));
+ for (i = 1; i < maxhops; i++) {
+ printf("%3d ", -i);
+ mtrace.hops = i;
+ for (j = 0; j < perhop; j++) {
+ mtrace.qry_id++;
+ mtrace.checksum = 0;
+ mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
+ if (send_query(fd, gw_addr, &mtrace) < 0) {
+ fprintf(stderr, "%s: sendto error: %s\n",
+ argv[0], strerror(errno));
+ ret = EXIT_FAILURE;
+ goto close_fd;
+ }
+ ret = wait_for_response(fd, &rhops);
+ if (ret > 0) {
+ if (i > rhops) {
+ ret = 0;
+ goto close_fd;
+ }
+ break;
+ }
+ printf(" *");
+ }
+ if (ret <= 0)
+ printf("\n");
+ }
+ ret = 0;
+close_fd:
+ close(fd);
+ exit(ret);
+}
+
+#else /* __linux__ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+ printf("%s implemented only for GNU/Linux\n", argv[0]);
+ exit(0);
+}
+
+#endif /* __linux__ */
--- /dev/null
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * 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.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#ifdef __linux__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+#include <assert.h>
+
+#include "mtracebis_netlink.h"
+
+int rcvbuf = 1024 * 1024;
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ if (rth->fd >= 0) {
+ close(rth->fd);
+ rth->fd = -1;
+ }
+}
+
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+ int protocol)
+{
+ socklen_t addr_len;
+ int sndbuf = 32768;
+
+ memset(rth, 0, sizeof(*rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+ if (rth->fd < 0) {
+ perror("Cannot open netlink socket");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+ perror("SO_SNDBUF");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
+ perror("SO_RCVBUF");
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+ perror("Cannot bind netlink socket");
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+ perror("Cannot getsockname");
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ fprintf(stderr, "Wrong address length %d\n", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.g.rtgen_family = family;
+
+ return send(rth->fd, (void*)&req, sizeof(req), 0);
+}
+
+int rtnl_send(struct rtnl_handle *rth, const char *buf, int len)
+{
+ return send(rth->fd, buf, len, 0);
+}
+
+int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int len)
+{
+ struct nlmsghdr *h;
+ int status;
+ char resp[1024];
+
+ status = send(rth->fd, buf, len, 0);
+ if (status < 0)
+ return status;
+
+ /* Check for immediate errors */
+ status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK);
+ if (status < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ return -1;
+ }
+
+ for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, (uint32_t)status);
+ h = NLMSG_NEXT(h, status)) {
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
+ fprintf(stderr, "ERROR truncated\n");
+ else
+ errno = -err->error;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr;
+ struct iovec iov[2] = {
+ { .iov_base = &nlh, .iov_len = sizeof(nlh) },
+ { .iov_base = req, .iov_len = len }
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg)
+{
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ iov.iov_base = buf;
+ while (1) {
+ int status;
+ const struct rtnl_dump_filter_arg *a;
+ int found_done = 0;
+ int msglen = 0;
+
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+
+ for (a = arg; a->filter; a++) {
+ struct nlmsghdr *h = (struct nlmsghdr*)buf;
+ msglen = status;
+
+ while (NLMSG_OK(h, (uint32_t)msglen)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump) {
+ if (a->junk) {
+ err = a->junk(&nladdr, h,
+ a->arg2);
+ if (err < 0)
+ return err;
+ }
+ goto skip_it;
+ }
+
+ if (h->nlmsg_type == NLMSG_DONE) {
+ found_done = 1;
+ break; /* process next filter */
+ }
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr,
+ "ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ err = a->filter(&nladdr, h, a->arg1);
+ if (err < 0)
+ return err;
+
+skip_it:
+ h = NLMSG_NEXT(h, msglen);
+ }
+ }
+
+ if (found_done)
+ return 0;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (msglen) {
+ fprintf(stderr, "!!!Remnant of size %d\n", msglen);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+ rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2)
+{
+ const struct rtnl_dump_filter_arg a[2] = {
+ { .filter = filter, .arg1 = arg1, .junk = junk, .arg2 = arg2 },
+ { .filter = NULL, .arg1 = NULL, .junk = NULL, .arg2 = NULL }
+ };
+
+ return rtnl_dump_filter_l(rth, a);
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk,
+ void *jarg)
+{
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = {
+ .iov_base = (void*) n,
+ .iov_len = n->nlmsg_len
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = peer;
+ nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+
+ if (answer == NULL)
+ n->nlmsg_flags |= NLM_F_ACK;
+
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ perror("Cannot talk to rtnetlink");
+ return -1;
+ }
+
+ memset(buf,0,sizeof(buf));
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ if ((int)nladdr.nl_pid != peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+ if (junk) {
+ err = junk(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+ /* Don't forget to skip that message. */
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (l < (int)sizeof(struct nlmsgerr)) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ if (errno == 0) {
+ if (answer)
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ fprintf(stderr, "Unexpected reply!!!\n");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+ rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[8192];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ if (errno == ENOBUFS)
+ continue;
+ return -1;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h =(struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct sockaddr_nl nladdr;
+ char buf[8192];
+ struct nlmsghdr *h = (void*)buf;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ while (1) {
+ int err, len;
+ int l;
+
+ status = fread(&buf, 1, sizeof(*h), rtnl);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status == 0)
+ return 0;
+
+ len = h->nlmsg_len;
+ l = len - sizeof(*h);
+
+ if (l<0 || len>(int)sizeof(buf)) {
+ fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
+ len, ftell(rtnl));
+ return -1;
+ }
+
+ status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+ if (status < 0) {
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status < l) {
+ fprintf(stderr, "rtnl-from_file: truncated message\n");
+ return -1;
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *rta;
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
+ fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 4);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+ int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) {
+ fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+
+ if (data)
+ memcpy(RTA_DATA(rta), data, alen);
+ else
+ assert(alen == 0);
+
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
+{
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len)) > maxlen) {
+ fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+
+ memcpy(NLMSG_TAIL(n), data, len);
+ memset((uint8_t *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
+ return 0;
+}
+
+struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type)
+{
+ struct rtattr *nest = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, NULL, 0);
+ return nest;
+}
+
+int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
+{
+ nest->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)nest;
+ return n->nlmsg_len;
+}
+
+struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type,
+ const void *data, int len)
+{
+ struct rtattr *start = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, data, len);
+ addattr_nest(n, maxlen, type);
+ return start;
+}
+
+int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start)
+{
+ struct rtattr *nest = start + NLMSG_ALIGN(start->rta_len);
+
+ start->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)start;
+ addattr_nest_end(n, nest);
+ return n->nlmsg_len;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if ((int)(RTA_ALIGN(rta->rta_len) + len) > maxlen) {
+ fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+ const void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if ((int)(RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len)) > maxlen) {
+ fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len)) {
+ if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return 0;
+}
+
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ int i = 0;
+
+ memset(tb, 0, sizeof(struct rtattr *) * max);
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max && i < max)
+ tb[i++] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return i;
+}
+
+int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len)
+{
+ if ((int)RTA_PAYLOAD(rta) < len)
+ return -1;
+ if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) {
+ rta = (struct rtattr *)(uint8_t *)RTA_DATA(rta)+RTA_ALIGN(len);
+ return parse_rtattr_nested(tb, max, rta);
+ }
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ return 0;
+}
+
+#endif /* __linux__ */
--- /dev/null
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * 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.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#ifdef __linux__
+
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_link.h>
+#include <linux/if_addr.h>
+#include <linux/neighbour.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ __u32 seq;
+ __u32 dump;
+};
+
+extern int rcvbuf;
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
+ struct nlmsghdr *n, void *);
+
+struct rtnl_dump_filter_arg
+{
+ rtnl_filter_t filter;
+ void *arg1;
+ rtnl_filter_t junk;
+ void *arg2;
+};
+
+extern int rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg);
+extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2);
+
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk,
+ void *jarg);
+extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int);
+extern int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int);
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
+extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
+extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type);
+extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest);
+extern struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, const void *data, int len);
+extern int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+#define parse_rtattr_nested(tb, max, rta) \
+ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+#define parse_rtattr_nested_compat(tb, max, rta, data, len) \
+({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
+ __parse_rtattr_nested_compat(tb, max, rta, len); })
+
+extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
+ void *jarg);
+extern int rtnl_from_file(FILE *, rtnl_filter_t handler,
+ void *jarg);
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((uint8_t *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#ifndef IFA_RTA
+#define IFA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+#ifndef IFA_PAYLOAD
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+#endif
+
+#ifndef IFLA_RTA
+#define IFLA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+#ifndef IFLA_PAYLOAD
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+#endif
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+#ifndef NDA_PAYLOAD
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#ifndef NDTA_RTA
+#define NDTA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+#endif
+#ifndef NDTA_PAYLOAD
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
+#endif
+
+#endif /* __LIBNETLINK_H__ */
+
+#endif /* __linux__ */
--- /dev/null
+/*
+ * Multicast Traceroute for FRRouting
+ * Copyright (C) 2018 Mladen Sablic
+ *
+ * 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
+ */
+
+#ifdef __linux__
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "mtracebis_netlink.h"
+#include "mtracebis_routeget.h"
+
+static int find_dst(struct nlmsghdr *n, struct in_addr *src, struct in_addr *gw)
+{
+ struct rtmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr *tb[RTA_MAX + 1];
+
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0) {
+ fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+ return -1;
+ }
+
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+ if (tb[RTA_PREFSRC])
+ src->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_PREFSRC]);
+ if (tb[RTA_GATEWAY])
+ gw->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_GATEWAY]);
+ if (tb[RTA_OIF])
+ return *(int *)RTA_DATA(tb[RTA_OIF]);
+ return 0;
+}
+
+int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw)
+{
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ int ret;
+ struct rtnl_handle rth = {.fd = -1};
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETROUTE;
+ req.r.rtm_family = AF_INET;
+ req.r.rtm_table = 0;
+ req.r.rtm_protocol = 0;
+ req.r.rtm_scope = 0;
+ req.r.rtm_type = 0;
+ req.r.rtm_src_len = 0;
+ req.r.rtm_dst_len = 0;
+ req.r.rtm_tos = 0;
+
+ addattr_l(&req.n, sizeof(req), RTA_DST, &dst.s_addr, 4);
+ req.r.rtm_dst_len = 32;
+
+ ret = rtnl_open(&rth, 0);
+
+ if (ret < 0)
+ return ret;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+ ret = -1;
+ goto close_rth;
+ }
+
+ ret = find_dst(&req.n, src, gw);
+close_rth:
+ rtnl_close(&rth);
+ return ret;
+}
+
+#endif /* __linux__ */
--- /dev/null
+/*
+ * Multicast Traceroute for FRRouting
+ * Copyright (C) 2018 Mladen Sablic
+ *
+ * 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
+ */
+
+#ifdef __linux__
+
+#ifndef ROUTEGET_H
+#define ROUTEGET_H
+
+#include <netinet/in.h>
+
+int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw);
+
+#endif /* ROUTEGET */
+
+#endif /* __linux__ */
#include "pim_bfd.h"
#include "bfd.h"
-static struct cmd_node pim_global_node = {
- PIM_NODE, "", 1 /* vtysh ? yes */
-};
-
static struct cmd_node interface_node = {
INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */
};
ALIAS(no_debug_msdp_packets, undebug_msdp_packets_cmd, "undebug msdp packets",
UNDEBUG_STR DEBUG_MSDP_STR DEBUG_MSDP_PACKETS_STR)
+DEFUN (debug_mtrace,
+ debug_mtrace_cmd,
+ "debug mtrace",
+ DEBUG_STR
+ DEBUG_MTRACE_STR)
+{
+ PIM_DO_DEBUG_MTRACE;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_mtrace,
+ no_debug_mtrace_cmd,
+ "no debug mtrace",
+ NO_STR
+ DEBUG_STR
+ DEBUG_MTRACE_STR)
+{
+ PIM_DONT_DEBUG_MTRACE;
+ return CMD_SUCCESS;
+}
+
DEFUN_NOSH (show_debugging_pim,
show_debugging_pim_cmd,
"show debugging [pim]",
{
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,
void pim_cmd_init(void)
{
- install_node(&pim_global_node, pim_global_config_write); /* PIM_NODE */
install_node(&interface_node,
pim_interface_config_write); /* INTERFACE_NODE */
if_cmd_init();
install_element(ENABLE_NODE, &debug_msdp_packets_cmd);
install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd);
install_element(ENABLE_NODE, &undebug_msdp_packets_cmd);
+ install_element(ENABLE_NODE, &debug_mtrace_cmd);
+ install_element(ENABLE_NODE, &no_debug_mtrace_cmd);
install_element(CONFIG_NODE, &debug_igmp_cmd);
install_element(CONFIG_NODE, &no_debug_igmp_cmd);
install_element(CONFIG_NODE, &debug_msdp_packets_cmd);
install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd);
install_element(CONFIG_NODE, &undebug_msdp_packets_cmd);
+ install_element(CONFIG_NODE, &debug_mtrace_cmd);
+ install_element(CONFIG_NODE, &no_debug_mtrace_cmd);
install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd);
install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd);
#define DEBUG_MSDP_EVENTS_STR "MSDP protocol events\n"
#define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n"
#define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n"
+#define DEBUG_MTRACE_STR "Mtrace protocol activity\n"
void pim_cmd_init(void);
#include "pim_igmp.h"
#include "pim_igmpv2.h"
#include "pim_igmpv3.h"
+#include "pim_igmp_mtrace.h"
#include "pim_iface.h"
#include "pim_sock.h"
#include "pim_mroute.h"
case PIM_IGMP_V2_LEAVE_GROUP:
return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
igmp_msg, igmp_msg_len);
+
+ case PIM_IGMP_MTRACE_RESPONSE:
+ return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
+ from_str, igmp_msg,
+ igmp_msg_len);
+ break;
+ case PIM_IGMP_MTRACE_QUERY_REQUEST:
+ return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
+ from_str, igmp_msg,
+ igmp_msg_len);
}
zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
#define PIM_IGMP_V2_LEAVE_GROUP (0x17)
+#define PIM_IGMP_MTRACE_RESPONSE (0x1E)
+#define PIM_IGMP_MTRACE_QUERY_REQUEST (0x1F)
#define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22)
#define IGMP_V3_REPORT_HEADER_SIZE (8)
--- /dev/null
+/*
+ * Multicast traceroute for FRRouting
+ * Copyright (C) 2017 Mladen Sablic
+ *
+ * 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 "pimd.h"
+#include "pim_util.h"
+#include "pim_sock.h"
+#include "pim_rp.h"
+#include "pim_oil.h"
+#include "pim_ifchannel.h"
+#include "pim_macro.h"
+#include "pim_igmp_mtrace.h"
+
+static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp)
+{
+ mtrace_rspp->arrival = 0;
+ mtrace_rspp->incoming.s_addr = 0;
+ mtrace_rspp->outgoing.s_addr = 0;
+ mtrace_rspp->prev_hop.s_addr = 0;
+ mtrace_rspp->in_count = MTRACE_UNKNOWN_COUNT;
+ mtrace_rspp->out_count = MTRACE_UNKNOWN_COUNT;
+ mtrace_rspp->total = MTRACE_UNKNOWN_COUNT;
+ mtrace_rspp->rtg_proto = 0;
+ mtrace_rspp->fwd_ttl = 0;
+ mtrace_rspp->mbz = 0;
+ mtrace_rspp->s = 0;
+ mtrace_rspp->src_mask = 0;
+ mtrace_rspp->fwd_code = MTRACE_FWD_CODE_NO_ERROR;
+}
+
+static void mtrace_rsp_debug(uint32_t qry_id, int rsp,
+ struct igmp_mtrace_rsp *mrspp)
+{
+ char inc_str[INET_ADDRSTRLEN];
+ char out_str[INET_ADDRSTRLEN];
+ char prv_str[INET_ADDRSTRLEN];
+
+ zlog_debug(
+ "Rx mt(%d) qid=%ud arr=%x in=%s out=%s prev=%s proto=%d fwd=%d",
+ rsp, ntohl(qry_id), mrspp->arrival,
+ inet_ntop(AF_INET, &(mrspp->incoming), inc_str,
+ sizeof(inc_str)),
+ inet_ntop(AF_INET, &(mrspp->outgoing), out_str,
+ sizeof(out_str)),
+ inet_ntop(AF_INET, &(mrspp->prev_hop), prv_str,
+ sizeof(prv_str)),
+ mrspp->rtg_proto, mrspp->fwd_code);
+}
+
+static void mtrace_debug(struct pim_interface *pim_ifp,
+ struct igmp_mtrace *mtracep, int mtrace_len)
+{
+ char inc_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ char src_str[INET_ADDRSTRLEN];
+ char dst_str[INET_ADDRSTRLEN];
+ char rsp_str[INET_ADDRSTRLEN];
+
+ zlog_debug(
+ "Rx mtrace packet incoming on %s: "
+ "hops=%d type=%d size=%d, grp=%s, src=%s,"
+ " dst=%s rsp=%s ttl=%d qid=%ud",
+ inet_ntop(AF_INET, &(pim_ifp->primary_address), inc_str,
+ sizeof(inc_str)),
+ mtracep->hops, mtracep->type, mtrace_len,
+ inet_ntop(AF_INET, &(mtracep->grp_addr), grp_str,
+ sizeof(grp_str)),
+ inet_ntop(AF_INET, &(mtracep->src_addr), src_str,
+ sizeof(src_str)),
+ inet_ntop(AF_INET, &(mtracep->dst_addr), dst_str,
+ sizeof(dst_str)),
+ inet_ntop(AF_INET, &(mtracep->rsp_addr), rsp_str,
+ sizeof(rsp_str)),
+ mtracep->rsp_ttl, ntohl(mtracep->qry_id));
+ if (mtrace_len > (int)sizeof(struct igmp_mtrace)) {
+
+ int i;
+
+ int responses = mtrace_len - sizeof(struct igmp_mtrace);
+
+ if ((responses % sizeof(struct igmp_mtrace_rsp)) != 0)
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug(
+ "Mtrace response block of wrong"
+ " length");
+
+ responses = responses / sizeof(struct igmp_mtrace_rsp);
+
+ for (i = 0; i < responses; i++)
+ mtrace_rsp_debug(mtracep->qry_id, i, &mtracep->rsp[i]);
+ }
+}
+
+/* 5.1 Query Arrival Time */
+static uint32_t query_arrival_time(void)
+{
+ struct timeval tv;
+ uint32_t qat;
+
+ char m_qat[] = "Query arrival time lookup failed: errno=%d: %s";
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(m_qat, errno, safe_strerror(errno));
+ return 0;
+ }
+ /* not sure second offset correct, as I get different value */
+ qat = ((tv.tv_sec + 32384) << 16) + ((tv.tv_usec << 10) / 15625);
+
+ return qat;
+}
+
+static int mtrace_send_packet(struct interface *ifp,
+ struct igmp_mtrace *mtracep,
+ size_t mtrace_buf_len, struct in_addr dst_addr,
+ struct in_addr group_addr)
+{
+ struct sockaddr_in to;
+ struct pim_interface *pim_ifp;
+ socklen_t tolen;
+ ssize_t sent;
+ int ret;
+ int fd;
+ char pim_str[INET_ADDRSTRLEN];
+ char rsp_str[INET_ADDRSTRLEN];
+ u_char ttl;
+
+ pim_ifp = ifp->info;
+
+ memset(&to, 0, sizeof(to));
+ to.sin_family = AF_INET;
+ to.sin_addr = dst_addr;
+ tolen = sizeof(to);
+
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("Sending mtrace packet to %s on %s",
+ inet_ntop(AF_INET, &mtracep->rsp_addr, rsp_str,
+ sizeof(rsp_str)),
+ inet_ntop(AF_INET, &pim_ifp->primary_address,
+ pim_str, sizeof(pim_str)));
+
+ fd = pim_socket_raw(IPPROTO_IGMP);
+
+ if (fd < 0)
+ return -1;
+
+ ret = pim_socket_bind(fd, ifp);
+
+ if (ret < 0) {
+ ret = -1;
+ goto close_fd;
+ }
+
+ if (IPV4_CLASS_DE(ntohl(dst_addr.s_addr))) {
+ if (IPV4_MC_LINKLOCAL(ntohl(dst_addr.s_addr))) {
+ ttl = 1;
+ } else {
+ if (mtracep->type == PIM_IGMP_MTRACE_RESPONSE)
+ ttl = mtracep->rsp_ttl;
+ else
+ ttl = 64;
+ }
+ ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
+ sizeof(ttl));
+
+ if (ret < 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn("Failed to set socket multicast TTL");
+ ret = -1;
+ goto close_fd;
+ }
+ }
+
+ sent = sendto(fd, (char *)mtracep, mtrace_buf_len, MSG_DONTWAIT,
+ (struct sockaddr *)&to, tolen);
+
+ if (sent != (ssize_t)mtrace_buf_len) {
+ char dst_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
+
+ pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+ pim_inet4_dump("<group?>", group_addr, group_str,
+ sizeof(group_str));
+ if (sent < 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Send mtrace request failed for %s on"
+ "%s: group=%s msg_size=%zd: errno=%d: "
+ " %s",
+ dst_str, ifp->name, group_str,
+ mtrace_buf_len, errno,
+ safe_strerror(errno));
+ } else {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Send mtrace request failed for %s on"
+ " %s: group=%s msg_size=%zd: sent=%zd",
+ dst_str, ifp->name, group_str,
+ mtrace_buf_len, sent);
+ }
+ ret = -1;
+ goto close_fd;
+ }
+ ret = 0;
+close_fd:
+ close(fd);
+ return ret;
+}
+
+static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr,
+ struct interface *interface)
+{
+ struct pim_nexthop nexthop;
+ struct sockaddr_in to;
+ struct interface *if_out;
+ socklen_t tolen;
+ int ret;
+ int fd;
+ int sent;
+ uint16_t checksum;
+
+ checksum = ip_hdr->ip_sum;
+
+ ip_hdr->ip_sum = 0;
+
+ if (checksum != in_cksum(ip_hdr, ip_hdr->ip_hl * 4))
+ return -1;
+
+ if (ip_hdr->ip_ttl-- <= 1)
+ return -1;
+
+ ip_hdr->ip_sum = in_cksum(ip_hdr, ip_hdr->ip_hl * 4);
+
+ fd = pim_socket_raw(IPPROTO_RAW);
+
+ if (fd < 0)
+ return -1;
+
+ pim_socket_ip_hdr(fd);
+
+ if (interface == NULL) {
+ ret = pim_nexthop_lookup(pim, &nexthop, ip_hdr->ip_dst, 0);
+
+ if (ret != 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Dropping mtrace packet, "
+ "no route to destination");
+ return -1;
+ }
+
+ if_out = nexthop.interface;
+ } else {
+ if_out = interface;
+ }
+
+ ret = pim_socket_bind(fd, if_out);
+
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+
+ memset(&to, 0, sizeof(to));
+ to.sin_family = AF_INET;
+ to.sin_addr = ip_hdr->ip_dst;
+ tolen = sizeof(to);
+
+ sent = sendto(fd, ip_hdr, ntohs(ip_hdr->ip_len), 0,
+ (struct sockaddr *)&to, tolen);
+
+ close(fd);
+
+ if (sent < 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Failed to forward mtrace packet:"
+ " sendto errno=%d, %s",
+ errno, safe_strerror(errno));
+ return -1;
+ }
+
+ if (PIM_DEBUG_MTRACE) {
+ zlog_debug("Fwd mtrace packet len=%u to %s ttl=%u",
+ ntohs(ip_hdr->ip_len), inet_ntoa(ip_hdr->ip_dst),
+ ip_hdr->ip_ttl);
+ }
+
+ return 0;
+}
+
+static int mtrace_mc_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
+{
+ struct prefix_sg sg;
+ struct channel_oil *c_oil;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct pim_ifchannel *ch = NULL;
+ int ret = -1;
+
+ memset(&sg, 0, sizeof(struct prefix_sg));
+ sg.grp = ip_hdr->ip_dst;
+
+ c_oil = pim_find_channel_oil(pim, &sg);
+
+ if (c_oil == NULL) {
+ if (PIM_DEBUG_MTRACE) {
+ zlog_debug(
+ "Dropping mtrace multicast packet "
+ "len=%u to %s ttl=%u",
+ ntohs(ip_hdr->ip_len),
+ inet_ntoa(ip_hdr->ip_dst), ip_hdr->ip_ttl);
+ }
+ return -1;
+ }
+ if (c_oil->up == NULL)
+ return -1;
+ if (c_oil->up->ifchannels == NULL)
+ return -1;
+ for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
+ if (pim_macro_chisin_oiflist(ch)) {
+ int r;
+
+ r = mtrace_un_forward_packet(pim, ip_hdr,
+ ch->interface);
+ if (r == 0)
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+
+static int mtrace_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
+{
+ if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
+ return mtrace_mc_forward_packet(pim, ip_hdr);
+ else
+ return mtrace_un_forward_packet(pim, ip_hdr, NULL);
+}
+
+/* 6.5 Sending Traceroute Responses */
+static int mtrace_send_mc_response(struct pim_instance *pim,
+ struct igmp_mtrace *mtracep,
+ size_t mtrace_len)
+{
+ struct prefix_sg sg;
+ struct channel_oil *c_oil;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct pim_ifchannel *ch = NULL;
+ int ret = -1;
+
+ memset(&sg, 0, sizeof(struct prefix_sg));
+ sg.grp = mtracep->rsp_addr;
+
+ c_oil = pim_find_channel_oil(pim, &sg);
+
+ if (c_oil == NULL) {
+ if (PIM_DEBUG_MTRACE) {
+ zlog_debug(
+ "Dropping mtrace multicast response packet "
+ "len=%u to %s",
+ (unsigned int)mtrace_len,
+ inet_ntoa(mtracep->rsp_addr));
+ }
+ return -1;
+ }
+ if (c_oil->up == NULL)
+ return -1;
+ if (c_oil->up->ifchannels == NULL)
+ return -1;
+ for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
+ if (pim_macro_chisin_oiflist(ch)) {
+ int r;
+
+ r = mtrace_send_packet(ch->interface, mtracep,
+ mtrace_len, mtracep->rsp_addr,
+ mtracep->grp_addr);
+ if (r == 0)
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int mtrace_send_response(struct pim_instance *pim,
+ struct igmp_mtrace *mtracep, size_t mtrace_len)
+{
+ struct pim_nexthop nexthop;
+ int ret;
+
+ mtracep->type = PIM_IGMP_MTRACE_RESPONSE;
+
+ mtracep->checksum = 0;
+ mtracep->checksum = in_cksum((char *)mtracep, mtrace_len);
+
+ if (IPV4_CLASS_DE(ntohl(mtracep->rsp_addr.s_addr))) {
+ struct pim_rpf *p_rpf;
+ char grp_str[INET_ADDRSTRLEN];
+
+ if (pim_rp_i_am_rp(pim, mtracep->rsp_addr))
+ return mtrace_send_mc_response(pim, mtracep,
+ mtrace_len);
+
+ p_rpf = pim_rp_g(pim, mtracep->rsp_addr);
+
+ if (p_rpf == NULL) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn("mtrace no RP for %s",
+ inet_ntop(AF_INET,
+ &(mtracep->rsp_addr),
+ grp_str, sizeof(grp_str)));
+ return -1;
+ }
+ nexthop = p_rpf->source_nexthop;
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("mtrace response to RP");
+ } else {
+ /* TODO: should use unicast rib lookup */
+ ret = pim_nexthop_lookup(pim, &nexthop, mtracep->rsp_addr, 1);
+
+ if (ret != 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Dropped response qid=%ud, no route to "
+ "response address",
+ mtracep->qry_id);
+ return -1;
+ }
+ }
+
+ return mtrace_send_packet(nexthop.interface, mtracep, mtrace_len,
+ mtracep->rsp_addr, mtracep->grp_addr);
+}
+
+int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ static uint32_t qry_id, qry_src;
+ char mtrace_buf[MTRACE_HDR_SIZE + MTRACE_MAX_HOPS * MTRACE_RSP_SIZE];
+ struct pim_nexthop nexthop;
+ struct interface *ifp;
+ struct interface *out_ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_interface *pim_out_ifp;
+ struct pim_instance *pim;
+ struct igmp_mtrace *mtracep;
+ struct igmp_mtrace_rsp *rspp;
+ struct in_addr nh_addr;
+ enum mtrace_fwd_code fwd_code = MTRACE_FWD_CODE_NO_ERROR;
+ int ret;
+ size_t r_len;
+ int last_rsp_ind = 0;
+ size_t mtrace_len;
+ uint16_t recv_checksum;
+ uint16_t checksum;
+
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+ pim = pim_ifp->pim;
+
+ /*
+ * 6. Router Behaviour
+ * Check if mtrace packet is addressed elsewhere and forward,
+ * if applicable
+ */
+ if (!IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
+ if (!if_lookup_exact_address(&ip_hdr->ip_dst, AF_INET,
+ pim->vrf_id))
+ return mtrace_forward_packet(pim, ip_hdr);
+
+ if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Recv mtrace packet from %s on %s: too short,"
+ " len=%d, min=%lu",
+ from_str, ifp->name, igmp_msg_len,
+ sizeof(struct igmp_mtrace));
+ return -1;
+ }
+
+ mtracep = (struct igmp_mtrace *)igmp_msg;
+
+ recv_checksum = mtracep->checksum;
+
+ mtracep->checksum = 0;
+
+ checksum = in_cksum(igmp_msg, igmp_msg_len);
+
+ if (recv_checksum != checksum) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Recv mtrace packet from %s on %s: checksum"
+ " mismatch: received=%x computed=%x",
+ from_str, ifp->name, recv_checksum, checksum);
+ return -1;
+ }
+
+ if (PIM_DEBUG_MTRACE)
+ mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
+
+ /* subtract header from message length */
+ r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
+
+ /* Classify mtrace packet, check if it is a query */
+ if (!r_len) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("Received IGMP multicast traceroute query");
+
+ /* 6.1.1 Packet verification */
+ if (!pim_if_connected_to_source(ifp, mtracep->dst_addr)) {
+ if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug(
+ "Dropping multicast query "
+ "on wrong interface");
+ return -1;
+ }
+ /* Unicast query on wrong interface */
+ fwd_code = MTRACE_FWD_CODE_WRONG_IF;
+ }
+ if (qry_id == mtracep->qry_id && qry_src == from.s_addr) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug(
+ "Dropping multicast query with "
+ "duplicate source and id");
+ return -1;
+ }
+ qry_id = mtracep->qry_id;
+ qry_src = from.s_addr;
+ }
+ /* if response fields length is equal to a whole number of responses */
+ else if ((r_len % sizeof(struct igmp_mtrace_rsp)) == 0) {
+ r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
+
+ if (r_len != 0)
+ last_rsp_ind = r_len / sizeof(struct igmp_mtrace_rsp);
+ if (last_rsp_ind > MTRACE_MAX_HOPS) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn("Mtrace request of excessive size");
+ return -1;
+ }
+ } else {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Recv mtrace packet from %s on %s: "
+ "invalid length %d",
+ from_str, ifp->name, igmp_msg_len);
+ return -1;
+ }
+
+ /* 6.2.1 Packet Verification - drop not link-local multicast */
+ if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))
+ && !IPV4_MC_LINKLOCAL(ntohl(ip_hdr->ip_dst.s_addr))) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Recv mtrace packet from %s on %s:"
+ " not link-local multicast %s",
+ from_str, ifp->name, inet_ntoa(ip_hdr->ip_dst));
+ return -1;
+ }
+
+ /* 6.2.2. Normal Processing */
+
+ /* 6.2.2. 1. */
+
+ if (last_rsp_ind == MTRACE_MAX_HOPS) {
+ mtracep->rsp[MTRACE_MAX_HOPS - 1].fwd_code =
+ MTRACE_FWD_CODE_NO_SPACE;
+ return mtrace_send_response(pim_ifp->pim, mtracep,
+ igmp_msg_len);
+ }
+
+ /* calculate new mtrace mtrace lenght with extra response */
+ mtrace_len = igmp_msg_len + sizeof(struct igmp_mtrace_rsp);
+
+ /* copy received query/request */
+ memcpy(mtrace_buf, igmp_msg, igmp_msg_len);
+
+ /* repoint mtracep pointer to copy */
+ mtracep = (struct igmp_mtrace *)mtrace_buf;
+
+ /* pointer for extra response field to be filled in */
+ rspp = &mtracep->rsp[last_rsp_ind];
+
+ /* initialize extra response field */
+ mtrace_rsp_init(rspp);
+
+ rspp->arrival = htonl(query_arrival_time());
+ rspp->outgoing = pim_ifp->primary_address;
+ rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT);
+
+ /* 6.2.2. 2. Attempt to determine forwarding information */
+
+ nh_addr.s_addr = 0;
+
+ ret = pim_nexthop_lookup(pim, &nexthop, mtracep->src_addr, 1);
+
+ if (ret == 0) {
+ char nexthop_str[INET_ADDRSTRLEN];
+
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("mtrace pim_nexthop_lookup OK");
+
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn("mtrace next_hop=%s",
+ inet_ntop(nexthop.mrib_nexthop_addr.family,
+ &nexthop.mrib_nexthop_addr.u.prefix,
+ nexthop_str, sizeof(nexthop_str)));
+
+ if (nexthop.mrib_nexthop_addr.family == AF_INET)
+ nh_addr = nexthop.mrib_nexthop_addr.u.prefix4;
+ }
+ /* 6.4 Forwarding Traceroute Requests: ... Otherwise, ... */
+ else {
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("mtrace not found neighbor");
+ if (!fwd_code)
+ rspp->fwd_code = MTRACE_FWD_CODE_NO_ROUTE;
+ else
+ rspp->fwd_code = fwd_code;
+ /* 6.5 Sending Traceroute Responses */
+ return mtrace_send_response(pim, mtracep, mtrace_len);
+ }
+
+ out_ifp = nexthop.interface;
+ pim_out_ifp = out_ifp->info;
+
+ rspp->incoming = pim_out_ifp->primary_address;
+ rspp->prev_hop = nh_addr;
+ rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
+ rspp->total = htonl(MTRACE_UNKNOWN_COUNT);
+ rspp->rtg_proto = MTRACE_RTG_PROTO_PIM;
+ rspp->s = 1;
+ rspp->src_mask = 32;
+
+ if (nh_addr.s_addr == 0) {
+ /* reached source? */
+ if (pim_if_connected_to_source(out_ifp, mtracep->src_addr))
+ return mtrace_send_response(pim, mtracep, mtrace_len);
+ /*
+ * 6.4 Forwarding Traceroute Requests:
+ * Previous-hop router not known
+ */
+ inet_aton(MCAST_ALL_ROUTERS, &nh_addr);
+ }
+
+ if (mtracep->hops <= (last_rsp_ind + 1))
+ return mtrace_send_response(pim, mtracep, mtrace_len);
+
+ mtracep->checksum = 0;
+
+ mtracep->checksum = in_cksum(mtrace_buf, mtrace_len);
+
+ return mtrace_send_packet(out_ifp, mtracep, mtrace_len, nh_addr,
+ mtracep->grp_addr);
+}
+
+int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ static uint32_t qry_id, rsp_dst;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_instance *pim;
+ struct igmp_mtrace *mtracep;
+ uint16_t recv_checksum;
+ uint16_t checksum;
+
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+ pim = pim_ifp->pim;
+
+ mtracep = (struct igmp_mtrace *)igmp_msg;
+
+ recv_checksum = mtracep->checksum;
+
+ mtracep->checksum = 0;
+
+ checksum = in_cksum(igmp_msg, igmp_msg_len);
+
+ if (recv_checksum != checksum) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Recv mtrace response from %s on %s: checksum"
+ " mismatch: received=%x computed=%x",
+ from_str, ifp->name, recv_checksum, checksum);
+ return -1;
+ }
+
+ mtracep->checksum = checksum;
+
+ if (PIM_DEBUG_MTRACE)
+ mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
+
+ /* Drop duplicate packets */
+ if (qry_id == mtracep->qry_id && rsp_dst == ip_hdr->ip_dst.s_addr) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("duplicate mtrace response packet dropped");
+ return -1;
+ }
+
+ qry_id = mtracep->qry_id;
+ rsp_dst = ip_hdr->ip_dst.s_addr;
+
+ return mtrace_forward_packet(pim, ip_hdr);
+}
--- /dev/null
+/*
+ * Multicast traceroute for FRRouting
+ * Copyright (C) 2017 Mladen Sablic
+ *
+ * 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 PIM_IGMP_MTRACE_H
+#define PIM_IGMP_MTRACE_H
+
+#include <zebra.h>
+
+#include "pim_igmp.h"
+
+#define MTRACE_MAX_HOPS (255)
+#define MTRACE_UNKNOWN_COUNT (0xffffffff)
+
+enum mtrace_fwd_code {
+ MTRACE_FWD_CODE_NO_ERROR = 0x00,
+ MTRACE_FWD_CODE_WRONG_IF = 0x01,
+ MTRACE_FWD_CODE_PRUNE_SENT = 0x02,
+ MTRACE_FWD_CODE_PRUNE_RCVD = 0x03,
+ MTRACE_FWD_CODE_SCOPED = 0x04,
+ MTRACE_FWD_CODE_NO_ROUTE = 0x05,
+ MTRACE_FWD_CODE_WRONG_LAST_HOP = 0x06,
+ MTRACE_FWD_CODE_NOT_FORWARDING = 0x07,
+ MTRACE_FWD_CODE_REACHED_RP = 0x08,
+ MTRACE_FWD_CODE_RPF_IF = 0x09,
+ MTRACE_FWD_CODE_NO_MULTICAST = 0x0A,
+ MTRACE_FWD_CODE_INFO_HIDDEN = 0x0B,
+ MTRACE_FWD_CODE_NO_SPACE = 0x81,
+ MTRACE_FWD_CODE_OLD_ROUTER = 0x82,
+ MTRACE_FWD_CODE_ADMIN_PROHIB = 0x83
+};
+
+enum mtrace_rtg_proto {
+ MTRACE_RTG_PROTO_DVMRP = 1,
+ MTRACE_RTG_PROTO_MOSPF = 2,
+ MTRACE_RTG_PROTO_PIM = 3,
+ MTRACE_RTG_PROTO_CBT = 4,
+ MTRACE_RTG_PROTO_PIM_SPECIAL = 5,
+ MTRACE_RTG_PROTO_PIM_STATIC = 6,
+ MTRACE_RTG_PROTO_DVMRP_STATIC = 7,
+ MTRACE_RTG_PROTO_PIM_MBGP = 8,
+ MTRACE_RTG_PROTO_CBT_SPECIAL = 9,
+ MTRACE_RTG_PROTO_CBT_STATIC = 10,
+ MTRACE_RTG_PROTO_PIM_ASSERT = 11,
+};
+
+struct igmp_mtrace_rsp {
+ uint32_t arrival;
+ struct in_addr incoming;
+ struct in_addr outgoing;
+ struct in_addr prev_hop;
+ uint32_t in_count;
+ uint32_t out_count;
+ uint32_t total;
+ uint32_t rtg_proto : 8;
+ uint32_t fwd_ttl : 8;
+ /* little endian order for next three fields */
+ uint32_t src_mask : 6;
+ uint32_t s : 1;
+ uint32_t mbz : 1;
+ uint32_t fwd_code : 8;
+} __attribute__((packed));
+
+struct igmp_mtrace {
+ uint8_t type;
+ uint8_t hops;
+ uint16_t checksum;
+ struct in_addr grp_addr;
+ struct in_addr src_addr;
+ struct in_addr dst_addr;
+ struct in_addr rsp_addr;
+ uint32_t rsp_ttl : 8;
+ uint32_t qry_id : 24;
+ struct igmp_mtrace_rsp rsp[0];
+} __attribute__((packed));
+
+#define MTRACE_HDR_SIZE (sizeof(struct igmp_mtrace))
+#define MTRACE_RSP_SIZE (sizeof(struct igmp_mtrace_rsp))
+
+int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len);
+
+int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len);
+
+#endif /* PIM_IGMP_MTRACE_H */
{
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);
if (!pim)
continue;
- if (vrf->vrf_id == VRF_DEFAULT)
- continue;
+ if (vrf->vrf_id != VRF_DEFAULT)
+ vty_frame(vty, "vrf %s\n", vrf->name);
- vty_frame(vty, "vrf %s\n", vrf->name);
pim_global_config_write_worker(pim, vty);
- vty_endframe(vty, "!\n");
+
+ if (vrf->vrf_id != VRF_DEFAULT)
+ vty_endframe(vty, "!\n");
}
return 0;
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));
int pim_parse_nexthop_update(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
- struct stream *s;
- struct prefix p;
struct nexthop *nexthop;
struct nexthop *nhlist_head = NULL;
struct nexthop *nhlist_tail = NULL;
- uint32_t metric, distance;
- u_char nexthop_num = 0;
int i;
struct pim_rpf rpf;
struct pim_nexthop_cache *pnc = NULL;
struct interface *ifp1 = NULL;
struct vrf *vrf = vrf_lookup_by_id(vrf_id);
struct pim_instance *pim;
+ struct zapi_route nhr;
if (!vrf)
return 0;
pim = vrf->info;
- s = zclient->ibuf;
- memset(&p, 0, sizeof(struct prefix));
- p.family = stream_getw(s);
- p.prefixlen = stream_getc(s);
- switch (p.family) {
- case AF_INET:
- p.u.prefix4.s_addr = stream_get_ipv4(s);
- break;
- case AF_INET6:
- stream_get(&p.u.prefix6, s, 16);
- break;
- default:
- break;
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) {
+ if (PIM_DEBUG_PIM_NHT)
+ zlog_debug("%s: Decode of nexthop update from zebra failed",
+ __PRETTY_FUNCTION__);
+ return 0;
}
if (command == ZEBRA_NEXTHOP_UPDATE) {
- rpf.rpf_addr.family = p.family;
- rpf.rpf_addr.prefixlen = p.prefixlen;
- rpf.rpf_addr.u.prefix4.s_addr = p.u.prefix4.s_addr;
+ prefix_copy(&rpf.rpf_addr, &nhr.prefix);
pnc = pim_nexthop_cache_find(pim, &rpf);
if (!pnc) {
if (PIM_DEBUG_PIM_NHT) {
}
pnc->last_update = pim_time_monotonic_usec();
- distance = stream_getc(s);
- metric = stream_getl(s);
- nexthop_num = stream_getc(s);
- if (nexthop_num) {
+ if (nhr.nexthop_num) {
pnc->nexthop_num = 0; // Only increment for pim enabled rpf.
- for (i = 0; i < nexthop_num; i++) {
- nexthop = nexthop_new();
- nexthop->type = stream_getc(s);
+ for (i = 0; i < nhr.nexthop_num; i++) {
+ nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]);
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
- nexthop->gate.ipv4.s_addr = stream_get_ipv4(s);
- nexthop->ifindex = stream_getl(s);
- break;
case NEXTHOP_TYPE_IFINDEX:
- nexthop->ifindex = stream_getl(s);
- break;
case NEXTHOP_TYPE_IPV4_IFINDEX:
- nexthop->gate.ipv4.s_addr = stream_get_ipv4(s);
- nexthop->ifindex = stream_getl(s);
- break;
case NEXTHOP_TYPE_IPV6:
- stream_get(&nexthop->gate.ipv6, s, 16);
+ case NEXTHOP_TYPE_BLACKHOLE:
break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
- stream_get(&nexthop->gate.ipv6, s, 16);
- nexthop->ifindex = stream_getl(s);
ifp1 = if_lookup_by_index(nexthop->ifindex,
pim->vrf_id);
nbr = pim_neighbor_find_if(ifp1);
PIM_NET_INADDR_ANY;
}
- break;
- default:
- /* do nothing */
break;
}
if (PIM_DEBUG_PIM_NHT) {
char p_str[PREFIX2STR_BUFFER];
- prefix2str(&p, p_str, sizeof(p_str));
+
+ prefix2str(&nhr.prefix, p_str, sizeof(p_str));
zlog_debug(
"%s: NHT addr %s(%s) %d-nhop via %s(%s) type %d distance:%u metric:%u ",
__PRETTY_FUNCTION__, p_str,
pim->vrf->name, i + 1,
inet_ntoa(nexthop->gate.ipv4),
- ifp->name, nexthop->type, distance,
- metric);
+ ifp->name, nexthop->type, nhr.distance,
+ nhr.metric);
}
if (!ifp->info) {
if (PIM_DEBUG_PIM_NHT) {
char buf[NEXTHOP_STRLEN];
+
zlog_debug(
"%s: multicast not enabled on input interface %s(%s) (ifindex=%d, addr %s)",
__PRETTY_FUNCTION__, ifp->name,
pnc->nexthop = nhlist_head;
if (pnc->nexthop_num) {
pnc->flags |= PIM_NEXTHOP_VALID;
- pnc->distance = distance;
- pnc->metric = metric;
+ pnc->distance = nhr.distance;
+ pnc->metric = nhr.metric;
}
} else {
pnc->flags &= ~PIM_NEXTHOP_VALID;
- pnc->nexthop_num = nexthop_num;
+ pnc->nexthop_num = nhr.nexthop_num;
nexthops_free(pnc->nexthop);
pnc->nexthop = NULL;
}
if (PIM_DEBUG_PIM_NHT) {
char buf[PREFIX2STR_BUFFER];
- prefix2str(&p, buf, sizeof(buf));
+ prefix2str(&nhr.prefix, buf, sizeof(buf));
zlog_debug(
- "%s: NHT Update for %s(%s) num_nh %d num_pim_nh %d vrf:%d up %ld rp %d",
- __PRETTY_FUNCTION__, buf, pim->vrf->name, nexthop_num,
- pnc->nexthop_num, vrf_id, pnc->upstream_hash->count,
+ "%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,
+ nhr.nexthop_num, pnc->nexthop_num, vrf_id,
+ pnc->upstream_hash->count,
listcount(pnc->rp_list));
}
XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
}
-static struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
- struct prefix_sg *sg)
+struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
+ struct prefix_sg *sg)
{
struct channel_oil *c_oil = NULL;
struct channel_oil lookup;
void pim_oil_terminate(struct pim_instance *pim);
void pim_channel_oil_free(struct channel_oil *c_oil);
+struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
+ struct prefix_sg *sg);
struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
struct prefix_sg *sg,
int input_vif_index);
++writes;
}
+ if (PIM_DEBUG_MTRACE) {
+ vty_out(vty, "debug mtrace\n");
+ ++writes;
+ }
+
if (PIM_DEBUG_MROUTE_DETAIL) {
vty_out(vty, "debug mroute detail\n");
++writes;
return writes;
}
-int pim_global_config_write(struct vty *vty)
-{
- return pim_global_config_write_worker(pimg, vty);
-}
-
int pim_interface_config_write(struct vty *vty)
{
struct pim_instance *pim;
#include "vty.h"
int pim_debug_config_write(struct vty *vty);
-int pim_global_config_write(struct vty *vty);
int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty);
int pim_interface_config_write(struct vty *vty);
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)
#define PIM_MASK_PIM_NHT (1 << 22)
#define PIM_MASK_PIM_NHT_DETAIL (1 << 23)
#define PIM_MASK_PIM_NHT_RP (1 << 24)
+#define PIM_MASK_MTRACE (1 << 25)
/* Remember 32 bits!!! */
/* PIM error codes */
#define PIM_DEBUG_PIM_NHT (qpim_debugs & PIM_MASK_PIM_NHT)
#define PIM_DEBUG_PIM_NHT_DETAIL (qpim_debugs & PIM_MASK_PIM_NHT_DETAIL)
#define PIM_DEBUG_PIM_NHT_RP (qpim_debugs & PIM_MASK_PIM_NHT_RP)
+#define PIM_DEBUG_MTRACE (qpim_debugs & PIM_MASK_MTRACE)
#define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS | PIM_MASK_MSDP_EVENTS))
#define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS | PIM_MASK_MSDP_PACKETS))
#define PIM_DO_DEBUG_MSDP_INTERNAL (qpim_debugs |= PIM_MASK_MSDP_INTERNAL)
#define PIM_DO_DEBUG_PIM_NHT (qpim_debugs |= PIM_MASK_PIM_NHT)
#define PIM_DO_DEBUG_PIM_NHT_RP (qpim_debugs |= PIM_MASK_PIM_NHT_RP)
+#define PIM_DO_DEBUG_MTRACE (qpim_debugs |= PIM_MASK_MTRACE)
#define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
#define PIM_DONT_DEBUG_MSDP_INTERNAL (qpim_debugs &= ~PIM_MASK_MSDP_INTERNAL)
#define PIM_DONT_DEBUG_PIM_NHT (qpim_debugs &= ~PIM_MASK_PIM_NHT)
#define PIM_DONT_DEBUG_PIM_NHT_RP (qpim_debugs &= ~PIM_MASK_PIM_NHT_RP)
+#define PIM_DONT_DEBUG_MTRACE (qpim_debugs &= ~PIM_MASK_MTRACE)
void pim_init(void);
void pim_terminate(void);
if PIMD
noinst_LIBRARIES += pimd/libpim.a
sbin_PROGRAMS += pimd/pimd
+bin_PROGRAMS += pimd/mtracebis
noinst_PROGRAMS += pimd/test_igmpv3_join
dist_examples_DATA += pimd/pimd.conf.sample
endif
pimd/pim_iface.c \
pimd/pim_ifchannel.c \
pimd/pim_igmp.c \
+ pimd/pim_igmp_mtrace.c \
pimd/pim_igmpv2.c \
pimd/pim_igmpv3.c \
pimd/pim_instance.c \
pimd/pim_ifchannel.h \
pimd/pim_igmp.h \
pimd/pim_igmp_join.h \
+ pimd/pim_igmp_mtrace.h \
pimd/pim_igmpv2.h \
pimd/pim_igmpv3.h \
pimd/pim_instance.h \
pimd/pim_zebra.h \
pimd/pim_zlookup.h \
pimd/pimd.h \
+ pimd/mtracebis_netlink.h \
+ pimd/mtracebis_routeget.h \
# end
pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la @LIBCAP@
pimd_test_igmpv3_join_LDADD = lib/libfrr.la
pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c
+
+pimd_mtracebis_LDADD = lib/libfrr.la
+pimd_mtracebis_SOURCES = pimd/mtracebis.c \
+ pimd/mtracebis_netlink.c \
+ pimd/mtracebis_routeget.c \
+ # end
-$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*
if (count >= MULTIPATH_NUM)
break;
api_nh = &api.nexthops[count];
+ api_nh->vrf_id = VRF_DEFAULT;
api_nh->gate = rinfo->nh.gate;
api_nh->type = NEXTHOP_TYPE_IPV4;
if (cmd == ZEBRA_ROUTE_ADD)
tmp_rinfo))
if (tmp_rinfo->type == ZEBRA_ROUTE_RIP
&& tmp_rinfo->nh.ifindex
- == ifc->ifp->ifindex)
- rinfo->metric_out =
+ == ifc->ifp->ifindex)
+ tmp_rinfo->metric_out =
RIP_METRIC_INFINITY;
- if (tmp_rinfo->type == ZEBRA_ROUTE_CONNECT
+
+ if (rinfo->type == ZEBRA_ROUTE_CONNECT
&& prefix_match((struct prefix *)p,
ifc->address))
- rinfo->metric_out = RIP_METRIC_INFINITY;
+ rinfo->metric_out =
+ RIP_METRIC_INFINITY;
}
/* Prepare preamble, auth headers, if needs be */
!
! RIPd sample configuration file
!
-! $Id: ripd.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $
-!
hostname ripd
password zebra
!
if (count >= MULTIPATH_NUM)
break;
api_nh = &api.nexthops[count];
+ api_nh->vrf_id = VRF_DEFAULT;
api_nh->gate.ipv6 = rinfo->nexthop;
api_nh->ifindex = rinfo->ifindex;
api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
!
! 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
#include "prefix.h"
#include "nexthop.h"
#include "log.h"
+#include "vrf.h"
+#include "zclient.h"
#include "sharpd/sharp_zebra.h"
#include "sharpd/sharp_vty.h"
DEFPY (install_routes,
install_routes_cmd,
- "install routes A.B.C.D$start nexthop A.B.C.D$nexthop (1-1000000)$routes",
+ "sharp install routes A.B.C.D$start nexthop A.B.C.D$nexthop (1-1000000)$routes",
+ "Sharp routing Protocol\n"
"install some routes\n"
"Routes to install\n"
"Address to start /32 generation at\n"
return CMD_SUCCESS;
}
+DEFPY(vrf_label, vrf_label_cmd,
+ "sharp label <ip$ipv4|ipv6$ipv6> vrf NAME$name label (0-100000)$label",
+ "Sharp Routing Protocol\n"
+ "Give a vrf a label\n"
+ "Pop and forward for IPv4\n"
+ "Pop and forward for IPv6\n"
+ VRF_CMD_HELP_STR
+ "The label to use, 0 specifies remove the label installed from previous\n"
+ "Specified range to use\n")
+{
+ struct vrf *vrf;
+ afi_t afi = (ipv4) ? AFI_IP : AFI_IP6;
+
+ if (strcmp(name, "default") == 0)
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ else
+ vrf = vrf_lookup_by_name(name);
+
+ if (!vrf) {
+ vty_out(vty, "Unable to find vrf you silly head");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (label == 0)
+ label = MPLS_LABEL_NONE;
+
+ vrf_label_add(vrf->vrf_id, afi, label);
+ return CMD_SUCCESS;
+}
+
DEFPY (remove_routes,
remove_routes_cmd,
- "remove routes A.B.C.D$start (1-1000000)$routes",
+ "sharp remove routes A.B.C.D$start (1-1000000)$routes",
+ "Sharp Routing Protocol\n"
"Remove some routes\n"
"Routes to remove\n"
"Starting spot\n"
{
install_element(ENABLE_NODE, &install_routes_cmd);
install_element(ENABLE_NODE, &remove_routes_cmd);
+ install_element(ENABLE_NODE, &vrf_label_cmd);
return;
}
zclient_send_reg_requests(zclient, VRF_DEFAULT);
}
+void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label)
+{
+ zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP);
+}
+
void route_add(struct prefix *p, struct nexthop *nh)
{
struct zapi_route api;
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api_nh = &api.nexthops[0];
+ api_nh->vrf_id = VRF_DEFAULT;
api_nh->gate.ipv4 = nh->gate.ipv4;
api_nh->type = nh->type;
api_nh->ifindex = nh->ifindex;
extern void sharp_zebra_init(void);
+extern void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label);
extern void route_add(struct prefix *p, struct nexthop *nh);
extern void route_delete(struct prefix *p);
#endif
sharpd/sharp_vty.c \
# end
+noinst_HEADERS += \
+ sharpd/sharp_vty.h \
+ sharpd/sharp_zebra.h \
+ # end
+
sharpd/sharp_vty_clippy.c: $(CLIPPY_DEPS)
sharpd/sharp_vty.$(OBJEXT): sharpd/sharp_vty_clippy.c
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"
/* AFI/SAFI validation */
int validate_afi;
- afi_t afi;
- safi_t safi;
+ iana_afi_t afi;
+ iana_safi_t safi;
#define VALID_AFI 1
#define INVALID_AFI 0
int afi_valid;
SHOULD_PARSE,
0,
1,
- AFI_IP,
- SAFI_UNICAST,
+ IANA_AFI_IPV4,
+ IANA_SAFI_UNICAST,
VALID_AFI,
},
{
SHOULD_PARSE,
0,
1,
- AFI_IP6,
- SAFI_UNICAST,
+ IANA_AFI_IPV6,
+ IANA_SAFI_UNICAST,
VALID_AFI,
},
/* 5 */
SHOULD_PARSE,
0,
1,
- AFI_IP,
- SAFI_MULTICAST,
+ IANA_AFI_IPV4,
+ IANA_SAFI_MULTICAST,
VALID_AFI,
},
/* 6 */
SHOULD_PARSE,
0,
1,
- AFI_IP6,
+ IANA_AFI_IPV6,
IANA_SAFI_MPLS_VPN,
VALID_AFI,
},
SHOULD_PARSE,
0,
1,
- AFI_IP6,
+ IANA_AFI_IPV6,
IANA_SAFI_MPLS_VPN,
VALID_AFI,
},
SHOULD_PARSE,
0,
1,
- AFI_IP,
+ IANA_AFI_IPV4,
IANA_SAFI_MPLS_VPN,
VALID_AFI,
},
SHOULD_ERR,
0,
1,
- AFI_IP,
- SAFI_UNICAST,
+ IANA_AFI_IPV4,
+ IANA_SAFI_UNICAST,
VALID_AFI,
},
{NULL, NULL, {0}, 0, 0}};
safi_t safi;
/* Convert AFI, SAFI to internal values, check. */
- if (bgp_map_afi_safi_iana2int(afi_int2iana(t->afi), t->safi,
- &afi, &safi)) {
+ if (bgp_map_afi_safi_iana2int(t->afi, t->safi, &afi, &safi)) {
if (t->afi_valid == VALID_AFI)
failed++;
}
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));
#define SHOULD_PARSE 0
#define SHOULD_ERR -1
int parses; /* whether it should parse or not */
-
- /* AFI/SAFI validation */
- afi_t afi;
- safi_t safi;
-#define VALID_AFI 1
-#define INVALID_AFI 0
- int afi_valid;
} mp_reach_segments[] = {
{
"IPv6",
},
(4 + 16 + 1 + 5),
SHOULD_PARSE,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-2",
},
(4 + 16 + 1 + 5 + 9),
SHOULD_PARSE,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-default",
},
(4 + 16 + 1 + 5 + 9 + 1),
SHOULD_PARSE,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-lnh",
},
(4 + 32 + 1 + 5 + 9 + 1),
SHOULD_PARSE,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-nhlen",
},
(4 + 32 + 1 + 5 + 9 + 1),
SHOULD_ERR,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-nhlen2",
},
(4 + 32 + 1 + 5 + 9 + 1),
SHOULD_ERR,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-nhlen3",
},
(4 + 16),
SHOULD_ERR,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-nhlen4",
},
(4 + 32 + 1 + 5 + 9 + 1),
SHOULD_ERR,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-nlri",
},
(4 + 32 + 1 + 5 + 9 + 1),
SHOULD_ERR,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv4",
},
(4 + 4 + 1 + 3 + 4 + 1),
SHOULD_PARSE,
- AFI_IP,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv4-nhlen",
},
(4 + 4 + 1 + 3 + 4 + 1),
SHOULD_ERR,
- AFI_IP,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv4-nlrilen",
},
(4 + 4 + 1 + 3 + 2 + 1),
SHOULD_ERR,
- AFI_IP,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv4-VPNv4",
},
(4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
SHOULD_PARSE,
- AFI_IP,
- IANA_SAFI_MPLS_VPN,
- VALID_AFI,
},
{
"IPv4-VPNv4-bogus-plen",
},
(3 + 1 + 3 * 4 + 1 + 3 + 4 + 1),
SHOULD_ERR,
- AFI_IP,
- IANA_SAFI_MPLS_VPN,
- VALID_AFI,
},
{
"IPv4-VPNv4-plen1-short",
},
(4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
SHOULD_ERR,
- AFI_IP,
- IANA_SAFI_MPLS_VPN,
- VALID_AFI,
},
{
"IPv4-VPNv4-plen1-long",
},
(4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
SHOULD_ERR,
- AFI_IP,
- IANA_SAFI_MPLS_VPN,
- VALID_AFI,
},
{
"IPv4-VPNv4-plenn-long",
},
(4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3) + 1),
SHOULD_ERR,
- AFI_IP,
- IANA_SAFI_MPLS_VPN,
- VALID_AFI,
},
{
"IPv4-VPNv4-plenn-short",
},
(4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
SHOULD_ERR,
- AFI_IP,
- IANA_SAFI_MPLS_VPN,
- VALID_AFI,
},
{
"IPv4-VPNv4-bogus-rd-type",
},
(4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
SHOULD_PARSE,
- AFI_IP,
- IANA_SAFI_MPLS_VPN,
- VALID_AFI,
},
{
"IPv4-VPNv4-0-nlri",
},
(4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3) + 1),
SHOULD_ERR,
- AFI_IP,
- IANA_SAFI_MPLS_VPN,
- VALID_AFI,
},
/* From bug #385 */
},
37,
SHOULD_ERR,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{NULL, NULL, {0}, 0, 0}};
},
(3 + 5),
SHOULD_PARSE,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-unreach2",
},
(3 + 5 + 9),
SHOULD_PARSE,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-unreach-default",
},
(3 + 5 + 9 + 1),
SHOULD_PARSE,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv6-unreach-nlri",
},
(3 + 5 + 9 + 1),
SHOULD_ERR,
- AFI_IP6,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv4-unreach",
},
(3 + 3 + 4 + 1),
SHOULD_PARSE,
- AFI_IP,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv4-unreach-nlrilen",
},
(3 + 3 + 2 + 1),
SHOULD_ERR,
- AFI_IP,
- SAFI_UNICAST,
- VALID_AFI,
},
{
"IPv4-unreach-VPNv4",
},
(3 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
SHOULD_PARSE,
- AFI_IP,
- IANA_SAFI_MPLS_VPN,
- VALID_AFI,
},
{NULL, NULL, {0}, 0, 0}};
/*
- * $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'
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
if (!strcmp(string, "forever")) {
item->type = sched_forever;
- } else if (isdigit(string[0])) {
+ } else if (isdigit((int)string[0])) {
item->type = sched_timeout;
if (parse_integer(string, &item->value) != 0)
badusage("invalid timeout value in schedule");
vtysh_scan += $(top_srcdir)/ospfd/ospf_ri.c
vtysh_scan += $(top_srcdir)/ospfd/ospf_routemap.c
vtysh_scan += $(top_srcdir)/ospfd/ospf_te.c
+vtysh_scan += $(top_srcdir)/ospfd/ospf_sr.c
vtysh_scan += $(top_srcdir)/ospfd/ospf_vty.c
endif
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");
}
return s;
end = s + size - 1;
- while (end >= s && isspace(*end))
+ while (end >= s && isspace((int)*end))
end--;
*(end + 1) = '\0';
- while (*s && isspace(*s))
+ while (*s && isspace((int)*s))
s++;
return s;
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;
}
"IP trace\n"
"Trace route to destination address or hostname\n")
+DEFUN (vtysh_mtrace,
+ vtysh_mtrace_cmd,
+ "mtrace WORD",
+ "Multicast trace route to multicast source\n"
+ "Multicast trace route to multicast source address\n")
+{
+ int idx = 1;
+
+ argv_find(argv, argc, "WORD", &idx);
+ execute_command("mtracebis", 1, argv[idx]->arg, NULL);
+ return CMD_SUCCESS;
+}
+
DEFUN (vtysh_ping6,
vtysh_ping6_cmd,
"ping ipv6 WORD",
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);
install_element(VIEW_NODE, &vtysh_ping_ip_cmd);
install_element(VIEW_NODE, &vtysh_traceroute_cmd);
install_element(VIEW_NODE, &vtysh_traceroute_ip_cmd);
+ install_element(VIEW_NODE, &vtysh_mtrace_cmd);
install_element(VIEW_NODE, &vtysh_ping6_cmd);
install_element(VIEW_NODE, &vtysh_traceroute6_cmd);
#if defined(HAVE_SHELL_ACCESS)
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);
for (i = 0; i < vector_active(configvec); i++)
if ((master = vector_slot(configvec, i)) != NULL) {
for (ALL_LIST_ELEMENTS(master, node, nnode, config)) {
- /* Don't print empty sections for interface/vrf.
+ /* Don't print empty sections for interface.
* Route maps on the
* other hand could have a legitimate empty
* section at the end.
+ * VRF is handled in the backend, we could have
+ * "configured" VRFs with static routes which
+ * are not under the VRF node.
*/
- if ((config->index == INTERFACE_NODE
- || config->index == VRF_NODE)
+ if (config->index == INTERFACE_NODE
&& list_isempty(config->line))
continue;
while (vtysh_rl_gets())
vtysh_execute(line_read);
+ vtysh_uninit();
+
history_truncate_file(history_file, 1000);
printf("\n");
afi_t afi;
struct prefix p;
struct nexthop nh = {
- .type = NEXTHOP_TYPE_IFINDEX, .ifindex = ifp->ifindex,
+ .type = NEXTHOP_TYPE_IFINDEX,
+ .ifindex = ifp->ifindex,
+ .vrf_id = ifp->vrf_id,
};
if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
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, 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, 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];
afi_t afi;
struct prefix p;
struct nexthop nh = {
- .type = NEXTHOP_TYPE_IFINDEX, .ifindex = ifp->ifindex,
+ .type = NEXTHOP_TYPE_IFINDEX,
+ .ifindex = ifp->ifindex,
+ .vrf_id = ifp->vrf_id,
};
if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL))
* 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)
{
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);
}
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);
/* 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),
/* 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 "privs.h"
#include "vrf.h"
+#include "zebra/rt.h"
#include "zebra/interface.h"
#include "zebra/zserv.h"
#include "zebra/debug.h"
SET_FLAG(zebra_flags, ZEBRA_FLAG_STATIC);
memset(&nh, 0, sizeof(nh));
+
+ nh.vrf_id = VRF_DEFAULT;
/* This is a reject or blackhole route */
if (flags & RTF_REJECT) {
nh.type = NEXTHOP_TYPE_BLACKHOLE;
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;
|| rtm->rtm_type == RTM_CHANGE)
rib_add(AFI_IP, SAFI_UNICAST, 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
|| rtm->rtm_type == RTM_CHANGE)
rib_add(AFI_IP6, SAFI_UNICAST, 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);
}
}
msg.rtm.rtm_flags |= RTF_MPLS;
if (mpls->smpls.smpls_label
- != htonl(MPLS_IMP_NULL_LABEL << MPLS_LABEL_OFFSET))
+ != htonl(MPLS_LABEL_IMPLICIT_NULL << MPLS_LABEL_OFFSET))
msg.rtm.rtm_mpls = MPLS_OP_PUSH;
}
#endif
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);
return NULL;
if (list_isempty(lbl_mgr.lc_list))
- lmc->start = MPLS_MIN_UNRESERVED_LABEL;
+ lmc->start = MPLS_LABEL_UNRESERVED_MIN;
else
lmc->start = ((struct label_manager_chunk *)listgetdata(
listtail(lbl_mgr.lc_list)))
->end
+ 1;
- if (lmc->start > MPLS_MAX_UNRESERVED_LABEL - size + 1) {
+ if (lmc->start > MPLS_LABEL_UNRESERVED_MAX - size + 1) {
zlog_err("Reached max labels. Start: %u, size: %u", lmc->start,
size);
XFREE(MTYPE_LM_CHUNK, lmc);
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;
}
/* 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.
*/
} rib_update_event_t;
extern struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *,
- ifindex_t);
+ ifindex_t,
+ vrf_id_t nh_vrf_id);
extern struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *,
enum blackhole_type);
extern struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *,
struct in_addr *,
- struct in_addr *);
+ struct in_addr *,
+ vrf_id_t nh_vrf_id);
extern struct nexthop *
route_entry_nexthop_ipv4_ifindex_add(struct route_entry *, struct in_addr *,
- struct in_addr *, ifindex_t);
+ struct in_addr *, ifindex_t,
+ vrf_id_t nh_vrf_id);
extern void route_entry_nexthop_delete(struct route_entry *re,
struct nexthop *nexthop);
extern struct nexthop *route_entry_nexthop_ipv6_add(struct route_entry *,
- struct in6_addr *);
+ struct in6_addr *,
+ vrf_id_t nh_vrf_id);
extern struct nexthop *
route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re,
- struct in6_addr *ipv6, ifindex_t ifindex);
+ struct in6_addr *ipv6, ifindex_t ifindex,
+ vrf_id_t nh_vrf_id);
extern void route_entry_nexthop_add(struct route_entry *re,
struct nexthop *nexthop);
extern void route_entry_copy_nexthops(struct route_entry *re,
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 *,
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 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);
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;
+ }
+ nh.vrf_id = nh_vrf_id;
+
+ rib_add(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p,
+ NULL, &nh, table, metric, mtu, distance, tag);
} else {
/* This is a multipath route */
re->table = table;
re->nexthop_num = 0;
re->uptime = time(NULL);
+ re->tag = tag;
for (;;) {
+ vrf_id_t nh_vrf_id;
if (len < (int)sizeof(*rtnh)
|| rtnh->rtnh_len > len)
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)
+ nh_vrf_id = ifp->vrf_id;
+ else {
+ zlog_warn(
+ "%s: Unknown interface %u specified, defaulting to VRF_DEFAULT",
+ __PRETTY_FUNCTION__,
+ index);
+ nh_vrf_id = VRF_DEFAULT;
+ }
+ } else
+ nh_vrf_id = vrf_id;
+
gate = 0;
if (rtnh->rtnh_len > sizeof(*rtnh)) {
memset(tb, 0, sizeof(tb));
if (index)
route_entry_nexthop_ipv4_ifindex_add(
re, gate,
- prefsrc, index);
+ prefsrc, index,
+ nh_vrf_id);
else
route_entry_nexthop_ipv4_add(
re, gate,
- prefsrc);
+ prefsrc,
+ nh_vrf_id);
} else if (rtm->rtm_family
== AF_INET6) {
if (index)
route_entry_nexthop_ipv6_ifindex_add(
- re, gate,
- index);
+ re, gate, index,
+ nh_vrf_id);
else
route_entry_nexthop_ipv6_add(
- re, gate);
+ re, gate,
+ nh_vrf_id);
}
} else
- route_entry_nexthop_ifindex_add(re,
- index);
+ route_entry_nexthop_ifindex_add(
+ re, index, nh_vrf_id);
len -= NLMSG_ALIGN(rtnh->rtnh_len);
rtnh = RTNH_NEXT(rtnh);
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];
+ int num_labels = 0;
char label_buf[256];
/*
* you fix this assumption
*/
label_buf[0] = '\0';
- /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP
- * (in the case of LER)
- */
- nh_label = nexthop->nh_label;
- if (rtmsg->rtm_family == AF_MPLS) {
- assert(nh_label);
- assert(nh_label->num_labels == 1);
- }
- if (nh_label && nh_label->num_labels) {
- int i, num_labels = 0;
- u_int32_t bos;
+ assert(nexthop);
+ for (struct nexthop *nh = nexthop; nh; nh = nh->rparent) {
char label_buf1[20];
- for (i = 0; i < nh_label->num_labels; i++) {
- if (nh_label->label[i] != MPLS_IMP_NULL_LABEL) {
- bos = ((i == (nh_label->num_labels - 1)) ? 1
- : 0);
- out_lse[i] = mpls_lse_encode(nh_label->label[i],
- 0, 0, bos);
- if (IS_ZEBRA_DEBUG_KERNEL) {
- if (!num_labels)
- sprintf(label_buf, "label %u",
- nh_label->label[i]);
- else {
- sprintf(label_buf1, "/%u",
- nh_label->label[i]);
- strlcat(label_buf, label_buf1,
- sizeof(label_buf));
- }
+ nh_label = nh->nh_label;
+ if (!nh_label || !nh_label->num_labels)
+ continue;
+
+ for (int i = 0; i < nh_label->num_labels; i++) {
+ if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL)
+ continue;
+
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ if (!num_labels)
+ sprintf(label_buf, "label %u",
+ nh_label->label[i]);
+ else {
+ sprintf(label_buf1, "/%u",
+ nh_label->label[i]);
+ strlcat(label_buf, label_buf1,
+ sizeof(label_buf));
}
- num_labels++;
}
+
+ out_lse[num_labels] =
+ mpls_lse_encode(nh_label->label[i], 0, 0, 0);
+ num_labels++;
}
- if (num_labels) {
- if (rtmsg->rtm_family == AF_MPLS)
- addattr_l(nlmsg, req_size, RTA_NEWDST, &out_lse,
- num_labels * sizeof(mpls_lse_t));
- else {
- struct rtattr *nest;
- u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
-
- addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE,
- &encap, sizeof(u_int16_t));
- nest = addattr_nest(nlmsg, req_size, RTA_ENCAP);
- addattr_l(nlmsg, req_size, MPLS_IPTUNNEL_DST,
- &out_lse,
- num_labels * sizeof(mpls_lse_t));
- addattr_nest_end(nlmsg, nest);
- }
+ }
+
+ if (num_labels) {
+ /* Set the BoS bit */
+ out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT);
+
+ if (rtmsg->rtm_family == AF_MPLS)
+ addattr_l(nlmsg, req_size, RTA_NEWDST, &out_lse,
+ num_labels * sizeof(mpls_lse_t));
+ else {
+ struct rtattr *nest;
+ u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
+
+ addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE, &encap,
+ sizeof(u_int16_t));
+ nest = addattr_nest(nlmsg, req_size, RTA_ENCAP);
+ addattr_l(nlmsg, req_size, MPLS_IPTUNNEL_DST, &out_lse,
+ num_labels * sizeof(mpls_lse_t));
+ addattr_nest_end(nlmsg, nest);
}
}
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];
+ int num_labels = 0;
char label_buf[256];
rtnh->rtnh_len = sizeof(*rtnh);
* you fix this assumption
*/
label_buf[0] = '\0';
- /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP
- * (in the case of LER)
- */
- nh_label = nexthop->nh_label;
- if (rtmsg->rtm_family == AF_MPLS) {
- assert(nh_label);
- assert(nh_label->num_labels == 1);
- }
- if (nh_label && nh_label->num_labels) {
- int i, num_labels = 0;
- u_int32_t bos;
+ assert(nexthop);
+ for (struct nexthop *nh = nexthop; nh; nh = nh->rparent) {
char label_buf1[20];
- for (i = 0; i < nh_label->num_labels; i++) {
- if (nh_label->label[i] != MPLS_IMP_NULL_LABEL) {
- bos = ((i == (nh_label->num_labels - 1)) ? 1
- : 0);
- out_lse[i] = mpls_lse_encode(nh_label->label[i],
- 0, 0, bos);
- if (IS_ZEBRA_DEBUG_KERNEL) {
- if (!num_labels)
- sprintf(label_buf, "label %u",
- nh_label->label[i]);
- else {
- sprintf(label_buf1, "/%u",
- nh_label->label[i]);
- strlcat(label_buf, label_buf1,
- sizeof(label_buf));
- }
+ nh_label = nh->nh_label;
+ if (!nh_label || !nh_label->num_labels)
+ continue;
+
+ for (int i = 0; i < nh_label->num_labels; i++) {
+ if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL)
+ continue;
+
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ if (!num_labels)
+ sprintf(label_buf, "label %u",
+ nh_label->label[i]);
+ else {
+ sprintf(label_buf1, "/%u",
+ nh_label->label[i]);
+ strlcat(label_buf, label_buf1,
+ sizeof(label_buf));
}
- num_labels++;
}
+
+ out_lse[num_labels] =
+ mpls_lse_encode(nh_label->label[i], 0, 0, 0);
+ num_labels++;
}
- if (num_labels) {
- if (rtmsg->rtm_family == AF_MPLS) {
- rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_NEWDST,
- &out_lse,
- num_labels * sizeof(mpls_lse_t));
- rtnh->rtnh_len += RTA_LENGTH(
- num_labels * sizeof(mpls_lse_t));
- } else {
- struct rtattr *nest;
- u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
- int len = rta->rta_len;
-
- rta_addattr_l(rta, NL_PKT_BUF_SIZE,
- RTA_ENCAP_TYPE, &encap,
- sizeof(u_int16_t));
- nest = rta_nest(rta, NL_PKT_BUF_SIZE,
- RTA_ENCAP);
- rta_addattr_l(rta, NL_PKT_BUF_SIZE,
- MPLS_IPTUNNEL_DST, &out_lse,
- num_labels * sizeof(mpls_lse_t));
- rta_nest_end(rta, nest);
- rtnh->rtnh_len += rta->rta_len - len;
- }
+ }
+
+ if (num_labels) {
+ /* Set the BoS bit */
+ out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT);
+
+ if (rtmsg->rtm_family == AF_MPLS) {
+ rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_NEWDST,
+ &out_lse,
+ num_labels * sizeof(mpls_lse_t));
+ rtnh->rtnh_len +=
+ RTA_LENGTH(num_labels * sizeof(mpls_lse_t));
+ } else {
+ struct rtattr *nest;
+ u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
+ int len = rta->rta_len;
+
+ rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_ENCAP_TYPE,
+ &encap, sizeof(u_int16_t));
+ nest = rta_nest(rta, NL_PKT_BUF_SIZE, RTA_ENCAP);
+ rta_addattr_l(rta, NL_PKT_BUF_SIZE, MPLS_IPTUNNEL_DST,
+ &out_lse,
+ num_labels * sizeof(mpls_lse_t));
+ rta_nest_end(rta, nest);
+ rtnh->rtnh_len += rta->rta_len - len;
}
}
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 */
prefix.prefixlen = ip_masklen(tmpaddr);
memset(&nh, 0, sizeof(nh));
+ nh.vrf_id = VRF_DEFAULT;
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);
+ zebra_flags, &prefix, NULL, &nh, 0, 0, 0, 0, 0);
}
void route_read(struct zebra_ns *zns)
!
! 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,
afi_t afi;
/* Uninstall label forwarding entry, if previously installed. */
- if (old_label != MPLS_INVALID_LABEL && old_label != MPLS_IMP_NULL_LABEL)
+ if (old_label != MPLS_INVALID_LABEL &&
+ old_label != MPLS_LABEL_IMPLICIT_NULL)
lsp_uninstall(zvrf, old_label);
/* Install label forwarding entry corr. to new label, if needed. */
if (fec->label == MPLS_INVALID_LABEL
- || fec->label == MPLS_IMP_NULL_LABEL)
+ || fec->label == MPLS_LABEL_IMPLICIT_NULL)
return 0;
afi = family2afi(PREFIX_FAMILY(&fec->rn->p));
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);
struct route_entry *match;
struct nexthop *match_nh;
- table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, VRF_DEFAULT);
+ table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, nexthop->vrf_id);
if (!table)
return 0;
struct route_node *rn;
struct route_entry *match;
- table = zebra_vrf_table(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT);
+ table = zebra_vrf_table(AFI_IP6, SAFI_UNICAST, nexthop->vrf_id);
if (!table)
return 0;
/* Check on nexthop based on type. */
switch (nexthop->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ /*
+ * Lookup if this type is special. The
+ * NEXTHOP_TYPE_IFINDEX is a pop and
+ * forward into a different table for
+ * processing. As such this ifindex
+ * passed to us may be a VRF device
+ * which will not be in the default
+ * VRF. So let's look in all of them
+ */
+ ifp = if_lookup_by_index(nexthop->ifindex, VRF_UNKNOWN);
+ if (ifp && if_is_operative(ifp))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ break;
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
if (nhlfe_nexthop_active_ipv4(nhlfe, nexthop))
case NEXTHOP_TYPE_IPV6_IFINDEX:
if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
- ifp = if_lookup_by_index(nexthop->ifindex, VRF_DEFAULT);
+ ifp = if_lookup_by_index(nexthop->ifindex,
+ nexthop->vrf_id);
if (ifp && if_is_operative(ifp))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
case NEXTHOP_TYPE_IPV6:
inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, size);
break;
+ case NEXTHOP_TYPE_IFINDEX:
+ snprintf(buf, size, "Ifindex: %u", nexthop->ifindex);
default:
break;
}
if (!cmp && nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
cmp = !(nhop->ifindex == ifindex);
break;
+ case NEXTHOP_TYPE_IFINDEX:
+ cmp = !(nhop->ifindex == ifindex);
+ break;
default:
break;
}
}
nexthop_add_labels(nexthop, lsp_type, 1, &out_label);
+ nexthop->vrf_id = VRF_DEFAULT;
nexthop->type = gtype;
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
if (ifindex)
nexthop->ifindex = ifindex;
break;
+ case NEXTHOP_TYPE_IFINDEX:
+ nexthop->ifindex = ifindex;
+ break;
default:
nexthop_free(nexthop);
XFREE(MTYPE_NHLFE, nhlfe);
* 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];
}
inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ));
if (nexthop->ifindex)
- json_object_string_add(
- json_nhlfe, "interface",
- ifindex2ifname(nexthop->ifindex, VRF_DEFAULT));
+ json_object_string_add(json_nhlfe, "interface",
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
break;
default:
break;
vty_out(vty, " via %s", inet_ntoa(nexthop->gate.ipv4));
if (nexthop->ifindex)
vty_out(vty, " dev %s",
- ifindex2ifname(nexthop->ifindex, VRF_DEFAULT));
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ));
if (nexthop->ifindex)
vty_out(vty, " dev %s",
- ifindex2ifname(nexthop->ifindex, VRF_DEFAULT));
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
break;
default:
break;
/* We cannot install a label forwarding entry if local label is the
* implicit-null label.
*/
- if (fec->label == MPLS_IMP_NULL_LABEL)
+ if (fec->label == MPLS_LABEL_IMPLICIT_NULL)
return 0;
if (lsp_install(zvrf, fec->label, rn, re))
int cur_op, new_op;
cur_op = (slsp->snhlfe_list->out_label
- == MPLS_IMP_NULL_LABEL);
- new_op = (out_label == MPLS_IMP_NULL_LABEL);
+ == MPLS_LABEL_IMPLICIT_NULL);
+ new_op = (out_label == MPLS_LABEL_IMPLICIT_NULL);
if (cur_op != new_op)
return 0;
}
nexthop = nhlfe->nexthop;
switch (nexthop->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index(
+ nexthop->ifindex, VRF_UNKNOWN);
+ vty_out(vty, "%15s", ifp->name);
+ break;
+ }
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
vty_out(vty, "%15s",
break;
}
- vty_out(vty, " %8d\n",
- nexthop->nh_label->label[0]);
+ if (nexthop->type != NEXTHOP_TYPE_IFINDEX)
+ vty_out(vty, " %8s\n",
+ mpls_label2str(
+ nexthop->nh_label
+ ->num_labels,
+ &nexthop->nh_label
+ ->label[0],
+ buf, BUFSIZ, 1));
+ else
+ vty_out(vty, "\n");
}
}
snhlfe2str(snhlfe, buf, sizeof(buf));
switch (snhlfe->out_label) {
- case MPLS_V4_EXP_NULL_LABEL:
- case MPLS_V6_EXP_NULL_LABEL:
+ case MPLS_LABEL_IPV4_EXPLICIT_NULL:
+ case MPLS_LABEL_IPV6_EXPLICIT_NULL:
strlcpy(lstr, "explicit-null", sizeof(lstr));
break;
- case MPLS_IMP_NULL_LABEL:
+ case MPLS_LABEL_IMPLICIT_NULL:
strlcpy(lstr, "implicit-null", sizeof(lstr));
break;
default:
return 1;
}
+/*
+ * Called when VRF becomes inactive, cleans up information but keeps
+ * the table itself.
+ * NOTE: Currently supported only for default VRF.
+ */
+void zebra_mpls_cleanup_tables(struct zebra_vrf *zvrf)
+{
+ hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL);
+}
+
/*
* Called upon process exiting, need to delete LSP forwarding
* entries from the kernel.
*/
void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi);
+/*
+ * Uninstall all Segment Routing NHLFEs for a particular LSP forwarding entry.
+ * If no other NHLFEs exist, the entry would be deleted.
+ */
+void mpls_sr_lsp_uninstall_all(struct hash_backet *backet, void *ctxt);
+
#if defined(HAVE_CUMULUS)
/*
* Check that the label values used in LSP creation are consistent. The
*/
int zebra_mpls_write_lsp_config(struct vty *vty, struct zebra_vrf *zvrf);
+/*
+ * Called when VRF becomes inactive, cleans up information but keeps
+ * the table itself.
+ * NOTE: Currently supported only for default VRF.
+ */
+void zebra_mpls_cleanup_tables(struct zebra_vrf *zvrf);
+
/*
* Called upon process exiting, need to delete LSP forwarding
* entries from the kernel.
return (route_distance(ZEBRA_ROUTE_LDP));
case ZEBRA_LSP_BGP:
return (route_distance(ZEBRA_ROUTE_BGP));
- default:
+ case ZEBRA_LSP_NONE:
+ case ZEBRA_LSP_SHARP:
+ case ZEBRA_LSP_SR:
return 150;
}
+
+ /*
+ * For some reason certain compilers do not believe
+ * that all the cases have been handled. And
+ * WTF does this work differently than when I removed
+ * the default case????
+ */
+ return 150;
}
/*
return ZEBRA_LSP_STATIC;
case ZEBRA_ROUTE_BGP:
return ZEBRA_LSP_BGP;
+ case ZEBRA_ROUTE_SHARP:
+ return ZEBRA_LSP_SHARP;
default:
return ZEBRA_LSP_NONE;
}
return ZEBRA_ROUTE_LDP;
case ZEBRA_LSP_BGP:
return ZEBRA_ROUTE_BGP;
+ case ZEBRA_LSP_SR:
+ return ZEBRA_ROUTE_OSPF;
case ZEBRA_LSP_NONE:
- default:
return ZEBRA_ROUTE_KERNEL;
+ case ZEBRA_LSP_SHARP:
+ return ZEBRA_ROUTE_SHARP;
}
+
+ /*
+ * For some reason certain compilers do not believe
+ * that all the cases have been handled. And
+ * WTF does this work differently than when I removed
+ * the default case????
+ */
+ return ZEBRA_ROUTE_KERNEL;
}
/* NHLFE type as printable string. */
return "LDP";
case ZEBRA_LSP_BGP:
return "BGP";
- default:
+ case ZEBRA_LSP_SR:
+ return "SR";
+ case ZEBRA_LSP_SHARP:
+ return "SHARP";
+ case ZEBRA_LSP_NONE:
return "Unknown";
}
+
+ /*
+ * For some reason certain compilers do not believe
+ * that all the cases have been handled. And
+ * WTF does this work differently than when I removed
+ * the default case????
+ */
+ return "Unknown";
}
static inline void mpls_mark_lsps_for_processing(struct zebra_vrf *zvrf)
return CMD_WARNING_CONFIG_FAILED;
}
- out_label = MPLS_IMP_NULL_LABEL; /* as initialization */
+ out_label = MPLS_LABEL_IMPLICIT_NULL; /* as initialization */
label = atoi(inlabel_str);
if (!IS_MPLS_UNRESERVED_LABEL(label)) {
vty_out(vty, "%% Invalid label\n");
if (outlabel_str) {
if (outlabel_str[0] == 'i')
- out_label = MPLS_IMP_NULL_LABEL;
+ out_label = MPLS_LABEL_IMPLICIT_NULL;
else if (outlabel_str[0] == 'e' && gtype == NEXTHOP_TYPE_IPV4)
- out_label = MPLS_V4_EXP_NULL_LABEL;
+ out_label = MPLS_LABEL_IPV4_EXPLICIT_NULL;
else if (outlabel_str[0] == 'e' && gtype == NEXTHOP_TYPE_IPV6)
- out_label = MPLS_V6_EXP_NULL_LABEL;
+ out_label = MPLS_LABEL_IPV6_EXPLICIT_NULL;
else
out_label = atoi(outlabel_str);
}
}
if (!strcmp(label_str, "implicit-null"))
- label = MPLS_IMP_NULL_LABEL;
+ label = MPLS_LABEL_IMPLICIT_NULL;
else if (!strcmp(label_str, "explicit-null")) {
if (p.family == AF_INET)
- label = MPLS_V4_EXP_NULL_LABEL;
+ label = MPLS_LABEL_IPV4_EXPLICIT_NULL;
else
- label = MPLS_V6_EXP_NULL_LABEL;
+ label = MPLS_LABEL_IPV6_EXPLICIT_NULL;
} else {
label = atoi(label_str);
if (!IS_MPLS_UNRESERVED_LABEL(label)) {
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_vrf.h"
#include "zebra_memory.h"
#include "rt.h"
+#include "zebra_vxlan.h"
DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space")
return dzns;
}
+/* Do global enable actions - open sockets, read kernel config etc. */
int zebra_ns_enable(ns_id_t ns_id, void **info)
{
struct zebra_ns *zns = (struct zebra_ns *)(*info);
rtadv_init(zns);
#endif
- zns->if_table = route_table_init();
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
ns_init();
+ /* Do any needed per-NS data structure allocation. */
+ dzns->if_table = route_table_init();
+ zebra_vxlan_ns_init(dzns);
+
+ /* Register zebra VRF callbacks, create and activate default VRF. */
zebra_vrf_init();
- zebra_ns_enable(0, (void **)&dzns);
+ /* Default NS is activated */
+ 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 we ever add more STREAM_GETXXX functions after the out_ctxt
+ * is allocated then we need to add this code back in
+ *
+ * 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))
[ZEBRA_ROUTE_BGP_DIRECT] = {ZEBRA_ROUTE_BGP_DIRECT, 20},
[ZEBRA_ROUTE_BGP_DIRECT_EXT] = {ZEBRA_ROUTE_BGP_DIRECT_EXT, 20},
[ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 100},
+ [ZEBRA_ROUTE_SHARP] = {ZEBRA_ROUTE_SHARP, 150},
/* no entry/default: 150 */
};
struct nexthop *route_entry_nexthop_ifindex_add(struct route_entry *re,
- ifindex_t ifindex)
+ ifindex_t ifindex,
+ vrf_id_t nh_vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
nexthop->type = NEXTHOP_TYPE_IFINDEX;
nexthop->ifindex = ifindex;
+ nexthop->vrf_id = nh_vrf_id;
route_entry_nexthop_add(re, nexthop);
struct nexthop *route_entry_nexthop_ipv4_add(struct route_entry *re,
struct in_addr *ipv4,
- struct in_addr *src)
+ struct in_addr *src,
+ vrf_id_t nh_vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
nexthop->type = NEXTHOP_TYPE_IPV4;
+ nexthop->vrf_id = nh_vrf_id;
nexthop->gate.ipv4 = *ipv4;
if (src)
nexthop->src.ipv4 = *src;
struct nexthop *route_entry_nexthop_ipv4_ifindex_add(struct route_entry *re,
struct in_addr *ipv4,
struct in_addr *src,
- ifindex_t ifindex)
+ ifindex_t ifindex,
+ vrf_id_t nh_vrf_id)
{
struct nexthop *nexthop;
struct interface *ifp;
nexthop = nexthop_new();
+ nexthop->vrf_id = nh_vrf_id;
nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
nexthop->gate.ipv4 = *ipv4;
if (src)
nexthop->src.ipv4 = *src;
nexthop->ifindex = ifindex;
- ifp = if_lookup_by_index(nexthop->ifindex, re->vrf_id);
+ ifp = if_lookup_by_index(nexthop->ifindex, nh_vrf_id);
/*Pending: need to think if null ifp here is ok during bootup?
There was a crash because ifp here was coming to be NULL */
if (ifp)
- if (connected_is_unnumbered(ifp)) {
+ if (connected_is_unnumbered(ifp) ||
+ CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) {
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK);
}
}
struct nexthop *route_entry_nexthop_ipv6_add(struct route_entry *re,
- struct in6_addr *ipv6)
+ struct in6_addr *ipv6,
+ vrf_id_t nh_vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
+ nexthop->vrf_id = nh_vrf_id;
nexthop->type = NEXTHOP_TYPE_IPV6;
nexthop->gate.ipv6 = *ipv6;
struct nexthop *route_entry_nexthop_ipv6_ifindex_add(struct route_entry *re,
struct in6_addr *ipv6,
- ifindex_t ifindex)
+ ifindex_t ifindex,
+ vrf_id_t nh_vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
+ nexthop->vrf_id = nh_vrf_id;
nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
nexthop->gate.ipv6 = *ipv6;
nexthop->ifindex = ifindex;
struct nexthop *nexthop;
nexthop = nexthop_new();
+ nexthop->vrf_id = VRF_DEFAULT;
nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
nexthop->bh_type = bh_type;
resolved_hop = nexthop_new();
SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+ resolved_hop->vrf_id = nexthop->vrf_id;
switch (newhop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
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(nexthop->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, nexthop->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, nexthop->vrf_id);
if (!table)
return 0;
/* However, do not resolve over default route unless explicitly
* allowed. */
if (is_default_prefix(&rn->p)
- && !nh_resolve_via_default(p.family))
+ && !rnh_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, nexthop->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,
+ nexthop->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,
- re->tag);
+ ret = zebra_route_map_check(family, re->type, p, nexthop,
+ nexthop->vrf_id, re->tag);
if (ret == RMAP_DENYMATCH) {
if (IS_ZEBRA_DEBUG_RIB) {
srcdest_rnode2str(rn, buf, sizeof(buf));
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,
+ nexthop->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))
dest->routes = re->next;
}
+ if (dest->selected_fib == re)
+ dest->selected_fib = NULL;
+
/* free RE and nexthops */
zebra_deregister_rnh_static_nexthops(re->vrf_id, re->nexthop, rn);
nexthops_free(re->nexthop);
{
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)
+ u_int32_t mtu, uint8_t distance, route_tag_t tag)
{
struct route_entry *re;
struct nexthop *nexthop;
re->vrf_id = 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. */
* match route to be exact if so specified
*/
if (is_default_prefix(&rn->p) &&
- !nh_resolve_via_default(rn->p.family))
+ !rnh_resolve_via_default(rn->p.family))
return NULL;
/* Identify appropriate route entry. */
state->type = re->type;
state->distance = re->distance;
state->metric = re->metric;
+ state->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) {
stream_putc(s, nexthop->type);
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
stream_put_in_addr(s,
&nexthop->gate.ipv4);
stream_putl(s, nexthop->ifindex);
case NEXTHOP_TYPE_IFINDEX:
stream_putl(s, nexthop->ifindex);
break;
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- stream_put_in_addr(s,
- &nexthop->gate.ipv4);
- stream_putl(s, nexthop->ifindex);
- break;
case NEXTHOP_TYPE_IPV6:
- stream_put(s, &nexthop->gate.ipv6, 16);
- stream_putl(s, nexthop->ifindex);
- break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
stream_put(s, &nexthop->gate.ipv6, 16);
stream_putl(s, nexthop->ifindex);
extern int zebra_rnh_ip_default_route;
extern int zebra_rnh_ipv6_default_route;
+static inline int rnh_resolve_via_default(int family)
+{
+ if (((family == AF_INET) && zebra_rnh_ip_default_route)
+ || ((family == AF_INET6) && zebra_rnh_ipv6_default_route))
+ return 1;
+ else
+ return 0;
+}
+
extern struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid,
rnh_type_t type);
extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid,
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 = nexthop->vrf_id;
nh_obj.source_protocol = re->type;
nh_obj.metric = re->metric;
nh_obj.tag = re->tag;
switch (si->type) {
case STATIC_IPV4_GATEWAY:
nexthop = route_entry_nexthop_ipv4_add(
- re, &si->addr.ipv4, NULL);
+ re, &si->addr.ipv4, NULL, si->nh_vrf_id);
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(
- re, &si->addr.ipv4, NULL, si->ifindex);
+ re, &si->addr.ipv4, NULL, si->ifindex,
+ si->nh_vrf_id);
break;
case STATIC_IFNAME:
- nexthop = route_entry_nexthop_ifindex_add(re,
- si->ifindex);
+ nexthop = route_entry_nexthop_ifindex_add(
+ re, si->ifindex, si->nh_vrf_id);
break;
case STATIC_BLACKHOLE:
nexthop = route_entry_nexthop_blackhole_add(
re, bh_type);
break;
case STATIC_IPV6_GATEWAY:
- nexthop = route_entry_nexthop_ipv6_add(re,
- &si->addr.ipv6);
+ nexthop = route_entry_nexthop_ipv6_add(
+ re, &si->addr.ipv6, si->nh_vrf_id);
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(
- re, &si->addr.ipv6, si->ifindex);
+ re, &si->addr.ipv6, si->ifindex, si->nh_vrf_id);
break;
}
/* Update label(s), if present. */
*/
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->mtu = 0;
re->vrf_id = si->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;
switch (si->type) {
case STATIC_IPV4_GATEWAY:
nexthop = route_entry_nexthop_ipv4_add(
- re, &si->addr.ipv4, NULL);
+ re, &si->addr.ipv4, NULL, si->nh_vrf_id);
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(
- re, &si->addr.ipv4, NULL, si->ifindex);
+ re, &si->addr.ipv4, NULL, si->ifindex,
+ si->nh_vrf_id);
break;
case STATIC_IFNAME:
- nexthop = route_entry_nexthop_ifindex_add(re,
- si->ifindex);
+ nexthop = route_entry_nexthop_ifindex_add(
+ re, si->ifindex, si->nh_vrf_id);
break;
case STATIC_BLACKHOLE:
nexthop = route_entry_nexthop_blackhole_add(
re, bh_type);
break;
case STATIC_IPV6_GATEWAY:
- nexthop = route_entry_nexthop_ipv6_add(re,
- &si->addr.ipv6);
+ nexthop = route_entry_nexthop_ipv6_add(
+ re, &si->addr.ipv6, si->nh_vrf_id);
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(
- re, &si->addr.ipv6, si->ifindex);
+ re, &si->addr.ipv6, si->ifindex, si->nh_vrf_id);
break;
}
/* Update label(s), if present. */
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,
extern struct zebra_t zebrad;
+static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi,
+ safi_t safi);
+static void zebra_rnhtable_node_cleanup(struct route_table *table,
+ struct route_node *node);
+
/* VRF information update. */
static void zebra_vrf_add_update(struct zebra_vrf *zvrf)
{
struct zebra_vrf *zvrf;
if (IS_ZEBRA_DEBUG_EVENT)
- zlog_info("ZVRF %s with id %u", vrf->name, vrf->vrf_id);
+ zlog_info("VRF %s created, id %u", vrf->name, vrf->vrf_id);
zvrf = zebra_vrf_alloc();
zvrf->zns = zebra_ns_lookup(
struct route_table *stable;
struct route_node *rn;
struct static_route *si;
+ struct route_table *table;
struct interface *ifp;
afi_t afi;
safi_t safi;
assert(zvrf);
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("VRF %s id %u is now active",
+ zvrf_name(zvrf), zvrf_id(zvrf));
+ /* Inform clients that the VRF is now active. This is an
+ * add for the clients.
+ */
zebra_vrf_add_update(zvrf);
+ /* Allocate tables */
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+ for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++)
+ zebra_vrf_table_create(zvrf, afi, safi);
+
+ table = route_table_init();
+ table->cleanup = zebra_rnhtable_node_cleanup;
+ zvrf->rnh_table[afi] = table;
+
+ table = route_table_init();
+ table->cleanup = zebra_rnhtable_node_cleanup;
+ zvrf->import_check_table[afi] = table;
+ }
+
+ /* Install any static routes configured for this VRF. */
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
stable = zvrf->stable[afi][safi];
}
}
+ /* Kick off any VxLAN-EVPN processing. */
+ zebra_vxlan_vrf_enable(zvrf);
+
return 0;
}
struct route_table *stable;
struct route_node *rn;
struct static_route *si;
+ struct route_table *table;
+ struct interface *ifp;
+ u_int32_t table_id;
afi_t afi;
safi_t safi;
+ unsigned i;
- if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("VRF %s id %u is now disabled.", zvrf_name(zvrf),
- zvrf_id(zvrf));
+ assert(zvrf);
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("VRF %s id %u is now inactive",
+ zvrf_name(zvrf), zvrf_id(zvrf));
+ /* Uninstall any static routes configured for this VRF. */
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
stable = zvrf->stable[afi][safi];
afi, safi, &rn->p, NULL, si);
}
+ /* Stop any VxLAN-EVPN processing. */
+ zebra_vxlan_vrf_disable(zvrf);
+
+ /* Inform clients that the VRF is now inactive. This is a
+ * delete for the clients.
+ */
+ zebra_vrf_delete_update(zvrf);
+
+ /* If asked to retain routes, there's nothing more to do. */
+ if (CHECK_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN))
+ return 0;
+
+ /* Remove all routes. */
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+ for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++)
+ rib_close_table(zvrf->table[afi][safi]);
+
+ if (vrf->vrf_id == VRF_DEFAULT)
+ for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX;
+ table_id++)
+ if (zvrf->other_table[afi][table_id])
+ rib_close_table(zvrf->other_table[afi][table_id]);
+ }
+
+ /* Cleanup Vxlan, MPLS and PW tables. */
+ zebra_vxlan_cleanup_tables(zvrf);
+ zebra_mpls_cleanup_tables(zvrf);
+ zebra_pw_exit(zvrf);
+
+ /* Remove link-local IPv4 addresses created for BGP unnumbered peering. */
+ FOR_ALL_INTERFACES (vrf, ifp)
+ if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(ifp);
+
+ /* clean-up work queues */
+ for (i = 0; i < MQ_SIZE; i++) {
+ struct listnode *lnode, *nnode;
+ struct route_node *rnode;
+ rib_dest_t *dest;
+
+ for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i], lnode, nnode, rnode)) {
+ dest = rib_dest_from_rnode(rnode);
+ if (dest && rib_dest_vrf(dest) == zvrf) {
+ route_unlock_node(rnode);
+ list_delete_node(zebrad.mq->subq[i], lnode);
+ zebrad.mq->size--;
+ }
+ }
+ }
+
+ /* Cleanup (free) routing tables and NHT tables. */
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+ void *table_info;
+
+ for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) {
+ table = zvrf->table[afi][safi];
+ table_info = table->info;
+ route_table_finish(table);
+ XFREE(MTYPE_RIB_TABLE_INFO, table_info);
+ zvrf->table[afi][safi] = NULL;
+ }
+
+ if (vrf->vrf_id == VRF_DEFAULT)
+ for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX;
+ table_id++)
+ if (zvrf->other_table[afi][table_id]) {
+ table = zvrf->other_table[afi][table_id];
+ table_info = table->info;
+ route_table_finish(table);
+ XFREE(MTYPE_RIB_TABLE_INFO, table_info);
+ zvrf->other_table[afi][table_id] = NULL;
+ }
+
+ route_table_finish(zvrf->rnh_table[afi]);
+ zvrf->rnh_table[afi] = NULL;
+ route_table_finish(zvrf->import_check_table[afi]);
+ zvrf->import_check_table[afi] = NULL;
+ }
+
return 0;
}
unsigned i;
assert(zvrf);
-
- zebra_vrf_delete_update(zvrf);
-
- /* uninstall everything */
- if (!CHECK_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN)) {
- struct interface *ifp;
-
- for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
- for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST;
- safi++)
- rib_close_table(zvrf->table[afi][safi]);
-
- if (vrf->vrf_id == VRF_DEFAULT)
- for (table_id = 0;
- table_id < ZEBRA_KERNEL_TABLE_MAX;
- table_id++)
- if (zvrf->other_table[afi][table_id])
- rib_close_table(
- zvrf->other_table
- [afi]
- [table_id]);
- }
-
- /* Cleanup Vxlan table and update kernel */
- zebra_vxlan_close_tables(zvrf);
-
- zebra_mpls_close_tables(zvrf);
- zebra_pw_exit(zvrf);
-
- FOR_ALL_INTERFACES (vrf, ifp)
- if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(ifp);
- }
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("VRF %s id %u deleted",
+ zvrf_name(zvrf), zvrf_id(zvrf));
/* clean-up work queues */
for (i = 0; i < MQ_SIZE; i++) {
struct route_node *rnode;
rib_dest_t *dest;
- for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i], lnode, nnode,
- rnode)) {
+ for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i], lnode, nnode, rnode)) {
dest = rib_dest_from_rnode(rnode);
if (dest && rib_dest_vrf(dest) == zvrf) {
route_unlock_node(rnode);
}
}
+ /* Free Vxlan and MPLS. */
+ zebra_vxlan_close_tables(zvrf);
+ zebra_mpls_close_tables(zvrf);
+
/* release allocated memory */
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
void *table_info;
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) {
table = zvrf->table[afi][safi];
- table_info = table->info;
- route_table_finish(table);
- XFREE(MTYPE_RIB_TABLE_INFO, table_info);
+ if (table) {
+ table_info = table->info;
+ route_table_finish(table);
+ XFREE(MTYPE_RIB_TABLE_INFO, table_info);
+ }
table = zvrf->stable[afi][safi];
route_table_finish(table);
}
- for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX;
- table_id++)
+ for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX; table_id++)
if (zvrf->other_table[afi][table_id]) {
table = zvrf->other_table[afi][table_id];
table_info = table->info;
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);
return 0;
}
+/* Return if this VRF has any FRR configuration or not.
+ * IMPORTANT: This function needs to be updated when additional configuration
+ * is added for a VRF.
+ */
+int zebra_vrf_has_config(struct zebra_vrf *zvrf)
+{
+ afi_t afi;
+ safi_t safi;
+ struct route_table *stable;
+
+ /* NOTE: This is a don't care for the default VRF, but we go through
+ * the motions to keep things consistent.
+ */
+ /* Any static routes? */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
+ stable = zvrf->stable[afi][safi];
+ if (!stable)
+ continue;
+ if (route_table_count(stable))
+ return 1;
+ }
+ }
+
+ /* EVPN L3-VNI? */
+ if (zvrf->l3vni)
+ return 1;
+
+ return 0;
+}
+
/* Lookup the routing table in a VRF based on both VRF-Id and table-id.
* NOTE: Table-id is relevant only in the Default VRF.
*/
zvrf = XCALLOC(MTYPE_ZEBRA_VRF, sizeof(struct zebra_vrf));
+ /* Allocate table for static route configuration. */
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) {
- zebra_vrf_table_create(zvrf, afi, safi);
if (afi == AFI_IP6)
table = srcdest_table_init();
else
table->cleanup = zebra_stable_node_cleanup;
zvrf->stable[afi][safi] = table;
}
-
- table = route_table_init();
- table->cleanup = zebra_rnhtable_node_cleanup;
- zvrf->rnh_table[afi] = table;
-
- table = route_table_init();
- table->cleanup = zebra_rnhtable_node_cleanup;
- zvrf->import_check_table[afi] = table;
}
zebra_vxlan_init_tables(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 (zvrf_id(zvrf) == VRF_DEFAULT) {
+ if (zvrf->l3vni)
+ vty_out(vty, "vni %u\n", zvrf->l3vni);
+ vty_out(vty, "!\n");
+ }
+
+ if (vrf_is_user_cfged(vrf)) {
vty_out(vty, "vrf %s\n", zvrf_name(zvrf));
+ if (zvrf->l3vni)
+ vty_out(vty, " vni %u\n", zvrf->l3vni);
vty_out(vty, "!\n");
}
+
+ 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)
+ 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_ {
*/
struct zebra_ns *zns;
+ /* MPLS Label to handle L3VPN <-> vrf popping */
+ mpls_label_t label[AFI_MAX];
+
/* MPLS static LSP config table */
struct hash *slsp_table;
*/
int advertise_gw_macip;
+ /* l3-vni info */
+ vni_t l3vni;
+
/* Route Installs */
uint64_t installs;
uint64_t removals;
struct zebra_vrf *zvrf);
extern struct route_table *
zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id, vrf_id_t vrf_id);
+extern int zebra_vrf_has_config(struct zebra_vrf *zvrf);
extern void zebra_vrf_init(void);
#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) {
case -2:
vty_out(vty,
"%% Cannot use reserved label(s) (%d-%d)\n",
- MPLS_MIN_RESERVED_LABEL,
- MPLS_MAX_RESERVED_LABEL);
+ MPLS_LABEL_RESERVED_MIN,
+ MPLS_LABEL_RESERVED_MAX);
break;
case -3:
vty_out(vty,
type = STATIC_IPV6_GATEWAY;
}
- if (!negate)
+ if (!negate) {
static_add_route(afi, safi, type, &p, src_p, gatep, ifname,
- bh_type, tag, distance, zvrf, &snh_label);
- else
+ bh_type, tag, distance, zvrf, nh_zvrf,
+ &snh_label);
+ /* Mark as having FRR configuration */
+ vrf_set_user_cfged(zvrf->vrf);
+ } else {
static_delete_route(afi, safi, type, &p, src_p, gatep, ifname,
tag, distance, zvrf, &snh_label);
+ /* If no other FRR config for this VRF, mark accordingly. */
+ if (!zebra_vrf_has_config(zvrf))
+ vrf_reset_user_cfged(zvrf->vrf);
+ }
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_name,
+ const char *label_str)
+{
+ struct zebra_vrf *zvrf;
+ struct vrf *vrf;
+
+ /* VRF id */
+ zvrf = zebra_vrf_lookup_by_name(vrf_name);
+
+ /* When trying to delete, the VRF must exist. */
+ if (negate && !zvrf) {
+ vty_out(vty, "%% vrf %s is not defined\n", vrf_name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* When trying to create, create the VRF if it doesn't exist.
+ * Note: The VRF isn't active until we hear about it from the kernel.
+ */
+ if (!zvrf) {
+ vrf = vrf_get(VRF_UNKNOWN, vrf_name);
+ if (!vrf) {
+ vty_out(vty, "%% Could not create vrf %s\n", vrf_name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ zvrf = vrf->info;
+ if (!zvrf) {
+ vty_out(vty, "%% Could not create vrf-info %s\n",
+ vrf_name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Mark as having FRR configuration */
+ vrf_set_user_cfged(vrf);
+ }
+ 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;
+
+ /*
+ * Coverity is complaining that prefix could
+ * be dereferenced, but we know that prefix will
+ * valid. Add an assert to make it happy
+ */
+ assert(prefix);
+ 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;
+ }
+
+ 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,
|(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;
+ }
+
+ 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);
}
/* 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) {
inet_ntoa(nexthop->gate.ipv4));
if (nexthop->ifindex)
vty_out(vty, ", via %s",
- ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ ifindex2ifname(
+ nexthop->ifindex,
+ nexthop->vrf_id));
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
buf, sizeof buf));
if (nexthop->ifindex)
vty_out(vty, ", via %s",
- ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ ifindex2ifname(
+ nexthop->ifindex,
+ nexthop->vrf_id));
break;
case NEXTHOP_TYPE_IFINDEX:
vty_out(vty, " directly connected, %s",
ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ nexthop->vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
vty_out(vty, " unreachable");
default:
break;
}
+
+ if (re->vrf_id != nexthop->vrf_id) {
+ struct vrf *vrf =
+ vrf_lookup_by_id(nexthop->vrf_id);
+
+ vty_out(vty, "(vrf %s)", vrf->name);
+ }
+
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
vty_out(vty, " (duplicate nexthop removed)");
nexthop->ifindex);
json_object_string_add(
json_nexthop, "interfaceName",
- ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ ifindex2ifname(
+ nexthop->ifindex,
+ nexthop->vrf_id));
}
break;
case NEXTHOP_TYPE_IPV6:
nexthop->ifindex);
json_object_string_add(
json_nexthop, "interfaceName",
- ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ ifindex2ifname(
+ nexthop->ifindex,
+ nexthop->vrf_id));
}
break;
json_object_string_add(
json_nexthop, "interfaceName",
ifindex2ifname(nexthop->ifindex,
- re->vrf_id));
+ nexthop->vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
json_object_boolean_true_add(json_nexthop,
break;
}
+ if (nexthop->vrf_id != re->vrf_id) {
+ struct vrf *vrf =
+ vrf_lookup_by_id(nexthop->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));
+ nexthop->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));
+ nexthop->vrf_id));
break;
case NEXTHOP_TYPE_IFINDEX:
vty_out(vty, " is directly connected, %s",
- ifindex2ifname(nexthop->ifindex, re->vrf_id));
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
vty_out(vty, " unreachable");
default:
break;
}
+
+ if (nexthop->vrf_id != re->vrf_id) {
+ struct vrf *vrf = vrf_lookup_by_id(nexthop->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;
+
+ /*
+ * Coverity is complaining that prefix could
+ * be dereferenced, but we know that prefix will
+ * valid. Add an assert to make it happy
+ */
+ assert(prefix);
+ 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;
+
+ 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_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;
+
+ 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_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;
+
+ 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_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;
+
+ 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_IP6, SAFI_UNICAST, no, prefix_str,
+ NULL, from_str, gate_str, ifname, NULL,
+ tag_str, distance_str, label);
}
/*
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));
else
vty_out(vty, "id %u table %u", zvrf_id(zvrf),
zvrf->table_id);
+ if (vrf_is_user_cfged(vrf))
+ vty_out(vty, " (configured)");
vty_out(vty, "\n");
}
return CMD_SUCCESS;
}
+DEFUN (default_vrf_vni_mapping,
+ default_vrf_vni_mapping_cmd,
+ "vni " CMD_VNI_RANGE,
+ "VNI corresponding to the DEFAULT VRF\n"
+ "VNI-ID\n")
+{
+ int ret = 0;
+ char err[ERR_STR_SZ];
+ struct zebra_vrf *zvrf = NULL;
+ vni_t vni = strtoul(argv[1]->arg, NULL, 10);
+
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ if (!zvrf)
+ return CMD_WARNING;
+
+ 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_default_vrf_vni_mapping,
+ no_default_vrf_vni_mapping_cmd,
+ "no vni " CMD_VNI_RANGE,
+ NO_STR
+ "VNI corresponding to DEFAULT VRF\n"
+ "VNI-ID")
+{
+ int ret = 0;
+ char err[ERR_STR_SZ];
+ vni_t vni = strtoul(argv[2]->arg, NULL, 10);
+ struct zebra_vrf *zvrf = NULL;
+
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ if (!zvrf)
+ return CMD_WARNING;
+
+ 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;
+}
+
+DEFUN (vrf_vni_mapping,
+ vrf_vni_mapping_cmd,
+ "vni " CMD_VNI_RANGE,
+ "VNI corresponding to tenant VRF\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);
+
+ /* Mark as having FRR configuration */
+ vrf_set_user_cfged(vrf);
+ 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 corresponding to tenant VRF\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;
+ }
+
+ /* If no other FRR config for this VRF, mark accordingly. */
+ if (!zebra_vrf_has_config(zvrf))
+ vrf_reset_user_cfged(vrf);
+
+ 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();
+ }
+
+ if (!uj)
+ vty_out(vty, "%-37s %-10s %-20s %-20s %-5s %-18s\n",
+ "VRF", "VNI", "VxLAN IF", "L3-SVI", "State", "Rmac");
+
+ RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+ zvrf = vrf->info;
+ if (!zvrf)
+ continue;
+
+ zebra_vxlan_print_vrf_vni(vty, zvrf, json_vrfs);
+ }
+
+ 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_global,
+ show_evpn_global_cmd,
+ "show evpn [json]",
+ SHOW_STR
+ "EVPN\n"
+ JSON_STR)
+{
+ u_char uj = use_json(argc, argv);
+
+ zebra_vxlan_print_evpn(vty, uj);
+ return CMD_SUCCESS;
+}
+
DEFUN (show_evpn_vni,
show_evpn_vni_cmd,
"show evpn vni [json]",
return CMD_SUCCESS;
}
+DEFUN (show_evpn_rmac_vni_mac,
+ show_evpn_rmac_vni_mac_cmd,
+ "show evpn rmac vni " 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_vni,
+ show_evpn_rmac_vni_cmd,
+ "show evpn rmac vni " 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_vni_all,
+ show_evpn_rmac_vni_all_cmd,
+ "show evpn rmac vni 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_vni_ip,
+ show_evpn_nh_vni_ip_cmd,
+ "show evpn next-hops vni " 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_vni,
+ show_evpn_nh_vni_cmd,
+ "show evpn next-hops vni " 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_vni_all,
+ show_evpn_nh_vni_all_cmd,
+ "show evpn next-hops vni 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;
}
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);
/* Commands for VRF */
install_element(VIEW_NODE, &show_ipv6_mroute_vrf_all_cmd);
+ install_element(VIEW_NODE, &show_evpn_global_cmd);
install_element(VIEW_NODE, &show_evpn_vni_cmd);
install_element(VIEW_NODE, &show_evpn_vni_vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_rmac_vni_mac_cmd);
+ install_element(VIEW_NODE, &show_evpn_rmac_vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_rmac_vni_all_cmd);
+ install_element(VIEW_NODE, &show_evpn_nh_vni_ip_cmd);
+ install_element(VIEW_NODE, &show_evpn_nh_vni_cmd);
+ install_element(VIEW_NODE, &show_evpn_nh_vni_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, &default_vrf_vni_mapping_cmd);
+ install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd);
+ install_element(VRF_NODE, &vrf_vni_mapping_cmd);
+ install_element(VRF_NODE, &no_vrf_vni_mapping_cmd);
+
}
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_memory.h"
#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 function declarations */
+static int ip_prefix_send_to_client(vrf_id_t vrf_id,
+ struct prefix *p,
+ uint16_t cmd);
static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json);
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 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);
: "Inactive");
}
}
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) {
+ if (!json)
+ vty_out(vty, " Default-gateway");
+ else
+ json_object_boolean_true_add(json, "defaultGateway");
+ }
if (json == NULL)
vty_out(vty, "\n");
}
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, " Refcount: %d\n", listcount(n->host_list));
+ vty_out(vty, " Prefixes:\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, "routerMac",
+ prefix_mac2str(&n->emac, buf2,
+ sizeof(buf2)));
+ json_object_int_add(json, "refCount", listcount(n->host_list));
+ 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, "prefixList", 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, " Refcount: %d\n", listcount(zrmac->host_list));
+ vty_out(vty, " Prefixes:\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, "routerMac",
+ prefix_mac2str(&zrmac->macaddr,
+ buf1,
+ sizeof(buf1)));
+ json_object_string_add(json, "vtepIp",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ json_object_int_add(json, "refCount",
+ listcount(zrmac->host_list));
+ 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, "prefixList", json_hosts);
+ }
+}
+
/*
* Print a specific MAC entry.
*/
vty_out(vty, " Auto Mac ");
}
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
+ vty_out(vty, " Sticky Mac ");
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW))
+ vty_out(vty, " Default-gateway Mac ");
+
vty_out(vty, "\n");
/* print all the associated neigh */
vty_out(vty, " Neighbors:\n");
}
}
+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\n",
+ ipaddr2str(&(n->ip), buf2, sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
+ } else {
+ json_object_string_add(json_nh, "nexthopIp",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ json_object_string_add(json_nh, "routerMac",
+ prefix_mac2str(&n->emac, buf1,
+ sizeof(buf1)));
+ 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\n", "IP", "RMAC");
+ } else
+ json_object_int_add(json_vni, "numNextHops", 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 #RMACs %u\n\n",
+ zl3vni->vni, num_rmacs);
+ vty_out(vty, "%-17s %-21s\n", "RMAC", "Remote VTEP");
+ } 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\n",
+ prefix_mac2str(&zrmac->macaddr, buf, sizeof(buf)),
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ } else {
+ json_object_string_add(json_rmac, "routerMac",
+ prefix_mac2str(&zrmac->macaddr, buf,
+ sizeof(buf)));
+ json_object_string_add(json_rmac, "vtepIp",
+ inet_ntoa(zrmac->fwd_info.r_vtep_ip));
+ 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, " Type: %s\n", "L3");
+ vty_out(vty, " Tenant VRF: %s\n",
+ zl3vni_vrf_name(zl3vni));
+ vty_out(vty, " Local Vtep Ip: %s\n",
+ 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, " Router MAC: %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, "type", "L3");
+ json_object_string_add(json, "localVtepIp",
+ inet_ntoa(zl3vni->local_vtep_ip));
+ json_object_string_add(json, "vxlanIntf",
+ zl3vni_vxlan_if_name(zl3vni));
+ json_object_string_add(json, "sviIntf",
+ 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, "routerMac",
+ 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, "l2Vnis", 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, " Type: %s\n", "L2");
+ vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zvni->vrf_id));
+ } else {
json_object_int_add(json, "vni", zvni->vni);
+ json_object_string_add(json, "type", "L2");
+ json_object_string_add(json, "vrf",
+ vrf_id_to_name(zvni->vrf_id));
+ }
if (!zvni->vxlan_if) { // unexpected
if (json == NULL)
}
num_macs = num_valid_macs(zvni);
num_neigh = hashcount(zvni->neigh_table);
- if (json == NULL)
- vty_out(vty, " VxLAN interface: %s ifIndex: %u VTEP IP: %s\n",
- zvni->vxlan_if->name, zvni->vxlan_if->ifindex,
+ if (json == NULL) {
+ vty_out(vty, " VxLAN interface: %s\n",
+ zvni->vxlan_if->name);
+ vty_out(vty, " VxLAN ifIndex: %u\n", zvni->vxlan_if->ifindex);
+ vty_out(vty," Local VTEP IP: %s\n",
inet_ntoa(zvni->local_vtep_ip));
- else {
+ } else {
json_object_string_add(json, "vxlanInterface",
zvni->vxlan_if->name);
json_object_int_add(json, "ifindex", zvni->vxlan_if->ifindex);
}
}
+/* print a L3 VNI hash entry */
+static void zl3vni_print_hash(struct hash_backet *backet,
+ void *ctx[])
+{
+ 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 %-4s %-21s %-8lu %-8lu %-15s %-37s\n",
+ zl3vni->vni, "L3",
+ zl3vni_vxlan_if_name(zl3vni),
+ hashcount(zl3vni->rmac_table),
+ hashcount(zl3vni->nh_table),
+ "n/a",
+ zl3vni_vrf_name(zl3vni));
+ } 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, "vxlanIf",
+ zl3vni_vxlan_if_name(zl3vni));
+ json_object_int_add(json_vni, "numMacs",
+ hashcount(zl3vni->rmac_table));
+ json_object_int_add(json_vni, "numArpNd",
+ hashcount(zl3vni->nh_table));
+ json_object_string_add(json_vni, "numRemoteVteps", "n/a");
+ json_object_string_add(json_vni, "type", "L3");
+ json_object_string_add(json_vni, "tenantVrf",
+ zl3vni_vrf_name(zl3vni));
+ 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 %-4s %-21s %-8u %-8u %-15u %-37s\n",
+ zvni->vni, "L2",
zvni->vxlan_if ? zvni->vxlan_if->name : "unknown",
- inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh,
- num_vteps);
+ num_macs, num_neigh, 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);
json_vni = json_object_new_object();
+ json_object_int_add(json_vni, "vni", zvni->vni);
+ json_object_string_add(json_vni, "type", "L2");
json_object_string_add(json_vni, "vxlanIf",
zvni->vxlan_if ? zvni->vxlan_if->name
: "unknown");
- json_object_string_add(json_vni, "vtepIp",
- inet_ntoa(zvni->local_vtep_ip));
json_object_int_add(json_vni, "numMacs", num_macs);
json_object_int_add(json_vni, "numArpNd", num_neigh);
json_object_int_add(json_vni, "numRemoteVteps", num_vteps);
+ json_object_string_add(json_vni, "tenantVrf",
+ vrf_id_to_name(zvni->vrf_id));
if (num_vteps) {
json_vtep_list = json_object_new_array();
for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) {
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,
ZEBRA_NEIGH_SET_ACTIVE(n);
zvni_neigh_send_add_to_client(
- zvni->vni, &n->ip, &n->emac, 0);
+ zvni->vni, &n->ip, &n->emac, n->flags);
} else {
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
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_neigh_send_add_to_client(vni_t vni,
struct ipaddr *ip,
- struct ethaddr *macaddr, u_char flags)
+ struct ethaddr *macaddr,
+ u_char neigh_flags)
{
+ u_char flags = 0;
+
+ if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+
return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags,
ZEBRA_MACIP_ADD);
}
zvni_gw_macip_add(ifp, zvni, &macaddr, &ip);
}
+ return 0;
+}
+
+static int zvni_advertise_subnet(zebra_vni_t *zvni,
+ struct interface *ifp,
+ int advertise)
+{
+ struct listnode *cnode = NULL, *cnnode = NULL;
+ struct connected *c = NULL;
+ struct ethaddr macaddr;
+
+ memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) {
+ struct prefix p;
+
+ memcpy(&p, c->address, sizeof(struct prefix));
+
+ /* skip link local address */
+ if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6))
+ continue;
+
+ apply_mask(&p);
+ if (advertise)
+ ip_prefix_send_to_client(ifp->vrf_id, &p,
+ ZEBRA_IP_PREFIX_ROUTE_ADD);
+ else
+ ip_prefix_send_to_client(ifp->vrf_id, &p,
+ ZEBRA_IP_PREFIX_ROUTE_DEL);
+ }
return 0;
}
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)
/* Set "local" forwarding info. */
SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW);
memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
mac->fwd_info.local.ifindex = ifp->ifindex;
mac->fwd_info.local.vid = vxl->access_vlan;
/* Set "local" forwarding info. */
SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW);
memcpy(&n->emac, macaddr, ETH_ALEN);
n->ifindex = ifp->ifindex;
+ /* Only advertise in BGP if the knob is enabled */
+ if (!advertise_gw_macip_enabled(zvni))
+ return 0;
+
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)));
zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
- ZEBRA_MAC_TYPE_GW);
+ n->flags);
return 0;
}
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 (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL))
return -1;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "SVI %s(%u) 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)));
-
- /* Remove neighbor from BGP. */
- zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac,
- ZEBRA_MAC_TYPE_GW);
+ /* only need to delete the entry from bgp if we sent it before */
+ if (advertise_gw_macip_enabled(zvni)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP",
+ ifp->vrf_id, ifp->name,
+ ifp->ifindex, zvni->vni,
+ prefix_mac2str(&(n->emac),
+ NULL,
+ ETHER_ADDR_STRLEN),
+ ipaddr2str(ip, buf2, sizeof(buf2)));
+
+ /* Remove neighbor from BGP. */
+ zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac,
+ ZEBRA_MACIP_TYPE_GW);
+ }
/* Delete this neighbor entry. */
zvni_neigh_del(zvni, n);
if (!zvni)
return;
- if (!advertise_gw_macip_enabled(zvni))
- return;
-
ifp = zvni->vxlan_if;
if (!ifp)
return;
{
struct mac_walk_ctx *wctx = arg;
zebra_mac_t *mac = backet->data;
- u_char sticky = 0;
if (((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_LOCAL))
|| ((wctx->flags & DEL_REMOTE_MAC)
&& IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip,
&wctx->r_vtep_ip))) {
if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) {
- sticky = CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1
- : 0;
zvni_mac_send_del_to_client(
wctx->zvni->vni, &mac->macaddr,
- (sticky ? ZEBRA_MAC_TYPE_STICKY : 0));
+ mac->flags);
}
if (wctx->uninstall)
* Inform BGP about local MAC addition.
*/
static int zvni_mac_send_add_to_client(vni_t vni,
- struct ethaddr *macaddr, u_char flags)
+ struct ethaddr *macaddr,
+ u_char mac_flags)
{
+ u_char flags = 0;
+
+ if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
+ if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+
return zvni_macip_send_msg_to_client(vni, macaddr, NULL, flags,
ZEBRA_MACIP_ADD);
}
* Inform BGP about local MAC deletion.
*/
static int zvni_mac_send_del_to_client(vni_t vni,
- struct ethaddr *macaddr, u_char flags)
+ struct ethaddr *macaddr,
+ u_char mac_flags)
{
+ u_char flags = 0;
+
+ if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
+ if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW))
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+
return zvni_macip_send_msg_to_client(vni, macaddr, NULL, flags,
ZEBRA_MACIP_DEL);
}
* 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;
vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);
if (vlan_if) {
- if (advertise_gw_macip_enabled(zvni)) {
- /* Add SVI MAC-IP */
- zvni_add_macip_for_intf(vlan_if, zvni);
+ /* Add SVI MAC-IP */
+ zvni_add_macip_for_intf(vlan_if, zvni);
- /* Add VRR MAC-IP - if any*/
- vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
- if (vrr_if)
- zvni_add_macip_for_intf(vrr_if, zvni);
- }
+ /* Add VRR MAC-IP - if any*/
+ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
+ if (vrr_if)
+ zvni_add_macip_for_intf(vrr_if, zvni);
neigh_read_for_vlan(zns, vlan_if);
}
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;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
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));
+ /* L3-VNI and L2-VNI are handled seperately */
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
- /* 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;
- }
+ /* associate with vxlan_if */
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ zl3vni->vxlan_if = ifp;
- zvni->local_vtep_ip = vxl->vtep_ip;
- zvni->vxlan_if = ifp;
+ /*
+ * 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);
- /* 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);
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+
+ } else {
+ 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);
+ }
}
}
/*
* Cleanup VNI/VTEP and update kernel
*/
-static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf)
+static void zvni_cleanup_all(struct hash_backet *backet, void *arg)
{
- zebra_vni_t *zvni;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_vrf *zvrf = (struct zebra_vrf *)arg;
zvni = (zebra_vni_t *)backet->data;
if (!zvni)
return;
+ /* remove from l3-vni list */
+ if (zvrf->l3vni)
+ zl3vni = zl3vni_lookup(zvrf->l3vni);
+ 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);
- /* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 1);
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all(zvni, 1);
+
+ /* Delete the hash entry. */
+ zvni_del(zvni);
+}
+
+/* cleanup L3VNI */
+static void zl3vni_cleanup_all(struct hash_backet *backet,
+ void *args)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = (zebra_l3vni_t *)backet->data;
+ if (!zl3vni)
+ return;
+
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+}
+
+static int is_host_present_in_host_list(struct list *list,
+ struct prefix *host)
+{
+ struct listnode *node = NULL;
+ struct prefix *p = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(list, node, p)) {
+ if (prefix_same(p, host))
+ return 1;
+ }
+ return 0;
+}
+
+static void host_list_add_host(struct list *list,
+ struct prefix *host)
+{
+ struct prefix *p = NULL;
+
+ p = XCALLOC(MTYPE_HOST_PREFIX, sizeof(struct prefix));
+ memcpy(p, host, sizeof(struct prefix));
+
+ 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 (!(n->flags & ZEBRA_NEIGH_REMOTE) ||
+ !(n->flags & ZEBRA_NEIGH_REMOTE_NH))
+ return 0;
+
+ if (!zl3vni->svi_if || !if_is_operative(zl3vni->svi_if))
+ 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 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)
+ return NULL;
+
+ 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 (!zl3vni)
+ return;
+
+ 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 (!zl3vni)
+ return;
+
+ 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);
+}
+
+static int ip_prefix_send_to_client(vrf_id_t vrf_id,
+ struct prefix *p,
+ uint16_t cmd)
+{
+ struct zserv *client = NULL;
+ struct stream *s = NULL;
+ char buf[PREFIX_STRLEN];
+
+ 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, cmd, vrf_id);
+ stream_put(s, p, sizeof(struct prefix));
+
+ /* Write packet size. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "Send ip prefix %s %s on vrf %s",
+ prefix2str(p, buf, sizeof(buf)),
+ (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL",
+ vrf_id_to_name(vrf_id));
+
+ if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD)
+ client->prefixadd_cnt++;
+ else
+ client->prefixdel_cnt++;
+
+ return zebra_server_send_message(client);
+}
+
+/* re-add remote rmac if needed */
+static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni,
+ struct ethaddr *rmac)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ zebra_mac_t *zrmac = NULL;
+
+ zrmac = zl3vni_rmac_lookup(zl3vni, rmac);
+ if (!zrmac)
+ return 0;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Del remote RMAC %s L3VNI %u - readd",
+ prefix_mac2str(rmac, buf, sizeof(buf)),
+ zl3vni->vni);
+
+ zl3vni_rmac_install(zl3vni, zrmac);
+ return 0;
+}
+
+/* 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\n", "MAC", "Remote VTEP");
+ } 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;
+ }
- /* Delete the hash entry. */
- zvni_del(zvni);
+ 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;
-/* Public functions */
+ 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\n", "IP", "RMAC");
+ } else
+ json_object_int_add(json, "numNextHops", 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);
+ }
+}
+
+void zebra_vxlan_print_vrf_vni(struct vty *vty, struct zebra_vrf *zvrf,
+ json_object *json_vrfs)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_lookup(zvrf->l3vni);
+ if (!zl3vni)
+ return;
+
+ if (!json_vrfs) {
+ vty_out(vty, "%-37s %-10u %-20s %-20s %-5s %-18s\n",
+ zvrf_name(zvrf),
+ zl3vni->vni,
+ zl3vni_vxlan_if_name(zl3vni),
+ zl3vni_svi_if_name(zl3vni),
+ zl3vni_state2str(zl3vni),
+ zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
+ } 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, "vni", zl3vni->vni);
+ json_object_string_add(json_vrf, "vxlanIntf",
+ zl3vni_vxlan_if_name(zl3vni));
+ json_object_string_add(json_vrf, "sviIntf",
+ zl3vni_svi_if_name(zl3vni));
+ json_object_string_add(json_vrf, "state",
+ zl3vni_state2str(zl3vni));
+ json_object_string_add(json_vrf, "routerMac",
+ zl3vni_rmac2str(zl3vni, buf,
+ sizeof(buf)));
+ json_object_array_add(json_vrfs, json_vrf);
+ }
+}
/*
* Display Neighbors for a VNI (VTY command handler).
void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni,
u_char use_json)
{
- zebra_vni_t *zvni;
json_object *json = NULL;
void *args[2];
+ zebra_l3vni_t *zl3vni = NULL;
+ zebra_vni_t *zvni = 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;
- }
+
if (use_json)
json = json_object_new_object();
args[0] = vty;
args[1] = json;
- zvni_print(zvni, (void *)args);
+
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
+ zl3vni_print(zl3vni, (void *)args);
+ } else {
+ 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;
+ }
+
+ zvni_print(zvni, (void *)args);
+ }
+
if (use_json) {
vty_out(vty, "%s\n", json_object_to_json_string_ext(
json, JSON_C_TO_STRING_PRETTY));
}
}
-/*
- * Display VNI hash table (VTY command handler).
- */
-void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
- u_char use_json)
+/* Display all global details for EVPN */
+void zebra_vxlan_print_evpn(struct vty *vty, u_char uj)
{
- u_int32_t num_vnis;
+ int num_l2vnis = 0;
+ int num_l3vnis = 0;
+ int num_vnis = 0;
json_object *json = NULL;
- void *args[2];
+ struct zebra_ns *zns = NULL;
+ struct zebra_vrf *zvrf = NULL;
if (!is_evpn_enabled())
return;
- num_vnis = hashcount(zvrf->vni_table);
- if (!num_vnis) {
- if (use_json)
- vty_out(vty, "{}\n");
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns)
return;
- }
- if (use_json) {
+
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ if (!zvrf)
+ return;
+
+ num_l3vnis = hashcount(zns->l3vni_table);
+ num_l2vnis = hashcount(zvrf->vni_table);
+ num_vnis = num_l2vnis + num_l3vnis;
+
+ if (uj) {
json = json_object_new_object();
json_object_string_add(json, "advertiseGatewayMacip",
zvrf->advertise_gw_macip ? "Yes" : "No");
json_object_int_add(json, "numVnis", num_vnis);
+ json_object_int_add(json, "numL2Vnis", num_l2vnis);
+ json_object_int_add(json, "numL3Vnis", num_l3vnis);
} else {
+ vty_out(vty, "L2 VNIs: %u\n", num_l2vnis);
+ vty_out(vty, "L3 VNIs: %u\n", num_l3vnis);
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",
- "VxLAN IF", "VTEP IP", "# MACs", "# ARPs",
- "# Remote VTEPs");
}
+
+ if (uj) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+}
+
+/*
+ * Display VNI hash table (VTY command handler).
+ */
+void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
+ u_char use_json)
+{
+ json_object *json = NULL;
+ struct zebra_ns *zns = NULL;
+ void *args[2];
+
+ if (!is_evpn_enabled())
+ return;
+
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if (!zns)
+ return;
+
+
+ if (use_json)
+ json = json_object_new_object();
+ else
+ vty_out(vty,
+ "%-10s %-4s %-21s %-8s %-8s %-15s %-37s\n",
+ "VNI", "Type", "VxLAN IF", "# MACs",
+ "# ARPs", "# Remote VTEPs", "Tenant VRF");
+
args[0] = vty;
args[1] = json;
+ /* Display all L2-VNIs */
hash_iterate(zvrf->vni_table,
(void (*)(struct hash_backet *, void *))zvni_print_hash,
args);
+ /* Display all L3-VNIs */
+ 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));
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);
+
+ return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, n->flags);
}
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) {
if (!mac && !n)
continue;
+ /* Ignore the delete if this mac is a gateway mac-ip */
+ if (mac && CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) &&
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) {
+ zlog_err("%u: Ignore Del for MAC %s neigh %s on VNI %u as it is configured as a default gateway",
+ zvrf_id(zvrf),
+ prefix_mac2str(&macaddr, buf, sizeof(buf)),
+ ipaddr2str(&ip, buf1, sizeof(buf1)),
+ vni);
+ continue;
+ }
+
/* Uninstall remote neighbor or MAC. */
if (n) {
/* When the MAC changes for an IP, it is possible the
int update_mac = 0, update_neigh = 0;
char buf[ETHER_ADDR_STRLEN];
char buf1[INET6_ADDRSTRLEN];
- u_char sticky;
+ u_char sticky = 0;
+ u_char flags = 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));
+
if (!EVPN_ENABLED(zvrf)) {
zlog_warn("%s: EVPN Not turned on yet we have received a remote_macip add zapi callback",
__PRETTY_FUNCTION__);
STREAM_GET(&vtep_ip.s_addr, s, IPV4_MAX_BYTELEN);
l += IPV4_MAX_BYTELEN;
- /* Get 'sticky' flag. */
- STREAM_GETC(s, sticky);
+ /* Get flags - sticky mac and/or gateway mac */
+ flags = stream_getc(s);
+ sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
l++;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
- "Recv MACIP Add %sMAC %s IP %s VNI %u Remote VTEP %s from %s",
- sticky ? "sticky " : "",
+ "Recv MACIP Add MAC %s IP %s VNI %u Remote VTEP %s with flags 0x%x from %s",
prefix_mac2str(&macaddr, buf, sizeof(buf)),
ipaddr2str(&ip, buf1, sizeof(buf1)), vni,
- inet_ntoa(vtep_ip),
+ inet_ntoa(vtep_ip), flags,
zebra_route_string(client->proto));
/* Locate VNI hash entry - expected to exist. */
zvni_vtep_install(zvni, &vtep_ip);
}
- /* First, check if the remote MAC is unknown or has a change. If
- * so,
- * that needs to be updated first. Note that client could
- * install
- * MAC and MACIP separately or just install the latter.
- */
mac = zvni_mac_lookup(zvni, &macaddr);
+
+ /* Ignore the update if the mac is already present
+ as a gateway mac */
+ if (mac && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW) &&
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("%u:Ignore MAC %s IP %s on VNI %u as MAC is already configured as gateway mac",
+ zvrf_id(zvrf),
+ prefix_mac2str(&macaddr,
+ buf, sizeof(buf)),
+ ipaddr2str(&ip, buf1,
+ sizeof(buf1)), vni);
+ continue;
+ }
+
+ /* check if the remote MAC is unknown or has a change.
+ * If so, that needs to be updated first. Note that client could
+ * install MAC and MACIP separately or just install the latter.
+ */
if (!mac || !CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)
|| (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1 : 0)
!= sticky
zebra_vni_t *zvni;
zebra_mac_t *mac;
char buf[ETHER_ADDR_STRLEN];
- u_char sticky;
zif = ifp->info;
assert(zif);
ifp->name, ifp->ifindex, vni);
/* Remove MAC from BGP. */
- sticky = CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1 : 0;
- zvni_mac_send_del_to_client(zvni->vni, macaddr,
- (sticky ? ZEBRA_MAC_TYPE_STICKY : 0));
+ zvni_mac_send_del_to_client(zvni->vni, macaddr, mac->flags);
/*
* If there are no neigh associated with the mac delete the mac
struct interface *br_if,
struct ethaddr *macaddr, vlanid_t vid)
{
- struct zebra_if *zif;
- struct zebra_l2info_vxlan *vxl;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
vni_t vni;
- zebra_vni_t *zvni;
- zebra_mac_t *mac;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+ zebra_mac_t *mac = NULL;
char buf[ETHER_ADDR_STRLEN];
zif = ifp->info;
if (!is_evpn_enabled())
return 0;
+ /* check if this is a remote RMAC and readd simillar to remote macs */
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni)
+ return zebra_vxlan_readd_remote_rmac(zl3vni, macaddr);
+
/* Locate hash entry; it is expected to exist. */
zvni = zvni_lookup(vni);
if (!zvni)
zebra_vni_t *zvni;
zebra_mac_t *mac;
char buf[ETHER_ADDR_STRLEN];
- u_char sticky;
/* We are interested in MACs only on ports or (port, VLAN) that
* map to a VNI.
return 0;
/* Remove MAC from BGP. */
- sticky = CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1 : 0;
- zvni_mac_send_del_to_client(zvni->vni, macaddr,
- (sticky ? ZEBRA_MAC_TYPE_STICKY : 0));
+ zvni_mac_send_del_to_client(zvni->vni, macaddr, mac->flags);
/* Update all the neigh entries associated with this mac */
zvni_process_neigh_on_local_mac_del(zvni, mac);
if (add) {
zvni_process_neigh_on_local_mac_add(zvni, mac);
return zvni_mac_send_add_to_client(zvni->vni, macaddr,
- sticky);
+ mac->flags);
}
return 0;
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)
}
- /* check if we are advertising gw macip routes */
- if (!advertise_gw_macip_enabled(zvni))
- return 0;
-
memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
if (p->family == AF_INET) {
}
/*
- * 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 = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_from_svi(ifp, link_if);
+ if (zl3vni) {
+
+ /* associate with svi */
+ zl3vni->svi_if = ifp;
+
+ /* 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));
+
+ /* 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
+ */
+int zebra_vxlan_if_down(struct interface *ifp)
+{
+ vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
zebra_vni_t *zvni;
- struct neigh_walk_ctx n_wctx;
- zvni = zvni_map_svi(ifp, link_if);
- if (!zvni)
+ /* Check if EVPN is enabled. */
+ if (!is_evpn_enabled())
return 0;
- if (!zvni->vxlan_if) {
- zlog_err("VNI %u hash %p doesn't have intf upon SVI up",
- zvni->vni, zvni);
- return -1;
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
+ /* process-if-down for l3-vni */
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L3-VNI %u is DOWN",
+ ifp->name, ifp->ifindex, vni);
+
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ } else {
+ /* process if-down for l2-vni */
+ 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;
+}
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("SVI %s(%u) VNI %u is UP, installing neighbors",
- ifp->name, ifp->ifindex, zvni->vni);
+/*
+ * Handle VxLAN interface up - update BGP if required.
+ */
+int zebra_vxlan_if_up(struct interface *ifp)
+{
+ vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* Check if EVPN is enabled. */
+ if (!is_evpn_enabled())
+ return 0;
+
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Intf %s(%u) L3-VNI %u is UP",
+ ifp->name, ifp->ifindex, vni);
- /* 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);
+ /* 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 */
+ 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;
+}
+
+/*
+ * Handle VxLAN interface delete. Locate and remove entry in hash table
+ * and update BGP, if required.
+ */
+int zebra_vxlan_if_del(struct interface *ifp)
+{
+ vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* Check if EVPN is enabled. */
+ if (!is_evpn_enabled())
+ return 0;
+
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Del L3-VNI %u intf %s(%u)",
+ vni, ifp->name, ifp->ifindex);
+
+ /* process oper-down for l3-vni */
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ /* 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*/
+ 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;
+}
+
+/*
+ * Handle VxLAN interface update - change to tunnel IP, master or VLAN.
+ */
+int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags)
+{
+ vni_t vni;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* Check if EVPN is enabled. */
+ if (!is_evpn_enabled())
+ return 0;
+
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
+
+ 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;
+ }
+
+ /* access-vlan change - process oper down, associate with new
+ * svi_if and then process oper up again
+ */
+ 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);
+ }
+ }
+
+ /* 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 {
+
+ /* 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;
+ }
+
+ 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;
+ }
+
+ /* 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->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
+
+ /* 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;
+
+ /* 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;
}
/*
- * Handle VxLAN interface down - update BGP if required, and do
- * internal cleanup.
+ * Handle VxLAN interface add.
*/
-int zebra_vxlan_if_down(struct interface *ifp)
+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;
+ zebra_vni_t *zvni = NULL;
+ zebra_l3vni_t *zl3vni = 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;
- }
-
- assert(zvni->vxlan_if == ifp);
-
- /* Delete this VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
- /* 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);
+ /* process if-add for l3-vni*/
+ 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);
- /* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 1);
+ /* associate with vxlan_if */
+ zl3vni->local_vtep_ip = vxl->vtep_ip;
+ zl3vni->vxlan_if = ifp;
- return 0;
-}
+ /* 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);
-/*
- * Handle VxLAN interface up - update BGP if required.
- */
-int zebra_vxlan_if_up(struct interface *ifp)
-{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
- vni_t vni;
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ } else {
- /* Check if EVPN is enabled. */
- if (!is_evpn_enabled())
- return 0;
+ /* process if-add for l2-vni */
+ struct interface *vlan_if = NULL;
- zif = ifp->info;
- assert(zif);
- vxl = &zif->l2info.vxl;
- vni = vxl->vni;
+ /* 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;
+ }
+ }
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Intf %s(%u) VNI %u is UP",
- ifp->name, ifp->ifindex, vni);
+ 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);
+ }
- /* 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;
- }
+ 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);
- assert(zvni->vxlan_if == ifp);
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
+ return 0;
- /* If part of a bridge, inform BGP about this VNI. */
- /* Also, read and populate local MACs and neighbors. */
- if (zif->brslave_info.br_if) {
+ /* Inform BGP */
zvni_send_add_to_client(zvni);
+
+ /* Read and populate local MACs and neighbors */
zvni_read_mac_neigh(zvni, ifp);
}
return 0;
}
-/*
- * Handle VxLAN interface delete. Locate and remove entry in hash table
- * and update BGP, if required.
- */
-int zebra_vxlan_if_del(struct interface *ifp)
+int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
+ vni_t vni,
+ char *err, int err_str_sz,
+ int add)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
- vni_t vni;
-
- /* Check if EVPN is enabled. */
- if (!is_evpn_enabled())
- return 0;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_vrf *zvrf_default = NULL;
- zif = ifp->info;
- assert(zif);
- vxl = &zif->l2info.vxl;
- vni = vxl->vni;
+ zvrf_default = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+ if (!zvrf_default)
+ return -1;
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Del VNI %u intf %s(%u)",
- vni, ifp->name, ifp->ifindex);
+ zlog_debug("vrf %s vni %u %s",
+ zvrf_name(zvrf),
+ vni,
+ add ? "ADD" : "DEL");
- /* 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;
- }
+ if (add) {
- /* Delete VNI from BGP. */
- zvni_send_del_to_client(zvni->vni);
+ zebra_vxlan_handle_vni_transition(zvrf, vni, add);
- /* 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);
+ /* 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;
+ }
- /* Free up all remote VTEPs, if any. */
- zvni_vtep_del_all(zvni, 0);
+ /* 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;
+ }
- /* 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;
- }
+ /* 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;
+ }
- return 0;
-}
+ /* associate the vrf with vni */
+ zvrf->l3vni = vni;
-/*
- * Handle VxLAN interface update - change to tunnel IP, master or VLAN.
- */
-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;
+ /* associate with vxlan-intf;
+ * we need to associate with the vxlan-intf first
+ */
+ zl3vni->vxlan_if = zl3vni_map_to_vxlan_if(zl3vni);
- /* Check if EVPN is enabled. */
- if (!is_evpn_enabled())
- return 0;
+ /* 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);
- zif = ifp->info;
- assert(zif);
- vxl = &zif->l2info.vxl;
- vni = vxl->vni;
+ /* formulate l2vni list */
+ hash_iterate(zvrf_default->vni_table,
+ zvni_add_to_l3vni_list, zl3vni);
- /* 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_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
- 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;
- }
+ } else {
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni) {
+ snprintf(err, err_str_sz, "VNI doesn't exist");
+ return -1;
+ }
- /* Handle other changes. */
- if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) {
- /* Remove all existing local neighbors 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);
+ 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_enable(struct zebra_vrf *zvrf)
+{
+ zebra_l3vni_t *zl3vni = 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)
+ if (zvrf->l3vni)
+ zl3vni = zl3vni_lookup(zvrf->l3vni);
+ if (!zl3vni)
return 0;
- /* 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);
+ zl3vni->vrf_id = zvrf_id(zvrf);
+ if (is_l3vni_oper_up(zl3vni))
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ 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;
+int zebra_vxlan_vrf_disable(struct zebra_vrf *zvrf)
+{
+ zebra_l3vni_t *zl3vni = NULL;
- zvni_read_mac_neigh(zvni, ifp);
+ if (zvrf->l3vni)
+ zl3vni = zl3vni_lookup(zvrf->l3vni);
+ if (!zl3vni)
+ return 0;
- memset(&m_wctx, 0, sizeof(struct mac_walk_ctx));
- m_wctx.zvni = zvni;
- hash_iterate(zvni->mac_table, zvni_install_mac_hash, &m_wctx);
+ zl3vni->vrf_id = VRF_UNKNOWN;
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ 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);
- }
+int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+ vni_t vni;
+
+ if (zvrf->l3vni)
+ zl3vni = zl3vni_lookup(zvrf->l3vni);
+ if (!zl3vni)
+ return 0;
+
+ vni = zl3vni->vni;
+ zl3vni_del(zl3vni);
+ zebra_vxlan_handle_vni_transition(zvrf, vni, 0);
return 0;
}
/*
- * Handle VxLAN interface add.
+ * Handle message from client to enable/disable advertisement of g/w macip
+ * routes
*/
-int zebra_vxlan_if_add(struct interface *ifp)
+int zebra_vxlan_advertise_subnet(struct zserv *client, u_short length,
+ struct zebra_vrf *zvrf)
{
- struct zebra_if *zif;
- zebra_vni_t *zvni;
- struct zebra_l2info_vxlan *vxl;
- vni_t vni;
+ struct stream *s;
+ int advertise;
+ vni_t vni = 0;
+ zebra_vni_t *zvni = NULL;
+ struct interface *ifp = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan zl2_info;
+ struct interface *vlan_if = NULL;
- /* Check if EVPN is enabled. */
- if (!is_evpn_enabled())
+ if (zvrf_id(zvrf) != VRF_DEFAULT) {
+ zlog_err("EVPN GW-MACIP Adv for non-default VRF %u",
+ zvrf_id(zvrf));
+ return -1;
+ }
+
+ s = client->ibuf;
+ advertise = stream_getc(s);
+ vni = stream_get3(s);
+
+ zvni = zvni_lookup(vni);
+ if (!zvni)
return 0;
- zif = ifp->info;
- assert(zif);
- vxl = &zif->l2info.vxl;
- vni = vxl->vni;
+ if (zvni->advertise_subnet == advertise)
+ return 0;
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);
+ "EVPN subnet Adv %s on VNI %d , currently %s",
+ advertise ? "enabled" : "disabled", vni,
+ zvni->advertise_subnet ? "enabled" : "disabled");
- /* 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;
+ zvni->advertise_subnet = advertise;
+
+ ifp = zvni->vxlan_if;
+ if (!ifp)
+ return 0;
+
+ zif = ifp->info;
/* 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);
+ zl2_info = zif->l2info.vxl;
+
+ vlan_if = zvni_map_to_svi(zl2_info.access_vlan,
+ zif->brslave_info.br_if);
+ if (!vlan_if)
+ return 0;
- /* Read and populate local MACs and neighbors */
- zvni_read_mac_neigh(zvni, ifp);
+ if (zvni->advertise_subnet)
+ zvni_advertise_subnet(zvni, vlan_if, 1);
+ else
+ zvni_advertise_subnet(zvni, vlan_if, 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:
"Zebra VRF VNI Table");
}
+/* Cleanup VNI info, but don't free the table. */
+void zebra_vxlan_cleanup_tables(struct zebra_vrf *zvrf)
+{
+ if (!zvrf)
+ return;
+ hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
+}
+
/* Close all VNI handling */
void zebra_vxlan_close_tables(struct zebra_vrf *zvrf)
{
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;
+}
#include "vlan.h"
#include "vxlan.h"
+#include "lib/json.h"
#include "zebra/zebra_vrf.h"
/* Is EVPN enabled? */
#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 int zebra_vxlan_vrf_enable(struct zebra_vrf *zvrf);
+extern int zebra_vxlan_vrf_disable(struct zebra_vrf *zvrf);
+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_evpn(struct vty *vty, 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_vrf_vni(struct vty *vty, struct zebra_vrf *zvrf,
+ json_object *json_vrfs);
extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
int add);
extern int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if);
u_short length, struct zebra_vrf *zvrf);
extern int zebra_vxlan_remote_vtep_del(struct zserv *client,
u_short length, struct zebra_vrf *zvrf);
+extern int zebra_vxlan_advertise_subnet(struct zserv *client, u_short length,
+ struct zebra_vrf *zvrf);
extern int zebra_vxlan_advertise_gw_macip(struct zserv *client,
u_short length,
struct zebra_vrf *zvrf);
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_cleanup_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_evpn(struct vty *vty, u_char uj)
+{
+}
+
+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)
+{
+}
+
int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
{
return 0;
void zebra_vxlan_close_tables(struct zebra_vrf *zvrf)
{
}
+
+void zebra_vxlan_cleanup_tables(struct zebra_vrf *zvrf)
+{
+}
#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
/* Flag for advertising gw macip */
u_int8_t advertise_gw_macip;
+ /* Flag for advertising gw macip */
+ u_int8_t advertise_subnet;
+
/* Corresponding VxLAN interface. */
struct interface *vxlan_if;
/* 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 */
+#define ZEBRA_MAC_DEF_GW 0x20
/* 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 */
+#define ZEBRA_NEIGH_DEF_GW 0x08
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 */
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)
continue;
api_nh = &api.nexthops[count];
+ api_nh->vrf_id = nexthop->vrf_id;
api_nh->type = nexthop->type;
switch (nexthop->type) {
case NEXTHOP_TYPE_BLACKHOLE:
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->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_ifindex_add(
- re, api_nh->ifindex);
+ re, api_nh->ifindex, re->vrf_id);
break;
case NEXTHOP_TYPE_IPV4:
nexthop = route_entry_nexthop_ipv4_add(
- re, &api_nh->gate.ipv4, NULL);
+ re, &api_nh->gate.ipv4, NULL,
+ re->vrf_id);
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);
+ re, &api_nh->gate.ipv4, NULL, ifindex,
+ re->vrf_id);
+
+ /* 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);
+ re, &api_nh->gate.ipv6, re->vrf_id);
break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
nexthop = route_entry_nexthop_ipv6_ifindex_add(
- re, &api_nh->gate.ipv6,
- api_nh->ifindex);
+ re, &api_nh->gate.ipv6, api_nh->ifindex,
+ re->vrf_id);
break;
case NEXTHOP_TYPE_BLACKHOLE:
nexthop = route_entry_nexthop_blackhole_add(
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) {
switch (nexthop_type) {
case NEXTHOP_TYPE_IFINDEX:
STREAM_GETL(s, ifindex);
- route_entry_nexthop_ifindex_add(re, ifindex);
+ route_entry_nexthop_ifindex_add(re, ifindex,
+ re->vrf_id);
break;
case NEXTHOP_TYPE_IPV4:
STREAM_GET(&nhop_addr.s_addr, s,
IPV4_MAX_BYTELEN);
nexthop = route_entry_nexthop_ipv4_add(
- re, &nhop_addr, NULL);
+ re, &nhop_addr, NULL, re->vrf_id);
/* For labeled-unicast, each nexthop is followed
* by label. */
if (CHECK_FLAG(message, ZAPI_MESSAGE_LABEL)) {
IPV4_MAX_BYTELEN);
STREAM_GETL(s, ifindex);
route_entry_nexthop_ipv4_ifindex_add(
- re, &nhop_addr, NULL, ifindex);
+ re, &nhop_addr, NULL, ifindex,
+ re->vrf_id);
break;
case NEXTHOP_TYPE_IPV6:
zlog_warn("%s: Please use ZEBRA_ROUTE_ADD if you want to pass v6 nexthops",
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:
nexthop =
route_entry_nexthop_ipv6_ifindex_add(
re, &nexthops[i],
- ifindices[i]);
+ ifindices[i],
+ re->vrf_id);
else
nexthop = route_entry_nexthop_ipv6_add(
- re, &nexthops[i]);
+ re, &nexthops[i], re->vrf_id);
if (CHECK_FLAG(message, ZAPI_MESSAGE_LABEL))
nexthop_add_labels(nexthop, label_type,
} else {
if ((i < if_count) && ifindices[i])
route_entry_nexthop_ifindex_add(
- re, ifindices[i]);
+ re, ifindices[i], re->vrf_id);
}
}
}
} else
src_pp = NULL;
+ /* VRF ID */
+ re->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
* these. Clients should send the same number of paired set of
STREAM_GET(&nhop_addr, s, 16);
STREAM_GETL(s, ifindex);
route_entry_nexthop_ipv6_ifindex_add(
- re, &nhop_addr, ifindex);
+ re, &nhop_addr, ifindex, re->vrf_id);
break;
case NEXTHOP_TYPE_IFINDEX:
if (if_count < multipath_num) {
nexthop =
route_entry_nexthop_ipv6_ifindex_add(
re, &nexthops[i],
- ifindices[i]);
+ ifindices[i],
+ re->vrf_id);
else
nexthop = route_entry_nexthop_ipv6_add(
- re, &nexthops[i]);
+ re, &nexthops[i], re->vrf_id);
if (CHECK_FLAG(message, ZAPI_MESSAGE_LABEL))
nexthop_add_labels(nexthop, label_type,
1, &labels[i]);
} else {
if ((i < if_count) && ifindices[i])
route_entry_nexthop_ifindex_add(
- re, ifindices[i]);
+ re, ifindices[i], re->vrf_id);
}
}
}
else
re->mtu = 0;
- /* VRF ID */
- re->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);
return 1;
}
+
+static void zread_vrf_label(struct zserv *client,
+ struct zebra_vrf *zvrf)
+{
+ struct interface *ifp;
+ mpls_label_t nlabel;
+ afi_t afi;
+ struct stream *s;
+ struct zebra_vrf *def_zvrf;
+ enum lsp_types_t ltype;
+
+ s = client->ibuf;
+ STREAM_GETL(s, nlabel);
+ STREAM_GETC(s, afi);
+ if (nlabel == zvrf->label[afi]) {
+ /*
+ * Nothing to do here move along
+ */
+ return;
+ }
+
+ STREAM_GETC(s, ltype);
+
+ if (zvrf->vrf->vrf_id != VRF_DEFAULT)
+ ifp = if_lookup_by_name(zvrf->vrf->name, zvrf->vrf->vrf_id);
+ else
+ ifp = if_lookup_by_name("lo", VRF_DEFAULT);
+
+ if (!ifp) {
+ zlog_debug("Unable to find specified Interface for %s",
+ zvrf->vrf->name);
+ return;
+ }
+
+ def_zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+
+ if (zvrf->label[afi] != MPLS_LABEL_NONE) {
+ afi_t scrubber;
+ bool really_remove;
+
+ really_remove = true;
+ for (scrubber = AFI_IP; scrubber < AFI_MAX ; scrubber++) {
+ if (scrubber == afi)
+ continue;
+
+ if (zvrf->label[scrubber] == MPLS_LABEL_NONE)
+ continue;
+
+ if (zvrf->label[afi] == zvrf->label[scrubber]) {
+ really_remove = false;
+ break;
+ }
+ }
+
+ if (really_remove)
+ mpls_lsp_uninstall(def_zvrf, ltype, zvrf->label[afi],
+ NEXTHOP_TYPE_IFINDEX, NULL,
+ ifp->ifindex);
+ }
+
+ if (nlabel != MPLS_LABEL_NONE)
+ mpls_lsp_install(def_zvrf, ltype, nlabel, MPLS_LABEL_IMPLICIT_NULL,
+ NEXTHOP_TYPE_IFINDEX, NULL, ifp->ifindex);
+
+ zvrf->label[afi] = nlabel;
+stream_failure:
+ return;
+}
+
static inline void zserv_handle_commands(struct zserv *client,
uint16_t command,
uint16_t length,
case ZEBRA_VRF_UNREGISTER:
zread_vrf_unregister(client, length, zvrf);
break;
+ case ZEBRA_VRF_LABEL:
+ zread_vrf_label(client, zvrf);
+ break;
case ZEBRA_BFD_CLIENT_REGISTER:
zebra_ptm_bfd_client_register(client, length);
break;
case ZEBRA_ADVERTISE_DEFAULT_GW:
zebra_vxlan_advertise_gw_macip(client, length, zvrf);
break;
+ case ZEBRA_ADVERTISE_SUBNET:
+ zebra_vxlan_advertise_subnet(client, length, zvrf);
+ break;
case ZEBRA_ADVERTISE_ALL_VNI:
zebra_vxlan_advertise_all_vni(client, length, zvrf);
break;
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);
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;
+ u_int32_t prefixadd_cnt;
+ u_int32_t prefixdel_cnt;
time_t connect_time;
time_t last_read_time;
vrf_id_t vrf_id, struct prefix *p,
enum zapi_route_notify_owner note);
-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);