]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: vpn-vrf route leaking
authorG. Paul Ziemba <paulz@labn.net>
Fri, 9 Mar 2018 20:52:55 +0000 (15:52 -0500)
committerLou Berger <lberger@labn.net>
Fri, 9 Mar 2018 21:42:40 +0000 (16:42 -0500)
    - add "debug bgp vpn label" CLI
    - improved debug messages for "debug bgp bestpath"
    - send vrf label to zebra after zebra informs bgpd of vrf_id
    - withdraw vrf_label from zebra if zebra informs bgpd that vrf_id is disabled
Signed-off-by: G. Paul Ziemba <paulz@labn.net>
14 files changed:
bgpd/bgp_debug.c
bgpd/bgp_debug.h
bgpd/bgp_main.c
bgpd/bgp_mplsvpn.c
bgpd/bgp_mplsvpn.h
bgpd/bgp_route.c
bgpd/bgp_route.h
bgpd/bgp_routemap.c
bgpd/bgp_vty.c
bgpd/bgp_vty.h
bgpd/bgp_zebra.c
bgpd/bgpd.c
bgpd/bgpd.h
bgpd/rfapi/bgp_rfapi_cfg.c

index e89f399e418d0e424564f89c48fb6296ace4f428..691cdd19d71530b5397dcfadd7e7144d2aff522e 100644 (file)
@@ -55,6 +55,7 @@ unsigned long conf_bgp_debug_zebra;
 unsigned long conf_bgp_debug_allow_martians;
 unsigned long conf_bgp_debug_nht;
 unsigned long conf_bgp_debug_update_groups;
+unsigned long conf_bgp_debug_vpn;
 
 unsigned long term_bgp_debug_as4;
 unsigned long term_bgp_debug_neighbor_events;
@@ -68,6 +69,7 @@ unsigned long term_bgp_debug_zebra;
 unsigned long term_bgp_debug_allow_martians;
 unsigned long term_bgp_debug_nht;
 unsigned long term_bgp_debug_update_groups;
+unsigned long term_bgp_debug_vpn;
 
 struct list *bgp_debug_neighbor_events_peers = NULL;
 struct list *bgp_debug_keepalive_peers = NULL;
@@ -1557,6 +1559,96 @@ DEFUN (no_debug_bgp_update_groups,
        return CMD_SUCCESS;
 }
 
+DEFUN (debug_bgp_vpn,
+       debug_bgp_vpn_cmd,
+       "debug bgp vpn <leak-from-vrf|leak-to-vrf|rmap-event|label>",
+       DEBUG_STR
+       BGP_STR
+       "VPN routes\n"
+       "leaked from vrf to vpn\n"
+       "leaked to vrf from vpn\n"
+       "route-map updates\n"
+       "labels\n")
+{
+       int idx = 3;
+
+       if (argv_find(argv, argc, "leak-from-vrf", &idx)) {
+               if (vty->node == CONFIG_NODE)
+                       DEBUG_ON(vpn, VPN_LEAK_FROM_VRF);
+               else
+                       TERM_DEBUG_ON(vpn, VPN_LEAK_FROM_VRF);
+       } else if (argv_find(argv, argc, "leak-to-vrf", &idx)) {
+               if (vty->node == CONFIG_NODE)
+                       DEBUG_ON(vpn, VPN_LEAK_TO_VRF);
+               else
+                       TERM_DEBUG_ON(vpn, VPN_LEAK_TO_VRF);
+       } else if (argv_find(argv, argc, "rmap-event", &idx)) {
+               if (vty->node == CONFIG_NODE)
+                       DEBUG_ON(vpn, VPN_LEAK_RMAP_EVENT);
+               else
+                       TERM_DEBUG_ON(vpn, VPN_LEAK_RMAP_EVENT);
+       } else if (argv_find(argv, argc, "label", &idx)) {
+               if (vty->node == CONFIG_NODE)
+                       DEBUG_ON(vpn, VPN_LEAK_LABEL);
+               else
+                       TERM_DEBUG_ON(vpn, VPN_LEAK_LABEL);
+       } else {
+               vty_out(vty, "%% unknown debug bgp vpn keyword\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (vty->node != CONFIG_NODE)
+               vty_out(vty, "enabled debug bgp vpn %s\n", argv[idx]->text);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_vpn,
+       no_debug_bgp_vpn_cmd,
+       "no debug bgp vpn <leak-from-vrf|leak-to-vrf|rmap-event|label>",
+       NO_STR
+       DEBUG_STR
+       BGP_STR
+       "VPN routes\n"
+       "leaked from vrf to vpn\n"
+       "leaked to vrf from vpn\n"
+       "route-map updates\n"
+       "labels\n")
+{
+       int idx = 4;
+
+       if (argv_find(argv, argc, "leak-from-vrf", &idx)) {
+               if (vty->node == CONFIG_NODE)
+                       DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF);
+               else
+                       TERM_DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF);
+
+       } else if (argv_find(argv, argc, "leak-to-vrf", &idx)) {
+               if (vty->node == CONFIG_NODE)
+                       DEBUG_OFF(vpn, VPN_LEAK_TO_VRF);
+               else
+                       TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF);
+       } else if (argv_find(argv, argc, "rmap-event", &idx)) {
+               if (vty->node == CONFIG_NODE)
+                       DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
+               else
+                       TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
+       } else if (argv_find(argv, argc, "label", &idx)) {
+               if (vty->node == CONFIG_NODE)
+                       DEBUG_OFF(vpn, VPN_LEAK_LABEL);
+               else
+                       TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
+       } else {
+               vty_out(vty, "%% unknown debug bgp vpn keyword\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (vty->node != CONFIG_NODE)
+               vty_out(vty, "disabled debug bgp vpn %s\n", argv[idx]->text);
+
+       return CMD_SUCCESS;
+}
+
 DEFUN (no_debug_bgp,
        no_debug_bgp_cmd,
        "no debug bgp",
@@ -1589,6 +1681,10 @@ DEFUN (no_debug_bgp,
        TERM_DEBUG_OFF(zebra, ZEBRA);
        TERM_DEBUG_OFF(allow_martians, ALLOW_MARTIANS);
        TERM_DEBUG_OFF(nht, NHT);
+       TERM_DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF);
+       TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF);
+       TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
+       TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
        vty_out(vty, "All possible debugging has been turned off\n");
 
        return CMD_SUCCESS;
@@ -1648,6 +1744,18 @@ DEFUN_NOSH (show_debugging_bgp,
 
        if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS))
                vty_out(vty, "  BGP allow martian next hop debugging is on\n");
+
+       if (BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF))
+               vty_out(vty,
+                       "  BGP route leak from vrf to vpn debugging is on\n");
+       if (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF))
+               vty_out(vty,
+                       "  BGP route leak to vrf from vpn debugging is on\n");
+       if (BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT))
+               vty_out(vty, "  BGP vpn route-map event debugging is on\n");
+       if (BGP_DEBUG(vpn, VPN_LEAK_LABEL))
+               vty_out(vty, "  BGP vpn label event debugging is on\n");
+
        vty_out(vty, "\n");
        return CMD_SUCCESS;
 }
@@ -1692,6 +1800,15 @@ int bgp_debug_count(void)
        if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS))
                ret++;
 
+       if (BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF))
+               ret++;
+       if (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF))
+               ret++;
+       if (BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT))
+               ret++;
+       if (BGP_DEBUG(vpn, VPN_LEAK_LABEL))
+               ret++;
+
        return ret;
 }
 
@@ -1768,6 +1885,23 @@ static int bgp_config_write_debug(struct vty *vty)
                write++;
        }
 
+       if (CONF_BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)) {
+               vty_out(vty, "debug bgp vpn leak-from-vrf\n");
+               write++;
+       }
+       if (CONF_BGP_DEBUG(vpn, VPN_LEAK_TO_VRF)) {
+               vty_out(vty, "debug bgp vpn leak-to-vrf\n");
+               write++;
+       }
+       if (CONF_BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT)) {
+               vty_out(vty, "debug bgp vpn rmap-event\n");
+               write++;
+       }
+       if (CONF_BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
+               vty_out(vty, "debug bgp vpn label\n");
+               write++;
+       }
+
        return write;
 }
 
@@ -1861,6 +1995,11 @@ void bgp_debug_init(void)
        install_element(CONFIG_NODE, &no_debug_bgp_bestpath_cmd);
        install_element(ENABLE_NODE, &no_debug_bgp_bestpath_prefix_cmd);
        install_element(CONFIG_NODE, &no_debug_bgp_bestpath_prefix_cmd);
+
+       install_element(ENABLE_NODE, &debug_bgp_vpn_cmd);
+       install_element(CONFIG_NODE, &debug_bgp_vpn_cmd);
+       install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd);
+       install_element(CONFIG_NODE, &no_debug_bgp_vpn_cmd);
 }
 
 /* Return true if this prefix is on the per_prefix_list of prefixes to debug
index 765e43f5b4330b4a87a3525129df7ecd6d5d39ea..57f8fef5f2a3871b89acef8c165641c3e083b5c2 100644 (file)
@@ -72,6 +72,7 @@ extern unsigned long conf_bgp_debug_zebra;
 extern unsigned long conf_bgp_debug_allow_martians;
 extern unsigned long conf_bgp_debug_nht;
 extern unsigned long conf_bgp_debug_update_groups;
+extern unsigned long conf_bgp_debug_vpn;
 
 extern unsigned long term_bgp_debug_as4;
 extern unsigned long term_bgp_debug_neighbor_events;
@@ -83,6 +84,7 @@ extern unsigned long term_bgp_debug_zebra;
 extern unsigned long term_bgp_debug_allow_martians;
 extern unsigned long term_bgp_debug_nht;
 extern unsigned long term_bgp_debug_update_groups;
+extern unsigned long term_bgp_debug_vpn;
 
 extern struct list *bgp_debug_neighbor_events_peers;
 extern struct list *bgp_debug_keepalive_peers;
@@ -111,6 +113,10 @@ struct bgp_debug_filter {
 #define BGP_DEBUG_ALLOW_MARTIANS      0x01
 #define BGP_DEBUG_NHT                 0x01
 #define BGP_DEBUG_UPDATE_GROUPS       0x01
+#define BGP_DEBUG_VPN_LEAK_FROM_VRF   0x01
+#define BGP_DEBUG_VPN_LEAK_TO_VRF     0x02
+#define BGP_DEBUG_VPN_LEAK_RMAP_EVENT 0x04
+#define BGP_DEBUG_VPN_LEAK_LABEL      0x08
 
 #define BGP_DEBUG_PACKET_SEND         0x01
 #define BGP_DEBUG_PACKET_SEND_DETAIL  0x02
index 703978601438b36603912fbeda9428b771a39e1e..bf60f9c11826f3584d13c642bc9fbbcf5770ec85 100644 (file)
@@ -265,6 +265,8 @@ static int bgp_vrf_enable(struct vrf *vrf)
                if (old_vrf_id != bgp->vrf_id)
                        bgp_update_redist_vrf_bitmaps(bgp, old_vrf_id);
                bgp_instance_up(bgp);
+               vpn_leak_zebra_vrf_label_update(bgp, AFI_IP);
+               vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6);
        }
 
        return 0;
@@ -283,6 +285,10 @@ static int bgp_vrf_disable(struct vrf *vrf)
 
        bgp = bgp_lookup_by_name(vrf->name);
        if (bgp) {
+
+               vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP);
+               vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP6);
+
                old_vrf_id = bgp->vrf_id;
                bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
                /* We have instance configured, unlink from VRF and make it
index ec4989de8fc5d87d108d124c2198217574659488..d87f78a7830dae257675914d0a04229031de40ab 100644 (file)
 #include "stream.h"
 #include "queue.h"
 #include "filter.h"
+#include "mpls.h"
 #include "lib/json.h"
+#include "lib/zclient.h"
 
 #include "bgpd/bgpd.h"
+#include "bgpd/bgp_debug.h"
 #include "bgpd/bgp_table.h"
 #include "bgpd/bgp_route.h"
 #include "bgpd/bgp_attr.h"
 #include "bgpd/bgp_packet.h"
 #include "bgpd/bgp_vty.h"
 #include "bgpd/bgp_vpn.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_nexthop.h"
 
 #if ENABLE_BGP_VNC
 #include "bgpd/rfapi/rfapi_backend.h"
 #endif
 
+/*
+ * Definitions and external declarations.
+ */
+extern struct zclient *zclient;
+
 extern int argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc,
                                     int *index, afi_t *afi)
 {
@@ -92,7 +103,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
        struct rd_as rd_as;
        struct rd_ip rd_ip;
        struct prefix_rd prd;
-       mpls_label_t label;
+       mpls_label_t label = {0};
        afi_t afi;
        safi_t safi;
        int addpath_encoded;
@@ -233,6 +244,979 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
 #undef VPN_PREFIXLEN_MIN_BYTES
 }
 
