[cmaster-next] [PATCH 4/7] bgpd: VRF vty configuration, RIB table creation

Philippe Guibert philippe.guibert at 6wind.com
Tue Dec 20 09:46:24 EST 2016


This commit introduces the BGP VRF configuration, and BGP VRF RIB
table.
It includes the ability for a BGP to configure its own route
distinguisher ( aka VRF).
A new subnode under router bgp command has been created: vrf <>.
New vty commands introduced:

vrf <NAME>
 rd <route distinguisher name>
 no rd <RD name>
 rt import <RT list>
 rt export <RT list>
 rt both <RT list>
 no rt import <>
 no rt export <>
 no rt both <>

It brings some improvements.
- for a BGP speaker when emitting BGP updates, the exported route
targets will be mapped to BGP extended communities associated with
the BGP update for the defined Route distinguisher.
This commit does not enhance the support for emitting those BGP
extended communities, but provides the mecanism.
- for a BGP speaker when receiving BGP updaets. Its import route
target will be looked up, in order to match NLRI route distinguisher.
Then, if matching, the entry would be exported to a RIB per VRF table.

As mentioned before, the commit also introduces a BGP VRF RIB table per
Route distinguisher configured. This table aims at receiving BGP NLRI
entries, with matching import and export route targets.
This commit does not take into account the fullfill of this table,
but creates it.

Signed-off-by: David Lamparter <equinox at opensourcerouting.org>
Signed-off-by: Philippe Guibert <philippe.guibert at 6wind.com>
---
 bgpd/Makefile.am  |   4 +-
 bgpd/bgp_memory.c |   3 +
 bgpd/bgp_memory.h |   2 +
 bgpd/bgp_route.c  |  57 ++++-
 bgpd/bgp_route.h  |  14 ++
 bgpd/bgp_table.h  |  10 +
 bgpd/bgp_vrf.c    | 733 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 bgpd/bgp_vrf.h    |  84 +++++++
 bgpd/bgp_vty.c    |   2 +
 bgpd/bgpd.c       |   6 +
 bgpd/bgpd.h       |   7 +
 11 files changed, 915 insertions(+), 7 deletions(-)
 create mode 100644 bgpd/bgp_vrf.c
 create mode 100644 bgpd/bgp_vrf.h

diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am
index 69c0504af451..50f364a6358e 100644
--- a/bgpd/Makefile.am
+++ b/bgpd/Makefile.am
@@ -78,7 +78,7 @@ libbgp_a_SOURCES = \
 	bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \
 	bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \
         bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c \
-	bgp_encap.c bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC)
+	bgp_encap.c bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_vrf.c
 
 noinst_HEADERS = \
 	bgp_memory.h \
@@ -88,7 +88,7 @@ noinst_HEADERS = \
 	bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \
 	bgp_advertise.h bgp_snmp.h bgp_vty.h bgp_mpath.h bgp_nht.h \
         bgp_updgrp.h bgp_bfd.h bgp_encap.h bgp_encap_tlv.h bgp_encap_types.h \
-	$(BGP_VNC_RFAPI_HD) 
+	$(BGP_VNC_RFAPI_HD) bgp_vrf.h
 
 bgpd_SOURCES = bgp_main.c
 bgpd_LDADD = libbgp.a  $(BGP_VNC_RFP_LIB) ../lib/libzebra.la @LIBCAP@ @LIBM@
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index 72c0311c1710..a3cb031631cc 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -111,3 +111,6 @@ DEFINE_MTYPE(BGPD, ENCAP_TLV,		"ENCAP TLV")
 
 DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS,	  "BGP TEA Options")
 DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS_VALUE, "BGP TEA Options Value")
+
+DEFINE_MTYPE(BGPD, BGP_VRF,               "BGP VRF Data Structures")
+DEFINE_MTYPE(BGPD, BGP_RT_SUB,            "BGP Route Target Data Structures")
diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h
index a4ce8b891b22..f4ed2b42dba4 100644
--- a/bgpd/bgp_memory.h
+++ b/bgpd/bgp_memory.h
@@ -108,4 +108,6 @@ DECLARE_MTYPE(ENCAP_TLV)
 DECLARE_MTYPE(BGP_TEA_OPTIONS)
 DECLARE_MTYPE(BGP_TEA_OPTIONS_VALUE)
 
+DECLARE_MTYPE(BGP_VRF)
+DECLARE_MTYPE(BGP_RT_SUB)
 #endif /* _QUAGGA_BGP_MEMORY_H */
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 58c88686ff51..5cd786426827 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -59,6 +59,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_mpath.h"
 #include "bgpd/bgp_nht.h"
 #include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_vrf.h"
 
 #if ENABLE_BGP_VNC
 #include "bgpd/rfapi/rfapi_backend.h"
@@ -70,6 +71,12 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 extern const char *bgp_origin_str[];
 extern const char *bgp_origin_long_str[];
 
+static void
+bgp_static_withdraw_safi (struct bgp *bgp, struct prefix *p, afi_t afi,
+                          safi_t safi, struct prefix_rd *prd, u_char *tag);
+static void
+bgp_static_free (struct bgp_static *bgp_static);
+
 struct bgp_node *
 bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix *p,
 		  struct prefix_rd *prd)
@@ -86,7 +93,12 @@ bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix
       prn = bgp_node_get (table, (struct prefix *) prd);
 
       if (prn->info == NULL)
