uint8_t *pnt = (uint8_t *)label_pnt;
if (pnt == NULL)
return;
+ if (label == BGP_PREVENT_VRF_2_VRF_LEAK) {
+ *label_pnt = label;
+ return;
+ }
*pnt++ = (label >> 12) & 0xff;
*pnt++ = (label >> 4) & 0xff;
*pnt++ = ((label << 4) + 1) & 0xff; /* S=1 */
return 0;
}
+static bool labels_same(struct bgp_info *bi, mpls_label_t *label, uint32_t n)
+{
+ uint32_t i;
+
+ if (!bi->extra) {
+ if (!n)
+ return true;
+ else
+ return false;
+ }
+
+ if (n != bi->extra->num_labels)
+ return false;
+
+ for (i = 0; i < n; ++i) {
+ if (label[i] != bi->extra->label[i])
+ return false;
+ }
+ return true;
+}
+
+/*
+ * make encoded route labels match specified encoded label set
+ */
+static void setlabels(
+ struct bgp_info *bi,
+ mpls_label_t *label, /* array of labels */
+ uint32_t num_labels)
+{
+ if (num_labels)
+ assert(label);
+ assert(num_labels <= BGP_MAX_LABELS);
+
+ if (!num_labels) {
+ if (bi->extra)
+ bi->extra->num_labels = 0;
+ return;
+ }
+
+ struct bgp_info_extra *extra = bgp_info_extra_get(bi);
+ uint32_t i;
+
+ for (i = 0; i < num_labels; ++i) {
+ extra->label[i] = label[i];
+ if (!bgp_is_valid_label(&label[i])) {
+ bgp_set_valid_label(&extra->label[i]);
+ }
+ }
+ extra->num_labels = num_labels;
+}
+
/*
* returns pointer to new bgp_info upon success
*/
safi_t safi,
struct bgp_info *source_bi,
mpls_label_t *label,
- int num_labels,
+ uint32_t num_labels,
void *parent,
struct bgp *bgp_orig,
struct prefix *nexthop_orig,
}
if (bi) {
+ bool labelssame = labels_same(bi, label, num_labels);
+
if (attrhash_cmp(bi->attr, new_attr)
+ && labelssame
&& !CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) {
bgp_attr_unintern(&new_attr);
bi->attr = new_attr;
bi->uptime = bgp_clock();
+ /*
+ * rewrite labels
+ */
+ if (!labelssame)
+ setlabels(bi, label, num_labels);
+
if (nexthop_self_flag)
bgp_info_set_flag(bn, bi, BGP_INFO_ANNC_NH_SELF);
if (nexthop_self_flag)
bgp_info_set_flag(bn, new, BGP_INFO_ANNC_NH_SELF);
+ if (num_labels)
+ setlabels(new, label, num_labels);
+
bgp_info_extra_get(new);
- if (label) {
- int i;
-
- for (i = 0; i < num_labels; ++i) {
- new->extra->label[i] = label[i];
- if (!bgp_is_valid_label(&label[i])) {
- if (debug) {
- zlog_debug(
- "%s: %s: marking label %d valid",
- __func__, buf_prefix, i);
- }
- bgp_set_valid_label(&new->extra->label[i]);
- }
- }
- new->extra->num_labels = num_labels;
- }
new->extra->parent = parent;
if (bgp_orig)
char *s = ecommunity_ecom2str(info_vrf->attr->ecommunity,
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
- zlog_debug("%s: info_vrf->type=%d, EC{%s}", __func__,
- info_vrf->type, s);
+ zlog_debug("%s: %s info_vrf->type=%d, EC{%s}", __func__,
+ bgp_vrf->name, info_vrf->type, s);
XFREE(MTYPE_ECOMMUNITY_STR, s);
}
return;
}
- /* loop check */
- if (info_vrf->extra && info_vrf->extra->bgp_orig == bgp_vpn)
+ /* loop check - should not be an imported route. */
+ if (info_vrf->extra && info_vrf->extra->bgp_orig)
return;
if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) {
if (debug)
- zlog_debug("%s: skipping: %s", __func__, debugmsg);
+ zlog_debug("%s: %s skipping: %s", __func__,
+ bgp_vrf->name, debugmsg);
return;
}
/* if policy nexthop not set, use 0 */
if (CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) {
-
struct prefix *nexthop =
&bgp_vrf->vpn_policy[afi].tovpn_nexthop;
+
switch (nexthop->family) {
case AF_INET:
/* prevent mp_nexthop_global_in <- self in bgp_route.c
assert(0);
}
} else {
- if (afi == AFI_IP) {
- /* For ipv4, copy to multiprotocol nexthop field */
- static_attr.mp_nexthop_global_in = static_attr.nexthop;
- static_attr.mp_nexthop_len = 4;
- /* XXX Leave static_attr.nexthop intact for NHT */
- static_attr.flag &= ~ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)) {
+ if (afi == AFI_IP) {
+ /*
+ * For ipv4, copy to multiprotocol
+ * nexthop field
+ */
+ static_attr.mp_nexthop_global_in =
+ static_attr.nexthop;
+ static_attr.mp_nexthop_len = 4;
+ /*
+ * XXX Leave static_attr.nexthop
+ * intact for NHT
+ */
+ static_attr.flag &=
+ ~ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ }
+ } else {
+ /* Update based on next-hop family to account for
+ * RFC 5549 (BGP unnumbered) scenario. Note that
+ * specific action is only needed for the case of
+ * IPv4 nexthops as the attr has been copied
+ * otherwise.
+ */
+ if (afi == AFI_IP &&
+ !BGP_ATTR_NEXTHOP_AFI_IP6(info_vrf->attr)) {
+ static_attr.mp_nexthop_global_in.s_addr =
+ static_attr.nexthop.s_addr;
+ static_attr.mp_nexthop_len = 4;
+ static_attr.flag |=
+ ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ }
}
nexthop_self_flag = 1;
}
info_vrf->type, info_vrf->sub_type);
}
- if (info_vrf->type != ZEBRA_ROUTE_BGP) {
- if (debug)
- zlog_debug("%s: wrong type %d", __func__,
- info_vrf->type);
- return;
- }
if (info_vrf->sub_type != BGP_ROUTE_NORMAL
- && info_vrf->sub_type != BGP_ROUTE_STATIC) {
+ && info_vrf->sub_type != BGP_ROUTE_STATIC
+ && info_vrf->sub_type != BGP_ROUTE_REDISTRIBUTE) {
if (debug)
zlog_debug("%s: wrong sub_type %d", __func__,
const char *debugmsg;
struct prefix nexthop_orig;
mpls_label_t *pLabels = NULL;
- int num_labels = 0;
+ uint32_t num_labels = 0;
int nexthop_self_flag = 1;
+ struct bgp_info *bi_ultimate = NULL;
+ int origin_local = 0;
+ struct bgp *src_vrf;
int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
nexthop_orig.family = nhfamily;
switch (nhfamily) {
-
case AF_INET:
/* save */
nexthop_orig.u.prefix4 = info_vpn->attr->mp_nexthop_global_in;
nexthop_orig.prefixlen = 32;
+
+ if (CHECK_FLAG(bgp_vrf->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ static_attr.nexthop.s_addr =
+ nexthop_orig.u.prefix4.s_addr;
+
+ static_attr.mp_nexthop_global_in =
+ info_vpn->attr->mp_nexthop_global_in;
+ static_attr.mp_nexthop_len =
+ info_vpn->attr->mp_nexthop_len;
+ }
static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
break;
-
case AF_INET6:
/* save */
nexthop_orig.u.prefix6 = info_vpn->attr->mp_nexthop_global;
nexthop_orig.prefixlen = 128;
+
+ if (CHECK_FLAG(bgp_vrf->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ static_attr.mp_nexthop_global = nexthop_orig.u.prefix6;
+ }
break;
}
-
/*
* route map handling
*/
/*
* ensure labels are copied
+ *
+ * However, there is a special case: if the route originated in
+ * another local VRF (as opposed to arriving via VPN), then the
+ * nexthop is reached by hairpinning through this router (me)
+ * using IP forwarding only (no LSP). Therefore, the route
+ * imported to the VRF should not have labels attached. Note
+ * that nexthop tracking is also involved: eliminating the
+ * labels for these routes enables the non-labeled nexthops
+ * from the originating VRF to be considered valid for this route.
*/
- if (info_vpn->extra && info_vpn->extra->num_labels) {
- num_labels = info_vpn->extra->num_labels;
- if (num_labels > BGP_MAX_LABELS)
- num_labels = BGP_MAX_LABELS;
- pLabels = info_vpn->extra->label;
+ if (!CHECK_FLAG(bgp_vrf->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ /* work back to original route */
+ for (bi_ultimate = info_vpn;
+ bi_ultimate->extra && bi_ultimate->extra->parent;
+ bi_ultimate = bi_ultimate->extra->parent)
+ ;
+
+ /*
+ * if original route was unicast,
+ * then it did not arrive over vpn
+ */
+ if (bi_ultimate->net) {
+ struct bgp_table *table;
+
+ table = bgp_node_table(bi_ultimate->net);
+ if (table && (table->safi == SAFI_UNICAST))
+ origin_local = 1;
+ }
+
+ /* copy labels */
+ if (!origin_local &&
+ info_vpn->extra && info_vpn->extra->num_labels) {
+ num_labels = info_vpn->extra->num_labels;
+ if (num_labels > BGP_MAX_LABELS)
+ num_labels = BGP_MAX_LABELS;
+ pLabels = info_vpn->extra->label;
+ }
}
+
if (debug) {
char buf_prefix[PREFIX_STRLEN];
prefix2str(p, buf_prefix, sizeof(buf_prefix));
num_labels);
}
+ /*
+ * For VRF-2-VRF route-leaking,
+ * the source will be the originating VRF.
+ */
+ if (info_vpn->extra && info_vpn->extra->bgp_orig)
+ src_vrf = info_vpn->extra->bgp_orig;
+ else
+ src_vrf = bgp_vpn;
+
leak_update(bgp_vrf, bn, new_attr, afi, safi, info_vpn,
pLabels, num_labels,
info_vpn, /* parent */
- bgp_vpn, &nexthop_orig, nexthop_self_flag, debug);
+ src_vrf, &nexthop_orig, nexthop_self_flag, debug);
}
void vpn_leak_to_vrf_update(struct bgp *bgp_vpn, /* from */
struct bgp_info *bi;
safi_t safi = SAFI_UNICAST;
int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
- struct bgp *bgp_vpn = bgp_get_default();
if (debug)
zlog_debug("%s: entry", __func__);
/*
- * Walk vrf table, delete bi with bgp_orig == bgp_vpn
+ * Walk vrf table, delete bi with bgp_orig in a different vrf
*/
for (bn = bgp_table_top(bgp_vrf->rib[afi][safi]); bn;
bn = bgp_route_next(bn)) {
for (bi = bn->info; bi; bi = bi->next) {
- if (bi->extra && bi->extra->bgp_orig == bgp_vpn) {
+ if (bi->extra && bi->extra->bgp_orig != bgp_vrf) {
/* delete route */
bgp_aggregate_decrement(bgp_vrf, &bn->p, bi,
}
}
+/*
+ * This function is called for definition/deletion/change to a route-map
+ */
static void vpn_policy_routemap_update(struct bgp *bgp, const char *rmap_name)
{
int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT);
for (afi = 0; afi < AFI_MAX; ++afi) {
- if (vpn_leak_to_vpn_active(bgp, afi, NULL)
- && bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN]
- && !strcmp(rmap_name,
+ if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN]
+ && !strcmp(rmap_name,
bgp->vpn_policy[afi]
.rmap_name[BGP_VPN_POLICY_DIR_TOVPN])) {
zlog_debug("%s: after vpn_leak_prechange",
__func__);
- if (!rmap)
- bgp->vpn_policy[afi]
- .rmap[BGP_VPN_POLICY_DIR_TOVPN] = NULL;
+ /* in case of definition/deletion */
+ bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN] =
+ rmap;
vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
bgp_get_default(), bgp);
+
if (debug)
zlog_debug("%s: after vpn_leak_postchange",
__func__);
}
- char *mapname = bgp->vpn_policy[afi]
- .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN];
-
- if (vpn_leak_from_vpn_active(bgp, afi, NULL) &&
- mapname &&
- !strcmp(rmap_name, mapname)) {
+ if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]
+ && !strcmp(rmap_name,
+ bgp->vpn_policy[afi]
+ .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN])) {
if (debug) {
zlog_debug("%s: rmap \"%s\" matches vrf-policy fromvpn for as %d afi %s",
vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi,
bgp_get_default(), bgp);
- if (!rmap) {
- bgp->vpn_policy[afi]
- .rmap[BGP_VPN_POLICY_DIR_FROMVPN] =
- NULL;
- }
+ /* in case of definition/deletion */
+ bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_FROMVPN] =
+ rmap;
vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, afi,
bgp_get_default(), bgp);
vpn_policy_routemap_update(bgp, rmap_name);
}
+void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi, safi_t safi)
+{
+ const char *export_name;
+ vpn_policy_direction_t idir, edir;
+ char *vname;
+ char buf[1000];
+ struct ecommunity *ecom;
+ bool first_export = false;
+
+ export_name = to_bgp->name ? to_bgp->name : BGP_DEFAULT_NAME;
+ idir = BGP_VPN_POLICY_DIR_FROMVPN;
+ edir = BGP_VPN_POLICY_DIR_TOVPN;
+
+ /*
+ * Cross-ref both VRFs. Also, note if this is the first time
+ * any VRF is importing from "import_vrf".
+ */
+ vname = (from_bgp->name ? XSTRDUP(MTYPE_TMP, from_bgp->name)
+ : XSTRDUP(MTYPE_TMP, BGP_DEFAULT_NAME));
+
+ listnode_add(to_bgp->vpn_policy[afi].import_vrf, vname);
+
+ if (!listcount(from_bgp->vpn_policy[afi].export_vrf))
+ first_export = true;
+ vname = XSTRDUP(MTYPE_TMP, export_name);
+ listnode_add(from_bgp->vpn_policy[afi].export_vrf, vname);
+
+ /* Update import RT for current VRF using export RT of the VRF we're
+ * importing from. First though, make sure "import_vrf" has that
+ * set.
+ */
+ if (first_export) {
+ form_auto_rd(from_bgp->router_id, from_bgp->vrf_rd_id,
+ &from_bgp->vrf_prd_auto);
+ from_bgp->vpn_policy[afi].tovpn_rd = from_bgp->vrf_prd_auto;
+ SET_FLAG(from_bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET);
+ prefix_rd2str(&from_bgp->vpn_policy[afi].tovpn_rd,
+ buf, sizeof(buf));
+ from_bgp->vpn_policy[afi].rtlist[edir] =
+ ecommunity_str2com(buf, ECOMMUNITY_ROUTE_TARGET, 0);
+ SET_FLAG(from_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT);
+ from_bgp->vpn_policy[afi].tovpn_label =
+ BGP_PREVENT_VRF_2_VRF_LEAK;
+ }
+ ecom = from_bgp->vpn_policy[afi].rtlist[edir];
+ if (to_bgp->vpn_policy[afi].rtlist[idir])
+ to_bgp->vpn_policy[afi].rtlist[idir] =
+ ecommunity_merge(to_bgp->vpn_policy[afi]
+ .rtlist[idir], ecom);
+ else
+ to_bgp->vpn_policy[afi].rtlist[idir] = ecommunity_dup(ecom);
+ SET_FLAG(to_bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_VRF_IMPORT);
+
+ /* Does "import_vrf" first need to export its routes or that
+ * is already done and we just need to import those routes
+ * from the global table?
+ */
+ if (first_export)
+ vpn_leak_postchange(edir, afi, bgp_get_default(), from_bgp);
+ else
+ vpn_leak_postchange(idir, afi, bgp_get_default(), to_bgp);
+}
+
+void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi, safi_t safi)
+{
+ const char *export_name, *tmp_name;
+ vpn_policy_direction_t idir, edir;
+ char *vname;
+ struct ecommunity *ecom;
+ struct listnode *node;
+
+ export_name = to_bgp->name ? to_bgp->name : BGP_DEFAULT_NAME;
+ tmp_name = from_bgp->name ? from_bgp->name : BGP_DEFAULT_NAME;
+ idir = BGP_VPN_POLICY_DIR_FROMVPN;
+ edir = BGP_VPN_POLICY_DIR_TOVPN;
+
+ /* Were we importing from "import_vrf"? */
+ for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi].import_vrf, node,
+ vname)) {
+ if (strcmp(vname, tmp_name) == 0)
+ break;
+ }
+
+ /*
+ * We do not check in the cli if the passed in bgp
+ * instance is actually imported into us before
+ * we call this function. As such if we do not
+ * find this in the import_vrf list than
+ * we just need to return safely.
+ */
+ if (!vname)
+ return;
+
+ /* Remove "import_vrf" from our import list. */
+ listnode_delete(to_bgp->vpn_policy[afi].import_vrf, vname);
+ XFREE(MTYPE_TMP, vname);
+
+ /* Remove routes imported from "import_vrf". */
+ /* TODO: In the current logic, we have to first remove all
+ * imported routes and then (if needed) import back routes
+ */
+ vpn_leak_prechange(idir, afi, bgp_get_default(), to_bgp);
+
+ if (to_bgp->vpn_policy[afi].import_vrf->count == 0) {
+ UNSET_FLAG(to_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT);
+ ecommunity_free(&to_bgp->vpn_policy[afi].rtlist[idir]);
+ } else {
+ ecom = from_bgp->vpn_policy[afi].rtlist[edir];
+ ecommunity_del_val(to_bgp->vpn_policy[afi].rtlist[idir],
+ (struct ecommunity_val *)ecom->val);
+ vpn_leak_postchange(idir, afi, bgp_get_default(), to_bgp);
+ }
+
+ /*
+ * What?
+ * So SA is assuming that since the ALL_LIST_ELEMENTS_RO
+ * below is checking for NULL that export_vrf can be
+ * NULL, consequently it is complaining( like a cabbage )
+ * that we could dereference and crash in the listcount(..)
+ * check below.
+ * So make it happy, under protest, with liberty and justice
+ * for all.
+ */
+ assert(from_bgp->vpn_policy[afi].export_vrf);
+
+ /* Remove us from "import_vrf's" export list. If no other VRF
+ * is importing from "import_vrf", cleanup appropriately.
+ */
+ for (ALL_LIST_ELEMENTS_RO(from_bgp->vpn_policy[afi].export_vrf,
+ node, vname)) {
+ if (strcmp(vname, export_name) == 0)
+ break;
+ }
+
+ /*
+ * If we have gotten to this point then the vname must
+ * exist. If not, we are in a world of trouble and
+ * have slag sitting around.
+ *
+ * import_vrf and export_vrf must match in having
+ * the in/out names as appropriate.
+ */
+ assert(vname);
+
+ listnode_delete(from_bgp->vpn_policy[afi].export_vrf, vname);
+ XFREE(MTYPE_TMP, vname);
+
+ if (!listcount(from_bgp->vpn_policy[afi].export_vrf)) {
+ vpn_leak_prechange(edir, afi, bgp_get_default(), from_bgp);
+ ecommunity_free(&from_bgp->vpn_policy[afi].rtlist[edir]);
+ UNSET_FLAG(from_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT);
+ memset(&from_bgp->vpn_policy[afi].tovpn_rd, 0,
+ sizeof(struct prefix_rd));
+ UNSET_FLAG(from_bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET);
+ from_bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
+
+ }
+}
+
/* For testing purpose, static route of MPLS-VPN. */
DEFUN (vpnv4_network,
vpnv4_network_cmd,