+/*
+ * This function informs zebra of the label this vrf sets on routes
+ * leaked to VPN. Zebra should install this label in the kernel with
+ * an action of "pop label and then use this vrf's IP FIB to route the PDU."
+ *
+ * Sending this vrf-label association is qualified by a) whether vrf->vpn
+ * exporting is active ("export vpn" is enabled, vpn-policy RD and RT list
+ * are set) and b) whether vpn-policy label is set.
+ *
+ * If any of these conditions do not hold, then we send MPLS_LABEL_NONE
+ * for this vrf, which zebra interprets to mean "delete this vrf-label
+ * association."
+ */
+void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi)
+{
+       mpls_label_t label = MPLS_LABEL_NONE;
+       const char *name = "default";
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+
+       if (debug && (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT)) {
+               name = bgp->name;
+       }
+
+       if (bgp->vrf_id == VRF_UNKNOWN) {
+               if (debug) {
+                       zlog_debug(
+                               "%s: vrf %s: afi %s: vrf_id not set, "
+                               "can't set zebra vrf label",
+                               __func__, name, afi2str(afi));
+               }
+               return;
+       }
+
+       if (vpn_leak_to_vpn_active(bgp, afi, NULL)) {
+               label = bgp->vpn_policy[afi].tovpn_label;
+       }
+
+       if (debug) {
+               zlog_debug("%s: vrf %s: afi %s: setting label %d for vrf id %d",
+                          __func__, name, afi2str(afi), label, bgp->vrf_id);
+       }
+
+       zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP);
+       bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label;
+}
+
+/*
+ * If zebra tells us vrf has become unconfigured, tell zebra not to
+ * use this label to forward to the vrf anymore
+ */
+void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi)
+{
+       mpls_label_t label = MPLS_LABEL_NONE;
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+
+       if (bgp->vrf_id == VRF_UNKNOWN) {
+               if (debug) {
+                       zlog_debug(
+                               "%s: vrf_id not set, can't delete zebra vrf label",
+                               __func__);
+               }
+               return;
+       }
+
+       if (debug) {
+               zlog_debug("%s: deleting label for vrf %s (id=%d)", __func__,
+                          (bgp->name ? bgp->name : "default"), bgp->vrf_id);
+       }
+
+       zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP);
+       bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label;
+}
+
+static int ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
+{
+       int i;
+       int j;
+
+       if (!e1 || !e2)
+               return 0;
+
+       for (i = 0; i < e1->size; ++i) {
+               for (j = 0; j < e2->size; ++j) {
+                       if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE),
+                                   e2->val + (j * ECOMMUNITY_SIZE),
+                                   ECOMMUNITY_SIZE)) {
+
+                               return 1;
+                       }
+               }
+       }
+       return 0;
+}
+
+/*
+ * returns pointer to new bgp_info upon success
+ */
+static struct bgp_info *
+leak_update(struct bgp *bgp, /* destination bgp instance */
+           struct bgp_node *bn, struct attr *new_attr, /* already interned */
+           afi_t afi, safi_t safi, struct bgp_info *source_bi, u_char type,
+           u_char sub_type, mpls_label_t *label, int num_labels, void *parent,
+           struct bgp *bgp_orig, struct prefix *nexthop_orig, int debug)
+{
+       struct prefix *p = &bn->p;
+       struct bgp_info *bi;
+       struct bgp_info *new;
+       char buf_prefix[PREFIX_STRLEN];
+       const char *pDestInstanceName = "default";
+
+       if (debug) {
+               prefix2str(&bn->p, buf_prefix, sizeof(buf_prefix));
+               if (bgp->name)
+                       pDestInstanceName = bgp->name;
+       }
+
+       /*
+        * match parent
+        */
+       for (bi = bn->info; bi; bi = bi->next) {
+               if (bi->extra && bi->extra->parent == parent)
+                       break;
+       }
+
+       if (bi) {
+               if (attrhash_cmp(bi->attr, new_attr)
+                   && !CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) {
+
+                       bgp_attr_unintern(&new_attr);
+                       if (debug)
+                               zlog_debug(
+                                       "%s: ->%s: %s: Found route, no change",
+                                       __func__, pDestInstanceName,
+                                       buf_prefix);
+                       return NULL;
+               }
+
+               /* attr is changed */
+               bgp_info_set_flag(bn, bi, BGP_INFO_ATTR_CHANGED);
+
+               /* Rewrite BGP route information. */
+               if (CHECK_FLAG(bi->flags, BGP_INFO_REMOVED))
+                       bgp_info_restore(bn, bi);
+               else
+                       bgp_aggregate_decrement(bgp, p, bi, afi, safi);
+               bgp_attr_unintern(&bi->attr);
+               bi->attr = new_attr;
+               bi->uptime = bgp_clock();
+
+               /* Process change. */
+               bgp_aggregate_increment(bgp, p, bi, afi, safi);
+               bgp_process(bgp, bn, afi, safi);
+               bgp_unlock_node(bn);
+
+               if (debug)
+                       zlog_debug("%s: ->%s: %s Found route, changed attr",
+                                  __func__, pDestInstanceName, buf_prefix);
+
+               return NULL;
+       }
+
+       new = info_make(type, sub_type, 0, bgp->peer_self, new_attr, bn);
+       SET_FLAG(new->flags, BGP_INFO_VALID);
+
+       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)
+               new->extra->bgp_orig = bgp_orig;
+       if (nexthop_orig)
+               new->extra->nexthop_orig = *nexthop_orig;
+
+       bgp_aggregate_increment(bgp, p, new, afi, safi);
+       bgp_info_add(bn, new);
+
+       bgp_unlock_node(bn);
+       bgp_process(bgp, bn, afi, safi);
+
+       if (debug)
+               zlog_debug("%s: ->%s: %s: Added new route", __func__,
+                          pDestInstanceName, buf_prefix);
+
+       return new;
+}
+
+/* cf vnc_import_bgp_add_route_mode_nvegroup() and add_vnc_route() */
+void vpn_leak_from_vrf_update(struct bgp *bgp_vpn,       /* to */
+                             struct bgp *bgp_vrf,       /* from */
+                             struct bgp_info *info_vrf) /* route */
+{
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
+       struct prefix *p = &info_vrf->net->p;
+       afi_t afi = family2afi(p->family);
+       struct attr static_attr = {0};
+       struct attr *new_attr = NULL;
+       safi_t safi = SAFI_MPLS_VPN;
+       mpls_label_t label_val;
+       mpls_label_t label;
+       struct bgp_node *bn;
+       const char *debugmsg;
+
+       if (debug) {
+               const char *s = "";
+
+               if (info_vrf->attr && info_vrf->attr->ecommunity) {
+                       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);
+       }
+
+       if (!bgp_vpn)
+               return;
+
+       if (!afi) {
+               if (debug)
+                       zlog_debug("%s: can't get afi of prefix", __func__);
+               return;
+       }
+
+       /* loop check */
+       if (info_vrf->extra && info_vrf->extra->bgp_orig == bgp_vpn)
+               return;
+
+
+       if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) {
+               if (debug)
+                       zlog_debug("%s: skipping: %s", __func__, debugmsg);
+               return;
+       }
+
+       bgp_attr_dup(&static_attr, info_vrf->attr); /* shallow copy */
+
+       /*
+        * route map handling
+        */
+       if (bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]) {
+               struct bgp_info info;
+               route_map_result_t ret;
+
+               memset(&info, 0, sizeof(info));
+               info.peer = bgp_vpn->peer_self;
+               info.attr = &static_attr;
+               ret = route_map_apply(
+                       bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN],
+                       p, RMAP_BGP, &info);
+               if (RMAP_DENYMATCH == ret) {
+                       bgp_attr_flush(&static_attr); /* free any added parts */
+                       if (debug)
+                               zlog_debug(
+                                       "%s: vrf %s route map \"%s\" says DENY, returning",
+                                       __func__, bgp_vrf->name,
+                                       bgp_vrf->vpn_policy[afi]
+                                               .rmap[BGP_VPN_POLICY_DIR_TOVPN]
+                                               ->name);
+                       return;
+               }
+       }
+
+       if (debug) {
+               const char *s = "";
+
+               if (static_attr.ecommunity) {
+                       s = ecommunity_ecom2str(static_attr.ecommunity,
+                                               ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+               }
+               zlog_debug("%s: post route map static_attr.ecommunity{%s}",
+                          __func__, s);
+       }
+
+       /*
+        * Add the vpn-policy rt-list
+        */
+       struct ecommunity *old_ecom;
+       struct ecommunity *new_ecom;
+
+       old_ecom = static_attr.ecommunity;
+       if (old_ecom) {
+               new_ecom = ecommunity_merge(
+                       ecommunity_dup(old_ecom),
+                       bgp_vrf->vpn_policy[afi]
+                               .rtlist[BGP_VPN_POLICY_DIR_TOVPN]);
+               if (!old_ecom->refcnt)
+                       ecommunity_free(&old_ecom);
+       } else {
+               new_ecom = ecommunity_dup(
+                       bgp_vrf->vpn_policy[afi]
+                               .rtlist[BGP_VPN_POLICY_DIR_TOVPN]);
+       }
+       static_attr.ecommunity = new_ecom;
+       SET_FLAG(static_attr.flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
+
+       if (debug) {
+               const char *s = "";
+
+               if (static_attr.ecommunity) {
+                       s = ecommunity_ecom2str(static_attr.ecommunity,
+                                               ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+               }
+               zlog_debug("%s: post merge static_attr.ecommunity{%s}",
+                          __func__, s);
+       }
+
+       /* Nexthop */
+       /* 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
+                        */
+                       static_attr.nexthop.s_addr = nexthop->u.prefix4.s_addr;
+
+                       static_attr.mp_nexthop_global_in = nexthop->u.prefix4;
+                       static_attr.mp_nexthop_len = 4;
+                       break;
+
+               case AF_INET6:
+                       static_attr.mp_nexthop_global = nexthop->u.prefix6;
+                       static_attr.mp_nexthop_len = 16;
+                       break;
+
+               default:
+                       assert(0);
+               }
+       } else {
+               switch (afi) {
+               case AFI_IP:
+               default:
+                       /* Clear ipv4 */
+                       static_attr.mp_nexthop_global_in.s_addr = 0;
+                       static_attr.mp_nexthop_len = 4;
+                       static_attr.nexthop.s_addr = 0; /* self */
+                       static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+                       break;
+
+               case AFI_IP6:
+                       /* Clear ipv6 */
+                       memset(&static_attr.mp_nexthop_global, 0,
+                              sizeof(static_attr.mp_nexthop_global));
+                       static_attr.mp_nexthop_len = 16; /* bytes */
+                       break;
+               }
+       }
+
+       label_val = bgp_vrf->vpn_policy[afi].tovpn_label;
+       if (label_val == MPLS_LABEL_NONE) {
+               /* TBD get from label manager */
+               label = MPLS_LABEL_IMPLICIT_NULL;
+       } else {
+               encode_label(label_val, &label);
+       }
+
+       /* Set originator ID to "me" */
+       SET_FLAG(static_attr.flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID));
+       static_attr.originator_id = bgp_vpn->router_id;
+
+
+       new_attr = bgp_attr_intern(
+               &static_attr);  /* hashed refcounted everything */
+       bgp_attr_flush(&static_attr); /* free locally-allocated parts */
+
+       if (debug) {
+               const char *s = "";
+
+               if (new_attr->ecommunity) {
+                       s = ecommunity_ecom2str(new_attr->ecommunity,
+                                               ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+               }
+               zlog_debug("%s: new_attr->ecommunity{%s}", __func__, s);
+       }
+
+       /* Now new_attr is an allocated interned attr */
+
+       bn = bgp_afi_node_get(bgp_vpn->rib[afi][safi], afi, safi, p,
+                             &(bgp_vrf->vpn_policy[afi].tovpn_rd));
+
+       struct bgp_info *new_info;
+
+       new_info = leak_update(bgp_vpn, bn, new_attr, afi, safi, info_vrf,
+                              ZEBRA_ROUTE_BGP, BGP_ROUTE_IMPORTED, &label, 1,
+                              info_vrf, bgp_vrf, NULL, debug);
+
+       /*
+        * Routes actually installed in the vpn RIB must also be
+        * offered to all vrfs (because now they originate from
+        * the vpn RIB).
+        *
+        * Acceptance into other vrfs depends on rt-lists.
+        * Originating vrf will not accept the looped back route
+        * because of loop checking.
+        */
+       if (new_info)
+               vpn_leak_to_vrf_update(bgp_vrf, new_info);
+}
+
+void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn,       /* to */
+                               struct bgp *bgp_vrf,       /* from */
+                               struct bgp_info *info_vrf) /* route */
+{
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
+       struct prefix *p = &info_vrf->net->p;
+       afi_t afi = family2afi(p->family);
+       safi_t safi = SAFI_MPLS_VPN;
+       struct bgp_info *bi;
+       struct bgp_node *bn;
+       const char *debugmsg;
+
+       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) {
+
+               if (debug)
+                       zlog_debug("%s: wrong sub_type %d", __func__,
+                                  info_vrf->sub_type);
+               return;
+       }
+       if (!bgp_vpn)
+               return;
+
+       if (!afi) {
+               if (debug)
+                       zlog_debug("%s: can't get afi of prefix", __func__);
+               return;
+       }
+
+       if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) {
+               if (debug)
+                       zlog_debug("%s: skipping: %s", __func__, debugmsg);
+               return;
+       }
+
+       if (debug)
+               zlog_debug("%s: withdrawing (info_vrf=%p)", __func__, info_vrf);
+
+       bn = bgp_afi_node_get(bgp_vpn->rib[afi][safi], afi, safi, p,
+                             &(bgp_vrf->vpn_policy[afi].tovpn_rd));
+
+       /*
+        * vrf -> vpn
+        * match original bi imported from
+        */
+       for (bi = (bn ? bn->info : NULL); bi; bi = bi->next) {
+               if (bi->extra && bi->extra->parent == info_vrf) {
+                       break;
+               }
+       }
+
+       if (bi) {
+               /* withdraw from looped vrfs as well */
+               vpn_leak_to_vrf_withdraw(bgp_vpn, bi);
+
+               bgp_aggregate_decrement(bgp_vpn, p, bi, afi, safi);
+               bgp_info_delete(bn, bi);
+               bgp_process(bgp_vpn, bn, afi, safi);
+       }
+       bgp_unlock_node(bn);
+}
+
+void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn, /* to */
+                                   struct bgp *bgp_vrf, /* from */
+                                   afi_t afi)
+{
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
+       struct bgp_node *prn;
+       safi_t safi = SAFI_MPLS_VPN;
+
+       /*
+        * Walk vpn table, delete bi with parent == bgp_vrf
+        * Walk vpn table, delete bi with bgp_orig == bgp_vrf
+        */
+       for (prn = bgp_table_top(bgp_vpn->rib[afi][safi]); prn;
+            prn = bgp_route_next(prn)) {
+
+               struct bgp_table *table;
+               struct bgp_node *bn;
+               struct bgp_info *bi;
+
+               /* This is the per-RD table of prefixes */
+               table = prn->info;
+
+               if (!table)
+                       continue;
+
+               for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) {
+
+                       char buf[PREFIX2STR_BUFFER];
+
+                       if (debug && bn->info) {
+                               zlog_debug(
+                                       "%s: looking at prefix %s", __func__,
+                                       prefix2str(&bn->p, buf, sizeof(buf)));
+                       }
+
+                       for (bi = bn->info; bi; bi = bi->next) {
+                               if (debug)
+                                       zlog_debug("%s: type %d, sub_type %d",
+                                                  __func__, bi->type,
+                                                  bi->sub_type);
+                               if (bi->sub_type != BGP_ROUTE_IMPORTED)
+                                       continue;
+                               if (!bi->extra)
+                                       continue;
+                               if ((struct bgp *)bi->extra->bgp_orig
+                                   == bgp_vrf) {
+                                       /* delete route */
+                                       if (debug)
+                                               zlog_debug("%s: deleting it\n",
+                                                          __func__);
+                                       bgp_aggregate_decrement(bgp_vpn, &bn->p,
+                                                               bi, afi, safi);
+                                       bgp_info_delete(bn, bi);
+                                       bgp_process(bgp_vpn, bn, afi, safi);
+                               }
+                       }
+               }
+       }
+}
+
+void vpn_leak_from_vrf_update_all(struct bgp *bgp_vpn, /* to */
+                                 struct bgp *bgp_vrf, /* from */
+                                 afi_t afi)
+{
+       struct bgp_node *bn;
+       struct bgp_info *bi;
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
+
+       if (debug)
+               zlog_debug("%s: entry, afi=%d, vrf=%s", __func__, afi,
+                          bgp_vrf->name);
+
+       for (bn = bgp_table_top(bgp_vrf->rib[afi][SAFI_UNICAST]); bn;
+            bn = bgp_route_next(bn)) {
+
+               if (debug)
+                       zlog_debug("%s: node=%p", __func__, bn);
+
+               for (bi = bn->info; bi; bi = bi->next) {
+                       if (debug)
+                               zlog_debug(
+                                       "%s: calling vpn_leak_from_vrf_update",
+                                       __func__);
+                       vpn_leak_from_vrf_update(bgp_vpn, bgp_vrf, bi);
+               }
+       }
+}
+
+static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf,       /* to */
+                                         struct bgp *bgp_vpn,       /* from */
+                                         struct bgp_info *info_vpn) /* route */
+{
+       struct prefix *p = &info_vpn->net->p;
+       afi_t afi = family2afi(p->family);
+
+       struct bgp_redist *red;
+       struct attr static_attr = {0};
+       struct attr *new_attr = NULL;
+       struct bgp_node *bn;
+       safi_t safi = SAFI_UNICAST;
+       const char *debugmsg;
+       struct prefix nexthop_orig;
+       mpls_label_t *pLabels = NULL;
+       int num_labels = 0;
+
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
+
+       if (!vpn_leak_from_vpn_active(bgp_vrf, afi, &debugmsg, &red)) {
+               if (debug)
+                       zlog_debug("%s: skipping: %s", __func__, debugmsg);
+               return;
+       }
+
+       /* Check for intersection of route targets */
+       if (!ecom_intersect(
+                   bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN],
+                   info_vpn->attr->ecommunity)) {
+
+               return;
+       }
+
+       if (debug)
+               zlog_debug("%s: updating to vrf %s", __func__, bgp_vrf->name);
+
+       bgp_attr_dup(&static_attr, info_vpn->attr); /* shallow copy */
+
+       /*
+        * Nexthop: stash and clear
+        *
+        * Nexthop is valid in context of VPN core, but not in destination vrf.
+        * Stash it for later label resolution by vrf ingress path and then
+        * overwrite with 0, i.e., "me", for the sake of vrf advertisement.
+        */
+       uint8_t nhfamily = NEXTHOP_FAMILY(info_vpn->attr->mp_nexthop_len);
+
+       memset(&nexthop_orig, 0, sizeof(nexthop_orig));
+       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;
+
+               static_attr.nexthop.s_addr = 0; /* self */
+               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;
+
+               memset(&static_attr.mp_nexthop_global, 0,
+                      sizeof(static_attr.mp_nexthop_global)); /* clear */
+               static_attr.mp_nexthop_len = 16;               /* bytes */
+               break;
+       }
+
+
+       /*
+        * route map handling
+        * For now, we apply two route maps: the "redist" route map and the
+        * vpn-policy route map. Once we finalize CLI syntax, one of these
+        * route maps will probably go away.
+        */
+       if (red->rmap.map) {
+               struct bgp_info info;
+               route_map_result_t ret;
+
+               memset(&info, 0, sizeof(info));
+               info.peer = bgp_vrf->peer_self;
+               info.attr = &static_attr;
+               ret = route_map_apply(red->rmap.map, p, RMAP_BGP, &info);
+               if (RMAP_DENYMATCH == ret) {
+                       bgp_attr_flush(&static_attr); /* free any added parts */
+                       if (debug)
+                               zlog_debug(
+                                       "%s: vrf %s redist route map \"%s\" says DENY, skipping",
+                                       __func__, bgp_vrf->name,
+                                       red->rmap.name);
+                       return;
+               }
+       }
+       if (bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_FROMVPN]) {
+               struct bgp_info info;
+               route_map_result_t ret;
+
+               memset(&info, 0, sizeof(info));
+               info.peer = bgp_vrf->peer_self;
+               info.attr = &static_attr;
+               ret = route_map_apply(bgp_vrf->vpn_policy[afi]
+                                             .rmap[BGP_VPN_POLICY_DIR_FROMVPN],
+                                     p, RMAP_BGP, &info);
+               if (RMAP_DENYMATCH == ret) {
+                       bgp_attr_flush(&static_attr); /* free any added parts */
+                       if (debug)
+                               zlog_debug(
+                                       "%s: vrf %s vpn-policy route map \"%s\" says DENY, returning",
+                                       __func__, bgp_vrf->name,
+                                       bgp_vrf->vpn_policy[afi]
+                                               .rmap[BGP_VPN_POLICY_DIR_FROMVPN]
+                                               ->name);
+                       return;
+               }
+       }
+
+       new_attr = bgp_attr_intern(&static_attr);
+       bgp_attr_flush(&static_attr);
+
+       bn = bgp_afi_node_get(bgp_vrf->rib[afi][safi], afi, safi, p, NULL);
+
+       /*
+        * ensure labels are copied
+        */
+       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 (debug) {
+               char buf_prefix[PREFIX_STRLEN];
+               prefix2str(p, buf_prefix, sizeof(buf_prefix));
+               zlog_debug("%s: pfx %s: num_labels %d", __func__, buf_prefix,
+                          num_labels);
+       }
+
+       leak_update(bgp_vrf, bn, new_attr, afi, safi, info_vpn, ZEBRA_ROUTE_BGP,
+                   BGP_ROUTE_IMPORTED, pLabels, num_labels,
+                   info_vpn, /* parent */
+                   bgp_vpn, &nexthop_orig, debug);
+}
+
+void vpn_leak_to_vrf_update(struct bgp *bgp_vpn,       /* from */
+                           struct bgp_info *info_vpn) /* route */
+{
+       struct listnode *mnode, *mnnode;
+       struct bgp *bgp;
+
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
+
+       if (debug)
+               zlog_debug("%s: start (info_vpn=%p)", __func__, info_vpn);
+
+       /* Loop over VRFs */
+       for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+
+               if (!info_vpn->extra
+                   || info_vpn->extra->bgp_orig != bgp) { /* no loop */
+                       vpn_leak_to_vrf_update_onevrf(bgp, bgp_vpn, info_vpn);
+               }
+       }
+}
+
+void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn,       /* from */
+                             struct bgp_info *info_vpn) /* route */
+{
+       struct prefix *p = &info_vpn->net->p;
+       afi_t afi = family2afi(p->family);
+       safi_t safi = SAFI_UNICAST;
+       struct bgp *bgp;
+       struct listnode *mnode, *mnnode;
+       struct bgp_redist *red;
+       struct bgp_node *bn;
+       struct bgp_info *bi;
+       const char *debugmsg;
+
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
+
+       if (debug)
+               zlog_debug("%s: start (info_vpn=%p)", __func__, info_vpn);
+
+
+       /* Loop over VRFs */
+       for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+               if (!vpn_leak_from_vpn_active(bgp, afi, &debugmsg, &red)) {
+                       if (debug)
+                               zlog_debug("%s: skipping: %s", __func__,
+                                          debugmsg);
+                       continue;
+               }
+
+               /* Check for intersection of route targets */
+               if (!ecom_intersect(bgp->vpn_policy[afi]
+                                           .rtlist[BGP_VPN_POLICY_DIR_FROMVPN],
+                                   info_vpn->attr->ecommunity)) {
+
+                       continue;
+               }
+
+               if (debug)
+                       zlog_debug("%s: withdrawing from vrf %s", __func__,
+                                  bgp->name);
+
+               bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL);
+               for (bi = (bn ? bn->info : NULL); bi; bi = bi->next) {
+                       if (bi->extra
+                           && (struct bgp_info *)bi->extra->parent
+                                      == info_vpn) {
+                               break;
+                       }
+               }
+
+               if (bi) {
+                       if (debug)
+                               zlog_debug("%s: deleting bi %p", __func__, bi);
+                       bgp_aggregate_decrement(bgp, p, bi, afi, safi);
+                       bgp_info_delete(bn, bi);
+                       bgp_process(bgp, bn, afi, safi);
+               }
+               bgp_unlock_node(bn);
+       }
+}
+
+void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, /* to */
+                                 afi_t afi)
+{
+       struct bgp_node *bn;
+       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
+        */
+       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) {
+
+                               /* delete route */
+                               bgp_aggregate_decrement(bgp_vrf, &bn->p, bi,
+                                                       afi, safi);
+                               bgp_info_delete(bn, bi);
+                               bgp_process(bgp_vrf, bn, afi, safi);
+                       }
+               }
+       }
+}
+
+void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, /* to */
+                               struct bgp *bgp_vpn, /* from */
+                               afi_t afi)
+{
+       struct prefix_rd prd;
+       struct bgp_node *prn;
+       safi_t safi = SAFI_MPLS_VPN;
+
+       /*
+        * Walk vpn table
+        */
+       for (prn = bgp_table_top(bgp_vpn->rib[afi][safi]); prn;
+            prn = bgp_route_next(prn)) {
+
+               struct bgp_table *table;
+               struct bgp_node *bn;
+               struct bgp_info *bi;
+
+               memset(&prd, 0, sizeof(prd));
+               prd.family = AF_UNSPEC;
+               prd.prefixlen = 64;
+               memcpy(prd.val, prn->p.u.val, 8);
+
+               /* This is the per-RD table of prefixes */
+               table = prn->info;
+
+               if (!table)
+                       continue;
+
+               for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) {
+
+                       for (bi = bn->info; bi; bi = bi->next) {
+
+                               if (bi->extra && bi->extra->bgp_orig == bgp_vrf)
+                                       continue;
+
+                               vpn_leak_to_vrf_update_onevrf(bgp_vrf, bgp_vpn,
+                                                             bi);
+                       }
+               }
+       }
+}
+
+static void vpn_policy_routemap_update(struct bgp *bgp, const char *rmap_name)
+{
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT);
+       afi_t afi;
+       struct route_map *rmap;
+
+       if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT
+           && bgp->inst_type != BGP_INSTANCE_TYPE_VRF) {
+
+               return;
+       }
+
+       rmap = route_map_lookup_by_name(rmap_name); /* NULL if deleted */
+
+       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,
+                              bgp->vpn_policy[afi]
+                                      .rmap_name[BGP_VPN_POLICY_DIR_TOVPN])) {
+
+                       if (debug)
+                               zlog_debug(
+                                       "%s: rmap \"%s\" matches vrf-policy tovpn for as %d afi %s",
+                                       __func__, rmap_name, bgp->as,
+                                       afi2str(afi));
+
+                       vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                          bgp_get_default(), bgp);
+                       if (debug)
+                               zlog_debug("%s: after vpn_leak_prechange",
+                                          __func__);
+
+                       if (!rmap)
+                               bgp->vpn_policy[afi]
+                                       .rmap[BGP_VPN_POLICY_DIR_TOVPN] = NULL;
+
+                       vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                           bgp_get_default(), bgp);
+                       if (debug)
+                               zlog_debug("%s: after vpn_leak_postchange",
+                                          __func__);
+               }
+
+               /*
+                * vpn -> vrf leaking currently can have two route-maps:
+                * 1. the vpn-policy tovpn route-map
+                * 2. the (per-afi) redistribute vpn route-map
+                */
+               char *mapname_vpn_policy =
+                       bgp->vpn_policy[afi]
+                               .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN];
+               struct bgp_redist *red = NULL;
+
+               if (vpn_leak_from_vpn_active(bgp, afi, NULL, &red)
+                   && ((mapname_vpn_policy
+                        && !strcmp(rmap_name, mapname_vpn_policy))
+                       || (red && red->rmap.name
+                           && !strcmp(red->rmap.name, rmap_name)))) {
+
+                       if (debug)
+                               zlog_debug(
+                                       "%s: rmap \"%s\" matches vrf-policy fromvpn"
+                                       " for as %d afi %s",
+                                       __func__, rmap_name, bgp->as,
+                                       afi2str(afi));
+
+                       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;
+
+                       vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, afi,
+                                           bgp_get_default(), bgp);
+               }
+       }
+}
+
+void vpn_policy_routemap_event(const char *rmap_name)
+{
+       int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT);
+       struct listnode *mnode, *mnnode;
+       struct bgp *bgp;
+
+       if (debug)
+               zlog_debug("%s: entry", __func__);
+
+       if (bm->bgp == NULL) /* may be called during cleanup */
+               return;
+
+       for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp))
+               vpn_policy_routemap_update(bgp, rmap_name);
+}
+
 /* For testing purpose, static route of MPLS-VPN. */
 DEFUN (vpnv4_network,
        vpnv4_network_cmd,
index 5c11f7526cea7db720ed6bbe7abdf4a6c81dc794..d0ad8ac8462b7fb20573c1fb62c9590682fae18e 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "bgpd/bgp_route.h"
 #include "bgpd/bgp_rd.h"
+#include "bgpd/bgp_zebra.h"
 
 #define MPLS_LABEL_IS_SPECIAL(label) ((label) <= MPLS_LABEL_EXTENSION)
 #define MPLS_LABEL_IS_NULL(label)                                              \
@@ -51,4 +52,122 @@ extern int bgp_show_mpls_vpn(struct vty *vty, afi_t afi, struct prefix_rd *prd,
                             enum bgp_show_type type, void *output_arg,
                             int tags, u_char use_json);
 
+extern void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, struct bgp *bgp_vrf,
+                                    struct bgp_info *info_vrf);
+
+extern void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, struct bgp *bgp_vrf,
+                                      struct bgp_info *info_vrf);
+
+extern void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn,
+                                          struct bgp *bgp_vrf, afi_t afi);
+
+extern void vpn_leak_from_vrf_update_all(struct bgp *bgp_vpn,
+                                        struct bgp *bgp_vrf, afi_t afi);
+
+extern void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, afi_t afi);
+
+extern void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, struct bgp *bgp_vpn,
+                                      afi_t afi);
+
+extern void vpn_leak_to_vrf_update(struct bgp *bgp_vpn,
+                                  struct bgp_info *info_vpn);
+
+extern void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn,
+                                    struct bgp_info *info_vpn);
+
+extern void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi);
+extern void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi);
+
+static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
+                                        const char **pmsg)
+{
+       /* Is vrf configured to export to vpn? */
+       if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
+                       BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) {
+               if (pmsg)
+                       *pmsg = "export not set";
+               return 0;
+       }
+
+       /* Is there an RT list set? */
+       if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) {
+               if (pmsg)
+                       *pmsg = "rtlist tovpn not defined";
+               return 0;
+       }
+
+       /* Is there an RD set? */
+       if (!CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
+                       BGP_VPN_POLICY_TOVPN_RD_SET)) {
+               if (pmsg)
+                       *pmsg = "rd not defined";
+               return 0;
+       }
+       return 1;
+}
+
+static inline int vpn_leak_from_vpn_active(struct bgp *bgp_vrf, afi_t afi,
+                                          const char **pmsg,
+                                          struct bgp_redist **pred)
+{
+       struct bgp_redist *red;
+
+       if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF
+           && bgp_vrf->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
+
+               if (pmsg)
+                       *pmsg = "destination bgp instance neither vrf nor default";
+               return 0;
+       }
+
+       /* Hijack zebra redist bits for this route type */
+       red = bgp_redist_lookup(bgp_vrf, afi, ZEBRA_ROUTE_BGP_VPN, 0);
+       if (red) {
+               if (pred)
+                       *pred = red;
+       } else {
+               if (pmsg)
+                       *pmsg = "redist not set";
+               return 0;
+       }
+       if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]) {
+               if (pmsg)
+                       *pmsg = "rtlist fromvpn not defined";
+               return 0;
+       }
+       return 1;
+}
+
+static inline void vpn_leak_prechange(vpn_policy_direction_t direction,
+                                     afi_t afi, struct bgp *bgp_vpn,
+                                     struct bgp *bgp_vrf)
+{
+       if (direction == BGP_VPN_POLICY_DIR_FROMVPN)
+               vpn_leak_to_vrf_withdraw_all(bgp_vrf, afi);
+       if (direction == BGP_VPN_POLICY_DIR_TOVPN)
+               vpn_leak_from_vrf_withdraw_all(bgp_vpn, bgp_vrf, afi);
+}
+
+static inline void vpn_leak_postchange(vpn_policy_direction_t direction,
+                                      afi_t afi, struct bgp *bgp_vpn,
+                                      struct bgp *bgp_vrf)
+{
+       if (direction == BGP_VPN_POLICY_DIR_FROMVPN)
+               vpn_leak_to_vrf_update_all(bgp_vrf, bgp_vpn, afi);
+       if (direction == BGP_VPN_POLICY_DIR_TOVPN) {
+
+               if (bgp_vrf->vpn_policy[afi].tovpn_label
+                   != bgp_vrf->vpn_policy[afi]
+                              .tovpn_zebra_vrf_label_last_sent) {
+                       vpn_leak_zebra_vrf_label_update(bgp_vrf, afi);
+               }
+
+               vpn_leak_from_vrf_update_all(bgp_vpn, bgp_vrf, afi);
+       }
+       if (direction == BGP_VPN_POLICY_DIR_TOVPN)
+               vpn_leak_from_vrf_update_all(bgp_vpn, bgp_vrf, afi);
+}
+
+extern void vpn_policy_routemap_event(const char *rmap_name);
+
 #endif /* _QUAGGA_BGP_MPLSVPN_H */