-	prn->info = bgp_table_init (afi, safi);
+        {
+          struct bgp_table *newtab = bgp_table_init (afi, safi);
+          if (prd)
+            newtab->prd = *prd;
+          prn->info = newtab;
+        }
       else
 	bgp_unlock_node (prn);
       table = prn->info;
@@ -1871,7 +1883,7 @@ bgp_zebra_has_route_changed (struct bgp_node *rn, struct bgp_info *selected)
   return 0;
 }
 
-struct bgp_process_queue
+struct bgp_process_queue 
 {
   struct bgp *bgp;
   struct bgp_node *rn;
@@ -6568,6 +6580,9 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
 
   attr = binfo->attr;
 
+  if (safi == SAFI_MPLS_LABELED_VPN)
+    safi = SAFI_MPLS_VPN;
+
   if (attr)
     {
       /* Line1 display AS-path, Aggregator */
@@ -7244,7 +7259,7 @@ static int
 bgp_show_community (struct vty *vty, const char *view_name, int argc,
 		    struct cmd_token **argv, int exact, afi_t afi, safi_t safi);
 
-static int
+int
 bgp_show_table (struct vty *vty, struct bgp *bgp, struct bgp_table *table,
                 enum bgp_show_type type, void *output_arg, u_char use_json)
 {
@@ -7658,7 +7673,7 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
 }
 
 /* Display specified route of BGP table. */
-static int
+int
 bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, 
                          struct bgp_table *rib, const char *ip_str,
                          afi_t afi, safi_t safi, struct prefix_rd *prd,
@@ -10530,7 +10545,6 @@ bgp_route_init (void)
   install_element (BGP_IPV4M_NODE, &aggregate_address_mask_cmd);
   install_element (BGP_IPV4M_NODE, &no_aggregate_address_cmd);
   install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_cmd);
-
   install_element (VIEW_NODE, &show_ip_bgp_instance_all_cmd);
   install_element (VIEW_NODE, &show_ip_bgp_ipv4_cmd);
   install_element (VIEW_NODE, &show_ip_bgp_route_cmd);
@@ -10640,3 +10654,36 @@ bgp_route_finish (void)
 	bgp_distance_table[afi][safi] = NULL;
       }
 }
+
+void bgp_vrf_clean_tables (struct bgp_vrf *vrf)
+{
+  afi_t afi;
+
+  if (vrf->rib == NULL || vrf->route == NULL)
+    return;
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    {
+      struct bgp_info *ri, *ri_next;
+      struct bgp_node *rn;
+
+      for (rn = bgp_table_top (vrf->rib[afi]); rn; rn = bgp_route_next (rn))
+        for (ri = rn->info; ri; ri = ri_next)
+          {
+            ri_next = ri->next;
+            bgp_info_reap (rn, ri);
+          }
+      bgp_table_finish (&vrf->rib[afi]);
+
+      for (rn = bgp_table_top (vrf->route[afi]); rn; rn = bgp_route_next (rn))
+        if (rn->info)
+          {
+            struct bgp_static *bs = rn->info;
+            bgp_static_withdraw_safi (vrf->bgp, &rn->p, afi, SAFI_MPLS_VPN,
+                        &vrf->outbound_rd, NULL);
+            bgp_static_free (bs);
+            rn->info = NULL;
+            bgp_unlock_node (rn);
+          }
+      bgp_table_finish (&vrf->route[afi]);
+    }
+}
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 1c6eac5f85d3..9f18535481a7 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -25,6 +25,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgp_table.h"
 
 struct bgp_nexthop_cache;
+struct bgp_vrf;
 
 #define BGP_SHOW_SCODE_HEADER "Status codes: s suppressed, d damped, "\
                               "h history, * valid, > best, = multipath,%s"\
@@ -94,6 +95,7 @@ struct bgp_info_extra
 
   } vnc;
 #endif
+  struct prefix_rd vrf_rd;
 };
 
 struct bgp_info
@@ -366,4 +368,16 @@ extern void bgp_info_restore (struct bgp_node *, struct bgp_info *);
 extern int bgp_info_cmp_compatible (struct bgp *, struct bgp_info *,
                                     struct bgp_info *, afi_t, safi_t );
 
+extern int
+bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, 
+                         struct bgp_table *rib, const char *ip_str,
+                         afi_t afi, safi_t safi, struct prefix_rd *prd,
+                         int prefix_check, enum bgp_path_type pathtype,
+                         u_char use_json);
+extern void bgp_vrf_clean_tables (struct bgp_vrf *vrf);
+
+extern int
+bgp_show_table (struct vty *vty, struct bgp *bgp, struct bgp_table *table,
+                enum bgp_show_type type, void *output_arg, u_char use_json);
+
 #endif /* _QUAGGA_BGP_ROUTE_H */
diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h
index 2f839c4af7dd..9f78838c40ae 100644
--- a/bgpd/bgp_table.h
+++ b/bgpd/bgp_table.h
@@ -23,8 +23,16 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
 #include "table.h"
 
+typedef enum
+{
+  BGP_TABLE_MAIN,
+  BGP_TABLE_VRF,
+} bgp_table_t;
+
 struct bgp_table
 {
+  bgp_table_t type;
+
   /* afi/safi of this table */
   afi_t afi;
   safi_t safi;
@@ -36,6 +44,8 @@ struct bgp_table
 
   struct route_table *route_table;
   uint64_t version;
+
+  struct prefix_rd prd;
 };
 
 struct bgp_node