index 35e9faf3ff4cf93bc631aec41cb18374744106eb..26f96d684a340214056d0af66978cd764bc02d58 100644 (file)
@@ -1364,6 +1364,16 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_info *ri,
        }
 #endif
 
+       if (((afi == AFI_IP) || (afi == AFI_IP6))
+           && ((safi == SAFI_MPLS_VPN) || (safi == SAFI_UNICAST))
+           && (ri->type == ZEBRA_ROUTE_BGP)
+           && (ri->sub_type == BGP_ROUTE_IMPORTED)) {
+
+               /* Applies to routes leaked vpn->vrf and vrf->vpn */
+
+               samepeer_safe = 1;
+       }
+
        /* With addpath we may be asked to TX all kinds of paths so make sure
         * ri is valid */
        if (!CHECK_FLAG(ri->flags, BGP_INFO_VALID)
@@ -1852,17 +1862,30 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn,
                            && (ri != old_select))
                                bgp_info_reap(rn, ri);
 
+                       if (debug)
+                               zlog_debug("%s: ri %p in holddown", __func__,
+                                          ri);
+
                        continue;
                }
 
                if (ri->peer && ri->peer != bgp->peer_self
                    && !CHECK_FLAG(ri->peer->sflags, PEER_STATUS_NSF_WAIT))
-                       if (ri->peer->status != Established)
+                       if (ri->peer->status != Established) {
+
+                               if (debug)
+                                       zlog_debug(
+                                               "%s: ri %p non self peer %s not estab state",
+                                               __func__, ri, ri->peer->host);
+
                                continue;
+                       }
 
                if (bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)
                    && (!CHECK_FLAG(ri->flags, BGP_INFO_DMED_SELECTED))) {
                        bgp_info_unset_flag(rn, ri, BGP_INFO_DMED_CHECK);
+                       if (debug)
+                               zlog_debug("%s: ri %p dmed", __func__, ri);
                        continue;
                }
 
@@ -1969,8 +1992,8 @@ int subgroup_process_announce_selected(struct update_subgroup *subgrp,
        if (BGP_DEBUG(update, UPDATE_OUT)) {
                char buf_prefix[PREFIX_STRLEN];
                prefix2str(p, buf_prefix, sizeof(buf_prefix));
-               zlog_debug("%s: p=%s, selected=%p",
-                   __func__, buf_prefix, selected);
+               zlog_debug("%s: p=%s, selected=%p", __func__, buf_prefix,
+                          selected);
        }
 
        /* First update is deferred until ORF or ROUTE-REFRESH is received */
@@ -2064,6 +2087,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
        struct bgp_info *new_select;
        struct bgp_info *old_select;
        struct bgp_info_pair old_and_new;
+       char pfx_buf[PREFIX2STR_BUFFER];
+       int debug = 0;
 
        /* Is it end of initial update? (after startup) */
        if (!rn) {
@@ -2081,6 +2106,13 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
                return;
        }
 
+       debug = bgp_debug_bestpath(&rn->p);
+       if (debug) {
+               prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf));
+               zlog_debug("%s: p=%s afi=%s, safi=%s start", __func__, pfx_buf,
+                          afi2str(afi), safi2str(safi));
+       }
+
        /* Best path selection. */
        bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], &old_and_new,
                           afi, safi);
@@ -2121,6 +2153,14 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
                bgp_unregister_for_label(rn);
        }
 