diff --git a/bgpd/bgp_vrf.c b/bgpd/bgp_vrf.c
new file mode 100644
index 000000000000..2c080fa8ceae
--- /dev/null
+++ b/bgpd/bgp_vrf.c
@@ -0,0 +1,733 @@
+/* BGP-4, BGP-4+ daemon program
+   Copyright (C) 2016, 6WIND
+
+This file is part of GNU Quagga.
+
+GNU Quagga is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU Quagga is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Quagga; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "linklist.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_vrf.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_attr.h"
+
+static int
+bgp_show_vrf (struct vty *vty, const char *vrf_name, afi_t afi,
+              enum bgp_show_type type, void *output_arg, u_char use_json);
+
+static int
+bgp_show_vrf_route (struct vty *vty, const char *vrf_name, const char *ip_str,
+                    afi_t afi, int prefix_check,u_char use_json);
+
+/* for vty access to bgp vrf node */
+DEFINE_QOBJ_TYPE(bgp_vrf)
+
+/* for vty config, BGP VRF node. */
+static struct cmd_node bgp_vrf_node =
+{
+  BGP_VRF_NODE,
+  "%s(bgp-vrf)# ",
+  1
+};
+
+static struct ecommunity * ecommunity_reintern (struct ecommunity *ecom)
+{
+  assert (ecom->refcnt > 0);
+  ecom->refcnt++;
+  return ecom;
+}
+
+/* route target internals */
+static unsigned int bgp_rt_hash_key (void *arg)
+{
+  const struct bgp_rt_sub *rt_sub = arg;
+  uint32_t *rtval = (uint32_t *)(char *)&rt_sub->rt;
+  return rtval[0] ^ rtval[1];
+}
+
+static int bgp_rt_hash_cmp (const void *a, const void *b)
+{
+  const struct bgp_rt_sub *aa = a, *bb = b;
+  return !memcmp(&aa->rt, &bb->rt, sizeof(aa->rt));
+}
+
+static void *
+bgp_rt_hash_alloc (void *dummy)
+{
+  struct bgp_rt_sub *ddummy = dummy, *rt_sub;
+  rt_sub = XMALLOC (MTYPE_BGP_RT_SUB, sizeof (*rt_sub));
+  rt_sub->rt = ddummy->rt;
+  rt_sub->vrfs = list_new();
+  return rt_sub;
+}
+
+static void
+bgp_rt_hash_dealloc (struct bgp_rt_sub *rt_sub)
+{
+  list_delete (rt_sub->vrfs);
+  XFREE (MTYPE_BGP_RT_SUB, rt_sub);
+}
+
+void
+bgp_vrf_rt_export_unset (struct bgp_vrf *vrf)
+{
+  if (vrf->rt_export)
+    ecommunity_unintern (&vrf->rt_export);
+}
+
+void
+bgp_vrf_rt_export_set (struct bgp_vrf *vrf, struct ecommunity *rt_export)
+{
+  if (vrf->rt_export)
+    ecommunity_unintern (&vrf->rt_export);
+
+  vrf->rt_export = ecommunity_reintern (rt_export);
+}
+
+void
+bgp_vrf_rt_import_unset (struct bgp_vrf *vrf)
+{
+  size_t i;
+
+  if (!vrf->rt_import)
+    return;
+
+  for (i = 0; i < (size_t)vrf->rt_import->size; i++)
+    {
+      struct bgp_rt_sub dummy, *rt_sub;
+      memcpy (&dummy.rt, vrf->rt_import->val + 8 * i, 8);
+
+      rt_sub = hash_lookup (vrf->bgp->rt_subscribers, &dummy);
+      assert(rt_sub);
+      listnode_delete (rt_sub->vrfs, vrf);
+      if (list_isempty (rt_sub->vrfs))
+        {
+          hash_release (vrf->bgp->rt_subscribers, rt_sub);
+          bgp_rt_hash_dealloc (rt_sub);
+        }
+    }
+
+  ecommunity_unintern (&vrf->rt_import);
+}
+
+void
+bgp_vrf_rt_import_set (struct bgp_vrf *vrf, struct ecommunity *rt_import)
+{
+  size_t i;
+  afi_t afi;
+
+  bgp_vrf_rt_import_unset (vrf);
+
+  vrf->rt_import = ecommunity_reintern (rt_import);
+
+  for (i = 0; i < (size_t)vrf->rt_import->size; i++)
+    {
+      struct bgp_rt_sub dummy, *rt_sub;
+      memcpy (&dummy.rt, vrf->rt_import->val + 8 * i, 8);
+
+      rt_sub = hash_get (vrf->bgp->rt_subscribers, &dummy, bgp_rt_hash_alloc);
+      listnode_add (rt_sub->vrfs, vrf);
+    }
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    bgp_vrf_apply_new_imports (vrf, afi);
+}
+
+/* bgp vrf internals */
+struct bgp_vrf *
+bgp_vrf_lookup_per_rn (struct bgp *bgp, int afi, struct bgp_node *vrf_rn)
+{
+  struct listnode *node;
+  struct bgp_vrf *vrf;
+
+  if(bgp_node_table (vrf_rn)->type != BGP_TABLE_VRF)
+    return NULL;
+  for (ALL_LIST_ELEMENTS_RO(bgp->vrfs, node, vrf))
+    if(vrf->rib[afi] == bgp_node_table (vrf_rn))
+      {
+        return vrf;
+      }
+  return NULL;
+}
+
+struct bgp_vrf *
+bgp_vrf_lookup (struct bgp *bgp, struct prefix_rd *outbound_rd)
+{
+  struct listnode *node;
+  struct bgp_vrf *vrf;
+
+  for (ALL_LIST_ELEMENTS_RO(bgp->vrfs, node, vrf))
+    if (!memcmp (outbound_rd->val, vrf->outbound_rd.val, 8))
+      return vrf;
+  return NULL;
+}
+
+struct bgp_vrf *
+bgp_vrf_update_rd (struct bgp *bgp, struct bgp_vrf *vrf, struct prefix_rd *outbound_rd)
+{
+  if (!vrf)
+    {
+      char vrf_rd_str[RD_ADDRSTRLEN];
+      prefix_rd2str (outbound_rd, vrf_rd_str, sizeof (vrf_rd_str));
+      if ( (vrf = bgp_vrf_lookup_per_name (bgp, vrf_rd_str, 1)) == NULL)
+        return NULL;
+    }
+  vrf->flag &= ~BGP_VRF_RD_UNSET;
+  vrf->outbound_rd = *outbound_rd;
+  return vrf;
+}
+
+/* delete RD <> command as well as RD export/import 
+ * and RIB table associated
+ */
+void
+bgp_vrf_delete_rd (struct bgp_vrf *vrf)
+{
+  char vrf_rd_str[RD_ADDRSTRLEN];
+
+  if (!vrf)
+    return;
+
+  prefix_rd2str(&vrf->outbound_rd, vrf_rd_str, sizeof(vrf_rd_str));
+  zlog_info ("deleting rd %s", vrf_rd_str);
+
+  bgp_vrf_clean_tables (vrf);
+
+  bgp_vrf_rt_import_unset (vrf);
+  if (vrf->rt_export)
+    ecommunity_unintern (&vrf->rt_export);
+  return;
+}
+
+static void
+bgp_vrf_delete_int (void *arg)
+{
+  struct bgp_vrf *vrf = arg;
+  char vrf_rd_str[RD_ADDRSTRLEN];
+
+  prefix_rd2str(&vrf->outbound_rd, vrf_rd_str, sizeof(vrf_rd_str));
+  zlog_info ("deleting vrf %s", vrf_rd_str);
+
+  bgp_vrf_delete_rd (vrf);
+
+  if (vrf->name)
+    free (vrf->name);
+  QOBJ_UNREG (vrf);
+  XFREE (MTYPE_BGP_VRF, vrf);
+}
+
+void
+bgp_vrf_context_delete (struct bgp_vrf *vrf)
+{
+  listnode_delete (vrf->bgp->vrfs, vrf);
+  bgp_vrf_delete_int(vrf);
+}
+
+void
+bgp_vrf_apply_new_imports (struct bgp_vrf *vrf, afi_t afi)
+{
+  struct bgp_node *rd_rn, *rn;
+  struct bgp_info *sel;
+  struct bgp_table *table;
+  struct ecommunity *ecom;
+  size_t i, j;
+  bool found;
+
+  if (!vrf->rt_import || vrf->rt_import->size == 0)
+    return;
+
+  for (rd_rn = bgp_table_top (vrf->bgp->rib[afi][SAFI_MPLS_VPN]); rd_rn;
+                  rd_rn = bgp_route_next (rd_rn))
+    if (rd_rn->info != NULL)
+      {
+        table = rd_rn->info;
+
+        for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+          {
+            for (sel = rn->info; sel; sel = sel->next)
+              if (CHECK_FLAG (sel->flags, BGP_INFO_SELECTED))
+                break;
+            if (!sel || !sel->attr || !sel->attr->extra)
+              continue;
+            ecom = sel->attr->extra->ecommunity;
+            if (!ecom)
+              continue;
+
+            found = false;
+            for (i = 0; i < (size_t)ecom->size && !found; i++)
+              for (j = 0; j < (size_t)vrf->rt_import->size && !found; j++)
+                if (!memcmp(ecom->val + i * 8, vrf->rt_import->val + j * 8, 8))
+                  found = true;
+            if (!found)
+              continue;
+          }
+      }
+}
+
+struct bgp_vrf *
+bgp_vrf_lookup_per_name (struct bgp *bgp, const char *name, int create)
+{
+  afi_t afi;
+  struct listnode *node;
+  struct bgp_vrf *vrf;
+  unsigned int len;
+
+  if (!name)
+    return NULL;
+  len = strlen(name);
+  for (ALL_LIST_ELEMENTS_RO(bgp->vrfs, node, vrf))
+    {
+      if (strlen (vrf->name) != len)
+        continue;
+      if (0 == strcmp (vrf->name, name))
+        break;
+    }
+  if (vrf || create == 0)
+    return vrf;
+  if ((vrf = XCALLOC (MTYPE_BGP_VRF, sizeof (struct bgp_vrf))) == NULL)
+    return NULL;
+  QOBJ_REG (vrf, bgp_vrf);
+  vrf->bgp = bgp;
+  vrf->name = strdup (name);
+  vrf->flag |= BGP_VRF_RD_UNSET;
+
+  for (afi = AFI_IP; afi < AFI_MAX; afi++)
+    {
+      vrf->route[afi] = bgp_table_init (afi, SAFI_UNICAST);
+      vrf->route[afi]->type = BGP_TABLE_VRF;
+      vrf->rib[afi] = bgp_table_init (afi, SAFI_UNICAST);
+      vrf->rib[afi]->type = BGP_TABLE_VRF;
+    }
+
+  listnode_add (bgp->vrfs, vrf);
+  return vrf;
+}
+
+/* VTY configuration and exploitation */
+
+DEFUN (bgp_vrf,
+       bgp_vrf_cmd,
+       "vrf WORD",
+       "BGP VRF\n"
+       "VRF Name\n"
+)
+{
+  VTY_DECLVAR_CONTEXT(bgp, bgp);
+  struct bgp_vrf *vrf;
+  if ( (vrf = bgp_vrf_lookup_per_name (bgp, (const char *)argv[1]->arg, 1)) == NULL)
+    return CMD_ERR_NO_MATCH;
+  VTY_PUSH_CONTEXT_SUB (BGP_VRF_NODE, vrf);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_vrf,
+       no_bgp_vrf_cmd,
+       "no vrf WORD",
+       NO_STR
+       "BGP VRF\n"
+       "VRF Name\n"
+)
+{
+  VTY_DECLVAR_CONTEXT(bgp, bgp);
+  struct bgp_vrf *vrf;
+
+  vrf = bgp_vrf_lookup_per_name (bgp, argv[2]->arg, 0);
+  if (! vrf)
+    {
+      vty_out (vty, "%% No VRF with name '%s'%s", argv[2]->arg, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  bgp_vrf_context_delete (vrf);
+  return CMD_SUCCESS;
+}
+
+DEFUN (exit_bgp_vrf,
+       exit_bgp_vrf_cmd,
+       "exit-bgp-vrf",
+       "Exit from BGP vrf configuration mode\n")
+{
+  if (vty->node == BGP_VRF_NODE)
+    vty->node = BGP_NODE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_vrf_rd,
+       bgp_vrf_rd_cmd,
+       "rd WORD",
+       "Route Distinguisher\n"
+       "Route Distinguisher Name\n"
+)
+{
+  VTY_DECLVAR_CONTEXT_SUB (bgp_vrf, vrf);
+  VTY_DECLVAR_CONTEXT(bgp, bgp);
+  struct prefix_rd prd;
+
+  if (! str2prefix_rd (argv[1]->arg, &prd))
+    {
+      vty_out (vty, "%% Invalid RD '%s'%s", argv[1]->arg, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  bgp_vrf_update_rd (bgp, vrf, &prd);
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_vrf_rt_export,
+       bgp_vrf_rt_export_cmd,
+       "rt export LINE...",
+       "Route Target\n"
+       "Export RT values\n"
+       "Export RT values\n"
+)
+{
+  VTY_DECLVAR_CONTEXT_SUB (bgp_vrf, vrf);
+  VTY_DECLVAR_CONTEXT(bgp, bgp);
+  struct ecommunity *ecom = NULL;
+  int idx_line = 2;
+  char *rts;
+
+
+  /* forge export list */
+  rts = argv_concat(argv, argc, idx_line);
+
+  /* convert list of ecoms string into ecom struct */
+  ecom = ecommunity_str2com (rts, ECOMMUNITY_ROUTE_TARGET, 0);
+  if (! ecom)
+    {
+      vty_out (vty, "%% Invalid RT '%s'%s", rts, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (rts)
+    XFREE (MTYPE_TMP, rts);
+
+  ecom = ecommunity_intern (ecom);
+  bgp_vrf_rt_export_set (vrf, ecom);
+  ecommunity_unintern (&ecom);
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_vrf_rt_import,
+       bgp_vrf_rt_import_cmd,
+       "rt import LINE...",
+       "Route Target\n"
+       "Import RT values\n"
+       "Import RT values\n"
+)
+{
+  VTY_DECLVAR_CONTEXT(bgp, bgp);
+  VTY_DECLVAR_CONTEXT_SUB (bgp_vrf, vrf);
+  struct ecommunity *ecom = NULL;
+  char *rts;
+  int idx_line = 2;
+
+  /* forge import list */
+  rts = argv_concat(argv, argc, idx_line);
+
+  /* convert list of ecoms string into ecom struct */
+  ecom = ecommunity_str2com (rts, ECOMMUNITY_ROUTE_TARGET, 0);
+  if (! ecom)
+    {
+      vty_out (vty, "%% Invalid RT '%s'%s", rts, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (rts)
+    XFREE (MTYPE_TMP, rts);
+
+  ecom = ecommunity_intern (ecom);
+  bgp_vrf_rt_import_set (vrf, ecom);
+  ecommunity_unintern (&ecom);
+  return CMD_SUCCESS;
+}
+
+DEFUN (bgp_vrf_rt_both,
+       bgp_vrf_rt_both_cmd,
+       "rt both LINE...",
+       "Route Target\n"
+       "Import and Export RT values\n"
+       "Import and Export RT values\n"
+)
+{
+  VTY_DECLVAR_CONTEXT(bgp, bgp);
+  VTY_DECLVAR_CONTEXT_SUB (bgp_vrf, vrf);
+  struct ecommunity *ecom = NULL, *ecom1;
+  char *rts;
+  int idx_line = 2;
+
+  /* forge export/import list */
+  rts = argv_concat(argv, argc, idx_line);
+
+  /* convert list of ecoms string into ecom struct */
+  ecom = ecommunity_str2com (rts, ECOMMUNITY_ROUTE_TARGET, 1);
+  if (! ecom)
+    {
+      vty_out (vty, "%% Invalid RT '%s'%s", rts, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  if (rts)
+    XFREE (MTYPE_TMP, rts);
+
+  ecom1 = ecommunity_intern (ecom);
+  bgp_vrf_rt_import_set (vrf, ecom1);
+  ecommunity_unintern (&ecom1);
+
+  ecom1 = ecommunity_intern (ecom);
+  bgp_vrf_rt_export_set (vrf, ecom1);
+  ecommunity_unintern (&ecom1);
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_vrf_rt_import,
+       no_bgp_vrf_rt_import_cmd,
+       "no rt import",
+       NO_STR
+       "Route Target\n"
+       "Import values\n"
+)
+{
+  VTY_DECLVAR_CONTEXT_SUB (bgp_vrf, vrf);
+
+  bgp_vrf_rt_import_unset (vrf);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_vrf_rt_export,
+       no_bgp_vrf_rt_export_cmd,
+       "no rt export",
+       NO_STR
+       "Route Target\n"
+       "Export RT values\n"
+)
+{
+  VTY_DECLVAR_CONTEXT_SUB (bgp_vrf, vrf);
+
+  bgp_vrf_rt_export_unset (vrf);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_vrf_rt_both,
+       no_bgp_vrf_rt_both_cmd,
+       "no rt both",
+       NO_STR
+       "Route Target\n"
+       "Import and Export RT values\n"
+)
+{
+  VTY_DECLVAR_CONTEXT_SUB (bgp_vrf, vrf);
+
+  bgp_vrf_rt_export_unset (vrf);
+  bgp_vrf_rt_import_unset (vrf);
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_vrf_rd,
+       no_bgp_vrf_rd_cmd,
+       "no rd WORD",
+       NO_STR
+       "BGP Route Distinguisher\n"
+       "Route Distinguisher\n"
+)
+{
+  VTY_DECLVAR_CONTEXT_SUB (bgp_vrf, vrf);
+
+  bgp_vrf_delete_rd (vrf);
+  return CMD_SUCCESS;
+}
+
+static int
+bgp_show_vrf (struct vty *vty, const char *vrf_name, afi_t afi,
+              enum bgp_show_type type, void *output_arg, u_char use_json)
+{
+  struct bgp *bgp = bgp_get_default();
+  struct bgp_vrf *vrf;
+
+  if (! bgp)
+    {
+      vty_out (vty, "%% No default BGP instance%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  vrf = bgp_vrf_lookup_per_name (bgp, vrf_name, 0);
+  if (! vrf)
+    {
+      vty_out (vty, "%% No VRF with name '%s'%s", vrf_name, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return bgp_show_table (vty, bgp, vrf->rib[afi], type, output_arg, use_json);
+}
+
+static int
+bgp_show_vrf_route (struct vty *vty, const char *vrf_name, const char *ip_str,
+                    afi_t afi, int prefix_check,u_char use_json)
+{
+  struct bgp *bgp = bgp_get_default();
+  struct bgp_vrf *vrf;
+
+  if (! bgp)
+    {
+      vty_out (vty, "%% No default BGP instance%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  vrf = bgp_vrf_lookup_per_name (bgp, vrf_name, 0);
+  if (! vrf)
+    {
+      vty_out (vty, "%% No VRF with name '%s'%s", vrf_name, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return bgp_show_route_in_table (vty, bgp, vrf->rib[afi], ip_str,
+                                  afi, SAFI_MPLS_LABELED_VPN, NULL,
+                                  prefix_check, BGP_PATH_ALL, use_json);
+}
+
+DEFUN (show_ip_bgp_vrf,
+       show_ip_bgp_vrf_cmd,
+       "show ip bgp bgpvrf WORD [json]",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "BGP VRF"
+       "BGP VRF Name"
+       JSON_STR)
+{
+  return bgp_show_vrf (vty, argv[4]->arg, AFI_IP, bgp_show_type_normal, NULL, use_json(argc, argv));
+}
+
+DEFUN (show_ipv6_bgp_vrf,
+       show_ipv6_bgp_vrf_cmd,
+       "show ipv6 bgp bgpvrf WORD [json]",
+       SHOW_STR
+       "IPv6"
+       BGP_STR
+       "BGP VRF"
+       "BGP VRF Name"
+       JSON_STR)
+{
+  return bgp_show_vrf (vty, argv[4]->arg, AFI_IP6, bgp_show_type_normal, NULL, use_json(argc, argv));
+}
+
+DEFUN (show_ip_bgp_vrf_route,
+       show_ip_bgp_vrf_route_cmd,
+       "show ip bgp bgpvrf WORD A.B.C.D [json]",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "VRF\n"
+       "BGP VRF Name\n"
+       "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n"
+       JSON_STR)
+{
+  return bgp_show_vrf_route (vty, argv[4]->arg, argv[5]->arg, AFI_IP, 0, use_json (argc,argv));
+}
+
+DEFUN (show_ipv6_bgp_vrf_route,
+       show_ipv6_bgp_vrf_route_cmd,
+       "show ipv6 bgp bgpvrf WORD X:X::X:X [json]",
+       SHOW_STR
+       IP_STR
+       BGP_STR
+       "VRF\n"
+       "BGP VRF Name\n"
+       "IPv6 prefix <network>/<length>\n"
+       "JavaScript Object Notation\n")
+{
+  return bgp_show_vrf_route (vty, argv[4]->arg, argv[5]->arg, AFI_IP6, 0, use_json (argc,argv));
+}
+
+/* BGP VRF init/delete/ show running */
+void
+bgp_bgpvrf_init (struct bgp *bgp)
+{
+  bgp->vrfs = list_new();
+  bgp->vrfs->del = bgp_vrf_delete_int;
+  bgp->rt_subscribers = hash_create(bgp_rt_hash_key, bgp_rt_hash_cmp);
+
+  install_node (&bgp_vrf_node, NULL);
+
+  install_element (BGP_NODE, &bgp_vrf_cmd);
+  install_element (BGP_NODE, &no_bgp_vrf_cmd);
+  install_element (BGP_VRF_NODE, &bgp_vrf_rd_cmd);
+  install_element (BGP_VRF_NODE, &no_bgp_vrf_rd_cmd);
+  install_element (BGP_VRF_NODE, &bgp_vrf_rt_export_cmd);
+  install_element (BGP_VRF_NODE, &bgp_vrf_rt_import_cmd);
+  install_element (BGP_VRF_NODE, &bgp_vrf_rt_both_cmd);
+  install_element (BGP_VRF_NODE, &no_bgp_vrf_rt_import_cmd);
+  install_element (BGP_VRF_NODE, &no_bgp_vrf_rt_export_cmd);
+  install_element (BGP_VRF_NODE, &no_bgp_vrf_rt_both_cmd);
+  install_element (BGP_VRF_NODE, &exit_bgp_vrf_cmd);
+
+  install_element (VIEW_NODE, &show_ip_bgp_vrf_cmd);
+  install_element (VIEW_NODE, &show_ip_bgp_vrf_route_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_vrf_cmd);
+  install_element (VIEW_NODE, &show_ipv6_bgp_vrf_route_cmd);
+}
+
+void
+bgp_bgpvrf_delete (struct bgp *bgp)
+{
+  if(bgp->vrfs)
+    list_delete (bgp->vrfs);
+  bgp->vrfs = NULL;
+  if(bgp->rt_subscribers)
+    {
+      hash_clean (bgp->rt_subscribers, NULL);
+      hash_free (bgp->rt_subscribers);
+      bgp->rt_subscribers = NULL;
+    }
+}
+
+void bgp_config_write_bgpvrf (struct vty *vty, struct bgp *bgp)
+{
+  struct bgp_vrf *vrf;
+  char rdstr[RD_ADDRSTRLEN];
+  char *str_p, *str2_p;
+  struct listnode *node;
+  
+  for (ALL_LIST_ELEMENTS_RO(bgp->vrfs, node, vrf))
+    {
+      vty_out(vty, " vrf %s%s", vrf->name, VTY_NEWLINE);
+      /* an RD has been configured */
+      if (!(vrf->flag & BGP_VRF_RD_UNSET))
+        {
+          str_p = prefix_rd2str(&(vrf->outbound_rd), rdstr, RD_ADDRSTRLEN);
+          vty_out(vty, "  rd %s%s", str_p == NULL?"<err>":str_p, VTY_NEWLINE);
+        }
+      if(vrf->rt_import)
+        {
+          str2_p = ecommunity_ecom2str (vrf->rt_import, ECOMMUNITY_FORMAT_ROUTE_MAP);
+          if(str2_p)
+            {
+              vty_out(vty, "  rt import %s%s", str2_p, VTY_NEWLINE);
+              XFREE (MTYPE_ECOMMUNITY_STR, str2_p);
+            }
+        }
+      if(vrf->rt_export)
+        {
+          str2_p = ecommunity_ecom2str (vrf->rt_export, ECOMMUNITY_FORMAT_ROUTE_MAP);
+          if(str2_p)
+            {
+              vty_out(vty, "  rt export %s%s", str2_p, VTY_NEWLINE);
+              XFREE (MTYPE_ECOMMUNITY_STR, str2_p);
+            }
+        }
+      vty_out (vty, " exit%s", VTY_NEWLINE);
+    }
+}
diff --git a/bgpd/bgp_vrf.h b/bgpd/bgp_vrf.h
new file mode 100644
index 000000000000..f8dd4489b0e0
--- /dev/null
+++ b/bgpd/bgp_vrf.h
@@ -0,0 +1,84 @@
+/* BGP VRF definition header
+   Copyright (C) 2016, 6WIND
+
+This file is part of GNU Quagga.
+
+GNU Quagga is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU Quagga is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Quagga; see the file COPYING.  If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef _QUAGGA_BGP_VRF_H
+#define _QUAGGA_BGP_VRF_H
+
+
+#include "qobj.h"
+#include "linklist.h"
+#include "prefix.h"
+
+#include "bgpd.h"
+#include "bgp_table.h"
+#include "bgp_ecommunity.h"
+
+struct bgp_rt_sub
+{
+  struct ecommunity_val rt;
+
+  struct list *vrfs;
+};
+
+struct bgp_vrf
+{
+  struct bgp *bgp;
+
+  char *name;
+
+  /* RD used for route advertisements */
+  struct prefix_rd outbound_rd;
+
+  /* import and export lists */
+  struct ecommunity *rt_import;
+  struct ecommunity *rt_export;
+
+  /* BGP routing information base.  */
+  struct bgp_table *rib[AFI_MAX];
+
+  /* Static route configuration.  */
+  struct bgp_table *route[AFI_MAX];
+
+  /* internal flag */
+#define BGP_VRF_RD_UNSET 1
+  uint16_t flag;
+
+  QOBJ_FIELDS
+};
+DECLARE_QOBJ_TYPE(bgp_vrf)
+
+extern void bgp_bgpvrf_init (struct bgp *bgp);
+extern void bgp_bgpvrf_delete (struct bgp *bgp);
+extern void bgp_config_write_bgpvrf (struct vty *vty, struct bgp *bgp);
+
+extern void bgp_vrf_context_delete (struct bgp_vrf *vrf);
+extern void bgp_vrf_delete_rd (struct bgp_vrf *vrf);
+extern struct bgp_vrf *bgp_vrf_update_rd (struct bgp *bgp, struct bgp_vrf *vrf, struct prefix_rd *outbound_rd);
+extern struct bgp_vrf *bgp_vrf_lookup (struct bgp *bgp, struct prefix_rd *outbound_rd);
+extern struct bgp_vrf *bgp_vrf_lookup_per_name (struct bgp *bgp, const char *name, int create);
+extern struct bgp_vrf *bgp_vrf_lookup_per_rn (struct bgp *bgp, int afi, struct bgp_node *vrf_rn);
+extern void bgp_vrf_rt_export_set (struct bgp_vrf *vrf, struct ecommunity *rt_export);
+extern void bgp_vrf_rt_import_set (struct bgp_vrf *vrf, struct ecommunity *rt_import);
+extern void bgp_vrf_apply_new_imports (struct bgp_vrf *vrf, afi_t afi);
+extern void bgp_vrf_rt_import_unset (struct bgp_vrf *vrf);
+extern void bgp_vrf_rt_export_unset (struct bgp_vrf *vrf);
+
+#endif /* _QUAGGA_BGP_VRF */
+
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 1c2cc037f9d8..416233b64dae 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -56,6 +56,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_packet.h"
 #include "bgpd/bgp_updgrp.h"
 #include "bgpd/bgp_bfd.h"
+#include "bgpd/bgp_vrf.h"
 
 static struct peer_group *
 listen_range_exists (struct bgp *bgp, struct prefix *range, int exact);
@@ -9745,6 +9746,7 @@ bgp_vty_init (void)
   install_default (BGP_VPNV6_NODE);
   install_default (BGP_ENCAP_NODE);
   install_default (BGP_ENCAPV6_NODE);
+  install_default (BGP_VRF_NODE);
 
   /* "bgp multiple-instance" commands. */
   install_element (CONFIG_NODE, &bgp_multiple_instance_cmd);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 30f6e0d859a5..a1b9d204ace1 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -34,6 +34,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "log.h"
 #include "plist.h"
 #include "linklist.h"
+#include "hash.h"
 #include "workqueue.h"
 #include "queue.h"
 #include "zclient.h"
@@ -77,6 +78,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_updgrp.h"
 #include "bgpd/bgp_bfd.h"
 #include "bgpd/bgp_memory.h"
+#include "bgpd/bgp_vrf.h"
 
 DEFINE_QOBJ_TYPE(bgp_master)
 DEFINE_QOBJ_TYPE(bgp)
@@ -2923,6 +2925,7 @@ bgp_create (as_t *as, const char *name, enum bgp_instance_type inst_type)
 
   bgp->wpkt_quanta = BGP_WRITE_PACKET_MAX;
   bgp->coalesce_time = BGP_DEFAULT_SUBGROUP_COALESCE_TIME;
+  bgp_bgpvrf_init (bgp);
 
   QOBJ_REG (bgp, bgp);
 
@@ -3168,6 +3171,7 @@ bgp_delete (struct bgp *bgp)
         bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
     }
 
+  bgp_bgpvrf_delete (bgp);
   /* Delete static routes (networks). */
   bgp_static_delete (bgp);
 
@@ -7436,6 +7440,8 @@ bgp_config_write (struct vty *vty)
 
       /* listen range and limit for dynamic BGP neighbors */
       bgp_config_write_listen (vty, bgp);
+      /* bgp vrf configuration */
+      bgp_config_write_bgpvrf (vty, bgp);
 
       /* No auto-summary */
       if (bgp_option_check (BGP_OPT_CONFIG_CISCO))
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 86fa207b6b7f..38c4471b5c83 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -62,6 +62,7 @@ typedef u_int16_t bgp_size_t;
     __typeof__ (b) _b = (b);	\
     _a > _b ? _a : _b; })
 
+
 enum bgp_af_index
 {
   BGP_AF_START,
@@ -361,8 +362,14 @@ struct bgp
   struct rfapi *rfapi;
 #endif
 
+  /* VRFs */
+  struct list *vrfs;
+
+  struct hash *rt_subscribers;
+
   QOBJ_FIELDS
 };
+
 DECLARE_QOBJ_TYPE(bgp)
 
 #define BGP_ROUTE_ADV_HOLD(bgp) (bgp->main_peers_update_hold)
-- 
2.1.4





More information about the dev mailing list