+       if (debug) {
+               prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf));
+               zlog_debug(
+                       "%s: p=%s afi=%s, safi=%s, old_select=%p, new_select=%p",
+                       __func__, pfx_buf, afi2str(afi), safi2str(safi),
+                       old_select, new_select);
+       }
+
        /* If best route remains the same and this is not due to user-initiated
         * clear, see exactly what needs to be done.
         */
@@ -2135,11 +2175,16 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
                        vnc_import_bgp_exterior_add_route(bgp, p, old_select);
 #endif
                        if (bgp_fibupd_safi(safi)
-                           && !bgp_option_check(BGP_OPT_NO_FIB)
-                           && new_select->type == ZEBRA_ROUTE_BGP
-                           && new_select->sub_type == BGP_ROUTE_NORMAL)
-                               bgp_zebra_announce(rn, p, old_select, bgp, afi,
-                                                  safi);
+                           && !bgp_option_check(BGP_OPT_NO_FIB)) {
+
+                               if (new_select->type == ZEBRA_ROUTE_BGP
+                                   && (new_select->sub_type == BGP_ROUTE_NORMAL
+                                       || new_select->sub_type
+                                                  == BGP_ROUTE_IMPORTED))
+
+                                       bgp_zebra_announce(rn, p, old_select,
+                                                          bgp, afi, safi);
+                       }
                }
                UNSET_FLAG(old_select->flags, BGP_INFO_MULTIPATH_CHG);
                bgp_zebra_clear_route_change_flags(rn);
@@ -2186,6 +2231,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
        if (old_select)
                bgp_info_unset_flag(rn, old_select, BGP_INFO_SELECTED);
        if (new_select) {
+               if (debug)
+                       zlog_debug("%s: setting SELECTED flag", __func__);
                bgp_info_set_flag(rn, new_select, BGP_INFO_SELECTED);
                bgp_info_unset_flag(rn, new_select, BGP_INFO_ATTR_CHANGED);
                UNSET_FLAG(new_select->flags, BGP_INFO_MULTIPATH_CHG);
@@ -2221,13 +2268,17 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
            && !bgp_option_check(BGP_OPT_NO_FIB)) {
                if (new_select && new_select->type == ZEBRA_ROUTE_BGP
                    && (new_select->sub_type == BGP_ROUTE_NORMAL
-                       || new_select->sub_type == BGP_ROUTE_AGGREGATE))
+                       || new_select->sub_type == BGP_ROUTE_AGGREGATE
+                       || new_select->sub_type == BGP_ROUTE_IMPORTED))
+
                        bgp_zebra_announce(rn, p, new_select, bgp, afi, safi);
                else {
                        /* Withdraw the route from the kernel. */
                        if (old_select && old_select->type == ZEBRA_ROUTE_BGP
                            && (old_select->sub_type == BGP_ROUTE_NORMAL
-                               || old_select->sub_type == BGP_ROUTE_AGGREGATE))
+                               || old_select->sub_type == BGP_ROUTE_AGGREGATE
+                               || old_select->sub_type == BGP_ROUTE_IMPORTED))
+
                                bgp_zebra_withdraw(p, old_select, safi);
                }
        }
@@ -3128,6 +3179,18 @@ int bgp_update(struct peer *peer, struct prefix *p, u_int32_t addpath_id,
                bgp_process(bgp, rn, afi, safi);
                bgp_unlock_node(rn);
 
+               if (SAFI_UNICAST == safi
+                   && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+                       || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+                       vpn_leak_from_vrf_update(bgp_get_default(), bgp, ri);
+               }
+               if ((SAFI_MPLS_VPN == safi)
+                   && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+                       vpn_leak_to_vrf_update(bgp, ri);
+               }
+
 #if ENABLE_BGP_VNC
                if (SAFI_MPLS_VPN == safi) {
                        mpls_label_t label_decoded = decode_label(label);
@@ -3244,6 +3307,16 @@ int bgp_update(struct peer *peer, struct prefix *p, u_int32_t addpath_id,
        /* Process change. */
        bgp_process(bgp, rn, afi, safi);
 
+       if (SAFI_UNICAST == safi
+           && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+               || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+               vpn_leak_from_vrf_update(bgp_get_default(), bgp, new);
+       }
+       if ((SAFI_MPLS_VPN == safi)
+           && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+               vpn_leak_to_vrf_update(bgp, new);
+       }
 #if ENABLE_BGP_VNC
        if (SAFI_MPLS_VPN == safi) {
                mpls_label_t label_decoded = decode_label(label);
@@ -3282,6 +3355,18 @@ filtered:
                if (safi == SAFI_EVPN)
                        bgp_evpn_unimport_route(bgp, afi, safi, p, ri);
 
+               if (SAFI_UNICAST == safi
+                   && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+                       || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+                       vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, ri);
+               }
+               if ((SAFI_MPLS_VPN == safi)
+                   && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+                       vpn_leak_to_vrf_withdraw(bgp, ri);
+               }
+
                bgp_rib_remove(rn, ri, peer, afi, safi);
        }
 
@@ -3368,9 +3453,19 @@ int bgp_withdraw(struct peer *peer, struct prefix *p, u_int32_t addpath_id,
        }
 
        /* Withdraw specified route from routing table. */
-       if (ri && !CHECK_FLAG(ri->flags, BGP_INFO_HISTORY))
+       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)) {
+               if (SAFI_UNICAST == safi
+                   && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+                       || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+                       vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, ri);
+               }
+               if ((SAFI_MPLS_VPN == safi)
+                   && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+                       vpn_leak_to_vrf_withdraw(bgp, ri);
+               }
+       } else if (bgp_debug_update(peer, p, NULL, 1)) {
                bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
                                        addpath_id ? 1 : 0, addpath_id, pfx_buf,
                                        sizeof(pfx_buf));
@@ -3854,7 +3949,9 @@ static void bgp_cleanup_table(struct bgp_table *table, safi_t safi)
                        if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED)
                            && ri->type == ZEBRA_ROUTE_BGP
                            && (ri->sub_type == BGP_ROUTE_NORMAL
-                               || ri->sub_type == BGP_ROUTE_AGGREGATE)) {
+                               || ri->sub_type == BGP_ROUTE_AGGREGATE
+                               || ri->sub_type == BGP_ROUTE_IMPORTED)) {
+
                                if (bgp_fibupd_safi(safi))
                                        bgp_zebra_withdraw(&rn->p, ri, safi);
                                bgp_info_reap(rn, ri);
@@ -4255,6 +4352,15 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p,
                        /* Process change. */
                        bgp_aggregate_increment(bgp, p, ri, afi, safi);
                        bgp_process(bgp, rn, afi, safi);
+
+                       if (SAFI_UNICAST == safi
+                           && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+                               || bgp->inst_type
+                                          == BGP_INSTANCE_TYPE_DEFAULT)) {
+                               vpn_leak_from_vrf_update(bgp_get_default(), bgp,
+                                                        ri);
+                       }
+
                        bgp_unlock_node(rn);
                        aspath_unintern(&attr.aspath);
                        return;
@@ -4302,6 +4408,12 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p,
        /* Process change. */
        bgp_process(bgp, rn, afi, safi);
 
+       if (SAFI_UNICAST == safi
+           && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+               || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+               vpn_leak_from_vrf_update(bgp_get_default(), bgp, new);
+       }
+
        /* Unintern original. */
        aspath_unintern(&attr.aspath);
 }
@@ -4322,6 +4434,11 @@ void bgp_static_withdraw(struct bgp *bgp, struct prefix *p, afi_t afi,
 
        /* Withdraw static BGP route from routing table. */
        if (ri) {
+               if (SAFI_UNICAST == safi
+                   && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+                       || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+                       vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, ri);
+               }
                bgp_aggregate_decrement(bgp, p, ri, afi, safi);
                bgp_unlink_nexthop(ri);
                bgp_info_delete(rn, ri);
@@ -4357,6 +4474,10 @@ static void bgp_static_withdraw_safi(struct bgp *bgp, struct prefix *p,
                        ri->peer, NULL, p, prd, ri->attr, afi, safi, ri->type,
                        1); /* Kill, since it is an administrative change */
 #endif
+               if (SAFI_MPLS_VPN == safi
+                   && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+                       vpn_leak_to_vrf_withdraw(bgp, ri);
+               }
                bgp_aggregate_decrement(bgp, p, ri, afi, safi);
                bgp_info_delete(rn, ri);
                bgp_process(bgp, rn, afi, safi);
@@ -4485,6 +4606,11 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p,
                        /* Process change. */
                        bgp_aggregate_increment(bgp, p, ri, afi, safi);
                        bgp_process(bgp, rn, afi, safi);
+
+                       if (SAFI_MPLS_VPN == safi
+                           && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+                               vpn_leak_to_vrf_update(bgp, ri);
+                       }
 #if ENABLE_BGP_VNC
                        rfapiProcessUpdate(ri->peer, NULL, p, &bgp_static->prd,
                                           ri->attr, afi, safi, ri->type,
@@ -4521,6 +4647,10 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p,
        /* Process change. */
        bgp_process(bgp, rn, afi, safi);
 
+       if (SAFI_MPLS_VPN == safi
+           && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+               vpn_leak_to_vrf_update(bgp, new);
+       }
 #if ENABLE_BGP_VNC
        rfapiProcessUpdate(new->peer, NULL, p, &bgp_static->prd, new->attr, afi,
                           safi, new->type, new->sub_type, &label);
@@ -5985,6 +6115,14 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
                                bgp_process(bgp, bn, afi, SAFI_UNICAST);
                                bgp_unlock_node(bn);
                                aspath_unintern(&attr.aspath);
+
+                               if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+                                   || (bgp->inst_type
+                                       == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+                                       vpn_leak_from_vrf_update(
+                                               bgp_get_default(), bgp, bi);
+                               }
                                return;
                        }
                }
@@ -5997,6 +6135,12 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
                bgp_info_add(bn, new);
                bgp_unlock_node(bn);
                bgp_process(bgp, bn, afi, SAFI_UNICAST);
+
+               if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+                   || (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+                       vpn_leak_from_vrf_update(bgp_get_default(), bgp, new);
+               }
        }
 
        /* Unintern original. */
@@ -6023,6 +6167,12 @@ void bgp_redistribute_delete(struct bgp *bgp, struct prefix *p, u_char type,
                                break;
 
                if (ri) {
+                       if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+                           || (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+                               vpn_leak_from_vrf_withdraw(bgp_get_default(),
+                                                          bgp, ri);
+                       }
                        bgp_aggregate_decrement(bgp, p, ri, afi, SAFI_UNICAST);
                        bgp_info_delete(rn, ri);
                        bgp_process(bgp, rn, afi, SAFI_UNICAST);
@@ -6048,6 +6198,12 @@ void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type,
                                break;
 
                if (ri) {
+                       if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+                           || (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+                               vpn_leak_from_vrf_withdraw(bgp_get_default(),
+                                                          bgp, ri);
+                       }
                        bgp_aggregate_decrement(bgp, &rn->p, ri, afi,
                                                SAFI_UNICAST);
                        bgp_info_delete(rn, ri);
index dffe2b8ddcfe91197487e7223bc6b0239608aa70..748c4f911092718b5f4abcac11190aafe81a812f 100644 (file)
@@ -114,6 +114,30 @@ struct bgp_info_extra {
        /* For imported routes into a VNI (or VRF), this points to the parent.
         */
        void *parent;
+
+       /*
+        * Some tunnelish parameters follow. Maybe consolidate into an
+        * internal tunnel structure?
+        */
+
+       /*
+        * Original bgp instance for imported routes. Needed for:
+        * 1. Find all routes from a specific vrf for deletion
+        * 2. vrf context of original nexthop
+        *
+        * Store pointer to bgp instance rather than bgp->vrf_id because
+        * bgp->vrf_id is not always valid (or may change?).
+        *
+        * Set to NULL if route is not imported from another bgp instance.
+        */
+       struct bgp *bgp_orig;
+
+       /*
+        * Nexthop in context of original bgp instance. Needed
+        * for label resolution of core mpls routes exported to a vrf.
+        * Set nexthop_orig.family to 0 if not valid.
+        */
+       struct prefix nexthop_orig;
 };
 
 struct bgp_info {
@@ -179,6 +203,7 @@ struct bgp_info {
 #ifdef ENABLE_BGP_VNC
 # define BGP_ROUTE_RFP          4 
 #endif
+#define BGP_ROUTE_IMPORTED     5        /* from another bgp instance/safi */
 
        u_short instance;
 
index 5371b372398187e55b9d870e5750ccdd0456dce7..b79c6a7c3a9072393667833945363062886d584c 100644 (file)
@@ -57,6 +57,7 @@
 #include "bgpd/bgp_evpn.h"
 #include "bgpd/bgp_evpn_private.h"
 #include "bgpd/bgp_evpn_vty.h"
+#include "bgpd/bgp_mplsvpn.h"
 
 #if ENABLE_BGP_VNC
 #include "bgpd/rfapi/bgp_rfapi_cfg.h"
@@ -3096,13 +3097,17 @@ static int bgp_route_map_process_update_cb(char *rmap_name)
        struct listnode *node, *nnode;
        struct bgp *bgp;
 
-       for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+       for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
                bgp_route_map_process_update(bgp, rmap_name, 1);
 
 #if ENABLE_BGP_VNC
-       zlog_debug("%s: calling vnc_routemap_update", __func__);
-       vnc_routemap_update(bgp, __func__);
+               /* zlog_debug("%s: calling vnc_routemap_update", __func__); */
+               vnc_routemap_update(bgp, __func__);
 #endif
+       }
+
+       vpn_policy_routemap_event(rmap_name);
+
        return 0;
 }
 
index c8e503d72aa419f962eb2cda16e5dcb9053b8c3b..4a5633c94d07f706c0dcee53fef08d324690614c 100644 (file)
@@ -6112,6 +6112,526 @@ ALIAS_HIDDEN(no_neighbor_addpath_tx_bestpath_per_as,
             NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
             "Use addpath to advertise the bestpath per each neighboring AS\n")
 
+
+DEFUN_NOSH (vpn_policy_afi,
+       vpn_policy_afi_cmd,
+       "vpn-policy <ipv4|ipv6>",
+       "Enter vpn-policy command mode\n"
+       BGP_AFI_HELP_STR)
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF
+           && bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
+
+               vty_out(vty,
+                       "vpn-policy supported only in core or vrf instances.\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       afi_t afi;
+       int idx = 0;
+
+       if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+               if (afi == AFI_IP)
+                       vty->node = BGP_VPNPOLICY_IPV4_NODE;
+               else
+                       vty->node = BGP_VPNPOLICY_IPV6_NODE;
+               return CMD_SUCCESS;
+       }
+       return CMD_WARNING_CONFIG_FAILED;
+}
+
+static int vpn_policy_afis(struct vty *vty, int *doafi)
+{
+       switch (vty->node) {
+       case BGP_VPNPOLICY_IPV4_NODE:
+               doafi[AFI_IP] = 1;
+               break;
+       case BGP_VPNPOLICY_IPV6_NODE:
+               doafi[AFI_IP6] = 1;
+               break;
+       default:
+               vty_out(vty,
+                       "%% context error: valid only in vpn-policy block\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       return CMD_SUCCESS;
+}
+
+static int argv_find_and_parse_vpn_policy_dirs(struct vty *vty,
+                                              struct cmd_token **argv,
+                                              int argc, int *idx, int *dodir)
+{
+       if (argv_find(argv, argc, "fromvpn", idx)) {
+               dodir[BGP_VPN_POLICY_DIR_FROMVPN] = 1;
+       } else if (argv_find(argv, argc, "tovpn", idx)) {
+               dodir[BGP_VPN_POLICY_DIR_TOVPN] = 1;
+       } else if (argv_find(argv, argc, "both", idx)) {
+               dodir[BGP_VPN_POLICY_DIR_FROMVPN] = 1;
+               dodir[BGP_VPN_POLICY_DIR_TOVPN] = 1;
+       } else {
+               vty_out(vty, "%% direction parse error\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       return CMD_SUCCESS;
+}
+
+DEFUN (vpn_policy_rd,
+       vpn_policy_rd_cmd,
+       "rd ASN:NN_OR_IP-ADDRESS:NN",
+       "Specify route distinguisher\n"
+       "Route Distinguisher (<as-number>:<number> | <ip-address>:<number>)\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       struct prefix_rd prd;
+       int ret;
+       int doafi[AFI_MAX] = {0};
+       afi_t afi;
+
+       ret = str2prefix_rd(argv[1]->arg, &prd);
+       if (!ret) {
+               vty_out(vty, "%% Malformed rd\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       ret = vpn_policy_afis(vty, doafi);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+
+       for (afi = 0; afi < AFI_MAX; ++afi) {
+               if (!doafi[afi])
+                       continue;
+
+               /* pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+                */
+               vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                  bgp_get_default(), bgp);
+
+               bgp->vpn_policy[afi].tovpn_rd = prd;
+               SET_FLAG(bgp->vpn_policy[afi].flags,
+                        BGP_VPN_POLICY_TOVPN_RD_SET);
+
+               /* post-change: re-export vpn routes */
+               vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                   bgp_get_default(), bgp);
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (vpn_policy_no_rd,
+       vpn_policy_no_rd_cmd,
+       "no rd",
+       NO_STR
+       "Specify route distinguisher\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       int ret;
+       int doafi[AFI_MAX] = {0};
+       afi_t afi;
+
+       ret = vpn_policy_afis(vty, doafi);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+
+       for (afi = 0; afi < AFI_MAX; ++afi) {
+               if (!doafi[afi])
+                       continue;
+
+               /* pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+                */
+               vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                  bgp_get_default(), bgp);
+
+               UNSET_FLAG(bgp->vpn_policy[afi].flags,
+                          BGP_VPN_POLICY_TOVPN_RD_SET);
+
+               /* post-change: re-export vpn routes */
+               vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                   bgp_get_default(), bgp);
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (vpn_policy_label,
+       vpn_policy_label_cmd,
+       "label (0-1048575)",
+       "label value for VRF\n"
+       "Label Value <0-1048575>\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       mpls_label_t label;
+       int doafi[AFI_MAX] = {0};
+       afi_t afi;
+       int ret;
+
+       label = strtoul(argv[1]->arg, NULL, 10);
+
+       ret = vpn_policy_afis(vty, doafi);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       for (afi = 0; afi < AFI_MAX; ++afi) {
+               if (!doafi[afi])
+                       continue;
+
+               /* pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+                */
+               vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                  bgp_get_default(), bgp);
+
+               bgp->vpn_policy[afi].tovpn_label = label;
+
+               /* post-change: re-export vpn routes */
+               vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                   bgp_get_default(), bgp);
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (vpn_policy_no_label,
+       vpn_policy_no_label_cmd,
+       "no label",
+       "Negate a command or set its defaults\n"
+       "label value for VRF\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       int doafi[AFI_MAX] = {0};
+       afi_t afi;
+       int ret;
+
+       ret = vpn_policy_afis(vty, doafi);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       for (afi = 0; afi < AFI_MAX; ++afi) {
+               if (!doafi[afi])
+                       continue;
+
+               /* pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+                */
+               vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                  bgp_get_default(), bgp);
+
+               bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
+
+               /* post-change: re-export vpn routes */
+               vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                   bgp_get_default(), bgp);
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFPY (vpn_policy_nexthop,
+       vpn_policy_nexthop_cmd,
+       "nexthop <A.B.C.D|X:X::X:X>$nexthop",
+       "Specify next hop to use for VRF advertised prefixes\n"
+       "IPv4 prefix\n"
+       "IPv6 prefix\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       int doafi[AFI_MAX] = {0};
+       afi_t afi;
+       int ret;
+       struct prefix p;
+
+       if (!sockunion2hostprefix(nexthop, &p))
+               return CMD_WARNING_CONFIG_FAILED;
+
+       ret = vpn_policy_afis(vty, doafi);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       for (afi = 0; afi < AFI_MAX; ++afi) {
+               if (!doafi[afi])
+                       continue;
+
+               /*
+                * pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+                */
+               vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                  bgp_get_default(), bgp);
+
+               bgp->vpn_policy[afi].tovpn_nexthop = p;
+               SET_FLAG(bgp->vpn_policy[afi].flags,
+                        BGP_VPN_POLICY_TOVPN_NEXTHOP_SET);
+
+               /* post-change: re-export vpn routes */
+               vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                   bgp_get_default(), bgp);
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (vpn_policy_no_nexthop,
+       vpn_policy_no_nexthop_cmd,
+       "no nexthop",
+       NO_STR
+       "Specify next hop to use for VRF advertised prefixes\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       int doafi[AFI_MAX] = {0};
+       afi_t afi;
+       int ret;
+
+       ret = vpn_policy_afis(vty, doafi);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       for (afi = 0; afi < AFI_MAX; ++afi) {
+               if (!doafi[afi])
+                       continue;
+
+               /* pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+                */
+               vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                  bgp_get_default(), bgp);
+
+               UNSET_FLAG(bgp->vpn_policy[afi].flags,
+                          BGP_VPN_POLICY_TOVPN_NEXTHOP_SET);
+
+               /* post-change: re-export vpn routes */
+               vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                   bgp_get_default(), bgp);
+       }
+
+       return CMD_SUCCESS;
+}
+
+static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
+                        struct ecommunity **list)
+{
+       struct ecommunity *ecom = NULL;
+       struct ecommunity *ecomadd;
+
+       for (; argc; --argc, ++argv) {
+
+               ecomadd = ecommunity_str2com(argv[0]->arg,
+                                            ECOMMUNITY_ROUTE_TARGET, 0);
+               if (!ecomadd) {
+                       vty_out(vty, "Malformed community-list value\n");
+                       if (ecom)
+                               ecommunity_free(&ecom);
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+
+               if (ecom) {
+                       ecommunity_merge(ecom, ecomadd);
+                       ecommunity_free(&ecomadd);
+               } else {
+                       ecom = ecomadd;
+               }
+       }
+
+       if (*list) {
+               ecommunity_free(&*list);
+       }
+       *list = ecom;
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (vpn_policy_rt,
+       vpn_policy_rt_cmd,
+       "rt <fromvpn|tovpn|both> RTLIST...",
+       "Specify route target list\n"
+       "fromvpn: match any\n"
+       "tovpn: set\n"
+       "both fromvpn: match any and tovpn: set\n"
+       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       int ret;
+       struct ecommunity *ecom = NULL;
+       int dodir[BGP_VPN_POLICY_DIR_MAX] = {0};
+       int doafi[AFI_MAX] = {0};
+       vpn_policy_direction_t dir;
+       afi_t afi;
+       int idx = 0;
+
+       ret = vpn_policy_afis(vty, doafi);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       ret = set_ecom_list(vty, argc - 2, argv + 2, &ecom);
+       if (ret != CMD_SUCCESS) {
+               return ret;
+       }
+
+       for (afi = 0; afi < AFI_MAX; ++afi) {
+               if (!doafi[afi])
+                       continue;
+               for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
+                       if (!dodir[dir])
+                               continue;
+
+                       vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+
+                       if (bgp->vpn_policy[afi].rtlist[dir])
+                               ecommunity_free(
+                                       &bgp->vpn_policy[afi].rtlist[dir]);
+                       bgp->vpn_policy[afi].rtlist[dir] = ecommunity_dup(ecom);
+
+                       vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+               }
+       }
+       ecommunity_free(&ecom);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (vpn_policy_no_rt,
+       vpn_policy_no_rt_cmd,
+       "no rt <fromvpn|tovpn|both>",
+       NO_STR
+       "Specify route target list\n"
+       "fromvpn: match any\n"
+       "tovpn: set\n"
+       "both fromvpn: match any and tovpn: set\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       int ret;
+       int dodir[BGP_VPN_POLICY_DIR_MAX] = {0};
+       int doafi[AFI_MAX] = {0};
+       vpn_policy_direction_t dir;
+       afi_t afi;
+       int idx = 0;
+
+       ret = vpn_policy_afis(vty, doafi);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       for (afi = 0; afi < AFI_MAX; ++afi) {
+               if (!doafi[afi])
+                       continue;
+               for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
+                       if (!dodir[dir])
+                               continue;
+
+                       vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+
+                       if (bgp->vpn_policy[afi].rtlist[dir])
+                               ecommunity_free(
+                                       &bgp->vpn_policy[afi].rtlist[dir]);
+                       bgp->vpn_policy[afi].rtlist[dir] = NULL;
+
+                       vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+               }
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (vpn_policy_route_map,
+       vpn_policy_route_map_cmd,
+       "route-map <fromvpn|tovpn> WORD",
+       "Specify route map\n"
+       "fromvpn: core vpn -> this vrf\n"
+       "tovpn: this vrf -> core vpn\n"
+       "name of route-map\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       int ret;
+       int dodir[BGP_VPN_POLICY_DIR_MAX] = {0};
+       int doafi[AFI_MAX] = {0};
+       vpn_policy_direction_t dir;
+       afi_t afi;
+       int map_name_arg = 2;
+       int idx = 0;
+
+       ret = vpn_policy_afis(vty, doafi);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       for (afi = 0; afi < AFI_MAX; ++afi) {
+               if (!doafi[afi])
+                       continue;
+               for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
+                       if (!dodir[dir])
+                               continue;
+
+                       vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+
+                       if (bgp->vpn_policy[afi].rmap_name[dir])
+                               XFREE(MTYPE_ROUTE_MAP_NAME,
+                                     bgp->vpn_policy[afi].rmap_name[dir]);
+                       bgp->vpn_policy[afi].rmap_name[dir] = XSTRDUP(
+                               MTYPE_ROUTE_MAP_NAME, argv[map_name_arg]->arg);
+                       bgp->vpn_policy[afi].rmap[dir] =
+                               route_map_lookup_by_name(
+                                       argv[map_name_arg]->arg);
+
+                       vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+               }
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (vpn_policy_no_route_map,
+       vpn_policy_no_route_map_cmd,
+       "no route-map <fromvpn|tovpn>",
+       NO_STR
+       "Specify route map\n"
+       "fromvpn: core vpn -> this vrf\n"
+       "tovpn: this vrf -> core vpn\n")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       int ret;
+       int dodir[BGP_VPN_POLICY_DIR_MAX] = {0};
+       int doafi[AFI_MAX] = {0};
+       vpn_policy_direction_t dir;
+       afi_t afi;
+       int idx = 0;
+
+       ret = vpn_policy_afis(vty, doafi);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir);
+       if (ret != CMD_SUCCESS)
+               return ret;
+
+       for (afi = 0; afi < AFI_MAX; ++afi) {
+               if (!doafi[afi])
+                       continue;
+               for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
+                       if (!dodir[dir])
+                               continue;
+
+                       vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+
+                       if (bgp->vpn_policy[afi].rmap_name[dir])
+                               XFREE(MTYPE_ROUTE_MAP_NAME,
+                                     bgp->vpn_policy[afi].rmap_name[dir]);
+                       bgp->vpn_policy[afi].rmap_name[dir] = NULL;
+                       bgp->vpn_policy[afi].rmap[dir] = NULL;
+
+                       vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+               }
+       }
+
+       return CMD_SUCCESS;
+}
+
 DEFUN_NOSH (address_family_ipv4_safi,
        address_family_ipv4_safi_cmd,
        "address-family ipv4 [<unicast|multicast|vpn|labeled-unicast>]",
@@ -11139,6 +11659,165 @@ void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi,
        }
 }
 
+/* This command is valid only in a bgp vrf instance or the default instance */
+DEFUN (bgp_export_vpn,
+       bgp_export_vpn_cmd,
+       "export vpn",
+       "Export routes to another routing protocol\n"
+       "to VPN RIB per vpn-policy")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       int was_off = 0;
+       afi_t afi;
+       safi_t safi;
+
+       if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type
+           && BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) {
+               vty_out(vty,
+                       "%% export vpn valid only for bgp vrf or default instance\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       afi = bgp_node_afi(vty);
+       safi = bgp_node_safi(vty);
+       if ((SAFI_UNICAST != safi) || ((AFI_IP != afi) && (AFI_IP6 != afi))) {
+               vty_out(vty,
+                       "%% export vpn valid only for unicast ipv4|ipv6\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (!CHECK_FLAG(bgp->af_flags[afi][safi],
+                       BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) {
+               was_off = 1;
+       }
+       SET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT);
+       if (was_off) {
+               /* trigger export current vrf */
+               zlog_debug("%s: calling postchange", __func__);
+               vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                   bgp_get_default(), bgp);
+       }
+       return CMD_SUCCESS;
+}
+
+DEFUN (bgp_no_export_vpn,
+       bgp_no_export_vpn_cmd,
+       "no export vpn",
+       NO_STR
+       "Export routes to another routing protocol\n"
+       "to VPN RIB per vpn-policy")
+{
+       VTY_DECLVAR_CONTEXT(bgp, bgp);
+       int was_on = 0;
+       afi_t afi;
+       safi_t safi;
+
+       if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type
+           && BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) {
+               vty_out(vty,
+                       "%% export vpn valid only for bgp vrf or default instance\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+       afi = bgp_node_afi(vty);
+       safi = bgp_node_safi(vty);
+       if ((SAFI_UNICAST != safi) || ((AFI_IP != afi) && (AFI_IP6 != afi))) {
+               vty_out(vty,
+                       "%% export vpn valid only for unicast ipv4|ipv6\n");
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (CHECK_FLAG(bgp->af_flags[afi][safi],
+                      BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) {
+               was_on = 1;
+       }
+       if (was_on) {
+               /* trigger un-export current vrf */
+               zlog_debug("%s: calling postchange", __func__);
+               vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+                                  bgp_get_default(), bgp);
+       }
+       UNSET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT);
+       return CMD_SUCCESS;
+}
+
+static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
+                                           afi_t afi)
+{
+       vty_frame(vty, " vpn-policy ipv%d\n", ((afi == AFI_IP) ? 4 : 6));
+
+       if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) {
+               vty_out(vty, "    label %u\n",
+                       bgp->vpn_policy[afi].tovpn_label);
+       }
+       if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
+                      BGP_VPN_POLICY_TOVPN_RD_SET)) {
+               char buf[RD_ADDRSTRLEN];
+               vty_out(vty, "    rd %s\n",
+                       prefix_rd2str(&bgp->vpn_policy[afi].tovpn_rd, buf,
+                                     sizeof(buf)));
+       }
+       if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
+                      BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) {
+
+               char buf[PREFIX_STRLEN];
+               if (inet_ntop(bgp->vpn_policy[afi].tovpn_nexthop.family,
+                             &bgp->vpn_policy[afi].tovpn_nexthop.u.prefix, buf,
+                             sizeof(buf))) {
+
+                       vty_out(vty, "    nexthop %s\n", buf);
+               }
+       }
+       if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]
+           && bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]
+           && ecommunity_cmp(
+                      bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN],
+                      bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN])) {
+
+               char *b = ecommunity_ecom2str(
+                       bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN],
+                       ECOMMUNITY_FORMAT_ROUTE_MAP, ECOMMUNITY_ROUTE_TARGET);
+               vty_out(vty, "    rt both %s\n", b);
+               XFREE(MTYPE_ECOMMUNITY_STR, b);
+       } else {
+               if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]) {
+                       char *b = ecommunity_ecom2str(
+                               bgp->vpn_policy[afi]
+                                       .rtlist[BGP_VPN_POLICY_DIR_FROMVPN],
+                               ECOMMUNITY_FORMAT_ROUTE_MAP,
+                               ECOMMUNITY_ROUTE_TARGET);
+                       vty_out(vty, "    rt fromvpn %s\n", b);
+                       XFREE(MTYPE_ECOMMUNITY_STR, b);
+               }
+               if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) {
+                       char *b = ecommunity_ecom2str(
+                               bgp->vpn_policy[afi]
+                                       .rtlist[BGP_VPN_POLICY_DIR_TOVPN],
+                               ECOMMUNITY_FORMAT_ROUTE_MAP,
+                               ECOMMUNITY_ROUTE_TARGET);
+                       vty_out(vty, "    rt tovpn %s\n", b);
+                       XFREE(MTYPE_ECOMMUNITY_STR, b);
+               }
+       }
+       if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]) {
+               vty_out(vty, "    route-map fromvpn %s\n",
+                       bgp->vpn_policy[afi]
+                               .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]);
+       }
+       if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN]) {
+               vty_out(vty, "    route-map tovpn %s\n",
+                       bgp->vpn_policy[afi]
+                               .rmap_name[BGP_VPN_POLICY_DIR_TOVPN]);
+       }
+
+       vty_endframe(vty, " exit\n");
+}
+
+void bgp_vpn_policy_config_write(struct vty *vty, struct bgp *bgp)
+{
+       bgp_vpn_policy_config_write_afi(vty, bgp, AFI_IP);
+       bgp_vpn_policy_config_write_afi(vty, bgp, AFI_IP6);
+}
+
+
 /* BGP node structure. */
 static struct cmd_node bgp_node = {
        BGP_NODE, "%s(config-router)# ", 1,
@@ -11180,6 +11859,12 @@ static struct cmd_node bgp_evpn_node = {BGP_EVPN_NODE,
 static struct cmd_node bgp_evpn_vni_node = {BGP_EVPN_VNI_NODE,
                                            "%s(config-router-af-vni)# ", 1};
 
+static struct cmd_node bgp_vpn_policy_ipv4_node = {
+       BGP_VPNPOLICY_IPV4_NODE, "%s(config-router-vpn-policy-ipv4)# ", 1};
+
+static struct cmd_node bgp_vpn_policy_ipv6_node = {
+       BGP_VPNPOLICY_IPV6_NODE, "%s(config-router-vpn-policy-ipv6)# ", 1};
+
 static void community_list_vty(void);
 
 static void bgp_ac_neighbor(vector comps, struct cmd_token *token)
@@ -11240,6 +11925,8 @@ void bgp_vty_init(void)
        install_node(&bgp_vpnv6_node, NULL);
        install_node(&bgp_evpn_node, NULL);
        install_node(&bgp_evpn_vni_node, NULL);
+       install_node(&bgp_vpn_policy_ipv4_node, NULL);
+       install_node(&bgp_vpn_policy_ipv6_node, NULL);
 
        /* Install default VTY commands to new nodes.  */
        install_default(BGP_NODE);
@@ -11253,6 +11940,8 @@ void bgp_vty_init(void)
        install_default(BGP_VPNV6_NODE);
        install_default(BGP_EVPN_NODE);
        install_default(BGP_EVPN_VNI_NODE);
+       install_default(BGP_VPNPOLICY_IPV4_NODE);
+       install_default(BGP_VPNPOLICY_IPV6_NODE);
 
        /* "bgp multiple-instance" commands. */
        install_element(CONFIG_NODE, &bgp_multiple_instance_cmd);
@@ -12312,6 +13001,12 @@ void bgp_vty_init(void)
        install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_rmap_metric_cmd);
        install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_rmap_cmd);
 
+       /* export vpn [route-map WORD] */
+       install_element(BGP_IPV4_NODE, &bgp_export_vpn_cmd);
+       install_element(BGP_IPV6_NODE, &bgp_export_vpn_cmd);
+       install_element(BGP_IPV4_NODE, &bgp_no_export_vpn_cmd);
+       install_element(BGP_IPV6_NODE, &bgp_no_export_vpn_cmd);
+
        /* ttl_security commands */
        install_element(BGP_NODE, &neighbor_ttl_security_cmd);
        install_element(BGP_NODE, &no_neighbor_ttl_security_cmd);
@@ -12330,6 +13025,30 @@ void bgp_vty_init(void)
 
        /* Community-list. */
        community_list_vty();
+
+       /* vpn-policy commands */
+       install_element(BGP_NODE, &vpn_policy_afi_cmd);
+       install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_rd_cmd);
+       install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_rd_cmd);
+       install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_label_cmd);
+       install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_label_cmd);
+       install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_nexthop_cmd);
+       install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_nexthop_cmd);
+       install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_rt_cmd);
+       install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_rt_cmd);
+       install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_route_map_cmd);
+       install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_route_map_cmd);
+
+       install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_rd_cmd);
+       install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_rd_cmd);
+       install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_label_cmd);
+       install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_label_cmd);
+       install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_nexthop_cmd);
+       install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_nexthop_cmd);
+       install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_rt_cmd);
+       install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_rt_cmd);
+       install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_route_map_cmd);
+       install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_route_map_cmd);
 }
 
 #include "memory.h"
index cbb41f0840a526e8e41559f9d08d0aa08510cb53..459c4ffcc3a293d26102eb85062fc5baa5b0f310 100644 (file)
@@ -71,4 +71,5 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty,
                                               safi_t *safi, struct bgp **bgp);
 extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
                                safi_t safi, u_char use_json);
+extern void bgp_vpn_policy_config_write(struct vty *vty, struct bgp *bgp);
 #endif /* _QUAGGA_BGP_VTY_H */
index 23f626e960d28208b89df8d267bc294ca2991071..22284fd28d9bd294930c10988008ee93574982be 100644 (file)
@@ -54,6 +54,7 @@
 #include "bgpd/rfapi/vnc_export_bgp.h"
 #endif
 #include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_mplsvpn.h"
 
 /* All information about zebra. */
 struct zclient *zclient = NULL;
@@ -987,6 +988,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
        struct bgp_info *mpinfo_cp = &local_info;
        route_tag_t tag;
        mpls_label_t label;
+       int nh_othervrf = 0;
 
        /* Don't try to install if we're not connected to Zebra or Zebra doesn't
         * know of this instance.
@@ -997,6 +999,12 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
        if (bgp->main_zebra_update_hold)
                return;
 
+       /*
+        * vrf leaking support (will have only one nexthop)
+        */
+       if (info->extra && info->extra->bgp_orig)
+               nh_othervrf = 1;
+
        /* Make Zebra API structure. */
        memset(&api, 0, sizeof(api));
        memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr));
@@ -1008,6 +1016,21 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
 
        peer = info->peer;
 
+       if (info->type == ZEBRA_ROUTE_BGP
+           && info->sub_type == BGP_ROUTE_IMPORTED) {
+
+               struct bgp_info *bi;
+
+               /*
+                * Look at parent chain for peer sort
+                */
+               for (bi = info; bi->extra && bi->extra->parent;
+                    bi = bi->extra->parent) {
+
+                       peer = ((struct bgp_info *)(bi->extra->parent))->peer;
+               }
+       }
+
        tag = info->attr->tag;
 
        /* When we create an aggregate route we must also install a Null0 route
@@ -1060,12 +1083,38 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
                if (nh_family == AF_INET) {
                        struct in_addr *nexthop;
 
-                       if (bgp->table_map[afi][safi].name) {
+                       if (bgp_debug_zebra(&api.prefix)) {
+                               char buf_prefix[PREFIX_STRLEN];
+                               prefix2str(&api.prefix, buf_prefix,
+                                          sizeof(buf_prefix));
+                               if (mpinfo->extra) {
+                                       zlog_debug(
+                                               "%s: p=%s, bgp_is_valid_label: %d",
+                                               __func__, buf_prefix,
+                                               bgp_is_valid_label(
+                                                       &mpinfo->extra
+                                                                ->label[0]));
+                               } else {
+                                       zlog_debug(
+                                               "%s: p=%s, extra is NULL, no label",
+                                               __func__, buf_prefix);
+                               }
+                       }
+
+                       if (bgp->table_map[afi][safi].name || nh_othervrf) {
                                /* Copy info and attributes, so the route-map
                                   apply doesn't modify the BGP route info. */
                                local_attr = *mpinfo->attr;
                                mpinfo_cp->attr = &local_attr;
+                               if (nh_othervrf) {
+                                       /* allow route-map to modify */
+                                       local_attr.nexthop =
+                                               info->extra->nexthop_orig.u
+                                                       .prefix4;
+                               }
+                       }
 
+                       if (bgp->table_map[afi][safi].name) {
                                if (!bgp_table_map_apply(
                                            bgp->table_map[afi][safi].map, p,
                                            mpinfo_cp))
@@ -1082,6 +1131,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
                        nexthop = &mpinfo_cp->attr->nexthop;
 
                        api_nh->gate.ipv4 = *nexthop;
+                       api_nh->vrf_id = nh_othervrf
+                                                ? info->extra->bgp_orig->vrf_id
+                                                : bgp->vrf_id;
                        /* EVPN type-2 routes are
                           programmed as onlink on l3-vni SVI
                         */
@@ -1095,6 +1147,21 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
 
                        ifindex = 0;
 
+                       if (bgp->table_map[afi][safi].name || nh_othervrf) {
+                               /* Copy info and attributes, so the route-map
+                                  apply doesn't modify the BGP route info. */
+                               local_attr = *mpinfo->attr;
+                               mpinfo_cp->attr = &local_attr;
+                               if (nh_othervrf) {
+                                       /* allow route-map to modify */
+                                       local_attr.mp_nexthop_global =
+                                               info->extra->nexthop_orig.u
+                                                       .prefix6;
+                                       local_attr.mp_nexthop_len =
+                                               BGP_ATTR_NHLEN_IPV6_GLOBAL;
+                               }
+                       }
+
                        if (bgp->table_map[afi][safi].name) {
                                /* Copy info and attributes, so the route-map
                                   apply doesn't modify the BGP route info. */
@@ -1139,6 +1206,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
                        api_nh->gate.ipv6 = *nexthop;
                        api_nh->ifindex = ifindex;
                        api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+                       /* api_nh->vrf_id is not set for normal case? */
+                       if (nh_othervrf)
+                               api_nh->vrf_id = info->extra->bgp_orig->vrf_id;
                }
 
                if (mpinfo->extra
@@ -1229,9 +1299,12 @@ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi)
 
        for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn))
                for (ri = rn->info; ri; ri = ri->next)
-                       if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED)
-                           && ri->type == ZEBRA_ROUTE_BGP
-                           && ri->sub_type == BGP_ROUTE_NORMAL)
+                       if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED) &&
+
+                           (ri->type == ZEBRA_ROUTE_BGP
+                            && (ri->sub_type == BGP_ROUTE_NORMAL
+                                || ri->sub_type == BGP_ROUTE_IMPORTED)))
+
                                bgp_zebra_announce(rn, &rn->p, ri, bgp, afi,
                                                   safi);
 }
@@ -1244,6 +1317,21 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, safi_t safi)
        peer = info->peer;
        assert(peer);
 
+       if (info->type == ZEBRA_ROUTE_BGP
+           && info->sub_type == BGP_ROUTE_IMPORTED) {
+
+               struct bgp_info *bi;
+
+               /*
+                * Look at parent chain for peer sort
+                */
+               for (bi = info; bi->extra && bi->extra->parent;
+                    bi = bi->extra->parent) {
+
+                       peer = ((struct bgp_info *)(bi->extra->parent))->peer;
+               }
+       }
+
        /* Don't try to install if we're not connected to Zebra or Zebra doesn't
         * know of this instance.
         */
@@ -1363,7 +1451,27 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type, u_short instance)
                }
 #endif
 
+               /* vpn -> vrf (happens within bgp but we hijack redist bits */
+               if ((bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
+                    || bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+                   && type == ZEBRA_ROUTE_BGP_VPN) {
+
+                       /* leak update all */
+                       vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi,
+                                          bgp_get_default(), bgp);
+               }
+
                vrf_bitmap_set(zclient->redist[afi][type], bgp->vrf_id);
+
+               /* vpn -> vrf (happens within bgp but we hijack redist bits */
+               if ((bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
+                    || bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+                   && type == ZEBRA_ROUTE_BGP_VPN) {
+
+                       /* leak update all */
+                       vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, afi,
+                                           bgp_get_default(), bgp);
+               }
        }
 
        /*
@@ -1484,11 +1592,6 @@ int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type,
                vrf_bitmap_unset(zclient->redist[afi][type], bgp->vrf_id);
        }
 
-#if ENABLE_BGP_VNC
-       if (bgp->vrf_id == VRF_DEFAULT && type == ZEBRA_ROUTE_VNC_DIRECT) {
-               vnc_export_bgp_disable(bgp, afi);
-       }
-#endif
 
        if (bgp_install_info_to_zebra(bgp)) {
                /* Send distribute delete message to zebra. */
@@ -1512,6 +1615,26 @@ int bgp_redistribute_unset(struct bgp *bgp, afi_t afi, int type,
 {
        struct bgp_redist *red;
 
+/*
+ * vnc and vpn->vrf checks must be before red check because
+ * they operate within bgpd irrespective of zebra connection
+ * status. red lookup fails if there is no zebra connection.
+ */
+#if ENABLE_BGP_VNC
+       if (bgp->vrf_id == VRF_DEFAULT && type == ZEBRA_ROUTE_VNC_DIRECT) {
+               vnc_export_bgp_disable(bgp, afi);
+       }
+#endif
+       /* vpn -> vrf (happend within bgp but we hijack redist bits */
+       if ((bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
+            || bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+           && type == ZEBRA_ROUTE_BGP_VPN) {
+
+               /* leak withdraw all */
+               vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi,
+                                  bgp_get_default(), bgp);
+       }
+
        red = bgp_redist_lookup(bgp, afi, type, instance);
        if (!red)
                return CMD_SUCCESS;
index fde72da4c8e33b93909b7d3442e41577bedea0a1..e7d58a021b243c58b34911f5fe2f26e18dcbf751 100644 (file)
@@ -2941,6 +2941,11 @@ static struct bgp *bgp_create(as_t *as, const char *name,
        }
 #endif /* ENABLE_BGP_VNC */
 
+       for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+               bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
+               bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent =
+                       MPLS_LABEL_NONE;
+       }
        if (name) {
                bgp->name = XSTRDUP(MTYPE_BGP, name);
        } else {
@@ -7127,6 +7132,12 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
        if (safi == SAFI_EVPN)
                bgp_config_write_evpn_info(vty, bgp, afi, safi);
 
+       if (CHECK_FLAG(bgp->af_flags[afi][safi],
+                      BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) {
+
+               vty_out(vty, "  export vpn\n");
+       }
+
        vty_endframe(vty, " exit-address-family\n");
 }
 
@@ -7393,6 +7404,8 @@ int bgp_config_write(struct vty *vty)
                if (bgp_option_check(BGP_OPT_CONFIG_CISCO))
                        vty_out(vty, " no auto-summary\n");
 
+               bgp_vpn_policy_config_write(vty, bgp);
+
                /* IPv4 unicast configuration.  */
                bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);
 
index e265da803f7bf9096d13c2c7d2673304785a44e5..41ae8e916fde195b9f7e8f2fb970d6f53bb2c818 100644 (file)
@@ -159,6 +159,12 @@ struct bgp_redist {
        struct bgp_rmap rmap;
 };
 
+typedef enum {
+       BGP_VPN_POLICY_DIR_FROMVPN = 0,
+       BGP_VPN_POLICY_DIR_TOVPN = 1,
+       BGP_VPN_POLICY_DIR_MAX = 2
+} vpn_policy_direction_t;
+
 /*
  * Type of 'struct bgp'.
  * - Default: The default instance
@@ -311,6 +317,7 @@ struct bgp {
        /* BGP Per AF flags */
        u_int16_t af_flags[AFI_MAX][SAFI_MAX];
 #define BGP_CONFIG_DAMPENING              (1 << 0)
+#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT  (1 << 1)
 
        /* Route table for next-hop lookup cache. */
        struct bgp_table *nexthop_cache_table[AFI_MAX];
@@ -448,6 +455,22 @@ struct bgp {
        /* route map for advertise ipv4/ipv6 unicast (type-5 routes) */
        struct bgp_rmap adv_cmd_rmap[AFI_MAX][SAFI_MAX];
 
+       /* vpn-policy */
+       struct {
+               struct ecommunity *rtlist[BGP_VPN_POLICY_DIR_MAX];
+               char *rmap_name[BGP_VPN_POLICY_DIR_MAX];
+               struct route_map *rmap[BGP_VPN_POLICY_DIR_MAX];
+
+               /* should be mpls_label_t? */
+               uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */
+               uint32_t tovpn_zebra_vrf_label_last_sent;
+               struct prefix_rd tovpn_rd;
+               struct prefix tovpn_nexthop; /* unset => set to router id */
+               uint32_t flags;
+#define BGP_VPN_POLICY_TOVPN_RD_SET            0x00000004
+#define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET       0x00000008
+       } vpn_policy[AFI_MAX];
+
        QOBJ_FIELDS
 };
 DECLARE_QOBJ_TYPE(bgp)
index 4c7c392ab89dcb07537f3ab2e503f03437955394..8c4d5ab04363b44b4ae403d8831a65e1c7846470 100644 (file)
@@ -2182,6 +2182,7 @@ void vnc_routemap_update(struct bgp *bgp, const char *unused)
        vnc_zlog_debug_verbose("%s done", __func__);
 }
 
+#if 0 /* superseded */
 static void vnc_routemap_event(route_map_event_t type, /* ignored */
                               const char *rmap_name)  /* ignored */
 {
@@ -2197,6 +2198,7 @@ static void vnc_routemap_event(route_map_event_t type, /* ignored */
 
        vnc_zlog_debug_verbose("%s: done", __func__);
 }
+#endif
 
 /*-------------------------------------------------------------------------
  *                     nve-group
@@ -3673,7 +3675,8 @@ bgp_rfapi_get_ecommunity_by_lni_label(struct bgp *bgp, uint32_t is_import,
 void bgp_rfapi_cfg_init(void)
 {
        /* main bgpd code does not use this hook, but vnc does */
-       route_map_event_hook(vnc_routemap_event);
+       /* superseded by bgp_route_map_process_update_cb() */
+       /* bgp_route_map_event_hook_add(vnc_routemap_event); */
 
        install_node(&bgp_vnc_defaults_node, NULL);
        install_node(&bgp_vnc_nve_group_node, NULL);